fix(organization): typecheck node exceeds the maximum length (#5372)

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
This commit is contained in:
Alex Yang
2025-10-17 03:08:55 +00:00
committed by GitHub
co-authored by cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
parent bae3cdf09b
commit 4b5f637bf0
13 changed files with 1078 additions and 195 deletions
+3 -1
View File
@@ -6,7 +6,9 @@
"typecheck": "tsc --project tsconfig.json"
},
"dependencies": {
"@better-auth/sso": "workspace:*",
"@better-auth/stripe": "workspace:*",
"better-auth": "workspace:*",
"better-sqlite3": "^12.4.1"
"stripe": "^18.5.0"
}
}
+209
View File
@@ -0,0 +1,209 @@
import { betterAuth } from "better-auth";
import {
bearer,
admin,
multiSession,
organization,
twoFactor,
oneTap,
oAuthProxy,
openAPI,
customSession,
deviceAuthorization,
lastLoginMethod,
} from "better-auth/plugins";
import { nextCookies } from "better-auth/next-js";
import { passkey } from "better-auth/plugins/passkey";
import { stripe } from "@better-auth/stripe";
import { sso } from "@better-auth/sso";
import { Stripe } from "stripe";
export const auth = betterAuth({
appName: "Better Auth Demo",
plugins: [
organization({}),
twoFactor({}),
passkey(),
openAPI(),
bearer(),
admin({ adminUserIds: ["EXD5zjob2SD6CBWcEQ6OpLRHcyoUbnaB"] }),
multiSession(),
oAuthProxy(),
nextCookies(),
oneTap(),
customSession(async (session) => {
return {
...session,
user: {
...session.user,
dd: "test",
},
};
}),
stripe({
stripeClient: new Stripe(process.env.STRIPE_KEY || "sk_test_"),
stripeWebhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
subscription: {
enabled: true,
allowReTrialsForDifferentPlans: true,
plans: () => {
const PRO_PRICE_ID = {
default:
process.env.STRIPE_PRO_PRICE_ID ??
"price_1RoxnRHmTADgihIt4y8c0lVE",
annual:
process.env.STRIPE_PRO_ANNUAL_PRICE_ID ??
"price_1RoxnoHmTADgihItzFvVP8KT",
};
const PLUS_PRICE_ID = {
default:
process.env.STRIPE_PLUS_PRICE_ID ??
"price_1RoxnJHmTADgihIthZTLmrPn",
annual:
process.env.STRIPE_PLUS_ANNUAL_PRICE_ID ??
"price_1Roxo5HmTADgihItEbJu5llL",
};
return [
{
name: "Plus",
priceId: PLUS_PRICE_ID.default,
annualDiscountPriceId: PLUS_PRICE_ID.annual,
freeTrial: {
days: 7,
},
},
{
name: "Pro",
priceId: PRO_PRICE_ID.default,
annualDiscountPriceId: PRO_PRICE_ID.annual,
freeTrial: {
days: 7,
},
},
];
},
},
}),
sso({
defaultSSO: [
{
domain: "http://localhost:3000",
providerId: "sso",
samlConfig: {
issuer: "http://localhost:3000/api/auth/sso/saml2/sp/metadata",
entryPoint:
"https://dummyidp.com/apps/app_01k16v4vb5yytywqjjvv2b3435",
cert: `-----BEGIN CERTIFICATE-----
MIIDBzCCAe+gAwIBAgIUCLBK4f75EXEe4gyroYnVaqLoSp4wDQYJKoZIhvcNAQEL
BQAwEzERMA8GA1UEAwwIZHVtbXlpZHAwHhcNMjQwNTEzMjE1NDE2WhcNMzQwNTEx
MjE1NDE2WjATMREwDwYDVQQDDAhkdW1teWlkcDCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAKhmgQmWb8NvGhz952XY4SlJlpWIK72RilhOZS9frDYhqWVJ
HsGH9Z7sSzrM/0+YvCyEWuZV9gpMeIaHZxEPDqW3RJ7KG51fn/s/qFvwctf+CZDj
yfGDzYs+XIgf7p56U48EmYeWpB/aUW64gSbnPqrtWmVFBisOfIx5aY3NubtTsn+g
0XbdX0L57+NgSvPQHXh/GPXA7xCIWm54G5kqjozxbKEFA0DS3yb6oHRQWHqIAM/7
mJMdUVZNIV1q7c2JIgAl23uDWq+2KTE2R5liP/KjvjwKonVKtTqGqX6ei25rsTHO
aDpBH/LdQK2txgsm7R7+IThWNvUI0TttrmwBqyMCAwEAAaNTMFEwHQYDVR0OBBYE
FD142gxIAJMhpgMkgpzmRNoW9XbEMB8GA1UdIwQYMBaAFD142gxIAJMhpgMkgpzm
RNoW9XbEMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADQd6k6z
FIc20GfGHY5C2MFwyGOmP5/UG/JiTq7Zky28G6D0NA0je+GztzXx7VYDfCfHxLcm
2k5t9nYhb9kVawiLUUDVF6s+yZUXA4gUA3KoTWh1/oRxR3ggW7dKYm9fsNOdQAbx
UUkzp7HLZ45ZlpKUS0hO7es+fPyF5KVw0g0SrtQWwWucnQMAQE9m+B0aOf+92y7J
QkdgdR8Gd/XZ4NZfoOnKV7A1utT4rWxYCgICeRTHx9tly5OhPW4hQr5qOpngcsJ9
vhr86IjznQXhfj3hql5lA3VbHW04ro37ROIkh2bShDq5dwJJHpYCGrF3MQv8S3m+
jzGhYL6m9gFTm/8=
-----END CERTIFICATE-----`,
spMetadata: {
metadata: `
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="http://localhost:3000/api/auth/sso/saml2/sp/metadata">
<md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>MIIE3jCCAsYCCQDE5FzoAkixzzANBgkqhkiG9w0BAQsFADAxMQswCQYDVQQGEwJVUzEQMA4GA1UECAwHRmxvcmlkYTEQMA4GA1UEBwwHT3JsYW5kbzAeFw0yMzExMTkxMjUyMTVaFw0zMzExMTYxMjUyMTVaMDExCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdGbG9yaWRhMRAwDgYDVQQHDAdPcmxhbmRvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2ELJsLZs4yBH7a2U5pA7xw+Oiut7b/ROKh2BqSTKRbEG4xy7WwljT02Mh7GTjLvswtZSUObWFO5v14HNORa3+J9JT2DH+9F+FJ770HX8a3cKYBNQt3xP4IeUyjI3QWzrGtkYPwSZ74tDpAUtuqPAxtoCaZXFDtX6lvCJDqiPnfxRZrKkepYWINSwu4DRpg6KoiPWRCYTsEcCzImInzlACdM97jpG1gLGA6a4dmjalQbRtvC56N0Z56gIhYq2F5JdzB2a10pqoIY8ggXZGIJS9I++8mmdTj6So5pPxLwnCYUhwDew1/DMbi9xIwYozs9pEtHCTn1l34jldDwTziVAxGQZO7QUuoMl997zqcPS7pVWRnfz5odKuytLvQDA0lRVfzOxtqbM3qVhoLT2iDmnuEtlZzgfbt4WEuT2538qxZJkFRpZQIrTj3ybqmWAv36Cp49dfeMwaqjhfX7/mVfbsPMSC653DSZBB+n+Uz0FC3QhH+vIdNhXNAQ5tBseHUR6pXiMnLtI/WVbMvpvFwK2faFTcx1oaP/Qk6yCq66tJvPbnatT9qGF8rdBJmAk9aBdQTI+hAh5mDtDweCrgVL+Tm/+Q85hSl4HGzH/LhLVS478tZVX+o+0yorZ35LCW3e4v8iX+1VEGSdg2ooOWtbSSXK2cYZr8ilyUQp0KueenR0CAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAsonAahruWuHlYbDNQVD0ryhL/b+ttKKqVeT87XYDkvVhlSSSVAKcCwK/UU6z8Ty9dODUkd93Qsbof8fGMlXeYCtDHMRanvWLtk4wVkAMyNkDYHzJ1FbO7v44ZBbqNzSLy2kosbRELlcz+P3/42xumlDqAw/k13tWUdlLDxb0pd8R5yBev6HkIdJBIWtKmUuI+e8F/yTNf5kY7HO1p0NeKdVeZw4Ydw33+BwVxVNmhIxzdP5ZFQv0XRFWhCMo/6RLEepCvWUp/T1WRFqgwAdURaQrvvfpjO/Ls+neht1SWDeP8RRgsDrXIc3gZfaD8q4liIDTZ6HsFi7FmLbZatU8jJ4pCstxQLCvmix+1zF6Fwa9V5OApSTbVqBOsDZbJxeAoSzy5Wx28wufAZT4Kc/OaViXPV5o/ordPs4EYKgd/eNFCgIsZYXe75rYXqnieAIfJEGddsLBpqlgLkwvf5KVS4QNqqX+2YubP63y+3sICq2ScdhO3LZs3nlqQ/SgMiJnCBbDUDZ9GGgJNJVVytcSz5IDQHeflrq/zTt1c4q1DO3CS7mimAnTCjetERRQ3mgY/2hRiuCDFj3Cy7QMjFs3vBsbWrjNWlqyveFmHDRkq34Om7eA2jl3LZ5u7vSm0/ylp/vtoysMjwEmw/0NA3hZPTG3OJxcvFcXBsz0SiFcd1U=</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:KeyDescriptor use="encryption">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>MIIE3jCCAsYCCQDE5FzoAkixzzANBgkqhkiG9w0BAQsFADAxMQswCQYDVQQGEwJVUzEQMA4GA1UECAwHRmxvcmlkYTEQMA4GA1UEBwwHT3JsYW5kbzAeFw0yMzExMTkxMjUyMTVaFw0zMzExMTYxMjUyMTVaMDExCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdGbG9yaWRhMRAwDgYDVQQHDAdPcmxhbmRvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2ELJsLZs4yBH7a2U5pA7xw+Oiut7b/ROKh2BqSTKRbEG4xy7WwljT02Mh7GTjLvswtZSUObWFO5v14HNORa3+J9JT2DH+9F+FJ770HX8a3cKYBNQt3xP4IeUyjI3QWzrGtkYPwSZ74tDpAUtuqPAxtoCaZXFDtX6lvCJDqiPnfxRZrKkepYWINSwu4DRpg6KoiPWRCYTsEcCzImInzlACdM97jpG1gLGA6a4dmjalQbRtvC56N0Z56gIhYq2F5JdzB2a10pqoIY8ggXZGIJS9I++8mmdTj6So5pPxLwnCYUhwDew1/DMbi9xIwYozs9pEtHCTn1l34jldDwTziVAxGQZO7QUuoMl997zqcPS7pVWRnfz5odKuytLvQDA0lRVfzOxtqbM3qVhoLT2iDmnuEtlZzgfbt4WEuT2538qxZJkFRpZQIrTj3ybqmWAv36Cp49dfeMwaqjhfX7/mVfbsPMSC653DSZBB+n+Uz0FC3QhH+vIdNhXNAQ5tBseHUR6pXiMnLtI/WVbMvpvFwK2faFTcx1oaP/Qk6yCq66tJvPbnatT9qGF8rdBJmAk9aBdQTI+hAh5mDtDweCrgVL+Tm/+Q85hSl4HGzH/LhLVS478tZVX+o+0yorZ35LCW3e4v8iX+1VEGSdg2ooOWtbSSXK2cYZr8ilyUQp0KueenR0CAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAsonAahruWuHlYbDNQVD0ryhL/b+ttKKqVeT87XYDkvVhlSSSVAKcCwK/UU6z8Ty9dODUkd93Qsbof8fGMlXeYCtDHMRanvWLtk4wVkAMyNkDYHzJ1FbO7v44ZBbqNzSLy2kosbRELlcz+P3/42xumlDqAw/k13tWUdlLDxb0pd8R5yBev6HkIdJBIWtKmUuI+e8F/yTNf5kY7HO1p0NeKdVeZw4Ydw33+BwVxVNmhIxzdP5ZFQv0XRFWhCMo/6RLEepCvWUp/T1WRFqgwAdURaQrvvfpjO/Ls+neht1SWDeP8RRgsDrXIc3gZfaD8q4liIDTZ6HsFi7FmLbZatU8jJ4pCstxQLCvmix+1zF6Fwa9V5OApSTbVqBOsDZbJxeAoSzy5Wx28wufAZT4Kc/OaViXPV5o/ordPs4EYKgd/eNFCgIsZYXe75rYXqnieAIfJEGddsLBpqlgLkwvf5KVS4QNqqX+2YubP63y+3sICq2ScdhO3LZs3nlqQ/SgMiJnCBbDUDZ9GGgJNJVVytcSz5IDQHeflrq/zTt1c4q1DO3CS7mimAnTCjetERRQ3mgY/2hRiuCDFj3Cy7QMjFs3vBsbWrjNWlqyveFmHDRkq34Om7eA2jl3LZ5u7vSm0/ylp/vtoysMjwEmw/0NA3hZPTG3OJxcvFcXBsz0SiFcd1U=</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:3000/api/auth/sso/saml2/sp/sls"/>
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:3000/api/auth/sso/saml2/sp/acs/sso" index="1"/>
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:3000/api/auth/sso/saml2/sp/acs/sso" index="1"/>
</md:SPSSODescriptor>
<md:Organization>
<md:OrganizationName xml:lang="en-US">Organization Name</md:OrganizationName>
<md:OrganizationDisplayName xml:lang="en-US">Organization DisplayName</md:OrganizationDisplayName>
<md:OrganizationURL xml:lang="en-US">http://localhost:3000/</md:OrganizationURL>
</md:Organization>
<md:ContactPerson contactType="technical">
<md:GivenName>Technical Contact Name</md:GivenName>
<md:EmailAddress>technical_contact@gmail.com</md:EmailAddress>
</md:ContactPerson>
<md:ContactPerson contactType="support">
<md:GivenName>Support Contact Name</md:GivenName>
<md:EmailAddress>support_contact@gmail.com</md:EmailAddress>
</md:ContactPerson>
</md:EntityDescriptor>
`,
},
idpMetadata: {
entityURL:
"https://dummyidp.com/apps/app_01k16v4vb5yytywqjjvv2b3435/metadata",
entityID:
"https://dummyidp.com/apps/app_01k16v4vb5yytywqjjvv2b3435",
redirectURL:
"https://dummyidp.com/apps/app_01k16v4vb5yytywqjjvv2b3435/sso",
singleSignOnService: [
{
Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
Location:
"https://dummyidp.com/apps/app_01k16v4vb5yytywqjjvv2b3435/sso",
},
],
cert: `-----BEGIN CERTIFICATE-----
MIIDBzCCAe+gAwIBAgIUCLBK4f75EXEe4gyroYnVaqLoSp4wDQYJKoZIhvcNAQEL
BQAwEzERMA8GA1UEAwwIZHVtbXlpZHAwHhcNMjQwNTEzMjE1NDE2WhcNMzQwNTEx
MjE1NDE2WjATMREwDwYDVQQDDAhkdW1teWlkcDCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAKhmgQmWb8NvGhz952XY4SlJlpWIK72RilhOZS9frDYhqWVJ
HsGH9Z7sSzrM/0+YvCyEWuZV9gpMeIaHZxEPDqW3RJ7KG51fn/s/qFvwctf+CZDj
yfGDzYs+XIgf7p56U48EmYeWpB/aUW64gSbnPqrtWmVFBisOfIx5aY3NubtTsn+g
0XbdX0L57+NgSvPQHXh/GPXA7xCIWm54G5kqjozxbKEFA0DS3yb6oHRQWHqIAM/7
mJMdUVZNIV1q7c2JIgAl23uDWq+2KTE2R5liP/KjvjwKonVKtTqGqX6ei25rsTHO
aDpBH/LdQK2txgsm7R7+IThWNvUI0TttrmwBqyMCAwEAAaNTMFEwHQYDVR0OBBYE
FD142gxIAJMhpgMkgpzmRNoW9XbEMB8GA1UdIwQYMBaAFD142gxIAJMhpgMkgpzm
RNoW9XbEMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADQd6k6z
FIc20GfGHY5C2MFwyGOmP5/UG/JiTq7Zky28G6D0NA0je+GztzXx7VYDfCfHxLcm
2k5t9nYhb9kVawiLUUDVF6s+yZUXA4gUA3KoTWh1/oRxR3ggW7dKYm9fsNOdQAbx
UUkzp7HLZ45ZlpKUS0hO7es+fPyF5KVw0g0SrtQWwWucnQMAQE9m+B0aOf+92y7J
QkdgdR8Gd/XZ4NZfoOnKV7A1utT4rWxYCgICeRTHx9tly5OhPW4hQr5qOpngcsJ9
vhr86IjznQXhfj3hql5lA3VbHW04ro37ROIkh2bShDq5dwJJHpYCGrF3MQv8S3m+
jzGhYL6m9gFTm/8=
-----END CERTIFICATE-----`,
},
callbackUrl: "/dashboard",
},
},
],
}),
deviceAuthorization({
expiresIn: "3min",
interval: "5s",
}),
lastLoginMethod(),
],
});
auth.api
.createOrganization({
body: {
name: "My Org",
slug: "my-org",
},
})
.catch();
+4 -4
View File
@@ -6,7 +6,7 @@ import {
} from "better-call";
import type { BetterAuthOptions } from "@better-auth/core";
import type { UnionToIntersection } from "../types/helper";
import { originCheckMiddleware } from "./middlewares/origin-check";
import { originCheckMiddleware } from "./middlewares";
import {
callbackOAuth,
forgetPassword,
@@ -37,9 +37,9 @@ import {
requestPasswordReset,
requestPasswordResetCallback,
} from "./routes";
import { ok } from "./routes/ok";
import { signUpEmail } from "./routes/sign-up";
import { error } from "./routes/error";
import { ok } from "./routes";
import { signUpEmail } from "./routes";
import { error } from "./routes";
import { type InternalLogger, logger } from "@better-auth/core/env";
import type { BetterAuthPlugin } from "@better-auth/core";
import { onRequestRateLimit } from "./rate-limiter";
@@ -21,7 +21,7 @@ function getVenderBaseURL() {
return vercel || netlify || render || aws || google || azure;
}
interface OAuthProxyOptions {
export interface OAuthProxyOptions {
/**
* The current URL of the application.
* The plugin will attempt to infer the current URL from your environment
@@ -81,13 +81,13 @@ function getTypeFromZodType(zodType: z.ZodType<any>) {
return allowedType.has(type) ? (type as AllowedType) : "string";
}
type FieldSchema = {
export type FieldSchema = {
type: DBFieldType;
default?: DBFieldAttributeConfig["defaultValue"] | "Generated at runtime";
readOnly?: boolean;
};
type OpenAPIModelSchema = {
export type OpenAPIModelSchema = {
type: "object";
properties: Record<string, FieldSchema>;
required?: string[];
@@ -6,6 +6,8 @@ import type { LiteralString } from "../../types/helper";
import { APIError } from "../../api";
import { createAuthEndpoint } from "@better-auth/core/api";
export type { FieldSchema, Path, OpenAPIModelSchema } from "./generator";
type ScalarTheme =
| "alternate"
| "default"
@@ -10,7 +10,7 @@ import type {
import type { Prettify } from "../../types/helper";
import { type AccessControl, type Role } from "../access";
import type { BetterAuthClientPlugin } from "@better-auth/core";
import { organization } from "./organization";
import { type OrganizationPlugin } from "./organization";
import { useAuthQuery } from "../../client";
import {
defaultStatements,
@@ -129,29 +129,27 @@ export const organizationClient = <CO extends OrganizationClientOptions>(
type Schema = CO["schema"];
return {
id: "organization",
$InferServerPlugin: {} as ReturnType<
typeof organization<{
ac: CO["ac"] extends AccessControl
? CO["ac"]
: AccessControl<DefaultStatements>;
roles: CO["roles"] extends Record<string, Role>
? CO["roles"]
: {
admin: Role;
member: Role;
owner: Role;
};
teams: {
enabled: CO["teams"] extends { enabled: true } ? true : false;
};
schema: Schema;
dynamicAccessControl: {
enabled: CO["dynamicAccessControl"] extends { enabled: true }
? true
: false;
};
}>
>,
$InferServerPlugin: {} as OrganizationPlugin<{
ac: CO["ac"] extends AccessControl
? CO["ac"]
: AccessControl<DefaultStatements>;
roles: CO["roles"] extends Record<string, Role>
? CO["roles"]
: {
admin: Role;
member: Role;
owner: Role;
};
teams: {
enabled: CO["teams"] extends { enabled: true } ? true : false;
};
schema: Schema;
dynamicAccessControl: {
enabled: CO["dynamicAccessControl"] extends { enabled: true }
? true
: false;
};
}>,
getActions: ($fetch, _$store, co) => ({
$Infer: {
ActiveOrganization: {} as OrganizationReturn,
@@ -2,7 +2,7 @@ import { APIError } from "better-call";
import * as z from "zod";
import type { BetterAuthPluginDBSchema } from "@better-auth/core/db";
import { createAuthEndpoint } from "@better-auth/core/api";
import { getSessionFromCtx } from "../../api/routes";
import { getSessionFromCtx } from "../../api";
import type { BetterAuthPlugin } from "@better-auth/core";
import { shimContext } from "../../utils/shim";
import { type AccessControl } from "../access";
@@ -50,6 +50,7 @@ import type {
InferInvitation,
InferMember,
InferOrganization,
OrganizationSchema,
Team,
TeamMember,
} from "./schema";
@@ -70,6 +71,211 @@ export function parseRoles(roles: string | string[]): string {
return Array.isArray(roles) ? roles.join(",") : roles;
}
export type DynamicAccessControlEndpoints<O extends OrganizationOptions> = {
createOrgRole: ReturnType<typeof createOrgRole<O>>;
deleteOrgRole: ReturnType<typeof deleteOrgRole<O>>;
listOrgRoles: ReturnType<typeof listOrgRoles<O>>;
getOrgRole: ReturnType<typeof getOrgRole<O>>;
updateOrgRole: ReturnType<typeof updateOrgRole<O>>;
};
export type TeamEndpoints<O extends OrganizationOptions> = {
createTeam: ReturnType<typeof createTeam<O>>;
listOrganizationTeams: ReturnType<typeof listOrganizationTeams<O>>;
removeTeam: ReturnType<typeof removeTeam<O>>;
updateTeam: ReturnType<typeof updateTeam<O>>;
setActiveTeam: ReturnType<typeof setActiveTeam<O>>;
listUserTeams: ReturnType<typeof listUserTeams<O>>;
listTeamMembers: ReturnType<typeof listTeamMembers<O>>;
addTeamMember: ReturnType<typeof addTeamMember<O>>;
removeTeamMember: ReturnType<typeof removeTeamMember<O>>;
};
export type OrganizationEndpoints<O extends OrganizationOptions> = {
createOrganization: ReturnType<typeof createOrganization<O>>;
updateOrganization: ReturnType<typeof updateOrganization<O>>;
deleteOrganization: ReturnType<typeof deleteOrganization<O>>;
setActiveOrganization: ReturnType<typeof setActiveOrganization<O>>;
getFullOrganization: ReturnType<typeof getFullOrganization<O>>;
listOrganizations: ReturnType<typeof listOrganizations<O>>;
createInvitation: ReturnType<typeof createInvitation<O>>;
cancelInvitation: ReturnType<typeof cancelInvitation<O>>;
acceptInvitation: ReturnType<typeof acceptInvitation<O>>;
getInvitation: ReturnType<typeof getInvitation<O>>;
rejectInvitation: ReturnType<typeof rejectInvitation<O>>;
listInvitations: ReturnType<typeof listInvitations<O>>;
getActiveMember: ReturnType<typeof getActiveMember<O>>;
checkOrganizationSlug: ReturnType<typeof checkOrganizationSlug<O>>;
addMember: ReturnType<typeof addMember<O>>;
removeMember: ReturnType<typeof removeMember<O>>;
updateMemberRole: ReturnType<typeof updateMemberRole<O>>;
leaveOrganization: ReturnType<typeof leaveOrganization<O>>;
listUserInvitations: ReturnType<typeof listUserInvitations<O>>;
listMembers: ReturnType<typeof listMembers<O>>;
getActiveMemberRole: ReturnType<typeof getActiveMemberRole<O>>;
hasPermission: ReturnType<typeof createHasPermission<O>>;
};
const createHasPermission = <O extends OrganizationOptions>(options: O) => {
type DefaultStatements = typeof defaultStatements;
type Statements = O["ac"] extends AccessControl<infer S>
? S
: DefaultStatements;
type PermissionType = {
[key in keyof Statements]?: Array<
Statements[key] extends readonly unknown[]
? Statements[key][number]
: never
>;
};
type PermissionExclusive =
| {
/**
* @deprecated Use `permissions` instead
*/
permission: PermissionType;
permissions?: never;
}
| {
permissions: PermissionType;
permission?: never;
};
return createAuthEndpoint(
"/organization/has-permission",
{
method: "POST",
requireHeaders: true,
body: z
.object({
organizationId: z.string().optional(),
})
.and(
z.union([
z.object({
permission: z.record(z.string(), z.array(z.string())),
permissions: z.undefined(),
}),
z.object({
permission: z.undefined(),
permissions: z.record(z.string(), z.array(z.string())),
}),
]),
),
use: [orgSessionMiddleware],
metadata: {
$Infer: {
body: {} as PermissionExclusive & {
organizationId?: string;
},
},
openapi: {
description: "Check if the user has permission",
requestBody: {
content: {
"application/json": {
schema: {
type: "object",
properties: {
permission: {
type: "object",
description: "The permission to check",
deprecated: true,
},
permissions: {
type: "object",
description: "The permission to check",
},
},
required: ["permissions"],
},
},
},
},
responses: {
"200": {
description: "Success",
content: {
"application/json": {
schema: {
type: "object",
properties: {
error: {
type: "string",
},
success: {
type: "boolean",
},
},
required: ["success"],
},
},
},
},
},
},
},
},
async (ctx) => {
const activeOrganizationId =
ctx.body.organizationId ||
ctx.context.session.session.activeOrganizationId;
if (!activeOrganizationId) {
throw new APIError("BAD_REQUEST", {
message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION,
});
}
const adapter = getOrgAdapter<O>(ctx.context, options);
const member = await adapter.findMemberByOrgId({
userId: ctx.context.session.user.id,
organizationId: activeOrganizationId,
});
if (!member) {
throw new APIError("UNAUTHORIZED", {
message:
ORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION,
});
}
const result = await hasPermission(
{
role: member.role,
options: options || {},
permissions: (ctx.body.permissions ?? ctx.body.permission) as any,
organizationId: activeOrganizationId,
},
ctx,
);
return ctx.json({
error: null,
success: result,
});
},
);
};
export type OrganizationPlugin<O extends OrganizationOptions> = {
id: "organization";
endpoints: OrganizationEndpoints<O> &
(O extends { teams: { enabled: true } } ? TeamEndpoints<O> : {}) &
(O extends { dynamicAccessControl: { enabled: true } }
? DynamicAccessControlEndpoints<O>
: {});
schema: OrganizationSchema<O>;
$Infer: {
Organization: InferOrganization<O>;
Invitation: InferInvitation<O>;
Member: InferMember<O>;
Team: O["teams"] extends { enabled: true } ? Team : any;
TeamMember: O["teams"] extends { enabled: true } ? TeamMember : any;
ActiveOrganization: Awaited<
ReturnType<ReturnType<typeof getFullOrganization<O>>>
>;
};
$ERROR_CODES: typeof ORGANIZATION_ERROR_CODES;
options: O;
};
/**
* Organization plugin for Better Auth. Organization allows you to create teams, members,
* and manage access control for your users.
@@ -77,15 +283,106 @@ export function parseRoles(roles: string | string[]): string {
* @example
* ```ts
* const auth = betterAuth({
* plugins: [
* organization({
* allowUserToCreateOrganization: true,
* }),
* ],
* plugins: [
* organization({
* allowUserToCreateOrganization: true,
* }),
* ],
* });
* ```
*/
export const organization = <O extends OrganizationOptions>(options?: O) => {
export function organization<
O extends OrganizationOptions & {
teams: { enabled: true };
},
>(
options?: O,
): {
id: "organization";
endpoints: OrganizationEndpoints<O> & TeamEndpoints<O>;
schema: OrganizationSchema<O>;
$Infer: {
Organization: InferOrganization<O>;
Invitation: InferInvitation<O>;
Member: InferMember<O>;
Team: O["teams"] extends { enabled: true } ? Team : any;
TeamMember: O["teams"] extends { enabled: true } ? TeamMember : any;
ActiveOrganization: Awaited<
ReturnType<ReturnType<typeof getFullOrganization<O>>>
>;
};
$ERROR_CODES: typeof ORGANIZATION_ERROR_CODES;
options: O;
};
export function organization<
O extends OrganizationOptions & {
teams: { enabled: true };
dynamicAccessControl: { enabled: true };
},
>(
options?: O,
): {
id: "organization";
endpoints: OrganizationEndpoints<O> &
TeamEndpoints<O> &
DynamicAccessControlEndpoints<O>;
schema: OrganizationSchema<O>;
$Infer: {
Organization: InferOrganization<O>;
Invitation: InferInvitation<O>;
Member: InferMember<O>;
Team: O["teams"] extends { enabled: true } ? Team : any;
TeamMember: O["teams"] extends { enabled: true } ? TeamMember : any;
ActiveOrganization: Awaited<
ReturnType<ReturnType<typeof getFullOrganization<O>>>
>;
};
$ERROR_CODES: typeof ORGANIZATION_ERROR_CODES;
options: O;
};
export function organization<
O extends OrganizationOptions & {
dynamicAccessControl: { enabled: true };
},
>(
options?: O,
): {
id: "organization";
endpoints: OrganizationEndpoints<O> & DynamicAccessControlEndpoints<O>;
schema: OrganizationSchema<O>;
$Infer: {
Organization: InferOrganization<O>;
Invitation: InferInvitation<O>;
Member: InferMember<O>;
Team: O["teams"] extends { enabled: true } ? Team : any;
TeamMember: O["teams"] extends { enabled: true } ? TeamMember : any;
ActiveOrganization: Awaited<
ReturnType<ReturnType<typeof getFullOrganization<O>>>
>;
};
$ERROR_CODES: typeof ORGANIZATION_ERROR_CODES;
options: O;
};
export function organization<O extends OrganizationOptions>(
options?: O,
): {
id: "organization";
endpoints: OrganizationEndpoints<O>;
schema: OrganizationSchema<O>;
$Infer: {
Organization: InferOrganization<O>;
Invitation: InferInvitation<O>;
Member: InferMember<O>;
Team: O["teams"] extends { enabled: true } ? Team : any;
TeamMember: O["teams"] extends { enabled: true } ? TeamMember : any;
ActiveOrganization: Awaited<
ReturnType<ReturnType<typeof getFullOrganization<O>>>
>;
};
$ERROR_CODES: typeof ORGANIZATION_ERROR_CODES;
options: O;
};
export function organization<O extends OrganizationOptions>(options?: O): any {
let endpoints = {
/**
* ### Endpoint
@@ -857,153 +1154,11 @@ export const organization = <O extends OrganizationOptions>(options?: O) => {
},
});
type DefaultStatements = typeof defaultStatements;
type Statements = O["ac"] extends AccessControl<infer S>
? S
: DefaultStatements;
type PermissionType = {
[key in keyof Statements]?: Array<
Statements[key] extends readonly unknown[]
? Statements[key][number]
: never
>;
};
type PermissionExclusive =
| {
/**
* @deprecated Use `permissions` instead
*/
permission: PermissionType;
permissions?: never;
}
| {
permissions: PermissionType;
permission?: never;
};
type OrganizationEndpoints = (O["dynamicAccessControl"] extends {
enabled: true;
}
? typeof dynamicAccessControlEndpoints
: {}) &
(O["teams"] extends { enabled: true } ? typeof teamEndpoints : {}) &
typeof endpoints;
return {
id: "organization",
endpoints: {
...(api as OrganizationEndpoints),
hasPermission: createAuthEndpoint(
"/organization/has-permission",
{
method: "POST",
requireHeaders: true,
body: z
.object({
organizationId: z.string().optional(),
})
.and(
z.union([
z.object({
permission: z.record(z.string(), z.array(z.string())),
permissions: z.undefined(),
}),
z.object({
permission: z.undefined(),
permissions: z.record(z.string(), z.array(z.string())),
}),
]),
),
use: [orgSessionMiddleware],
metadata: {
$Infer: {
body: {} as PermissionExclusive & {
organizationId?: string;
},
},
openapi: {
description: "Check if the user has permission",
requestBody: {
content: {
"application/json": {
schema: {
type: "object",
properties: {
permission: {
type: "object",
description: "The permission to check",
deprecated: true,
},
permissions: {
type: "object",
description: "The permission to check",
},
},
required: ["permissions"],
},
},
},
},
responses: {
"200": {
description: "Success",
content: {
"application/json": {
schema: {
type: "object",
properties: {
error: {
type: "string",
},
success: {
type: "boolean",
},
},
required: ["success"],
},
},
},
},
},
},
},
},
async (ctx) => {
const activeOrganizationId =
ctx.body.organizationId ||
ctx.context.session.session.activeOrganizationId;
if (!activeOrganizationId) {
throw new APIError("BAD_REQUEST", {
message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION,
});
}
const adapter = getOrgAdapter<O>(ctx.context, options);
const member = await adapter.findMemberByOrgId({
userId: ctx.context.session.user.id,
organizationId: activeOrganizationId,
});
if (!member) {
throw new APIError("UNAUTHORIZED", {
message:
ORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION,
});
}
const result = await hasPermission(
{
role: member.role,
options: options || {},
permissions: (ctx.body.permissions ?? ctx.body.permission) as any,
organizationId: activeOrganizationId,
},
ctx,
);
return ctx.json({
error: null,
success: result,
});
},
),
...(api as unknown as OrganizationEndpoints<O>),
hasPermission: createHasPermission(options as O),
},
schema: {
...(schema as BetterAuthPluginDBSchema),
@@ -1057,4 +1212,4 @@ export const organization = <O extends OrganizationOptions>(options?: O) => {
$ERROR_CODES: ORGANIZATION_ERROR_CODES,
options: options as O,
} satisfies BetterAuthPlugin;
};
}
@@ -4,6 +4,516 @@ import type { OrganizationOptions } from "./types";
import type { InferAdditionalFieldsFromPluginOptions } from "../../db";
import type { Prettify } from "better-call";
// TODO: we need a better way to define plugin schema and endpoints
export type OrganizationSchema<O extends OrganizationOptions> =
O["dynamicAccessControl"] extends { enabled: true }
? {
organizationRole: {
modelName: O["schema"] extends {
organizationRole: { modelName: infer M };
}
? M extends string
? M
: string | undefined
: string | undefined;
fields: {
organizationId: {
type: "string";
required: true;
references: {
model: "organization";
field: "id";
};
fieldName?: O["schema"] extends {
organizationRole: {
fields: { organizationId: infer F };
};
}
? F extends string
? F
: string | undefined
: string | undefined;
};
role: {
type: "string";
required: true;
fieldName?: O["schema"] extends {
organizationRole: {
fields: { role: infer F };
};
}
? F extends string
? F
: string | undefined
: string | undefined;
};
permission: {
type: "string";
required: true;
fieldName?: O["schema"] extends {
organizationRole: {
fields: { permission: infer F };
};
}
? F extends string
? F
: string | undefined
: string | undefined;
};
createdAt: {
type: "date";
required: true;
defaultValue: Date;
fieldName?: O["schema"] extends {
organizationRole: {
fields: { createdAt: infer F };
};
}
? F extends string
? F
: string | undefined
: string | undefined;
};
updatedAt: {
type: "date";
required: false;
fieldName?: O["schema"] extends {
organizationRole: {
fields: { updatedAt: infer F };
};
}
? F extends string
? F
: string | undefined
: string | undefined;
};
} & (O["schema"] extends {
organizationRole: { additionalFields: infer F };
}
? F
: {});
};
}
: {} & (O["teams"] extends { enabled: true }
? {
team: {
modelName: O["schema"] extends { team: { modelName: infer M } }
? M extends string
? M
: string | undefined
: string | undefined;
fields: {
name: {
type: "string";
required: true;
fieldName?: O["schema"] extends {
team: { fields: { name: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
organizationId: {
type: "string";
required: true;
references: {
model: "organization";
field: "id";
};
fieldName?: O["schema"] extends {
team: { fields: { organizationId: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
createdAt: {
type: "date";
required: true;
fieldName?: O["schema"] extends {
team: { fields: { createdAt: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
updatedAt: {
type: "date";
required: false;
fieldName?: O["schema"] extends {
team: { fields: { updatedAt: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
} & (O["schema"] extends { team: { additionalFields: infer F } }
? F
: {});
};
teamMember: {
modelName: O["schema"] extends {
teamMember: { modelName: infer M };
}
? M extends string
? M
: string | undefined
: string | undefined;
fields: {
teamId: {
type: "string";
required: true;
references: {
model: "team";
field: "id";
};
fieldName?: O["schema"] extends {
teamMember: { fields: { teamId: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
userId: {
type: "string";
required: true;
references: {
model: "user";
field: "id";
};
fieldName?: O["schema"] extends {
teamMember: { fields: { userId: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
createdAt: {
type: "date";
required: false;
fieldName?: O["schema"] extends {
teamMember: { fields: { createdAt: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
};
};
}
: {}) & {
organization: {
modelName: O["schema"] extends {
organization: { modelName: infer M };
}
? M extends string
? M
: string | undefined
: string | undefined;
fields: {
name: {
type: "string";
required: true;
sortable: true;
fieldName?: O["schema"] extends {
organization: { fields: { name: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
slug: {
type: "string";
required: true;
unique: true;
sortable: true;
fieldName?: O["schema"] extends {
organization: { fields: { slug: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
logo: {
type: "string";
required: false;
fieldName?: O["schema"] extends {
organization: { fields: { logo: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
createdAt: {
type: "date";
required: true;
fieldName?: O["schema"] extends {
organization: { fields: { createdAt: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
updatedAt: {
type: "date";
required: false;
fieldName?: O["schema"] extends {
organization: { fields: { updatedAt: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
};
};
member: {
modelName: O["schema"] extends { member: { modelName: infer M } }
? M extends string
? M
: string | undefined
: string | undefined;
fields: {
organizationId: {
type: "string";
required: true;
references: {
model: "organization";
field: "id";
};
fieldName?: O["schema"] extends {
member: { fields: { organizationId: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
userId: {
type: "string";
required: true;
references: {
model: "user";
field: "id";
};
fieldName?: O["schema"] extends {
member: { fields: { userId: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
role: {
type: "string";
required: true;
fieldName?: O["schema"] extends {
member: { fields: { role: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
defaultValue: "member";
};
createdAt: {
type: "date";
required: true;
fieldName?: O["schema"] extends {
member: { fields: { createdAt: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
} & (O["schema"] extends { member: { additionalFields: infer F } }
? F
: {});
};
invitation: {
modelName: O["schema"] extends {
invitation: { modelName: infer M };
}
? M
: string | undefined;
fields: {
organizationId: {
type: "string";
required: true;
references: {
model: "organization";
field: "id";
};
fieldName?: O["schema"] extends {
invitation: { fields: { organizationId: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
email: {
type: "string";
required: true;
sortable: true;
fieldName?: O["schema"] extends {
invitation: { fields: { email: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
role: {
type: "string";
required: true;
sortable: true;
fieldName?: O["schema"] extends {
invitation: { fields: { role: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
status: {
type: "string";
required: true;
sortable: true;
fieldName?: O["schema"] extends {
invitation: { fields: { status: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
defaultValue: "pending";
};
expiresAt: {
type: "date";
required: false;
fieldName?: O["schema"] extends {
invitation: { fields: { expiresAt: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
createdAt: {
type: "date";
required: true;
fieldName?: O["schema"] extends {
invitation: { fields: { createdAt: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
defaultValue: Date;
};
inviterId: {
type: "string";
required: true;
references: {
model: "user";
field: "id";
};
fieldName?: O["schema"] extends {
invitation: { fields: { inviterId: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
} & (O["schema"] extends {
invitation: { additionalFields: infer F };
}
? F
: {}) &
O extends { teams: { enabled: true } }
? {
teamId: {
type: "string";
required: false;
sortable: true;
fieldName?: O["schema"] extends {
invitation: { fields: { teamId: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
}
: {};
};
session: {
fields: {
activeOrganizationId: {
type: "string";
required: false;
fieldName?: O["schema"] extends {
session: { fields: { activeOrganizationId: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
} & O["teams"] extends { enabled: true }
? {
activeTeamId: {
type: "string";
required: false;
};
activeOrganizationId: {
type: "string";
required: false;
fieldName?: O["schema"] extends {
session: { fields: { activeOrganizationId: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
}
: {
activeOrganizationId: {
type: "string";
required: false;
fieldName?: O["schema"] extends {
session: { fields: { activeOrganizationId: infer F } };
}
? F extends string
? F
: string | undefined
: string | undefined;
};
} & (O["schema"] extends {
session: { additionalFields: infer F };
}
? F extends string
? F
: string | undefined
: {});
};
};
export const role = z.string();
export const invitationStatus = z
.enum(["pending", "accepted", "rejected", "canceled"])
+1 -1
View File
@@ -44,7 +44,7 @@ describe("general types", async (it) => {
twoFactorEnabled: boolean | undefined | null;
}>();
expectTypeOf<typeof auth.$Infer.Session.session>().toEqualTypeOf<{
expectTypeOf<typeof auth.$Infer.Session.session>().toMatchObjectType<{
id: string;
userId: string;
expiresAt: Date;
+1 -2
View File
@@ -4,7 +4,6 @@ import type {
DBFieldType,
DBPrimitive,
BetterAuthDBSchema,
DBPreservedModels,
} from "./type";
import type { BetterAuthPluginDBSchema } from "./plugin";
export type { BetterAuthPluginDBSchema } from "./plugin";
@@ -23,7 +22,7 @@ export type {
DBPrimitive,
BetterAuthDBSchema,
DBPreservedModels,
};
} from "./type";
/**
* @deprecated Backport for 1.3.x, we will remove this in 1.4.x
+2 -2
View File
@@ -6,7 +6,7 @@ import type {
DBFieldAttribute,
DBPreservedModels,
SecondaryStorage,
} from "../db/type";
} from "../db";
import type { Account, RateLimit, Session, User, Verification } from "../db";
import type { Database as BunDatabase } from "bun:sqlite";
import type { DatabaseSync } from "node:sqlite";
@@ -15,7 +15,7 @@ import type { SocialProviderList, SocialProviders } from "../social-providers";
import type { Logger } from "../env";
import type { AuthContext, GenericEndpointContext } from "./context";
import type { AuthMiddleware } from "../api";
import type { BetterAuthPlugin } from "..";
import type { BetterAuthPlugin } from "./plugin";
type KyselyDatabaseType = "postgres" | "mysql" | "sqlite" | "mssql";
type OmitId<T extends { id: unknown }> = Omit<T, "id">;
+14 -6
View File
@@ -352,7 +352,7 @@ importers:
version: 12.23.12(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
geist:
specifier: ^1.4.2
version: 1.4.2(next@16.0.0-beta.0(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0))
version: 1.4.2(next@16.0.0-beta.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0))
input-otp:
specifier: ^1.4.2
version: 1.4.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
@@ -608,7 +608,7 @@ importers:
version: 15.8.3(@oramacloud/client@2.1.4)(@tanstack/react-router@1.131.27(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(algoliasearch@5.36.0)(lucide-react@0.542.0(react@19.2.0))(next@16.0.0-beta.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(tailwindcss@4.1.13)
geist:
specifier: ^1.4.2
version: 1.4.2(next@16.0.0-beta.0(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0))
version: 1.4.2(next@16.0.0-beta.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0))
gray-matter:
specifier: ^4.0.3
version: 4.0.3
@@ -839,12 +839,18 @@ importers:
e2e/smoke/test/fixtures/tsconfig-decelration:
dependencies:
'@better-auth/sso':
specifier: workspace:*
version: link:../../../../../packages/sso
'@better-auth/stripe':
specifier: workspace:*
version: link:../../../../../packages/stripe
better-auth:
specifier: workspace:*
version: link:../../../../../packages/better-auth
better-sqlite3:
specifier: ^12.4.1
version: 12.4.1
stripe:
specifier: ^18.5.0
version: 18.5.0(@types/node@24.7.2)
e2e/smoke/test/fixtures/tsconfig-exact-optional-property-types:
dependencies:
@@ -17523,7 +17529,9 @@ snapshots:
metro-runtime: 0.83.3
transitivePeerDependencies:
- '@babel/core'
- bufferutil
- supports-color
- utf-8-validate
optional: true
'@react-native/normalize-colors@0.74.89': {}
@@ -21641,7 +21649,7 @@ snapshots:
function-bind@1.1.2: {}
geist@1.4.2(next@16.0.0-beta.0(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0)):
geist@1.4.2(next@16.0.0-beta.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0)):
dependencies:
next: 16.0.0-beta.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0)