better-convex 0.5.8 → 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 +123 -107
  2. package/dist/auth/index.js +562 -112
  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)
@@ -703,8 +815,8 @@ const httpAdapter = (ctx, { authFunctions, debugLogs, triggers }) => {
703
815
  }), { limit: 2 });
704
816
  if (countResult.docs.length === 0) throw new Error(`No ${data.model} found matching criteria`);
705
817
  if (countResult.docs.length > 1) throw new Error(`Multiple ${data.model} found matching criteria. Expected exactly 1.`);
706
- const onUpdateHandle = authFunctions.onUpdate && triggers?.[data.model]?.onUpdate ? await createFunctionHandle(authFunctions.onUpdate) : void 0;
707
- const beforeUpdateHandle = authFunctions.beforeUpdate && triggers?.[data.model]?.beforeUpdate ? await createFunctionHandle(authFunctions.beforeUpdate) : void 0;
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;
708
820
  return await ctx.runMutation(authFunctions.updateOne, {
709
821
  beforeUpdateHandle,
710
822
  input: {
@@ -719,8 +831,8 @@ const httpAdapter = (ctx, { authFunctions, debugLogs, triggers }) => {
719
831
  },
720
832
  updateMany: async (data) => {
721
833
  if (!("runMutation" in ctx)) throw new Error("ctx is not a mutation ctx");
722
- const onUpdateHandle = authFunctions.onUpdate && triggers?.[data.model]?.onUpdate ? await createFunctionHandle(authFunctions.onUpdate) : void 0;
723
- 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;
724
836
  return (await handlePagination(async ({ paginationOpts }) => await ctx.runMutation(authFunctions.updateMany, {
725
837
  beforeUpdateHandle,
726
838
  input: {
@@ -735,20 +847,21 @@ const httpAdapter = (ctx, { authFunctions, debugLogs, triggers }) => {
735
847
  }
736
848
  });
737
849
  };
738
- const dbAdapter = (ctx, createAuthOptions, { authFunctions, debugLogs, schema, triggers }) => {
739
- const betterAuthSchema = getAuthTables(createAuthOptions({}));
850
+ const dbAdapter = (ctx, getAuthOptions, { authFunctions, debugLogs, schema, triggers }) => {
851
+ const betterAuthSchema = getAuthTables(getAuthOptions({}));
740
852
  return createAdapterFactory({
741
853
  config: {
742
854
  ...adapterConfig,
743
855
  debugLogs: debugLogs || false
744
856
  },
745
857
  adapter: ({ options }) => {
858
+ const getTriggers = (model) => triggers?.[model];
746
859
  options.telemetry = { enabled: false };
747
860
  return {
748
861
  id: "convex",
749
862
  options: { isRunMutationCtx: isRunMutationCtx(ctx) },
750
863
  count: async (data) => {
751
- 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, {
752
865
  ...data,
753
866
  paginationOpts,
754
867
  where: parseWhere(w)
@@ -760,9 +873,9 @@ const dbAdapter = (ctx, createAuthOptions, { authFunctions, debugLogs, schema, t
760
873
  }, schema, betterAuthSchema))).docs.length;
761
874
  },
762
875
  create: async ({ data, model, select }) => {
763
- 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;
764
877
  return await createHandler(ctx, {
765
- 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,
766
879
  input: {
767
880
  data,
768
881
  model
@@ -771,17 +884,15 @@ const dbAdapter = (ctx, createAuthOptions, { authFunctions, debugLogs, schema, t
771
884
  onCreateHandle
772
885
  }, schema, betterAuthSchema);
773
886
  },
774
- createSchema: async ({ file, tables }) => {
775
- const { createSchema } = await import("../create-schema-B4CUvqik.js");
776
- return createSchema({
777
- file,
778
- tables
779
- });
780
- },
887
+ createSchema: async ({ file, tables }) => createAuthSchema({
888
+ file,
889
+ schema,
890
+ tables
891
+ }),
781
892
  delete: async (data) => {
782
- 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;
783
894
  await deleteOneHandler(ctx, {
784
- 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,
785
896
  input: {
786
897
  model: data.model,
787
898
  where: parseWhere(data.where)
@@ -790,8 +901,8 @@ const dbAdapter = (ctx, createAuthOptions, { authFunctions, debugLogs, schema, t
790
901
  }, schema, betterAuthSchema);
791
902
  },
792
903
  deleteMany: async (data) => {
793
- const onDeleteHandle = authFunctions.onDelete && triggers?.[data.model]?.onDelete ? await createFunctionHandle(authFunctions.onDelete) : void 0;
794
- 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;
795
906
  return (await handlePagination(async ({ paginationOpts }) => await deleteManyHandler(ctx, {
796
907
  beforeDeleteHandle,
797
908
  input: {
@@ -805,7 +916,7 @@ const dbAdapter = (ctx, createAuthOptions, { authFunctions, debugLogs, schema, t
805
916
  findMany: async (data) => {
806
917
  if (data.offset) throw new Error("offset not supported");
807
918
  if (data.where?.some((w) => w.connector === "OR")) {
808
- 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, {
809
920
  ...data,
810
921
  paginationOpts,
811
922
  where: parseWhere(w)
@@ -841,9 +952,9 @@ const dbAdapter = (ctx, createAuthOptions, { authFunctions, debugLogs, schema, t
841
952
  }, schema, betterAuthSchema), { limit: 2 });
842
953
  if (countResult.docs.length === 0) throw new Error(`No ${data.model} found matching criteria`);
843
954
  if (countResult.docs.length > 1) throw new Error(`Multiple ${data.model} found matching criteria. Expected exactly 1.`);
844
- const onUpdateHandle = authFunctions.onUpdate && triggers?.[data.model]?.onUpdate ? await createFunctionHandle(authFunctions.onUpdate) : void 0;
955
+ const onUpdateHandle = authFunctions.onUpdate && getTriggers(data.model)?.onUpdate ? await createFunctionHandle(authFunctions.onUpdate) : void 0;
845
956
  return await updateOneHandler(ctx, {
846
- 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,
847
958
  input: {
848
959
  model: data.model,
849
960
  update: data.update,
@@ -855,8 +966,8 @@ const dbAdapter = (ctx, createAuthOptions, { authFunctions, debugLogs, schema, t
855
966
  throw new Error("where clause not supported");
856
967
  },
857
968
  updateMany: async (data) => {
858
- const onUpdateHandle = authFunctions.onUpdate && triggers?.[data.model]?.onUpdate ? await createFunctionHandle(authFunctions.onUpdate) : void 0;
859
- 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;
860
971
  return (await handlePagination(async ({ paginationOpts }) => await updateManyHandler(ctx, {
861
972
  beforeUpdateHandle,
862
973
  input: {
@@ -877,24 +988,34 @@ const dbAdapter = (ctx, createAuthOptions, { authFunctions, debugLogs, schema, t
877
988
  const createClient = (config) => ({
878
989
  authFunctions: config.authFunctions,
879
990
  triggers: config.triggers,
880
- adapter: (ctx, createAuthOptions) => dbAdapter(ctx, createAuthOptions, config),
881
- httpAdapter: (ctx) => httpAdapter(ctx, config),
991
+ adapter: (ctx, getAuthOptions) => isQueryCtx(ctx) ? dbAdapter(ctx, getAuthOptions, config) : httpAdapter(ctx, config),
882
992
  triggersApi: () => {
883
- 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);
884
999
  return {
885
1000
  beforeCreate: mutationBuilder({
886
1001
  args: {
887
1002
  data: v.any(),
888
1003
  model: v.string()
889
1004
  },
890
- 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
+ }
891
1009
  }),
892
1010
  beforeDelete: mutationBuilder({
893
1011
  args: {
894
1012
  doc: v.any(),
895
1013
  model: v.string()
896
1014
  },
897
- 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
+ }
898
1019
  }),
899
1020
  beforeUpdate: mutationBuilder({
900
1021
  args: {
@@ -902,7 +1023,10 @@ const createClient = (config) => ({
902
1023
  model: v.string(),
903
1024
  update: v.any()
904
1025
  },
905
- 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
+ }
906
1030
  }),
907
1031
  onCreate: mutationBuilder({
908
1032
  args: {
@@ -910,7 +1034,8 @@ const createClient = (config) => ({
910
1034
  model: v.string()
911
1035
  },
912
1036
  handler: async (ctx, args) => {
913
- await config?.triggers?.[args.model]?.onCreate?.(ctx, args.doc);
1037
+ const triggerCtx = await resolveTriggerCtx(ctx);
1038
+ await getTriggers(args.model)?.onCreate?.(triggerCtx, args.doc);
914
1039
  }
915
1040
  }),
916
1041
  onDelete: mutationBuilder({
@@ -919,7 +1044,8 @@ const createClient = (config) => ({
919
1044
  model: v.string()
920
1045
  },
921
1046
  handler: async (ctx, args) => {
922
- await config?.triggers?.[args.model]?.onDelete?.(ctx, args.doc);
1047
+ const triggerCtx = await resolveTriggerCtx(ctx);
1048
+ await getTriggers(args.model)?.onDelete?.(triggerCtx, args.doc);
923
1049
  }
924
1050
  }),
925
1051
  onUpdate: mutationBuilder({
@@ -929,7 +1055,8 @@ const createClient = (config) => ({
929
1055
  oldDoc: v.any()
930
1056
  },
931
1057
  handler: async (ctx, args) => {
932
- 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);
933
1060
  }
934
1061
  })
935
1062
  };
@@ -959,6 +1086,7 @@ const getSession = async (ctx, _sessionId) => {
959
1086
  if (!identity) return null;
960
1087
  sessionId = identity.sessionId;
961
1088
  }
1089
+ if (!sessionId) return null;
962
1090
  return await ctx.db.get(sessionId);
963
1091
  };
964
1092
  const getHeaders = async (ctx, session) => {
@@ -984,18 +1112,18 @@ const getHeaders = async (ctx, session) => {
984
1112
  *
985
1113
  * const app = new Hono();
986
1114
  * app.use('/api/*', cors({ origin: process.env.SITE_URL, credentials: true }));
987
- * app.use(authMiddleware(createAuth));
1115
+ * app.use(authMiddleware(getAuth));
988
1116
  *
989
1117
  * export default createHttpRouter(app, appRouter);
990
1118
  * ```
991
1119
  */
992
- function authMiddleware(createAuth, opts = {}) {
1120
+ function authMiddleware(getAuth, opts = {}) {
993
1121
  const basePath = opts.basePath ?? "/api/auth";
994
1122
  return async (c, next) => {
995
1123
  if (c.req.path === "/.well-known/openid-configuration") return c.redirect(`${process.env.CONVEX_SITE_URL}${basePath}/convex/.well-known/openid-configuration`);
996
1124
  if (c.req.path.startsWith(basePath)) {
997
1125
  if (opts.verbose) console.log("request headers", c.req.raw.headers);
998
- const response = await createAuth(c.env).handler(c.req.raw);
1126
+ const response = await getAuth(c.env).handler(c.req.raw);
999
1127
  if (opts.verbose) console.log("response headers", response.headers);
1000
1128
  return response;
1001
1129
  }
@@ -1004,4 +1132,326 @@ function authMiddleware(createAuth, opts = {}) {
1004
1132
  }
1005
1133
 
1006
1134
  //#endregion
1007
- 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 };