@better-auth/core 1.4.5 → 1.4.6-beta.3

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.
Files changed (56) hide show
  1. package/.turbo/turbo-build.log +30 -29
  2. package/dist/api/index.d.mts +1 -2
  3. package/dist/api/index.mjs +3 -2
  4. package/dist/async_hooks/index.d.mts +1 -1
  5. package/dist/async_hooks/index.mjs +2 -1
  6. package/dist/async_hooks-CrTStdt6.mjs +45 -0
  7. package/dist/context/index.d.mts +2 -3
  8. package/dist/context/index.mjs +3 -2
  9. package/dist/{context-DgQ9XGBl.mjs → context-su4uu82y.mjs} +1 -1
  10. package/dist/db/adapter/index.d.mts +2 -3
  11. package/dist/db/adapter/index.mjs +951 -1
  12. package/dist/db/index.d.mts +2 -3
  13. package/dist/db/index.mjs +2 -1
  14. package/dist/env/index.d.mts +1 -1
  15. package/dist/env/index.mjs +1 -1
  16. package/dist/error/index.mjs +3 -2
  17. package/dist/{error-BhAKg8LX.mjs → error-CMXuwPsa.mjs} +1 -1
  18. package/dist/get-tables-BGfrxIVZ.mjs +252 -0
  19. package/dist/{index-D_XSRX55.d.mts → index-BfhTkcnW.d.mts} +273 -8
  20. package/dist/{index-DgwIISs7.d.mts → index-Da4Ujjef.d.mts} +0 -1
  21. package/dist/index.d.mts +2 -3
  22. package/dist/oauth2/index.d.mts +1 -2
  23. package/dist/oauth2/index.mjs +1 -1
  24. package/dist/social-providers/index.d.mts +1 -2
  25. package/dist/social-providers/index.mjs +4 -4
  26. package/dist/utils/index.d.mts +10 -1
  27. package/dist/utils/index.mjs +3 -2
  28. package/dist/utils-BqQC77zO.mjs +43 -0
  29. package/package.json +2 -2
  30. package/src/async_hooks/convex.spec.ts +12 -0
  31. package/src/async_hooks/index.ts +60 -25
  32. package/src/db/adapter/factory.ts +1362 -0
  33. package/src/db/adapter/get-default-field-name.ts +59 -0
  34. package/src/db/adapter/get-default-model-name.ts +51 -0
  35. package/src/db/adapter/get-field-attributes.ts +62 -0
  36. package/src/db/adapter/get-field-name.ts +43 -0
  37. package/src/db/adapter/get-id-field.ts +141 -0
  38. package/src/db/adapter/get-model-name.ts +36 -0
  39. package/src/db/adapter/index.ts +4 -0
  40. package/src/db/adapter/types.ts +161 -0
  41. package/src/db/adapter/utils.ts +61 -0
  42. package/src/db/get-tables.ts +276 -0
  43. package/src/db/index.ts +2 -0
  44. package/src/db/test/get-tables.test.ts +62 -0
  45. package/src/oauth2/oauth-provider.ts +1 -1
  46. package/src/types/context.ts +10 -0
  47. package/src/types/helper.ts +4 -0
  48. package/src/utils/id.ts +5 -0
  49. package/src/utils/index.ts +3 -0
  50. package/src/utils/json.ts +25 -0
  51. package/src/utils/string.ts +3 -0
  52. package/dist/async_hooks-BfRfbd1J.mjs +0 -18
  53. package/dist/utils-C5EN75oV.mjs +0 -7
  54. /package/dist/{env-8yWFh7b8.mjs → env-D6s-lvJz.mjs} +0 -0
  55. /package/dist/{index-X1Fs3IbM.d.mts → index-D4vfN5ui.d.mts} +0 -0
  56. /package/dist/{oauth2-B2XPHgx5.mjs → oauth2-7k48hhcV.mjs} +0 -0
@@ -0,0 +1,276 @@
1
+ import type { BetterAuthOptions } from "../types";
2
+ import type { BetterAuthDBSchema, DBFieldAttribute } from "./type";
3
+
4
+ export const getAuthTables = (
5
+ options: BetterAuthOptions,
6
+ ): BetterAuthDBSchema => {
7
+ const pluginSchema = (options.plugins ?? []).reduce(
8
+ (acc, plugin) => {
9
+ const schema = plugin.schema;
10
+ if (!schema) return acc;
11
+ for (const [key, value] of Object.entries(schema)) {
12
+ acc[key] = {
13
+ fields: {
14
+ ...acc[key]?.fields,
15
+ ...value.fields,
16
+ },
17
+ modelName: value.modelName || key,
18
+ };
19
+ }
20
+ return acc;
21
+ },
22
+ {} as Record<
23
+ string,
24
+ { fields: Record<string, DBFieldAttribute>; modelName: string }
25
+ >,
26
+ );
27
+
28
+ const shouldAddRateLimitTable = options.rateLimit?.storage === "database";
29
+ const rateLimitTable = {
30
+ rateLimit: {
31
+ modelName: options.rateLimit?.modelName || "rateLimit",
32
+ fields: {
33
+ key: {
34
+ type: "string",
35
+ fieldName: options.rateLimit?.fields?.key || "key",
36
+ },
37
+ count: {
38
+ type: "number",
39
+ fieldName: options.rateLimit?.fields?.count || "count",
40
+ },
41
+ lastRequest: {
42
+ type: "number",
43
+ bigint: true,
44
+ fieldName: options.rateLimit?.fields?.lastRequest || "lastRequest",
45
+ },
46
+ },
47
+ },
48
+ } satisfies BetterAuthDBSchema;
49
+
50
+ const { user, session, account, ...pluginTables } = pluginSchema;
51
+
52
+ const sessionTable = {
53
+ session: {
54
+ modelName: options.session?.modelName || "session",
55
+ fields: {
56
+ expiresAt: {
57
+ type: "date",
58
+ required: true,
59
+ fieldName: options.session?.fields?.expiresAt || "expiresAt",
60
+ },
61
+ token: {
62
+ type: "string",
63
+ required: true,
64
+ fieldName: options.session?.fields?.token || "token",
65
+ unique: true,
66
+ },
67
+ createdAt: {
68
+ type: "date",
69
+ required: true,
70
+ fieldName: options.session?.fields?.createdAt || "createdAt",
71
+ defaultValue: () => new Date(),
72
+ },
73
+ updatedAt: {
74
+ type: "date",
75
+ required: true,
76
+ fieldName: options.session?.fields?.updatedAt || "updatedAt",
77
+ onUpdate: () => new Date(),
78
+ },
79
+ ipAddress: {
80
+ type: "string",
81
+ required: false,
82
+ fieldName: options.session?.fields?.ipAddress || "ipAddress",
83
+ },
84
+ userAgent: {
85
+ type: "string",
86
+ required: false,
87
+ fieldName: options.session?.fields?.userAgent || "userAgent",
88
+ },
89
+ userId: {
90
+ type: "string",
91
+ fieldName: options.session?.fields?.userId || "userId",
92
+ references: {
93
+ model: options.user?.modelName || "user",
94
+ field: "id",
95
+ onDelete: "cascade",
96
+ },
97
+ required: true,
98
+ index: true,
99
+ },
100
+ ...session?.fields,
101
+ ...options.session?.additionalFields,
102
+ },
103
+ order: 2,
104
+ },
105
+ } satisfies BetterAuthDBSchema;
106
+
107
+ return {
108
+ user: {
109
+ modelName: options.user?.modelName || "user",
110
+ fields: {
111
+ name: {
112
+ type: "string",
113
+ required: true,
114
+ fieldName: options.user?.fields?.name || "name",
115
+ sortable: true,
116
+ },
117
+ email: {
118
+ type: "string",
119
+ unique: true,
120
+ required: true,
121
+ fieldName: options.user?.fields?.email || "email",
122
+ sortable: true,
123
+ },
124
+ emailVerified: {
125
+ type: "boolean",
126
+ defaultValue: false,
127
+ required: true,
128
+ fieldName: options.user?.fields?.emailVerified || "emailVerified",
129
+ input: false,
130
+ },
131
+ image: {
132
+ type: "string",
133
+ required: false,
134
+ fieldName: options.user?.fields?.image || "image",
135
+ },
136
+ createdAt: {
137
+ type: "date",
138
+ defaultValue: () => new Date(),
139
+ required: true,
140
+ fieldName: options.user?.fields?.createdAt || "createdAt",
141
+ },
142
+ updatedAt: {
143
+ type: "date",
144
+ defaultValue: () => new Date(),
145
+ onUpdate: () => new Date(),
146
+ required: true,
147
+ fieldName: options.user?.fields?.updatedAt || "updatedAt",
148
+ },
149
+ ...user?.fields,
150
+ ...options.user?.additionalFields,
151
+ },
152
+ order: 1,
153
+ },
154
+ //only add session table if it's not stored in secondary storage
155
+ ...(!options.secondaryStorage || options.session?.storeSessionInDatabase
156
+ ? sessionTable
157
+ : {}),
158
+ account: {
159
+ modelName: options.account?.modelName || "account",
160
+ fields: {
161
+ accountId: {
162
+ type: "string",
163
+ required: true,
164
+ fieldName: options.account?.fields?.accountId || "accountId",
165
+ },
166
+ providerId: {
167
+ type: "string",
168
+ required: true,
169
+ fieldName: options.account?.fields?.providerId || "providerId",
170
+ },
171
+ userId: {
172
+ type: "string",
173
+ references: {
174
+ model: options.user?.modelName || "user",
175
+ field: "id",
176
+ onDelete: "cascade",
177
+ },
178
+ required: true,
179
+ fieldName: options.account?.fields?.userId || "userId",
180
+ index: true,
181
+ },
182
+ accessToken: {
183
+ type: "string",
184
+ required: false,
185
+ fieldName: options.account?.fields?.accessToken || "accessToken",
186
+ },
187
+ refreshToken: {
188
+ type: "string",
189
+ required: false,
190
+ fieldName: options.account?.fields?.refreshToken || "refreshToken",
191
+ },
192
+ idToken: {
193
+ type: "string",
194
+ required: false,
195
+ fieldName: options.account?.fields?.idToken || "idToken",
196
+ },
197
+ accessTokenExpiresAt: {
198
+ type: "date",
199
+ required: false,
200
+ fieldName:
201
+ options.account?.fields?.accessTokenExpiresAt ||
202
+ "accessTokenExpiresAt",
203
+ },
204
+ refreshTokenExpiresAt: {
205
+ type: "date",
206
+ required: false,
207
+ fieldName:
208
+ options.account?.fields?.refreshTokenExpiresAt ||
209
+ "refreshTokenExpiresAt",
210
+ },
211
+ scope: {
212
+ type: "string",
213
+ required: false,
214
+ fieldName: options.account?.fields?.scope || "scope",
215
+ },
216
+ password: {
217
+ type: "string",
218
+ required: false,
219
+ fieldName: options.account?.fields?.password || "password",
220
+ },
221
+ createdAt: {
222
+ type: "date",
223
+ required: true,
224
+ fieldName: options.account?.fields?.createdAt || "createdAt",
225
+ defaultValue: () => new Date(),
226
+ },
227
+ updatedAt: {
228
+ type: "date",
229
+ required: true,
230
+ fieldName: options.account?.fields?.updatedAt || "updatedAt",
231
+ onUpdate: () => new Date(),
232
+ },
233
+ ...account?.fields,
234
+ ...options.account?.additionalFields,
235
+ },
236
+ order: 3,
237
+ },
238
+ verification: {
239
+ modelName: options.verification?.modelName || "verification",
240
+ fields: {
241
+ identifier: {
242
+ type: "string",
243
+ required: true,
244
+ fieldName: options.verification?.fields?.identifier || "identifier",
245
+ index: true,
246
+ },
247
+ value: {
248
+ type: "string",
249
+ required: true,
250
+ fieldName: options.verification?.fields?.value || "value",
251
+ },
252
+ expiresAt: {
253
+ type: "date",
254
+ required: true,
255
+ fieldName: options.verification?.fields?.expiresAt || "expiresAt",
256
+ },
257
+ createdAt: {
258
+ type: "date",
259
+ required: true,
260
+ defaultValue: () => new Date(),
261
+ fieldName: options.verification?.fields?.createdAt || "createdAt",
262
+ },
263
+ updatedAt: {
264
+ type: "date",
265
+ required: true,
266
+ defaultValue: () => new Date(),
267
+ onUpdate: () => new Date(),
268
+ fieldName: options.verification?.fields?.updatedAt || "updatedAt",
269
+ },
270
+ },
271
+ order: 4,
272
+ },
273
+ ...pluginTables,
274
+ ...(shouldAddRateLimitTable ? rateLimitTable : {}),
275
+ } satisfies BetterAuthDBSchema;
276
+ };
package/src/db/index.ts CHANGED
@@ -49,3 +49,5 @@ export type Primitive = DBPrimitive;
49
49
  * @deprecated Backport for 1.3.x, we will remove this in 1.4.x
50
50
  */
51
51
  export type BetterAuthDbSchema = BetterAuthDBSchema;
52
+
53
+ export { getAuthTables } from "./get-tables";
@@ -0,0 +1,62 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { getAuthTables } from "../get-tables";
3
+
4
+ describe("getAuthTables", () => {
5
+ it("should use correct field name for refreshTokenExpiresAt", () => {
6
+ const tables = getAuthTables({
7
+ account: {
8
+ fields: {
9
+ refreshTokenExpiresAt: "custom_refresh_token_expires_at",
10
+ },
11
+ },
12
+ });
13
+
14
+ const accountTable = tables.account;
15
+ const refreshTokenExpiresAtField =
16
+ accountTable!.fields.refreshTokenExpiresAt!;
17
+
18
+ expect(refreshTokenExpiresAtField.fieldName).toBe(
19
+ "custom_refresh_token_expires_at",
20
+ );
21
+ });
22
+
23
+ it("should not use accessTokenExpiresAt field name for refreshTokenExpiresAt", () => {
24
+ const tables = getAuthTables({
25
+ account: {
26
+ fields: {
27
+ accessTokenExpiresAt: "custom_access_token_expires_at",
28
+ refreshTokenExpiresAt: "custom_refresh_token_expires_at",
29
+ },
30
+ },
31
+ });
32
+
33
+ const accountTable = tables.account;
34
+ const refreshTokenExpiresAtField =
35
+ accountTable!.fields.refreshTokenExpiresAt!;
36
+ const accessTokenExpiresAtField =
37
+ accountTable!.fields.accessTokenExpiresAt!;
38
+
39
+ expect(refreshTokenExpiresAtField.fieldName).toBe(
40
+ "custom_refresh_token_expires_at",
41
+ );
42
+ expect(accessTokenExpiresAtField.fieldName).toBe(
43
+ "custom_access_token_expires_at",
44
+ );
45
+ expect(refreshTokenExpiresAtField.fieldName).not.toBe(
46
+ accessTokenExpiresAtField.fieldName,
47
+ );
48
+ });
49
+
50
+ it("should use default field names when no custom names provided", () => {
51
+ const tables = getAuthTables({});
52
+
53
+ const accountTable = tables.account;
54
+ const refreshTokenExpiresAtField =
55
+ accountTable!.fields.refreshTokenExpiresAt!;
56
+ const accessTokenExpiresAtField =
57
+ accountTable!.fields.accessTokenExpiresAt!;
58
+
59
+ expect(refreshTokenExpiresAtField.fieldName).toBe("refreshTokenExpiresAt");
60
+ expect(accessTokenExpiresAtField.fieldName).toBe("accessTokenExpiresAt");
61
+ });
62
+ });
@@ -150,7 +150,7 @@ export type ProviderOptions<Profile extends Record<string, any> = any> = {
150
150
  [key: string]: any;
151
151
  };
152
152
  data: any;
153
- }>)
153
+ } | null>)
154
154
  | undefined;
155
155
  /**
156
156
  * Custom function to refresh a token
@@ -165,6 +165,16 @@ export type AuthContext<Options extends BetterAuthOptions = BetterAuthOptions> =
165
165
  appName: string;
166
166
  baseURL: string;
167
167
  trustedOrigins: string[];
168
+ /**
169
+ * Verifies whether url is a trusted origin according to the "trustedOrigins" configuration
170
+ * @param url The url to verify against the "trustedOrigins" configuration
171
+ * @param settings Specify supported pattern matching settings
172
+ * @returns {boolean} true if the URL matches the origin pattern, false otherwise.
173
+ */
174
+ isTrustedOrigin: (
175
+ url: string,
176
+ settings?: { allowRelativePaths: boolean },
177
+ ) => boolean;
168
178
  oauthConfig: {
169
179
  /**
170
180
  * This is dangerous and should only be used in dev or staging environments.
@@ -4,3 +4,7 @@ export type LiteralString = "" | (string & Record<never, never>);
4
4
  export type LiteralUnion<LiteralType, BaseType extends Primitive> =
5
5
  | LiteralType
6
6
  | (BaseType & Record<never, never>);
7
+
8
+ export type Prettify<T> = {
9
+ [K in keyof T]: T[K];
10
+ } & {};
@@ -0,0 +1,5 @@
1
+ import { createRandomStringGenerator } from "@better-auth/utils/random";
2
+
3
+ export const generateId = (size?: number) => {
4
+ return createRandomStringGenerator("a-z", "A-Z", "0-9")(size || 32);
5
+ };
@@ -1 +1,4 @@
1
1
  export { defineErrorCodes } from "./error-codes";
2
+ export { generateId } from "./id";
3
+ export { safeJSONParse } from "./json";
4
+ export { capitalizeFirstLetter } from "./string";
@@ -0,0 +1,25 @@
1
+ import { logger } from "../env";
2
+
3
+ export function safeJSONParse<T>(data: unknown): T | null {
4
+ function reviver(_: string, value: any): any {
5
+ if (typeof value === "string") {
6
+ const iso8601Regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
7
+ if (iso8601Regex.test(value)) {
8
+ const date = new Date(value);
9
+ if (!isNaN(date.getTime())) {
10
+ return date;
11
+ }
12
+ }
13
+ }
14
+ return value;
15
+ }
16
+ try {
17
+ if (typeof data !== "string") {
18
+ return data as T;
19
+ }
20
+ return JSON.parse(data, reviver);
21
+ } catch (e) {
22
+ logger.error("Error parsing JSON", { error: e });
23
+ return null;
24
+ }
25
+ }
@@ -0,0 +1,3 @@
1
+ export function capitalizeFirstLetter(str: string) {
2
+ return str.charAt(0).toUpperCase() + str.slice(1);
3
+ }
@@ -1,18 +0,0 @@
1
- //#region src/async_hooks/index.ts
2
- const AsyncLocalStoragePromise = import(
3
- /* @vite-ignore */
4
- /* webpackIgnore: true */
5
- "node:async_hooks"
6
- ).then((mod) => mod.AsyncLocalStorage).catch((err) => {
7
- if ("AsyncLocalStorage" in globalThis) return globalThis.AsyncLocalStorage;
8
- console.warn("[better-auth] Warning: AsyncLocalStorage is not available in this environment. Some features may not work as expected.");
9
- console.warn("[better-auth] Please read more about this warning at https://better-auth.com/docs/installation#mount-handler");
10
- console.warn("[better-auth] If you are using Cloudflare Workers, please see: https://developers.cloudflare.com/workers/configuration/compatibility-flags/#nodejs-compatibility-flag");
11
- throw err;
12
- });
13
- async function getAsyncLocalStorage() {
14
- return AsyncLocalStoragePromise;
15
- }
16
-
17
- //#endregion
18
- export { getAsyncLocalStorage as t };
@@ -1,7 +0,0 @@
1
- //#region src/utils/error-codes.ts
2
- function defineErrorCodes(codes) {
3
- return codes;
4
- }
5
-
6
- //#endregion
7
- export { defineErrorCodes as t };
File without changes