@neutralauth/internal-auth 0.10.11

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 (147) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +39 -0
  3. package/dist/auth-config.d.ts +43 -0
  4. package/dist/auth-config.d.ts.map +1 -0
  5. package/dist/auth-config.js +43 -0
  6. package/dist/auth-config.js.map +1 -0
  7. package/dist/auth-options.d.ts +3 -0
  8. package/dist/auth-options.d.ts.map +1 -0
  9. package/dist/auth-options.js +40 -0
  10. package/dist/auth-options.js.map +1 -0
  11. package/dist/auth.d.ts +2 -0
  12. package/dist/auth.d.ts.map +1 -0
  13. package/dist/auth.js +4 -0
  14. package/dist/auth.js.map +1 -0
  15. package/dist/client/adapter-utils.d.ts +66 -0
  16. package/dist/client/adapter-utils.d.ts.map +1 -0
  17. package/dist/client/adapter-utils.js +437 -0
  18. package/dist/client/adapter-utils.js.map +1 -0
  19. package/dist/client/adapter.d.ts +14 -0
  20. package/dist/client/adapter.d.ts.map +1 -0
  21. package/dist/client/adapter.js +274 -0
  22. package/dist/client/adapter.js.map +1 -0
  23. package/dist/client/create-api.d.ts +141 -0
  24. package/dist/client/create-api.d.ts.map +1 -0
  25. package/dist/client/create-api.js +205 -0
  26. package/dist/client/create-api.js.map +1 -0
  27. package/dist/client/create-client.d.ts +183 -0
  28. package/dist/client/create-client.d.ts.map +1 -0
  29. package/dist/client/create-client.js +311 -0
  30. package/dist/client/create-client.js.map +1 -0
  31. package/dist/client/create-schema.d.ts +19 -0
  32. package/dist/client/create-schema.d.ts.map +1 -0
  33. package/dist/client/create-schema.js +114 -0
  34. package/dist/client/create-schema.js.map +1 -0
  35. package/dist/client/index.d.ts +7 -0
  36. package/dist/client/index.d.ts.map +1 -0
  37. package/dist/client/index.js +10 -0
  38. package/dist/client/index.js.map +1 -0
  39. package/dist/client/plugins/index.d.ts +3 -0
  40. package/dist/client/plugins/index.d.ts.map +1 -0
  41. package/dist/client/plugins/index.js +3 -0
  42. package/dist/client/plugins/index.js.map +1 -0
  43. package/dist/component/_generated/api.d.ts +36 -0
  44. package/dist/component/_generated/api.d.ts.map +1 -0
  45. package/dist/component/_generated/api.js +31 -0
  46. package/dist/component/_generated/api.js.map +1 -0
  47. package/dist/component/_generated/component.d.ts +787 -0
  48. package/dist/component/_generated/component.d.ts.map +1 -0
  49. package/dist/component/_generated/component.js +11 -0
  50. package/dist/component/_generated/component.js.map +1 -0
  51. package/dist/component/_generated/dataModel.d.ts +46 -0
  52. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  53. package/dist/component/_generated/dataModel.js +11 -0
  54. package/dist/component/_generated/dataModel.js.map +1 -0
  55. package/dist/component/_generated/server.d.ts +121 -0
  56. package/dist/component/_generated/server.d.ts.map +1 -0
  57. package/dist/component/_generated/server.js +78 -0
  58. package/dist/component/_generated/server.js.map +1 -0
  59. package/dist/component/adapter.d.ts +130 -0
  60. package/dist/component/adapter.d.ts.map +1 -0
  61. package/dist/component/adapter.js +5 -0
  62. package/dist/component/adapter.js.map +1 -0
  63. package/dist/component/adapterTest.d.ts +10 -0
  64. package/dist/component/adapterTest.d.ts.map +1 -0
  65. package/dist/component/adapterTest.js +409 -0
  66. package/dist/component/adapterTest.js.map +1 -0
  67. package/dist/component/convex.config.d.ts +3 -0
  68. package/dist/component/convex.config.d.ts.map +1 -0
  69. package/dist/component/convex.config.js +4 -0
  70. package/dist/component/convex.config.js.map +1 -0
  71. package/dist/component/schema.d.ts +474 -0
  72. package/dist/component/schema.d.ts.map +1 -0
  73. package/dist/component/schema.js +139 -0
  74. package/dist/component/schema.js.map +1 -0
  75. package/dist/nextjs/client.d.ts +4 -0
  76. package/dist/nextjs/client.d.ts.map +1 -0
  77. package/dist/nextjs/client.js +37 -0
  78. package/dist/nextjs/client.js.map +1 -0
  79. package/dist/nextjs/index.d.ts +22 -0
  80. package/dist/nextjs/index.d.ts.map +1 -0
  81. package/dist/nextjs/index.js +98 -0
  82. package/dist/nextjs/index.js.map +1 -0
  83. package/dist/plugins/convex/client.d.ts +6 -0
  84. package/dist/plugins/convex/client.d.ts.map +1 -0
  85. package/dist/plugins/convex/client.js +7 -0
  86. package/dist/plugins/convex/client.js.map +1 -0
  87. package/dist/plugins/convex/index.d.ts +322 -0
  88. package/dist/plugins/convex/index.d.ts.map +1 -0
  89. package/dist/plugins/convex/index.js +422 -0
  90. package/dist/plugins/convex/index.js.map +1 -0
  91. package/dist/plugins/cross-domain/client.d.ts +132 -0
  92. package/dist/plugins/cross-domain/client.d.ts.map +1 -0
  93. package/dist/plugins/cross-domain/client.js +192 -0
  94. package/dist/plugins/cross-domain/client.js.map +1 -0
  95. package/dist/plugins/cross-domain/index.d.ts +51 -0
  96. package/dist/plugins/cross-domain/index.d.ts.map +1 -0
  97. package/dist/plugins/cross-domain/index.js +173 -0
  98. package/dist/plugins/cross-domain/index.js.map +1 -0
  99. package/dist/plugins/index.d.ts +3 -0
  100. package/dist/plugins/index.d.ts.map +1 -0
  101. package/dist/plugins/index.js +3 -0
  102. package/dist/plugins/index.js.map +1 -0
  103. package/dist/react/index.d.ts +80 -0
  104. package/dist/react/index.d.ts.map +1 -0
  105. package/dist/react/index.js +190 -0
  106. package/dist/react/index.js.map +1 -0
  107. package/dist/react-start/index.d.ts +13 -0
  108. package/dist/react-start/index.d.ts.map +1 -0
  109. package/dist/react-start/index.js +101 -0
  110. package/dist/react-start/index.js.map +1 -0
  111. package/dist/utils/index.d.ts +33 -0
  112. package/dist/utils/index.d.ts.map +1 -0
  113. package/dist/utils/index.js +91 -0
  114. package/dist/utils/index.js.map +1 -0
  115. package/package.json +208 -0
  116. package/src/auth-config.ts +80 -0
  117. package/src/auth-options.ts +54 -0
  118. package/src/auth.ts +4 -0
  119. package/src/client/adapter-utils.ts +639 -0
  120. package/src/client/adapter.test.ts +83 -0
  121. package/src/client/adapter.ts +363 -0
  122. package/src/client/create-api.ts +339 -0
  123. package/src/client/create-client.ts +452 -0
  124. package/src/client/create-schema.ts +166 -0
  125. package/src/client/index.ts +22 -0
  126. package/src/client/plugins/index.ts +2 -0
  127. package/src/component/_generated/api.ts +52 -0
  128. package/src/component/_generated/component.ts +2008 -0
  129. package/src/component/_generated/dataModel.ts +60 -0
  130. package/src/component/_generated/server.ts +161 -0
  131. package/src/component/adapter.ts +13 -0
  132. package/src/component/adapterTest.ts +505 -0
  133. package/src/component/convex.config.ts +5 -0
  134. package/src/component/schema.ts +142 -0
  135. package/src/nextjs/client.tsx +54 -0
  136. package/src/nextjs/index.ts +152 -0
  137. package/src/plugins/convex/client.ts +9 -0
  138. package/src/plugins/convex/index.ts +596 -0
  139. package/src/plugins/cross-domain/client.test.ts +217 -0
  140. package/src/plugins/cross-domain/client.ts +234 -0
  141. package/src/plugins/cross-domain/index.ts +199 -0
  142. package/src/plugins/index.ts +2 -0
  143. package/src/react/index.tsx +304 -0
  144. package/src/react-start/index.ts +153 -0
  145. package/src/react-start/vite-env.d.ts +2 -0
  146. package/src/test.ts +18 -0
  147. package/src/utils/index.ts +171 -0
@@ -0,0 +1,505 @@
1
+ import { createClient } from "../client/index.js";
2
+ import type { GenericCtx } from "../client/index.js";
3
+ import { api } from "./_generated/api.js";
4
+ import { action } from "./_generated/server.js";
5
+ import type { GenericActionCtx } from "convex/server";
6
+ import type { DataModel } from "./_generated/dataModel.js";
7
+ import type { BetterAuthOptions } from "better-auth";
8
+ import type { EmptyObject } from "convex-helpers";
9
+
10
+ // Hide vitest imports from esbuild, keep them out of the bundle
11
+ import type {
12
+ beforeEach as beforeEachType,
13
+ test as testType,
14
+ expect as expectType,
15
+ } from "vitest";
16
+ import type { runAdapterTest as runAdapterTestType } from "better-auth/adapters/test";
17
+
18
+ const getTestImports = async () => {
19
+ const vitestImportName = "vitest";
20
+ const { beforeEach, test, expect } = await import(vitestImportName);
21
+ const betterAuthAdaptersTestImportName = "better-auth/adapters/test";
22
+ const { runAdapterTest } = await import(betterAuthAdaptersTestImportName);
23
+ return { beforeEach, test, expect, runAdapterTest } as {
24
+ beforeEach: typeof beforeEachType;
25
+ test: typeof testType;
26
+ expect: typeof expectType;
27
+ runAdapterTest: typeof runAdapterTestType;
28
+ };
29
+ };
30
+
31
+ export const getAdapter: (
32
+ ctx: GenericCtx<DataModel>
33
+ ) => Parameters<typeof runAdapterTestType>[0]["getAdapter"] =
34
+ (ctx: GenericCtx<DataModel>) =>
35
+ async (opts?: Omit<BetterAuthOptions, "database">) => {
36
+ const authComponent = createClient<DataModel>(api as any, {
37
+ verbose: false,
38
+ });
39
+ const adapterFactory = authComponent.adapter(ctx);
40
+ const options = {
41
+ ...(opts ?? {}),
42
+ user: {
43
+ ...(opts?.user ?? {}),
44
+ // We don't currently support custom schema for tests, need to find a
45
+ // way to do this.
46
+ fields: undefined,
47
+ },
48
+ };
49
+ return adapterFactory(options);
50
+ };
51
+
52
+ // Tests need to run inside of a Convex function to use the Convex adapter
53
+ export const runTests = action(
54
+ async (
55
+ ctx: GenericActionCtx<DataModel>,
56
+ args: { disableTests: Record<string, boolean> }
57
+ ) => {
58
+ const { runAdapterTest } = await getTestImports();
59
+ runAdapterTest({
60
+ getAdapter: getAdapter(ctx),
61
+ disableTests: args.disableTests,
62
+ });
63
+ }
64
+ );
65
+
66
+ export const runCustomTests = action(
67
+ async (ctx: GenericActionCtx<DataModel>, _args: EmptyObject) => {
68
+ const { beforeEach, test, expect } = await getTestImports();
69
+ runCustomAdapterTests({
70
+ beforeEach,
71
+ test,
72
+ expect,
73
+ getAdapter: getAdapter(ctx),
74
+ });
75
+ }
76
+ );
77
+
78
+ function runCustomAdapterTests({
79
+ beforeEach,
80
+ test,
81
+ expect,
82
+ getAdapter,
83
+ }: {
84
+ getAdapter: Parameters<typeof runAdapterTestType>[0]["getAdapter"];
85
+ beforeEach: typeof beforeEachType;
86
+ test: typeof testType;
87
+ expect: typeof expectType;
88
+ }) {
89
+ beforeEach(async () => {
90
+ const adapter = await getAdapter();
91
+ await adapter.deleteMany({
92
+ model: "user",
93
+ where: [],
94
+ });
95
+ await adapter.deleteMany({
96
+ model: "session",
97
+ where: [],
98
+ });
99
+ });
100
+ test("should handle lone range operators", async () => {
101
+ const adapter = await getAdapter();
102
+ const user = await adapter.create({
103
+ model: "user",
104
+ data: {
105
+ name: "ab",
106
+ email: "a@a.com",
107
+ },
108
+ });
109
+ expect(
110
+ await adapter.findMany({
111
+ model: "user",
112
+ where: [
113
+ {
114
+ field: "name",
115
+ operator: "lt",
116
+ value: "a",
117
+ },
118
+ ],
119
+ })
120
+ ).toEqual([]);
121
+ expect(
122
+ await adapter.findMany({
123
+ model: "user",
124
+ where: [
125
+ {
126
+ field: "name",
127
+ operator: "lte",
128
+ value: "a",
129
+ },
130
+ ],
131
+ })
132
+ ).toEqual([]);
133
+ expect(
134
+ await adapter.findMany({
135
+ model: "user",
136
+ where: [
137
+ {
138
+ field: "name",
139
+ operator: "gt",
140
+ value: "a",
141
+ },
142
+ ],
143
+ })
144
+ ).toEqual([user]);
145
+ expect(
146
+ await adapter.findMany({
147
+ model: "user",
148
+ where: [
149
+ {
150
+ field: "name",
151
+ operator: "gte",
152
+ value: "ab",
153
+ },
154
+ ],
155
+ })
156
+ ).toEqual([user]);
157
+ });
158
+
159
+ test("should handle compound indexes that include id field", async () => {
160
+ const adapter = await getAdapter();
161
+ const user = await adapter.create({
162
+ model: "user",
163
+ data: {
164
+ name: "foo",
165
+ email: "foo@bar.com",
166
+ },
167
+ });
168
+ expect(
169
+ await adapter.findOne({
170
+ model: "user",
171
+ where: [
172
+ {
173
+ field: "id",
174
+ value: user.id,
175
+ },
176
+ {
177
+ field: "name",
178
+ value: "wrong name",
179
+ },
180
+ ],
181
+ })
182
+ ).toEqual(null);
183
+ expect(
184
+ await adapter.findOne({
185
+ model: "user",
186
+ where: [
187
+ {
188
+ field: "id",
189
+ value: user.id,
190
+ },
191
+ {
192
+ field: "name",
193
+ value: "foo",
194
+ },
195
+ ],
196
+ })
197
+ ).toEqual(user);
198
+ expect(
199
+ await adapter.findOne({
200
+ model: "user",
201
+ where: [
202
+ {
203
+ field: "id",
204
+ value: user.id,
205
+ },
206
+ {
207
+ field: "name",
208
+ value: "foo",
209
+ operator: "lt",
210
+ },
211
+ ],
212
+ })
213
+ ).toEqual(null);
214
+ expect(
215
+ await adapter.findOne({
216
+ model: "user",
217
+ where: [
218
+ {
219
+ field: "id",
220
+ value: user.id,
221
+ },
222
+ {
223
+ field: "name",
224
+ value: "foo",
225
+ operator: "lte",
226
+ },
227
+ ],
228
+ })
229
+ ).toEqual(user);
230
+ expect(
231
+ await adapter.findOne({
232
+ model: "user",
233
+ where: [
234
+ {
235
+ field: "id",
236
+ value: user.id,
237
+ },
238
+ {
239
+ field: "name",
240
+ value: "foo",
241
+ operator: "gt",
242
+ },
243
+ ],
244
+ })
245
+ ).toEqual(null);
246
+ expect(
247
+ await adapter.findOne({
248
+ model: "user",
249
+ where: [
250
+ {
251
+ field: "id",
252
+ value: user.id,
253
+ },
254
+ {
255
+ field: "name",
256
+ value: "foo",
257
+ operator: "gte",
258
+ },
259
+ ],
260
+ })
261
+ ).toEqual(user);
262
+ expect(
263
+ await adapter.findOne({
264
+ model: "user",
265
+ where: [
266
+ {
267
+ field: "id",
268
+ value: user.id,
269
+ },
270
+ {
271
+ field: "name",
272
+ operator: "in",
273
+ value: ["wrong", "name"],
274
+ },
275
+ ],
276
+ })
277
+ ).toEqual(null);
278
+ expect(
279
+ await adapter.findOne({
280
+ model: "user",
281
+ where: [
282
+ {
283
+ field: "id",
284
+ value: user.id,
285
+ },
286
+ {
287
+ field: "name",
288
+ operator: "in",
289
+ value: ["foo"],
290
+ },
291
+ ],
292
+ })
293
+ ).toEqual(user);
294
+ });
295
+ test("should automatically paginate", async () => {
296
+ const adapter = await getAdapter();
297
+ for (let i = 0; i < 300; i++) {
298
+ await adapter.create({
299
+ model: "user",
300
+ data: {
301
+ name: `foo${i}`,
302
+ email: `foo${i}@bar.com`,
303
+ },
304
+ });
305
+ }
306
+ // Better Auth defaults to a limit of 100
307
+ expect(
308
+ await adapter.findMany({
309
+ model: "user",
310
+ })
311
+ ).toHaveLength(100);
312
+
313
+ // Pagination has a hardcoded numItems max of 200, this tests that it can handle
314
+ // specified limits beyond that
315
+ expect(
316
+ await adapter.findMany({
317
+ model: "user",
318
+ limit: 250,
319
+ })
320
+ ).toHaveLength(250);
321
+ expect(
322
+ await adapter.findMany({
323
+ model: "user",
324
+ limit: 350,
325
+ })
326
+ ).toHaveLength(300);
327
+ });
328
+ test("should handle OR where clauses", async () => {
329
+ const adapter = await getAdapter();
330
+ const user = await adapter.create({
331
+ model: "user",
332
+ data: {
333
+ name: "foo",
334
+ email: "foo@bar.com",
335
+ },
336
+ });
337
+ expect(
338
+ await adapter.findOne({
339
+ model: "user",
340
+ where: [
341
+ { field: "name", value: "bar", connector: "OR" },
342
+ { field: "name", value: "foo", connector: "OR" },
343
+ ],
344
+ })
345
+ ).toEqual(user);
346
+ });
347
+ test("should handle OR where clauses with sortBy", async () => {
348
+ const adapter = await getAdapter();
349
+ const fooUser = await adapter.create({
350
+ model: "user",
351
+ data: {
352
+ name: "foo",
353
+ email: "foo@bar.com",
354
+ },
355
+ });
356
+ const barUser = await adapter.create({
357
+ model: "user",
358
+ data: {
359
+ name: "bar",
360
+ email: "bar@bar.com",
361
+ },
362
+ });
363
+ await adapter.create({
364
+ model: "user",
365
+ data: {
366
+ name: "baz",
367
+ email: "baz@bar.com",
368
+ },
369
+ });
370
+ expect(
371
+ await adapter.findMany({
372
+ model: "user",
373
+ where: [
374
+ { field: "name", value: "bar", connector: "OR" },
375
+ { field: "name", value: "foo", connector: "OR" },
376
+ ],
377
+ sortBy: { field: "name", direction: "asc" },
378
+ })
379
+ ).toEqual([barUser, fooUser]);
380
+ expect(
381
+ await adapter.findMany({
382
+ model: "user",
383
+ where: [
384
+ { field: "name", value: "bar", connector: "OR" },
385
+ { field: "name", value: "foo", connector: "OR" },
386
+ ],
387
+ sortBy: { field: "name", direction: "desc" },
388
+ })
389
+ ).toEqual([fooUser, barUser]);
390
+ });
391
+ test("should handle count", async () => {
392
+ const adapter = await getAdapter();
393
+ await adapter.create({
394
+ model: "user",
395
+ data: {
396
+ name: "foo",
397
+ email: "foo@bar.com",
398
+ },
399
+ });
400
+ await adapter.create({
401
+ model: "user",
402
+ data: {
403
+ name: "bar",
404
+ email: "bar@bar.com",
405
+ },
406
+ });
407
+ expect(
408
+ await adapter.count({
409
+ model: "user",
410
+ where: [{ field: "name", value: "foo" }],
411
+ })
412
+ ).toEqual(1);
413
+ });
414
+ test("should handle queries with no index", async () => {
415
+ const adapter = await getAdapter();
416
+ const user = await adapter.create({
417
+ model: "user",
418
+ data: {
419
+ name: "foo",
420
+ email: "foo@bar.com",
421
+ emailVerified: true,
422
+ },
423
+ });
424
+ expect(
425
+ await adapter.findOne({
426
+ model: "user",
427
+ where: [{ field: "emailVerified", value: true }],
428
+ })
429
+ ).toEqual(user);
430
+ expect(
431
+ await adapter.findOne({
432
+ model: "user",
433
+ where: [{ field: "emailVerified", value: false }],
434
+ })
435
+ ).toEqual(null);
436
+ });
437
+
438
+ test("should handle compound operator on non-unique field without an index", async () => {
439
+ const adapter = await getAdapter();
440
+ await adapter.create({
441
+ model: "account",
442
+ data: {
443
+ accountId: "foo",
444
+ providerId: "bar",
445
+ userId: "baz",
446
+ accessTokenExpiresAt: null,
447
+ createdAt: Date.now(),
448
+ updatedAt: Date.now(),
449
+ },
450
+ });
451
+ expect(
452
+ await adapter.findOne({
453
+ model: "account",
454
+ where: [
455
+ {
456
+ operator: "lt",
457
+ connector: "AND",
458
+ field: "accessTokenExpiresAt",
459
+ value: Date.now(),
460
+ },
461
+ {
462
+ operator: "ne",
463
+ connector: "AND",
464
+ field: "accessTokenExpiresAt",
465
+ value: null,
466
+ },
467
+ ],
468
+ })
469
+ ).toEqual(null);
470
+ });
471
+
472
+ test("should fail to create a record with a unique field that already exists", async () => {
473
+ const adapter = await getAdapter();
474
+ await adapter.create({
475
+ model: "user",
476
+ data: { name: "foo", email: "foo@bar.com" },
477
+ });
478
+ await expect(
479
+ adapter.create({
480
+ model: "user",
481
+ data: { name: "foo", email: "foo@bar.com" },
482
+ })
483
+ ).rejects.toThrow("user email already exists");
484
+ });
485
+
486
+ test("should be able to compare against a date", async () => {
487
+ const adapter = await getAdapter();
488
+ const now = new Date().toISOString();
489
+ const user = await adapter.create({
490
+ model: "user",
491
+ data: {
492
+ name: "foo",
493
+ email: "foo@bar.com",
494
+ createdAt: now,
495
+ },
496
+ });
497
+ expect(
498
+ await adapter.findOne({
499
+ model: "user",
500
+ where: [{ field: "createdAt", value: now }],
501
+ })
502
+ ).toEqual(user);
503
+ expect(user.createdAt).toBeInstanceOf(Date);
504
+ });
505
+ }
@@ -0,0 +1,5 @@
1
+ import { defineComponent } from "convex/server";
2
+
3
+ const component = defineComponent("betterAuth");
4
+
5
+ export default component;
@@ -0,0 +1,142 @@
1
+ // This file is auto-generated. Do not edit this file manually.
2
+ // To regenerate the schema, run:
3
+ // `npx @better-auth/cli generate --output src/component/schema.ts -y`
4
+
5
+ import { defineSchema, defineTable } from "convex/server";
6
+ import { v } from "convex/values";
7
+
8
+ export const tables = {
9
+ user: defineTable({
10
+ name: v.string(),
11
+ email: v.string(),
12
+ emailVerified: v.boolean(),
13
+ image: v.optional(v.union(v.null(), v.string())),
14
+ createdAt: v.number(),
15
+ updatedAt: v.number(),
16
+ twoFactorEnabled: v.optional(v.union(v.null(), v.boolean())),
17
+ isAnonymous: v.optional(v.union(v.null(), v.boolean())),
18
+ username: v.optional(v.union(v.null(), v.string())),
19
+ displayUsername: v.optional(v.union(v.null(), v.string())),
20
+ phoneNumber: v.optional(v.union(v.null(), v.string())),
21
+ phoneNumberVerified: v.optional(v.union(v.null(), v.boolean())),
22
+ userId: v.optional(v.union(v.null(), v.string())),
23
+ })
24
+ .index("email_name", ["email", "name"])
25
+ .index("name", ["name"])
26
+ .index("userId", ["userId"])
27
+ .index("username", ["username"])
28
+ .index("phoneNumber", ["phoneNumber"]),
29
+ session: defineTable({
30
+ expiresAt: v.number(),
31
+ token: v.string(),
32
+ createdAt: v.number(),
33
+ updatedAt: v.number(),
34
+ ipAddress: v.optional(v.union(v.null(), v.string())),
35
+ userAgent: v.optional(v.union(v.null(), v.string())),
36
+ userId: v.string(),
37
+ })
38
+ .index("expiresAt", ["expiresAt"])
39
+ .index("expiresAt_userId", ["expiresAt", "userId"])
40
+ .index("token", ["token"])
41
+ .index("userId", ["userId"]),
42
+ account: defineTable({
43
+ accountId: v.string(),
44
+ providerId: v.string(),
45
+ userId: v.string(),
46
+ accessToken: v.optional(v.union(v.null(), v.string())),
47
+ refreshToken: v.optional(v.union(v.null(), v.string())),
48
+ idToken: v.optional(v.union(v.null(), v.string())),
49
+ accessTokenExpiresAt: v.optional(v.union(v.null(), v.number())),
50
+ refreshTokenExpiresAt: v.optional(v.union(v.null(), v.number())),
51
+ scope: v.optional(v.union(v.null(), v.string())),
52
+ password: v.optional(v.union(v.null(), v.string())),
53
+ createdAt: v.number(),
54
+ updatedAt: v.number(),
55
+ })
56
+ .index("accountId", ["accountId"])
57
+ .index("accountId_providerId", ["accountId", "providerId"])
58
+ .index("providerId_userId", ["providerId", "userId"])
59
+ .index("userId", ["userId"]),
60
+ verification: defineTable({
61
+ identifier: v.string(),
62
+ value: v.string(),
63
+ expiresAt: v.number(),
64
+ createdAt: v.number(),
65
+ updatedAt: v.number(),
66
+ })
67
+ .index("expiresAt", ["expiresAt"])
68
+ .index("identifier", ["identifier"]),
69
+ twoFactor: defineTable({
70
+ secret: v.string(),
71
+ backupCodes: v.string(),
72
+ userId: v.string(),
73
+ }).index("userId", ["userId"]),
74
+ passkey: defineTable({
75
+ name: v.optional(v.union(v.null(), v.string())),
76
+ publicKey: v.string(),
77
+ userId: v.string(),
78
+ credentialID: v.string(),
79
+ counter: v.number(),
80
+ deviceType: v.string(),
81
+ backedUp: v.boolean(),
82
+ transports: v.optional(v.union(v.null(), v.string())),
83
+ createdAt: v.optional(v.union(v.null(), v.number())),
84
+ aaguid: v.optional(v.union(v.null(), v.string())),
85
+ })
86
+ .index("credentialID", ["credentialID"])
87
+ .index("userId", ["userId"]),
88
+ oauthApplication: defineTable({
89
+ name: v.optional(v.union(v.null(), v.string())),
90
+ icon: v.optional(v.union(v.null(), v.string())),
91
+ metadata: v.optional(v.union(v.null(), v.string())),
92
+ clientId: v.optional(v.union(v.null(), v.string())),
93
+ clientSecret: v.optional(v.union(v.null(), v.string())),
94
+ redirectURLs: v.optional(v.union(v.null(), v.string())),
95
+ type: v.optional(v.union(v.null(), v.string())),
96
+ disabled: v.optional(v.union(v.null(), v.boolean())),
97
+ userId: v.optional(v.union(v.null(), v.string())),
98
+ createdAt: v.optional(v.union(v.null(), v.number())),
99
+ updatedAt: v.optional(v.union(v.null(), v.number())),
100
+ })
101
+ .index("clientId", ["clientId"])
102
+ .index("userId", ["userId"]),
103
+ oauthAccessToken: defineTable({
104
+ accessToken: v.optional(v.union(v.null(), v.string())),
105
+ refreshToken: v.optional(v.union(v.null(), v.string())),
106
+ accessTokenExpiresAt: v.optional(v.union(v.null(), v.number())),
107
+ refreshTokenExpiresAt: v.optional(v.union(v.null(), v.number())),
108
+ clientId: v.optional(v.union(v.null(), v.string())),
109
+ userId: v.optional(v.union(v.null(), v.string())),
110
+ scopes: v.optional(v.union(v.null(), v.string())),
111
+ createdAt: v.optional(v.union(v.null(), v.number())),
112
+ updatedAt: v.optional(v.union(v.null(), v.number())),
113
+ })
114
+ .index("accessToken", ["accessToken"])
115
+ .index("refreshToken", ["refreshToken"])
116
+ .index("clientId", ["clientId"])
117
+ .index("userId", ["userId"]),
118
+ oauthConsent: defineTable({
119
+ clientId: v.optional(v.union(v.null(), v.string())),
120
+ userId: v.optional(v.union(v.null(), v.string())),
121
+ scopes: v.optional(v.union(v.null(), v.string())),
122
+ createdAt: v.optional(v.union(v.null(), v.number())),
123
+ updatedAt: v.optional(v.union(v.null(), v.number())),
124
+ consentGiven: v.optional(v.union(v.null(), v.boolean())),
125
+ })
126
+ .index("clientId_userId", ["clientId", "userId"])
127
+ .index("userId", ["userId"]),
128
+ jwks: defineTable({
129
+ publicKey: v.string(),
130
+ privateKey: v.string(),
131
+ createdAt: v.number(),
132
+ }),
133
+ rateLimit: defineTable({
134
+ key: v.optional(v.union(v.null(), v.string())),
135
+ count: v.optional(v.union(v.null(), v.number())),
136
+ lastRequest: v.optional(v.union(v.null(), v.number())),
137
+ }).index("key", ["key"]),
138
+ };
139
+
140
+ const schema = defineSchema(tables);
141
+
142
+ export default schema;
@@ -0,0 +1,54 @@
1
+ import { useConvexAuth, useQuery } from "convex/react";
2
+ import type { Preloaded } from "convex/react";
3
+ import { makeFunctionReference } from "convex/server";
4
+ import type { FunctionReference } from "convex/server";
5
+ import { jsonToConvex } from "convex/values";
6
+ import { useEffect, useMemo, useState } from "react";
7
+
8
+ const useConvexPreloadedQuery = <Query extends FunctionReference<"query">>(
9
+ preloadedQuery: Preloaded<Query>,
10
+ { requireAuth = true }: { requireAuth?: boolean } = {}
11
+ ): Query["_returnType"] => {
12
+ const { isLoading, isAuthenticated } = useConvexAuth();
13
+ const [preloadExpired, setPreloadExpired] = useState(false);
14
+ useEffect(() => {
15
+ if (requireAuth && !isLoading && !isAuthenticated) {
16
+ setPreloadExpired(true);
17
+ }
18
+ }, [requireAuth, isLoading, isAuthenticated]);
19
+ const args = useMemo(
20
+ () => jsonToConvex(preloadedQuery._argsJSON),
21
+ [preloadedQuery._argsJSON]
22
+ ) as Query["_args"];
23
+ const preloadedResult = useMemo(
24
+ () => jsonToConvex(preloadedQuery._valueJSON),
25
+ [preloadedQuery._valueJSON]
26
+ );
27
+ const result = useQuery(
28
+ makeFunctionReference(preloadedQuery._name) as Query,
29
+ requireAuth && !isAuthenticated ? ("skip" as const) : args
30
+ );
31
+ useEffect(() => {
32
+ if (result !== undefined) {
33
+ setPreloadExpired(true);
34
+ }
35
+ }, [result]);
36
+ if (requireAuth) {
37
+ return preloadExpired ? result : preloadedResult;
38
+ }
39
+ return result === undefined ? preloadedResult : result;
40
+ };
41
+
42
+ export const usePreloadedAuthQuery = <Query extends FunctionReference<"query">>(
43
+ preloadedQuery: Preloaded<Query>
44
+ ): Query["_returnType"] | null => {
45
+ const { isLoading } = useConvexAuth();
46
+ const latestData = useConvexPreloadedQuery(preloadedQuery);
47
+ const [data, setData] = useState(latestData);
48
+ useEffect(() => {
49
+ if (!isLoading) {
50
+ setData(latestData);
51
+ }
52
+ }, [latestData, isLoading]);
53
+ return data;
54
+ };