@convex-dev/better-auth 0.7.0-alpha.8 → 0.7.0

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 (107) hide show
  1. package/dist/commonjs/client/adapter.d.ts +10 -1
  2. package/dist/commonjs/client/adapter.d.ts.map +1 -1
  3. package/dist/commonjs/client/adapter.js +190 -192
  4. package/dist/commonjs/client/adapter.js.map +1 -1
  5. package/dist/commonjs/client/index.d.ts +283 -179
  6. package/dist/commonjs/client/index.d.ts.map +1 -1
  7. package/dist/commonjs/client/index.js +59 -67
  8. package/dist/commonjs/client/index.js.map +1 -1
  9. package/dist/commonjs/component/adapterTest.d.ts +19 -0
  10. package/dist/commonjs/component/adapterTest.d.ts.map +1 -0
  11. package/dist/commonjs/component/adapterTest.js +82 -0
  12. package/dist/commonjs/component/adapterTest.js.map +1 -0
  13. package/dist/commonjs/component/lib.d.ts +308 -536
  14. package/dist/commonjs/component/lib.d.ts.map +1 -1
  15. package/dist/commonjs/component/lib.js +469 -292
  16. package/dist/commonjs/component/lib.js.map +1 -1
  17. package/dist/commonjs/component/schema.d.ts +465 -26
  18. package/dist/commonjs/component/schema.d.ts.map +1 -1
  19. package/dist/commonjs/component/schema.js +334 -18
  20. package/dist/commonjs/component/schema.js.map +1 -1
  21. package/dist/commonjs/component/util.d.ts +944 -68
  22. package/dist/commonjs/component/util.d.ts.map +1 -1
  23. package/dist/commonjs/nextjs/index.d.ts.map +1 -1
  24. package/dist/commonjs/nextjs/index.js +3 -9
  25. package/dist/commonjs/nextjs/index.js.map +1 -1
  26. package/dist/commonjs/plugins/convex/index.d.ts +14 -11
  27. package/dist/commonjs/plugins/convex/index.d.ts.map +1 -1
  28. package/dist/commonjs/plugins/convex/index.js +3 -2
  29. package/dist/commonjs/plugins/convex/index.js.map +1 -1
  30. package/dist/commonjs/plugins/cross-domain/client.d.ts +1 -1
  31. package/dist/commonjs/plugins/cross-domain/index.d.ts +5 -3
  32. package/dist/commonjs/plugins/cross-domain/index.d.ts.map +1 -1
  33. package/dist/commonjs/plugins/cross-domain/index.js +19 -5
  34. package/dist/commonjs/plugins/cross-domain/index.js.map +1 -1
  35. package/dist/commonjs/react/client.d.ts +1 -1
  36. package/dist/commonjs/react/client.d.ts.map +1 -1
  37. package/dist/commonjs/react/client.js +3 -9
  38. package/dist/commonjs/react/client.js.map +1 -1
  39. package/dist/commonjs/react-start/index.d.ts +4 -4
  40. package/dist/commonjs/react-start/index.d.ts.map +1 -1
  41. package/dist/commonjs/react-start/index.js +3 -0
  42. package/dist/commonjs/react-start/index.js.map +1 -1
  43. package/dist/commonjs/utils/index.d.ts +2 -0
  44. package/dist/commonjs/utils/index.d.ts.map +1 -0
  45. package/dist/commonjs/utils/index.js +8 -0
  46. package/dist/commonjs/utils/index.js.map +1 -0
  47. package/dist/esm/client/adapter.d.ts +10 -1
  48. package/dist/esm/client/adapter.d.ts.map +1 -1
  49. package/dist/esm/client/adapter.js +190 -192
  50. package/dist/esm/client/adapter.js.map +1 -1
  51. package/dist/esm/client/index.d.ts +283 -179
  52. package/dist/esm/client/index.d.ts.map +1 -1
  53. package/dist/esm/client/index.js +59 -67
  54. package/dist/esm/client/index.js.map +1 -1
  55. package/dist/esm/component/adapterTest.d.ts +19 -0
  56. package/dist/esm/component/adapterTest.d.ts.map +1 -0
  57. package/dist/esm/component/adapterTest.js +82 -0
  58. package/dist/esm/component/adapterTest.js.map +1 -0
  59. package/dist/esm/component/lib.d.ts +308 -536
  60. package/dist/esm/component/lib.d.ts.map +1 -1
  61. package/dist/esm/component/lib.js +469 -292
  62. package/dist/esm/component/lib.js.map +1 -1
  63. package/dist/esm/component/schema.d.ts +465 -26
  64. package/dist/esm/component/schema.d.ts.map +1 -1
  65. package/dist/esm/component/schema.js +334 -18
  66. package/dist/esm/component/schema.js.map +1 -1
  67. package/dist/esm/component/util.d.ts +944 -68
  68. package/dist/esm/component/util.d.ts.map +1 -1
  69. package/dist/esm/nextjs/index.d.ts.map +1 -1
  70. package/dist/esm/nextjs/index.js +3 -9
  71. package/dist/esm/nextjs/index.js.map +1 -1
  72. package/dist/esm/plugins/convex/index.d.ts +14 -11
  73. package/dist/esm/plugins/convex/index.d.ts.map +1 -1
  74. package/dist/esm/plugins/convex/index.js +3 -2
  75. package/dist/esm/plugins/convex/index.js.map +1 -1
  76. package/dist/esm/plugins/cross-domain/client.d.ts +1 -1
  77. package/dist/esm/plugins/cross-domain/index.d.ts +5 -3
  78. package/dist/esm/plugins/cross-domain/index.d.ts.map +1 -1
  79. package/dist/esm/plugins/cross-domain/index.js +19 -5
  80. package/dist/esm/plugins/cross-domain/index.js.map +1 -1
  81. package/dist/esm/react/client.d.ts +1 -1
  82. package/dist/esm/react/client.d.ts.map +1 -1
  83. package/dist/esm/react/client.js +3 -9
  84. package/dist/esm/react/client.js.map +1 -1
  85. package/dist/esm/react-start/index.d.ts +4 -4
  86. package/dist/esm/react-start/index.d.ts.map +1 -1
  87. package/dist/esm/react-start/index.js +3 -0
  88. package/dist/esm/react-start/index.js.map +1 -1
  89. package/dist/esm/utils/index.d.ts +2 -0
  90. package/dist/esm/utils/index.d.ts.map +1 -0
  91. package/dist/esm/utils/index.js +8 -0
  92. package/dist/esm/utils/index.js.map +1 -0
  93. package/package.json +24 -7
  94. package/src/client/adapter.test.ts +378 -0
  95. package/src/client/adapter.ts +206 -198
  96. package/src/client/index.ts +60 -80
  97. package/src/component/_generated/api.d.ts +2189 -171
  98. package/src/component/adapterTest.ts +141 -0
  99. package/src/component/lib.ts +648 -342
  100. package/src/component/schema.ts +349 -18
  101. package/src/nextjs/index.ts +3 -14
  102. package/src/plugins/convex/index.ts +5 -2
  103. package/src/plugins/cross-domain/index.ts +19 -5
  104. package/src/react/client.tsx +5 -11
  105. package/src/react-start/index.ts +4 -1
  106. package/src/client/cors.ts +0 -425
  107. /package/src/{util.ts → utils/index.ts} +0 -0
@@ -1,34 +1,140 @@
1
1
  import { BetterAuth } from "./index";
2
- import { transformInput } from "../component/lib";
3
- import { createAdapter } from "better-auth/adapters";
2
+ import {
3
+ AdapterDebugLogs,
4
+ CleanedWhere,
5
+ createAdapter,
6
+ } from "better-auth/adapters";
4
7
  import {
5
8
  GenericActionCtx,
6
9
  GenericMutationCtx,
7
10
  GenericQueryCtx,
11
+ PaginationOptions,
12
+ PaginationResult,
8
13
  } from "convex/server";
14
+ import { SetOptional } from "type-fest";
15
+
16
+ const handlePagination = async (
17
+ next: ({
18
+ paginationOpts,
19
+ }: {
20
+ paginationOpts: PaginationOptions;
21
+ }) => Promise<
22
+ SetOptional<PaginationResult<any>, "page"> & { count?: number }
23
+ >,
24
+ { limit, numItems }: { limit?: number; numItems?: number } = {}
25
+ ) => {
26
+ const state: {
27
+ isDone: boolean;
28
+ cursor: string | null;
29
+ docs: any[];
30
+ count: number;
31
+ } = {
32
+ isDone: false,
33
+ cursor: null,
34
+ docs: [],
35
+ count: 0,
36
+ };
37
+ const onResult = (
38
+ result: SetOptional<PaginationResult<any>, "page"> & { count?: number }
39
+ ) => {
40
+ state.cursor =
41
+ result.pageStatus === "SplitRecommended" ||
42
+ result.pageStatus === "SplitRequired"
43
+ ? result.splitCursor ?? result.continueCursor
44
+ : result.continueCursor;
45
+ if (result.page) {
46
+ state.docs.push(...result.page);
47
+ state.isDone = (limit && state.docs.length >= limit) || result.isDone;
48
+ return;
49
+ }
50
+ // Update and delete only return a count
51
+ if (result.count) {
52
+ state.count += result.count;
53
+ state.isDone = (limit && state.count >= limit) || result.isDone;
54
+ return;
55
+ }
56
+ state.isDone = result.isDone;
57
+ };
58
+
59
+ do {
60
+ const result = await next({
61
+ paginationOpts: {
62
+ numItems: Math.min(
63
+ numItems ?? 200,
64
+ (limit ?? 200) - state.docs.length,
65
+ 200
66
+ ),
67
+ cursor: state.cursor,
68
+ },
69
+ });
70
+ onResult(result);
71
+ } while (!state.isDone);
72
+ return state;
73
+ };
9
74
 
10
- export const convexAdapter = <
11
- Ctx extends
12
- | GenericQueryCtx<any>
13
- | GenericMutationCtx<any>
14
- | GenericActionCtx<any>,
15
- >(
16
- ctx: Ctx,
17
- component: BetterAuth
18
- ) =>
19
- createAdapter({
75
+ type ConvexCleanedWhere = CleanedWhere & {
76
+ value: string | number | boolean | string[] | number[] | null;
77
+ };
78
+
79
+ const parseWhere = (where?: CleanedWhere[]): ConvexCleanedWhere[] => {
80
+ return where?.map((where) => {
81
+ if (where.value instanceof Date) {
82
+ return {
83
+ ...where,
84
+ value: where.value.getTime(),
85
+ };
86
+ }
87
+ return where;
88
+ }) as ConvexCleanedWhere[];
89
+ };
90
+
91
+ type GenericCtx =
92
+ | GenericQueryCtx<any>
93
+ | GenericMutationCtx<any>
94
+ | GenericActionCtx<any>;
95
+
96
+ interface ConvexAdapterConfig {
97
+ /**
98
+ * Helps you debug issues with the adapter.
99
+ */
100
+ debugLogs?: AdapterDebugLogs;
101
+ }
102
+ export const convexAdapter = (
103
+ ctx: GenericCtx,
104
+ component: BetterAuth,
105
+ config: ConvexAdapterConfig = {}
106
+ ) => {
107
+ const { debugLogs } = config;
108
+ return createAdapter({
20
109
  config: {
21
110
  adapterId: "convex",
22
111
  adapterName: "Convex Adapter",
23
- debugLogs: component.config.verbose ?? false,
112
+ debugLogs: component.config.verbose ?? debugLogs ?? false,
24
113
  disableIdGeneration: true,
114
+ supportsNumericIds: false,
115
+ usePlural: false,
116
+ mapKeysTransformOutput: {
117
+ _id: "id",
118
+ },
119
+ customTransformInput: ({ data, fieldAttributes }) => {
120
+ if (data && fieldAttributes.type === "date") {
121
+ return data.getTime();
122
+ }
123
+ return data;
124
+ },
125
+ customTransformOutput: ({ data, fieldAttributes }) => {
126
+ if (data && fieldAttributes.type === "date") {
127
+ return new Date(data);
128
+ }
129
+ return data;
130
+ },
25
131
  },
26
- adapter: ({ schema }) => {
132
+ adapter: () => {
27
133
  return {
28
134
  id: "convex",
29
135
  create: async ({ model, data, select }): Promise<any> => {
30
136
  if (!("runMutation" in ctx)) {
31
- throw new Error("ctx is not an action ctx");
137
+ throw new Error("ctx is not a mutation ctx");
32
138
  }
33
139
  if (select) {
34
140
  throw new Error("select is not supported");
@@ -39,223 +145,125 @@ export const convexAdapter = <
39
145
  : model === "session"
40
146
  ? component.config.authFunctions.createSession
41
147
  : component.component.lib.create;
42
- return ctx.runMutation(createFn, {
43
- input: { table: model, ...transformInput(model, data) },
148
+ return await ctx.runMutation(createFn, {
149
+ input: { model, data },
44
150
  });
45
151
  },
46
- findOne: async ({ model, where }): Promise<any> => {
47
- if (where.length === 1 && where[0].operator === "eq") {
48
- const { value, field } = where[0];
49
- const result = await ctx.runQuery(component.component.lib.getBy, {
50
- table: model,
51
- field,
52
- unique:
53
- field === "id" ? true : schema[model].fields[field].unique,
54
- value: value instanceof Date ? value.getTime() : value,
55
- });
56
- return result;
57
- }
58
- if (
59
- model === "account" &&
60
- where.length === 2 &&
61
- where[0].field === "accountId" &&
62
- where[1].field === "providerId" &&
63
- where[0].connector === "AND"
64
- ) {
65
- return ctx.runQuery(
66
- component.component.lib.getAccountByAccountIdAndProviderId,
67
- {
68
- accountId: where[0].value as string,
69
- providerId: where[1].value as string,
152
+ findOne: async (data): Promise<any> => {
153
+ if (data.where?.every((w) => w.connector === "OR")) {
154
+ for (const w of data.where) {
155
+ const result = await ctx.runQuery(
156
+ component.component.lib.findOne,
157
+ {
158
+ ...data,
159
+ where: parseWhere([w]),
160
+ }
161
+ );
162
+ if (result) {
163
+ return result;
70
164
  }
71
- );
165
+ }
72
166
  }
73
- throw new Error("where clause not supported");
167
+ return await ctx.runQuery(component.component.lib.findOne, {
168
+ ...data,
169
+ where: parseWhere(data.where),
170
+ });
74
171
  },
75
- findMany: async ({
76
- model,
77
- where,
78
- sortBy,
79
- offset,
80
- limit,
81
- }): Promise<any[]> => {
82
- if (offset) {
172
+ findMany: async (data): Promise<any[]> => {
173
+ if (data.offset) {
83
174
  throw new Error("offset not supported");
84
175
  }
85
- if (
86
- model === "jwks" &&
87
- !where &&
88
- (!sortBy ||
89
- (sortBy?.field === "createdAt" && sortBy?.direction === "desc"))
90
- ) {
91
- return ctx.runQuery(component.component.lib.getJwks, { limit });
92
- }
93
- if (
94
- where?.length !== 1 ||
95
- (where[0].operator && where[0].operator !== "eq")
96
- ) {
97
- throw new Error("where clause not supported");
98
- }
99
- if (model === "verification" && where[0].field === "identifier") {
100
- return ctx.runQuery(
101
- component.component.lib.listVerificationsByIdentifier,
102
- {
103
- identifier: where[0].value as string,
104
- sortBy,
105
- limit,
106
- }
107
- );
176
+ if (data.where?.some((w) => w.connector === "OR")) {
177
+ throw new Error("OR connector not supported in findMany");
108
178
  }
109
- if (model === "account" && where[0].field === "userId" && !sortBy) {
110
- return ctx.runQuery(component.component.lib.getAccountsByUserId, {
111
- userId: where[0].value as any,
112
- limit,
113
- });
179
+ const result = await handlePagination(
180
+ async ({ paginationOpts }) => {
181
+ return await ctx.runQuery(component.component.lib.findMany, {
182
+ ...data,
183
+ where: parseWhere(data.where),
184
+ paginationOpts,
185
+ });
186
+ },
187
+ { limit: data.limit }
188
+ );
189
+ return result.docs;
190
+ },
191
+ count: async (data) => {
192
+ // Yes, count is just findMany returning a number.
193
+ if (data.where?.some((w) => w.connector === "OR")) {
194
+ throw new Error("OR connector not supported in findMany");
114
195
  }
115
- if (model === "session" && where[0].field === "userId" && !sortBy) {
116
- return ctx.runQuery(component.component.lib.getSessionsByUserId, {
117
- userId: where[0].value as any,
118
- limit,
196
+ const result = await handlePagination(async ({ paginationOpts }) => {
197
+ return await ctx.runQuery(component.component.lib.findMany, {
198
+ ...data,
199
+ where: parseWhere(data.where),
200
+ paginationOpts,
119
201
  });
120
- }
121
- throw new Error("where clause not supported");
122
- },
123
- count: async ({ where }) => {
124
- throw new Error("count not implemented");
125
- // return 0;
202
+ });
203
+ return result.docs?.length ?? 0;
126
204
  },
127
- update: async ({ model, where, update }): Promise<any> => {
205
+ update: async (data): Promise<any> => {
128
206
  if (!("runMutation" in ctx)) {
129
- throw new Error("ctx is not an action ctx");
207
+ throw new Error("ctx is not a mutation ctx");
130
208
  }
131
- if (where?.length === 1 && where[0].operator === "eq") {
132
- const { value, field } = where[0];
209
+ if (data.where?.length === 1 && data.where[0].operator === "eq") {
133
210
  const updateFn =
134
- model === "user"
211
+ data.model === "user"
135
212
  ? component.config.authFunctions.updateUser
136
- : component.component.lib.update;
213
+ : component.component.lib.updateOne;
137
214
  return ctx.runMutation(updateFn, {
138
215
  input: {
139
- table: model as any,
140
- where: {
141
- field,
142
- value: value instanceof Date ? value.getTime() : value,
143
- },
144
- value: transformInput(model, update as any),
216
+ model: data.model,
217
+ where: parseWhere(data.where),
218
+ update: data.update as any,
145
219
  },
146
220
  });
147
221
  }
148
222
  throw new Error("where clause not supported");
149
223
  },
150
- delete: async ({ model, where }) => {
224
+ delete: async (data) => {
151
225
  if (!("runMutation" in ctx)) {
152
- throw new Error("ctx is not an action ctx");
226
+ throw new Error("ctx is not a mutation ctx");
153
227
  }
154
- if (where?.length === 1 && where[0].operator === "eq") {
155
- const { field, value } = where[0];
156
- const deleteFn =
157
- model === "user"
158
- ? component.config.authFunctions.deleteUser
159
- : component.component.lib.deleteBy;
160
- await ctx.runMutation(deleteFn, {
161
- table: model,
162
- field,
163
- value: value instanceof Date ? value.getTime() : value,
164
- });
165
- return;
166
- }
167
- throw new Error("where clause not supported");
168
- // return null
228
+ const deleteFn =
229
+ data.model === "user"
230
+ ? component.config.authFunctions.deleteUser
231
+ : component.component.lib.deleteOne;
232
+ await ctx.runMutation(deleteFn, {
233
+ model: data.model,
234
+ where: parseWhere(data.where),
235
+ });
236
+ return;
169
237
  },
170
- deleteMany: async ({ model, where }) => {
171
- if (!("runAction" in ctx)) {
172
- throw new Error("ctx is not an action ctx");
173
- }
174
- if (
175
- model === "verification" &&
176
- where?.length === 1 &&
177
- where[0].operator === "lt" &&
178
- where[0].field === "expiresAt"
179
- ) {
180
- return ctx.runAction(
181
- component.component.lib.deleteOldVerifications,
182
- {
183
- currentTimestamp: Date.now(),
184
- }
185
- );
238
+ deleteMany: async (data) => {
239
+ if (!("runMutation" in ctx)) {
240
+ throw new Error("ctx is not a mutation ctx");
186
241
  }
187
- if (where?.length === 1 && where[0].field === "userId") {
188
- return ctx.runAction(component.component.lib.deleteAllForUser, {
189
- table: model,
190
- userId: where[0].value as any,
242
+ const result = await handlePagination(async ({ paginationOpts }) => {
243
+ return await ctx.runMutation(component.component.lib.deleteMany, {
244
+ ...data,
245
+ where: parseWhere(data.where),
246
+ paginationOpts,
191
247
  });
192
- }
193
- if (
194
- model === "session" &&
195
- where?.length === 2 &&
196
- where[0].operator === "eq" &&
197
- where[0].connector === "AND" &&
198
- where[0].field === "userId" &&
199
- where[1].operator === "lte" &&
200
- where[1].field === "expiresAt" &&
201
- typeof where[1].value === "number"
202
- ) {
203
- return ctx.runMutation(
204
- component.component.lib.deleteExpiredSessions,
205
- {
206
- userId: where[0].value as string,
207
- expiresAt: where[1].value as number,
208
- }
209
- );
210
- }
211
- throw new Error("where clause not supported");
212
- // return count;
248
+ });
249
+ return result.count;
213
250
  },
214
- updateMany: async ({ model, where, update }) => {
251
+ updateMany: async (data) => {
215
252
  if (!("runMutation" in ctx)) {
216
253
  throw new Error("ctx is not an action ctx");
217
254
  }
218
- if (
219
- model === "twoFactor" &&
220
- where?.length === 1 &&
221
- where[0].operator === "eq" &&
222
- where[0].field === "userId"
223
- ) {
224
- return ctx.runMutation(component.component.lib.updateTwoFactor, {
225
- userId: where[0].value as string,
226
- update: transformInput(model, update as any),
255
+ const result = await handlePagination(async ({ paginationOpts }) => {
256
+ return await ctx.runMutation(component.component.lib.updateMany, {
257
+ input: {
258
+ ...data,
259
+ where: parseWhere(data.where),
260
+ paginationOpts,
261
+ },
227
262
  });
228
- }
229
- if (
230
- model === "account" &&
231
- where?.length === 2 &&
232
- where[0].operator === "eq" &&
233
- where[0].connector === "AND" &&
234
- where[0].field === "userId" &&
235
- where[1].operator === "eq" &&
236
- where[1].field === "providerId"
237
- ) {
238
- return ctx.runMutation(
239
- component.component.lib.updateUserProviderAccounts,
240
- {
241
- userId: where[0].value as string,
242
- providerId: where[1].value as string,
243
- update: transformInput(model, update as any),
244
- }
245
- );
246
- }
247
- throw new Error("updateMany not implemented");
248
- //return 0;
249
- /*
250
- const { model, where, update } = data;
251
- const table = db[model];
252
- const res = convertWhereClause(where, table, model);
253
- res.forEach((record) => {
254
- Object.assign(record, update);
255
263
  });
256
- return res[0] || null;
257
- */
264
+ return result.count;
258
265
  },
259
266
  };
260
267
  },
261
268
  });
269
+ };