@better-auth/core 1.4.17 → 1.5.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +259 -178
- package/LICENSE.md +15 -12
- package/dist/api/index.d.mts +27 -28
- package/dist/api/index.mjs +2 -1
- package/dist/api/index.mjs.map +1 -0
- package/dist/async_hooks/index.d.mts +2 -1
- package/dist/async_hooks/index.mjs +2 -1
- package/dist/async_hooks/index.mjs.map +1 -0
- package/dist/async_hooks/pure.index.d.mts +2 -1
- package/dist/async_hooks/pure.index.mjs +2 -1
- package/dist/async_hooks/pure.index.mjs.map +1 -0
- package/dist/context/endpoint-context.d.mts +3 -2
- package/dist/context/endpoint-context.mjs +4 -3
- package/dist/context/endpoint-context.mjs.map +1 -0
- package/dist/context/global.d.mts +2 -2
- package/dist/context/global.mjs +3 -2
- package/dist/context/global.mjs.map +1 -0
- package/dist/context/index.d.mts +2 -2
- package/dist/context/index.mjs +2 -2
- package/dist/context/request-state.d.mts +2 -1
- package/dist/context/request-state.mjs +4 -3
- package/dist/context/request-state.mjs.map +1 -0
- package/dist/context/transaction.d.mts +12 -3
- package/dist/context/transaction.mjs +55 -11
- package/dist/context/transaction.mjs.map +1 -0
- package/dist/db/adapter/factory.d.mts +3 -10
- package/dist/db/adapter/factory.mjs +27 -54
- package/dist/db/adapter/factory.mjs.map +1 -0
- package/dist/db/adapter/get-default-field-name.d.mts +2 -1
- package/dist/db/adapter/get-default-field-name.mjs +3 -2
- package/dist/db/adapter/get-default-field-name.mjs.map +1 -0
- package/dist/db/adapter/get-default-model-name.d.mts +2 -1
- package/dist/db/adapter/get-default-model-name.mjs +5 -4
- package/dist/db/adapter/get-default-model-name.mjs.map +1 -0
- package/dist/db/adapter/get-field-attributes.d.mts +3 -2
- package/dist/db/adapter/get-field-attributes.mjs +2 -1
- package/dist/db/adapter/get-field-attributes.mjs.map +1 -0
- package/dist/db/adapter/get-field-name.d.mts +2 -1
- package/dist/db/adapter/get-field-name.mjs +2 -1
- package/dist/db/adapter/get-field-name.mjs.map +1 -0
- package/dist/db/adapter/get-id-field.d.mts +3 -2
- package/dist/db/adapter/get-id-field.mjs +3 -3
- package/dist/db/adapter/get-id-field.mjs.map +1 -0
- package/dist/db/adapter/get-model-name.d.mts +2 -1
- package/dist/db/adapter/get-model-name.mjs +2 -1
- package/dist/db/adapter/get-model-name.mjs.map +1 -0
- package/dist/db/adapter/index.d.mts +4 -3
- package/dist/db/adapter/index.mjs +2 -2
- package/dist/db/adapter/types.d.mts +4 -35
- package/dist/db/adapter/utils.d.mts +2 -1
- package/dist/db/adapter/utils.mjs +2 -1
- package/dist/db/adapter/utils.mjs.map +1 -0
- package/dist/db/get-tables.d.mts +2 -1
- package/dist/db/get-tables.mjs +46 -39
- package/dist/db/get-tables.mjs.map +1 -0
- package/dist/db/plugin.d.mts +2 -1
- package/dist/db/schema/account.d.mts +2 -1
- package/dist/db/schema/account.mjs +2 -1
- package/dist/db/schema/account.mjs.map +1 -0
- package/dist/db/schema/rate-limit.d.mts +2 -1
- package/dist/db/schema/rate-limit.mjs +2 -1
- package/dist/db/schema/rate-limit.mjs.map +1 -0
- package/dist/db/schema/session.d.mts +2 -1
- package/dist/db/schema/session.mjs +2 -1
- package/dist/db/schema/session.mjs.map +1 -0
- package/dist/db/schema/shared.d.mts +2 -1
- package/dist/db/schema/shared.mjs +2 -1
- package/dist/db/schema/shared.mjs.map +1 -0
- package/dist/db/schema/user.d.mts +2 -1
- package/dist/db/schema/user.mjs +2 -1
- package/dist/db/schema/user.mjs.map +1 -0
- package/dist/db/schema/verification.d.mts +2 -1
- package/dist/db/schema/verification.mjs +2 -1
- package/dist/db/schema/verification.mjs.map +1 -0
- package/dist/db/type.d.mts +5 -1
- package/dist/env/color-depth.d.mts +2 -1
- package/dist/env/color-depth.mjs +2 -1
- package/dist/env/color-depth.mjs.map +1 -0
- package/dist/env/env-impl.d.mts +3 -2
- package/dist/env/env-impl.mjs +9 -8
- package/dist/env/env-impl.mjs.map +1 -0
- package/dist/env/logger.d.mts +2 -1
- package/dist/env/logger.mjs +3 -2
- package/dist/env/logger.mjs.map +1 -0
- package/dist/error/codes.d.mts +65 -44
- package/dist/error/codes.mjs +8 -3
- package/dist/error/codes.mjs.map +1 -0
- package/dist/error/index.d.mts +12 -2
- package/dist/error/index.mjs +17 -1
- package/dist/error/index.mjs.map +1 -0
- package/dist/index.d.mts +5 -5
- package/dist/oauth2/client-credentials-token.d.mts +2 -1
- package/dist/oauth2/client-credentials-token.mjs +2 -1
- package/dist/oauth2/client-credentials-token.mjs.map +1 -0
- package/dist/oauth2/create-authorization-url.d.mts +2 -1
- package/dist/oauth2/create-authorization-url.mjs +2 -1
- package/dist/oauth2/create-authorization-url.mjs.map +1 -0
- package/dist/oauth2/oauth-provider.d.mts +3 -2
- package/dist/oauth2/refresh-access-token.d.mts +2 -3
- package/dist/oauth2/refresh-access-token.mjs +2 -1
- package/dist/oauth2/refresh-access-token.mjs.map +1 -0
- package/dist/oauth2/utils.d.mts +2 -1
- package/dist/oauth2/utils.mjs +2 -1
- package/dist/oauth2/utils.mjs.map +1 -0
- package/dist/oauth2/validate-authorization-code.d.mts +2 -1
- package/dist/oauth2/validate-authorization-code.mjs +6 -5
- package/dist/oauth2/validate-authorization-code.mjs.map +1 -0
- package/dist/oauth2/verify.d.mts +7 -13
- package/dist/oauth2/verify.mjs +3 -2
- package/dist/oauth2/verify.mjs.map +1 -0
- package/dist/social-providers/apple.d.mts +2 -1
- package/dist/social-providers/apple.mjs +6 -3
- package/dist/social-providers/apple.mjs.map +1 -0
- package/dist/social-providers/atlassian.d.mts +2 -1
- package/dist/social-providers/atlassian.mjs +2 -1
- package/dist/social-providers/atlassian.mjs.map +1 -0
- package/dist/social-providers/cognito.d.mts +2 -1
- package/dist/social-providers/cognito.mjs +3 -3
- package/dist/social-providers/cognito.mjs.map +1 -0
- package/dist/social-providers/discord.d.mts +2 -1
- package/dist/social-providers/discord.mjs +2 -1
- package/dist/social-providers/discord.mjs.map +1 -0
- package/dist/social-providers/dropbox.d.mts +2 -1
- package/dist/social-providers/dropbox.mjs +3 -2
- package/dist/social-providers/dropbox.mjs.map +1 -0
- package/dist/social-providers/facebook.d.mts +2 -1
- package/dist/social-providers/facebook.mjs +13 -12
- package/dist/social-providers/facebook.mjs.map +1 -0
- package/dist/social-providers/figma.d.mts +2 -1
- package/dist/social-providers/figma.mjs +2 -1
- package/dist/social-providers/figma.mjs.map +1 -0
- package/dist/social-providers/github.d.mts +3 -2
- package/dist/social-providers/github.mjs +22 -5
- package/dist/social-providers/github.mjs.map +1 -0
- package/dist/social-providers/gitlab.d.mts +2 -1
- package/dist/social-providers/gitlab.mjs +2 -1
- package/dist/social-providers/gitlab.mjs.map +1 -0
- package/dist/social-providers/google.d.mts +2 -1
- package/dist/social-providers/google.mjs +5 -5
- package/dist/social-providers/google.mjs.map +1 -0
- package/dist/social-providers/huggingface.d.mts +2 -1
- package/dist/social-providers/huggingface.mjs +2 -1
- package/dist/social-providers/huggingface.mjs.map +1 -0
- package/dist/social-providers/index.d.mts +3 -2
- package/dist/social-providers/index.mjs +2 -1
- package/dist/social-providers/index.mjs.map +1 -0
- package/dist/social-providers/kakao.d.mts +2 -1
- package/dist/social-providers/kakao.mjs +2 -1
- package/dist/social-providers/kakao.mjs.map +1 -0
- package/dist/social-providers/kick.d.mts +2 -1
- package/dist/social-providers/kick.mjs +2 -1
- package/dist/social-providers/kick.mjs.map +1 -0
- package/dist/social-providers/line.d.mts +2 -1
- package/dist/social-providers/line.mjs +2 -1
- package/dist/social-providers/line.mjs.map +1 -0
- package/dist/social-providers/linear.d.mts +2 -1
- package/dist/social-providers/linear.mjs +2 -1
- package/dist/social-providers/linear.mjs.map +1 -0
- package/dist/social-providers/linkedin.d.mts +2 -1
- package/dist/social-providers/linkedin.mjs +2 -1
- package/dist/social-providers/linkedin.mjs.map +1 -0
- package/dist/social-providers/microsoft-entra-id.d.mts +2 -1
- package/dist/social-providers/microsoft-entra-id.mjs +2 -1
- package/dist/social-providers/microsoft-entra-id.mjs.map +1 -0
- package/dist/social-providers/naver.d.mts +11 -20
- package/dist/social-providers/naver.mjs +2 -1
- package/dist/social-providers/naver.mjs.map +1 -0
- package/dist/social-providers/notion.d.mts +2 -1
- package/dist/social-providers/notion.mjs +2 -1
- package/dist/social-providers/notion.mjs.map +1 -0
- package/dist/social-providers/paybin.d.mts +2 -1
- package/dist/social-providers/paybin.mjs +2 -1
- package/dist/social-providers/paybin.mjs.map +1 -0
- package/dist/social-providers/paypal.d.mts +2 -1
- package/dist/social-providers/paypal.mjs +2 -1
- package/dist/social-providers/paypal.mjs.map +1 -0
- package/dist/social-providers/polar.d.mts +2 -1
- package/dist/social-providers/polar.mjs +2 -1
- package/dist/social-providers/polar.mjs.map +1 -0
- package/dist/social-providers/reddit.d.mts +2 -1
- package/dist/social-providers/reddit.mjs +2 -1
- package/dist/social-providers/reddit.mjs.map +1 -0
- package/dist/social-providers/roblox.d.mts +2 -1
- package/dist/social-providers/roblox.mjs +2 -1
- package/dist/social-providers/roblox.mjs.map +1 -0
- package/dist/social-providers/salesforce.d.mts +2 -1
- package/dist/social-providers/salesforce.mjs +2 -1
- package/dist/social-providers/salesforce.mjs.map +1 -0
- package/dist/social-providers/slack.d.mts +2 -1
- package/dist/social-providers/slack.mjs +2 -1
- package/dist/social-providers/slack.mjs.map +1 -0
- package/dist/social-providers/spotify.d.mts +2 -1
- package/dist/social-providers/spotify.mjs +2 -1
- package/dist/social-providers/spotify.mjs.map +1 -0
- package/dist/social-providers/tiktok.d.mts +3 -3
- package/dist/social-providers/tiktok.mjs +2 -1
- package/dist/social-providers/tiktok.mjs.map +1 -0
- package/dist/social-providers/twitch.d.mts +2 -1
- package/dist/social-providers/twitch.mjs +2 -1
- package/dist/social-providers/twitch.mjs.map +1 -0
- package/dist/social-providers/twitter.d.mts +14 -25
- package/dist/social-providers/twitter.mjs +2 -1
- package/dist/social-providers/twitter.mjs.map +1 -0
- package/dist/social-providers/vercel.d.mts +2 -1
- package/dist/social-providers/vercel.mjs +2 -1
- package/dist/social-providers/vercel.mjs.map +1 -0
- package/dist/social-providers/vk.d.mts +2 -1
- package/dist/social-providers/vk.mjs +2 -1
- package/dist/social-providers/vk.mjs.map +1 -0
- package/dist/social-providers/zoom.d.mts +3 -10
- package/dist/social-providers/zoom.mjs +2 -1
- package/dist/social-providers/zoom.mjs.map +1 -0
- package/dist/types/context.d.mts +64 -11
- package/dist/types/cookie.d.mts +2 -1
- package/dist/types/helper.d.mts +3 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/types/init-options.d.mts +79 -86
- package/dist/types/plugin-client.d.mts +10 -1
- package/dist/types/plugin.d.mts +12 -8
- package/dist/utils/db.d.mts +12 -0
- package/dist/utils/db.mjs +17 -0
- package/dist/utils/db.mjs.map +1 -0
- package/dist/utils/deprecate.d.mts +2 -2
- package/dist/utils/deprecate.mjs +2 -1
- package/dist/utils/deprecate.mjs.map +1 -0
- package/dist/utils/error-codes.d.mts +8 -3
- package/dist/utils/error-codes.mjs +7 -2
- package/dist/utils/error-codes.mjs.map +1 -0
- package/dist/utils/id.d.mts +2 -1
- package/dist/utils/id.mjs +2 -1
- package/dist/utils/id.mjs.map +1 -0
- package/dist/utils/ip.d.mts +2 -1
- package/dist/utils/ip.mjs +2 -1
- package/dist/utils/ip.mjs.map +1 -0
- package/dist/utils/json.d.mts +2 -1
- package/dist/utils/json.mjs +2 -1
- package/dist/utils/json.mjs.map +1 -0
- package/dist/utils/string.d.mts +2 -1
- package/dist/utils/string.mjs +2 -1
- package/dist/utils/string.mjs.map +1 -0
- package/dist/utils/url.d.mts +2 -1
- package/dist/utils/url.mjs +2 -1
- package/dist/utils/url.mjs.map +1 -0
- package/package.json +16 -13
- package/src/context/index.ts +1 -0
- package/src/context/transaction.ts +72 -9
- package/src/db/adapter/factory.ts +4 -54
- package/src/db/adapter/get-id-field.ts +2 -4
- package/src/db/adapter/types.ts +2 -41
- package/src/db/get-tables.ts +48 -37
- package/src/db/test/get-tables.test.ts +33 -0
- package/src/env/env-impl.ts +2 -2
- package/src/env/logger.ts +1 -1
- package/src/error/codes.ts +22 -1
- package/src/error/index.ts +26 -1
- package/src/oauth2/oauth-provider.ts +1 -1
- package/src/oauth2/refresh-access-token.ts +0 -2
- package/src/oauth2/validate-authorization-code.ts +7 -12
- package/src/oauth2/validate-token.test.ts +174 -0
- package/src/social-providers/apple.ts +14 -4
- package/src/social-providers/cognito.ts +1 -2
- package/src/social-providers/dropbox.ts +1 -1
- package/src/social-providers/facebook.ts +3 -3
- package/src/social-providers/github.ts +25 -3
- package/src/social-providers/google.ts +3 -4
- package/src/social-providers/zoom.ts +0 -8
- package/src/types/context.ts +94 -8
- package/src/types/helper.ts +8 -0
- package/src/types/index.ts +14 -2
- package/src/types/init-options.ts +83 -86
- package/src/types/plugin-client.ts +10 -0
- package/src/types/plugin.ts +9 -5
- package/src/utils/db.ts +20 -0
- package/src/utils/error-codes.ts +21 -4
- package/tsdown.config.ts +5 -1
- package/dist/utils/index.d.mts +0 -8
- package/dist/utils/index.mjs +0 -9
- package/src/utils/index.ts +0 -7
package/dist/utils/deprecate.mjs
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deprecate.mjs","names":[],"sources":["../../src/utils/deprecate.ts"],"sourcesContent":["import type { InternalLogger } from \"../env\";\n\n/**\n * Wraps a function to log a deprecation warning at once.\n */\nexport function deprecate<T extends (...args: any[]) => any>(\n\tfn: T,\n\tmessage: string,\n\tlogger?: InternalLogger,\n): T {\n\tlet warned = false;\n\n\treturn function (this: any, ...args: Parameters<T>): ReturnType<T> {\n\t\tif (!warned) {\n\t\t\tconst warn = logger?.warn ?? console.warn;\n\t\t\twarn(`[Deprecation] ${message}`);\n\t\t\twarned = true;\n\t\t}\n\t\treturn fn.apply(this, args);\n\t} as T;\n}\n"],"mappings":";;;;AAKA,SAAgB,UACf,IACA,SACA,QACI;CACJ,IAAI,SAAS;AAEb,QAAO,SAAqB,GAAG,MAAoC;AAClE,MAAI,CAAC,QAAQ;AAEZ,IADa,QAAQ,QAAQ,QAAQ,MAChC,iBAAiB,UAAU;AAChC,YAAS;;AAEV,SAAO,GAAG,MAAM,MAAM,KAAK"}
|
|
@@ -2,8 +2,13 @@
|
|
|
2
2
|
type UpperLetter = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z";
|
|
3
3
|
type SpecialCharacter = "_";
|
|
4
4
|
type IsValidUpperSnakeCase<S extends string> = S extends `${infer F}${infer R}` ? F extends UpperLetter | SpecialCharacter ? IsValidUpperSnakeCase<R> : false : true;
|
|
5
|
-
type InvalidKeyError<K
|
|
5
|
+
type InvalidKeyError<K extends string> = `Invalid error code key: "${K}" - must only contain uppercase letters (A-Z) and underscores (_)`;
|
|
6
6
|
type ValidateErrorCodes<T> = { [K in keyof T]: K extends string ? IsValidUpperSnakeCase<K> extends false ? InvalidKeyError<K> : T[K] : T[K] };
|
|
7
|
-
|
|
7
|
+
type RawError<K extends string = string> = {
|
|
8
|
+
readonly code: K;
|
|
9
|
+
message: string;
|
|
10
|
+
};
|
|
11
|
+
declare function defineErrorCodes<const T extends Record<string, string>, R extends { [K in keyof T & string]: RawError<K> }>(codes: ValidateErrorCodes<T>): R;
|
|
8
12
|
//#endregion
|
|
9
|
-
export { defineErrorCodes };
|
|
13
|
+
export { RawError, defineErrorCodes };
|
|
14
|
+
//# sourceMappingURL=error-codes.d.mts.map
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
//#region src/utils/error-codes.ts
|
|
2
2
|
function defineErrorCodes(codes) {
|
|
3
|
-
return codes
|
|
3
|
+
return Object.fromEntries(Object.entries(codes).map(([key, value]) => [key, {
|
|
4
|
+
code: key,
|
|
5
|
+
message: value,
|
|
6
|
+
toString: () => key
|
|
7
|
+
}]));
|
|
4
8
|
}
|
|
5
9
|
|
|
6
10
|
//#endregion
|
|
7
|
-
export { defineErrorCodes };
|
|
11
|
+
export { defineErrorCodes };
|
|
12
|
+
//# sourceMappingURL=error-codes.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-codes.mjs","names":[],"sources":["../../src/utils/error-codes.ts"],"sourcesContent":["type UpperLetter =\n\t| \"A\"\n\t| \"B\"\n\t| \"C\"\n\t| \"D\"\n\t| \"E\"\n\t| \"F\"\n\t| \"G\"\n\t| \"H\"\n\t| \"I\"\n\t| \"J\"\n\t| \"K\"\n\t| \"L\"\n\t| \"M\"\n\t| \"N\"\n\t| \"O\"\n\t| \"P\"\n\t| \"Q\"\n\t| \"R\"\n\t| \"S\"\n\t| \"T\"\n\t| \"U\"\n\t| \"V\"\n\t| \"W\"\n\t| \"X\"\n\t| \"Y\"\n\t| \"Z\";\ntype SpecialCharacter = \"_\";\n\ntype IsValidUpperSnakeCase<S extends string> = S extends `${infer F}${infer R}`\n\t? F extends UpperLetter | SpecialCharacter\n\t\t? IsValidUpperSnakeCase<R>\n\t\t: false\n\t: true;\n\ntype InvalidKeyError<K extends string> =\n\t`Invalid error code key: \"${K}\" - must only contain uppercase letters (A-Z) and underscores (_)`;\n\ntype ValidateErrorCodes<T> = {\n\t[K in keyof T]: K extends string\n\t\t? IsValidUpperSnakeCase<K> extends false\n\t\t\t? InvalidKeyError<K>\n\t\t\t: T[K]\n\t\t: T[K];\n};\n\nexport type RawError<K extends string = string> = {\n\treadonly code: K;\n\tmessage: string;\n};\n\nexport function defineErrorCodes<\n\tconst T extends Record<string, string>,\n\tR extends {\n\t\t[K in keyof T & string]: RawError<K>;\n\t},\n>(codes: ValidateErrorCodes<T>): R {\n\treturn Object.fromEntries(\n\t\tObject.entries(codes).map(([key, value]) => [\n\t\t\tkey,\n\t\t\t{\n\t\t\t\tcode: key,\n\t\t\t\tmessage: value,\n\t\t\t\ttoString: () => key,\n\t\t\t},\n\t\t]),\n\t) as any;\n}\n"],"mappings":";AAmDA,SAAgB,iBAKd,OAAiC;AAClC,QAAO,OAAO,YACb,OAAO,QAAQ,MAAM,CAAC,KAAK,CAAC,KAAK,WAAW,CAC3C,KACA;EACC,MAAM;EACN,SAAS;EACT,gBAAgB;EAChB,CACD,CAAC,CACF"}
|
package/dist/utils/id.d.mts
CHANGED
package/dist/utils/id.mjs
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id.mjs","names":[],"sources":["../../src/utils/id.ts"],"sourcesContent":["import { createRandomStringGenerator } from \"@better-auth/utils/random\";\n\nexport const generateId = (size?: number) => {\n\treturn createRandomStringGenerator(\"a-z\", \"A-Z\", \"0-9\")(size || 32);\n};\n"],"mappings":";;;AAEA,MAAa,cAAc,SAAkB;AAC5C,QAAO,4BAA4B,OAAO,OAAO,MAAM,CAAC,QAAQ,GAAG"}
|
package/dist/utils/ip.d.mts
CHANGED
|
@@ -51,4 +51,5 @@ declare function normalizeIP(ip: string, options?: NormalizeIPOptions): string;
|
|
|
51
51
|
*/
|
|
52
52
|
declare function createRateLimitKey(ip: string, path: string): string;
|
|
53
53
|
//#endregion
|
|
54
|
-
export { createRateLimitKey, isValidIP, normalizeIP };
|
|
54
|
+
export { createRateLimitKey, isValidIP, normalizeIP };
|
|
55
|
+
//# sourceMappingURL=ip.d.mts.map
|
package/dist/utils/ip.mjs
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ip.mjs","names":[],"sources":["../../src/utils/ip.ts"],"sourcesContent":["import * as z from \"zod\";\n\n/**\n * Normalizes an IP address for consistent rate limiting.\n *\n * Features:\n * - Normalizes IPv6 to canonical lowercase form\n * - Converts IPv4-mapped IPv6 to IPv4\n * - Supports IPv6 subnet extraction\n * - Handles all edge cases (::1, ::, etc.)\n */\n\ninterface NormalizeIPOptions {\n\t/**\n\t * For IPv6 addresses, extract the subnet prefix instead of full address.\n\t * Common values: 32, 48, 64, 128 (default: 128 = full address)\n\t *\n\t * @default 128\n\t */\n\tipv6Subnet?: 128 | 64 | 48 | 32;\n}\n\n/**\n * Checks if an IP is valid IPv4 or IPv6\n */\nexport function isValidIP(ip: string): boolean {\n\treturn z.ipv4().safeParse(ip).success || z.ipv6().safeParse(ip).success;\n}\n\n/**\n * Checks if an IP is IPv6\n */\nfunction isIPv6(ip: string): boolean {\n\treturn z.ipv6().safeParse(ip).success;\n}\n\n/**\n * Converts IPv4-mapped IPv6 address to IPv4\n * e.g., \"::ffff:192.0.2.1\" -> \"192.0.2.1\"\n */\nfunction extractIPv4FromMapped(ipv6: string): string | null {\n\tconst lower = ipv6.toLowerCase();\n\n\t// Handle ::ffff:192.0.2.1 format\n\tif (lower.startsWith(\"::ffff:\")) {\n\t\tconst ipv4Part = lower.substring(7);\n\t\t// Check if it's a valid IPv4\n\t\tif (z.ipv4().safeParse(ipv4Part).success) {\n\t\t\treturn ipv4Part;\n\t\t}\n\t}\n\n\t// Handle full form: 0:0:0:0:0:ffff:192.0.2.1\n\tconst parts = ipv6.split(\":\");\n\tif (parts.length === 7 && parts[5]?.toLowerCase() === \"ffff\") {\n\t\tconst ipv4Part = parts[6];\n\t\tif (ipv4Part && z.ipv4().safeParse(ipv4Part).success) {\n\t\t\treturn ipv4Part;\n\t\t}\n\t}\n\n\t// Handle hex-encoded IPv4 in mapped address\n\t// e.g., ::ffff:c000:0201 -> 192.0.2.1\n\tif (lower.includes(\"::ffff:\") || lower.includes(\":ffff:\")) {\n\t\tconst groups = expandIPv6(ipv6);\n\t\tif (\n\t\t\tgroups.length === 8 &&\n\t\t\tgroups[0] === \"0000\" &&\n\t\t\tgroups[1] === \"0000\" &&\n\t\t\tgroups[2] === \"0000\" &&\n\t\t\tgroups[3] === \"0000\" &&\n\t\t\tgroups[4] === \"0000\" &&\n\t\t\tgroups[5] === \"ffff\" &&\n\t\t\tgroups[6] &&\n\t\t\tgroups[7]\n\t\t) {\n\t\t\t// Convert last two groups to IPv4\n\t\t\tconst byte1 = Number.parseInt(groups[6].substring(0, 2), 16);\n\t\t\tconst byte2 = Number.parseInt(groups[6].substring(2, 4), 16);\n\t\t\tconst byte3 = Number.parseInt(groups[7].substring(0, 2), 16);\n\t\t\tconst byte4 = Number.parseInt(groups[7].substring(2, 4), 16);\n\t\t\treturn `${byte1}.${byte2}.${byte3}.${byte4}`;\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Expands a compressed IPv6 address to full form\n * e.g., \"2001:db8::1\" -> [\"2001\", \"0db8\", \"0000\", \"0000\", \"0000\", \"0000\", \"0000\", \"0001\"]\n */\nfunction expandIPv6(ipv6: string): string[] {\n\t// Handle :: notation (zero compression)\n\tif (ipv6.includes(\"::\")) {\n\t\tconst sides = ipv6.split(\"::\");\n\t\tconst left = sides[0] ? sides[0].split(\":\") : [];\n\t\tconst right = sides[1] ? sides[1].split(\":\") : [];\n\n\t\t// Calculate missing groups\n\t\tconst totalGroups = 8;\n\t\tconst missingGroups = totalGroups - left.length - right.length;\n\t\tconst zeros = Array(missingGroups).fill(\"0000\");\n\n\t\t// Pad existing groups to 4 digits\n\t\tconst paddedLeft = left.map((g) => g.padStart(4, \"0\"));\n\t\tconst paddedRight = right.map((g) => g.padStart(4, \"0\"));\n\n\t\treturn [...paddedLeft, ...zeros, ...paddedRight];\n\t}\n\n\t// No compression, just pad each group\n\treturn ipv6.split(\":\").map((g) => g.padStart(4, \"0\"));\n}\n\n/**\n * Normalizes an IPv6 address to canonical form\n * e.g., \"2001:DB8::1\" -> \"2001:0db8:0000:0000:0000:0000:0000:0001\"\n */\nfunction normalizeIPv6(\n\tipv6: string,\n\tsubnetPrefix?: 128 | 32 | 48 | 64,\n): string {\n\tconst groups = expandIPv6(ipv6);\n\n\tif (subnetPrefix && subnetPrefix < 128) {\n\t\t// Apply subnet mask\n\t\tconst prefix = subnetPrefix;\n\t\tlet bitsRemaining: number = prefix;\n\n\t\tconst maskedGroups = groups.map((group) => {\n\t\t\tif (bitsRemaining <= 0) {\n\t\t\t\treturn \"0000\";\n\t\t\t}\n\t\t\tif (bitsRemaining >= 16) {\n\t\t\t\tbitsRemaining -= 16;\n\t\t\t\treturn group;\n\t\t\t}\n\n\t\t\t// Partial mask for this group\n\t\t\tconst value = Number.parseInt(group, 16);\n\t\t\tconst mask = (0xffff << (16 - bitsRemaining)) & 0xffff;\n\t\t\tconst masked = value & mask;\n\t\t\tbitsRemaining = 0;\n\t\t\treturn masked.toString(16).padStart(4, \"0\");\n\t\t});\n\n\t\treturn maskedGroups.join(\":\").toLowerCase();\n\t}\n\n\treturn groups.join(\":\").toLowerCase();\n}\n\n/**\n * Normalizes an IP address (IPv4 or IPv6) for consistent rate limiting.\n *\n * @param ip - The IP address to normalize\n * @param options - Normalization options\n * @returns Normalized IP address\n *\n * @example\n * normalizeIP(\"2001:DB8::1\")\n * // -> \"2001:0db8:0000:0000:0000:0000:0000:0000\"\n *\n * @example\n * normalizeIP(\"::ffff:192.0.2.1\")\n * // -> \"192.0.2.1\" (converted to IPv4)\n *\n * @example\n * normalizeIP(\"2001:db8::1\", { ipv6Subnet: 64 })\n * // -> \"2001:0db8:0000:0000:0000:0000:0000:0000\" (subnet /64)\n */\nexport function normalizeIP(\n\tip: string,\n\toptions: NormalizeIPOptions = {},\n): string {\n\t// IPv4 addresses are already normalized\n\tif (z.ipv4().safeParse(ip).success) {\n\t\treturn ip.toLowerCase();\n\t}\n\n\t// Check if it's IPv6\n\tif (!isIPv6(ip)) {\n\t\t// Return as-is if not valid (shouldn't happen due to prior validation)\n\t\treturn ip.toLowerCase();\n\t}\n\n\t// Check for IPv4-mapped IPv6\n\tconst ipv4 = extractIPv4FromMapped(ip);\n\tif (ipv4) {\n\t\treturn ipv4.toLowerCase();\n\t}\n\n\t// Normalize IPv6\n\tconst subnetPrefix = options.ipv6Subnet || 64;\n\treturn normalizeIPv6(ip, subnetPrefix);\n}\n\n/**\n * Creates a rate limit key from IP and path\n * Uses a separator to prevent collision attacks\n *\n * @param ip - The IP address (should be normalized)\n * @param path - The request path\n * @returns Rate limit key\n */\nexport function createRateLimitKey(ip: string, path: string): string {\n\t// Use | as separator to prevent collision attacks\n\t// e.g., \"192.0.2.1\" + \"/sign-in\" vs \"192.0.2\" + \".1/sign-in\"\n\treturn `${ip}|${path}`;\n}\n"],"mappings":"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"}
|
package/dist/utils/json.d.mts
CHANGED
package/dist/utils/json.mjs
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.mjs","names":[],"sources":["../../src/utils/json.ts"],"sourcesContent":["import { logger } from \"../env\";\n\nexport function safeJSONParse<T>(data: unknown): T | null {\n\tfunction reviver(_: string, value: any): any {\n\t\tif (typeof value === \"string\") {\n\t\t\tconst iso8601Regex = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?Z$/;\n\t\t\tif (iso8601Regex.test(value)) {\n\t\t\t\tconst date = new Date(value);\n\t\t\t\tif (!isNaN(date.getTime())) {\n\t\t\t\t\treturn date;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn value;\n\t}\n\ttry {\n\t\tif (typeof data !== \"string\") {\n\t\t\treturn data as T;\n\t\t}\n\t\treturn JSON.parse(data, reviver);\n\t} catch (e) {\n\t\tlogger.error(\"Error parsing JSON\", { error: e });\n\t\treturn null;\n\t}\n}\n"],"mappings":";;;;AAEA,SAAgB,cAAiB,MAAyB;CACzD,SAAS,QAAQ,GAAW,OAAiB;AAC5C,MAAI,OAAO,UAAU,UAEpB;OADqB,mDACJ,KAAK,MAAM,EAAE;IAC7B,MAAM,OAAO,IAAI,KAAK,MAAM;AAC5B,QAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CACzB,QAAO;;;AAIV,SAAO;;AAER,KAAI;AACH,MAAI,OAAO,SAAS,SACnB,QAAO;AAER,SAAO,KAAK,MAAM,MAAM,QAAQ;UACxB,GAAG;AACX,SAAO,MAAM,sBAAsB,EAAE,OAAO,GAAG,CAAC;AAChD,SAAO"}
|
package/dist/utils/string.d.mts
CHANGED
package/dist/utils/string.mjs
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"string.mjs","names":[],"sources":["../../src/utils/string.ts"],"sourcesContent":["export function capitalizeFirstLetter(str: string) {\n\treturn str.charAt(0).toUpperCase() + str.slice(1);\n}\n"],"mappings":";AAAA,SAAgB,sBAAsB,KAAa;AAClD,QAAO,IAAI,OAAO,EAAE,CAAC,aAAa,GAAG,IAAI,MAAM,EAAE"}
|
package/dist/utils/url.d.mts
CHANGED
package/dist/utils/url.mjs
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.mjs","names":[],"sources":["../../src/utils/url.ts"],"sourcesContent":["/**\n * Normalizes a request pathname by removing the basePath prefix and trailing slashes.\n * This is useful for matching paths against configured path lists.\n *\n * @param requestUrl - The full request URL\n * @param basePath - The base path of the auth API (e.g., \"/api/auth\")\n * @returns The normalized path without basePath prefix or trailing slashes,\n * or \"/\" if URL parsing fails\n *\n * @example\n * normalizePathname(\"http://localhost:3000/api/auth/sso/saml2/callback/provider1\", \"/api/auth\")\n * // Returns: \"/sso/saml2/callback/provider1\"\n *\n * normalizePathname(\"http://localhost:3000/sso/saml2/callback/provider1/\", \"/\")\n * // Returns: \"/sso/saml2/callback/provider1\"\n */\nexport function normalizePathname(\n\trequestUrl: string,\n\tbasePath: string,\n): string {\n\tlet pathname: string;\n\ttry {\n\t\tpathname = new URL(requestUrl).pathname.replace(/\\/+$/, \"\") || \"/\";\n\t} catch {\n\t\treturn \"/\";\n\t}\n\n\tif (basePath === \"/\" || basePath === \"\") {\n\t\treturn pathname;\n\t}\n\n\t// Check for exact match or proper path boundary (basePath followed by \"/\" or end)\n\t// This prevents \"/api/auth\" from matching \"/api/authevil/...\"\n\tif (pathname === basePath) {\n\t\treturn \"/\";\n\t}\n\n\tif (pathname.startsWith(basePath + \"/\")) {\n\t\treturn pathname.slice(basePath.length).replace(/\\/+$/, \"\") || \"/\";\n\t}\n\n\treturn pathname;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAgBA,SAAgB,kBACf,YACA,UACS;CACT,IAAI;AACJ,KAAI;AACH,aAAW,IAAI,IAAI,WAAW,CAAC,SAAS,QAAQ,QAAQ,GAAG,IAAI;SACxD;AACP,SAAO;;AAGR,KAAI,aAAa,OAAO,aAAa,GACpC,QAAO;AAKR,KAAI,aAAa,SAChB,QAAO;AAGR,KAAI,SAAS,WAAW,WAAW,IAAI,CACtC,QAAO,SAAS,MAAM,SAAS,OAAO,CAAC,QAAQ,QAAQ,GAAG,IAAI;AAG/D,QAAO"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0-beta.10",
|
|
4
4
|
"description": "The most comprehensive authentication framework for TypeScript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
"types": "./dist/error/index.d.mts",
|
|
49
49
|
"default": "./dist/error/index.mjs"
|
|
50
50
|
},
|
|
51
|
-
"./utils": {
|
|
52
|
-
"dev-source": "./src/utils
|
|
53
|
-
"types": "./dist/utils
|
|
54
|
-
"default": "./dist/utils
|
|
51
|
+
"./utils/*": {
|
|
52
|
+
"dev-source": "./src/utils/*.ts",
|
|
53
|
+
"types": "./dist/utils/*.d.mts",
|
|
54
|
+
"default": "./dist/utils/*.mjs"
|
|
55
55
|
},
|
|
56
56
|
"./social-providers": {
|
|
57
57
|
"dev-source": "./src/social-providers/index.ts",
|
|
@@ -97,6 +97,9 @@
|
|
|
97
97
|
"utils": [
|
|
98
98
|
"dist/utils/index.d.mts"
|
|
99
99
|
],
|
|
100
|
+
"utils/*": [
|
|
101
|
+
"dist/utils/*.d.mts"
|
|
102
|
+
],
|
|
100
103
|
"social-providers": [
|
|
101
104
|
"dist/social-providers/index.d.mts"
|
|
102
105
|
],
|
|
@@ -112,22 +115,22 @@
|
|
|
112
115
|
}
|
|
113
116
|
},
|
|
114
117
|
"devDependencies": {
|
|
115
|
-
"@better-auth/utils": "0.3.
|
|
118
|
+
"@better-auth/utils": "0.3.1",
|
|
116
119
|
"@better-fetch/fetch": "1.1.21",
|
|
117
|
-
"better-call": "1.
|
|
120
|
+
"better-call": "1.2.0",
|
|
118
121
|
"jose": "^6.1.0",
|
|
119
|
-
"kysely": "^0.28.
|
|
120
|
-
"nanostores": "^1.0
|
|
121
|
-
"tsdown": "^0.
|
|
122
|
+
"kysely": "^0.28.10",
|
|
123
|
+
"nanostores": "^1.1.0",
|
|
124
|
+
"tsdown": "^0.20.1"
|
|
122
125
|
},
|
|
123
126
|
"dependencies": {
|
|
124
127
|
"@standard-schema/spec": "^1.0.0",
|
|
125
|
-
"zod": "^4.3.
|
|
128
|
+
"zod": "^4.3.6"
|
|
126
129
|
},
|
|
127
130
|
"peerDependencies": {
|
|
128
|
-
"@better-auth/utils": "0.3.
|
|
131
|
+
"@better-auth/utils": "0.3.1",
|
|
129
132
|
"@better-fetch/fetch": "1.1.21",
|
|
130
|
-
"better-call": "1.
|
|
133
|
+
"better-call": "1.2.0",
|
|
131
134
|
"jose": "^6.1.0",
|
|
132
135
|
"kysely": "^0.28.5",
|
|
133
136
|
"nanostores": "^1.0.1"
|
package/src/context/index.ts
CHANGED
|
@@ -3,6 +3,11 @@ import { getAsyncLocalStorage } from "@better-auth/core/async_hooks";
|
|
|
3
3
|
import type { DBAdapter, DBTransactionAdapter } from "../db/adapter";
|
|
4
4
|
import { __getBetterAuthGlobal } from "./global";
|
|
5
5
|
|
|
6
|
+
type HookContext = {
|
|
7
|
+
adapter: DBTransactionAdapter;
|
|
8
|
+
pendingHooks: Array<() => Promise<void>>;
|
|
9
|
+
};
|
|
10
|
+
|
|
6
11
|
const ensureAsyncStorage = async () => {
|
|
7
12
|
const betterAuthGlobal = __getBetterAuthGlobal();
|
|
8
13
|
if (!betterAuthGlobal.context.adapterAsyncStorage) {
|
|
@@ -10,7 +15,7 @@ const ensureAsyncStorage = async () => {
|
|
|
10
15
|
betterAuthGlobal.context.adapterAsyncStorage = new AsyncLocalStorage();
|
|
11
16
|
}
|
|
12
17
|
return betterAuthGlobal.context
|
|
13
|
-
.adapterAsyncStorage as AsyncLocalStorage<
|
|
18
|
+
.adapterAsyncStorage as AsyncLocalStorage<HookContext>;
|
|
14
19
|
};
|
|
15
20
|
|
|
16
21
|
/**
|
|
@@ -27,7 +32,8 @@ export const getCurrentAdapter = async (
|
|
|
27
32
|
): Promise<DBTransactionAdapter> => {
|
|
28
33
|
return ensureAsyncStorage()
|
|
29
34
|
.then((als) => {
|
|
30
|
-
|
|
35
|
+
const store = als.getStore();
|
|
36
|
+
return store?.adapter || fallback;
|
|
31
37
|
})
|
|
32
38
|
.catch(() => {
|
|
33
39
|
return fallback;
|
|
@@ -38,11 +44,28 @@ export const runWithAdapter = async <R>(
|
|
|
38
44
|
adapter: DBAdapter,
|
|
39
45
|
fn: () => R,
|
|
40
46
|
): Promise<R> => {
|
|
41
|
-
let called =
|
|
47
|
+
let called = false;
|
|
42
48
|
return ensureAsyncStorage()
|
|
43
|
-
.then((als) => {
|
|
49
|
+
.then(async (als) => {
|
|
44
50
|
called = true;
|
|
45
|
-
|
|
51
|
+
const pendingHooks: Array<() => Promise<void>> = [];
|
|
52
|
+
let result: Awaited<R>;
|
|
53
|
+
let error: unknown;
|
|
54
|
+
let hasError = false;
|
|
55
|
+
try {
|
|
56
|
+
result = await als.run({ adapter, pendingHooks }, fn);
|
|
57
|
+
} catch (err) {
|
|
58
|
+
error = err;
|
|
59
|
+
hasError = true;
|
|
60
|
+
}
|
|
61
|
+
// Execute pending hooks after the function completes (even if it threw)
|
|
62
|
+
for (const hook of pendingHooks) {
|
|
63
|
+
await hook();
|
|
64
|
+
}
|
|
65
|
+
if (hasError) {
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
return result!;
|
|
46
69
|
})
|
|
47
70
|
.catch((err) => {
|
|
48
71
|
if (!called) {
|
|
@@ -58,11 +81,27 @@ export const runWithTransaction = async <R>(
|
|
|
58
81
|
): Promise<R> => {
|
|
59
82
|
let called = true;
|
|
60
83
|
return ensureAsyncStorage()
|
|
61
|
-
.then((als) => {
|
|
84
|
+
.then(async (als) => {
|
|
62
85
|
called = true;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
86
|
+
const pendingHooks: Array<() => Promise<void>> = [];
|
|
87
|
+
let result: Awaited<R>;
|
|
88
|
+
let error: unknown;
|
|
89
|
+
let hasError = false;
|
|
90
|
+
try {
|
|
91
|
+
result = await adapter.transaction(async (trx) => {
|
|
92
|
+
return als.run({ adapter: trx, pendingHooks }, fn);
|
|
93
|
+
});
|
|
94
|
+
} catch (e) {
|
|
95
|
+
hasError = true;
|
|
96
|
+
error = e;
|
|
97
|
+
}
|
|
98
|
+
for (const hook of pendingHooks) {
|
|
99
|
+
await hook();
|
|
100
|
+
}
|
|
101
|
+
if (hasError) {
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
return result!;
|
|
66
105
|
})
|
|
67
106
|
.catch((err) => {
|
|
68
107
|
if (!called) {
|
|
@@ -71,3 +110,27 @@ export const runWithTransaction = async <R>(
|
|
|
71
110
|
throw err;
|
|
72
111
|
});
|
|
73
112
|
};
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Queue a hook to be executed after the current transaction commits.
|
|
116
|
+
* If not in a transaction, the hook will execute immediately.
|
|
117
|
+
*/
|
|
118
|
+
export const queueAfterTransactionHook = async (
|
|
119
|
+
hook: () => Promise<void>,
|
|
120
|
+
): Promise<void> => {
|
|
121
|
+
return ensureAsyncStorage()
|
|
122
|
+
.then((als) => {
|
|
123
|
+
const store = als.getStore();
|
|
124
|
+
if (store) {
|
|
125
|
+
// We're in a transaction context, queue the hook
|
|
126
|
+
store.pendingHooks.push(hook);
|
|
127
|
+
} else {
|
|
128
|
+
// Not in a transaction, execute immediately
|
|
129
|
+
return hook();
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
.catch(() => {
|
|
133
|
+
// No async storage available, execute immediately
|
|
134
|
+
return hook();
|
|
135
|
+
});
|
|
136
|
+
};
|
|
@@ -71,9 +71,7 @@ export const createAdapterFactory =
|
|
|
71
71
|
disableTransformJoin: cfg.disableTransformJoin ?? false,
|
|
72
72
|
} satisfies AdapterFactoryConfig;
|
|
73
73
|
|
|
74
|
-
const useNumberId =
|
|
75
|
-
options.advanced?.database?.useNumberId === true ||
|
|
76
|
-
options.advanced?.database?.generateId === "serial";
|
|
74
|
+
const useNumberId = options.advanced?.database?.generateId === "serial";
|
|
77
75
|
if (useNumberId && config.supportsNumericIds === false) {
|
|
78
76
|
throw new BetterAuthError(
|
|
79
77
|
`[${config.adapterName}] Your database or database adapter does not support numeric ids. Please disable "useNumberId" in your config.`,
|
|
@@ -189,9 +187,7 @@ export const createAdapterFactory =
|
|
|
189
187
|
const fields = schema[defaultModelName]!.fields;
|
|
190
188
|
|
|
191
189
|
const newMappedKeys = config.mapKeysTransformInput ?? {};
|
|
192
|
-
const useNumberId =
|
|
193
|
-
options.advanced?.database?.useNumberId ||
|
|
194
|
-
options.advanced?.database?.generateId === "serial";
|
|
190
|
+
const useNumberId = options.advanced?.database?.generateId === "serial";
|
|
195
191
|
fields.id = idField({
|
|
196
192
|
customModelName: defaultModelName,
|
|
197
193
|
forceAllowId: forceAllowId && "id" in data,
|
|
@@ -309,9 +305,7 @@ export const createAdapterFactory =
|
|
|
309
305
|
const idKey = Object.entries(newMappedKeys).find(
|
|
310
306
|
([_, v]) => v === "id",
|
|
311
307
|
)?.[0];
|
|
312
|
-
const useNumberId =
|
|
313
|
-
options.advanced?.database?.useNumberId ||
|
|
314
|
-
options.advanced?.database?.generateId === "serial";
|
|
308
|
+
const useNumberId = options.advanced?.database?.generateId === "serial";
|
|
315
309
|
tableSchema[idKey ?? "id"] = {
|
|
316
310
|
type: useNumberId ? "number" : "string",
|
|
317
311
|
};
|
|
@@ -523,9 +517,7 @@ export const createAdapterFactory =
|
|
|
523
517
|
model: defaultModelName,
|
|
524
518
|
});
|
|
525
519
|
|
|
526
|
-
const useNumberId =
|
|
527
|
-
options.advanced?.database?.useNumberId ||
|
|
528
|
-
options.advanced?.database?.generateId === "serial";
|
|
520
|
+
const useNumberId = options.advanced?.database?.generateId === "serial";
|
|
529
521
|
|
|
530
522
|
if (
|
|
531
523
|
defaultFieldName === "id" ||
|
|
@@ -1284,42 +1276,6 @@ export const createAdapterFactory =
|
|
|
1284
1276
|
delete tables.session;
|
|
1285
1277
|
}
|
|
1286
1278
|
|
|
1287
|
-
if (
|
|
1288
|
-
options.rateLimit &&
|
|
1289
|
-
options.rateLimit.storage === "database" &&
|
|
1290
|
-
// rate-limit will default to enabled in production,
|
|
1291
|
-
// and given storage is database, it will try to use the rate-limit table,
|
|
1292
|
-
// so we should make sure to generate rate-limit table schema
|
|
1293
|
-
(typeof options.rateLimit.enabled === "undefined" ||
|
|
1294
|
-
// and of course if they forcefully set to true, then they want rate-limit,
|
|
1295
|
-
// thus we should also generate rate-limit table schema
|
|
1296
|
-
options.rateLimit.enabled === true)
|
|
1297
|
-
) {
|
|
1298
|
-
tables.ratelimit = {
|
|
1299
|
-
modelName: options.rateLimit.modelName ?? "ratelimit",
|
|
1300
|
-
fields: {
|
|
1301
|
-
key: {
|
|
1302
|
-
type: "string",
|
|
1303
|
-
unique: true,
|
|
1304
|
-
required: true,
|
|
1305
|
-
fieldName: options.rateLimit.fields?.key ?? "key",
|
|
1306
|
-
},
|
|
1307
|
-
count: {
|
|
1308
|
-
type: "number",
|
|
1309
|
-
required: true,
|
|
1310
|
-
fieldName: options.rateLimit.fields?.count ?? "count",
|
|
1311
|
-
},
|
|
1312
|
-
lastRequest: {
|
|
1313
|
-
type: "number",
|
|
1314
|
-
required: true,
|
|
1315
|
-
bigint: true,
|
|
1316
|
-
defaultValue: () => Date.now(),
|
|
1317
|
-
fieldName:
|
|
1318
|
-
options.rateLimit.fields?.lastRequest ?? "lastRequest",
|
|
1319
|
-
},
|
|
1320
|
-
},
|
|
1321
|
-
};
|
|
1322
|
-
}
|
|
1323
1279
|
return adapterInstance.createSchema!({ file, tables });
|
|
1324
1280
|
}
|
|
1325
1281
|
: undefined,
|
|
@@ -1390,9 +1346,3 @@ function formatMethod(method: string) {
|
|
|
1390
1346
|
function formatAction(action: string) {
|
|
1391
1347
|
return `${TTY_COLORS.dim}(${action})${TTY_COLORS.reset}`;
|
|
1392
1348
|
}
|
|
1393
|
-
|
|
1394
|
-
/**
|
|
1395
|
-
* @deprecated Use `createAdapterFactory` instead. This export will be removed in a future version.
|
|
1396
|
-
* @alias
|
|
1397
|
-
*/
|
|
1398
|
-
export const createAdapter = createAdapterFactory;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { logger } from "../../env";
|
|
2
2
|
import type { BetterAuthOptions } from "../../types";
|
|
3
|
-
import { generateId as defaultGenerateId } from "../../utils";
|
|
3
|
+
import { generateId as defaultGenerateId } from "../../utils/id";
|
|
4
4
|
import type { BetterAuthDBSchema, DBFieldAttribute } from "../type";
|
|
5
5
|
import { initGetDefaultModelName } from "./get-default-model-name";
|
|
6
6
|
|
|
@@ -31,9 +31,7 @@ export const initGetIdField = ({
|
|
|
31
31
|
customModelName?: string;
|
|
32
32
|
forceAllowId?: boolean;
|
|
33
33
|
}) => {
|
|
34
|
-
const useNumberId =
|
|
35
|
-
options.advanced?.database?.useNumberId ||
|
|
36
|
-
options.advanced?.database?.generateId === "serial";
|
|
34
|
+
const useNumberId = options.advanced?.database?.generateId === "serial";
|
|
37
35
|
const useUUIDs = options.advanced?.database?.generateId === "uuid";
|
|
38
36
|
|
|
39
37
|
const shouldGenerateId: boolean = (() => {
|
package/src/db/adapter/types.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { BetterAuthOptions } from "../../types";
|
|
2
|
-
import type { Prettify } from "../../types/helper";
|
|
3
2
|
import type { BetterAuthDBSchema, DBFieldAttribute } from "../type";
|
|
4
3
|
import type {
|
|
5
|
-
|
|
6
|
-
CustomAdapter
|
|
4
|
+
CleanedWhere,
|
|
5
|
+
CustomAdapter,
|
|
7
6
|
DBAdapterFactoryConfig,
|
|
8
7
|
JoinConfig,
|
|
9
8
|
DBTransactionAdapter as TransactionAdapter,
|
|
@@ -127,45 +126,7 @@ export type AdapterFactoryCustomizeAdapterCreator = (config: {
|
|
|
127
126
|
}) => W extends undefined ? undefined : CleanedWhere[];
|
|
128
127
|
}) => CustomAdapter;
|
|
129
128
|
|
|
130
|
-
/**
|
|
131
|
-
* @deprecated Use `CustomAdapter` from `@better-auth/core/db/adapter` instead.
|
|
132
|
-
*/
|
|
133
|
-
export interface CustomAdapter extends Omit<CoreCustomAdapter, "createSchema"> {
|
|
134
|
-
createSchema?:
|
|
135
|
-
| ((props: {
|
|
136
|
-
/**
|
|
137
|
-
* The file the user may have passed in to the `generate` command as the expected schema file output path.
|
|
138
|
-
*/
|
|
139
|
-
file?: string;
|
|
140
|
-
/**
|
|
141
|
-
* The tables from the user's Better-Auth instance schema.
|
|
142
|
-
*/
|
|
143
|
-
tables: BetterAuthDBSchema;
|
|
144
|
-
}) => Promise<AdapterSchemaCreation>)
|
|
145
|
-
| undefined;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* @deprecated Use `CleanedWhere` from `@better-auth/core/db/adapter` instead.
|
|
150
|
-
*/
|
|
151
|
-
export type CleanedWhere = Prettify<Required<Where>>;
|
|
152
|
-
|
|
153
129
|
export type AdapterTestDebugLogs = {
|
|
154
130
|
resetDebugLogs: () => void;
|
|
155
131
|
printDebugLogs: () => void;
|
|
156
132
|
};
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* @deprecated Use `AdapterFactoryOptions` instead. This export will be removed in a future version.
|
|
160
|
-
*/
|
|
161
|
-
export type CreateAdapterOptions = AdapterFactoryOptions;
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* @deprecated Use `AdapterFactoryConfig` instead. This export will be removed in a future version.
|
|
165
|
-
*/
|
|
166
|
-
export type AdapterConfig = AdapterFactoryConfig;
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* @deprecated Use `AdapterFactoryCustomizeAdapterCreator` instead. This export will be removed in a future version.
|
|
170
|
-
*/
|
|
171
|
-
export type CreateCustomAdapter = AdapterFactoryCustomizeAdapterCreator;
|
package/src/db/get-tables.ts
CHANGED
|
@@ -32,16 +32,21 @@ export const getAuthTables = (
|
|
|
32
32
|
fields: {
|
|
33
33
|
key: {
|
|
34
34
|
type: "string",
|
|
35
|
+
unique: true,
|
|
36
|
+
required: true,
|
|
35
37
|
fieldName: options.rateLimit?.fields?.key || "key",
|
|
36
38
|
},
|
|
37
39
|
count: {
|
|
38
40
|
type: "number",
|
|
41
|
+
required: true,
|
|
39
42
|
fieldName: options.rateLimit?.fields?.count || "count",
|
|
40
43
|
},
|
|
41
44
|
lastRequest: {
|
|
42
45
|
type: "number",
|
|
43
46
|
bigint: true,
|
|
47
|
+
required: true,
|
|
44
48
|
fieldName: options.rateLimit?.fields?.lastRequest || "lastRequest",
|
|
49
|
+
defaultValue: () => Date.now(),
|
|
45
50
|
},
|
|
46
51
|
},
|
|
47
52
|
},
|
|
@@ -50,6 +55,46 @@ export const getAuthTables = (
|
|
|
50
55
|
const { user, session, account, verification, ...pluginTables } =
|
|
51
56
|
pluginSchema;
|
|
52
57
|
|
|
58
|
+
const verificationTable = {
|
|
59
|
+
verification: {
|
|
60
|
+
modelName: options.verification?.modelName || "verification",
|
|
61
|
+
fields: {
|
|
62
|
+
identifier: {
|
|
63
|
+
type: "string",
|
|
64
|
+
required: true,
|
|
65
|
+
fieldName: options.verification?.fields?.identifier || "identifier",
|
|
66
|
+
index: true,
|
|
67
|
+
},
|
|
68
|
+
value: {
|
|
69
|
+
type: "string",
|
|
70
|
+
required: true,
|
|
71
|
+
fieldName: options.verification?.fields?.value || "value",
|
|
72
|
+
},
|
|
73
|
+
expiresAt: {
|
|
74
|
+
type: "date",
|
|
75
|
+
required: true,
|
|
76
|
+
fieldName: options.verification?.fields?.expiresAt || "expiresAt",
|
|
77
|
+
},
|
|
78
|
+
createdAt: {
|
|
79
|
+
type: "date",
|
|
80
|
+
required: true,
|
|
81
|
+
defaultValue: () => new Date(),
|
|
82
|
+
fieldName: options.verification?.fields?.createdAt || "createdAt",
|
|
83
|
+
},
|
|
84
|
+
updatedAt: {
|
|
85
|
+
type: "date",
|
|
86
|
+
required: true,
|
|
87
|
+
defaultValue: () => new Date(),
|
|
88
|
+
onUpdate: () => new Date(),
|
|
89
|
+
fieldName: options.verification?.fields?.updatedAt || "updatedAt",
|
|
90
|
+
},
|
|
91
|
+
...verification?.fields,
|
|
92
|
+
...options.verification?.additionalFields,
|
|
93
|
+
},
|
|
94
|
+
order: 4,
|
|
95
|
+
},
|
|
96
|
+
} satisfies BetterAuthDBSchema;
|
|
97
|
+
|
|
53
98
|
const sessionTable = {
|
|
54
99
|
session: {
|
|
55
100
|
modelName: options.session?.modelName || "session",
|
|
@@ -242,43 +287,9 @@ export const getAuthTables = (
|
|
|
242
287
|
},
|
|
243
288
|
order: 3,
|
|
244
289
|
},
|
|
245
|
-
verification
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
identifier: {
|
|
249
|
-
type: "string",
|
|
250
|
-
required: true,
|
|
251
|
-
fieldName: options.verification?.fields?.identifier || "identifier",
|
|
252
|
-
index: true,
|
|
253
|
-
},
|
|
254
|
-
value: {
|
|
255
|
-
type: "string",
|
|
256
|
-
required: true,
|
|
257
|
-
fieldName: options.verification?.fields?.value || "value",
|
|
258
|
-
},
|
|
259
|
-
expiresAt: {
|
|
260
|
-
type: "date",
|
|
261
|
-
required: true,
|
|
262
|
-
fieldName: options.verification?.fields?.expiresAt || "expiresAt",
|
|
263
|
-
},
|
|
264
|
-
createdAt: {
|
|
265
|
-
type: "date",
|
|
266
|
-
required: true,
|
|
267
|
-
defaultValue: () => new Date(),
|
|
268
|
-
fieldName: options.verification?.fields?.createdAt || "createdAt",
|
|
269
|
-
},
|
|
270
|
-
updatedAt: {
|
|
271
|
-
type: "date",
|
|
272
|
-
required: true,
|
|
273
|
-
defaultValue: () => new Date(),
|
|
274
|
-
onUpdate: () => new Date(),
|
|
275
|
-
fieldName: options.verification?.fields?.updatedAt || "updatedAt",
|
|
276
|
-
},
|
|
277
|
-
...verification?.fields,
|
|
278
|
-
...options.verification?.additionalFields,
|
|
279
|
-
},
|
|
280
|
-
order: 4,
|
|
281
|
-
},
|
|
290
|
+
...(!options.secondaryStorage || options.verification?.storeInDatabase
|
|
291
|
+
? verificationTable
|
|
292
|
+
: {}),
|
|
282
293
|
...pluginTables,
|
|
283
294
|
...(shouldAddRateLimitTable ? rateLimitTable : {}),
|
|
284
295
|
} satisfies BetterAuthDBSchema;
|