better-convex 0.5.7 → 0.6.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 (38) hide show
  1. package/dist/auth/index.d.ts +54 -38
  2. package/dist/auth/index.js +578 -114
  3. package/dist/auth-client/index.d.ts +2 -1
  4. package/dist/auth-client/index.js +2 -1
  5. package/dist/auth-config/index.d.ts +45 -0
  6. package/dist/auth-config/index.js +24 -0
  7. package/dist/auth-nextjs/index.d.ts +2 -2
  8. package/dist/auth-nextjs/index.js +2 -2
  9. package/dist/{caller-factory-CeZ07fQ2.js → caller-factory-B1FvYSKr.js} +18 -12
  10. package/dist/cli.mjs +246 -0
  11. package/dist/{codegen-CSeApTME.cjs → codegen-DkpPBVPn.mjs} +28 -60
  12. package/dist/context-utils-DSuX99Da.d.ts +17 -0
  13. package/dist/{create-schema-B4CUvqik.js → create-schema-DhWXOhnU.js} +1 -1
  14. package/dist/create-schema-orm-DplxTtYj.js +145 -0
  15. package/dist/crpc/index.d.ts +3 -3
  16. package/dist/crpc/index.js +4 -3
  17. package/dist/customFunctions-C1okqCzL.js +377 -0
  18. package/dist/{http-types-BrMbHGYR.d.ts → http-types-BRLY10NX.d.ts} +70 -20
  19. package/dist/index-BQkhP2ny.d.ts +1326 -0
  20. package/dist/orm/index.d.ts +2977 -0
  21. package/dist/orm/index.js +3 -0
  22. package/dist/orm-Banm-XXb.js +8812 -0
  23. package/dist/react/index.d.ts +46 -9
  24. package/dist/react/index.js +362 -123
  25. package/dist/rsc/index.d.ts +7 -4
  26. package/dist/rsc/index.js +10 -7
  27. package/dist/server/index.d.ts +4 -617
  28. package/dist/server/index.js +914 -50
  29. package/dist/transformer-CTNSPjwp.js +194 -0
  30. package/dist/types-jftzhhuc.d.ts +42 -0
  31. package/dist/{types-DrFf50wo.d.ts → types-o-5rYcTr.d.ts} +1 -1
  32. package/dist/watcher.mjs +41 -0
  33. package/package.json +10 -12
  34. package/dist/cli.cjs +0 -215
  35. package/dist/watcher.cjs +0 -29
  36. /package/dist/{error-BPjr9_gg.js → error-BZUhlhYz.js} +0 -0
  37. /package/dist/{meta-utils-DS5fA5GB.js → meta-utils-DCpLSBWB.js} +0 -0
  38. /package/dist/{query-options-Dbyr-NY1.js → query-options-BL1Q0X7q.js} +0 -0
@@ -1,13 +1,12 @@
1
- import { isRunMutationCtx } from "@convex-dev/better-auth/utils";
1
+ import { a as partial, h as asyncMap, l as isQueryCtx, n as customCtx, r as customMutation, u as isRunMutationCtx } from "../customFunctions-C1okqCzL.js";
2
+ import { C as stream, D as unsetToken, L as eq, S as mergedStream } from "../orm-Banm-XXb.js";
3
+ import { convex } from "@convex-dev/better-auth/plugins";
2
4
  import { createAdapterFactory } from "better-auth/adapters";
3
5
  import { getAuthTables } from "better-auth/db";
4
- import { createFunctionHandle, internalActionGeneric, internalMutationGeneric, internalQueryGeneric, paginationOptsValidator } from "convex/server";
5
- import { asyncMap } from "convex-helpers";
6
- import { prop, sortBy, unique } from "remeda";
6
+ import { ROUTABLE_HTTP_METHODS, createFunctionHandle, httpActionGeneric, httpRouter, internalActionGeneric, internalMutationGeneric, internalQueryGeneric, paginationOptsValidator } from "convex/server";
7
+ import { prop, sortBy, uniqueBy } from "remeda";
7
8
  import { v } from "convex/values";
8
- import { partial } from "convex-helpers/validators";
9
9
  import { stripIndent } from "common-tags";
10
- import { mergedStream, stream } from "convex-helpers/server/stream";
11
10
 
12
11
  //#region src/auth/adapter-utils.ts
13
12
  const adapterWhereValidator = v.object({
@@ -209,9 +208,13 @@ const paginate = async (ctx, schema, betterAuthSchema, args) => {
209
208
  page: []
210
209
  };
211
210
  }
211
+ const paginationLimit = args.paginationOpts.numItems ?? args.limit ?? 200;
212
+ const paginationMaxScan = Math.max(args.paginationOpts.maximumRowsRead ?? 0, paginationLimit + 1, 200);
212
213
  const paginationOpts = {
213
- ...args.paginationOpts,
214
- maximumRowsRead: Math.max((args.paginationOpts.numItems ?? 0) + 1, 200)
214
+ cursor: args.paginationOpts.cursor,
215
+ endCursor: args.paginationOpts.endCursor,
216
+ limit: paginationLimit,
217
+ maxScan: paginationMaxScan
215
218
  };
216
219
  const inWhere = args.where?.find((w) => w.operator === "in");
217
220
  if (inWhere) {
@@ -281,134 +284,224 @@ const whereValidator = (schema, tableName) => v.object({
281
284
  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("not_in"), v.literal("ne"), v.literal("contains"), v.literal("starts_with"), v.literal("ends_with"))),
282
285
  value: v.union(v.string(), v.number(), v.boolean(), v.array(v.string()), v.array(v.number()), v.null())
283
286
  });
287
+ const resolveSchemaTableName = (schema, betterAuthSchema, model) => {
288
+ if (schema.tables[model]) return model;
289
+ const modelConfig = betterAuthSchema?.[model];
290
+ if (modelConfig?.modelName && schema.tables[modelConfig.modelName]) return modelConfig.modelName;
291
+ for (const [key, value] of Object.entries(betterAuthSchema ?? {})) {
292
+ if (value?.modelName !== model) continue;
293
+ if (schema.tables[key]) return key;
294
+ if (schema.tables[value.modelName]) return value.modelName;
295
+ }
296
+ };
297
+ const resolveOrmTable = (ctx, schema, betterAuthSchema, model) => {
298
+ if (!ctx?.orm || typeof ctx.orm.insert !== "function" || typeof ctx.orm.update !== "function" || typeof ctx.orm.delete !== "function") return;
299
+ const tableName = resolveSchemaTableName(schema, betterAuthSchema, model);
300
+ if (!tableName) return;
301
+ const table = schema.tables[tableName];
302
+ if (!table || !table._id) return;
303
+ return {
304
+ table,
305
+ tableName
306
+ };
307
+ };
308
+ const normalizeUpdateForOrm = (update) => Object.fromEntries(Object.entries(update).map(([key, value]) => [key, value === void 0 ? unsetToken : value]));
309
+ const ormInsert = async (ctx, table, data) => (await ctx.orm.insert(table).values(data).returning())[0];
310
+ const ormUpdate = async (ctx, table, id, update) => (await ctx.orm.update(table).set(normalizeUpdateForOrm(update)).returning().where(eq(table._id, id)))[0];
311
+ const ormDelete = async (ctx, table, id) => {
312
+ await ctx.orm.delete(table).where(eq(table._id, id));
313
+ };
314
+ const withBothIdFields = (doc) => {
315
+ const existingUnderscoreId = doc._id;
316
+ const existingId = doc.id;
317
+ const id = existingUnderscoreId ?? existingId;
318
+ if (!id) return doc;
319
+ return {
320
+ ...doc,
321
+ _id: existingUnderscoreId ?? id,
322
+ id: existingId ?? id
323
+ };
324
+ };
325
+ const isPlainObject = (value) => !!value && typeof value === "object" && !Array.isArray(value);
326
+ const serializeDatesForConvex = (value) => {
327
+ if (value instanceof Date) return value.getTime();
328
+ if (Array.isArray(value)) {
329
+ let result;
330
+ for (let index = 0; index < value.length; index += 1) {
331
+ const entry = value[index];
332
+ const serialized = serializeDatesForConvex(entry);
333
+ if (serialized !== entry) {
334
+ if (!result) result = value.slice();
335
+ result[index] = serialized;
336
+ }
337
+ }
338
+ return result ?? value;
339
+ }
340
+ if (!isPlainObject(value)) return value;
341
+ let serialized;
342
+ for (const key in value) {
343
+ if (!Object.hasOwn(value, key)) continue;
344
+ const nested = value[key];
345
+ const encoded = serializeDatesForConvex(nested);
346
+ if (encoded !== nested) {
347
+ if (!serialized) serialized = { ...value };
348
+ serialized[key] = encoded;
349
+ }
350
+ }
351
+ return serialized ?? value;
352
+ };
353
+ const toConvexSafe = (value) => serializeDatesForConvex(value);
284
354
  const createHandler = async (ctx, args, schema, betterAuthSchema) => {
285
355
  let data = args.input.data;
286
356
  if (!args.skipBeforeHooks && args.beforeCreateHandle) {
287
- const transformedData = await ctx.runMutation(args.beforeCreateHandle, {
357
+ const transformedData = await ctx.runMutation(args.beforeCreateHandle, serializeDatesForConvex({
288
358
  data,
289
359
  model: args.input.model
290
- });
360
+ }));
291
361
  if (transformedData !== void 0) data = transformedData;
292
362
  }
293
363
  await checkUniqueFields(ctx, schema, betterAuthSchema, args.input.model, data);
294
- const id = await ctx.db.insert(args.input.model, data);
295
- const doc = await ctx.db.get(id);
364
+ const ormTable = resolveOrmTable(ctx, schema, betterAuthSchema, args.input.model);
365
+ const doc = ormTable ? await ormInsert(ctx, ormTable.table, data) : await (async () => {
366
+ const id = await ctx.db.insert(args.input.model, data);
367
+ return ctx.db.get(id);
368
+ })();
296
369
  if (!doc) throw new Error(`Failed to create ${args.input.model}`);
297
- const result = selectFields(doc, args.select);
298
- if (args.onCreateHandle) await ctx.runMutation(args.onCreateHandle, {
299
- doc,
300
- model: args.input.model
301
- });
302
- return result;
370
+ const normalizedDoc = ormTable ? withBothIdFields(doc) : doc;
371
+ const result = await selectFields(normalizedDoc, args.select);
372
+ if (args.onCreateHandle) {
373
+ const hookDoc = normalizedDoc;
374
+ await ctx.runMutation(args.onCreateHandle, serializeDatesForConvex({
375
+ doc: hookDoc,
376
+ model: args.input.model
377
+ }));
378
+ }
379
+ return toConvexSafe(result);
303
380
  };
304
- const findOneHandler = async (ctx, args, schema, betterAuthSchema) => await listOne(ctx, schema, betterAuthSchema, args);
305
- const findManyHandler = async (ctx, args, schema, betterAuthSchema) => await paginate(ctx, schema, betterAuthSchema, args);
381
+ const findOneHandler = async (ctx, args, schema, betterAuthSchema) => toConvexSafe(await listOne(ctx, schema, betterAuthSchema, args));
382
+ const findManyHandler = async (ctx, args, schema, betterAuthSchema) => toConvexSafe(await paginate(ctx, schema, betterAuthSchema, args));
306
383
  const updateOneHandler = async (ctx, args, schema, betterAuthSchema) => {
307
384
  const doc = await listOne(ctx, schema, betterAuthSchema, args.input);
308
385
  if (!doc) throw new Error(`Failed to update ${args.input.model}`);
309
386
  let update = args.input.update;
310
387
  if (args.beforeUpdateHandle) {
311
- const transformedUpdate = await ctx.runMutation(args.beforeUpdateHandle, {
388
+ const transformedUpdate = await ctx.runMutation(args.beforeUpdateHandle, serializeDatesForConvex({
312
389
  doc,
313
390
  model: args.input.model,
314
391
  update
315
- });
392
+ }));
316
393
  if (transformedUpdate !== void 0) update = transformedUpdate;
317
394
  }
318
395
  await checkUniqueFields(ctx, schema, betterAuthSchema, args.input.model, update, doc);
319
- await ctx.db.patch(doc._id, update);
320
- const updatedDoc = await ctx.db.get(doc._id);
396
+ const ormTable = resolveOrmTable(ctx, schema, betterAuthSchema, args.input.model);
397
+ const updatedDoc = ormTable ? await ormUpdate(ctx, ormTable.table, doc._id, update) : await (async () => {
398
+ await ctx.db.patch(doc._id, update);
399
+ return ctx.db.get(doc._id);
400
+ })();
321
401
  if (!updatedDoc) throw new Error(`Failed to update ${args.input.model}`);
322
- if (args.onUpdateHandle) await ctx.runMutation(args.onUpdateHandle, {
323
- model: args.input.model,
324
- newDoc: updatedDoc,
325
- oldDoc: doc
326
- });
327
- return updatedDoc;
402
+ const normalizedUpdatedDoc = ormTable ? withBothIdFields(updatedDoc) : updatedDoc;
403
+ if (args.onUpdateHandle) {
404
+ const hookNewDoc = normalizedUpdatedDoc;
405
+ await ctx.runMutation(args.onUpdateHandle, serializeDatesForConvex({
406
+ model: args.input.model,
407
+ newDoc: hookNewDoc,
408
+ oldDoc: doc
409
+ }));
410
+ }
411
+ return toConvexSafe(normalizedUpdatedDoc);
328
412
  };
329
413
  const updateManyHandler = async (ctx, args, schema, betterAuthSchema) => {
330
414
  const { page, ...result } = await paginate(ctx, schema, betterAuthSchema, {
331
415
  ...args.input,
332
416
  paginationOpts: args.paginationOpts
333
417
  });
418
+ const ormTable = resolveOrmTable(ctx, schema, betterAuthSchema, args.input.model);
334
419
  if (args.input.update) {
335
420
  if (hasUniqueFields(betterAuthSchema, args.input.model, args.input.update ?? {}) && page.length > 1) throw new Error(`Attempted to set unique fields in multiple documents in ${args.input.model} with the same value. Fields: ${Object.keys(args.input.update ?? {}).join(", ")}`);
336
421
  await asyncMap(page, async (doc) => {
337
422
  let update = args.input.update;
338
423
  if (args.beforeUpdateHandle) {
339
- const transformedUpdate = await ctx.runMutation(args.beforeUpdateHandle, {
424
+ const transformedUpdate = await ctx.runMutation(args.beforeUpdateHandle, serializeDatesForConvex({
340
425
  doc,
341
426
  model: args.input.model,
342
427
  update
343
- });
428
+ }));
344
429
  if (transformedUpdate !== void 0) update = transformedUpdate;
345
430
  }
346
431
  await checkUniqueFields(ctx, schema, betterAuthSchema, args.input.model, update ?? {}, doc);
347
- await ctx.db.patch(doc._id, update);
432
+ const newDoc = ormTable ? await ormUpdate(ctx, ormTable.table, doc._id, update ?? {}) : await (async () => {
433
+ await ctx.db.patch(doc._id, update);
434
+ return ctx.db.get(doc._id);
435
+ })();
348
436
  if (args.onUpdateHandle) {
349
- const newDoc = await ctx.db.get(doc._id);
350
- await ctx.runMutation(args.onUpdateHandle, {
437
+ const hookNewDoc = ormTable ? withBothIdFields(newDoc) : newDoc;
438
+ await ctx.runMutation(args.onUpdateHandle, serializeDatesForConvex({
351
439
  model: args.input.model,
352
- newDoc,
440
+ newDoc: hookNewDoc,
353
441
  oldDoc: doc
354
- });
442
+ }));
355
443
  }
356
444
  });
357
445
  }
358
- return {
446
+ return toConvexSafe({
359
447
  ...result,
360
448
  count: page.length,
361
449
  ids: page.map((doc) => doc._id)
362
- };
450
+ });
363
451
  };
364
452
  const deleteOneHandler = async (ctx, args, schema, betterAuthSchema) => {
365
453
  const doc = await listOne(ctx, schema, betterAuthSchema, args.input);
366
454
  if (!doc) return;
367
455
  let hookDoc = doc;
368
456
  if (!args.skipBeforeHooks && args.beforeDeleteHandle) {
369
- const transformedDoc = await ctx.runMutation(args.beforeDeleteHandle, {
457
+ const transformedDoc = await ctx.runMutation(args.beforeDeleteHandle, serializeDatesForConvex({
370
458
  doc,
371
459
  model: args.input.model
372
- });
460
+ }));
373
461
  if (transformedDoc !== void 0) hookDoc = transformedDoc;
374
462
  }
375
- await ctx.db.delete(doc._id);
376
- if (args.onDeleteHandle) await ctx.runMutation(args.onDeleteHandle, {
463
+ const ormTable = resolveOrmTable(ctx, schema, betterAuthSchema, args.input.model);
464
+ if (ormTable) await ormDelete(ctx, ormTable.table, doc._id);
465
+ else await ctx.db.delete(doc._id);
466
+ if (args.onDeleteHandle) await ctx.runMutation(args.onDeleteHandle, serializeDatesForConvex({
377
467
  doc: hookDoc,
378
468
  model: args.input.model
379
- });
380
- return hookDoc;
469
+ }));
470
+ return toConvexSafe(hookDoc);
381
471
  };
382
472
  const deleteManyHandler = async (ctx, args, schema, betterAuthSchema) => {
383
473
  const { page, ...result } = await paginate(ctx, schema, betterAuthSchema, {
384
474
  ...args.input,
385
475
  paginationOpts: args.paginationOpts
386
476
  });
477
+ const ormTable = resolveOrmTable(ctx, schema, betterAuthSchema, args.input.model);
387
478
  await asyncMap(page, async (doc) => {
388
479
  let hookDoc = doc;
389
480
  if (!args.skipBeforeHooks && args.beforeDeleteHandle) {
390
- const transformedDoc = await ctx.runMutation(args.beforeDeleteHandle, {
481
+ const transformedDoc = await ctx.runMutation(args.beforeDeleteHandle, serializeDatesForConvex({
391
482
  doc,
392
483
  model: args.input.model
393
- });
484
+ }));
394
485
  if (transformedDoc !== void 0) hookDoc = transformedDoc;
395
486
  }
396
- await ctx.db.delete(doc._id);
397
- if (args.onDeleteHandle) await ctx.runMutation(args.onDeleteHandle, {
487
+ if (ormTable) await ormDelete(ctx, ormTable.table, doc._id);
488
+ else await ctx.db.delete(doc._id);
489
+ if (args.onDeleteHandle) await ctx.runMutation(args.onDeleteHandle, serializeDatesForConvex({
398
490
  doc: hookDoc,
399
491
  model: args.input.model
400
- });
492
+ }));
401
493
  });
402
- return {
494
+ return toConvexSafe({
403
495
  ...result,
404
496
  count: page.length,
405
497
  ids: page.map((doc) => doc._id)
406
- };
498
+ });
407
499
  };
408
- const createApi = (schema, createAuth, options) => {
409
- const betterAuthSchema = getAuthTables(createAuth({}).options);
410
- const { internalMutation, skipValidation } = options ?? {};
411
- const mutationBuilder = internalMutation ?? internalMutationGeneric;
500
+ const createApi = (schema, getAuth, options) => {
501
+ const betterAuthSchema = getAuthTables(getAuth({}).options);
502
+ const { internalMutation, skipValidation, context } = options ?? {};
503
+ const mutationBuilderBase = internalMutation ?? internalMutationGeneric;
504
+ const mutationBuilder = context ? customMutation(mutationBuilderBase, customCtx(async (ctx) => await context?.(ctx) ?? ctx)) : mutationBuilderBase;
412
505
  const anyInput = v.object({
413
506
  data: v.any(),
414
507
  model: v.string()
@@ -516,13 +609,13 @@ const createApi = (schema, createAuth, options) => {
516
609
  getLatestJwks: internalActionGeneric({
517
610
  args: {},
518
611
  handler: async (ctx) => {
519
- return createAuth(ctx).api.getLatestJwks();
612
+ return getAuth(ctx).api.getLatestJwks();
520
613
  }
521
614
  }),
522
615
  rotateKeys: internalActionGeneric({
523
616
  args: {},
524
617
  handler: async (ctx) => {
525
- return createAuth(ctx).api.rotateKeys();
618
+ return getAuth(ctx).api.rotateKeys();
526
619
  }
527
620
  })
528
621
  };
@@ -569,6 +662,10 @@ const parseWhere = (where) => {
569
662
  return w;
570
663
  });
571
664
  };
665
+ const uniqueDocs = (docs) => uniqueBy(docs, (doc) => {
666
+ if (doc && typeof doc === "object") return doc._id ?? doc.id ?? doc;
667
+ return doc;
668
+ });
572
669
  const adapterConfig = {
573
670
  adapterId: "convex",
574
671
  adapterName: "Convex Adapter",
@@ -591,19 +688,36 @@ const adapterConfig = {
591
688
  return data;
592
689
  }
593
690
  };
594
- const httpAdapter = (ctx, { authFunctions, debugLogs, triggers }) => {
691
+ const ORM_SCHEMA_OPTIONS = Symbol.for("better-convex:OrmSchemaOptions");
692
+ const hasOrmSchemaMetadata = (schema) => !!schema && typeof schema === "object" && ORM_SCHEMA_OPTIONS in schema;
693
+ const createAuthSchema = async ({ file, schema, tables }) => {
694
+ if (hasOrmSchemaMetadata(schema)) {
695
+ const { createSchemaOrm } = await import("../create-schema-orm-DplxTtYj.js");
696
+ return createSchemaOrm({
697
+ file,
698
+ tables
699
+ });
700
+ }
701
+ const { createSchema } = await import("../create-schema-DhWXOhnU.js");
702
+ return createSchema({
703
+ file,
704
+ tables
705
+ });
706
+ };
707
+ const httpAdapter = (ctx, { authFunctions, debugLogs, schema, triggers }) => {
595
708
  return createAdapterFactory({
596
709
  config: {
597
710
  ...adapterConfig,
598
711
  debugLogs: debugLogs || false
599
712
  },
600
713
  adapter: ({ options }) => {
714
+ const getTriggers = (model) => triggers?.[model];
601
715
  options.telemetry = { enabled: false };
602
716
  return {
603
717
  id: "convex",
604
718
  options: { isRunMutationCtx: isRunMutationCtx(ctx) },
605
719
  count: async (data) => {
606
- if (data.where?.some((w) => w.connector === "OR")) return unique((await asyncMap(data.where, async (w) => handlePagination(async ({ paginationOpts }) => await ctx.runQuery(authFunctions.findMany, {
720
+ if (data.where?.some((w) => w.connector === "OR")) return uniqueDocs((await asyncMap(data.where, async (w) => handlePagination(async ({ paginationOpts }) => await ctx.runQuery(authFunctions.findMany, {
607
721
  ...data,
608
722
  paginationOpts,
609
723
  where: parseWhere(w)
@@ -616,8 +730,8 @@ const httpAdapter = (ctx, { authFunctions, debugLogs, triggers }) => {
616
730
  },
617
731
  create: async ({ data, model, select }) => {
618
732
  if (!("runMutation" in ctx)) throw new Error("ctx is not a mutation ctx");
619
- const onCreateHandle = authFunctions.onCreate && triggers?.[model]?.onCreate ? await createFunctionHandle(authFunctions.onCreate) : void 0;
620
- const beforeCreateHandle = authFunctions.beforeCreate && triggers?.[model]?.beforeCreate ? await createFunctionHandle(authFunctions.beforeCreate) : void 0;
733
+ const onCreateHandle = authFunctions.onCreate && getTriggers(model)?.onCreate ? await createFunctionHandle(authFunctions.onCreate) : void 0;
734
+ const beforeCreateHandle = authFunctions.beforeCreate && getTriggers(model)?.beforeCreate ? await createFunctionHandle(authFunctions.beforeCreate) : void 0;
621
735
  return await ctx.runMutation(authFunctions.create, {
622
736
  beforeCreateHandle,
623
737
  input: {
@@ -628,17 +742,15 @@ const httpAdapter = (ctx, { authFunctions, debugLogs, triggers }) => {
628
742
  onCreateHandle
629
743
  });
630
744
  },
631
- createSchema: async ({ file, tables }) => {
632
- const { createSchema } = await import("../create-schema-B4CUvqik.js");
633
- return createSchema({
634
- file,
635
- tables
636
- });
637
- },
745
+ createSchema: async ({ file, tables }) => createAuthSchema({
746
+ file,
747
+ schema,
748
+ tables
749
+ }),
638
750
  delete: async (data) => {
639
751
  if (!("runMutation" in ctx)) throw new Error("ctx is not a mutation ctx");
640
- const onDeleteHandle = authFunctions.onDelete && triggers?.[data.model]?.onDelete ? await createFunctionHandle(authFunctions.onDelete) : void 0;
641
- const beforeDeleteHandle = authFunctions.beforeDelete && triggers?.[data.model]?.beforeDelete ? await createFunctionHandle(authFunctions.beforeDelete) : void 0;
752
+ const onDeleteHandle = authFunctions.onDelete && getTriggers(data.model)?.onDelete ? await createFunctionHandle(authFunctions.onDelete) : void 0;
753
+ const beforeDeleteHandle = authFunctions.beforeDelete && getTriggers(data.model)?.beforeDelete ? await createFunctionHandle(authFunctions.beforeDelete) : void 0;
642
754
  await ctx.runMutation(authFunctions.deleteOne, {
643
755
  beforeDeleteHandle,
644
756
  input: {
@@ -650,8 +762,8 @@ const httpAdapter = (ctx, { authFunctions, debugLogs, triggers }) => {
650
762
  },
651
763
  deleteMany: async (data) => {
652
764
  if (!("runMutation" in ctx)) throw new Error("ctx is not a mutation ctx");
653
- const onDeleteHandle = authFunctions.onDelete && triggers?.[data.model]?.onDelete ? await createFunctionHandle(authFunctions.onDelete) : void 0;
654
- const beforeDeleteHandle = authFunctions.beforeDelete && triggers?.[data.model]?.beforeDelete ? await createFunctionHandle(authFunctions.beforeDelete) : void 0;
765
+ const onDeleteHandle = authFunctions.onDelete && getTriggers(data.model)?.onDelete ? await createFunctionHandle(authFunctions.onDelete) : void 0;
766
+ const beforeDeleteHandle = authFunctions.beforeDelete && getTriggers(data.model)?.beforeDelete ? await createFunctionHandle(authFunctions.beforeDelete) : void 0;
655
767
  return (await handlePagination(async ({ paginationOpts }) => await ctx.runMutation(authFunctions.deleteMany, {
656
768
  beforeDeleteHandle,
657
769
  input: {
@@ -665,7 +777,7 @@ const httpAdapter = (ctx, { authFunctions, debugLogs, triggers }) => {
665
777
  findMany: async (data) => {
666
778
  if (data.offset) throw new Error("offset not supported");
667
779
  if (data.where?.some((w) => w.connector === "OR")) {
668
- const docs = unique((await asyncMap(data.where, async (w) => handlePagination(async ({ paginationOpts }) => await ctx.runQuery(authFunctions.findMany, {
780
+ const docs = uniqueDocs((await asyncMap(data.where, async (w) => handlePagination(async ({ paginationOpts }) => await ctx.runQuery(authFunctions.findMany, {
669
781
  ...data,
670
782
  paginationOpts,
671
783
  where: parseWhere(w)
@@ -695,9 +807,16 @@ const httpAdapter = (ctx, { authFunctions, debugLogs, triggers }) => {
695
807
  },
696
808
  update: async (data) => {
697
809
  if (!("runMutation" in ctx)) throw new Error("ctx is not a mutation ctx");
698
- if (data.where?.length === 1 && data.where[0].operator === "eq") {
699
- const onUpdateHandle = authFunctions.onUpdate && triggers?.[data.model]?.onUpdate ? await createFunctionHandle(authFunctions.onUpdate) : void 0;
700
- const beforeUpdateHandle = authFunctions.beforeUpdate && triggers?.[data.model]?.beforeUpdate ? await createFunctionHandle(authFunctions.beforeUpdate) : void 0;
810
+ if (data.where?.length && data.where.every((w) => (w.operator === "eq" || w.operator === void 0) && w.connector !== "OR")) {
811
+ const countResult = await handlePagination(async ({ paginationOpts }) => await ctx.runQuery(authFunctions.findMany, {
812
+ model: data.model,
813
+ paginationOpts,
814
+ where: parseWhere(data.where)
815
+ }), { limit: 2 });
816
+ if (countResult.docs.length === 0) throw new Error(`No ${data.model} found matching criteria`);
817
+ if (countResult.docs.length > 1) throw new Error(`Multiple ${data.model} found matching criteria. Expected exactly 1.`);
818
+ const onUpdateHandle = authFunctions.onUpdate && getTriggers(data.model)?.onUpdate ? await createFunctionHandle(authFunctions.onUpdate) : void 0;
819
+ const beforeUpdateHandle = authFunctions.beforeUpdate && getTriggers(data.model)?.beforeUpdate ? await createFunctionHandle(authFunctions.beforeUpdate) : void 0;
701
820
  return await ctx.runMutation(authFunctions.updateOne, {
702
821
  beforeUpdateHandle,
703
822
  input: {
@@ -712,8 +831,8 @@ const httpAdapter = (ctx, { authFunctions, debugLogs, triggers }) => {
712
831
  },
713
832
  updateMany: async (data) => {
714
833
  if (!("runMutation" in ctx)) throw new Error("ctx is not a mutation ctx");
715
- const onUpdateHandle = authFunctions.onUpdate && triggers?.[data.model]?.onUpdate ? await createFunctionHandle(authFunctions.onUpdate) : void 0;
716
- const beforeUpdateHandle = authFunctions.beforeUpdate && triggers?.[data.model]?.beforeUpdate ? await createFunctionHandle(authFunctions.beforeUpdate) : void 0;
834
+ const onUpdateHandle = authFunctions.onUpdate && getTriggers(data.model)?.onUpdate ? await createFunctionHandle(authFunctions.onUpdate) : void 0;
835
+ const beforeUpdateHandle = authFunctions.beforeUpdate && getTriggers(data.model)?.beforeUpdate ? await createFunctionHandle(authFunctions.beforeUpdate) : void 0;
717
836
  return (await handlePagination(async ({ paginationOpts }) => await ctx.runMutation(authFunctions.updateMany, {
718
837
  beforeUpdateHandle,
719
838
  input: {
@@ -728,20 +847,21 @@ const httpAdapter = (ctx, { authFunctions, debugLogs, triggers }) => {
728
847
  }
729
848
  });
730
849
  };
731
- const dbAdapter = (ctx, createAuthOptions, { authFunctions, debugLogs, schema, triggers }) => {
732
- const betterAuthSchema = getAuthTables(createAuthOptions({}));
850
+ const dbAdapter = (ctx, getAuthOptions, { authFunctions, debugLogs, schema, triggers }) => {
851
+ const betterAuthSchema = getAuthTables(getAuthOptions({}));
733
852
  return createAdapterFactory({
734
853
  config: {
735
854
  ...adapterConfig,
736
855
  debugLogs: debugLogs || false
737
856
  },
738
857
  adapter: ({ options }) => {
858
+ const getTriggers = (model) => triggers?.[model];
739
859
  options.telemetry = { enabled: false };
740
860
  return {
741
861
  id: "convex",
742
862
  options: { isRunMutationCtx: isRunMutationCtx(ctx) },
743
863
  count: async (data) => {
744
- if (data.where?.some((w) => w.connector === "OR")) return unique((await asyncMap(data.where, async (w) => handlePagination(async ({ paginationOpts }) => await findManyHandler(ctx, {
864
+ if (data.where?.some((w) => w.connector === "OR")) return uniqueDocs((await asyncMap(data.where, async (w) => handlePagination(async ({ paginationOpts }) => await findManyHandler(ctx, {
745
865
  ...data,
746
866
  paginationOpts,
747
867
  where: parseWhere(w)
@@ -753,9 +873,9 @@ const dbAdapter = (ctx, createAuthOptions, { authFunctions, debugLogs, schema, t
753
873
  }, schema, betterAuthSchema))).docs.length;
754
874
  },
755
875
  create: async ({ data, model, select }) => {
756
- const onCreateHandle = authFunctions.onCreate && triggers?.[model]?.onCreate ? await createFunctionHandle(authFunctions.onCreate) : void 0;
876
+ const onCreateHandle = authFunctions.onCreate && getTriggers(model)?.onCreate ? await createFunctionHandle(authFunctions.onCreate) : void 0;
757
877
  return await createHandler(ctx, {
758
- beforeCreateHandle: authFunctions.beforeCreate && triggers?.[model]?.beforeCreate ? await createFunctionHandle(authFunctions.beforeCreate) : void 0,
878
+ beforeCreateHandle: authFunctions.beforeCreate && getTriggers(model)?.beforeCreate ? await createFunctionHandle(authFunctions.beforeCreate) : void 0,
759
879
  input: {
760
880
  data,
761
881
  model
@@ -764,17 +884,15 @@ const dbAdapter = (ctx, createAuthOptions, { authFunctions, debugLogs, schema, t
764
884
  onCreateHandle
765
885
  }, schema, betterAuthSchema);
766
886
  },
767
- createSchema: async ({ file, tables }) => {
768
- const { createSchema } = await import("../create-schema-B4CUvqik.js");
769
- return createSchema({
770
- file,
771
- tables
772
- });
773
- },
887
+ createSchema: async ({ file, tables }) => createAuthSchema({
888
+ file,
889
+ schema,
890
+ tables
891
+ }),
774
892
  delete: async (data) => {
775
- const onDeleteHandle = authFunctions.onDelete && triggers?.[data.model]?.onDelete ? await createFunctionHandle(authFunctions.onDelete) : void 0;
893
+ const onDeleteHandle = authFunctions.onDelete && getTriggers(data.model)?.onDelete ? await createFunctionHandle(authFunctions.onDelete) : void 0;
776
894
  await deleteOneHandler(ctx, {
777
- beforeDeleteHandle: authFunctions.beforeDelete && triggers?.[data.model]?.beforeDelete ? await createFunctionHandle(authFunctions.beforeDelete) : void 0,
895
+ beforeDeleteHandle: authFunctions.beforeDelete && getTriggers(data.model)?.beforeDelete ? await createFunctionHandle(authFunctions.beforeDelete) : void 0,
778
896
  input: {
779
897
  model: data.model,
780
898
  where: parseWhere(data.where)
@@ -783,8 +901,8 @@ const dbAdapter = (ctx, createAuthOptions, { authFunctions, debugLogs, schema, t
783
901
  }, schema, betterAuthSchema);
784
902
  },
785
903
  deleteMany: async (data) => {
786
- const onDeleteHandle = authFunctions.onDelete && triggers?.[data.model]?.onDelete ? await createFunctionHandle(authFunctions.onDelete) : void 0;
787
- const beforeDeleteHandle = authFunctions.beforeDelete && triggers?.[data.model]?.beforeDelete ? await createFunctionHandle(authFunctions.beforeDelete) : void 0;
904
+ const onDeleteHandle = authFunctions.onDelete && getTriggers(data.model)?.onDelete ? await createFunctionHandle(authFunctions.onDelete) : void 0;
905
+ const beforeDeleteHandle = authFunctions.beforeDelete && getTriggers(data.model)?.beforeDelete ? await createFunctionHandle(authFunctions.beforeDelete) : void 0;
788
906
  return (await handlePagination(async ({ paginationOpts }) => await deleteManyHandler(ctx, {
789
907
  beforeDeleteHandle,
790
908
  input: {
@@ -798,7 +916,7 @@ const dbAdapter = (ctx, createAuthOptions, { authFunctions, debugLogs, schema, t
798
916
  findMany: async (data) => {
799
917
  if (data.offset) throw new Error("offset not supported");
800
918
  if (data.where?.some((w) => w.connector === "OR")) {
801
- const docs = unique((await asyncMap(data.where, async (w) => handlePagination(async ({ paginationOpts }) => await findManyHandler(ctx, {
919
+ const docs = uniqueDocs((await asyncMap(data.where, async (w) => handlePagination(async ({ paginationOpts }) => await findManyHandler(ctx, {
802
920
  ...data,
803
921
  paginationOpts,
804
922
  where: parseWhere(w)
@@ -826,10 +944,17 @@ const dbAdapter = (ctx, createAuthOptions, { authFunctions, debugLogs, schema, t
826
944
  }, schema, betterAuthSchema);
827
945
  },
828
946
  update: async (data) => {
829
- if (data.where?.length === 1 && data.where[0].operator === "eq") {
830
- const onUpdateHandle = authFunctions.onUpdate && triggers?.[data.model]?.onUpdate ? await createFunctionHandle(authFunctions.onUpdate) : void 0;
947
+ if (data.where?.length && data.where.every((w) => (w.operator === "eq" || w.operator === void 0) && w.connector !== "OR")) {
948
+ const countResult = await handlePagination(async ({ paginationOpts }) => await findManyHandler(ctx, {
949
+ model: data.model,
950
+ paginationOpts,
951
+ where: parseWhere(data.where)
952
+ }, schema, betterAuthSchema), { limit: 2 });
953
+ if (countResult.docs.length === 0) throw new Error(`No ${data.model} found matching criteria`);
954
+ if (countResult.docs.length > 1) throw new Error(`Multiple ${data.model} found matching criteria. Expected exactly 1.`);
955
+ const onUpdateHandle = authFunctions.onUpdate && getTriggers(data.model)?.onUpdate ? await createFunctionHandle(authFunctions.onUpdate) : void 0;
831
956
  return await updateOneHandler(ctx, {
832
- beforeUpdateHandle: authFunctions.beforeUpdate && triggers?.[data.model]?.beforeUpdate ? await createFunctionHandle(authFunctions.beforeUpdate) : void 0,
957
+ beforeUpdateHandle: authFunctions.beforeUpdate && getTriggers(data.model)?.beforeUpdate ? await createFunctionHandle(authFunctions.beforeUpdate) : void 0,
833
958
  input: {
834
959
  model: data.model,
835
960
  update: data.update,
@@ -841,8 +966,8 @@ const dbAdapter = (ctx, createAuthOptions, { authFunctions, debugLogs, schema, t
841
966
  throw new Error("where clause not supported");
842
967
  },
843
968
  updateMany: async (data) => {
844
- const onUpdateHandle = authFunctions.onUpdate && triggers?.[data.model]?.onUpdate ? await createFunctionHandle(authFunctions.onUpdate) : void 0;
845
- const beforeUpdateHandle = authFunctions.beforeUpdate && triggers?.[data.model]?.beforeUpdate ? await createFunctionHandle(authFunctions.beforeUpdate) : void 0;
969
+ const onUpdateHandle = authFunctions.onUpdate && getTriggers(data.model)?.onUpdate ? await createFunctionHandle(authFunctions.onUpdate) : void 0;
970
+ const beforeUpdateHandle = authFunctions.beforeUpdate && getTriggers(data.model)?.beforeUpdate ? await createFunctionHandle(authFunctions.beforeUpdate) : void 0;
846
971
  return (await handlePagination(async ({ paginationOpts }) => await updateManyHandler(ctx, {
847
972
  beforeUpdateHandle,
848
973
  input: {
@@ -863,24 +988,34 @@ const dbAdapter = (ctx, createAuthOptions, { authFunctions, debugLogs, schema, t
863
988
  const createClient = (config) => ({
864
989
  authFunctions: config.authFunctions,
865
990
  triggers: config.triggers,
866
- adapter: (ctx, createAuthOptions) => dbAdapter(ctx, createAuthOptions, config),
867
- httpAdapter: (ctx) => httpAdapter(ctx, config),
991
+ adapter: (ctx, getAuthOptions) => isQueryCtx(ctx) ? dbAdapter(ctx, getAuthOptions, config) : httpAdapter(ctx, config),
868
992
  triggersApi: () => {
869
- const mutationBuilder = config.internalMutation ?? internalMutationGeneric;
993
+ const mutationBuilderBase = config.internalMutation ?? internalMutationGeneric;
994
+ const hasMutationCtxTransforms = config.context !== void 0;
995
+ const transformMutationCtx = async (ctx) => await config.context?.(ctx) ?? ctx;
996
+ const mutationBuilder = hasMutationCtxTransforms ? customMutation(mutationBuilderBase, customCtx(async (ctx) => await transformMutationCtx(ctx))) : mutationBuilderBase;
997
+ const getTriggers = (model) => config.triggers?.[model];
998
+ const resolveTriggerCtx = async (ctx) => hasMutationCtxTransforms ? ctx : await transformMutationCtx(ctx);
870
999
  return {
871
1000
  beforeCreate: mutationBuilder({
872
1001
  args: {
873
1002
  data: v.any(),
874
1003
  model: v.string()
875
1004
  },
876
- handler: async (ctx, args) => await config?.triggers?.[args.model]?.beforeCreate?.(ctx, args.data) ?? args.data
1005
+ handler: async (ctx, args) => {
1006
+ const triggerCtx = await resolveTriggerCtx(ctx);
1007
+ return await getTriggers(args.model)?.beforeCreate?.(triggerCtx, args.data) ?? args.data;
1008
+ }
877
1009
  }),
878
1010
  beforeDelete: mutationBuilder({
879
1011
  args: {
880
1012
  doc: v.any(),
881
1013
  model: v.string()
882
1014
  },
883
- handler: async (ctx, args) => await config?.triggers?.[args.model]?.beforeDelete?.(ctx, args.doc) ?? args.doc
1015
+ handler: async (ctx, args) => {
1016
+ const triggerCtx = await resolveTriggerCtx(ctx);
1017
+ return await getTriggers(args.model)?.beforeDelete?.(triggerCtx, args.doc) ?? args.doc;
1018
+ }
884
1019
  }),
885
1020
  beforeUpdate: mutationBuilder({
886
1021
  args: {
@@ -888,7 +1023,10 @@ const createClient = (config) => ({
888
1023
  model: v.string(),
889
1024
  update: v.any()
890
1025
  },
891
- handler: async (ctx, args) => await config?.triggers?.[args.model]?.beforeUpdate?.(ctx, args.doc, args.update) ?? args.update
1026
+ handler: async (ctx, args) => {
1027
+ const triggerCtx = await resolveTriggerCtx(ctx);
1028
+ return await getTriggers(args.model)?.beforeUpdate?.(triggerCtx, args.doc, args.update) ?? args.update;
1029
+ }
892
1030
  }),
893
1031
  onCreate: mutationBuilder({
894
1032
  args: {
@@ -896,7 +1034,8 @@ const createClient = (config) => ({
896
1034
  model: v.string()
897
1035
  },
898
1036
  handler: async (ctx, args) => {
899
- await config?.triggers?.[args.model]?.onCreate?.(ctx, args.doc);
1037
+ const triggerCtx = await resolveTriggerCtx(ctx);
1038
+ await getTriggers(args.model)?.onCreate?.(triggerCtx, args.doc);
900
1039
  }
901
1040
  }),
902
1041
  onDelete: mutationBuilder({
@@ -905,7 +1044,8 @@ const createClient = (config) => ({
905
1044
  model: v.string()
906
1045
  },
907
1046
  handler: async (ctx, args) => {
908
- await config?.triggers?.[args.model]?.onDelete?.(ctx, args.doc);
1047
+ const triggerCtx = await resolveTriggerCtx(ctx);
1048
+ await getTriggers(args.model)?.onDelete?.(triggerCtx, args.doc);
909
1049
  }
910
1050
  }),
911
1051
  onUpdate: mutationBuilder({
@@ -915,7 +1055,8 @@ const createClient = (config) => ({
915
1055
  oldDoc: v.any()
916
1056
  },
917
1057
  handler: async (ctx, args) => {
918
- await config?.triggers?.[args.model]?.onUpdate?.(ctx, args.newDoc, args.oldDoc);
1058
+ const triggerCtx = await resolveTriggerCtx(ctx);
1059
+ await getTriggers(args.model)?.onUpdate?.(triggerCtx, args.newDoc, args.oldDoc);
919
1060
  }
920
1061
  })
921
1062
  };
@@ -945,6 +1086,7 @@ const getSession = async (ctx, _sessionId) => {
945
1086
  if (!identity) return null;
946
1087
  sessionId = identity.sessionId;
947
1088
  }
1089
+ if (!sessionId) return null;
948
1090
  return await ctx.db.get(sessionId);
949
1091
  };
950
1092
  const getHeaders = async (ctx, session) => {
@@ -970,18 +1112,18 @@ const getHeaders = async (ctx, session) => {
970
1112
  *
971
1113
  * const app = new Hono();
972
1114
  * app.use('/api/*', cors({ origin: process.env.SITE_URL, credentials: true }));
973
- * app.use(authMiddleware(createAuth));
1115
+ * app.use(authMiddleware(getAuth));
974
1116
  *
975
1117
  * export default createHttpRouter(app, appRouter);
976
1118
  * ```
977
1119
  */
978
- function authMiddleware(createAuth, opts = {}) {
1120
+ function authMiddleware(getAuth, opts = {}) {
979
1121
  const basePath = opts.basePath ?? "/api/auth";
980
1122
  return async (c, next) => {
981
1123
  if (c.req.path === "/.well-known/openid-configuration") return c.redirect(`${process.env.CONVEX_SITE_URL}${basePath}/convex/.well-known/openid-configuration`);
982
1124
  if (c.req.path.startsWith(basePath)) {
983
1125
  if (opts.verbose) console.log("request headers", c.req.raw.headers);
984
- const response = await createAuth(c.env).handler(c.req.raw);
1126
+ const response = await getAuth(c.env).handler(c.req.raw);
985
1127
  if (opts.verbose) console.log("response headers", response.headers);
986
1128
  return response;
987
1129
  }
@@ -990,4 +1132,326 @@ function authMiddleware(createAuth, opts = {}) {
990
1132
  }
991
1133
 
992
1134
  //#endregion
993
- export { adapterArgsValidator, adapterConfig, adapterWhereValidator, authMiddleware, checkUniqueFields, createApi, createClient, createHandler, dbAdapter, deleteManyHandler, deleteOneHandler, findManyHandler, findOneHandler, getAuthUserId, getAuthUserIdentity, getHeaders, getSession, handlePagination, hasUniqueFields, httpAdapter, listOne, paginate, selectFields, updateManyHandler, updateOneHandler };
1135
+ //#region src/internal/upstream/server/cors.ts
1136
+ /** biome-ignore-all lint: vendored upstream helper source */
1137
+ /**
1138
+ * Vendored from upstream helper repository at commit c5e52c8.
1139
+ * Source path: packages/convex_helpers/server/cors.ts
1140
+ */
1141
+ /**
1142
+ * This file defines a CorsHttpRouter class that extends Convex's HttpRouter.
1143
+ * It provides CORS (Cross-Origin Resource Sharing) support for HTTP routes.
1144
+ *
1145
+ * The CorsHttpRouter:
1146
+ * 1. Allows specifying allowed origins for CORS.
1147
+ * 2. Overrides the route method to add CORS headers to all non-OPTIONS requests.
1148
+ * 3. Automatically adds an OPTIONS route to handle CORS preflight requests.
1149
+ * 4. Uses the handleCors helper function to apply CORS headers consistently.
1150
+ *
1151
+ * This router simplifies the process of making Convex HTTP endpoints
1152
+ * accessible to web applications hosted on different domains while
1153
+ * maintaining proper CORS configuration.
1154
+ */
1155
+ const DEFAULT_EXPOSED_HEADERS = ["Content-Range", "Accept-Ranges"];
1156
+ /**
1157
+ * Factory function to create a router that adds CORS support to routes.
1158
+ * @param allowedOrigins An array of allowed origins for CORS.
1159
+ * @returns A function to use instead of http.route when you want CORS.
1160
+ */
1161
+ const corsRouter = (http, corsConfig) => {
1162
+ const allowedExactMethodsByPath = /* @__PURE__ */ new Map();
1163
+ const allowedPrefixMethodsByPath = /* @__PURE__ */ new Map();
1164
+ return {
1165
+ http,
1166
+ route: (routeSpec) => {
1167
+ const tempRouter = httpRouter();
1168
+ tempRouter.exactRoutes = http.exactRoutes;
1169
+ tempRouter.prefixRoutes = http.prefixRoutes;
1170
+ const config = {
1171
+ ...corsConfig,
1172
+ ...routeSpec
1173
+ };
1174
+ const httpCorsHandler = handleCors({
1175
+ originalHandler: routeSpec.handler,
1176
+ allowedMethods: [routeSpec.method],
1177
+ ...config
1178
+ });
1179
+ /**
1180
+ * Figure out what kind of route we're adding: exact or prefix and handle
1181
+ * accordingly.
1182
+ */
1183
+ if ("path" in routeSpec) {
1184
+ let methods = allowedExactMethodsByPath.get(routeSpec.path);
1185
+ if (!methods) {
1186
+ methods = /* @__PURE__ */ new Set();
1187
+ allowedExactMethodsByPath.set(routeSpec.path, methods);
1188
+ }
1189
+ methods.add(routeSpec.method);
1190
+ tempRouter.route({
1191
+ path: routeSpec.path,
1192
+ method: routeSpec.method,
1193
+ handler: httpCorsHandler
1194
+ });
1195
+ handleExactRoute(tempRouter, routeSpec, config, Array.from(methods));
1196
+ } else {
1197
+ let methods = allowedPrefixMethodsByPath.get(routeSpec.pathPrefix);
1198
+ if (!methods) {
1199
+ methods = /* @__PURE__ */ new Set();
1200
+ allowedPrefixMethodsByPath.set(routeSpec.pathPrefix, methods);
1201
+ }
1202
+ methods.add(routeSpec.method);
1203
+ tempRouter.route({
1204
+ pathPrefix: routeSpec.pathPrefix,
1205
+ method: routeSpec.method,
1206
+ handler: httpCorsHandler
1207
+ });
1208
+ handlePrefixRoute(tempRouter, routeSpec, config, Array.from(methods));
1209
+ }
1210
+ /**
1211
+ * Copy the routes from the temporary router to the main router.
1212
+ */
1213
+ http.exactRoutes = new Map(tempRouter.exactRoutes);
1214
+ http.prefixRoutes = new Map(tempRouter.prefixRoutes);
1215
+ }
1216
+ };
1217
+ };
1218
+ /**
1219
+ * Handles exact route matching and adds OPTIONS handler.
1220
+ * @param tempRouter Temporary router instance.
1221
+ * @param routeSpec Route specification for exact matching.
1222
+ */
1223
+ function handleExactRoute(tempRouter, routeSpec, config, allowedMethods) {
1224
+ const currentMethodsForPath = tempRouter.exactRoutes.get(routeSpec.path);
1225
+ /**
1226
+ * Add the OPTIONS handler for the given path
1227
+ */
1228
+ const optionsHandler = createOptionsHandlerForMethods(allowedMethods, config);
1229
+ currentMethodsForPath?.set("OPTIONS", optionsHandler);
1230
+ tempRouter.exactRoutes.set(routeSpec.path, new Map(currentMethodsForPath));
1231
+ }
1232
+ /**
1233
+ * Handles prefix route matching and adds OPTIONS handler.
1234
+ * @param tempRouter Temporary router instance.
1235
+ * @param routeSpec Route specification for prefix matching.
1236
+ */
1237
+ function handlePrefixRoute(tempRouter, routeSpec, config, allowedMethods) {
1238
+ /**
1239
+ * prefixRoutes is structured differently than exactRoutes. It's defined as
1240
+ * a Map<string, Map<string, PublicHttpAction>> where the KEY is the
1241
+ * METHOD and the VALUE is a map of paths and handlers.
1242
+ */
1243
+ const optionsHandler = createOptionsHandlerForMethods(allowedMethods, config);
1244
+ const optionsPrefixes = tempRouter.prefixRoutes.get("OPTIONS") || /* @__PURE__ */ new Map();
1245
+ optionsPrefixes.set(routeSpec.pathPrefix, optionsHandler);
1246
+ tempRouter.prefixRoutes.set("OPTIONS", optionsPrefixes);
1247
+ }
1248
+ /**
1249
+ * Creates an OPTIONS handler for the given HTTP methods.
1250
+ * @param methods Array of HTTP methods to be allowed.
1251
+ * @returns A CORS-enabled OPTIONS handler.
1252
+ */
1253
+ function createOptionsHandlerForMethods(methods, config) {
1254
+ return handleCors({
1255
+ ...config,
1256
+ allowedMethods: methods
1257
+ });
1258
+ }
1259
+ /**
1260
+ * handleCors() is a higher-order function that wraps a Convex HTTP action handler to add CORS support.
1261
+ * It allows for customization of allowed HTTP methods and origins for cross-origin requests.
1262
+ *
1263
+ * The function:
1264
+ * 1. Validates and normalizes the allowed HTTP methods.
1265
+ * 2. Generates appropriate CORS headers based on the provided configuration.
1266
+ * 3. Handles preflight OPTIONS requests automatically.
1267
+ * 4. Wraps the original handler to add CORS headers to its response.
1268
+ *
1269
+ * This helper simplifies the process of making Convex HTTP actions accessible
1270
+ * to web applications hosted on different domains.
1271
+ */
1272
+ const SECONDS_IN_A_DAY = 3600 * 24;
1273
+ /**
1274
+ * Example CORS origins:
1275
+ * - "*" (allow all origins)
1276
+ * - "https://example.com" (allow a specific domain)
1277
+ * - "https://*.example.com" (allow all subdomains of example.com)
1278
+ * - "https://example1.com, https://example2.com" (allow multiple specific domains)
1279
+ * - "null" (allow requests from data URLs or local files)
1280
+ */
1281
+ const handleCors = ({ originalHandler, allowedMethods = ["OPTIONS"], allowedOrigins = ["*"], allowedHeaders = ["Content-Type"], exposedHeaders = DEFAULT_EXPOSED_HEADERS, allowCredentials = false, browserCacheMaxAge = SECONDS_IN_A_DAY, enforceAllowOrigins = false, debug = false }) => {
1282
+ const filteredMethods = Array.from(new Set(allowedMethods.map((method) => method.toUpperCase()))).filter((method) => ROUTABLE_HTTP_METHODS.includes(method));
1283
+ if (filteredMethods.length === 0) throw new Error("No valid HTTP methods provided");
1284
+ /**
1285
+ * Ensure OPTIONS is not duplicated if it was passed in
1286
+ * E.g. if allowedMethods = ["GET", "OPTIONS"]
1287
+ */
1288
+ const allowMethods = filteredMethods.includes("OPTIONS") ? filteredMethods.join(", ") : [...filteredMethods].join(", ");
1289
+ /**
1290
+ * Build up the set of CORS headers
1291
+ */
1292
+ const commonHeaders = { Vary: "Origin" };
1293
+ if (allowCredentials) commonHeaders["Access-Control-Allow-Credentials"] = "true";
1294
+ if (exposedHeaders.length > 0) commonHeaders["Access-Control-Expose-Headers"] = exposedHeaders.join(", ");
1295
+ async function parseAllowedOrigins(request) {
1296
+ return Array.isArray(allowedOrigins) ? allowedOrigins : await allowedOrigins(request);
1297
+ }
1298
+ async function isAllowedOrigin(request) {
1299
+ const requestOrigin = request.headers.get("origin");
1300
+ if (!requestOrigin) return false;
1301
+ return (await parseAllowedOrigins(request)).some((allowed) => {
1302
+ if (allowed === "*") return true;
1303
+ if (allowed === requestOrigin) return true;
1304
+ if (allowed.startsWith("*.")) {
1305
+ const wildcardDomain = allowed.slice(1);
1306
+ const rootDomain = allowed.slice(2);
1307
+ try {
1308
+ const url = new URL(requestOrigin);
1309
+ return url.protocol === "https:" && (url.hostname.endsWith(wildcardDomain) || url.hostname === rootDomain);
1310
+ } catch {
1311
+ return false;
1312
+ }
1313
+ }
1314
+ return false;
1315
+ });
1316
+ }
1317
+ /**
1318
+ * Return our modified HTTP action
1319
+ */
1320
+ return httpActionGeneric(async (ctx, request) => {
1321
+ if (debug) console.log("CORS request", {
1322
+ path: request.url,
1323
+ origin: request.headers.get("origin"),
1324
+ headers: request.headers,
1325
+ method: request.method,
1326
+ body: request.body
1327
+ });
1328
+ const requestOrigin = request.headers.get("origin");
1329
+ const parsedAllowedOrigins = await parseAllowedOrigins(request);
1330
+ if (debug) console.log("allowed origins", parsedAllowedOrigins);
1331
+ let allowOrigins = null;
1332
+ if (parsedAllowedOrigins.includes("*") && requestOrigin && !allowCredentials) allowOrigins = requestOrigin;
1333
+ else if (requestOrigin) {
1334
+ if (await isAllowedOrigin(request)) allowOrigins = requestOrigin;
1335
+ }
1336
+ if (enforceAllowOrigins && !allowOrigins) {
1337
+ console.error(`Request from origin ${requestOrigin} blocked, missing from allowed origins: ${parsedAllowedOrigins.join()}`);
1338
+ return new Response(null, { status: 403 });
1339
+ }
1340
+ /**
1341
+ * OPTIONS has no handler and just returns headers
1342
+ */
1343
+ if (request.method === "OPTIONS") {
1344
+ const responseHeaders = new Headers({
1345
+ ...commonHeaders,
1346
+ ...allowOrigins ? { "Access-Control-Allow-Origin": allowOrigins } : {},
1347
+ "Access-Control-Allow-Methods": allowMethods,
1348
+ "Access-Control-Allow-Headers": allowedHeaders.join(", "),
1349
+ "Access-Control-Max-Age": browserCacheMaxAge.toString()
1350
+ });
1351
+ if (debug) console.log("CORS OPTIONS response headers", responseHeaders);
1352
+ return new Response(null, {
1353
+ status: 204,
1354
+ headers: responseHeaders
1355
+ });
1356
+ }
1357
+ /**
1358
+ * If the method is not OPTIONS, it must pass a handler
1359
+ */
1360
+ if (!originalHandler) throw new Error("No PublicHttpAction provider to CORS handler");
1361
+ const originalResponse = await ("_handler" in originalHandler ? originalHandler["_handler"] : originalHandler)(ctx, request);
1362
+ /**
1363
+ * Second, get a copy of the original response's headers and add the
1364
+ * allow origin header if it's allowed
1365
+ */
1366
+ const newHeaders = new Headers(originalResponse.headers);
1367
+ if (allowOrigins) newHeaders.set("Access-Control-Allow-Origin", allowOrigins);
1368
+ /**
1369
+ * Third, add or update our other CORS headers
1370
+ */
1371
+ Object.entries(commonHeaders).forEach(([key, value]) => {
1372
+ newHeaders.set(key, value);
1373
+ });
1374
+ if (debug) console.log("CORS response headers", newHeaders);
1375
+ /**
1376
+ * Fourth, return the modified Response.
1377
+ * A Response object is immutable, so we create a new one to return here.
1378
+ */
1379
+ return new Response(originalResponse.body, {
1380
+ status: originalResponse.status,
1381
+ statusText: originalResponse.statusText,
1382
+ headers: newHeaders
1383
+ });
1384
+ });
1385
+ };
1386
+
1387
+ //#endregion
1388
+ //#region src/auth/registerRoutes.ts
1389
+ /** biome-ignore-all lint/suspicious/noConsole: lib */
1390
+ const registerRoutes = (http, getAuth, opts = {}) => {
1391
+ const staticAuth = getAuth({});
1392
+ const path = staticAuth.options.basePath ?? "/api/auth";
1393
+ const authRequestHandler = httpActionGeneric(async (ctx, request) => {
1394
+ if (opts?.verbose) {
1395
+ console.log("options.baseURL", staticAuth.options.baseURL);
1396
+ console.log("request headers", request.headers);
1397
+ }
1398
+ const response = await getAuth(ctx).handler(request);
1399
+ if (opts?.verbose) console.log("response headers", response.headers);
1400
+ return response;
1401
+ });
1402
+ if (!http.lookup("/.well-known/openid-configuration", "GET")) http.route({
1403
+ handler: httpActionGeneric(async () => {
1404
+ const url = `${process.env.CONVEX_SITE_URL}${path}/convex/.well-known/openid-configuration`;
1405
+ return Response.redirect(url);
1406
+ }),
1407
+ method: "GET",
1408
+ path: "/.well-known/openid-configuration"
1409
+ });
1410
+ if (!opts.cors) {
1411
+ http.route({
1412
+ handler: authRequestHandler,
1413
+ method: "GET",
1414
+ pathPrefix: `${path}/`
1415
+ });
1416
+ http.route({
1417
+ handler: authRequestHandler,
1418
+ method: "POST",
1419
+ pathPrefix: `${path}/`
1420
+ });
1421
+ return;
1422
+ }
1423
+ const corsOpts = typeof opts.cors === "boolean" ? {
1424
+ allowedHeaders: [],
1425
+ allowedOrigins: [],
1426
+ exposedHeaders: []
1427
+ } : opts.cors;
1428
+ let trustedOriginsOption;
1429
+ const cors = corsRouter(http, {
1430
+ allowCredentials: true,
1431
+ allowedHeaders: [
1432
+ "Content-Type",
1433
+ "Better-Auth-Cookie",
1434
+ "Authorization"
1435
+ ].concat(corsOpts.allowedHeaders ?? []),
1436
+ debug: opts?.verbose,
1437
+ enforceAllowOrigins: false,
1438
+ exposedHeaders: ["Set-Better-Auth-Cookie"].concat(corsOpts.exposedHeaders ?? []),
1439
+ allowedOrigins: async (request) => {
1440
+ trustedOriginsOption = trustedOriginsOption ?? (await staticAuth.$context).options.trustedOrigins ?? [];
1441
+ return (Array.isArray(trustedOriginsOption) ? trustedOriginsOption : await trustedOriginsOption?.(request) ?? []).map((origin) => origin.endsWith("*") && origin.length > 1 ? origin.slice(0, -1) : origin).concat(corsOpts.allowedOrigins ?? []);
1442
+ }
1443
+ });
1444
+ cors.route({
1445
+ handler: authRequestHandler,
1446
+ method: "GET",
1447
+ pathPrefix: `${path}/`
1448
+ });
1449
+ cors.route({
1450
+ handler: authRequestHandler,
1451
+ method: "POST",
1452
+ pathPrefix: `${path}/`
1453
+ });
1454
+ };
1455
+
1456
+ //#endregion
1457
+ export { adapterArgsValidator, adapterConfig, adapterWhereValidator, authMiddleware, checkUniqueFields, convex, createApi, createClient, createHandler, dbAdapter, deleteManyHandler, deleteOneHandler, findManyHandler, findOneHandler, getAuthUserId, getAuthUserIdentity, getHeaders, getSession, handlePagination, hasUniqueFields, httpAdapter, listOne, paginate, registerRoutes, selectFields, updateManyHandler, updateOneHandler };