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.
- package/dist/auth/index.d.ts +54 -38
- package/dist/auth/index.js +578 -114
- package/dist/auth-client/index.d.ts +2 -1
- package/dist/auth-client/index.js +2 -1
- package/dist/auth-config/index.d.ts +45 -0
- package/dist/auth-config/index.js +24 -0
- package/dist/auth-nextjs/index.d.ts +2 -2
- package/dist/auth-nextjs/index.js +2 -2
- package/dist/{caller-factory-CeZ07fQ2.js → caller-factory-B1FvYSKr.js} +18 -12
- package/dist/cli.mjs +246 -0
- package/dist/{codegen-CSeApTME.cjs → codegen-DkpPBVPn.mjs} +28 -60
- package/dist/context-utils-DSuX99Da.d.ts +17 -0
- package/dist/{create-schema-B4CUvqik.js → create-schema-DhWXOhnU.js} +1 -1
- package/dist/create-schema-orm-DplxTtYj.js +145 -0
- package/dist/crpc/index.d.ts +3 -3
- package/dist/crpc/index.js +4 -3
- package/dist/customFunctions-C1okqCzL.js +377 -0
- package/dist/{http-types-BrMbHGYR.d.ts → http-types-BRLY10NX.d.ts} +70 -20
- package/dist/index-BQkhP2ny.d.ts +1326 -0
- package/dist/orm/index.d.ts +2977 -0
- package/dist/orm/index.js +3 -0
- package/dist/orm-Banm-XXb.js +8812 -0
- package/dist/react/index.d.ts +46 -9
- package/dist/react/index.js +362 -123
- package/dist/rsc/index.d.ts +7 -4
- package/dist/rsc/index.js +10 -7
- package/dist/server/index.d.ts +4 -617
- package/dist/server/index.js +914 -50
- package/dist/transformer-CTNSPjwp.js +194 -0
- package/dist/types-jftzhhuc.d.ts +42 -0
- package/dist/{types-DrFf50wo.d.ts → types-o-5rYcTr.d.ts} +1 -1
- package/dist/watcher.mjs +41 -0
- package/package.json +10 -12
- package/dist/cli.cjs +0 -215
- package/dist/watcher.cjs +0 -29
- /package/dist/{error-BPjr9_gg.js → error-BZUhlhYz.js} +0 -0
- /package/dist/{meta-utils-DS5fA5GB.js → meta-utils-DCpLSBWB.js} +0 -0
- /package/dist/{query-options-Dbyr-NY1.js → query-options-BL1Q0X7q.js} +0 -0
package/dist/auth/index.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { isRunMutationCtx } from "
|
|
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 {
|
|
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
|
-
|
|
214
|
-
|
|
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
|
|
295
|
-
const doc = await ctx.
|
|
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
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
320
|
-
const updatedDoc = await ctx.
|
|
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
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
376
|
-
if (
|
|
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.
|
|
397
|
-
|
|
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,
|
|
409
|
-
const betterAuthSchema = getAuthTables(
|
|
410
|
-
const { internalMutation, skipValidation } = options ?? {};
|
|
411
|
-
const
|
|
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
|
|
612
|
+
return getAuth(ctx).api.getLatestJwks();
|
|
520
613
|
}
|
|
521
614
|
}),
|
|
522
615
|
rotateKeys: internalActionGeneric({
|
|
523
616
|
args: {},
|
|
524
617
|
handler: async (ctx) => {
|
|
525
|
-
return
|
|
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
|
|
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
|
|
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 &&
|
|
620
|
-
const beforeCreateHandle = authFunctions.beforeCreate &&
|
|
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
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
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 &&
|
|
641
|
-
const beforeDeleteHandle = authFunctions.beforeDelete &&
|
|
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 &&
|
|
654
|
-
const beforeDeleteHandle = authFunctions.beforeDelete &&
|
|
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 =
|
|
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
|
|
699
|
-
const
|
|
700
|
-
|
|
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 &&
|
|
716
|
-
const beforeUpdateHandle = authFunctions.beforeUpdate &&
|
|
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,
|
|
732
|
-
const betterAuthSchema = getAuthTables(
|
|
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
|
|
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 &&
|
|
876
|
+
const onCreateHandle = authFunctions.onCreate && getTriggers(model)?.onCreate ? await createFunctionHandle(authFunctions.onCreate) : void 0;
|
|
757
877
|
return await createHandler(ctx, {
|
|
758
|
-
beforeCreateHandle: authFunctions.beforeCreate &&
|
|
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
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
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 &&
|
|
893
|
+
const onDeleteHandle = authFunctions.onDelete && getTriggers(data.model)?.onDelete ? await createFunctionHandle(authFunctions.onDelete) : void 0;
|
|
776
894
|
await deleteOneHandler(ctx, {
|
|
777
|
-
beforeDeleteHandle: authFunctions.beforeDelete &&
|
|
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 &&
|
|
787
|
-
const beforeDeleteHandle = authFunctions.beforeDelete &&
|
|
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 =
|
|
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
|
|
830
|
-
const
|
|
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 &&
|
|
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 &&
|
|
845
|
-
const beforeUpdateHandle = authFunctions.beforeUpdate &&
|
|
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,
|
|
867
|
-
httpAdapter: (ctx) => httpAdapter(ctx, config),
|
|
991
|
+
adapter: (ctx, getAuthOptions) => isQueryCtx(ctx) ? dbAdapter(ctx, getAuthOptions, config) : httpAdapter(ctx, config),
|
|
868
992
|
triggersApi: () => {
|
|
869
|
-
const
|
|
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) =>
|
|
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) =>
|
|
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) =>
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
1115
|
+
* app.use(authMiddleware(getAuth));
|
|
974
1116
|
*
|
|
975
1117
|
* export default createHttpRouter(app, appRouter);
|
|
976
1118
|
* ```
|
|
977
1119
|
*/
|
|
978
|
-
function authMiddleware(
|
|
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
|
|
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
|
-
|
|
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 };
|