@convex-dev/better-auth 0.7.0-alpha.12 → 0.7.0-alpha.2

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 (108) hide show
  1. package/dist/commonjs/client/adapter.d.ts +1 -10
  2. package/dist/commonjs/client/adapter.d.ts.map +1 -1
  3. package/dist/commonjs/client/adapter.js +192 -183
  4. package/dist/commonjs/client/adapter.js.map +1 -1
  5. package/dist/commonjs/client/index.d.ts +179 -238
  6. package/dist/commonjs/client/index.d.ts.map +1 -1
  7. package/dist/commonjs/client/index.js +67 -60
  8. package/dist/commonjs/client/index.js.map +1 -1
  9. package/dist/commonjs/component/lib.d.ts +548 -218
  10. package/dist/commonjs/component/lib.d.ts.map +1 -1
  11. package/dist/commonjs/component/lib.js +286 -315
  12. package/dist/commonjs/component/lib.js.map +1 -1
  13. package/dist/commonjs/component/schema.d.ts +28 -90
  14. package/dist/commonjs/component/schema.d.ts.map +1 -1
  15. package/dist/commonjs/component/schema.js +18 -76
  16. package/dist/commonjs/component/schema.js.map +1 -1
  17. package/dist/commonjs/component/util.d.ts +86 -148
  18. package/dist/commonjs/component/util.d.ts.map +1 -1
  19. package/dist/commonjs/nextjs/index.d.ts.map +1 -1
  20. package/dist/commonjs/nextjs/index.js +0 -3
  21. package/dist/commonjs/nextjs/index.js.map +1 -1
  22. package/dist/commonjs/plugins/convex/index.d.ts +11 -14
  23. package/dist/commonjs/plugins/convex/index.d.ts.map +1 -1
  24. package/dist/commonjs/plugins/convex/index.js +4 -10
  25. package/dist/commonjs/plugins/convex/index.js.map +1 -1
  26. package/dist/commonjs/plugins/cross-domain/client.d.ts +1 -1
  27. package/dist/commonjs/plugins/cross-domain/index.d.ts +3 -5
  28. package/dist/commonjs/plugins/cross-domain/index.d.ts.map +1 -1
  29. package/dist/commonjs/plugins/cross-domain/index.js +5 -19
  30. package/dist/commonjs/plugins/cross-domain/index.js.map +1 -1
  31. package/dist/commonjs/react/client.d.ts +1 -1
  32. package/dist/commonjs/react/client.d.ts.map +1 -1
  33. package/dist/commonjs/react/client.js +9 -3
  34. package/dist/commonjs/react/client.js.map +1 -1
  35. package/dist/commonjs/react-start/index.d.ts +3 -37
  36. package/dist/commonjs/react-start/index.d.ts.map +1 -1
  37. package/dist/commonjs/react-start/index.js +4 -20
  38. package/dist/commonjs/react-start/index.js.map +1 -1
  39. package/dist/esm/client/adapter.d.ts +1 -10
  40. package/dist/esm/client/adapter.d.ts.map +1 -1
  41. package/dist/esm/client/adapter.js +192 -183
  42. package/dist/esm/client/adapter.js.map +1 -1
  43. package/dist/esm/client/index.d.ts +179 -238
  44. package/dist/esm/client/index.d.ts.map +1 -1
  45. package/dist/esm/client/index.js +67 -60
  46. package/dist/esm/client/index.js.map +1 -1
  47. package/dist/esm/component/lib.d.ts +548 -218
  48. package/dist/esm/component/lib.d.ts.map +1 -1
  49. package/dist/esm/component/lib.js +286 -315
  50. package/dist/esm/component/lib.js.map +1 -1
  51. package/dist/esm/component/schema.d.ts +28 -90
  52. package/dist/esm/component/schema.d.ts.map +1 -1
  53. package/dist/esm/component/schema.js +18 -76
  54. package/dist/esm/component/schema.js.map +1 -1
  55. package/dist/esm/component/util.d.ts +86 -148
  56. package/dist/esm/component/util.d.ts.map +1 -1
  57. package/dist/esm/nextjs/index.d.ts.map +1 -1
  58. package/dist/esm/nextjs/index.js +0 -3
  59. package/dist/esm/nextjs/index.js.map +1 -1
  60. package/dist/esm/plugins/convex/index.d.ts +11 -14
  61. package/dist/esm/plugins/convex/index.d.ts.map +1 -1
  62. package/dist/esm/plugins/convex/index.js +4 -10
  63. package/dist/esm/plugins/convex/index.js.map +1 -1
  64. package/dist/esm/plugins/cross-domain/client.d.ts +1 -1
  65. package/dist/esm/plugins/cross-domain/index.d.ts +3 -5
  66. package/dist/esm/plugins/cross-domain/index.d.ts.map +1 -1
  67. package/dist/esm/plugins/cross-domain/index.js +5 -19
  68. package/dist/esm/plugins/cross-domain/index.js.map +1 -1
  69. package/dist/esm/react/client.d.ts +1 -1
  70. package/dist/esm/react/client.d.ts.map +1 -1
  71. package/dist/esm/react/client.js +9 -3
  72. package/dist/esm/react/client.js.map +1 -1
  73. package/dist/esm/react-start/index.d.ts +3 -37
  74. package/dist/esm/react-start/index.d.ts.map +1 -1
  75. package/dist/esm/react-start/index.js +4 -20
  76. package/dist/esm/react-start/index.js.map +1 -1
  77. package/package.json +5 -20
  78. package/src/client/adapter.ts +195 -191
  79. package/src/client/cors.ts +425 -0
  80. package/src/client/index.ts +80 -61
  81. package/src/component/_generated/api.d.ts +149 -605
  82. package/src/component/lib.ts +335 -444
  83. package/src/component/schema.ts +19 -81
  84. package/src/nextjs/index.ts +0 -3
  85. package/src/plugins/convex/index.ts +4 -12
  86. package/src/plugins/cross-domain/index.ts +5 -19
  87. package/src/react/client.tsx +11 -5
  88. package/src/react-start/index.ts +6 -33
  89. package/dist/commonjs/component/adapterTest.d.ts +0 -19
  90. package/dist/commonjs/component/adapterTest.d.ts.map +0 -1
  91. package/dist/commonjs/component/adapterTest.js +0 -82
  92. package/dist/commonjs/component/adapterTest.js.map +0 -1
  93. package/dist/commonjs/utils/index.d.ts +0 -2
  94. package/dist/commonjs/utils/index.d.ts.map +0 -1
  95. package/dist/commonjs/utils/index.js +0 -8
  96. package/dist/commonjs/utils/index.js.map +0 -1
  97. package/dist/esm/component/adapterTest.d.ts +0 -19
  98. package/dist/esm/component/adapterTest.d.ts.map +0 -1
  99. package/dist/esm/component/adapterTest.js +0 -82
  100. package/dist/esm/component/adapterTest.js.map +0 -1
  101. package/dist/esm/utils/index.d.ts +0 -2
  102. package/dist/esm/utils/index.d.ts.map +0 -1
  103. package/dist/esm/utils/index.js +0 -8
  104. package/dist/esm/utils/index.js.map +0 -1
  105. package/src/client/adapter.test.ts +0 -144
  106. package/src/component/adapterTest.ts +0 -141
  107. package/src/react-start/vite-env.d.ts +0 -2
  108. /package/src/{utils/index.ts → util.ts} +0 -0
@@ -1,380 +1,351 @@
1
- import { mutation, query } from "../component/_generated/server.js";
1
+ import { action, mutation, query, } from "../component/_generated/server.js";
2
2
  import { asyncMap } from "convex-helpers";
3
3
  import { v } from "convex/values";
4
- import schema, { specialFields } from "../component/schema.js";
5
- import { paginationOptsValidator, } from "convex/server";
4
+ import { api } from "../component/_generated/api.js";
5
+ import schema from "../component/schema.js";
6
+ import { paginationOptsValidator } from "convex/server";
6
7
  import { paginator } from "convex-helpers/server/pagination";
7
8
  import { partial } from "convex-helpers/validators";
8
- export const adapterWhereValidator = v.object({
9
- field: v.string(),
10
- operator: v.optional(v.union(v.literal("lt"), v.literal("lte"), v.literal("gt"), v.literal("gte"), v.literal("eq"), v.literal("in"), v.literal("ne"), v.literal("contains"), v.literal("starts_with"), v.literal("ends_with"))),
11
- value: v.union(v.string(), v.number(), v.boolean(), v.array(v.string()), v.array(v.number()), v.null()),
12
- connector: v.optional(v.union(v.literal("AND"), v.literal("OR"))),
13
- });
14
- export const adapterArgsValidator = v.object({
15
- model: v.string(),
16
- where: v.optional(v.array(adapterWhereValidator)),
17
- sortBy: v.optional(v.object({
18
- field: v.string(),
19
- direction: v.union(v.literal("asc"), v.literal("desc")),
20
- })),
21
- select: v.optional(v.array(v.string())),
22
- limit: v.optional(v.number()),
23
- unique: v.optional(v.boolean()),
24
- });
25
- const getUniqueFields = (table, input) => {
26
- const fields = specialFields[table];
27
- if (!fields) {
28
- return [];
29
- }
30
- return Object.entries(fields)
31
- .filter(([key, value]) => value.unique && Object.keys(input).includes(key))
32
- .map(([key]) => key);
33
- };
34
- const checkUniqueFields = async (ctx, table, input, doc) => {
35
- const uniqueFields = getUniqueFields(table, input);
36
- if (!uniqueFields.length) {
37
- return;
38
- }
39
- for (const field of uniqueFields) {
40
- const existingDoc = await ctx.db
41
- .query(table)
42
- .withIndex(field, (q) => q.eq(field, input[field]))
43
- .unique();
44
- if (existingDoc && existingDoc._id !== doc?._id) {
45
- throw new Error(`${table} ${field} already exists`);
46
- }
47
- }
48
- };
49
- const findIndex = async (args) => {
50
- if (!args.where && !args.sortBy) {
51
- return;
52
- }
53
- if (args.where?.some((w) => w.field === "id")) {
54
- throw new Error("id is not a valid index field");
55
- }
56
- if (args.where?.some((w) => w.connector && w.connector !== "AND")) {
57
- throw new Error(`OR connector not supported: ${JSON.stringify(args.where)}`);
58
- }
59
- if (args.where?.some((w) => w.operator &&
60
- !["lt", "lte", "gt", "gte", "eq", "in"].includes(w.operator))) {
61
- throw new Error(`where clause not supported: ${JSON.stringify(args.where)}`);
62
- }
63
- const lowerBounds = args.where?.filter((w) => w.operator === "lt" || w.operator === "lte") ??
64
- [];
65
- if (lowerBounds.length > 1) {
66
- throw new Error(`cannot have more than one lower bound where clause: ${JSON.stringify(args.where)}`);
67
- }
68
- const upperBounds = args.where?.filter((w) => w.operator === "gt" || w.operator === "gte") ??
69
- [];
70
- if (upperBounds.length > 1) {
71
- throw new Error(`cannot have more than one upper bound where clause: ${JSON.stringify(args.where)}`);
72
- }
73
- const lowerBound = lowerBounds[0];
74
- const upperBound = upperBounds[0];
75
- if (lowerBound && upperBound && lowerBound.field !== upperBound.field) {
76
- throw new Error(`lower bound and upper bound must have the same field: ${JSON.stringify(args.where)}`);
77
- }
78
- const boundField = lowerBound?.field || upperBound?.field;
79
- if (boundField &&
80
- args.where?.some((w) => w.field === boundField && w !== lowerBound && w !== upperBound)) {
81
- throw new Error(`too many where clauses on the bound field: ${JSON.stringify(args.where)}`);
82
- }
83
- const indexFields = args.where
84
- ?.filter((w) => !w.operator || w.operator === "eq")
85
- .sort((a, b) => {
86
- return a.field.localeCompare(b.field);
87
- })
88
- .map((w) => [w.field, w.value]) ?? [];
89
- if (!indexFields?.length && !boundField && !args.sortBy) {
90
- return;
91
- }
92
- const indexes = schema.tables[args.model][" indexes"]();
93
- const sortField = args.sortBy?.field;
94
- // We internally use _creationTime in place of Better Auth's createdAt
95
- const indexName = indexFields
96
- .map(([field]) => field)
97
- .join("_")
98
- .concat(boundField && boundField !== "createdAt"
99
- ? `${indexFields.length ? "_" : ""}${boundField}`
100
- : "")
101
- .concat(sortField && sortField !== "createdAt" && boundField !== sortField
102
- ? `${indexFields.length || boundField ? "_" : ""}${sortField}`
103
- : "");
104
- if (!indexName && !boundField && !sortField) {
105
- return;
106
- }
107
- // Use the built in creationTime index if bounding or sorting by createdAt
108
- // with no other fields
109
- const index = !indexName
110
- ? {
111
- indexDescriptor: "by_creation_time",
112
- fields: [],
113
- }
114
- : indexes.find(({ indexDescriptor }) => {
115
- return boundField === "createdAt" || sortField === "createdAt"
116
- ? indexDescriptor === indexName
117
- : indexDescriptor.startsWith(indexName);
118
- });
119
- if (!index) {
120
- throw new Error(`Index ${indexName} not found for table ${args.model}`);
121
- }
9
+ export const transformInput = (model, data) => {
122
10
  return {
123
- index: {
124
- indexDescriptor: index.indexDescriptor,
125
- fields: [...index.fields, "_creationTime"],
126
- },
127
- boundField,
128
- sortField,
129
- values: {
130
- eq: indexFields.map(([, value]) => value),
131
- lt: lowerBound?.operator === "lt" ? lowerBound.value : undefined,
132
- lte: lowerBound?.operator === "lte" ? lowerBound.value : undefined,
133
- gt: upperBound?.operator === "gt" ? upperBound.value : undefined,
134
- gte: upperBound?.operator === "gte" ? upperBound.value : undefined,
135
- },
11
+ ...Object.fromEntries(Object.entries(data).map(([key, value]) => {
12
+ if (value instanceof Date) {
13
+ return [key, value.getTime()];
14
+ }
15
+ return [key, value];
16
+ })),
136
17
  };
137
18
  };
138
- const selectFields = (doc, select) => {
139
- if (!doc) {
140
- return null;
141
- }
142
- if (!select?.length) {
143
- return doc;
144
- }
145
- return select.reduce((acc, field) => {
146
- acc[field] = doc[field];
147
- return acc;
148
- }, {});
19
+ export const transformOutput = ({ _id, _creationTime, ...data }, _model) => {
20
+ // Provide the expected id field, but it can be overwritten if
21
+ // the model has an id field
22
+ return { id: _id, ...data };
149
23
  };
150
- // This is the core function for reading from the database, it parses and
151
- // validates where conditions, selects indexes, and allows the caller to
152
- // optionally paginate as needed.
153
- const paginate = async (ctx, args) => {
154
- // If any index is id, we can only return a single document
155
- const idWhere = args.where?.find((w) => w.field === "id");
156
- if (idWhere) {
157
- const doc = await ctx.db.get(idWhere.value);
158
- return {
159
- page: [selectFields(doc, args.select)].filter(Boolean),
160
- isDone: true,
161
- continueCursor: "",
162
- };
24
+ // Get the session via sessionId in jwt claims
25
+ export const getCurrentSession = query({
26
+ args: {},
27
+ handler: async (ctx) => {
28
+ const identity = await ctx.auth.getUserIdentity();
29
+ if (!identity) {
30
+ return null;
31
+ }
32
+ return ctx.db.get(identity.sessionId);
33
+ },
34
+ });
35
+ export const getByHelper = async (ctx, args) => {
36
+ if (args.field === "id") {
37
+ return ctx.db.get(args.value);
163
38
  }
164
- const { index, values, boundField } = (await findIndex(args)) ?? {};
165
- const query = paginator(ctx.db, schema).query(args.model);
166
- const hasValues = values?.eq?.length ||
167
- values?.lt ||
168
- values?.lte ||
169
- values?.gt ||
170
- values?.gte;
171
- const indexedQuery = index && index.indexDescriptor !== "by_creation_time"
172
- ? query.withIndex(index.indexDescriptor, hasValues
173
- ? (q) => {
174
- for (const [idx, value] of (values?.eq ?? []).entries()) {
175
- q = q.eq(index.fields[idx], value);
176
- }
177
- if (values?.lt) {
178
- q = q.lt(boundField, values.lt);
179
- }
180
- if (values?.lte) {
181
- q = q.lte(boundField, values.lte);
182
- }
183
- if (values?.gt) {
184
- q = q.gt(boundField, values.gt);
185
- }
186
- if (values?.gte) {
187
- q = q.gte(boundField, values.gte);
188
- }
189
- return q;
190
- }
191
- : undefined)
192
- : query;
193
- const orderedQuery = args.sortBy
194
- ? indexedQuery.order(args.sortBy.direction === "asc" ? "asc" : "desc")
195
- : indexedQuery;
196
- const result = await orderedQuery.paginate(args.paginationOpts);
197
- return {
198
- ...result,
199
- page: result.page.map((doc) => selectFields(doc, args.select)),
200
- };
39
+ const query = ctx.db
40
+ .query(args.table)
41
+ .withIndex(args.field, (q) => q.eq(args.field, args.value));
42
+ return args.unique ? await query.unique() : await query.first();
201
43
  };
202
- const listOne = async (ctx, args) => {
203
- return (await paginate(ctx, {
204
- ...args,
205
- paginationOpts: {
206
- numItems: 1,
207
- cursor: null,
208
- },
209
- })).page[0];
44
+ export const getByArgsValidator = {
45
+ table: v.string(),
46
+ field: v.string(),
47
+ unique: v.optional(v.boolean()),
48
+ value: v.union(v.string(), v.number(), v.boolean(), v.array(v.string()), v.array(v.number()), v.null()),
210
49
  };
50
+ // Generic functions
51
+ export const getByQuery = query({
52
+ args: getByArgsValidator,
53
+ handler: async (ctx, args) => {
54
+ const doc = await getByHelper(ctx, args);
55
+ if (!doc) {
56
+ return;
57
+ }
58
+ return transformOutput(doc, args.table);
59
+ },
60
+ });
61
+ export { getByQuery as getBy };
211
62
  export const create = mutation({
212
- args: {
213
- input: v.union(...Object.entries(schema.tables).map(([model, table]) => v.object({
214
- model: v.literal(model),
215
- where: v.optional(v.array(adapterWhereValidator)),
216
- data: v.object(table.validator.fields),
63
+ args: v.object({
64
+ input: v.union(...Object.values(schema.tables).map((table) => v.object({
65
+ table: v.string(),
66
+ ...table.validator.fields,
217
67
  }))),
218
- },
68
+ }),
219
69
  handler: async (ctx, args) => {
220
- await checkUniqueFields(ctx, args.input.model, args.input.data);
221
- const id = await ctx.db.insert(args.input.model, args.input.data);
70
+ const { table, ...input } = args.input;
71
+ const id = await ctx.db.insert(table, {
72
+ ...input,
73
+ });
222
74
  const doc = await ctx.db.get(id);
223
75
  if (!doc) {
224
- throw new Error(`Failed to create ${args.input.model}`);
76
+ throw new Error(`Failed to create ${table}`);
77
+ }
78
+ return transformOutput(doc, table);
79
+ },
80
+ });
81
+ export const updateArgsInputValidator = (table) => {
82
+ return v.object({
83
+ table: v.literal(table),
84
+ where: v.object({ field: v.string(), value: getByArgsValidator.value }),
85
+ value: v.record(v.string(), v.any()),
86
+ });
87
+ };
88
+ const updateArgsValidator = {
89
+ input: v.union(updateArgsInputValidator("account"), updateArgsInputValidator("session"), updateArgsInputValidator("verification"), updateArgsInputValidator("user")),
90
+ };
91
+ export const update = mutation({
92
+ args: updateArgsValidator,
93
+ handler: async (ctx, args) => {
94
+ const { table, where, value } = args.input;
95
+ const doc = where.field === "id"
96
+ ? await ctx.db.get(where.value)
97
+ : await getByHelper(ctx, { table, ...where });
98
+ if (!doc) {
99
+ throw new Error(`Failed to update ${table}`);
100
+ }
101
+ await ctx.db.patch(doc._id, value);
102
+ const updatedDoc = await ctx.db.get(doc._id);
103
+ if (!updatedDoc) {
104
+ throw new Error(`Failed to update ${table}`);
105
+ }
106
+ return transformOutput(updatedDoc, table);
107
+ },
108
+ });
109
+ export const deleteBy = mutation({
110
+ args: getByArgsValidator,
111
+ handler: async (ctx, args) => {
112
+ const doc = await getByHelper(ctx, args);
113
+ if (!doc) {
114
+ return;
225
115
  }
116
+ await ctx.db.delete(doc._id);
117
+ // onDeleteUser requires userId from the doc,
118
+ // so just return the whole thing
226
119
  return doc;
227
120
  },
228
121
  });
229
- export const findOne = query({
230
- args: adapterArgsValidator,
122
+ // Single purpose functions
123
+ export const getAccountsByUserId = query({
124
+ args: { userId: v.string(), limit: v.optional(v.number()) },
231
125
  handler: async (ctx, args) => {
232
- return await listOne(ctx, args);
126
+ const query = ctx.db
127
+ .query("account")
128
+ .withIndex("userId", (q) => q.eq("userId", args.userId));
129
+ const docs = args.limit
130
+ ? await query.take(args.limit)
131
+ : await query.collect();
132
+ return docs.map((doc) => transformOutput(doc, "account"));
233
133
  },
234
134
  });
235
- export const findMany = query({
135
+ export const getSessionsByUserId = query({
136
+ args: { userId: v.string(), limit: v.optional(v.number()) },
137
+ handler: async (ctx, args) => {
138
+ const query = ctx.db
139
+ .query("session")
140
+ .withIndex("userId", (q) => q.eq("userId", args.userId));
141
+ const docs = args.limit
142
+ ? await query.take(args.limit)
143
+ : await query.collect();
144
+ return docs.map((doc) => transformOutput(doc, "session"));
145
+ },
146
+ });
147
+ export const getJwks = query({
236
148
  args: {
237
- ...adapterArgsValidator.fields,
238
- paginationOpts: paginationOptsValidator,
149
+ limit: v.optional(v.number()),
239
150
  },
240
151
  handler: async (ctx, args) => {
241
- return await paginate(ctx, args);
152
+ const query = ctx.db.query("jwks");
153
+ const docs = args.limit
154
+ ? await query.take(args.limit)
155
+ : await query.collect();
156
+ return docs.map((doc) => transformOutput(doc, "jwks"));
242
157
  },
243
158
  });
244
- export const updateOne = mutation({
159
+ export const listVerificationsByIdentifier = query({
245
160
  args: {
246
- input: v.union(...Object.entries(schema.tables).map(([model, table]) => v.object({
247
- model: v.literal(model),
248
- where: v.optional(v.array(adapterWhereValidator)),
249
- update: v.object(Object.fromEntries(Object.entries(table.validator.fields).map(([key, value]) => [
250
- key,
251
- value.isOptional === "required" ? v.optional(value) : value,
252
- ]))),
253
- }))),
161
+ identifier: v.string(),
162
+ sortBy: v.optional(v.object({
163
+ field: v.string(),
164
+ direction: v.union(v.literal("asc"), v.literal("desc")),
165
+ })),
166
+ limit: v.optional(v.number()),
254
167
  },
255
168
  handler: async (ctx, args) => {
256
- const doc = await listOne(ctx, args.input);
257
- if (!doc) {
258
- throw new Error(`Failed to update ${args.input.model}`);
169
+ if (args.sortBy && args.sortBy.field !== "createdAt") {
170
+ throw new Error(`Unsupported sortBy field: ${args.sortBy.field}`);
259
171
  }
260
- await checkUniqueFields(ctx, args.input.model, args.input.update, doc);
261
- await ctx.db.patch(doc._id, args.input.update);
262
- const updatedDoc = await ctx.db.get(doc._id);
263
- if (!updatedDoc) {
264
- throw new Error(`Failed to update ${args.input.model}`);
265
- }
266
- return updatedDoc;
172
+ const query = ctx.db
173
+ .query("verification")
174
+ .withIndex("identifier", (q) => q.eq("identifier", args.identifier))
175
+ .order(args.sortBy?.field === "createdAt" && args.sortBy?.direction
176
+ ? args.sortBy.direction
177
+ : "asc");
178
+ const docs = args.limit
179
+ ? await query.take(args.limit)
180
+ : await query.collect();
181
+ return docs.map((doc) => transformOutput(doc, "verification"));
267
182
  },
268
183
  });
269
- export const updateMany = mutation({
184
+ export const deleteOldVerificationsPage = mutation({
270
185
  args: {
271
- ...adapterArgsValidator.fields,
272
- update: v.optional(v.object(partial(schema.tables.user.validator.fields))),
273
- paginationOpts: paginationOptsValidator,
186
+ currentTimestamp: v.number(),
187
+ paginationOpts: v.optional(paginationOptsValidator),
274
188
  },
275
189
  handler: async (ctx, args) => {
276
- const { page, ...result } = await paginate(ctx, args);
277
- if (args.update) {
278
- const uniqueFields = getUniqueFields(args.model, args.update ?? {});
279
- if (uniqueFields.length && page.length > 1) {
280
- throw new Error(`Attempted to set unique fields in multiple documents in ${args.model} with the same value. Fields: ${uniqueFields.join(", ")}`);
281
- }
282
- await asyncMap(page, async (doc) => {
283
- await checkUniqueFields(ctx, args.model, args.update ?? {}, doc);
284
- await ctx.db.patch(doc._id, args.update);
285
- });
286
- }
287
- return {
288
- ...result,
289
- count: page.length,
190
+ const paginationOpts = args.paginationOpts ?? {
191
+ numItems: 500,
192
+ cursor: null,
290
193
  };
194
+ const { page, ...result } = await paginator(ctx.db, schema)
195
+ .query("verification")
196
+ .withIndex("expiresAt", (q) => q.lt("expiresAt", args.currentTimestamp))
197
+ .paginate(paginationOpts);
198
+ await asyncMap(page, async (doc) => {
199
+ await ctx.db.delete(doc._id);
200
+ });
201
+ return { ...result, count: page.length };
291
202
  },
292
203
  });
293
- export const deleteOne = mutation({
294
- args: adapterArgsValidator,
204
+ export const deleteOldVerifications = action({
205
+ args: {
206
+ currentTimestamp: v.number(),
207
+ },
295
208
  handler: async (ctx, args) => {
296
- const doc = await listOne(ctx, args);
297
- if (!doc) {
298
- return;
299
- }
300
- await ctx.db.delete(doc._id);
301
- return doc;
209
+ let count = 0;
210
+ let cursor = null;
211
+ let isDone = false;
212
+ do {
213
+ const result = await ctx.runMutation(api.lib.deleteOldVerificationsPage, {
214
+ currentTimestamp: args.currentTimestamp,
215
+ paginationOpts: {
216
+ numItems: 500,
217
+ cursor,
218
+ },
219
+ });
220
+ count += result.count;
221
+ cursor =
222
+ result.pageStatus &&
223
+ result.splitCursor &&
224
+ ["SplitRecommended", "SplitRequired"].includes(result.pageStatus)
225
+ ? result.splitCursor
226
+ : result.continueCursor;
227
+ isDone = result.isDone;
228
+ } while (!isDone);
229
+ return count;
302
230
  },
303
231
  });
304
- export const deleteMany = mutation({
232
+ export const deleteExpiredSessions = mutation({
305
233
  args: {
306
- ...adapterArgsValidator.fields,
307
- paginationOpts: paginationOptsValidator,
234
+ userId: v.string(),
235
+ expiresAt: v.number(),
308
236
  },
309
237
  handler: async (ctx, args) => {
310
- const { page, ...result } = await paginate(ctx, args);
311
- await asyncMap(page, async (doc) => {
238
+ const docs = await ctx.db
239
+ .query("session")
240
+ .withIndex("userId_expiresAt", (q) => q.eq("userId", args.userId).lt("expiresAt", args.expiresAt))
241
+ .collect();
242
+ await asyncMap(docs, async (doc) => {
312
243
  await ctx.db.delete(doc._id);
313
244
  });
314
- return {
315
- ...result,
316
- count: page.length,
245
+ return docs.length;
246
+ },
247
+ });
248
+ export const deleteAllForUserPage = mutation({
249
+ args: {
250
+ table: v.string(),
251
+ userId: v.string(),
252
+ paginationOpts: v.optional(paginationOptsValidator),
253
+ },
254
+ handler: async (ctx, args) => {
255
+ const paginationOpts = args.paginationOpts ?? {
256
+ numItems: 500,
257
+ cursor: null,
317
258
  };
259
+ const { page, ...result } = await paginator(ctx.db, schema)
260
+ .query(args.table)
261
+ .withIndex("userId", (q) => q.eq("userId", args.userId))
262
+ .paginate(paginationOpts);
263
+ await asyncMap(page, async (doc) => {
264
+ await ctx.db.delete(doc._id);
265
+ });
266
+ return { ...result, count: page.length };
318
267
  },
319
268
  });
320
- // Get the session via sessionId in jwt claims
321
- // TODO: this needs a refresh, subquery only necessary for actions
322
- export const getCurrentSession = query({
323
- args: {},
324
- handler: async (ctx) => {
325
- const identity = await ctx.auth.getUserIdentity();
326
- if (!identity) {
327
- return null;
269
+ export const deleteAllForUser = action({
270
+ args: {
271
+ table: v.string(),
272
+ userId: v.string(),
273
+ },
274
+ handler: async (ctx, args) => {
275
+ let count = 0;
276
+ let cursor = null;
277
+ let isDone = false;
278
+ do {
279
+ const result = await ctx.runMutation(api.lib.deleteAllForUserPage, {
280
+ table: args.table,
281
+ userId: args.userId,
282
+ paginationOpts: {
283
+ numItems: 500,
284
+ cursor,
285
+ },
286
+ });
287
+ count += result.count;
288
+ cursor =
289
+ result.pageStatus &&
290
+ result.splitCursor &&
291
+ ["SplitRecommended", "SplitRequired"].includes(result.pageStatus)
292
+ ? result.splitCursor
293
+ : result.continueCursor;
294
+ isDone = result.isDone;
295
+ } while (!isDone);
296
+ return count;
297
+ },
298
+ });
299
+ export const getAccountByAccountIdAndProviderId = query({
300
+ args: { accountId: v.string(), providerId: v.string() },
301
+ handler: async (ctx, args) => {
302
+ const doc = await ctx.db
303
+ .query("account")
304
+ .withIndex("providerId_accountId", (q) => q.eq("providerId", args.providerId).eq("accountId", args.accountId))
305
+ .unique();
306
+ if (!doc) {
307
+ return;
328
308
  }
329
- return ctx.db.get(identity.sessionId);
309
+ return transformOutput(doc, "account");
330
310
  },
331
311
  });
332
- // TODO: rewrite functions below here to be dynamic
333
- export const getIn = query({
334
- args: adapterArgsValidator,
312
+ export const updateUserProviderAccounts = mutation({
313
+ args: {
314
+ userId: v.string(),
315
+ providerId: v.string(),
316
+ update: v.object(partial(schema.tables.account.validator.fields)),
317
+ },
335
318
  handler: async (ctx, args) => {
336
- const where = args.where?.[0];
337
- if (!where || where.operator !== "in" || args.where?.length !== 1) {
338
- throw new Error("where must be a single in clause");
319
+ const docs = await ctx.db
320
+ .query("account")
321
+ .withIndex("userId_providerId", (q) => q.eq("userId", args.userId).eq("providerId", args.providerId))
322
+ .collect();
323
+ if (docs.length === 0) {
324
+ return 0;
339
325
  }
340
- return (await asyncMap(where.value, async (value) => {
341
- if (where.field === "id") {
342
- return [await ctx.db.get(value)];
343
- }
344
- const query = ctx.db
345
- .query(args.model)
346
- .withIndex(where.field, (q) => q.eq(where.field, value));
347
- if (args.limit) {
348
- return await query.take(args.limit);
349
- }
350
- return await query.collect();
351
- }))
352
- .flat()
353
- .filter(Boolean);
326
+ await asyncMap(docs, async (doc) => {
327
+ await ctx.db.patch(doc._id, args.update);
328
+ });
329
+ return docs.length;
354
330
  },
355
331
  });
356
- export const deleteIn = mutation({
332
+ export const updateTwoFactor = mutation({
357
333
  args: {
358
- input: v.union(v.object({
359
- table: v.literal("session"),
360
- field: v.literal("token"),
361
- values: v.array(v.string()),
362
- })),
334
+ userId: v.string(),
335
+ update: v.object(partial(schema.tables.twoFactor.validator.fields)),
363
336
  },
364
337
  handler: async (ctx, args) => {
365
- const { table, field, values } = args.input;
366
- const docs = await asyncMap(values, async (value) => {
367
- const doc = await ctx.db
368
- .query(table)
369
- .withIndex(field, (q) => q.eq(field, value))
370
- .unique();
371
- if (!doc) {
372
- return;
373
- }
374
- await ctx.db.delete(doc._id);
375
- return doc;
338
+ const docs = await ctx.db
339
+ .query("twoFactor")
340
+ .withIndex("userId", (q) => q.eq("userId", args.userId))
341
+ .collect();
342
+ if (docs.length === 0) {
343
+ return 0;
344
+ }
345
+ await asyncMap(docs, async (doc) => {
346
+ await ctx.db.patch(doc._id, args.update);
376
347
  });
377
- return docs.filter(Boolean).length;
348
+ return docs.length;
378
349
  },
379
350
  });
380
351
  //# sourceMappingURL=lib.js.map