kitcn 0.23.0 → 0.25.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/aggregate/index.d.ts +11 -1
- package/dist/aggregate/index.js +1 -1
- package/dist/auth/generated/index.d.ts +1 -1
- package/dist/auth/generated/index.js +1 -1
- package/dist/auth/http/index.d.ts +1 -1
- package/dist/auth/index.d.ts +4 -4
- package/dist/auth/index.js +10 -9
- package/dist/auth/nextjs/index.d.ts +2 -2
- package/dist/auth/nextjs/index.js +3 -3
- package/dist/auth/start/server/index.d.ts +1 -1
- package/dist/auth/start/server/index.js +1 -1
- package/dist/{builder-CsxVc5xC.js → builder-1Vx-tEnS.js} +3 -3
- package/dist/{caller-factory-CWa0ELLD.js → caller-factory-Dd3H7j3V.js} +3 -3
- package/dist/{where-clause-compiler-B_H3oio5.d.ts → capabilities-Bem7xvGK.d.ts} +2958 -4145
- package/dist/cli.mjs +155 -60
- package/dist/{convex-plugin-D8B0oCFq.js → convex-plugin-DfOhBU9g.js} +1 -1
- package/dist/{create-schema-orm-B3f2Kc8O.js → create-schema-orm-Bu2JCiBS.js} +1 -1
- package/dist/crpc/index.d.ts +3 -3
- package/dist/crpc/index.js +4 -4
- package/dist/{customFunctions-DxEEO4Dq.js → customFunctions-BbhgdRGl.js} +2 -2
- package/dist/definitions-D9vhJ6OV.js +215 -0
- package/dist/extensions-DzjCJXYl.js +55 -0
- package/dist/filter-expression-Dydt8wS0.js +402 -0
- package/dist/{generated-contract-disabled-B-nmd7Ne.d.ts → generated-contract-disabled-BEc4d98x.d.ts} +2 -2
- package/dist/{http-types-BoSDAh4Y.d.ts → http-types-90mapMP9.d.ts} +33 -3
- package/dist/id-Bibg34Yb.js +37 -0
- package/dist/{local-env-yFKub75x.mjs → local-env-Du24tdbp.mjs} +2243 -2113
- package/dist/{middleware-Br5zubu7.d.ts → middleware-CE_MC6Gi.d.ts} +1 -1
- package/dist/{middleware-DUd1Sj39.js → middleware-cJDRHTRY.js} +1 -1
- package/dist/orm/aggregate-index/index.d.ts +20 -0
- package/dist/orm/aggregate-index/index.js +2136 -0
- package/dist/orm/index.d.ts +5 -4
- package/dist/orm/index.js +126 -4292
- package/dist/orm/migrations/index.d.ts +3 -0
- package/dist/orm/migrations/index.js +506 -0
- package/dist/plugins/index.d.ts +1 -1
- package/dist/plugins/index.js +1 -1
- package/dist/{procedure-caller-DQxnLBS_.js → procedure-caller-DIzXfX2E.js} +2 -2
- package/dist/{procedure-name-C55TynK3.d.ts → procedure-name-B09-cf1-.d.ts} +3 -3
- package/dist/{query-context-BzihIpnM.js → query-context-DOm5Xm3H.js} +1 -402
- package/dist/{query-options-C_eBSIXG.js → query-options-DUiOj01a.js} +33 -1
- package/dist/ratelimit/index.d.ts +47 -11
- package/dist/ratelimit/index.js +256 -102
- package/dist/react/index.d.ts +9 -6
- package/dist/react/index.js +36 -17
- package/dist/rsc/index.d.ts +6 -9
- package/dist/rsc/index.js +7 -11
- package/dist/runtime-DfGsZtx3.js +1074 -0
- package/dist/schema-Besl-rpv.js +1411 -0
- package/dist/server/index.d.ts +4 -4
- package/dist/server/index.js +4 -4
- package/dist/solid/index.d.ts +9 -6
- package/dist/solid/index.js +36 -17
- package/dist/{runtime-6DJxpDfb.js → table-rmJm5Qio.js} +3 -1095
- package/dist/{token-xlpENMVn.js → token-DcV_0fkF.js} +1 -1
- package/dist/{types-C0Xl7P8K.d.ts → types-In-ZZOsd.d.ts} +1 -1
- package/dist/{validators-C7LelqTN.js → validators-Dmbhsd3U.js} +1 -1
- package/dist/watcher.mjs +1 -1
- package/dist/where-clause-compiler-mohWFiG0.d.ts +2042 -0
- package/package.json +3 -2
- package/skills/kitcn/SKILL.md +19 -18
- package/skills/kitcn/references/features/aggregates.md +21 -0
- package/skills/kitcn/references/features/auth-organizations.md +3 -3
- package/skills/kitcn/references/features/auth-polar.md +2 -1
- package/skills/kitcn/references/features/auth.md +8 -1
- package/skills/kitcn/references/features/http.md +26 -1
- package/skills/kitcn/references/features/ratelimit.md +11 -0
- package/skills/kitcn/references/features/react.md +1 -1
- package/skills/kitcn/references/setup/auth.md +52 -42
- package/skills/kitcn/references/setup/index.md +1 -1
- package/skills/kitcn/references/setup/server.md +37 -6
- /package/dist/{api-entry-N3nBOlI2.js → api-entry-CkDpGYVg.js} +0 -0
- /package/dist/{context-utils-BBUtBqjN.d.ts → context-utils-DwZ3Cam1.d.ts} +0 -0
- /package/dist/{create-schema-BXrKE2YY.js → create-schema-BRFjdgRr.js} +0 -0
- /package/dist/{error-Bvo7YEhk.js → error-CMLeCadS.js} +0 -0
- /package/dist/{generated-contract-disabled-BXaz7JCE.js → generated-contract-disabled-CZa0iyV0.js} +0 -0
- /package/dist/{meta-utils-D9K4fICl.js → meta-utils-D2lqN2Lg.js} +0 -0
- /package/dist/{query-context-CNo9ffvI.d.ts → query-context-DJONf8X5.d.ts} +0 -0
- /package/dist/{token-B9Bjcqug.d.ts → token-kQaqFby4.d.ts} +0 -0
- /package/dist/{transformer-yZuBWo8v.js → transformer-D8wO-kEj.js} +0 -0
- /package/dist/{types-BCl8gfGy.d.ts → types-Bf3XQex5.d.ts} +0 -0
- /package/dist/{types-DF2cg_w0.d.ts → types-MHYXboyy.d.ts} +0 -0
- /package/dist/{types-CnTpHR1F.d.ts → types-eNzQmbqM.d.ts} +0 -0
- /package/dist/{upstream-BR6sBLg3.js → upstream-BCgGZX6q.js} +0 -0
- /package/dist/{validators-BhsByJeg.d.ts → validators-V4eM2-Jg.d.ts} +0 -0
|
@@ -4,8 +4,8 @@ import fs from "node:fs";
|
|
|
4
4
|
import path, { join } from "node:path";
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
6
6
|
import { v } from "convex/values";
|
|
7
|
-
import "convex/server";
|
|
8
7
|
import { createColors } from "picocolors";
|
|
8
|
+
import "convex/server";
|
|
9
9
|
|
|
10
10
|
//#region src/orm/builders/column-builder.ts
|
|
11
11
|
/**
|
|
@@ -220,2213 +220,2228 @@ const OrmSchemaRelations = Symbol.for("kitcn:OrmSchemaRelations");
|
|
|
220
220
|
const OrmSchemaTriggers = Symbol.for("kitcn:OrmSchemaTriggers");
|
|
221
221
|
|
|
222
222
|
//#endregion
|
|
223
|
-
//#region src/orm/
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
223
|
+
//#region src/orm/index-utils.ts
|
|
224
|
+
function getIndexes(table) {
|
|
225
|
+
const indexes = table.getIndexes?.();
|
|
226
|
+
return Array.isArray(indexes) ? indexes : [];
|
|
227
|
+
}
|
|
228
|
+
function getAggregateIndexes(table) {
|
|
229
|
+
const indexes = table.getAggregateIndexes?.();
|
|
230
|
+
return Array.isArray(indexes) ? indexes : [];
|
|
231
|
+
}
|
|
232
|
+
function getRankIndexes(table) {
|
|
233
|
+
const indexes = table.getRankIndexes?.();
|
|
234
|
+
return Array.isArray(indexes) ? indexes : [];
|
|
235
|
+
}
|
|
233
236
|
|
|
234
237
|
//#endregion
|
|
235
|
-
//#region src/
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
return
|
|
238
|
+
//#region src/cli/utils/highlighter.ts
|
|
239
|
+
const isTruthyEnvFlag = (value) => value !== void 0 && value !== "" && value !== "0";
|
|
240
|
+
const isColorEnabled = () => {
|
|
241
|
+
if (process.env.FORCE_COLOR === "0") return false;
|
|
242
|
+
if (isTruthyEnvFlag(process.env.FORCE_COLOR)) return true;
|
|
243
|
+
if (isTruthyEnvFlag(process.env.NO_COLOR)) return false;
|
|
244
|
+
return Boolean(process.stdout.isTTY && process.env.TERM !== "dumb");
|
|
245
|
+
};
|
|
246
|
+
const withColors = (callback) => callback(createColors(isColorEnabled()));
|
|
247
|
+
const highlighter = {
|
|
248
|
+
bold(value) {
|
|
249
|
+
return withColors((colors) => colors.bold(value));
|
|
250
|
+
},
|
|
251
|
+
dim(value) {
|
|
252
|
+
return withColors((colors) => colors.dim(value));
|
|
253
|
+
},
|
|
254
|
+
info(value) {
|
|
255
|
+
return withColors((colors) => colors.cyan(value));
|
|
256
|
+
},
|
|
257
|
+
success(value) {
|
|
258
|
+
return withColors((colors) => colors.green(value));
|
|
259
|
+
},
|
|
260
|
+
warn(value) {
|
|
261
|
+
return withColors((colors) => colors.yellow(value));
|
|
262
|
+
},
|
|
263
|
+
error(value) {
|
|
264
|
+
return withColors((colors) => colors.red(value));
|
|
265
|
+
},
|
|
266
|
+
path(value) {
|
|
267
|
+
return withColors((colors) => colors.bold(value));
|
|
259
268
|
}
|
|
260
269
|
};
|
|
261
|
-
function boolean$1(name) {
|
|
262
|
-
return new ConvexBooleanBuilder(name ?? "");
|
|
263
|
-
}
|
|
264
270
|
|
|
265
271
|
//#endregion
|
|
266
|
-
//#region src/
|
|
267
|
-
/** @deprecated Use `v.string()` instead. Any string value. */
|
|
268
|
-
const string = v.string();
|
|
269
|
-
/** @deprecated Use `v.float64()` instead. JavaScript number, represented as a float64 in the database. */
|
|
270
|
-
const number = v.float64();
|
|
271
|
-
/** @deprecated Use `v.float64()` instead. JavaScript number, represented as a float64 in the database. */
|
|
272
|
-
const float64 = v.float64();
|
|
273
|
-
/** @deprecated Use `v.boolean()` instead. boolean value. For typing it only as true, use `l(true)` */
|
|
274
|
-
const boolean = v.boolean();
|
|
275
|
-
/** @deprecated Use `v.int64()` instead. bigint, though stored as an int64 in the database. */
|
|
276
|
-
const biging = v.int64();
|
|
277
|
-
/** @deprecated Use `v.int64()` instead. bigint, though stored as an int64 in the database. */
|
|
278
|
-
const int64 = v.int64();
|
|
279
|
-
/** @deprecated Use `v.any()` instead. Any Convex value */
|
|
280
|
-
const any = v.any();
|
|
281
|
-
/** @deprecated Use `v.null()` instead. Null value. Underscore is so it doesn't shadow the null builtin */
|
|
282
|
-
const null_ = v.null();
|
|
283
|
-
/** @deprecated Use `v.*()` instead. */
|
|
284
|
-
const { id: id$1, object, array, bytes, literal, optional, union } = v;
|
|
285
|
-
/** @deprecated Use `v.bytes()` instead. ArrayBuffer validator. */
|
|
286
|
-
const arrayBuffer = v.bytes();
|
|
287
|
-
/** Mark fields as deprecated with this permissive validator typed as null */
|
|
288
|
-
const deprecated = v.optional(v.any());
|
|
272
|
+
//#region src/cli/utils/lazy-deps.ts
|
|
289
273
|
/**
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
* This is the inverse of `v.optional()`. It takes a validator that may be optional
|
|
293
|
-
* and returns the equivalent required validator.
|
|
294
|
-
*
|
|
295
|
-
* ```ts
|
|
296
|
-
* const optionalString = v.optional(v.string());
|
|
297
|
-
* const requiredString = vRequired(optionalString); // v.string()
|
|
274
|
+
* Heavy CLI-only dependencies, resolved on first use instead of at module init.
|
|
298
275
|
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
276
|
+
* `kitcn --version`, `kitcn --help` and every `--json` call an agent makes must
|
|
277
|
+
* not pay to boot esbuild's native service, Babel's parser, jiti, dotenv or the
|
|
278
|
+
* interactive prompt stack — the CLI is non-interactive by default and most
|
|
279
|
+
* commands touch none of them.
|
|
303
280
|
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
281
|
+
* Each accessor is memoized and stays synchronous so call sites keep their
|
|
282
|
+
* current signatures, and each dependency is loaded through exactly one
|
|
283
|
+
* mechanism: `@clack/prompts` compares against a module-private `Symbol` in
|
|
284
|
+
* `isCancel`, so a second copy of the module would silently break cancellation.
|
|
306
285
|
*/
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
case "literal": return v.literal(validator.value);
|
|
319
|
-
case "bytes": return v.bytes();
|
|
320
|
-
case "object": return v.object(validator.fields);
|
|
321
|
-
case "array": return v.array(validator.element);
|
|
322
|
-
case "record": return v.record(validator.key, validator.value);
|
|
323
|
-
case "union": return v.union(...validator.members);
|
|
324
|
-
default: throw new Error("Unknown Convex validator type: " + kind);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
286
|
+
const require$1 = createRequire(import.meta.url);
|
|
287
|
+
let babelParser;
|
|
288
|
+
let clackPrompts;
|
|
289
|
+
let dotenv;
|
|
290
|
+
let esbuild;
|
|
291
|
+
let jiti;
|
|
292
|
+
const loadBabelParser = () => babelParser ??= require$1("@babel/parser");
|
|
293
|
+
const loadClackPrompts = () => clackPrompts ??= require$1("@clack/prompts");
|
|
294
|
+
const loadDotenv = () => dotenv ??= require$1("dotenv");
|
|
295
|
+
const loadEsbuild = () => esbuild ??= require$1("esbuild");
|
|
296
|
+
const loadJiti = () => jiti ??= require$1("jiti");
|
|
327
297
|
|
|
328
298
|
//#endregion
|
|
329
|
-
//#region src/
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
}
|
|
336
|
-
function isColumnBuilder$1(value) {
|
|
337
|
-
return isRecord$2(value) && value[entityKind] === "ColumnBuilder";
|
|
338
|
-
}
|
|
339
|
-
function toRequiredValidator(validator) {
|
|
340
|
-
return validator.isOptional === "optional" ? vRequired(validator) : validator;
|
|
341
|
-
}
|
|
342
|
-
function toRequiredBuilderValidator(validator) {
|
|
343
|
-
const requiredValidator = toRequiredValidator(validator);
|
|
344
|
-
if (requiredValidator.kind !== "union") return requiredValidator;
|
|
345
|
-
const nonNullMembers = requiredValidator.members.filter((member) => member.kind !== "null");
|
|
346
|
-
if (nonNullMembers.length !== 1) return requiredValidator;
|
|
347
|
-
const [member] = nonNullMembers;
|
|
348
|
-
if (member.kind === "object" || member.kind === "array") return member;
|
|
349
|
-
return requiredValidator;
|
|
350
|
-
}
|
|
351
|
-
function formatInvalidInput(path, value) {
|
|
352
|
-
return `${path} expected a column builder, Convex validator, or nested object shape. Got ${Array.isArray(value) ? "array" : value === null ? "null" : typeof value}.`;
|
|
353
|
-
}
|
|
354
|
-
function objectShapeToValidator(shape, path) {
|
|
355
|
-
const fields = {};
|
|
356
|
-
for (const [key, value] of Object.entries(shape)) fields[key] = nestedInputToValidator(value, `${path}.${key}`);
|
|
357
|
-
return v.object(fields);
|
|
358
|
-
}
|
|
359
|
-
function nestedInputToValidator(input, path) {
|
|
360
|
-
if (isColumnBuilder$1(input)) return toRequiredBuilderValidator(input.convexValidator);
|
|
361
|
-
if (isValidator(input)) return toRequiredValidator(input);
|
|
362
|
-
if (isRecord$2(input)) return objectShapeToValidator(input, path);
|
|
363
|
-
throw new Error(formatInvalidInput(path, input));
|
|
364
|
-
}
|
|
365
|
-
var ConvexCustomBuilder = class extends ConvexColumnBuilder {
|
|
366
|
-
static [entityKind] = "ConvexCustomBuilder";
|
|
367
|
-
constructor(name, validator) {
|
|
368
|
-
super(name, "any", "ConvexCustom");
|
|
369
|
-
this.config.validator = validator;
|
|
370
|
-
}
|
|
371
|
-
get convexValidator() {
|
|
372
|
-
const validator = this.config.validator;
|
|
373
|
-
if (this.config.notNull) return validator;
|
|
374
|
-
return v.optional(v.union(v.null(), validator));
|
|
375
|
-
}
|
|
376
|
-
build() {
|
|
377
|
-
return this.convexValidator;
|
|
378
|
-
}
|
|
379
|
-
};
|
|
380
|
-
function custom(a, b) {
|
|
381
|
-
if (b !== void 0) return new ConvexCustomBuilder(a, b);
|
|
382
|
-
return new ConvexCustomBuilder("", a);
|
|
383
|
-
}
|
|
384
|
-
/**
|
|
385
|
-
* Creates an array column from a nested validator or builder.
|
|
386
|
-
*
|
|
387
|
-
* Values in nested arrays are always compiled as required validators.
|
|
388
|
-
*/
|
|
389
|
-
function arrayOf(element) {
|
|
390
|
-
return custom(v.array(nestedInputToValidator(element, "arrayOf(element)"))).$type();
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* Creates an object column from either:
|
|
394
|
-
* - a nested shape of validators/builders, or
|
|
395
|
-
* - a validator/builder describing homogeneous record values
|
|
396
|
-
*
|
|
397
|
-
* Fields in nested objects are always compiled as required validators.
|
|
398
|
-
*/
|
|
399
|
-
function objectOf(input) {
|
|
400
|
-
if (isColumnBuilder$1(input) || isValidator(input)) return custom(v.record(v.string(), nestedInputToValidator(input, "objectOf(value)"))).$type();
|
|
401
|
-
if (!isRecord$2(input)) throw new Error(formatInvalidInput("objectOf(shape)", input));
|
|
402
|
-
return custom(objectShapeToValidator(input, "objectOf(shape)")).$type();
|
|
403
|
-
}
|
|
404
|
-
/**
|
|
405
|
-
* Convenience wrapper for Convex "JSON" values.
|
|
406
|
-
*
|
|
407
|
-
* Note: This is Convex JSON (runtime `v.any()`), not SQL JSON/JSONB.
|
|
408
|
-
*/
|
|
409
|
-
function json() {
|
|
410
|
-
return custom(v.any()).$type();
|
|
411
|
-
}
|
|
299
|
+
//#region src/cli/utils/crpc-builder-stub.ts
|
|
300
|
+
const CRPC_BUILDER_STUB_SOURCE = `const createMiddleware = (handler = undefined) => ({
|
|
301
|
+
_handler: handler,
|
|
302
|
+
pipe(nextHandler = undefined) {
|
|
303
|
+
return createMiddleware(nextHandler);
|
|
304
|
+
},
|
|
305
|
+
});
|
|
412
306
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
307
|
+
const toMetaObject = (value = undefined) =>
|
|
308
|
+
value && typeof value === "object" ? value : {};
|
|
309
|
+
|
|
310
|
+
const createProcedureExport = (type, state, handler) => ({
|
|
311
|
+
_crpcMeta: {
|
|
312
|
+
type,
|
|
313
|
+
internal: state.internal ?? false,
|
|
314
|
+
...toMetaObject(state.meta),
|
|
315
|
+
},
|
|
316
|
+
...(state.httpRoute ? { _crpcHttpRoute: state.httpRoute } : {}),
|
|
317
|
+
_handler: handler,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
const createProcedureBuilder = (state = {}) => {
|
|
321
|
+
const builder = {
|
|
322
|
+
internal() {
|
|
323
|
+
return createProcedureBuilder({ ...state, internal: true });
|
|
324
|
+
},
|
|
325
|
+
use() {
|
|
326
|
+
return createProcedureBuilder(state);
|
|
327
|
+
},
|
|
328
|
+
meta(value = undefined) {
|
|
329
|
+
return createProcedureBuilder({
|
|
330
|
+
...state,
|
|
331
|
+
meta: {
|
|
332
|
+
...toMetaObject(state.meta),
|
|
333
|
+
...toMetaObject(value),
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
},
|
|
337
|
+
input() {
|
|
338
|
+
return createProcedureBuilder(state);
|
|
339
|
+
},
|
|
340
|
+
params() {
|
|
341
|
+
return createProcedureBuilder(state);
|
|
342
|
+
},
|
|
343
|
+
searchParams() {
|
|
344
|
+
return createProcedureBuilder(state);
|
|
345
|
+
},
|
|
346
|
+
paginated(options = undefined) {
|
|
347
|
+
return createProcedureBuilder({
|
|
348
|
+
...state,
|
|
349
|
+
meta:
|
|
350
|
+
typeof options?.limit === "number"
|
|
351
|
+
? {
|
|
352
|
+
...toMetaObject(state.meta),
|
|
353
|
+
limit: options.limit,
|
|
354
|
+
}
|
|
355
|
+
: state.meta,
|
|
356
|
+
});
|
|
357
|
+
},
|
|
358
|
+
output() {
|
|
359
|
+
return createProcedureBuilder(state);
|
|
360
|
+
},
|
|
361
|
+
form() {
|
|
362
|
+
return createProcedureBuilder(state);
|
|
363
|
+
},
|
|
364
|
+
route(path, method) {
|
|
365
|
+
return createProcedureBuilder({
|
|
366
|
+
...state,
|
|
367
|
+
httpRoute:
|
|
368
|
+
typeof path === "string" && typeof method === "string"
|
|
369
|
+
? { path, method: method.toUpperCase() }
|
|
370
|
+
: undefined,
|
|
371
|
+
});
|
|
372
|
+
},
|
|
373
|
+
get(path) {
|
|
374
|
+
return builder.route(path, "GET");
|
|
375
|
+
},
|
|
376
|
+
post(path) {
|
|
377
|
+
return builder.route(path, "POST");
|
|
378
|
+
},
|
|
379
|
+
put(path) {
|
|
380
|
+
return builder.route(path, "PUT");
|
|
381
|
+
},
|
|
382
|
+
patch(path) {
|
|
383
|
+
return builder.route(path, "PATCH");
|
|
384
|
+
},
|
|
385
|
+
delete(path) {
|
|
386
|
+
return builder.route(path, "DELETE");
|
|
387
|
+
},
|
|
388
|
+
query(handler = undefined) {
|
|
389
|
+
return createProcedureExport("query", state, handler);
|
|
390
|
+
},
|
|
391
|
+
mutation(handler = undefined) {
|
|
392
|
+
return createProcedureExport("mutation", state, handler);
|
|
393
|
+
},
|
|
394
|
+
action(handler = undefined) {
|
|
395
|
+
return createProcedureExport("action", state, handler);
|
|
396
|
+
},
|
|
397
|
+
middleware(handler = undefined) {
|
|
398
|
+
return createMiddleware(handler);
|
|
399
|
+
},
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
return builder;
|
|
441
403
|
};
|
|
442
|
-
function id(tableName) {
|
|
443
|
-
return new ConvexIdBuilder("", tableName);
|
|
444
|
-
}
|
|
445
404
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
get convexValidator() {
|
|
461
|
-
if (this.config.notNull) return v.number();
|
|
462
|
-
return v.optional(v.union(v.null(), v.number()));
|
|
463
|
-
}
|
|
464
|
-
/**
|
|
465
|
-
* Compile to Convex validator
|
|
466
|
-
* .notNull() → v.number()
|
|
467
|
-
* nullable → v.optional(v.number())
|
|
468
|
-
*/
|
|
469
|
-
build() {
|
|
470
|
-
return this.convexValidator;
|
|
471
|
-
}
|
|
405
|
+
const flattenRouterRecord = (record = {}, prefix = "") => {
|
|
406
|
+
const procedures = {};
|
|
407
|
+
for (const [key, value] of Object.entries(record)) {
|
|
408
|
+
const procedurePath = prefix ? prefix + "." + key : key;
|
|
409
|
+
if (value?._def?.router === true) {
|
|
410
|
+
Object.assign(
|
|
411
|
+
procedures,
|
|
412
|
+
flattenRouterRecord(value._def.record ?? {}, procedurePath)
|
|
413
|
+
);
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
procedures[procedurePath] = value;
|
|
417
|
+
}
|
|
418
|
+
return procedures;
|
|
472
419
|
};
|
|
473
|
-
function integer(name) {
|
|
474
|
-
return new ConvexNumberBuilder(name ?? "");
|
|
475
|
-
}
|
|
476
420
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
421
|
+
const createRouter = (record = {}) => ({
|
|
422
|
+
_def: {
|
|
423
|
+
router: true,
|
|
424
|
+
procedures: flattenRouterRecord(record),
|
|
425
|
+
record,
|
|
426
|
+
},
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
export const initCRPC = {
|
|
430
|
+
meta() {
|
|
431
|
+
return this;
|
|
432
|
+
},
|
|
433
|
+
dataModel() {
|
|
434
|
+
return this;
|
|
435
|
+
},
|
|
436
|
+
context() {
|
|
437
|
+
return this;
|
|
438
|
+
},
|
|
439
|
+
middleware(handler = undefined) {
|
|
440
|
+
return createMiddleware(handler);
|
|
441
|
+
},
|
|
442
|
+
create() {
|
|
443
|
+
return {
|
|
444
|
+
query: createProcedureBuilder(),
|
|
445
|
+
mutation: createProcedureBuilder(),
|
|
446
|
+
action: createProcedureBuilder(),
|
|
447
|
+
httpAction: createProcedureBuilder(),
|
|
448
|
+
middleware: createMiddleware,
|
|
449
|
+
router: createRouter,
|
|
450
|
+
};
|
|
451
|
+
},
|
|
503
452
|
};
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
453
|
+
|
|
454
|
+
export const httpAction = createProcedureBuilder();
|
|
455
|
+
`;
|
|
507
456
|
|
|
508
457
|
//#endregion
|
|
509
|
-
//#region src/
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
configurable: true
|
|
526
|
-
});
|
|
527
|
-
Object.defineProperty(extension, OrmSchemaExtensionTriggers, {
|
|
528
|
-
value: state.triggers,
|
|
529
|
-
enumerable: false,
|
|
530
|
-
configurable: true
|
|
531
|
-
});
|
|
532
|
-
if (capabilities.canRelations) defineChainMethod(extension, "relations", (relations) => createSchemaExtensionChain({
|
|
533
|
-
...state,
|
|
534
|
-
relations
|
|
535
|
-
}, {
|
|
536
|
-
canRelations: false,
|
|
537
|
-
canTriggers: true
|
|
538
|
-
}));
|
|
539
|
-
if (capabilities.canTriggers) defineChainMethod(extension, "triggers", (triggers) => createSchemaExtensionChain({
|
|
540
|
-
...state,
|
|
541
|
-
triggers
|
|
542
|
-
}, {
|
|
543
|
-
canRelations: false,
|
|
544
|
-
canTriggers: false
|
|
545
|
-
}));
|
|
546
|
-
return extension;
|
|
547
|
-
}
|
|
548
|
-
function defineSchemaExtension(key, tables) {
|
|
549
|
-
return createSchemaExtensionChain({
|
|
550
|
-
key,
|
|
551
|
-
tables,
|
|
552
|
-
relations: void 0,
|
|
553
|
-
triggers: void 0
|
|
554
|
-
}, {
|
|
555
|
-
canRelations: true,
|
|
556
|
-
canTriggers: true
|
|
557
|
-
});
|
|
458
|
+
//#region src/cli/utils/project-jiti.ts
|
|
459
|
+
const require = createRequire(import.meta.url);
|
|
460
|
+
const JITI_EXPORT_CONDITION_PRIORITY = [
|
|
461
|
+
"bun",
|
|
462
|
+
"import",
|
|
463
|
+
"module",
|
|
464
|
+
"default",
|
|
465
|
+
"require"
|
|
466
|
+
];
|
|
467
|
+
const SERVER_PARSER_SHIM_SOURCE = `${CRPC_BUILDER_STUB_SOURCE}
|
|
468
|
+
|
|
469
|
+
export class CRPCError extends Error {
|
|
470
|
+
constructor(options = {}) {
|
|
471
|
+
super(options.message ?? options.code ?? "CRPC error");
|
|
472
|
+
this.code = options.code;
|
|
473
|
+
}
|
|
558
474
|
}
|
|
559
475
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
return new ConvexIndexBuilder(this.name, columns, this.unique);
|
|
571
|
-
}
|
|
476
|
+
export const createEnv = ({ schema }) => () =>
|
|
477
|
+
typeof schema?.parse === "function" ? schema.parse(process.env) : process.env;
|
|
478
|
+
export const createHttpRouter = (_app, httpRouter) => httpRouter ?? {};
|
|
479
|
+
export const createCallerFactory = () => () => ({});
|
|
480
|
+
export const createApiLeaf = (fnOrRoot, pathOrMeta, maybeMeta) => {
|
|
481
|
+
const meta = maybeMeta ?? pathOrMeta;
|
|
482
|
+
const fn = Array.isArray(pathOrMeta)
|
|
483
|
+
? pathOrMeta.reduce((current, segment) => current?.[segment], fnOrRoot)
|
|
484
|
+
: fnOrRoot;
|
|
485
|
+
return Object.assign(fn ?? {}, meta ?? {}, { functionRef: fn });
|
|
572
486
|
};
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
487
|
+
export const createGeneratedFunctionReference = (name) => ({
|
|
488
|
+
[Symbol.for("functionName")]: name,
|
|
489
|
+
});
|
|
490
|
+
export const registerProcedureNameLookup = () => {};
|
|
491
|
+
export const typedProcedureResolver = (_functionRef, resolver) => resolver;
|
|
492
|
+
export const createGeneratedRegistryRuntime = () => ({
|
|
493
|
+
getCallerFactory() {
|
|
494
|
+
return () => ({});
|
|
495
|
+
},
|
|
496
|
+
getHandlerFactory() {
|
|
497
|
+
return () => ({});
|
|
498
|
+
},
|
|
499
|
+
});
|
|
500
|
+
`;
|
|
501
|
+
const resolveJitiExportTarget = (target) => {
|
|
502
|
+
if (typeof target === "string") return target;
|
|
503
|
+
if (Array.isArray(target)) {
|
|
504
|
+
for (const entry of target) {
|
|
505
|
+
const resolved = resolveJitiExportTarget(entry);
|
|
506
|
+
if (resolved) return resolved;
|
|
507
|
+
}
|
|
508
|
+
return null;
|
|
592
509
|
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
this.name = name;
|
|
510
|
+
if (!target || typeof target !== "object") return null;
|
|
511
|
+
const record = target;
|
|
512
|
+
for (const condition of JITI_EXPORT_CONDITION_PRIORITY) {
|
|
513
|
+
const resolved = resolveJitiExportTarget(record[condition]);
|
|
514
|
+
if (resolved) return resolved;
|
|
599
515
|
}
|
|
600
|
-
|
|
601
|
-
|
|
516
|
+
for (const value of Object.values(record)) {
|
|
517
|
+
const resolved = resolveJitiExportTarget(value);
|
|
518
|
+
if (resolved) return resolved;
|
|
602
519
|
}
|
|
520
|
+
return null;
|
|
603
521
|
};
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
};
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
this.config.filterFields = fields;
|
|
618
|
-
return this;
|
|
522
|
+
const buildLocalPackageExportAliases = (cwd, packageName) => {
|
|
523
|
+
const packageDir = path.join(cwd, "node_modules", ...packageName.split("/"));
|
|
524
|
+
const packageJsonPath = path.join(packageDir, "package.json");
|
|
525
|
+
if (!fs.existsSync(packageJsonPath)) return {};
|
|
526
|
+
const exportsField = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")).exports;
|
|
527
|
+
if (!exportsField || typeof exportsField !== "object" || Array.isArray(exportsField)) return {};
|
|
528
|
+
const aliases = {};
|
|
529
|
+
for (const [exportKey, exportTarget] of Object.entries(exportsField)) {
|
|
530
|
+
const resolvedTarget = resolveJitiExportTarget(exportTarget);
|
|
531
|
+
if (!resolvedTarget || !resolvedTarget.startsWith("./")) continue;
|
|
532
|
+
const specifier = exportKey === "." ? packageName : exportKey.startsWith("./") ? `${packageName}${exportKey.slice(1)}` : null;
|
|
533
|
+
if (!specifier) continue;
|
|
534
|
+
aliases[specifier] = path.join(packageDir, resolvedTarget);
|
|
619
535
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
536
|
+
return aliases;
|
|
537
|
+
};
|
|
538
|
+
const ensureServerParserShim = (cwd) => {
|
|
539
|
+
const shimDir = path.join(cwd, "node_modules", ".kitcn");
|
|
540
|
+
const shimPath = path.join(shimDir, "project-jiti-server-shim.mjs");
|
|
541
|
+
fs.mkdirSync(shimDir, { recursive: true });
|
|
542
|
+
if (!fs.existsSync(shimPath) || fs.readFileSync(shimPath, "utf8") !== SERVER_PARSER_SHIM_SOURCE) fs.writeFileSync(shimPath, SERVER_PARSER_SHIM_SOURCE, "utf8");
|
|
543
|
+
return shimPath;
|
|
544
|
+
};
|
|
545
|
+
const trimTsconfigWildcardSuffix = (value) => value.endsWith("/*") ? value.slice(0, -2) : value;
|
|
546
|
+
const loadTypeScript = () => {
|
|
547
|
+
try {
|
|
548
|
+
return require("typescript");
|
|
549
|
+
} catch {
|
|
550
|
+
return null;
|
|
623
551
|
}
|
|
624
552
|
};
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
553
|
+
const buildTsconfigPathAliases = (cwd) => {
|
|
554
|
+
const typescript = loadTypeScript();
|
|
555
|
+
if (!typescript) return {};
|
|
556
|
+
const configPath = typescript.findConfigFile(cwd, fs.existsSync, "tsconfig.json");
|
|
557
|
+
if (!configPath) return {};
|
|
558
|
+
const readResult = typescript.readConfigFile(configPath, typescript.sys.readFile);
|
|
559
|
+
if (readResult.error) return {};
|
|
560
|
+
const parsedConfig = typescript.parseJsonConfigFileContent(readResult.config, typescript.sys, path.dirname(configPath));
|
|
561
|
+
const baseUrl = typeof parsedConfig.options.baseUrl === "string" && parsedConfig.options.baseUrl.length > 0 ? parsedConfig.options.baseUrl : path.dirname(configPath);
|
|
562
|
+
const paths = parsedConfig.options.paths;
|
|
563
|
+
if (!paths) return {};
|
|
564
|
+
const aliases = {};
|
|
565
|
+
for (const [specifier, targets] of Object.entries(paths)) {
|
|
566
|
+
const firstTarget = targets[0];
|
|
567
|
+
if (!firstTarget) continue;
|
|
568
|
+
const aliasKey = trimTsconfigWildcardSuffix(specifier);
|
|
569
|
+
const aliasTarget = trimTsconfigWildcardSuffix(firstTarget);
|
|
570
|
+
if (!aliasKey || aliasKey === "*") continue;
|
|
571
|
+
aliases[aliasKey] = path.resolve(baseUrl, aliasTarget);
|
|
630
572
|
}
|
|
631
|
-
|
|
632
|
-
|
|
573
|
+
return aliases;
|
|
574
|
+
};
|
|
575
|
+
const getProjectServerParserShimPath = (cwd = process.cwd()) => ensureServerParserShim(cwd);
|
|
576
|
+
const createProjectJiti = (cwd = process.cwd()) => loadJiti().createJiti(cwd, {
|
|
577
|
+
interopDefault: true,
|
|
578
|
+
jsx: { runtime: "automatic" },
|
|
579
|
+
moduleCache: false,
|
|
580
|
+
tryNative: false,
|
|
581
|
+
alias: {
|
|
582
|
+
...buildTsconfigPathAliases(cwd),
|
|
583
|
+
...buildLocalPackageExportAliases(cwd, "kitcn"),
|
|
584
|
+
...buildLocalPackageExportAliases(cwd, "convex"),
|
|
585
|
+
"kitcn/server": getProjectServerParserShimPath(cwd)
|
|
633
586
|
}
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
//#endregion
|
|
590
|
+
//#region src/orm/builders/convex-column-builder.ts
|
|
591
|
+
/**
|
|
592
|
+
* Convex-specific column builder base class
|
|
593
|
+
*
|
|
594
|
+
* All Convex column builders (ConvexTextBuilder, ConvexIntegerBuilder, etc.)
|
|
595
|
+
* inherit from this class.
|
|
596
|
+
*/
|
|
597
|
+
var ConvexColumnBuilder = class extends ColumnBuilder {
|
|
598
|
+
static [entityKind] = "ConvexColumnBuilder";
|
|
634
599
|
};
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
600
|
+
|
|
601
|
+
//#endregion
|
|
602
|
+
//#region src/orm/builders/boolean.ts
|
|
603
|
+
/**
|
|
604
|
+
* Boolean column builder class
|
|
605
|
+
* Compiles to v.boolean() or v.optional(v.boolean())
|
|
606
|
+
*/
|
|
607
|
+
var ConvexBooleanBuilder = class extends ConvexColumnBuilder {
|
|
608
|
+
static [entityKind] = "ConvexBooleanBuilder";
|
|
638
609
|
constructor(name) {
|
|
639
|
-
|
|
610
|
+
super(name, "boolean", "ConvexBoolean");
|
|
640
611
|
}
|
|
641
|
-
|
|
642
|
-
|
|
612
|
+
/**
|
|
613
|
+
* Expose Convex validator for schema integration
|
|
614
|
+
*/
|
|
615
|
+
get convexValidator() {
|
|
616
|
+
if (this.config.notNull) return v.boolean();
|
|
617
|
+
return v.optional(v.union(v.null(), v.boolean()));
|
|
643
618
|
}
|
|
644
|
-
|
|
645
|
-
|
|
619
|
+
/**
|
|
620
|
+
* Compile to Convex validator
|
|
621
|
+
* .notNull() → v.boolean()
|
|
622
|
+
* nullable → v.optional(v.boolean())
|
|
623
|
+
*/
|
|
624
|
+
build() {
|
|
625
|
+
return this.convexValidator;
|
|
646
626
|
}
|
|
647
627
|
};
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
[entityKind] = "ConvexAggregateIndexBuilder";
|
|
651
|
-
config;
|
|
652
|
-
constructor(name, columns) {
|
|
653
|
-
this.config = {
|
|
654
|
-
name,
|
|
655
|
-
columns,
|
|
656
|
-
countFields: [],
|
|
657
|
-
sumFields: [],
|
|
658
|
-
avgFields: [],
|
|
659
|
-
minFields: [],
|
|
660
|
-
maxFields: []
|
|
661
|
-
};
|
|
662
|
-
}
|
|
663
|
-
count(...fields) {
|
|
664
|
-
this.config.countFields = [...this.config.countFields, ...fields];
|
|
665
|
-
return this;
|
|
666
|
-
}
|
|
667
|
-
sum(...fields) {
|
|
668
|
-
this.config.sumFields = [...this.config.sumFields, ...fields];
|
|
669
|
-
return this;
|
|
670
|
-
}
|
|
671
|
-
avg(...fields) {
|
|
672
|
-
this.config.avgFields = [...this.config.avgFields, ...fields];
|
|
673
|
-
return this;
|
|
674
|
-
}
|
|
675
|
-
min(...fields) {
|
|
676
|
-
this.config.minFields = [...this.config.minFields, ...fields];
|
|
677
|
-
return this;
|
|
678
|
-
}
|
|
679
|
-
max(...fields) {
|
|
680
|
-
this.config.maxFields = [...this.config.maxFields, ...fields];
|
|
681
|
-
return this;
|
|
682
|
-
}
|
|
683
|
-
};
|
|
684
|
-
var ConvexRankIndexBuilderOn = class {
|
|
685
|
-
static [entityKind] = "ConvexRankIndexBuilderOn";
|
|
686
|
-
[entityKind] = "ConvexRankIndexBuilderOn";
|
|
687
|
-
constructor(name) {
|
|
688
|
-
this.name = name;
|
|
689
|
-
}
|
|
690
|
-
partitionBy(...columns) {
|
|
691
|
-
return new ConvexRankIndexBuilder(this.name, columns, []);
|
|
692
|
-
}
|
|
693
|
-
all() {
|
|
694
|
-
return new ConvexRankIndexBuilder(this.name, [], []);
|
|
695
|
-
}
|
|
696
|
-
};
|
|
697
|
-
var ConvexRankIndexBuilder = class {
|
|
698
|
-
static [entityKind] = "ConvexRankIndexBuilder";
|
|
699
|
-
[entityKind] = "ConvexRankIndexBuilder";
|
|
700
|
-
config;
|
|
701
|
-
constructor(name, partitionColumns, orderColumns) {
|
|
702
|
-
this.config = {
|
|
703
|
-
name,
|
|
704
|
-
partitionColumns,
|
|
705
|
-
orderColumns,
|
|
706
|
-
sumField: void 0
|
|
707
|
-
};
|
|
708
|
-
}
|
|
709
|
-
orderBy(...columns) {
|
|
710
|
-
const orderColumns = columns.map((entry) => {
|
|
711
|
-
if (entry && typeof entry === "object" && "column" in entry && "direction" in entry) {
|
|
712
|
-
const builder = entry.column;
|
|
713
|
-
if (!builder) throw new Error("rankIndex orderBy() expected a column builder.");
|
|
714
|
-
return {
|
|
715
|
-
column: builder,
|
|
716
|
-
direction: entry.direction
|
|
717
|
-
};
|
|
718
|
-
}
|
|
719
|
-
return {
|
|
720
|
-
column: entry,
|
|
721
|
-
direction: "asc"
|
|
722
|
-
};
|
|
723
|
-
});
|
|
724
|
-
this.config.orderColumns = [...this.config.orderColumns, ...orderColumns];
|
|
725
|
-
return this;
|
|
726
|
-
}
|
|
727
|
-
sum(field) {
|
|
728
|
-
this.config.sumField = field;
|
|
729
|
-
return this;
|
|
730
|
-
}
|
|
731
|
-
};
|
|
732
|
-
var ConvexVectorIndexBuilder = class {
|
|
733
|
-
static [entityKind] = "ConvexVectorIndexBuilder";
|
|
734
|
-
[entityKind] = "ConvexVectorIndexBuilder";
|
|
735
|
-
config;
|
|
736
|
-
constructor(name, vectorField) {
|
|
737
|
-
this.config = {
|
|
738
|
-
name,
|
|
739
|
-
vectorField,
|
|
740
|
-
dimensions: void 0,
|
|
741
|
-
filterFields: [],
|
|
742
|
-
staged: false
|
|
743
|
-
};
|
|
744
|
-
}
|
|
745
|
-
dimensions(dimensions) {
|
|
746
|
-
if (!Number.isInteger(dimensions)) throw new Error(`Vector index '${this.config.name}' dimensions must be an integer, got ${dimensions}`);
|
|
747
|
-
if (dimensions <= 0) throw new Error(`Vector index '${this.config.name}' dimensions must be positive, got ${dimensions}`);
|
|
748
|
-
if (dimensions > 1e4) console.warn(`Vector index '${this.config.name}' has unusually large dimensions (${dimensions}). Common values: 768, 1536, 3072`);
|
|
749
|
-
this.config.dimensions = dimensions;
|
|
750
|
-
return this;
|
|
751
|
-
}
|
|
752
|
-
filter(...fields) {
|
|
753
|
-
this.config.filterFields = fields;
|
|
754
|
-
return this;
|
|
755
|
-
}
|
|
756
|
-
staged() {
|
|
757
|
-
this.config.staged = true;
|
|
758
|
-
return this;
|
|
759
|
-
}
|
|
760
|
-
};
|
|
761
|
-
function index(name) {
|
|
762
|
-
return new ConvexIndexBuilderOn(name, false);
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
//#endregion
|
|
766
|
-
//#region src/orm/rls/policies.ts
|
|
767
|
-
var RlsPolicy = class {
|
|
768
|
-
static [entityKind] = "RlsPolicy";
|
|
769
|
-
[entityKind] = "RlsPolicy";
|
|
770
|
-
as;
|
|
771
|
-
for;
|
|
772
|
-
to;
|
|
773
|
-
using;
|
|
774
|
-
withCheck;
|
|
775
|
-
/** @internal */
|
|
776
|
-
_linkedTable;
|
|
777
|
-
constructor(name, config) {
|
|
778
|
-
this.name = name;
|
|
779
|
-
if (config) {
|
|
780
|
-
this.as = config.as;
|
|
781
|
-
this.for = config.for;
|
|
782
|
-
this.to = config.to;
|
|
783
|
-
this.using = config.using;
|
|
784
|
-
this.withCheck = config.withCheck;
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
link(table) {
|
|
788
|
-
this._linkedTable = table;
|
|
789
|
-
return this;
|
|
790
|
-
}
|
|
791
|
-
};
|
|
792
|
-
function isRlsPolicy(value) {
|
|
793
|
-
return !!value && typeof value === "object" && value[entityKind] === "RlsPolicy";
|
|
628
|
+
function boolean$1(name) {
|
|
629
|
+
return new ConvexBooleanBuilder(name ?? "");
|
|
794
630
|
}
|
|
795
631
|
|
|
796
632
|
//#endregion
|
|
797
|
-
//#region src/
|
|
798
|
-
/**
|
|
799
|
-
|
|
800
|
-
*/
|
|
801
|
-
const
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
const
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
633
|
+
//#region src/internal/upstream/validators.ts
|
|
634
|
+
/** @deprecated Use `v.string()` instead. Any string value. */
|
|
635
|
+
const string = v.string();
|
|
636
|
+
/** @deprecated Use `v.float64()` instead. JavaScript number, represented as a float64 in the database. */
|
|
637
|
+
const number = v.float64();
|
|
638
|
+
/** @deprecated Use `v.float64()` instead. JavaScript number, represented as a float64 in the database. */
|
|
639
|
+
const float64 = v.float64();
|
|
640
|
+
/** @deprecated Use `v.boolean()` instead. boolean value. For typing it only as true, use `l(true)` */
|
|
641
|
+
const boolean = v.boolean();
|
|
642
|
+
/** @deprecated Use `v.int64()` instead. bigint, though stored as an int64 in the database. */
|
|
643
|
+
const biging = v.int64();
|
|
644
|
+
/** @deprecated Use `v.int64()` instead. bigint, though stored as an int64 in the database. */
|
|
645
|
+
const int64 = v.int64();
|
|
646
|
+
/** @deprecated Use `v.any()` instead. Any Convex value */
|
|
647
|
+
const any = v.any();
|
|
648
|
+
/** @deprecated Use `v.null()` instead. Null value. Underscore is so it doesn't shadow the null builtin */
|
|
649
|
+
const null_ = v.null();
|
|
650
|
+
/** @deprecated Use `v.*()` instead. */
|
|
651
|
+
const { id: id$1, object, array, bytes, literal, optional, union } = v;
|
|
652
|
+
/** @deprecated Use `v.bytes()` instead. ArrayBuffer validator. */
|
|
653
|
+
const arrayBuffer = v.bytes();
|
|
654
|
+
/** Mark fields as deprecated with this permissive validator typed as null */
|
|
655
|
+
const deprecated = v.optional(v.any());
|
|
820
656
|
/**
|
|
821
|
-
*
|
|
657
|
+
* Converts an optional validator to a required validator.
|
|
822
658
|
*
|
|
823
|
-
*
|
|
824
|
-
*
|
|
659
|
+
* This is the inverse of `v.optional()`. It takes a validator that may be optional
|
|
660
|
+
* and returns the equivalent required validator.
|
|
825
661
|
*
|
|
826
|
-
*
|
|
827
|
-
*
|
|
662
|
+
* ```ts
|
|
663
|
+
* const optionalString = v.optional(v.string());
|
|
664
|
+
* const requiredString = vRequired(optionalString); // v.string()
|
|
665
|
+
*
|
|
666
|
+
* // Already required validators are returned as-is
|
|
667
|
+
* const alreadyRequired = v.string();
|
|
668
|
+
* const stillRequired = vRequired(alreadyRequired); // v.string()
|
|
669
|
+
* ```
|
|
670
|
+
*
|
|
671
|
+
* @param validator The validator to make required.
|
|
672
|
+
* @returns A required version of the validator.
|
|
828
673
|
*/
|
|
829
|
-
function
|
|
830
|
-
const
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
674
|
+
function vRequired(validator) {
|
|
675
|
+
const { kind, isOptional } = validator;
|
|
676
|
+
if (isOptional === "required") return validator;
|
|
677
|
+
switch (kind) {
|
|
678
|
+
case "id": return v.id(validator.tableName);
|
|
679
|
+
case "string": return v.string();
|
|
680
|
+
case "float64": return v.float64();
|
|
681
|
+
case "int64": return v.int64();
|
|
682
|
+
case "boolean": return v.boolean();
|
|
683
|
+
case "null": return v.null();
|
|
684
|
+
case "any": return v.any();
|
|
685
|
+
case "literal": return v.literal(validator.value);
|
|
686
|
+
case "bytes": return v.bytes();
|
|
687
|
+
case "object": return v.object(validator.fields);
|
|
688
|
+
case "array": return v.array(validator.element);
|
|
689
|
+
case "record": return v.record(validator.key, validator.value);
|
|
690
|
+
case "union": return v.union(...validator.members);
|
|
691
|
+
default: throw new Error("Unknown Convex validator type: " + kind);
|
|
838
692
|
}
|
|
839
|
-
};
|
|
840
|
-
function isConvexIndexBuilder(value) {
|
|
841
|
-
return typeof value === "object" && value !== null && value[entityKind] === "ConvexIndexBuilder";
|
|
842
693
|
}
|
|
843
|
-
|
|
844
|
-
|
|
694
|
+
|
|
695
|
+
//#endregion
|
|
696
|
+
//#region src/orm/builders/custom.ts
|
|
697
|
+
function isRecord$2(value) {
|
|
698
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
845
699
|
}
|
|
846
|
-
function
|
|
847
|
-
return typeof value === "
|
|
700
|
+
function isValidator(value) {
|
|
701
|
+
return isRecord$2(value) && typeof value.kind === "string" && typeof value.isOptional === "string";
|
|
848
702
|
}
|
|
849
|
-
function
|
|
850
|
-
return
|
|
703
|
+
function isColumnBuilder$1(value) {
|
|
704
|
+
return isRecord$2(value) && value[entityKind] === "ColumnBuilder";
|
|
851
705
|
}
|
|
852
|
-
function
|
|
853
|
-
return
|
|
706
|
+
function toRequiredValidator(validator) {
|
|
707
|
+
return validator.isOptional === "optional" ? vRequired(validator) : validator;
|
|
854
708
|
}
|
|
855
|
-
function
|
|
856
|
-
|
|
709
|
+
function toRequiredBuilderValidator(validator) {
|
|
710
|
+
const requiredValidator = toRequiredValidator(validator);
|
|
711
|
+
if (requiredValidator.kind !== "union") return requiredValidator;
|
|
712
|
+
const nonNullMembers = requiredValidator.members.filter((member) => member.kind !== "null");
|
|
713
|
+
if (nonNullMembers.length !== 1) return requiredValidator;
|
|
714
|
+
const [member] = nonNullMembers;
|
|
715
|
+
if (member.kind === "object" || member.kind === "array") return member;
|
|
716
|
+
return requiredValidator;
|
|
857
717
|
}
|
|
858
|
-
function
|
|
859
|
-
return
|
|
718
|
+
function formatInvalidInput(path, value) {
|
|
719
|
+
return `${path} expected a column builder, Convex validator, or nested object shape. Got ${Array.isArray(value) ? "array" : value === null ? "null" : typeof value}.`;
|
|
860
720
|
}
|
|
861
|
-
function
|
|
862
|
-
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
return typeof value === "object" && value !== null && value[entityKind] === "ConvexCheckBuilder";
|
|
866
|
-
}
|
|
867
|
-
function isConvexSearchIndexBuilderOn(value) {
|
|
868
|
-
return typeof value === "object" && value !== null && value[entityKind] === "ConvexSearchIndexBuilderOn";
|
|
869
|
-
}
|
|
870
|
-
function isConvexUniqueConstraintBuilder(value) {
|
|
871
|
-
return typeof value === "object" && value !== null && value[entityKind] === "ConvexUniqueConstraintBuilder";
|
|
872
|
-
}
|
|
873
|
-
function isConvexSearchIndexBuilder(value) {
|
|
874
|
-
return typeof value === "object" && value !== null && value[entityKind] === "ConvexSearchIndexBuilder";
|
|
875
|
-
}
|
|
876
|
-
function isConvexVectorIndexBuilderOn(value) {
|
|
877
|
-
return typeof value === "object" && value !== null && value[entityKind] === "ConvexVectorIndexBuilderOn";
|
|
878
|
-
}
|
|
879
|
-
function isConvexVectorIndexBuilder(value) {
|
|
880
|
-
return typeof value === "object" && value !== null && value[entityKind] === "ConvexVectorIndexBuilder";
|
|
881
|
-
}
|
|
882
|
-
function isConvexDeletionBuilder(value) {
|
|
883
|
-
return typeof value === "object" && value !== null && value[entityKind] === "ConvexDeletionBuilder";
|
|
884
|
-
}
|
|
885
|
-
function isConvexLifecycleBuilder(value) {
|
|
886
|
-
return typeof value === "object" && value !== null && value[entityKind] === "ConvexLifecycleBuilder";
|
|
887
|
-
}
|
|
888
|
-
function getColumnName(column) {
|
|
889
|
-
const config = column.config;
|
|
890
|
-
if (!config?.name) throw new Error("Invalid index column: expected a convexTable column builder.");
|
|
891
|
-
return config.name;
|
|
721
|
+
function objectShapeToValidator(shape, path) {
|
|
722
|
+
const fields = {};
|
|
723
|
+
for (const [key, value] of Object.entries(shape)) fields[key] = nestedInputToValidator(value, `${path}.${key}`);
|
|
724
|
+
return v.object(fields);
|
|
892
725
|
}
|
|
893
|
-
function
|
|
894
|
-
return
|
|
726
|
+
function nestedInputToValidator(input, path) {
|
|
727
|
+
if (isColumnBuilder$1(input)) return toRequiredBuilderValidator(input.convexValidator);
|
|
728
|
+
if (isValidator(input)) return toRequiredValidator(input);
|
|
729
|
+
if (isRecord$2(input)) return objectShapeToValidator(input, path);
|
|
730
|
+
throw new Error(formatInvalidInput(path, input));
|
|
895
731
|
}
|
|
896
|
-
|
|
897
|
-
|
|
732
|
+
var ConvexCustomBuilder = class extends ConvexColumnBuilder {
|
|
733
|
+
static [entityKind] = "ConvexCustomBuilder";
|
|
734
|
+
constructor(name, validator) {
|
|
735
|
+
super(name, "any", "ConvexCustom");
|
|
736
|
+
this.config.validator = validator;
|
|
737
|
+
}
|
|
738
|
+
get convexValidator() {
|
|
739
|
+
const validator = this.config.validator;
|
|
740
|
+
if (this.config.notNull) return validator;
|
|
741
|
+
return v.optional(v.union(v.null(), validator));
|
|
742
|
+
}
|
|
743
|
+
build() {
|
|
744
|
+
return this.convexValidator;
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
function custom(a, b) {
|
|
748
|
+
if (b !== void 0) return new ConvexCustomBuilder(a, b);
|
|
749
|
+
return new ConvexCustomBuilder("", a);
|
|
898
750
|
}
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
751
|
+
/**
|
|
752
|
+
* Creates an array column from a nested validator or builder.
|
|
753
|
+
*
|
|
754
|
+
* Values in nested arrays are always compiled as required validators.
|
|
755
|
+
*/
|
|
756
|
+
function arrayOf(element) {
|
|
757
|
+
return custom(v.array(nestedInputToValidator(element, "arrayOf(element)"))).$type();
|
|
902
758
|
}
|
|
903
|
-
|
|
904
|
-
|
|
759
|
+
/**
|
|
760
|
+
* Creates an object column from either:
|
|
761
|
+
* - a nested shape of validators/builders, or
|
|
762
|
+
* - a validator/builder describing homogeneous record values
|
|
763
|
+
*
|
|
764
|
+
* Fields in nested objects are always compiled as required validators.
|
|
765
|
+
*/
|
|
766
|
+
function objectOf(input) {
|
|
767
|
+
if (isColumnBuilder$1(input) || isValidator(input)) return custom(v.record(v.string(), nestedInputToValidator(input, "objectOf(value)"))).$type();
|
|
768
|
+
if (!isRecord$2(input)) throw new Error(formatInvalidInput("objectOf(shape)", input));
|
|
769
|
+
return custom(objectShapeToValidator(input, "objectOf(shape)")).$type();
|
|
905
770
|
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
771
|
+
/**
|
|
772
|
+
* Convenience wrapper for Convex "JSON" values.
|
|
773
|
+
*
|
|
774
|
+
* Note: This is Convex JSON (runtime `v.any()`), not SQL JSON/JSONB.
|
|
775
|
+
*/
|
|
776
|
+
function json() {
|
|
777
|
+
return custom(v.any()).$type();
|
|
909
778
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
779
|
+
|
|
780
|
+
//#endregion
|
|
781
|
+
//#region src/orm/builders/id.ts
|
|
782
|
+
/**
|
|
783
|
+
* ID column builder class
|
|
784
|
+
* Compiles to v.id(tableName) or v.optional(v.id(tableName))
|
|
785
|
+
*/
|
|
786
|
+
var ConvexIdBuilder = class extends ConvexColumnBuilder {
|
|
787
|
+
static [entityKind] = "ConvexIdBuilder";
|
|
788
|
+
constructor(name, tableName) {
|
|
789
|
+
super(name, "string", "ConvexId");
|
|
790
|
+
this.tableName = tableName;
|
|
791
|
+
this.config.referenceTable = tableName;
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Expose Convex validator for schema integration
|
|
795
|
+
*/
|
|
796
|
+
get convexValidator() {
|
|
797
|
+
if (this.config.notNull) return v.id(this.tableName);
|
|
798
|
+
return v.optional(v.union(v.null(), v.id(this.tableName)));
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Compile to Convex validator
|
|
802
|
+
* .notNull() → v.id(tableName)
|
|
803
|
+
* nullable → v.optional(v.id(tableName))
|
|
804
|
+
*/
|
|
805
|
+
build() {
|
|
806
|
+
return this.convexValidator;
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
function id(tableName) {
|
|
810
|
+
return new ConvexIdBuilder("", tableName);
|
|
914
811
|
}
|
|
915
|
-
|
|
916
|
-
|
|
812
|
+
|
|
813
|
+
//#endregion
|
|
814
|
+
//#region src/orm/builders/number.ts
|
|
815
|
+
/**
|
|
816
|
+
* Number column builder class
|
|
817
|
+
* Compiles to v.number() or v.optional(v.number())
|
|
818
|
+
*/
|
|
819
|
+
var ConvexNumberBuilder = class extends ConvexColumnBuilder {
|
|
820
|
+
static [entityKind] = "ConvexNumberBuilder";
|
|
821
|
+
constructor(name) {
|
|
822
|
+
super(name, "number", "ConvexNumber");
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Expose Convex validator for schema integration
|
|
826
|
+
*/
|
|
827
|
+
get convexValidator() {
|
|
828
|
+
if (this.config.notNull) return v.number();
|
|
829
|
+
return v.optional(v.union(v.null(), v.number()));
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* Compile to Convex validator
|
|
833
|
+
* .notNull() → v.number()
|
|
834
|
+
* nullable → v.optional(v.number())
|
|
835
|
+
*/
|
|
836
|
+
build() {
|
|
837
|
+
return this.convexValidator;
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
function integer(name) {
|
|
841
|
+
return new ConvexNumberBuilder(name ?? "");
|
|
917
842
|
}
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
843
|
+
|
|
844
|
+
//#endregion
|
|
845
|
+
//#region src/orm/builders/text.ts
|
|
846
|
+
/**
|
|
847
|
+
* Text column builder class
|
|
848
|
+
* Compiles to v.string() or v.optional(v.string())
|
|
849
|
+
*/
|
|
850
|
+
var ConvexTextBuilder = class extends ConvexColumnBuilder {
|
|
851
|
+
static [entityKind] = "ConvexTextBuilder";
|
|
852
|
+
constructor(name) {
|
|
853
|
+
super(name, "string", "ConvexText");
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Expose Convex validator for schema integration
|
|
857
|
+
*/
|
|
858
|
+
get convexValidator() {
|
|
859
|
+
if (this.config.notNull) return v.string();
|
|
860
|
+
return v.optional(v.union(v.null(), v.string()));
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Compile to Convex validator
|
|
864
|
+
* .notNull() → v.string()
|
|
865
|
+
* nullable → v.optional(v.string())
|
|
866
|
+
*/
|
|
867
|
+
build() {
|
|
868
|
+
return this.convexValidator;
|
|
869
|
+
}
|
|
870
|
+
};
|
|
871
|
+
function text(name) {
|
|
872
|
+
return new ConvexTextBuilder(name ?? "");
|
|
921
873
|
}
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
874
|
+
|
|
875
|
+
//#endregion
|
|
876
|
+
//#region src/orm/extensions.ts
|
|
877
|
+
function defineChainMethod(target, key, value) {
|
|
878
|
+
Object.defineProperty(target, key, {
|
|
879
|
+
value,
|
|
880
|
+
enumerable: false,
|
|
881
|
+
configurable: true
|
|
882
|
+
});
|
|
925
883
|
}
|
|
926
|
-
function
|
|
927
|
-
const
|
|
928
|
-
|
|
884
|
+
function createSchemaExtensionChain(state, capabilities) {
|
|
885
|
+
const extension = {
|
|
886
|
+
key: state.key,
|
|
887
|
+
tables: state.tables
|
|
888
|
+
};
|
|
889
|
+
Object.defineProperty(extension, OrmSchemaExtensionRelations, {
|
|
890
|
+
value: state.relations,
|
|
891
|
+
enumerable: false,
|
|
892
|
+
configurable: true
|
|
893
|
+
});
|
|
894
|
+
Object.defineProperty(extension, OrmSchemaExtensionTriggers, {
|
|
895
|
+
value: state.triggers,
|
|
896
|
+
enumerable: false,
|
|
897
|
+
configurable: true
|
|
898
|
+
});
|
|
899
|
+
if (capabilities.canRelations) defineChainMethod(extension, "relations", (relations) => createSchemaExtensionChain({
|
|
900
|
+
...state,
|
|
901
|
+
relations
|
|
902
|
+
}, {
|
|
903
|
+
canRelations: false,
|
|
904
|
+
canTriggers: true
|
|
905
|
+
}));
|
|
906
|
+
if (capabilities.canTriggers) defineChainMethod(extension, "triggers", (triggers) => createSchemaExtensionChain({
|
|
907
|
+
...state,
|
|
908
|
+
triggers
|
|
909
|
+
}, {
|
|
910
|
+
canRelations: false,
|
|
911
|
+
canTriggers: false
|
|
912
|
+
}));
|
|
913
|
+
return extension;
|
|
929
914
|
}
|
|
930
|
-
function
|
|
931
|
-
|
|
932
|
-
|
|
915
|
+
function defineSchemaExtension(key, tables) {
|
|
916
|
+
return createSchemaExtensionChain({
|
|
917
|
+
key,
|
|
918
|
+
tables,
|
|
919
|
+
relations: void 0,
|
|
920
|
+
triggers: void 0
|
|
921
|
+
}, {
|
|
922
|
+
canRelations: true,
|
|
923
|
+
canTriggers: true
|
|
924
|
+
});
|
|
933
925
|
}
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
if (![
|
|
948
|
-
"ConvexNumber",
|
|
949
|
-
"ConvexTimestamp",
|
|
950
|
-
"ConvexDate"
|
|
951
|
-
].includes(columnType)) throw new Error(`rankIndex '${indexName}' orderBy() supports integer()/timestamp()/date() columns only. Field '${getColumnName(column)}' is type '${columnType}'.`);
|
|
952
|
-
}
|
|
953
|
-
const dedupeFieldNames = (fields) => [...new Set(fields)];
|
|
954
|
-
const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
955
|
-
const isColumnBuilder = (value) => isRecord$1(value) && typeof value.build === "function";
|
|
956
|
-
const getDiscriminatorConfig = (value) => {
|
|
957
|
-
if (!isColumnBuilder(value)) return;
|
|
958
|
-
const discriminator = value.config?.discriminator;
|
|
959
|
-
if (!discriminator) return;
|
|
960
|
-
return discriminator;
|
|
926
|
+
|
|
927
|
+
//#endregion
|
|
928
|
+
//#region src/orm/indexes.ts
|
|
929
|
+
var ConvexIndexBuilderOn = class {
|
|
930
|
+
static [entityKind] = "ConvexIndexBuilderOn";
|
|
931
|
+
[entityKind] = "ConvexIndexBuilderOn";
|
|
932
|
+
constructor(name, unique) {
|
|
933
|
+
this.name = name;
|
|
934
|
+
this.unique = unique;
|
|
935
|
+
}
|
|
936
|
+
on(...columns) {
|
|
937
|
+
return new ConvexIndexBuilder(this.name, columns, this.unique);
|
|
938
|
+
}
|
|
961
939
|
};
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
940
|
+
var ConvexIndexBuilder = class {
|
|
941
|
+
static [entityKind] = "ConvexIndexBuilder";
|
|
942
|
+
[entityKind] = "ConvexIndexBuilder";
|
|
943
|
+
config;
|
|
944
|
+
constructor(name, columns, unique) {
|
|
945
|
+
this.config = {
|
|
946
|
+
name,
|
|
947
|
+
columns,
|
|
948
|
+
unique,
|
|
949
|
+
where: void 0
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Partial index conditions are not supported in Convex.
|
|
954
|
+
* This method is kept for Drizzle API parity.
|
|
955
|
+
*/
|
|
956
|
+
where(condition) {
|
|
957
|
+
this.config.where = condition;
|
|
958
|
+
return this;
|
|
959
|
+
}
|
|
968
960
|
};
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
resolvedColumns[columnName] = rawBuilder;
|
|
975
|
-
const discriminatorConfig = getDiscriminatorConfig(rawBuilder);
|
|
976
|
-
if (!discriminatorConfig) continue;
|
|
977
|
-
if (!isRecord$1(discriminatorConfig.variants) || Object.keys(discriminatorConfig.variants).length === 0) throw new Error(`discriminator('${tableName}.${columnName}') requires at least one variant.`);
|
|
978
|
-
const alias = discriminatorConfig.as === void 0 ? DEFAULT_POLYMORPHIC_ALIAS : discriminatorConfig.as;
|
|
979
|
-
if (typeof alias !== "string" || alias.length === 0) throw new Error(`discriminator('${tableName}.${columnName}').as must be a non-empty string.`);
|
|
980
|
-
pendingPolymorphic.push({
|
|
981
|
-
discriminator: columnName,
|
|
982
|
-
alias,
|
|
983
|
-
variants: discriminatorConfig.variants
|
|
984
|
-
});
|
|
961
|
+
var ConvexSearchIndexBuilderOn = class {
|
|
962
|
+
static [entityKind] = "ConvexSearchIndexBuilderOn";
|
|
963
|
+
[entityKind] = "ConvexSearchIndexBuilderOn";
|
|
964
|
+
constructor(name) {
|
|
965
|
+
this.name = name;
|
|
985
966
|
}
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
967
|
+
on(searchField) {
|
|
968
|
+
return new ConvexSearchIndexBuilder(this.name, searchField);
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
var ConvexSearchIndexBuilder = class {
|
|
972
|
+
static [entityKind] = "ConvexSearchIndexBuilder";
|
|
973
|
+
[entityKind] = "ConvexSearchIndexBuilder";
|
|
974
|
+
config;
|
|
975
|
+
constructor(name, searchField) {
|
|
976
|
+
this.config = {
|
|
977
|
+
name,
|
|
978
|
+
searchField,
|
|
979
|
+
filterFields: [],
|
|
980
|
+
staged: false
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
filter(...fields) {
|
|
984
|
+
this.config.filterFields = fields;
|
|
985
|
+
return this;
|
|
986
|
+
}
|
|
987
|
+
staged() {
|
|
988
|
+
this.config.staged = true;
|
|
989
|
+
return this;
|
|
990
|
+
}
|
|
991
|
+
};
|
|
992
|
+
var ConvexVectorIndexBuilderOn = class {
|
|
993
|
+
static [entityKind] = "ConvexVectorIndexBuilderOn";
|
|
994
|
+
[entityKind] = "ConvexVectorIndexBuilderOn";
|
|
995
|
+
constructor(name) {
|
|
996
|
+
this.name = name;
|
|
997
|
+
}
|
|
998
|
+
on(vectorField) {
|
|
999
|
+
return new ConvexVectorIndexBuilder(this.name, vectorField);
|
|
1000
|
+
}
|
|
1001
|
+
};
|
|
1002
|
+
var ConvexAggregateIndexBuilderOn = class {
|
|
1003
|
+
static [entityKind] = "ConvexAggregateIndexBuilderOn";
|
|
1004
|
+
[entityKind] = "ConvexAggregateIndexBuilderOn";
|
|
1005
|
+
constructor(name) {
|
|
1006
|
+
this.name = name;
|
|
1007
|
+
}
|
|
1008
|
+
on(...columns) {
|
|
1009
|
+
return new ConvexAggregateIndexBuilder(this.name, columns);
|
|
1010
|
+
}
|
|
1011
|
+
all() {
|
|
1012
|
+
return new ConvexAggregateIndexBuilder(this.name, []);
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
1015
|
+
var ConvexAggregateIndexBuilder = class {
|
|
1016
|
+
static [entityKind] = "ConvexAggregateIndexBuilder";
|
|
1017
|
+
[entityKind] = "ConvexAggregateIndexBuilder";
|
|
1018
|
+
config;
|
|
1019
|
+
constructor(name, columns) {
|
|
1020
|
+
this.config = {
|
|
1021
|
+
name,
|
|
1022
|
+
columns,
|
|
1023
|
+
countFields: [],
|
|
1024
|
+
sumFields: [],
|
|
1025
|
+
avgFields: [],
|
|
1026
|
+
minFields: [],
|
|
1027
|
+
maxFields: []
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
count(...fields) {
|
|
1031
|
+
this.config.countFields = [...this.config.countFields, ...fields];
|
|
1032
|
+
return this;
|
|
1033
|
+
}
|
|
1034
|
+
sum(...fields) {
|
|
1035
|
+
this.config.sumFields = [...this.config.sumFields, ...fields];
|
|
1036
|
+
return this;
|
|
1037
|
+
}
|
|
1038
|
+
avg(...fields) {
|
|
1039
|
+
this.config.avgFields = [...this.config.avgFields, ...fields];
|
|
1040
|
+
return this;
|
|
1041
|
+
}
|
|
1042
|
+
min(...fields) {
|
|
1043
|
+
this.config.minFields = [...this.config.minFields, ...fields];
|
|
1044
|
+
return this;
|
|
1045
|
+
}
|
|
1046
|
+
max(...fields) {
|
|
1047
|
+
this.config.maxFields = [...this.config.maxFields, ...fields];
|
|
1048
|
+
return this;
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
var ConvexRankIndexBuilderOn = class {
|
|
1052
|
+
static [entityKind] = "ConvexRankIndexBuilderOn";
|
|
1053
|
+
[entityKind] = "ConvexRankIndexBuilderOn";
|
|
1054
|
+
constructor(name) {
|
|
1055
|
+
this.name = name;
|
|
1056
|
+
}
|
|
1057
|
+
partitionBy(...columns) {
|
|
1058
|
+
return new ConvexRankIndexBuilder(this.name, columns, []);
|
|
1059
|
+
}
|
|
1060
|
+
all() {
|
|
1061
|
+
return new ConvexRankIndexBuilder(this.name, [], []);
|
|
1062
|
+
}
|
|
1063
|
+
};
|
|
1064
|
+
var ConvexRankIndexBuilder = class {
|
|
1065
|
+
static [entityKind] = "ConvexRankIndexBuilder";
|
|
1066
|
+
[entityKind] = "ConvexRankIndexBuilder";
|
|
1067
|
+
config;
|
|
1068
|
+
constructor(name, partitionColumns, orderColumns) {
|
|
1069
|
+
this.config = {
|
|
1070
|
+
name,
|
|
1071
|
+
partitionColumns,
|
|
1072
|
+
orderColumns,
|
|
1073
|
+
sumField: void 0
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
orderBy(...columns) {
|
|
1077
|
+
const orderColumns = columns.map((entry) => {
|
|
1078
|
+
if (entry && typeof entry === "object" && "column" in entry && "direction" in entry) {
|
|
1079
|
+
const builder = entry.column;
|
|
1080
|
+
if (!builder) throw new Error("rankIndex orderBy() expected a column builder.");
|
|
1081
|
+
return {
|
|
1082
|
+
column: builder,
|
|
1083
|
+
direction: entry.direction
|
|
1084
|
+
};
|
|
1014
1085
|
}
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1086
|
+
return {
|
|
1087
|
+
column: entry,
|
|
1088
|
+
direction: "asc"
|
|
1018
1089
|
};
|
|
1019
|
-
}
|
|
1020
|
-
for (const [fieldName, { builder }] of generatedFieldMap.entries()) resolvedColumns[fieldName] = builder;
|
|
1021
|
-
polymorphicConfigs.push({
|
|
1022
|
-
discriminator: pending.discriminator,
|
|
1023
|
-
alias: pending.alias,
|
|
1024
|
-
generatedFieldNames: Object.freeze([...generatedFieldMap.keys()]),
|
|
1025
|
-
variants: Object.freeze(variantRuntime)
|
|
1026
1090
|
});
|
|
1091
|
+
this.config.orderColumns = [...this.config.orderColumns, ...orderColumns];
|
|
1092
|
+
return this;
|
|
1027
1093
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
const resolvedAvgFields = dedupeFieldNames(avgFields.map((column) => {
|
|
1087
|
-
const field = assertColumnInTable(column, table.tableName, `Aggregate index '${name}' avg`);
|
|
1088
|
-
assertAggregateAvgFieldType(column, name);
|
|
1089
|
-
return field;
|
|
1090
|
-
}));
|
|
1091
|
-
assertNoReservedCreatedAtIndexFields(resolvedAvgFields, `Aggregate index '${name}' avg`);
|
|
1092
|
-
const resolvedMinFields = dedupeFieldNames(minFields.map((column) => {
|
|
1093
|
-
const field = assertColumnInTable(column, table.tableName, `Aggregate index '${name}' min`);
|
|
1094
|
-
assertAggregateComparableFieldType(column, name, "min");
|
|
1095
|
-
return field;
|
|
1096
|
-
}));
|
|
1097
|
-
assertNoReservedCreatedAtIndexFields(resolvedMinFields, `Aggregate index '${name}' min`);
|
|
1098
|
-
const resolvedMaxFields = dedupeFieldNames(maxFields.map((column) => {
|
|
1099
|
-
const field = assertColumnInTable(column, table.tableName, `Aggregate index '${name}' max`);
|
|
1100
|
-
assertAggregateComparableFieldType(column, name, "max");
|
|
1101
|
-
return field;
|
|
1102
|
-
}));
|
|
1103
|
-
assertNoReservedCreatedAtIndexFields(resolvedMaxFields, `Aggregate index '${name}' max`);
|
|
1104
|
-
table.addAggregateIndex(name, {
|
|
1105
|
-
fields,
|
|
1106
|
-
countFields: resolvedCountFields,
|
|
1107
|
-
sumFields: resolvedSumFields,
|
|
1108
|
-
avgFields: resolvedAvgFields,
|
|
1109
|
-
minFields: resolvedMinFields,
|
|
1110
|
-
maxFields: resolvedMaxFields
|
|
1111
|
-
});
|
|
1112
|
-
continue;
|
|
1113
|
-
}
|
|
1114
|
-
if (isConvexRankIndexBuilder(entry)) {
|
|
1115
|
-
const { name, partitionColumns, orderColumns, sumField } = entry.config;
|
|
1116
|
-
if (!orderColumns.length) throw new Error(`rankIndex '${name}' on '${table.tableName}' must declare at least one orderBy(...) column.`);
|
|
1117
|
-
const resolvedPartitionFields = dedupeFieldNames(partitionColumns.map((column) => assertColumnInTable(column, table.tableName, `rankIndex '${name}'`)));
|
|
1118
|
-
assertNoReservedCreatedAtIndexFields(resolvedPartitionFields, `rankIndex '${name}' partitionBy`);
|
|
1119
|
-
const resolvedOrderFields = orderColumns.map((entry) => {
|
|
1120
|
-
const field = assertColumnInTable(entry.column, table.tableName, `rankIndex '${name}' orderBy`);
|
|
1121
|
-
assertNoReservedCreatedAtIndexFields([field], `rankIndex '${name}'`);
|
|
1122
|
-
assertRankOrderFieldType(entry.column, name);
|
|
1123
|
-
return {
|
|
1124
|
-
field,
|
|
1125
|
-
direction: entry.direction
|
|
1126
|
-
};
|
|
1127
|
-
});
|
|
1128
|
-
const resolvedSumField = sumField ? (() => {
|
|
1129
|
-
const field = assertColumnInTable(sumField, table.tableName, `rankIndex '${name}' sum`);
|
|
1130
|
-
assertNoReservedCreatedAtIndexFields([field], `rankIndex '${name}'`);
|
|
1131
|
-
assertAggregateSumFieldType(sumField, name);
|
|
1132
|
-
return field;
|
|
1133
|
-
})() : void 0;
|
|
1134
|
-
table.addRankIndex(name, {
|
|
1135
|
-
partitionFields: resolvedPartitionFields,
|
|
1136
|
-
orderFields: resolvedOrderFields,
|
|
1137
|
-
sumField: resolvedSumField
|
|
1138
|
-
});
|
|
1139
|
-
continue;
|
|
1140
|
-
}
|
|
1141
|
-
if (isConvexUniqueConstraintBuilder(entry)) {
|
|
1142
|
-
const { name, columns, nullsNotDistinct } = entry.config;
|
|
1143
|
-
const fields = columns.map((column) => assertColumnInTable(column, table.tableName, "Unique constraint"));
|
|
1144
|
-
assertNoReservedCreatedAtIndexFields(fields, "Unique constraint");
|
|
1145
|
-
const indexName = getUniqueIndexName(table.tableName, fields, name);
|
|
1146
|
-
table.addIndex(indexName, fields);
|
|
1147
|
-
table.addUniqueIndex(indexName, fields, nullsNotDistinct);
|
|
1148
|
-
continue;
|
|
1149
|
-
}
|
|
1150
|
-
if (isConvexForeignKeyBuilder(entry)) {
|
|
1151
|
-
const { name, columns, foreignColumns, onDelete, onUpdate } = entry.config;
|
|
1152
|
-
if (columns.length === 0 || foreignColumns.length === 0) throw new Error(`Foreign key on '${table.tableName}' requires at least one column.`);
|
|
1153
|
-
if (columns.length !== foreignColumns.length) throw new Error(`Foreign key on '${table.tableName}' must specify matching columns and foreignColumns.`);
|
|
1154
|
-
const localFields = columns.map((column) => assertColumnInTable(column, table.tableName, "Foreign key"));
|
|
1155
|
-
const foreignTableName = getColumnTableName(foreignColumns[0]);
|
|
1156
|
-
if (!foreignTableName) throw new Error(`Foreign key on '${table.tableName}' references a column without a table.`);
|
|
1157
|
-
const foreignTable = getColumnTable(foreignColumns[0]);
|
|
1158
|
-
const foreignFields = foreignColumns.map((column) => {
|
|
1159
|
-
const tableName = getColumnTableName(column);
|
|
1160
|
-
if (tableName && tableName !== foreignTableName) throw new Error(`Foreign key on '${table.tableName}' mixes foreign columns from '${foreignTableName}' and '${tableName}'.`);
|
|
1161
|
-
return getColumnName(column);
|
|
1162
|
-
});
|
|
1163
|
-
table.addForeignKey({
|
|
1164
|
-
name,
|
|
1165
|
-
columns: localFields,
|
|
1166
|
-
foreignTableName,
|
|
1167
|
-
foreignTable,
|
|
1168
|
-
foreignColumns: foreignFields,
|
|
1169
|
-
onDelete,
|
|
1170
|
-
onUpdate
|
|
1171
|
-
});
|
|
1172
|
-
continue;
|
|
1173
|
-
}
|
|
1174
|
-
if (isConvexCheckBuilder(entry)) {
|
|
1175
|
-
const { name, expression } = entry.config;
|
|
1176
|
-
table.addCheck(name, expression);
|
|
1177
|
-
continue;
|
|
1178
|
-
}
|
|
1179
|
-
if (isConvexSearchIndexBuilder(entry)) {
|
|
1180
|
-
const { name, searchField, filterFields, staged } = entry.config;
|
|
1181
|
-
const searchFieldName = assertColumnInTable(searchField, table.tableName, `Search index '${name}'`);
|
|
1182
|
-
assertNoReservedCreatedAtIndexFields([searchFieldName], `Search index '${name}'`);
|
|
1183
|
-
assertSearchFieldType(searchField, name);
|
|
1184
|
-
const filterFieldNames = filterFields.map((field) => assertColumnInTable(field, table.tableName, `Search index '${name}'`));
|
|
1185
|
-
assertNoReservedCreatedAtIndexFields(filterFieldNames, `Search index '${name}'`);
|
|
1186
|
-
table.addSearchIndex(name, {
|
|
1187
|
-
searchField: searchFieldName,
|
|
1188
|
-
filterFields: filterFieldNames,
|
|
1189
|
-
staged
|
|
1190
|
-
});
|
|
1191
|
-
continue;
|
|
1192
|
-
}
|
|
1193
|
-
if (isConvexVectorIndexBuilder(entry)) {
|
|
1194
|
-
const { name, vectorField, dimensions, filterFields, staged } = entry.config;
|
|
1195
|
-
if (dimensions === void 0) throw new Error(`Vector index '${name}' is missing dimensions. Call .dimensions(n) before using.`);
|
|
1196
|
-
const vectorFieldName = assertColumnInTable(vectorField, table.tableName, `Vector index '${name}'`);
|
|
1197
|
-
assertNoReservedCreatedAtIndexFields([vectorFieldName], `Vector index '${name}'`);
|
|
1198
|
-
assertVectorFieldType(vectorField, name);
|
|
1199
|
-
const columnDimensions = getColumnDimensions(vectorField);
|
|
1200
|
-
if (columnDimensions !== void 0 && columnDimensions !== dimensions) throw new Error(`Vector index '${name}' dimensions (${dimensions}) do not match vector column '${vectorFieldName}' dimensions (${columnDimensions}).`);
|
|
1201
|
-
const filterFieldNames = filterFields.map((field) => assertColumnInTable(field, table.tableName, `Vector index '${name}'`));
|
|
1202
|
-
assertNoReservedCreatedAtIndexFields(filterFieldNames, `Vector index '${name}'`);
|
|
1203
|
-
table.addVectorIndex(name, {
|
|
1204
|
-
vectorField: vectorFieldName,
|
|
1205
|
-
dimensions,
|
|
1206
|
-
filterFields: filterFieldNames,
|
|
1207
|
-
staged
|
|
1208
|
-
});
|
|
1209
|
-
continue;
|
|
1094
|
+
sum(field) {
|
|
1095
|
+
this.config.sumField = field;
|
|
1096
|
+
return this;
|
|
1097
|
+
}
|
|
1098
|
+
};
|
|
1099
|
+
var ConvexVectorIndexBuilder = class {
|
|
1100
|
+
static [entityKind] = "ConvexVectorIndexBuilder";
|
|
1101
|
+
[entityKind] = "ConvexVectorIndexBuilder";
|
|
1102
|
+
config;
|
|
1103
|
+
constructor(name, vectorField) {
|
|
1104
|
+
this.config = {
|
|
1105
|
+
name,
|
|
1106
|
+
vectorField,
|
|
1107
|
+
dimensions: void 0,
|
|
1108
|
+
filterFields: [],
|
|
1109
|
+
staged: false
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
dimensions(dimensions) {
|
|
1113
|
+
if (!Number.isInteger(dimensions)) throw new Error(`Vector index '${this.config.name}' dimensions must be an integer, got ${dimensions}`);
|
|
1114
|
+
if (dimensions <= 0) throw new Error(`Vector index '${this.config.name}' dimensions must be positive, got ${dimensions}`);
|
|
1115
|
+
if (dimensions > 1e4) console.warn(`Vector index '${this.config.name}' has unusually large dimensions (${dimensions}). Common values: 768, 1536, 3072`);
|
|
1116
|
+
this.config.dimensions = dimensions;
|
|
1117
|
+
return this;
|
|
1118
|
+
}
|
|
1119
|
+
filter(...fields) {
|
|
1120
|
+
this.config.filterFields = fields;
|
|
1121
|
+
return this;
|
|
1122
|
+
}
|
|
1123
|
+
staged() {
|
|
1124
|
+
this.config.staged = true;
|
|
1125
|
+
return this;
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
function index(name) {
|
|
1129
|
+
return new ConvexIndexBuilderOn(name, false);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
//#endregion
|
|
1133
|
+
//#region src/orm/rls/policies.ts
|
|
1134
|
+
var RlsPolicy = class {
|
|
1135
|
+
static [entityKind] = "RlsPolicy";
|
|
1136
|
+
[entityKind] = "RlsPolicy";
|
|
1137
|
+
as;
|
|
1138
|
+
for;
|
|
1139
|
+
to;
|
|
1140
|
+
using;
|
|
1141
|
+
withCheck;
|
|
1142
|
+
/** @internal */
|
|
1143
|
+
_linkedTable;
|
|
1144
|
+
constructor(name, config) {
|
|
1145
|
+
this.name = name;
|
|
1146
|
+
if (config) {
|
|
1147
|
+
this.as = config.as;
|
|
1148
|
+
this.for = config.for;
|
|
1149
|
+
this.to = config.to;
|
|
1150
|
+
this.using = config.using;
|
|
1151
|
+
this.withCheck = config.withCheck;
|
|
1210
1152
|
}
|
|
1211
|
-
throw new Error(`Unsupported extra config value in convexTable('${table.tableName}').`);
|
|
1212
1153
|
}
|
|
1154
|
+
link(table) {
|
|
1155
|
+
this._linkedTable = table;
|
|
1156
|
+
return this;
|
|
1157
|
+
}
|
|
1158
|
+
};
|
|
1159
|
+
function isRlsPolicy(value) {
|
|
1160
|
+
return !!value && typeof value === "object" && value[entityKind] === "RlsPolicy";
|
|
1213
1161
|
}
|
|
1162
|
+
|
|
1163
|
+
//#endregion
|
|
1164
|
+
//#region src/orm/table.ts
|
|
1214
1165
|
/**
|
|
1215
|
-
*
|
|
1216
|
-
* Provides all properties required by Convex's TableDefinition
|
|
1217
|
-
*
|
|
1218
|
-
* Following convex-ents pattern:
|
|
1219
|
-
* - Private fields for indexes (matches TableDefinition structure)
|
|
1220
|
-
* - Duck typing (defineSchema only checks object shape)
|
|
1221
|
-
* - Direct validator storage (no re-wrapping)
|
|
1166
|
+
* Reserved Convex system table names that cannot be used
|
|
1222
1167
|
*/
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
[
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
constructor(name, columns, polymorphicConfigs) {
|
|
1261
|
-
validateTableName(name);
|
|
1262
|
-
for (const columnName of Object.keys(columns)) if (RESERVED_COLUMN_NAMES.has(columnName)) throw new Error(`Column name '${columnName}' is reserved. System fields are managed by Convex ORM.`);
|
|
1263
|
-
this[TableName] = name;
|
|
1264
|
-
const namedColumns = Object.fromEntries(Object.entries(columns).map(([columnName, builder]) => {
|
|
1265
|
-
builder.config.name = columnName;
|
|
1266
|
-
builder.config.tableName = name;
|
|
1267
|
-
builder.config.table = this;
|
|
1268
|
-
return [columnName, builder];
|
|
1269
|
-
}));
|
|
1270
|
-
this[Columns] = namedColumns;
|
|
1271
|
-
this.tableName = name;
|
|
1272
|
-
if (polymorphicConfigs && polymorphicConfigs.length > 0) this[TablePolymorphic] = polymorphicConfigs;
|
|
1273
|
-
this.validator = createValidatorFromColumns(namedColumns);
|
|
1274
|
-
for (const [columnName, builder] of Object.entries(namedColumns)) {
|
|
1275
|
-
const config = builder.config;
|
|
1276
|
-
if (config?.isUnique) {
|
|
1277
|
-
const indexName = getUniqueIndexName(name, [columnName], config.uniqueName);
|
|
1278
|
-
const nullsNotDistinct = config.uniqueNulls === "not distinct";
|
|
1279
|
-
this.addIndex(indexName, [columnName]);
|
|
1280
|
-
this.addUniqueIndex(indexName, [columnName], nullsNotDistinct);
|
|
1281
|
-
}
|
|
1282
|
-
if (config?.referenceTable && (!config.foreignKeyConfigs || config.foreignKeyConfigs.length === 0)) this.addForeignKey({
|
|
1283
|
-
name: void 0,
|
|
1284
|
-
columns: [columnName],
|
|
1285
|
-
foreignTableName: config.referenceTable,
|
|
1286
|
-
foreignColumns: ["_id"]
|
|
1287
|
-
});
|
|
1288
|
-
if (config?.foreignKeyConfigs?.length) for (const foreignConfig of config.foreignKeyConfigs) this.deferredForeignKeys.push({
|
|
1289
|
-
localColumnName: columnName,
|
|
1290
|
-
ref: foreignConfig.ref,
|
|
1291
|
-
config: foreignConfig.config
|
|
1292
|
-
});
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
getPolymorphicConfigs() {
|
|
1296
|
-
return this[TablePolymorphic];
|
|
1297
|
-
}
|
|
1298
|
-
/**
|
|
1299
|
-
* Internal: add index to table from builder extraConfig
|
|
1300
|
-
*
|
|
1301
|
-
*/
|
|
1302
|
-
addIndex(name, fields) {
|
|
1303
|
-
this.indexes.push({
|
|
1304
|
-
indexDescriptor: name,
|
|
1305
|
-
fields
|
|
1306
|
-
});
|
|
1307
|
-
}
|
|
1308
|
-
/**
|
|
1309
|
-
* Internal: add unique index metadata for runtime enforcement
|
|
1310
|
-
*/
|
|
1311
|
-
addUniqueIndex(name, fields, nullsNotDistinct) {
|
|
1312
|
-
this.uniqueIndexes.push({
|
|
1313
|
-
name,
|
|
1314
|
-
fields,
|
|
1315
|
-
nullsNotDistinct
|
|
1316
|
-
});
|
|
1317
|
-
}
|
|
1318
|
-
addAggregateIndex(name, config) {
|
|
1319
|
-
if (this.aggregateIndexes.some((index) => index.name === name) || this.rankIndexes.some((index) => index.name === name)) throw new Error(`Duplicate aggregate index '${name}' on '${this.tableName}'.`);
|
|
1320
|
-
this.aggregateIndexes.push({
|
|
1321
|
-
name,
|
|
1322
|
-
fields: config.fields,
|
|
1323
|
-
countFields: config.countFields,
|
|
1324
|
-
sumFields: config.sumFields,
|
|
1325
|
-
avgFields: config.avgFields,
|
|
1326
|
-
minFields: config.minFields,
|
|
1327
|
-
maxFields: config.maxFields
|
|
1328
|
-
});
|
|
1168
|
+
const RESERVED_TABLES = new Set(["_storage", "_scheduled_functions"]);
|
|
1169
|
+
const RESERVED_COLUMN_NAMES = new Set([
|
|
1170
|
+
"id",
|
|
1171
|
+
"_id",
|
|
1172
|
+
"_creationTime"
|
|
1173
|
+
]);
|
|
1174
|
+
const DEFAULT_POLYMORPHIC_ALIAS = "details";
|
|
1175
|
+
const CONVEX_TABLE_FIELD_LIMIT = 1024;
|
|
1176
|
+
/**
|
|
1177
|
+
* Valid table name pattern: starts with letter/underscore, contains only alphanumeric and underscore
|
|
1178
|
+
*/
|
|
1179
|
+
const TABLE_NAME_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
1180
|
+
/**
|
|
1181
|
+
* Validate table name against Convex constraints
|
|
1182
|
+
*/
|
|
1183
|
+
function validateTableName(name) {
|
|
1184
|
+
if (RESERVED_TABLES.has(name)) throw new Error(`Table name '${name}' is reserved. System tables cannot be redefined.`);
|
|
1185
|
+
if (!TABLE_NAME_REGEX.test(name)) throw new Error(`Invalid table name '${name}'. Must start with letter, contain only alphanumeric and underscore.`);
|
|
1186
|
+
}
|
|
1187
|
+
/**
|
|
1188
|
+
* Create a Convex object validator from column builders
|
|
1189
|
+
*
|
|
1190
|
+
* Extracts .convexValidator from each column and creates v.object({...})
|
|
1191
|
+
* This is the core factory that bridges ORM columns to Convex validators.
|
|
1192
|
+
*
|
|
1193
|
+
* @param columns - Record of column name to column builder
|
|
1194
|
+
* @returns Convex object validator
|
|
1195
|
+
*/
|
|
1196
|
+
function createValidatorFromColumns(columns) {
|
|
1197
|
+
const validatorFields = Object.fromEntries(Object.entries(columns).map(([key, builder]) => [key, builder.convexValidator]));
|
|
1198
|
+
return v.object(validatorFields);
|
|
1199
|
+
}
|
|
1200
|
+
var ConvexDeletionBuilder = class {
|
|
1201
|
+
static [entityKind] = "ConvexDeletionBuilder";
|
|
1202
|
+
[entityKind] = "ConvexDeletionBuilder";
|
|
1203
|
+
constructor(config) {
|
|
1204
|
+
this.config = config;
|
|
1329
1205
|
}
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1206
|
+
};
|
|
1207
|
+
function isConvexIndexBuilder(value) {
|
|
1208
|
+
return typeof value === "object" && value !== null && value[entityKind] === "ConvexIndexBuilder";
|
|
1209
|
+
}
|
|
1210
|
+
function isConvexIndexBuilderOn(value) {
|
|
1211
|
+
return typeof value === "object" && value !== null && value[entityKind] === "ConvexIndexBuilderOn";
|
|
1212
|
+
}
|
|
1213
|
+
function isConvexAggregateIndexBuilder(value) {
|
|
1214
|
+
return typeof value === "object" && value !== null && value[entityKind] === "ConvexAggregateIndexBuilder";
|
|
1215
|
+
}
|
|
1216
|
+
function isConvexAggregateIndexBuilderOn(value) {
|
|
1217
|
+
return typeof value === "object" && value !== null && value[entityKind] === "ConvexAggregateIndexBuilderOn";
|
|
1218
|
+
}
|
|
1219
|
+
function isConvexRankIndexBuilderOn(value) {
|
|
1220
|
+
return typeof value === "object" && value !== null && value[entityKind] === "ConvexRankIndexBuilderOn";
|
|
1221
|
+
}
|
|
1222
|
+
function isConvexRankIndexBuilder(value) {
|
|
1223
|
+
return typeof value === "object" && value !== null && value[entityKind] === "ConvexRankIndexBuilder";
|
|
1224
|
+
}
|
|
1225
|
+
function isConvexUniqueConstraintBuilderOn(value) {
|
|
1226
|
+
return typeof value === "object" && value !== null && value[entityKind] === "ConvexUniqueConstraintBuilderOn";
|
|
1227
|
+
}
|
|
1228
|
+
function isConvexForeignKeyBuilder(value) {
|
|
1229
|
+
return typeof value === "object" && value !== null && value[entityKind] === "ConvexForeignKeyBuilder";
|
|
1230
|
+
}
|
|
1231
|
+
function isConvexCheckBuilder(value) {
|
|
1232
|
+
return typeof value === "object" && value !== null && value[entityKind] === "ConvexCheckBuilder";
|
|
1233
|
+
}
|
|
1234
|
+
function isConvexSearchIndexBuilderOn(value) {
|
|
1235
|
+
return typeof value === "object" && value !== null && value[entityKind] === "ConvexSearchIndexBuilderOn";
|
|
1236
|
+
}
|
|
1237
|
+
function isConvexUniqueConstraintBuilder(value) {
|
|
1238
|
+
return typeof value === "object" && value !== null && value[entityKind] === "ConvexUniqueConstraintBuilder";
|
|
1239
|
+
}
|
|
1240
|
+
function isConvexSearchIndexBuilder(value) {
|
|
1241
|
+
return typeof value === "object" && value !== null && value[entityKind] === "ConvexSearchIndexBuilder";
|
|
1242
|
+
}
|
|
1243
|
+
function isConvexVectorIndexBuilderOn(value) {
|
|
1244
|
+
return typeof value === "object" && value !== null && value[entityKind] === "ConvexVectorIndexBuilderOn";
|
|
1245
|
+
}
|
|
1246
|
+
function isConvexVectorIndexBuilder(value) {
|
|
1247
|
+
return typeof value === "object" && value !== null && value[entityKind] === "ConvexVectorIndexBuilder";
|
|
1248
|
+
}
|
|
1249
|
+
function isConvexDeletionBuilder(value) {
|
|
1250
|
+
return typeof value === "object" && value !== null && value[entityKind] === "ConvexDeletionBuilder";
|
|
1251
|
+
}
|
|
1252
|
+
function isConvexLifecycleBuilder(value) {
|
|
1253
|
+
return typeof value === "object" && value !== null && value[entityKind] === "ConvexLifecycleBuilder";
|
|
1254
|
+
}
|
|
1255
|
+
function getColumnName(column) {
|
|
1256
|
+
const config = column.config;
|
|
1257
|
+
if (!config?.name) throw new Error("Invalid index column: expected a convexTable column builder.");
|
|
1258
|
+
return config.name;
|
|
1259
|
+
}
|
|
1260
|
+
function getColumnType(column) {
|
|
1261
|
+
return column.config?.columnType;
|
|
1262
|
+
}
|
|
1263
|
+
function getColumnDimensions(column) {
|
|
1264
|
+
return column.config?.dimensions;
|
|
1265
|
+
}
|
|
1266
|
+
function getColumnTableName(column) {
|
|
1267
|
+
const config = column.config;
|
|
1268
|
+
return config?.tableName ?? config?.referenceTable;
|
|
1269
|
+
}
|
|
1270
|
+
function getColumnTable(column) {
|
|
1271
|
+
return column.config?.table;
|
|
1272
|
+
}
|
|
1273
|
+
function getUniqueIndexName(tableName, fields, explicitName) {
|
|
1274
|
+
if (explicitName) return explicitName;
|
|
1275
|
+
return `${tableName}_${fields.join("_")}_unique`;
|
|
1276
|
+
}
|
|
1277
|
+
function assertColumnInTable(column, expectedTable, context) {
|
|
1278
|
+
const tableName = getColumnTableName(column);
|
|
1279
|
+
if (tableName && tableName !== expectedTable) throw new Error(`${context} references column from '${tableName}', but belongs to '${expectedTable}'.`);
|
|
1280
|
+
return getColumnName(column);
|
|
1281
|
+
}
|
|
1282
|
+
function assertNoReservedCreatedAtIndexFields(fields, context) {
|
|
1283
|
+
if (fields.includes("createdAt")) throw new Error(`${context} cannot use 'createdAt'. 'createdAt' is reserved and maps to internal '_creationTime'.`);
|
|
1284
|
+
}
|
|
1285
|
+
function assertSearchFieldType(column, indexName) {
|
|
1286
|
+
const columnType = getColumnType(column) ?? "unknown";
|
|
1287
|
+
if (columnType !== "ConvexText") throw new Error(`Search index '${indexName}' only supports text() columns. Field '${getColumnName(column)}' is type '${columnType}'.`);
|
|
1288
|
+
}
|
|
1289
|
+
function assertVectorFieldType(column, indexName) {
|
|
1290
|
+
const columnType = getColumnType(column) ?? "unknown";
|
|
1291
|
+
if (columnType !== "ConvexVector") throw new Error(`Vector index '${indexName}' requires a vector() column. Field '${getColumnName(column)}' is type '${columnType}'.`);
|
|
1292
|
+
}
|
|
1293
|
+
function assertAggregateSumFieldType(column, indexName) {
|
|
1294
|
+
const columnType = getColumnType(column) ?? "unknown";
|
|
1295
|
+
if (!["ConvexNumber", "ConvexTimestamp"].includes(columnType)) throw new Error(`aggregateIndex '${indexName}' sum() supports integer()/timestamp() columns only. Field '${getColumnName(column)}' is type '${columnType}'.`);
|
|
1296
|
+
}
|
|
1297
|
+
function assertAggregateAvgFieldType(column, indexName) {
|
|
1298
|
+
const columnType = getColumnType(column) ?? "unknown";
|
|
1299
|
+
if (!["ConvexNumber", "ConvexTimestamp"].includes(columnType)) throw new Error(`aggregateIndex '${indexName}' avg() supports integer()/timestamp() columns only. Field '${getColumnName(column)}' is type '${columnType}'.`);
|
|
1300
|
+
}
|
|
1301
|
+
function assertAggregateComparableFieldType(column, indexName, method) {
|
|
1302
|
+
const columnType = getColumnType(column) ?? "unknown";
|
|
1303
|
+
if (![
|
|
1304
|
+
"ConvexNumber",
|
|
1305
|
+
"ConvexTimestamp",
|
|
1306
|
+
"ConvexDate",
|
|
1307
|
+
"ConvexText",
|
|
1308
|
+
"ConvexBoolean",
|
|
1309
|
+
"ConvexId"
|
|
1310
|
+
].includes(columnType)) throw new Error(`aggregateIndex '${indexName}' ${method}() does not support column type '${columnType}' on '${getColumnName(column)}'.`);
|
|
1311
|
+
}
|
|
1312
|
+
function assertRankOrderFieldType(column, indexName) {
|
|
1313
|
+
const columnType = getColumnType(column) ?? "unknown";
|
|
1314
|
+
if (![
|
|
1315
|
+
"ConvexNumber",
|
|
1316
|
+
"ConvexTimestamp",
|
|
1317
|
+
"ConvexDate"
|
|
1318
|
+
].includes(columnType)) throw new Error(`rankIndex '${indexName}' orderBy() supports integer()/timestamp()/date() columns only. Field '${getColumnName(column)}' is type '${columnType}'.`);
|
|
1319
|
+
}
|
|
1320
|
+
const dedupeFieldNames = (fields) => [...new Set(fields)];
|
|
1321
|
+
const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1322
|
+
const isColumnBuilder = (value) => isRecord$1(value) && typeof value.build === "function";
|
|
1323
|
+
const getDiscriminatorConfig = (value) => {
|
|
1324
|
+
if (!isColumnBuilder(value)) return;
|
|
1325
|
+
const discriminator = value.config?.discriminator;
|
|
1326
|
+
if (!discriminator) return;
|
|
1327
|
+
return discriminator;
|
|
1328
|
+
};
|
|
1329
|
+
const getPolymorphicFieldSignature = (column) => {
|
|
1330
|
+
const validator = column.convexValidator ?? column.build();
|
|
1331
|
+
return JSON.stringify({
|
|
1332
|
+
columnType: column.config?.columnType,
|
|
1333
|
+
validator: validator?.json
|
|
1334
|
+
});
|
|
1335
|
+
};
|
|
1336
|
+
function resolveTableColumns(tableName, columns) {
|
|
1337
|
+
const resolvedColumns = {};
|
|
1338
|
+
const pendingPolymorphic = [];
|
|
1339
|
+
for (const [columnName, rawBuilder] of Object.entries(columns)) {
|
|
1340
|
+
if (!isColumnBuilder(rawBuilder)) throw new Error(`Column '${columnName}' on '${tableName}' must be a column builder.`);
|
|
1341
|
+
resolvedColumns[columnName] = rawBuilder;
|
|
1342
|
+
const discriminatorConfig = getDiscriminatorConfig(rawBuilder);
|
|
1343
|
+
if (!discriminatorConfig) continue;
|
|
1344
|
+
if (!isRecord$1(discriminatorConfig.variants) || Object.keys(discriminatorConfig.variants).length === 0) throw new Error(`discriminator('${tableName}.${columnName}') requires at least one variant.`);
|
|
1345
|
+
const alias = discriminatorConfig.as === void 0 ? DEFAULT_POLYMORPHIC_ALIAS : discriminatorConfig.as;
|
|
1346
|
+
if (typeof alias !== "string" || alias.length === 0) throw new Error(`discriminator('${tableName}.${columnName}').as must be a non-empty string.`);
|
|
1347
|
+
pendingPolymorphic.push({
|
|
1348
|
+
discriminator: columnName,
|
|
1349
|
+
alias,
|
|
1350
|
+
variants: discriminatorConfig.variants
|
|
1337
1351
|
});
|
|
1338
1352
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
})
|
|
1380
|
-
}
|
|
1381
|
-
/**
|
|
1382
|
-
* Internal: attach an RLS policy to this table
|
|
1383
|
-
*/
|
|
1384
|
-
addRlsPolicy(policy) {
|
|
1385
|
-
this[RlsPolicies].push(policy);
|
|
1386
|
-
this[EnableRLS] = true;
|
|
1387
|
-
}
|
|
1388
|
-
/**
|
|
1389
|
-
* Internal: return attached RLS policies
|
|
1390
|
-
*/
|
|
1391
|
-
getRlsPolicies() {
|
|
1392
|
-
return this[RlsPolicies];
|
|
1393
|
-
}
|
|
1394
|
-
/**
|
|
1395
|
-
* Internal: check if RLS is enabled on this table
|
|
1396
|
-
*/
|
|
1397
|
-
isRlsEnabled() {
|
|
1398
|
-
return this[EnableRLS];
|
|
1399
|
-
}
|
|
1400
|
-
/**
|
|
1401
|
-
* Internal: add foreign key metadata for runtime enforcement
|
|
1402
|
-
*/
|
|
1403
|
-
addForeignKey(definition) {
|
|
1404
|
-
const matches = (existing) => {
|
|
1405
|
-
if (existing.foreignTableName !== definition.foreignTableName) return false;
|
|
1406
|
-
if (existing.columns.length !== definition.columns.length) return false;
|
|
1407
|
-
if (existing.foreignColumns.length !== definition.foreignColumns.length) return false;
|
|
1408
|
-
for (let i = 0; i < existing.columns.length; i++) if (existing.columns[i] !== definition.columns[i]) return false;
|
|
1409
|
-
for (let i = 0; i < existing.foreignColumns.length; i++) if (existing.foreignColumns[i] !== definition.foreignColumns[i]) return false;
|
|
1410
|
-
return true;
|
|
1411
|
-
};
|
|
1412
|
-
this.foreignKeys = this.foreignKeys.filter((existing) => !matches(existing));
|
|
1413
|
-
this.foreignKeys.push(definition);
|
|
1353
|
+
if (pendingPolymorphic.length > 1) throw new Error(`Only one discriminator(...) column is currently supported on '${tableName}'.`);
|
|
1354
|
+
const polymorphicConfigs = [];
|
|
1355
|
+
for (const pending of pendingPolymorphic) {
|
|
1356
|
+
if (pending.alias in resolvedColumns) throw new Error(`discriminator('${tableName}.${pending.discriminator}') alias '${pending.alias}' collides with an existing column.`);
|
|
1357
|
+
const generatedFieldMap = /* @__PURE__ */ new Map();
|
|
1358
|
+
const variantRuntime = {};
|
|
1359
|
+
for (const [variantKey, rawVariantColumns] of Object.entries(pending.variants)) {
|
|
1360
|
+
if (!isRecord$1(rawVariantColumns)) throw new Error(`discriminator('${tableName}.${pending.discriminator}') variant '${variantKey}' must be an object.`);
|
|
1361
|
+
const fieldNames = [];
|
|
1362
|
+
const requiredFieldNames = [];
|
|
1363
|
+
for (const [fieldName, rawFieldBuilder] of Object.entries(rawVariantColumns)) {
|
|
1364
|
+
if (!isColumnBuilder(rawFieldBuilder)) throw new Error(`discriminator('${tableName}.${pending.discriminator}').variants.${variantKey}.${fieldName} must be a column builder.`);
|
|
1365
|
+
if (fieldName in resolvedColumns) throw new Error(`discriminator('${tableName}.${pending.discriminator}').variants.${variantKey}.${fieldName} collides with an existing table column.`);
|
|
1366
|
+
const fieldBuilder = rawFieldBuilder;
|
|
1367
|
+
const fieldConfig = fieldBuilder.config;
|
|
1368
|
+
const isRequiredForVariant = fieldConfig?.notNull === true && fieldConfig.hasDefault !== true && typeof fieldConfig.defaultFn !== "function";
|
|
1369
|
+
const signature = getPolymorphicFieldSignature(fieldBuilder);
|
|
1370
|
+
const existing = generatedFieldMap.get(fieldName);
|
|
1371
|
+
if (existing && existing.signature !== signature) throw new Error(`discriminator('${tableName}.${pending.discriminator}') field '${fieldName}' has conflicting builder signatures across variants.`);
|
|
1372
|
+
if (!existing) {
|
|
1373
|
+
if (fieldConfig) fieldConfig.notNull = false;
|
|
1374
|
+
generatedFieldMap.set(fieldName, {
|
|
1375
|
+
builder: fieldBuilder,
|
|
1376
|
+
signature
|
|
1377
|
+
});
|
|
1378
|
+
}
|
|
1379
|
+
fieldNames.push(fieldName);
|
|
1380
|
+
if (isRequiredForVariant) requiredFieldNames.push(fieldName);
|
|
1381
|
+
}
|
|
1382
|
+
variantRuntime[variantKey] = {
|
|
1383
|
+
fieldNames,
|
|
1384
|
+
requiredFieldNames
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1387
|
+
for (const [fieldName, { builder }] of generatedFieldMap.entries()) resolvedColumns[fieldName] = builder;
|
|
1388
|
+
polymorphicConfigs.push({
|
|
1389
|
+
discriminator: pending.discriminator,
|
|
1390
|
+
alias: pending.alias,
|
|
1391
|
+
generatedFieldNames: Object.freeze([...generatedFieldMap.keys()]),
|
|
1392
|
+
variants: Object.freeze(variantRuntime)
|
|
1393
|
+
});
|
|
1414
1394
|
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1395
|
+
if (Object.keys(resolvedColumns).length > CONVEX_TABLE_FIELD_LIMIT) throw new Error(`Table '${tableName}' exceeds Convex field count limit (${CONVEX_TABLE_FIELD_LIMIT}) after discriminator expansion.`);
|
|
1396
|
+
return {
|
|
1397
|
+
columns: resolvedColumns,
|
|
1398
|
+
polymorphicConfigs
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
function applyExtraConfig(table, config) {
|
|
1402
|
+
if (!config) return;
|
|
1403
|
+
const entries = Array.isArray(config) ? config : Object.values(config);
|
|
1404
|
+
for (const entry of entries) {
|
|
1405
|
+
if (isConvexIndexBuilderOn(entry)) throw new Error(`Invalid index definition on '${table.tableName}'. Did you forget to call .on(...)?`);
|
|
1406
|
+
if (isConvexUniqueConstraintBuilderOn(entry)) throw new Error(`Invalid unique constraint definition on '${table.tableName}'. Did you forget to call .on(...)?`);
|
|
1407
|
+
if (isConvexAggregateIndexBuilderOn(entry)) throw new Error(`Invalid aggregate index definition on '${table.tableName}'. Did you forget to call .on(...) or .all()?`);
|
|
1408
|
+
if (isConvexRankIndexBuilderOn(entry)) throw new Error(`Invalid rank index definition on '${table.tableName}'. Did you forget to call .partitionBy(...) or .all()?`);
|
|
1409
|
+
if (isConvexSearchIndexBuilderOn(entry)) throw new Error(`Invalid search index definition on '${table.tableName}'. Did you forget to call .on(...)?`);
|
|
1410
|
+
if (isConvexVectorIndexBuilderOn(entry)) throw new Error(`Invalid vector index definition on '${table.tableName}'. Did you forget to call .on(...)?`);
|
|
1411
|
+
if (isRlsPolicy(entry)) {
|
|
1412
|
+
const target = entry._linkedTable ?? table;
|
|
1413
|
+
if (typeof target.addRlsPolicy === "function") target.addRlsPolicy(entry);
|
|
1414
|
+
else {
|
|
1415
|
+
const policies = target[RlsPolicies] ?? [];
|
|
1416
|
+
policies.push(entry);
|
|
1417
|
+
target[RlsPolicies] = policies;
|
|
1418
|
+
target[EnableRLS] = true;
|
|
1425
1419
|
}
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
if (
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1420
|
+
continue;
|
|
1421
|
+
}
|
|
1422
|
+
if (isConvexDeletionBuilder(entry)) {
|
|
1423
|
+
if (table[TableDeleteConfig]) throw new Error(`Only one deletion(...) config can be defined for '${table.tableName}'.`);
|
|
1424
|
+
table[TableDeleteConfig] = {
|
|
1425
|
+
mode: entry.config.mode,
|
|
1426
|
+
delayMs: entry.config.delayMs
|
|
1427
|
+
};
|
|
1428
|
+
continue;
|
|
1429
|
+
}
|
|
1430
|
+
if (isConvexLifecycleBuilder(entry)) throw new Error(`Lifecycle hooks are no longer supported inside convexTable('${table.tableName}', ..., extraConfig). Export schema triggers with defineTriggers(relations, { ... }) from schema.ts.`);
|
|
1431
|
+
if (isConvexIndexBuilder(entry)) {
|
|
1432
|
+
const { name, columns, unique, where } = entry.config;
|
|
1433
|
+
if (where) throw new Error(`Convex does not support partial indexes. Remove .where(...) from index '${name}'.`);
|
|
1434
|
+
if (unique) {}
|
|
1435
|
+
const fields = columns.map((column) => assertColumnInTable(column, table.tableName, `Index '${name}'`));
|
|
1436
|
+
assertNoReservedCreatedAtIndexFields(fields, `Index '${name}'`);
|
|
1437
|
+
table.addIndex(name, fields);
|
|
1438
|
+
if (unique) table.addUniqueIndex(name, fields, false);
|
|
1439
|
+
continue;
|
|
1440
|
+
}
|
|
1441
|
+
if (isConvexAggregateIndexBuilder(entry)) {
|
|
1442
|
+
const { name, columns, countFields, sumFields, avgFields, minFields, maxFields } = entry.config;
|
|
1443
|
+
const fields = columns.map((column) => assertColumnInTable(column, table.tableName, `Aggregate index '${name}'`));
|
|
1444
|
+
assertNoReservedCreatedAtIndexFields(fields, `Aggregate index '${name}'`);
|
|
1445
|
+
const resolvedCountFields = dedupeFieldNames(countFields.map((column) => assertColumnInTable(column, table.tableName, `Aggregate index '${name}' count`)));
|
|
1446
|
+
assertNoReservedCreatedAtIndexFields(resolvedCountFields, `Aggregate index '${name}' count`);
|
|
1447
|
+
const resolvedSumFields = dedupeFieldNames(sumFields.map((column) => {
|
|
1448
|
+
const field = assertColumnInTable(column, table.tableName, `Aggregate index '${name}' sum`);
|
|
1449
|
+
assertAggregateSumFieldType(column, name);
|
|
1450
|
+
return field;
|
|
1451
|
+
}));
|
|
1452
|
+
assertNoReservedCreatedAtIndexFields(resolvedSumFields, `Aggregate index '${name}' sum`);
|
|
1453
|
+
const resolvedAvgFields = dedupeFieldNames(avgFields.map((column) => {
|
|
1454
|
+
const field = assertColumnInTable(column, table.tableName, `Aggregate index '${name}' avg`);
|
|
1455
|
+
assertAggregateAvgFieldType(column, name);
|
|
1456
|
+
return field;
|
|
1457
|
+
}));
|
|
1458
|
+
assertNoReservedCreatedAtIndexFields(resolvedAvgFields, `Aggregate index '${name}' avg`);
|
|
1459
|
+
const resolvedMinFields = dedupeFieldNames(minFields.map((column) => {
|
|
1460
|
+
const field = assertColumnInTable(column, table.tableName, `Aggregate index '${name}' min`);
|
|
1461
|
+
assertAggregateComparableFieldType(column, name, "min");
|
|
1462
|
+
return field;
|
|
1463
|
+
}));
|
|
1464
|
+
assertNoReservedCreatedAtIndexFields(resolvedMinFields, `Aggregate index '${name}' min`);
|
|
1465
|
+
const resolvedMaxFields = dedupeFieldNames(maxFields.map((column) => {
|
|
1466
|
+
const field = assertColumnInTable(column, table.tableName, `Aggregate index '${name}' max`);
|
|
1467
|
+
assertAggregateComparableFieldType(column, name, "max");
|
|
1468
|
+
return field;
|
|
1469
|
+
}));
|
|
1470
|
+
assertNoReservedCreatedAtIndexFields(resolvedMaxFields, `Aggregate index '${name}' max`);
|
|
1471
|
+
table.addAggregateIndex(name, {
|
|
1472
|
+
fields,
|
|
1473
|
+
countFields: resolvedCountFields,
|
|
1474
|
+
sumFields: resolvedSumFields,
|
|
1475
|
+
avgFields: resolvedAvgFields,
|
|
1476
|
+
minFields: resolvedMinFields,
|
|
1477
|
+
maxFields: resolvedMaxFields
|
|
1478
|
+
});
|
|
1479
|
+
continue;
|
|
1480
|
+
}
|
|
1481
|
+
if (isConvexRankIndexBuilder(entry)) {
|
|
1482
|
+
const { name, partitionColumns, orderColumns, sumField } = entry.config;
|
|
1483
|
+
if (!orderColumns.length) throw new Error(`rankIndex '${name}' on '${table.tableName}' must declare at least one orderBy(...) column.`);
|
|
1484
|
+
const resolvedPartitionFields = dedupeFieldNames(partitionColumns.map((column) => assertColumnInTable(column, table.tableName, `rankIndex '${name}'`)));
|
|
1485
|
+
assertNoReservedCreatedAtIndexFields(resolvedPartitionFields, `rankIndex '${name}' partitionBy`);
|
|
1486
|
+
const resolvedOrderFields = orderColumns.map((entry) => {
|
|
1487
|
+
const field = assertColumnInTable(entry.column, table.tableName, `rankIndex '${name}' orderBy`);
|
|
1488
|
+
assertNoReservedCreatedAtIndexFields([field], `rankIndex '${name}'`);
|
|
1489
|
+
assertRankOrderFieldType(entry.column, name);
|
|
1490
|
+
return {
|
|
1491
|
+
field,
|
|
1492
|
+
direction: entry.direction
|
|
1493
|
+
};
|
|
1494
|
+
});
|
|
1495
|
+
const resolvedSumField = sumField ? (() => {
|
|
1496
|
+
const field = assertColumnInTable(sumField, table.tableName, `rankIndex '${name}' sum`);
|
|
1497
|
+
assertNoReservedCreatedAtIndexFields([field], `rankIndex '${name}'`);
|
|
1498
|
+
assertAggregateSumFieldType(sumField, name);
|
|
1499
|
+
return field;
|
|
1500
|
+
})() : void 0;
|
|
1501
|
+
table.addRankIndex(name, {
|
|
1502
|
+
partitionFields: resolvedPartitionFields,
|
|
1503
|
+
orderFields: resolvedOrderFields,
|
|
1504
|
+
sumField: resolvedSumField
|
|
1505
|
+
});
|
|
1506
|
+
continue;
|
|
1507
|
+
}
|
|
1508
|
+
if (isConvexUniqueConstraintBuilder(entry)) {
|
|
1509
|
+
const { name, columns, nullsNotDistinct } = entry.config;
|
|
1510
|
+
const fields = columns.map((column) => assertColumnInTable(column, table.tableName, "Unique constraint"));
|
|
1511
|
+
assertNoReservedCreatedAtIndexFields(fields, "Unique constraint");
|
|
1512
|
+
const indexName = getUniqueIndexName(table.tableName, fields, name);
|
|
1513
|
+
table.addIndex(indexName, fields);
|
|
1514
|
+
table.addUniqueIndex(indexName, fields, nullsNotDistinct);
|
|
1515
|
+
continue;
|
|
1516
|
+
}
|
|
1517
|
+
if (isConvexForeignKeyBuilder(entry)) {
|
|
1518
|
+
const { name, columns, foreignColumns, onDelete, onUpdate } = entry.config;
|
|
1519
|
+
if (columns.length === 0 || foreignColumns.length === 0) throw new Error(`Foreign key on '${table.tableName}' requires at least one column.`);
|
|
1520
|
+
if (columns.length !== foreignColumns.length) throw new Error(`Foreign key on '${table.tableName}' must specify matching columns and foreignColumns.`);
|
|
1521
|
+
const localFields = columns.map((column) => assertColumnInTable(column, table.tableName, "Foreign key"));
|
|
1522
|
+
const foreignTableName = getColumnTableName(foreignColumns[0]);
|
|
1523
|
+
if (!foreignTableName) throw new Error(`Foreign key on '${table.tableName}' references a column without a table.`);
|
|
1524
|
+
const foreignTable = getColumnTable(foreignColumns[0]);
|
|
1525
|
+
const foreignFields = foreignColumns.map((column) => {
|
|
1526
|
+
const tableName = getColumnTableName(column);
|
|
1527
|
+
if (tableName && tableName !== foreignTableName) throw new Error(`Foreign key on '${table.tableName}' mixes foreign columns from '${foreignTableName}' and '${tableName}'.`);
|
|
1528
|
+
return getColumnName(column);
|
|
1529
|
+
});
|
|
1530
|
+
table.addForeignKey({
|
|
1531
|
+
name,
|
|
1532
|
+
columns: localFields,
|
|
1434
1533
|
foreignTableName,
|
|
1435
1534
|
foreignTable,
|
|
1436
|
-
foreignColumns:
|
|
1437
|
-
onDelete
|
|
1438
|
-
onUpdate
|
|
1535
|
+
foreignColumns: foreignFields,
|
|
1536
|
+
onDelete,
|
|
1537
|
+
onUpdate
|
|
1439
1538
|
});
|
|
1539
|
+
continue;
|
|
1440
1540
|
}
|
|
1441
|
-
|
|
1541
|
+
if (isConvexCheckBuilder(entry)) {
|
|
1542
|
+
const { name, expression } = entry.config;
|
|
1543
|
+
table.addCheck(name, expression);
|
|
1544
|
+
continue;
|
|
1545
|
+
}
|
|
1546
|
+
if (isConvexSearchIndexBuilder(entry)) {
|
|
1547
|
+
const { name, searchField, filterFields, staged } = entry.config;
|
|
1548
|
+
const searchFieldName = assertColumnInTable(searchField, table.tableName, `Search index '${name}'`);
|
|
1549
|
+
assertNoReservedCreatedAtIndexFields([searchFieldName], `Search index '${name}'`);
|
|
1550
|
+
assertSearchFieldType(searchField, name);
|
|
1551
|
+
const filterFieldNames = filterFields.map((field) => assertColumnInTable(field, table.tableName, `Search index '${name}'`));
|
|
1552
|
+
assertNoReservedCreatedAtIndexFields(filterFieldNames, `Search index '${name}'`);
|
|
1553
|
+
table.addSearchIndex(name, {
|
|
1554
|
+
searchField: searchFieldName,
|
|
1555
|
+
filterFields: filterFieldNames,
|
|
1556
|
+
staged
|
|
1557
|
+
});
|
|
1558
|
+
continue;
|
|
1559
|
+
}
|
|
1560
|
+
if (isConvexVectorIndexBuilder(entry)) {
|
|
1561
|
+
const { name, vectorField, dimensions, filterFields, staged } = entry.config;
|
|
1562
|
+
if (dimensions === void 0) throw new Error(`Vector index '${name}' is missing dimensions. Call .dimensions(n) before using.`);
|
|
1563
|
+
const vectorFieldName = assertColumnInTable(vectorField, table.tableName, `Vector index '${name}'`);
|
|
1564
|
+
assertNoReservedCreatedAtIndexFields([vectorFieldName], `Vector index '${name}'`);
|
|
1565
|
+
assertVectorFieldType(vectorField, name);
|
|
1566
|
+
const columnDimensions = getColumnDimensions(vectorField);
|
|
1567
|
+
if (columnDimensions !== void 0 && columnDimensions !== dimensions) throw new Error(`Vector index '${name}' dimensions (${dimensions}) do not match vector column '${vectorFieldName}' dimensions (${columnDimensions}).`);
|
|
1568
|
+
const filterFieldNames = filterFields.map((field) => assertColumnInTable(field, table.tableName, `Vector index '${name}'`));
|
|
1569
|
+
assertNoReservedCreatedAtIndexFields(filterFieldNames, `Vector index '${name}'`);
|
|
1570
|
+
table.addVectorIndex(name, {
|
|
1571
|
+
vectorField: vectorFieldName,
|
|
1572
|
+
dimensions,
|
|
1573
|
+
filterFields: filterFieldNames,
|
|
1574
|
+
staged
|
|
1575
|
+
});
|
|
1576
|
+
continue;
|
|
1577
|
+
}
|
|
1578
|
+
throw new Error(`Unsupported extra config value in convexTable('${table.tableName}').`);
|
|
1442
1579
|
}
|
|
1580
|
+
}
|
|
1581
|
+
/**
|
|
1582
|
+
* ConvexTable implementation class
|
|
1583
|
+
* Provides all properties required by Convex's TableDefinition
|
|
1584
|
+
*
|
|
1585
|
+
* Following convex-ents pattern:
|
|
1586
|
+
* - Private fields for indexes (matches TableDefinition structure)
|
|
1587
|
+
* - Duck typing (defineSchema only checks object shape)
|
|
1588
|
+
* - Direct validator storage (no re-wrapping)
|
|
1589
|
+
*/
|
|
1590
|
+
var ConvexTableImpl = class {
|
|
1443
1591
|
/**
|
|
1444
|
-
*
|
|
1592
|
+
* Required by TableDefinition
|
|
1593
|
+
* Public validator property containing v.object({...}) with all column validators
|
|
1445
1594
|
*/
|
|
1446
|
-
|
|
1447
|
-
this.resolveDeferredForeignKeys();
|
|
1448
|
-
return this.foreignKeys;
|
|
1449
|
-
}
|
|
1450
|
-
addCheck(name, expression) {
|
|
1451
|
-
this.checks.push({
|
|
1452
|
-
name,
|
|
1453
|
-
expression
|
|
1454
|
-
});
|
|
1455
|
-
}
|
|
1456
|
-
getChecks() {
|
|
1457
|
-
return this.checks;
|
|
1458
|
-
}
|
|
1595
|
+
validator;
|
|
1459
1596
|
/**
|
|
1460
|
-
*
|
|
1597
|
+
* TableDefinition private fields
|
|
1598
|
+
* These satisfy structural typing requirements for defineSchema()
|
|
1461
1599
|
*/
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1600
|
+
indexes = [];
|
|
1601
|
+
uniqueIndexes = [];
|
|
1602
|
+
aggregateIndexes = [];
|
|
1603
|
+
rankIndexes = [];
|
|
1604
|
+
foreignKeys = [];
|
|
1605
|
+
deferredForeignKeys = [];
|
|
1606
|
+
deferredForeignKeysResolved = false;
|
|
1607
|
+
stagedDbIndexes = [];
|
|
1608
|
+
searchIndexes = [];
|
|
1609
|
+
stagedSearchIndexes = [];
|
|
1610
|
+
vectorIndexes = [];
|
|
1611
|
+
stagedVectorIndexes = [];
|
|
1612
|
+
checks = [];
|
|
1471
1613
|
/**
|
|
1472
|
-
*
|
|
1614
|
+
* Symbol-based metadata storage
|
|
1473
1615
|
*/
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
if (config.staged) this.stagedVectorIndexes.push(entry);
|
|
1482
|
-
else this.vectorIndexes.push(entry);
|
|
1483
|
-
}
|
|
1616
|
+
[TableName];
|
|
1617
|
+
[Columns];
|
|
1618
|
+
[Brand] = "ConvexTable";
|
|
1619
|
+
[EnableRLS] = false;
|
|
1620
|
+
[RlsPolicies] = [];
|
|
1621
|
+
[TableDeleteConfig];
|
|
1622
|
+
[TablePolymorphic];
|
|
1484
1623
|
/**
|
|
1485
|
-
*
|
|
1486
|
-
* Mirrors convex/server TableDefinition.export().
|
|
1624
|
+
* Public tableName for convenience
|
|
1487
1625
|
*/
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
applyExtraConfig(rawTable, extraConfig?.(table));
|
|
1523
|
-
return table;
|
|
1524
|
-
};
|
|
1525
|
-
const convexTableWithRLS = (name, columns, extraConfig) => {
|
|
1526
|
-
const table = convexTableInternal(name, columns, extraConfig);
|
|
1527
|
-
table[EnableRLS] = true;
|
|
1528
|
-
return table;
|
|
1529
|
-
};
|
|
1530
|
-
const convexTable = Object.assign(convexTableInternal, { withRLS: convexTableWithRLS });
|
|
1531
|
-
|
|
1532
|
-
//#endregion
|
|
1533
|
-
//#region src/orm/aggregate-index/schema.ts
|
|
1534
|
-
const AGGREGATE_BUCKET_TABLE = "aggregate_bucket";
|
|
1535
|
-
const AGGREGATE_MEMBER_TABLE = "aggregate_member";
|
|
1536
|
-
const AGGREGATE_EXTREMA_TABLE = "aggregate_extrema";
|
|
1537
|
-
const AGGREGATE_RANK_TREE_TABLE = "aggregate_rank_tree";
|
|
1538
|
-
const AGGREGATE_RANK_NODE_TABLE = "aggregate_rank_node";
|
|
1539
|
-
const AGGREGATE_STATE_TABLE = "aggregate_state";
|
|
1540
|
-
const countBucketTable = convexTable(AGGREGATE_BUCKET_TABLE, {
|
|
1541
|
-
tableKey: text().notNull(),
|
|
1542
|
-
indexName: text().notNull(),
|
|
1543
|
-
keyHash: text().notNull(),
|
|
1544
|
-
keyParts: arrayOf(json()).notNull(),
|
|
1545
|
-
count: integer().notNull(),
|
|
1546
|
-
sumValues: objectOf(integer().notNull()).notNull(),
|
|
1547
|
-
nonNullCountValues: objectOf(integer().notNull()).notNull(),
|
|
1548
|
-
updatedAt: integer().notNull()
|
|
1549
|
-
}, (t) => [index("by_table_index_hash").on(t.tableKey, t.indexName, t.keyHash), index("by_table_index").on(t.tableKey, t.indexName)]);
|
|
1550
|
-
const countMemberTable = convexTable(AGGREGATE_MEMBER_TABLE, {
|
|
1551
|
-
kind: text().notNull(),
|
|
1552
|
-
tableKey: text().notNull(),
|
|
1553
|
-
indexName: text().notNull(),
|
|
1554
|
-
docId: text().notNull(),
|
|
1555
|
-
keyHash: text().notNull(),
|
|
1556
|
-
keyParts: arrayOf(json()).notNull(),
|
|
1557
|
-
sumValues: objectOf(integer().notNull()).notNull(),
|
|
1558
|
-
nonNullCountValues: objectOf(integer().notNull()).notNull(),
|
|
1559
|
-
extremaValues: objectOf(json()).notNull(),
|
|
1560
|
-
rankNamespace: json(),
|
|
1561
|
-
rankKey: json(),
|
|
1562
|
-
rankSumValue: integer(),
|
|
1563
|
-
updatedAt: integer().notNull()
|
|
1564
|
-
}, (t) => [index("by_kind_table_index_doc").on(t.kind, t.tableKey, t.indexName, t.docId), index("by_kind_table_index").on(t.kind, t.tableKey, t.indexName)]);
|
|
1565
|
-
const countExtremaTable = convexTable(AGGREGATE_EXTREMA_TABLE, {
|
|
1566
|
-
tableKey: text().notNull(),
|
|
1567
|
-
indexName: text().notNull(),
|
|
1568
|
-
keyHash: text().notNull(),
|
|
1569
|
-
fieldName: text().notNull(),
|
|
1570
|
-
valueHash: text().notNull(),
|
|
1571
|
-
value: json().notNull(),
|
|
1572
|
-
sortKey: text().notNull(),
|
|
1573
|
-
count: integer().notNull(),
|
|
1574
|
-
updatedAt: integer().notNull()
|
|
1575
|
-
}, (t) => [
|
|
1576
|
-
index("by_table_index").on(t.tableKey, t.indexName),
|
|
1577
|
-
index("by_table_index_hash_field_value").on(t.tableKey, t.indexName, t.keyHash, t.fieldName, t.valueHash),
|
|
1578
|
-
index("by_table_index_hash_field_sort").on(t.tableKey, t.indexName, t.keyHash, t.fieldName, t.sortKey)
|
|
1579
|
-
]);
|
|
1580
|
-
const countStateTable = convexTable(AGGREGATE_STATE_TABLE, {
|
|
1581
|
-
kind: text().notNull(),
|
|
1582
|
-
tableKey: text().notNull(),
|
|
1583
|
-
indexName: text().notNull(),
|
|
1584
|
-
keyDefinitionHash: text().notNull(),
|
|
1585
|
-
metricDefinitionHash: text().notNull(),
|
|
1586
|
-
status: text().notNull(),
|
|
1587
|
-
cursor: text(),
|
|
1588
|
-
processed: integer().notNull(),
|
|
1589
|
-
startedAt: integer().notNull(),
|
|
1590
|
-
updatedAt: integer().notNull(),
|
|
1591
|
-
completedAt: integer(),
|
|
1592
|
-
lastError: text()
|
|
1593
|
-
}, (t) => [
|
|
1594
|
-
index("by_kind_table_index").on(t.kind, t.tableKey, t.indexName),
|
|
1595
|
-
index("by_kind_status").on(t.kind, t.status),
|
|
1596
|
-
index("by_table_status").on(t.tableKey, t.status)
|
|
1597
|
-
]);
|
|
1598
|
-
const rankTreeTable = convexTable(AGGREGATE_RANK_TREE_TABLE, {
|
|
1599
|
-
aggregateName: text().notNull(),
|
|
1600
|
-
maxNodeSize: integer().notNull(),
|
|
1601
|
-
namespace: json(),
|
|
1602
|
-
root: id(AGGREGATE_RANK_NODE_TABLE).notNull()
|
|
1603
|
-
}, (tree) => [index("by_namespace").on(tree.namespace), index("by_aggregate_name").on(tree.aggregateName)]);
|
|
1604
|
-
const rankNodeTable = convexTable(AGGREGATE_RANK_NODE_TABLE, {
|
|
1605
|
-
aggregate: objectOf({
|
|
1606
|
-
count: integer().notNull(),
|
|
1607
|
-
sum: integer().notNull()
|
|
1608
|
-
}),
|
|
1609
|
-
items: arrayOf(objectOf({
|
|
1610
|
-
k: json(),
|
|
1611
|
-
v: json(),
|
|
1612
|
-
s: integer().notNull()
|
|
1613
|
-
})).notNull(),
|
|
1614
|
-
subtrees: arrayOf(text().notNull()).notNull()
|
|
1615
|
-
});
|
|
1616
|
-
const aggregateStorageTables = {
|
|
1617
|
-
[AGGREGATE_BUCKET_TABLE]: countBucketTable,
|
|
1618
|
-
[AGGREGATE_MEMBER_TABLE]: countMemberTable,
|
|
1619
|
-
[AGGREGATE_EXTREMA_TABLE]: countExtremaTable,
|
|
1620
|
-
[AGGREGATE_RANK_TREE_TABLE]: rankTreeTable,
|
|
1621
|
-
[AGGREGATE_RANK_NODE_TABLE]: rankNodeTable,
|
|
1622
|
-
[AGGREGATE_STATE_TABLE]: countStateTable
|
|
1623
|
-
};
|
|
1624
|
-
function aggregateExtension() {
|
|
1625
|
-
return defineSchemaExtension("aggregate", aggregateStorageTables);
|
|
1626
|
-
}
|
|
1627
|
-
|
|
1628
|
-
//#endregion
|
|
1629
|
-
//#region src/orm/migrations/schema.ts
|
|
1630
|
-
const MIGRATION_STATE_TABLE = "migration_state";
|
|
1631
|
-
const MIGRATION_RUN_TABLE = "migration_run";
|
|
1632
|
-
const migrationStateTable = convexTable(MIGRATION_STATE_TABLE, {
|
|
1633
|
-
migrationId: text().notNull(),
|
|
1634
|
-
checksum: text().notNull(),
|
|
1635
|
-
applied: boolean$1().notNull(),
|
|
1636
|
-
status: text().notNull(),
|
|
1637
|
-
direction: text(),
|
|
1638
|
-
runId: text(),
|
|
1639
|
-
cursor: text(),
|
|
1640
|
-
processed: integer().notNull(),
|
|
1641
|
-
startedAt: integer(),
|
|
1642
|
-
updatedAt: integer().notNull(),
|
|
1643
|
-
completedAt: integer(),
|
|
1644
|
-
lastError: text(),
|
|
1645
|
-
writeMode: text().notNull()
|
|
1646
|
-
}, (t) => [index("by_migration_id").on(t.migrationId), index("by_status").on(t.status)]);
|
|
1647
|
-
const migrationRunTable = convexTable(MIGRATION_RUN_TABLE, {
|
|
1648
|
-
runId: text().notNull(),
|
|
1649
|
-
direction: text().notNull(),
|
|
1650
|
-
status: text().notNull(),
|
|
1651
|
-
dryRun: boolean$1().notNull(),
|
|
1652
|
-
allowDrift: boolean$1().notNull(),
|
|
1653
|
-
migrationIds: custom(v.array(v.string())).notNull(),
|
|
1654
|
-
currentIndex: integer().notNull(),
|
|
1655
|
-
startedAt: integer().notNull(),
|
|
1656
|
-
updatedAt: integer().notNull(),
|
|
1657
|
-
completedAt: integer(),
|
|
1658
|
-
cancelRequested: boolean$1().notNull(),
|
|
1659
|
-
lastError: text()
|
|
1660
|
-
}, (t) => [index("by_run_id").on(t.runId), index("by_status").on(t.status)]);
|
|
1661
|
-
const migrationStorageTables = {
|
|
1662
|
-
[MIGRATION_STATE_TABLE]: migrationStateTable,
|
|
1663
|
-
[MIGRATION_RUN_TABLE]: migrationRunTable
|
|
1664
|
-
};
|
|
1665
|
-
function migrationExtension() {
|
|
1666
|
-
return defineSchemaExtension("migration", migrationStorageTables);
|
|
1667
|
-
}
|
|
1668
|
-
|
|
1669
|
-
//#endregion
|
|
1670
|
-
//#region src/orm/relations.ts
|
|
1671
|
-
var RelationsBuilderTable = class {
|
|
1672
|
-
static [entityKind] = "RelationsBuilderTable";
|
|
1673
|
-
_;
|
|
1674
|
-
constructor(table, name) {
|
|
1675
|
-
this._ = {
|
|
1676
|
-
name,
|
|
1677
|
-
table
|
|
1678
|
-
};
|
|
1679
|
-
}
|
|
1680
|
-
};
|
|
1681
|
-
var RelationsBuilderColumn = class {
|
|
1682
|
-
static [entityKind] = "RelationsBuilderColumn";
|
|
1683
|
-
_;
|
|
1684
|
-
constructor(column, tableName, key) {
|
|
1685
|
-
this._ = {
|
|
1686
|
-
tableName,
|
|
1687
|
-
column,
|
|
1688
|
-
key
|
|
1689
|
-
};
|
|
1690
|
-
}
|
|
1691
|
-
through(column) {
|
|
1692
|
-
return new RelationsBuilderJunctionColumn(this._.column, this._.tableName, this._.key, column);
|
|
1693
|
-
}
|
|
1694
|
-
};
|
|
1695
|
-
var RelationsBuilderJunctionColumn = class {
|
|
1696
|
-
static [entityKind] = "RelationsBuilderColumn";
|
|
1697
|
-
_;
|
|
1698
|
-
constructor(column, tableName, key, through) {
|
|
1699
|
-
this._ = {
|
|
1700
|
-
tableName,
|
|
1701
|
-
column,
|
|
1702
|
-
through,
|
|
1703
|
-
key
|
|
1704
|
-
};
|
|
1705
|
-
}
|
|
1706
|
-
};
|
|
1707
|
-
var RelationsHelperStatic = class {
|
|
1708
|
-
static [entityKind] = "RelationsHelperStatic";
|
|
1709
|
-
constructor(tables) {
|
|
1710
|
-
const one = {};
|
|
1711
|
-
const many = {};
|
|
1712
|
-
for (const [tableName, table] of Object.entries(tables)) {
|
|
1713
|
-
one[tableName] = (config) => new One(tables, table, tableName, config);
|
|
1714
|
-
many[tableName] = (config) => new Many(tables, table, tableName, config);
|
|
1626
|
+
tableName;
|
|
1627
|
+
constructor(name, columns, polymorphicConfigs) {
|
|
1628
|
+
validateTableName(name);
|
|
1629
|
+
for (const columnName of Object.keys(columns)) if (RESERVED_COLUMN_NAMES.has(columnName)) throw new Error(`Column name '${columnName}' is reserved. System fields are managed by Convex ORM.`);
|
|
1630
|
+
this[TableName] = name;
|
|
1631
|
+
const namedColumns = Object.fromEntries(Object.entries(columns).map(([columnName, builder]) => {
|
|
1632
|
+
builder.config.name = columnName;
|
|
1633
|
+
builder.config.tableName = name;
|
|
1634
|
+
builder.config.table = this;
|
|
1635
|
+
return [columnName, builder];
|
|
1636
|
+
}));
|
|
1637
|
+
this[Columns] = namedColumns;
|
|
1638
|
+
this.tableName = name;
|
|
1639
|
+
if (polymorphicConfigs && polymorphicConfigs.length > 0) this[TablePolymorphic] = polymorphicConfigs;
|
|
1640
|
+
this.validator = createValidatorFromColumns(namedColumns);
|
|
1641
|
+
for (const [columnName, builder] of Object.entries(namedColumns)) {
|
|
1642
|
+
const config = builder.config;
|
|
1643
|
+
if (config?.isUnique) {
|
|
1644
|
+
const indexName = getUniqueIndexName(name, [columnName], config.uniqueName);
|
|
1645
|
+
const nullsNotDistinct = config.uniqueNulls === "not distinct";
|
|
1646
|
+
this.addIndex(indexName, [columnName]);
|
|
1647
|
+
this.addUniqueIndex(indexName, [columnName], nullsNotDistinct);
|
|
1648
|
+
}
|
|
1649
|
+
if (config?.referenceTable && (!config.foreignKeyConfigs || config.foreignKeyConfigs.length === 0)) this.addForeignKey({
|
|
1650
|
+
name: void 0,
|
|
1651
|
+
columns: [columnName],
|
|
1652
|
+
foreignTableName: config.referenceTable,
|
|
1653
|
+
foreignColumns: ["_id"]
|
|
1654
|
+
});
|
|
1655
|
+
if (config?.foreignKeyConfigs?.length) for (const foreignConfig of config.foreignKeyConfigs) this.deferredForeignKeys.push({
|
|
1656
|
+
localColumnName: columnName,
|
|
1657
|
+
ref: foreignConfig.ref,
|
|
1658
|
+
config: foreignConfig.config
|
|
1659
|
+
});
|
|
1715
1660
|
}
|
|
1716
|
-
this.one = one;
|
|
1717
|
-
this.many = many;
|
|
1718
1661
|
}
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
};
|
|
1722
|
-
function createRelationsHelper(tables) {
|
|
1723
|
-
const helperStatic = new RelationsHelperStatic(tables);
|
|
1724
|
-
const relationsTables = Object.entries(tables).reduce((acc, [tKey, value]) => {
|
|
1725
|
-
const rTable = new RelationsBuilderTable(value, tKey);
|
|
1726
|
-
const columns = Object.entries(getTableColumns(value)).reduce((colsAcc, [cKey, column]) => {
|
|
1727
|
-
colsAcc[cKey] = new RelationsBuilderColumn(column, tKey, cKey);
|
|
1728
|
-
return colsAcc;
|
|
1729
|
-
}, {});
|
|
1730
|
-
const relationsTable = Object.assign(rTable, columns);
|
|
1731
|
-
Object.defineProperty(relationsTable, "_id", {
|
|
1732
|
-
get() {
|
|
1733
|
-
throw new Error("`_id` is no longer public in relations. Use `id`.");
|
|
1734
|
-
},
|
|
1735
|
-
enumerable: false,
|
|
1736
|
-
configurable: false
|
|
1737
|
-
});
|
|
1738
|
-
acc[tKey] = relationsTable;
|
|
1739
|
-
return acc;
|
|
1740
|
-
}, {});
|
|
1741
|
-
return Object.assign(helperStatic, relationsTables);
|
|
1742
|
-
}
|
|
1743
|
-
function extractTablesFromSchema(schema) {
|
|
1744
|
-
const schemaTables = schema && typeof schema === "object" && !Array.isArray(schema) && "tables" in schema && schema.tables && typeof schema.tables === "object" && !Array.isArray(schema.tables) ? schema.tables : schema;
|
|
1745
|
-
return Object.fromEntries(Object.entries(schemaTables).filter(([_, e]) => isConvexTable(e)));
|
|
1746
|
-
}
|
|
1747
|
-
var Relation = class {
|
|
1748
|
-
static [entityKind] = "RelationV2";
|
|
1749
|
-
fieldName;
|
|
1750
|
-
sourceColumns;
|
|
1751
|
-
targetColumns;
|
|
1752
|
-
alias;
|
|
1753
|
-
where;
|
|
1754
|
-
sourceTable;
|
|
1755
|
-
targetTable;
|
|
1756
|
-
through;
|
|
1757
|
-
throughTable;
|
|
1758
|
-
isReversed;
|
|
1759
|
-
/** @internal */
|
|
1760
|
-
sourceColumnTableNames = [];
|
|
1761
|
-
/** @internal */
|
|
1762
|
-
targetColumnTableNames = [];
|
|
1763
|
-
constructor(targetTable, targetTableName) {
|
|
1764
|
-
this.targetTableName = targetTableName;
|
|
1765
|
-
this.targetTable = targetTable;
|
|
1662
|
+
getPolymorphicConfigs() {
|
|
1663
|
+
return this[TablePolymorphic];
|
|
1766
1664
|
}
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
this.where = config?.where;
|
|
1776
|
-
if (config?.from) this.sourceColumns = (Array.isArray(config.from) ? config.from : [config.from]).map((it) => {
|
|
1777
|
-
this.throughTable ??= it._.through ? tables[it._.through._.tableName] : void 0;
|
|
1778
|
-
this.sourceColumnTableNames.push(it._.tableName);
|
|
1779
|
-
return it._.column;
|
|
1665
|
+
/**
|
|
1666
|
+
* Internal: add index to table from builder extraConfig
|
|
1667
|
+
*
|
|
1668
|
+
*/
|
|
1669
|
+
addIndex(name, fields) {
|
|
1670
|
+
this.indexes.push({
|
|
1671
|
+
indexDescriptor: name,
|
|
1672
|
+
fields
|
|
1780
1673
|
});
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1674
|
+
}
|
|
1675
|
+
/**
|
|
1676
|
+
* Internal: add unique index metadata for runtime enforcement
|
|
1677
|
+
*/
|
|
1678
|
+
addUniqueIndex(name, fields, nullsNotDistinct) {
|
|
1679
|
+
this.uniqueIndexes.push({
|
|
1680
|
+
name,
|
|
1681
|
+
fields,
|
|
1682
|
+
nullsNotDistinct
|
|
1785
1683
|
});
|
|
1786
|
-
if (this.throughTable) this.through = {
|
|
1787
|
-
source: (Array.isArray(config?.from) ? config.from : config?.from ? [config.from] : []).map((c) => c._.through),
|
|
1788
|
-
target: (Array.isArray(config?.to) ? config.to : config?.to ? [config.to] : []).map((c) => c._.through)
|
|
1789
|
-
};
|
|
1790
|
-
this.optional = config?.optional ?? true;
|
|
1791
1684
|
}
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
this.throughTable ??= it._.through ? tables[it._.through._.tableName] : void 0;
|
|
1803
|
-
this.sourceColumnTableNames.push(it._.tableName);
|
|
1804
|
-
return it._.column;
|
|
1685
|
+
addAggregateIndex(name, config) {
|
|
1686
|
+
if (this.aggregateIndexes.some((index) => index.name === name) || this.rankIndexes.some((index) => index.name === name)) throw new Error(`Duplicate aggregate index '${name}' on '${this.tableName}'.`);
|
|
1687
|
+
this.aggregateIndexes.push({
|
|
1688
|
+
name,
|
|
1689
|
+
fields: config.fields,
|
|
1690
|
+
countFields: config.countFields,
|
|
1691
|
+
sumFields: config.sumFields,
|
|
1692
|
+
avgFields: config.avgFields,
|
|
1693
|
+
minFields: config.minFields,
|
|
1694
|
+
maxFields: config.maxFields
|
|
1805
1695
|
});
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1696
|
+
}
|
|
1697
|
+
addRankIndex(name, config) {
|
|
1698
|
+
if (this.rankIndexes.some((index) => index.name === name) || this.aggregateIndexes.some((index) => index.name === name)) throw new Error(`Duplicate aggregate index '${name}' on '${this.tableName}'.`);
|
|
1699
|
+
this.rankIndexes.push({
|
|
1700
|
+
name,
|
|
1701
|
+
partitionFields: config.partitionFields,
|
|
1702
|
+
orderFields: config.orderFields,
|
|
1703
|
+
sumField: config.sumField
|
|
1810
1704
|
});
|
|
1811
|
-
if (this.throughTable) this.through = {
|
|
1812
|
-
source: (Array.isArray(config?.from) ? config.from : config?.from ? [config.from] : []).map((c) => c._.through),
|
|
1813
|
-
target: (Array.isArray(config?.to) ? config.to : config?.to ? [config.to] : []).map((c) => c._.through)
|
|
1814
|
-
};
|
|
1815
1705
|
}
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1706
|
+
getAggregateIndexes() {
|
|
1707
|
+
return [...this.aggregateIndexes];
|
|
1708
|
+
}
|
|
1709
|
+
getRankIndexes() {
|
|
1710
|
+
return [...this.rankIndexes];
|
|
1711
|
+
}
|
|
1712
|
+
/**
|
|
1713
|
+
* Internal: expose unique index metadata for mutation enforcement
|
|
1714
|
+
*/
|
|
1715
|
+
getUniqueIndexes() {
|
|
1716
|
+
return this.uniqueIndexes;
|
|
1717
|
+
}
|
|
1718
|
+
/**
|
|
1719
|
+
* Internal: expose index metadata for runtime enforcement
|
|
1720
|
+
*/
|
|
1721
|
+
getIndexes() {
|
|
1722
|
+
return this.indexes.map((entry) => ({
|
|
1723
|
+
name: entry.indexDescriptor,
|
|
1724
|
+
fields: entry.fields
|
|
1725
|
+
}));
|
|
1726
|
+
}
|
|
1727
|
+
/**
|
|
1728
|
+
* Internal: expose search index metadata for runtime query execution
|
|
1729
|
+
*/
|
|
1730
|
+
getSearchIndexes() {
|
|
1731
|
+
return this.searchIndexes.map((entry) => ({
|
|
1732
|
+
name: entry.indexDescriptor,
|
|
1733
|
+
searchField: entry.searchField,
|
|
1734
|
+
filterFields: entry.filterFields
|
|
1735
|
+
}));
|
|
1736
|
+
}
|
|
1737
|
+
/**
|
|
1738
|
+
* Internal: expose vector index metadata for runtime query execution
|
|
1739
|
+
*/
|
|
1740
|
+
getVectorIndexes() {
|
|
1741
|
+
return this.vectorIndexes.map((entry) => ({
|
|
1742
|
+
name: entry.indexDescriptor,
|
|
1743
|
+
vectorField: entry.vectorField,
|
|
1744
|
+
dimensions: entry.dimensions,
|
|
1745
|
+
filterFields: entry.filterFields
|
|
1746
|
+
}));
|
|
1747
|
+
}
|
|
1748
|
+
/**
|
|
1749
|
+
* Internal: attach an RLS policy to this table
|
|
1750
|
+
*/
|
|
1751
|
+
addRlsPolicy(policy) {
|
|
1752
|
+
this[RlsPolicies].push(policy);
|
|
1753
|
+
this[EnableRLS] = true;
|
|
1754
|
+
}
|
|
1755
|
+
/**
|
|
1756
|
+
* Internal: return attached RLS policies
|
|
1757
|
+
*/
|
|
1758
|
+
getRlsPolicies() {
|
|
1759
|
+
return this[RlsPolicies];
|
|
1864
1760
|
}
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
if (
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1761
|
+
/**
|
|
1762
|
+
* Internal: check if RLS is enabled on this table
|
|
1763
|
+
*/
|
|
1764
|
+
isRlsEnabled() {
|
|
1765
|
+
return this[EnableRLS];
|
|
1766
|
+
}
|
|
1767
|
+
/**
|
|
1768
|
+
* Internal: add foreign key metadata for runtime enforcement
|
|
1769
|
+
*/
|
|
1770
|
+
addForeignKey(definition) {
|
|
1771
|
+
const matches = (existing) => {
|
|
1772
|
+
if (existing.foreignTableName !== definition.foreignTableName) return false;
|
|
1773
|
+
if (existing.columns.length !== definition.columns.length) return false;
|
|
1774
|
+
if (existing.foreignColumns.length !== definition.foreignColumns.length) return false;
|
|
1775
|
+
for (let i = 0; i < existing.columns.length; i++) if (existing.columns[i] !== definition.columns[i]) return false;
|
|
1776
|
+
for (let i = 0; i < existing.foreignColumns.length; i++) if (existing.foreignColumns[i] !== definition.foreignColumns[i]) return false;
|
|
1777
|
+
return true;
|
|
1778
|
+
};
|
|
1779
|
+
this.foreignKeys = this.foreignKeys.filter((existing) => !matches(existing));
|
|
1780
|
+
this.foreignKeys.push(definition);
|
|
1781
|
+
}
|
|
1782
|
+
resolveDeferredForeignKeys() {
|
|
1783
|
+
if (this.deferredForeignKeysResolved) return;
|
|
1784
|
+
this.deferredForeignKeysResolved = true;
|
|
1785
|
+
for (const deferred of this.deferredForeignKeys) {
|
|
1786
|
+
let foreignColumn;
|
|
1787
|
+
try {
|
|
1788
|
+
foreignColumn = deferred.ref();
|
|
1789
|
+
} catch (error) {
|
|
1790
|
+
const reason = error instanceof Error ? ` ${error.message}` : "";
|
|
1791
|
+
throw new Error(`Failed to resolve foreign key reference for '${this.tableName}.${deferred.localColumnName}'. Use references(() => targetTable.column) after both tables are declared.${reason}`);
|
|
1882
1792
|
}
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
if (!reverseRelation) throw new Error(`${relationPrintName}: no reverse relation between "${targetTableName}" and "${sourceTableName}"`);
|
|
1793
|
+
const foreignTableName = getColumnTableName(foreignColumn);
|
|
1794
|
+
if (!foreignTableName) throw new Error(`Foreign key on '${this.tableName}.${deferred.localColumnName}' references a column without a table. Use references(() => targetTable.column).`);
|
|
1795
|
+
const foreignTable = getColumnTable(foreignColumn);
|
|
1796
|
+
if (!foreignTable) throw new Error(`Foreign key on '${this.tableName}.${deferred.localColumnName}' references a column without table metadata. Replace references(() => id('tableName')) with references(() => table.id).`);
|
|
1797
|
+
const foreignColumnName = getColumnName(foreignColumn);
|
|
1798
|
+
this.addForeignKey({
|
|
1799
|
+
name: deferred.config.name,
|
|
1800
|
+
columns: [deferred.localColumnName],
|
|
1801
|
+
foreignTableName,
|
|
1802
|
+
foreignTable,
|
|
1803
|
+
foreignColumns: [foreignColumnName],
|
|
1804
|
+
onDelete: deferred.config.onDelete,
|
|
1805
|
+
onUpdate: deferred.config.onUpdate
|
|
1806
|
+
});
|
|
1898
1807
|
}
|
|
1899
|
-
|
|
1900
|
-
relation.sourceColumns = reverseRelation.targetColumns;
|
|
1901
|
-
relation.targetColumns = reverseRelation.sourceColumns;
|
|
1902
|
-
relation.through = reverseRelation.through ? {
|
|
1903
|
-
source: reverseRelation.through.target,
|
|
1904
|
-
target: reverseRelation.through.source
|
|
1905
|
-
} : void 0;
|
|
1906
|
-
relation.throughTable = reverseRelation.throughTable;
|
|
1907
|
-
relation.isReversed = !where;
|
|
1908
|
-
relation.where = where ?? reverseRelation.where;
|
|
1808
|
+
this.deferredForeignKeys = [];
|
|
1909
1809
|
}
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
return value instanceof Relation;
|
|
1917
|
-
}
|
|
1918
|
-
function getTableColumns(table) {
|
|
1919
|
-
const columns = table[Columns];
|
|
1920
|
-
const system = {};
|
|
1921
|
-
if (table.id) system.id = table.id;
|
|
1922
|
-
if (table.createdAt || table._creationTime) system.createdAt = table._creationTime ?? table.createdAt;
|
|
1923
|
-
return {
|
|
1924
|
-
...columns,
|
|
1925
|
-
...system
|
|
1926
|
-
};
|
|
1927
|
-
}
|
|
1928
|
-
|
|
1929
|
-
//#endregion
|
|
1930
|
-
//#region src/orm/triggers.ts
|
|
1931
|
-
function defineTriggers(schema, triggers) {
|
|
1932
|
-
return triggers;
|
|
1933
|
-
}
|
|
1934
|
-
|
|
1935
|
-
//#endregion
|
|
1936
|
-
//#region src/orm/schema.ts
|
|
1937
|
-
const BUILTIN_SCHEMA_EXTENSIONS = [aggregateExtension(), migrationExtension()];
|
|
1938
|
-
function resolveSchemaExtensions(extensions) {
|
|
1939
|
-
const resolved = [...BUILTIN_SCHEMA_EXTENSIONS, ...extensions ?? []];
|
|
1940
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1941
|
-
for (const extension of resolved) {
|
|
1942
|
-
if (seen.has(extension.key)) throw new Error(`defineSchema received duplicate extension '${extension.key}'. Remove duplicate extension registrations.`);
|
|
1943
|
-
seen.add(extension.key);
|
|
1810
|
+
/**
|
|
1811
|
+
* Internal: expose foreign key metadata for mutation enforcement
|
|
1812
|
+
*/
|
|
1813
|
+
getForeignKeys() {
|
|
1814
|
+
this.resolveDeferredForeignKeys();
|
|
1815
|
+
return this.foreignKeys;
|
|
1944
1816
|
}
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
for (const { source, config } of sources) for (const [tableName, relationConfig] of Object.entries(config)) {
|
|
1951
|
-
if (!relationConfig) continue;
|
|
1952
|
-
let tableRelations = merged[tableName];
|
|
1953
|
-
if (!tableRelations) {
|
|
1954
|
-
tableRelations = Object.create(null);
|
|
1955
|
-
merged[tableName] = tableRelations;
|
|
1956
|
-
}
|
|
1957
|
-
for (const [fieldName, relation] of Object.entries(relationConfig)) {
|
|
1958
|
-
const relationKey = `${tableName}.${fieldName}`;
|
|
1959
|
-
const existingSource = relationOrigins.get(relationKey);
|
|
1960
|
-
if (existingSource) throw new Error(`defineSchema relation field '${relationKey}' is defined more than once (${existingSource} and ${source}).`);
|
|
1961
|
-
tableRelations[fieldName] = relation;
|
|
1962
|
-
relationOrigins.set(relationKey, source);
|
|
1963
|
-
}
|
|
1817
|
+
addCheck(name, expression) {
|
|
1818
|
+
this.checks.push({
|
|
1819
|
+
name,
|
|
1820
|
+
expression
|
|
1821
|
+
});
|
|
1964
1822
|
}
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
function mergeTriggerConfigs(sources) {
|
|
1968
|
-
const merged = {};
|
|
1969
|
-
const triggerOrigins = /* @__PURE__ */ new Map();
|
|
1970
|
-
for (const { source, config } of sources) for (const [tableName, tableConfig] of Object.entries(config)) {
|
|
1971
|
-
if (!tableConfig || typeof tableConfig !== "object" || Array.isArray(tableConfig)) {
|
|
1972
|
-
const triggerKey = `${tableName}`;
|
|
1973
|
-
const existingSource = triggerOrigins.get(triggerKey);
|
|
1974
|
-
if (existingSource) throw new Error(`defineSchema trigger '${triggerKey}' is defined more than once (${existingSource} and ${source}).`);
|
|
1975
|
-
merged[tableName] = tableConfig;
|
|
1976
|
-
triggerOrigins.set(triggerKey, source);
|
|
1977
|
-
continue;
|
|
1978
|
-
}
|
|
1979
|
-
let mergedTable = merged[tableName];
|
|
1980
|
-
if (!mergedTable) {
|
|
1981
|
-
mergedTable = Object.create(null);
|
|
1982
|
-
merged[tableName] = mergedTable;
|
|
1983
|
-
}
|
|
1984
|
-
for (const [hookKey, hookValue] of Object.entries(tableConfig)) {
|
|
1985
|
-
if ((hookKey === "create" || hookKey === "update" || hookKey === "delete") && hookValue && typeof hookValue === "object" && !Array.isArray(hookValue)) {
|
|
1986
|
-
let mergedOperation = mergedTable[hookKey];
|
|
1987
|
-
if (!mergedOperation || typeof mergedOperation !== "object") {
|
|
1988
|
-
mergedOperation = Object.create(null);
|
|
1989
|
-
mergedTable[hookKey] = mergedOperation;
|
|
1990
|
-
}
|
|
1991
|
-
for (const [operationHookKey, operationHookValue] of Object.entries(hookValue)) {
|
|
1992
|
-
const triggerKey = `${tableName}.${hookKey}.${operationHookKey}`;
|
|
1993
|
-
const existingSource = triggerOrigins.get(triggerKey);
|
|
1994
|
-
if (existingSource) throw new Error(`defineSchema trigger '${triggerKey}' is defined more than once (${existingSource} and ${source}).`);
|
|
1995
|
-
mergedOperation[operationHookKey] = operationHookValue;
|
|
1996
|
-
triggerOrigins.set(triggerKey, source);
|
|
1997
|
-
}
|
|
1998
|
-
continue;
|
|
1999
|
-
}
|
|
2000
|
-
const triggerKey = `${tableName}.${hookKey}`;
|
|
2001
|
-
const existingSource = triggerOrigins.get(triggerKey);
|
|
2002
|
-
if (existingSource) throw new Error(`defineSchema trigger '${triggerKey}' is defined more than once (${existingSource} and ${source}).`);
|
|
2003
|
-
mergedTable[hookKey] = hookValue;
|
|
2004
|
-
triggerOrigins.set(triggerKey, source);
|
|
2005
|
-
}
|
|
1823
|
+
getChecks() {
|
|
1824
|
+
return this.checks;
|
|
2006
1825
|
}
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
const OrmSchemaComposerState = Symbol("kitcn:OrmSchemaComposerState");
|
|
2019
|
-
function getSchemaComposerState(schema) {
|
|
2020
|
-
if (!schema || typeof schema !== "object") return;
|
|
2021
|
-
return schema[OrmSchemaComposerState];
|
|
2022
|
-
}
|
|
2023
|
-
function finalizeSchemaMetadata(schema) {
|
|
2024
|
-
if (!schema || typeof schema !== "object") return;
|
|
2025
|
-
const composerState = getSchemaComposerState(schema);
|
|
2026
|
-
if (!composerState) return;
|
|
2027
|
-
const schemaObject = schema;
|
|
2028
|
-
const resolvedExtensions = schemaObject[OrmSchemaExtensions] ?? resolveSchemaExtensions(composerState.extensions);
|
|
2029
|
-
const extensionTableNames = schemaObject[OrmSchemaExtensionTables] ?? Object.freeze([]);
|
|
2030
|
-
const options = schemaObject[OrmSchemaOptions];
|
|
2031
|
-
const extensionRelationFactories = resolvedExtensions.map((extension) => ({
|
|
2032
|
-
key: extension.key,
|
|
2033
|
-
relations: extension[OrmSchemaExtensionRelations]
|
|
2034
|
-
})).filter((entry) => entry.relations !== void 0);
|
|
2035
|
-
const extensionTriggerFactories = resolvedExtensions.map((extension) => ({
|
|
2036
|
-
key: extension.key,
|
|
2037
|
-
triggers: extension[OrmSchemaExtensionTriggers]
|
|
2038
|
-
})).filter((entry) => entry.triggers !== void 0);
|
|
2039
|
-
const hasExtensionRelations = extensionRelationFactories.length > 0;
|
|
2040
|
-
const hasExtensionTriggers = extensionTriggerFactories.length > 0;
|
|
2041
|
-
const shouldBuildRelations = hasExtensionRelations || hasExtensionTriggers || Boolean(composerState.relations) || Boolean(composerState.triggers);
|
|
2042
|
-
let relations = schemaObject[OrmSchemaRelations];
|
|
2043
|
-
if (!relations && shouldBuildRelations) {
|
|
2044
|
-
relations = hasExtensionRelations || composerState.relations ? defineRelations(schema, (helpers) => mergeRelationConfigs([...extensionRelationFactories.map((extension) => ({
|
|
2045
|
-
source: `extension '${extension.key}'`,
|
|
2046
|
-
config: extension.relations(helpers)
|
|
2047
|
-
})), ...composerState.relations ? [{
|
|
2048
|
-
source: "schema.relations()",
|
|
2049
|
-
config: composerState.relations(helpers)
|
|
2050
|
-
}] : []])) : defineRelations(schema);
|
|
2051
|
-
defineMetadata(relations, OrmSchemaDefinition, schema);
|
|
2052
|
-
if (options) defineMetadata(relations, OrmSchemaOptions, options);
|
|
2053
|
-
defineMetadata(relations, OrmSchemaExtensionTables, extensionTableNames);
|
|
2054
|
-
defineMetadata(relations, OrmSchemaExtensions, resolvedExtensions);
|
|
2055
|
-
defineMetadata(schema, OrmSchemaRelations, relations);
|
|
1826
|
+
/**
|
|
1827
|
+
* Internal: add search index to table from builder extraConfig
|
|
1828
|
+
*/
|
|
1829
|
+
addSearchIndex(name, config) {
|
|
1830
|
+
const entry = {
|
|
1831
|
+
indexDescriptor: name,
|
|
1832
|
+
searchField: config.searchField,
|
|
1833
|
+
filterFields: config.filterFields ?? []
|
|
1834
|
+
};
|
|
1835
|
+
if (config.staged) this.stagedSearchIndexes.push(entry);
|
|
1836
|
+
else this.searchIndexes.push(entry);
|
|
2056
1837
|
}
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
return Boolean(process.stdout.isTTY && process.env.TERM !== "dumb");
|
|
2087
|
-
};
|
|
2088
|
-
const withColors = (callback) => callback(createColors(isColorEnabled()));
|
|
2089
|
-
const highlighter = {
|
|
2090
|
-
bold(value) {
|
|
2091
|
-
return withColors((colors) => colors.bold(value));
|
|
2092
|
-
},
|
|
2093
|
-
dim(value) {
|
|
2094
|
-
return withColors((colors) => colors.dim(value));
|
|
2095
|
-
},
|
|
2096
|
-
info(value) {
|
|
2097
|
-
return withColors((colors) => colors.cyan(value));
|
|
2098
|
-
},
|
|
2099
|
-
success(value) {
|
|
2100
|
-
return withColors((colors) => colors.green(value));
|
|
2101
|
-
},
|
|
2102
|
-
warn(value) {
|
|
2103
|
-
return withColors((colors) => colors.yellow(value));
|
|
2104
|
-
},
|
|
2105
|
-
error(value) {
|
|
2106
|
-
return withColors((colors) => colors.red(value));
|
|
2107
|
-
},
|
|
2108
|
-
path(value) {
|
|
2109
|
-
return withColors((colors) => colors.bold(value));
|
|
1838
|
+
/**
|
|
1839
|
+
* Internal: add vector index to table from builder extraConfig
|
|
1840
|
+
*/
|
|
1841
|
+
addVectorIndex(name, config) {
|
|
1842
|
+
const entry = {
|
|
1843
|
+
indexDescriptor: name,
|
|
1844
|
+
vectorField: config.vectorField,
|
|
1845
|
+
dimensions: config.dimensions,
|
|
1846
|
+
filterFields: config.filterFields ?? []
|
|
1847
|
+
};
|
|
1848
|
+
if (config.staged) this.stagedVectorIndexes.push(entry);
|
|
1849
|
+
else this.vectorIndexes.push(entry);
|
|
1850
|
+
}
|
|
1851
|
+
/**
|
|
1852
|
+
* Export the contents of this definition for Convex schema tooling.
|
|
1853
|
+
* Mirrors convex/server TableDefinition.export().
|
|
1854
|
+
*/
|
|
1855
|
+
export() {
|
|
1856
|
+
const documentType = this.validator.json;
|
|
1857
|
+
if (typeof documentType !== "object") throw new Error("Invalid validator: please make sure that the parameter of `defineTable` is valid (see https://docs.convex.dev/database/schemas)");
|
|
1858
|
+
return {
|
|
1859
|
+
indexes: this.indexes,
|
|
1860
|
+
stagedDbIndexes: this.stagedDbIndexes,
|
|
1861
|
+
searchIndexes: this.searchIndexes,
|
|
1862
|
+
stagedSearchIndexes: this.stagedSearchIndexes,
|
|
1863
|
+
vectorIndexes: this.vectorIndexes,
|
|
1864
|
+
stagedVectorIndexes: this.stagedVectorIndexes,
|
|
1865
|
+
documentType
|
|
1866
|
+
};
|
|
2110
1867
|
}
|
|
2111
1868
|
};
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
const loadBabelParser = () => babelParser ??= require$1("@babel/parser");
|
|
2135
|
-
const loadClackPrompts = () => clackPrompts ??= require$1("@clack/prompts");
|
|
2136
|
-
const loadDotenv = () => dotenv ??= require$1("dotenv");
|
|
2137
|
-
const loadEsbuild = () => esbuild ??= require$1("esbuild");
|
|
2138
|
-
const loadJiti = () => jiti ??= require$1("jiti");
|
|
2139
|
-
|
|
2140
|
-
//#endregion
|
|
2141
|
-
//#region src/cli/utils/crpc-builder-stub.ts
|
|
2142
|
-
const CRPC_BUILDER_STUB_SOURCE = `const createMiddleware = (handler = undefined) => ({
|
|
2143
|
-
_handler: handler,
|
|
2144
|
-
pipe(nextHandler = undefined) {
|
|
2145
|
-
return createMiddleware(nextHandler);
|
|
2146
|
-
},
|
|
2147
|
-
});
|
|
2148
|
-
|
|
2149
|
-
const toMetaObject = (value = undefined) =>
|
|
2150
|
-
value && typeof value === "object" ? value : {};
|
|
2151
|
-
|
|
2152
|
-
const createProcedureExport = (type, state, handler) => ({
|
|
2153
|
-
_crpcMeta: {
|
|
2154
|
-
type,
|
|
2155
|
-
internal: state.internal ?? false,
|
|
2156
|
-
...toMetaObject(state.meta),
|
|
2157
|
-
},
|
|
2158
|
-
...(state.httpRoute ? { _crpcHttpRoute: state.httpRoute } : {}),
|
|
2159
|
-
_handler: handler,
|
|
2160
|
-
});
|
|
2161
|
-
|
|
2162
|
-
const createProcedureBuilder = (state = {}) => {
|
|
2163
|
-
const builder = {
|
|
2164
|
-
internal() {
|
|
2165
|
-
return createProcedureBuilder({ ...state, internal: true });
|
|
2166
|
-
},
|
|
2167
|
-
use() {
|
|
2168
|
-
return createProcedureBuilder(state);
|
|
2169
|
-
},
|
|
2170
|
-
meta(value = undefined) {
|
|
2171
|
-
return createProcedureBuilder({
|
|
2172
|
-
...state,
|
|
2173
|
-
meta: {
|
|
2174
|
-
...toMetaObject(state.meta),
|
|
2175
|
-
...toMetaObject(value),
|
|
2176
|
-
},
|
|
2177
|
-
});
|
|
2178
|
-
},
|
|
2179
|
-
input() {
|
|
2180
|
-
return createProcedureBuilder(state);
|
|
2181
|
-
},
|
|
2182
|
-
params() {
|
|
2183
|
-
return createProcedureBuilder(state);
|
|
2184
|
-
},
|
|
2185
|
-
searchParams() {
|
|
2186
|
-
return createProcedureBuilder(state);
|
|
2187
|
-
},
|
|
2188
|
-
paginated(options = undefined) {
|
|
2189
|
-
return createProcedureBuilder({
|
|
2190
|
-
...state,
|
|
2191
|
-
meta:
|
|
2192
|
-
typeof options?.limit === "number"
|
|
2193
|
-
? {
|
|
2194
|
-
...toMetaObject(state.meta),
|
|
2195
|
-
limit: options.limit,
|
|
2196
|
-
}
|
|
2197
|
-
: state.meta,
|
|
2198
|
-
});
|
|
2199
|
-
},
|
|
2200
|
-
output() {
|
|
2201
|
-
return createProcedureBuilder(state);
|
|
2202
|
-
},
|
|
2203
|
-
form() {
|
|
2204
|
-
return createProcedureBuilder(state);
|
|
2205
|
-
},
|
|
2206
|
-
route(path, method) {
|
|
2207
|
-
return createProcedureBuilder({
|
|
2208
|
-
...state,
|
|
2209
|
-
httpRoute:
|
|
2210
|
-
typeof path === "string" && typeof method === "string"
|
|
2211
|
-
? { path, method: method.toUpperCase() }
|
|
2212
|
-
: undefined,
|
|
2213
|
-
});
|
|
2214
|
-
},
|
|
2215
|
-
get(path) {
|
|
2216
|
-
return builder.route(path, "GET");
|
|
2217
|
-
},
|
|
2218
|
-
post(path) {
|
|
2219
|
-
return builder.route(path, "POST");
|
|
2220
|
-
},
|
|
2221
|
-
put(path) {
|
|
2222
|
-
return builder.route(path, "PUT");
|
|
2223
|
-
},
|
|
2224
|
-
patch(path) {
|
|
2225
|
-
return builder.route(path, "PATCH");
|
|
2226
|
-
},
|
|
2227
|
-
delete(path) {
|
|
2228
|
-
return builder.route(path, "DELETE");
|
|
2229
|
-
},
|
|
2230
|
-
query(handler = undefined) {
|
|
2231
|
-
return createProcedureExport("query", state, handler);
|
|
2232
|
-
},
|
|
2233
|
-
mutation(handler = undefined) {
|
|
2234
|
-
return createProcedureExport("mutation", state, handler);
|
|
2235
|
-
},
|
|
2236
|
-
action(handler = undefined) {
|
|
2237
|
-
return createProcedureExport("action", state, handler);
|
|
2238
|
-
},
|
|
2239
|
-
middleware(handler = undefined) {
|
|
2240
|
-
return createMiddleware(handler);
|
|
2241
|
-
},
|
|
2242
|
-
};
|
|
2243
|
-
|
|
2244
|
-
return builder;
|
|
1869
|
+
const convexTableInternal = (name, columns, extraConfig) => {
|
|
1870
|
+
const expanded = resolveTableColumns(name, columns);
|
|
1871
|
+
const rawTable = new ConvexTableImpl(name, expanded.columns, expanded.polymorphicConfigs);
|
|
1872
|
+
const systemFields = createSystemFields(name);
|
|
1873
|
+
for (const builder of Object.values(systemFields)) builder.config.table = rawTable;
|
|
1874
|
+
const table = Object.assign(rawTable, systemFields, rawTable[Columns]);
|
|
1875
|
+
const internalCreationTime = systemFields._creationTime;
|
|
1876
|
+
if (Object.hasOwn(table, "_creationTime")) table._creationTime = void 0;
|
|
1877
|
+
Object.defineProperty(table, "_creationTime", {
|
|
1878
|
+
value: internalCreationTime,
|
|
1879
|
+
enumerable: false,
|
|
1880
|
+
configurable: true,
|
|
1881
|
+
writable: false
|
|
1882
|
+
});
|
|
1883
|
+
Object.defineProperty(table, "_id", {
|
|
1884
|
+
value: systemFields.id,
|
|
1885
|
+
enumerable: false,
|
|
1886
|
+
configurable: true,
|
|
1887
|
+
writable: false
|
|
1888
|
+
});
|
|
1889
|
+
applyExtraConfig(rawTable, extraConfig?.(table));
|
|
1890
|
+
return table;
|
|
2245
1891
|
};
|
|
2246
|
-
|
|
2247
|
-
const
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
const procedurePath = prefix ? prefix + "." + key : key;
|
|
2251
|
-
if (value?._def?.router === true) {
|
|
2252
|
-
Object.assign(
|
|
2253
|
-
procedures,
|
|
2254
|
-
flattenRouterRecord(value._def.record ?? {}, procedurePath)
|
|
2255
|
-
);
|
|
2256
|
-
continue;
|
|
2257
|
-
}
|
|
2258
|
-
procedures[procedurePath] = value;
|
|
2259
|
-
}
|
|
2260
|
-
return procedures;
|
|
1892
|
+
const convexTableWithRLS = (name, columns, extraConfig) => {
|
|
1893
|
+
const table = convexTableInternal(name, columns, extraConfig);
|
|
1894
|
+
table[EnableRLS] = true;
|
|
1895
|
+
return table;
|
|
2261
1896
|
};
|
|
1897
|
+
const convexTable = Object.assign(convexTableInternal, { withRLS: convexTableWithRLS });
|
|
2262
1898
|
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
1899
|
+
//#endregion
|
|
1900
|
+
//#region src/orm/aggregate-index/schema.ts
|
|
1901
|
+
const AGGREGATE_BUCKET_TABLE = "aggregate_bucket";
|
|
1902
|
+
const AGGREGATE_MEMBER_TABLE = "aggregate_member";
|
|
1903
|
+
const AGGREGATE_EXTREMA_TABLE = "aggregate_extrema";
|
|
1904
|
+
const AGGREGATE_RANK_TREE_TABLE = "aggregate_rank_tree";
|
|
1905
|
+
const AGGREGATE_RANK_NODE_TABLE = "aggregate_rank_node";
|
|
1906
|
+
const AGGREGATE_STATE_TABLE = "aggregate_state";
|
|
1907
|
+
const countBucketTable = convexTable(AGGREGATE_BUCKET_TABLE, {
|
|
1908
|
+
tableKey: text().notNull(),
|
|
1909
|
+
indexName: text().notNull(),
|
|
1910
|
+
keyHash: text().notNull(),
|
|
1911
|
+
keyParts: arrayOf(json()).notNull(),
|
|
1912
|
+
count: integer().notNull(),
|
|
1913
|
+
sumValues: objectOf(integer().notNull()).notNull(),
|
|
1914
|
+
nonNullCountValues: objectOf(integer().notNull()).notNull(),
|
|
1915
|
+
updatedAt: integer().notNull()
|
|
1916
|
+
}, (t) => [index("by_table_index_hash").on(t.tableKey, t.indexName, t.keyHash), index("by_table_index").on(t.tableKey, t.indexName)]);
|
|
1917
|
+
const countMemberTable = convexTable(AGGREGATE_MEMBER_TABLE, {
|
|
1918
|
+
kind: text().notNull(),
|
|
1919
|
+
tableKey: text().notNull(),
|
|
1920
|
+
indexName: text().notNull(),
|
|
1921
|
+
docId: text().notNull(),
|
|
1922
|
+
keyHash: text().notNull(),
|
|
1923
|
+
keyParts: arrayOf(json()).notNull(),
|
|
1924
|
+
sumValues: objectOf(integer().notNull()).notNull(),
|
|
1925
|
+
nonNullCountValues: objectOf(integer().notNull()).notNull(),
|
|
1926
|
+
extremaValues: objectOf(json()).notNull(),
|
|
1927
|
+
rankNamespace: json(),
|
|
1928
|
+
rankKey: json(),
|
|
1929
|
+
rankSumValue: integer(),
|
|
1930
|
+
updatedAt: integer().notNull()
|
|
1931
|
+
}, (t) => [index("by_kind_table_index_doc").on(t.kind, t.tableKey, t.indexName, t.docId), index("by_kind_table_index").on(t.kind, t.tableKey, t.indexName)]);
|
|
1932
|
+
const countExtremaTable = convexTable(AGGREGATE_EXTREMA_TABLE, {
|
|
1933
|
+
tableKey: text().notNull(),
|
|
1934
|
+
indexName: text().notNull(),
|
|
1935
|
+
keyHash: text().notNull(),
|
|
1936
|
+
fieldName: text().notNull(),
|
|
1937
|
+
valueHash: text().notNull(),
|
|
1938
|
+
value: json().notNull(),
|
|
1939
|
+
sortKey: text().notNull(),
|
|
1940
|
+
count: integer().notNull(),
|
|
1941
|
+
updatedAt: integer().notNull()
|
|
1942
|
+
}, (t) => [
|
|
1943
|
+
index("by_table_index").on(t.tableKey, t.indexName),
|
|
1944
|
+
index("by_table_index_hash_field_value").on(t.tableKey, t.indexName, t.keyHash, t.fieldName, t.valueHash),
|
|
1945
|
+
index("by_table_index_hash_field_sort").on(t.tableKey, t.indexName, t.keyHash, t.fieldName, t.sortKey)
|
|
1946
|
+
]);
|
|
1947
|
+
const countStateTable = convexTable(AGGREGATE_STATE_TABLE, {
|
|
1948
|
+
kind: text().notNull(),
|
|
1949
|
+
tableKey: text().notNull(),
|
|
1950
|
+
indexName: text().notNull(),
|
|
1951
|
+
keyDefinitionHash: text().notNull(),
|
|
1952
|
+
metricDefinitionHash: text().notNull(),
|
|
1953
|
+
status: text().notNull(),
|
|
1954
|
+
cursor: text(),
|
|
1955
|
+
processed: integer().notNull(),
|
|
1956
|
+
startedAt: integer().notNull(),
|
|
1957
|
+
updatedAt: integer().notNull(),
|
|
1958
|
+
completedAt: integer(),
|
|
1959
|
+
lastError: text()
|
|
1960
|
+
}, (t) => [
|
|
1961
|
+
index("by_kind_table_index").on(t.kind, t.tableKey, t.indexName),
|
|
1962
|
+
index("by_kind_status").on(t.kind, t.status),
|
|
1963
|
+
index("by_table_status").on(t.tableKey, t.status)
|
|
1964
|
+
]);
|
|
1965
|
+
const rankTreeTable = convexTable(AGGREGATE_RANK_TREE_TABLE, {
|
|
1966
|
+
aggregateName: text().notNull(),
|
|
1967
|
+
maxNodeSize: integer().notNull(),
|
|
1968
|
+
namespace: json(),
|
|
1969
|
+
root: id(AGGREGATE_RANK_NODE_TABLE).notNull()
|
|
1970
|
+
}, (tree) => [index("by_namespace").on(tree.namespace), index("by_aggregate_name").on(tree.aggregateName)]);
|
|
1971
|
+
const rankNodeTable = convexTable(AGGREGATE_RANK_NODE_TABLE, {
|
|
1972
|
+
aggregate: objectOf({
|
|
1973
|
+
count: integer().notNull(),
|
|
1974
|
+
sum: integer().notNull()
|
|
1975
|
+
}),
|
|
1976
|
+
items: arrayOf(objectOf({
|
|
1977
|
+
k: json(),
|
|
1978
|
+
v: json(),
|
|
1979
|
+
s: integer().notNull()
|
|
1980
|
+
})).notNull(),
|
|
1981
|
+
subtrees: arrayOf(text().notNull()).notNull()
|
|
2269
1982
|
});
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
},
|
|
2278
|
-
context() {
|
|
2279
|
-
return this;
|
|
2280
|
-
},
|
|
2281
|
-
middleware(handler = undefined) {
|
|
2282
|
-
return createMiddleware(handler);
|
|
2283
|
-
},
|
|
2284
|
-
create() {
|
|
2285
|
-
return {
|
|
2286
|
-
query: createProcedureBuilder(),
|
|
2287
|
-
mutation: createProcedureBuilder(),
|
|
2288
|
-
action: createProcedureBuilder(),
|
|
2289
|
-
httpAction: createProcedureBuilder(),
|
|
2290
|
-
middleware: createMiddleware,
|
|
2291
|
-
router: createRouter,
|
|
2292
|
-
};
|
|
2293
|
-
},
|
|
1983
|
+
const aggregateStorageTables = {
|
|
1984
|
+
[AGGREGATE_BUCKET_TABLE]: countBucketTable,
|
|
1985
|
+
[AGGREGATE_MEMBER_TABLE]: countMemberTable,
|
|
1986
|
+
[AGGREGATE_EXTREMA_TABLE]: countExtremaTable,
|
|
1987
|
+
[AGGREGATE_RANK_TREE_TABLE]: rankTreeTable,
|
|
1988
|
+
[AGGREGATE_RANK_NODE_TABLE]: rankNodeTable,
|
|
1989
|
+
[AGGREGATE_STATE_TABLE]: countStateTable
|
|
2294
1990
|
};
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
1991
|
+
function aggregateExtension() {
|
|
1992
|
+
return defineSchemaExtension("aggregate", aggregateStorageTables);
|
|
1993
|
+
}
|
|
2298
1994
|
|
|
2299
1995
|
//#endregion
|
|
2300
|
-
//#region src/
|
|
2301
|
-
const
|
|
2302
|
-
const
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
1996
|
+
//#region src/orm/migrations/schema.ts
|
|
1997
|
+
const MIGRATION_STATE_TABLE = "migration_state";
|
|
1998
|
+
const MIGRATION_RUN_TABLE = "migration_run";
|
|
1999
|
+
const migrationStateTable = convexTable(MIGRATION_STATE_TABLE, {
|
|
2000
|
+
migrationId: text().notNull(),
|
|
2001
|
+
checksum: text().notNull(),
|
|
2002
|
+
applied: boolean$1().notNull(),
|
|
2003
|
+
status: text().notNull(),
|
|
2004
|
+
direction: text(),
|
|
2005
|
+
runId: text(),
|
|
2006
|
+
cursor: text(),
|
|
2007
|
+
processed: integer().notNull(),
|
|
2008
|
+
startedAt: integer(),
|
|
2009
|
+
updatedAt: integer().notNull(),
|
|
2010
|
+
completedAt: integer(),
|
|
2011
|
+
lastError: text(),
|
|
2012
|
+
writeMode: text().notNull()
|
|
2013
|
+
}, (t) => [index("by_migration_id").on(t.migrationId), index("by_status").on(t.status)]);
|
|
2014
|
+
const migrationRunTable = convexTable(MIGRATION_RUN_TABLE, {
|
|
2015
|
+
runId: text().notNull(),
|
|
2016
|
+
direction: text().notNull(),
|
|
2017
|
+
status: text().notNull(),
|
|
2018
|
+
dryRun: boolean$1().notNull(),
|
|
2019
|
+
allowDrift: boolean$1().notNull(),
|
|
2020
|
+
migrationIds: custom(v.array(v.string())).notNull(),
|
|
2021
|
+
currentIndex: integer().notNull(),
|
|
2022
|
+
startedAt: integer().notNull(),
|
|
2023
|
+
updatedAt: integer().notNull(),
|
|
2024
|
+
completedAt: integer(),
|
|
2025
|
+
cancelRequested: boolean$1().notNull(),
|
|
2026
|
+
lastError: text()
|
|
2027
|
+
}, (t) => [index("by_run_id").on(t.runId), index("by_status").on(t.status)]);
|
|
2028
|
+
const migrationStorageTables = {
|
|
2029
|
+
[MIGRATION_STATE_TABLE]: migrationStateTable,
|
|
2030
|
+
[MIGRATION_RUN_TABLE]: migrationRunTable
|
|
2031
|
+
};
|
|
2032
|
+
function migrationExtension() {
|
|
2033
|
+
return defineSchemaExtension("migration", migrationStorageTables);
|
|
2316
2034
|
}
|
|
2317
2035
|
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2036
|
+
//#endregion
|
|
2037
|
+
//#region src/orm/relations.ts
|
|
2038
|
+
var RelationsBuilderTable = class {
|
|
2039
|
+
static [entityKind] = "RelationsBuilderTable";
|
|
2040
|
+
_;
|
|
2041
|
+
constructor(table, name) {
|
|
2042
|
+
this._ = {
|
|
2043
|
+
name,
|
|
2044
|
+
table
|
|
2045
|
+
};
|
|
2046
|
+
}
|
|
2328
2047
|
};
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
}
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2048
|
+
var RelationsBuilderColumn = class {
|
|
2049
|
+
static [entityKind] = "RelationsBuilderColumn";
|
|
2050
|
+
_;
|
|
2051
|
+
constructor(column, tableName, key) {
|
|
2052
|
+
this._ = {
|
|
2053
|
+
tableName,
|
|
2054
|
+
column,
|
|
2055
|
+
key
|
|
2056
|
+
};
|
|
2057
|
+
}
|
|
2058
|
+
through(column) {
|
|
2059
|
+
return new RelationsBuilderJunctionColumn(this._.column, this._.tableName, this._.key, column);
|
|
2060
|
+
}
|
|
2061
|
+
};
|
|
2062
|
+
var RelationsBuilderJunctionColumn = class {
|
|
2063
|
+
static [entityKind] = "RelationsBuilderColumn";
|
|
2064
|
+
_;
|
|
2065
|
+
constructor(column, tableName, key, through) {
|
|
2066
|
+
this._ = {
|
|
2067
|
+
tableName,
|
|
2068
|
+
column,
|
|
2069
|
+
through,
|
|
2070
|
+
key
|
|
2071
|
+
};
|
|
2072
|
+
}
|
|
2073
|
+
};
|
|
2074
|
+
var RelationsHelperStatic = class {
|
|
2075
|
+
static [entityKind] = "RelationsHelperStatic";
|
|
2076
|
+
constructor(tables) {
|
|
2077
|
+
const one = {};
|
|
2078
|
+
const many = {};
|
|
2079
|
+
for (const [tableName, table] of Object.entries(tables)) {
|
|
2080
|
+
one[tableName] = (config) => new One(tables, table, tableName, config);
|
|
2081
|
+
many[tableName] = (config) => new Many(tables, table, tableName, config);
|
|
2349
2082
|
}
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
if (!target || typeof target !== "object") return null;
|
|
2353
|
-
const record = target;
|
|
2354
|
-
for (const condition of JITI_EXPORT_CONDITION_PRIORITY) {
|
|
2355
|
-
const resolved = resolveJitiExportTarget(record[condition]);
|
|
2356
|
-
if (resolved) return resolved;
|
|
2083
|
+
this.one = one;
|
|
2084
|
+
this.many = many;
|
|
2357
2085
|
}
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2086
|
+
one;
|
|
2087
|
+
many;
|
|
2088
|
+
};
|
|
2089
|
+
function createRelationsHelper(tables) {
|
|
2090
|
+
const helperStatic = new RelationsHelperStatic(tables);
|
|
2091
|
+
const relationsTables = Object.entries(tables).reduce((acc, [tKey, value]) => {
|
|
2092
|
+
const rTable = new RelationsBuilderTable(value, tKey);
|
|
2093
|
+
const columns = Object.entries(getTableColumns(value)).reduce((colsAcc, [cKey, column]) => {
|
|
2094
|
+
colsAcc[cKey] = new RelationsBuilderColumn(column, tKey, cKey);
|
|
2095
|
+
return colsAcc;
|
|
2096
|
+
}, {});
|
|
2097
|
+
const relationsTable = Object.assign(rTable, columns);
|
|
2098
|
+
Object.defineProperty(relationsTable, "_id", {
|
|
2099
|
+
get() {
|
|
2100
|
+
throw new Error("`_id` is no longer public in relations. Use `id`.");
|
|
2101
|
+
},
|
|
2102
|
+
enumerable: false,
|
|
2103
|
+
configurable: false
|
|
2104
|
+
});
|
|
2105
|
+
acc[tKey] = relationsTable;
|
|
2106
|
+
return acc;
|
|
2107
|
+
}, {});
|
|
2108
|
+
return Object.assign(helperStatic, relationsTables);
|
|
2109
|
+
}
|
|
2110
|
+
function extractTablesFromSchema(schema) {
|
|
2111
|
+
const schemaTables = schema && typeof schema === "object" && !Array.isArray(schema) && "tables" in schema && schema.tables && typeof schema.tables === "object" && !Array.isArray(schema.tables) ? schema.tables : schema;
|
|
2112
|
+
return Object.fromEntries(Object.entries(schemaTables).filter(([_, e]) => isConvexTable(e)));
|
|
2113
|
+
}
|
|
2114
|
+
var Relation = class {
|
|
2115
|
+
static [entityKind] = "RelationV2";
|
|
2116
|
+
fieldName;
|
|
2117
|
+
sourceColumns;
|
|
2118
|
+
targetColumns;
|
|
2119
|
+
alias;
|
|
2120
|
+
where;
|
|
2121
|
+
sourceTable;
|
|
2122
|
+
targetTable;
|
|
2123
|
+
through;
|
|
2124
|
+
throughTable;
|
|
2125
|
+
isReversed;
|
|
2126
|
+
/** @internal */
|
|
2127
|
+
sourceColumnTableNames = [];
|
|
2128
|
+
/** @internal */
|
|
2129
|
+
targetColumnTableNames = [];
|
|
2130
|
+
constructor(targetTable, targetTableName) {
|
|
2131
|
+
this.targetTableName = targetTableName;
|
|
2132
|
+
this.targetTable = targetTable;
|
|
2361
2133
|
}
|
|
2362
|
-
return null;
|
|
2363
2134
|
};
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2135
|
+
var One = class extends Relation {
|
|
2136
|
+
static [entityKind] = "OneV2";
|
|
2137
|
+
relationType = "one";
|
|
2138
|
+
optional;
|
|
2139
|
+
constructor(tables, targetTable, targetTableName, config) {
|
|
2140
|
+
super(targetTable, targetTableName);
|
|
2141
|
+
this.alias = config?.alias;
|
|
2142
|
+
this.where = config?.where;
|
|
2143
|
+
if (config?.from) this.sourceColumns = (Array.isArray(config.from) ? config.from : [config.from]).map((it) => {
|
|
2144
|
+
this.throughTable ??= it._.through ? tables[it._.through._.tableName] : void 0;
|
|
2145
|
+
this.sourceColumnTableNames.push(it._.tableName);
|
|
2146
|
+
return it._.column;
|
|
2147
|
+
});
|
|
2148
|
+
if (config?.to) this.targetColumns = (Array.isArray(config.to) ? config.to : [config.to]).map((it) => {
|
|
2149
|
+
this.throughTable ??= it._.through ? tables[it._.through._.tableName] : void 0;
|
|
2150
|
+
this.targetColumnTableNames.push(it._.tableName);
|
|
2151
|
+
return it._.column;
|
|
2152
|
+
});
|
|
2153
|
+
if (this.throughTable) this.through = {
|
|
2154
|
+
source: (Array.isArray(config?.from) ? config.from : config?.from ? [config.from] : []).map((c) => c._.through),
|
|
2155
|
+
target: (Array.isArray(config?.to) ? config.to : config?.to ? [config.to] : []).map((c) => c._.through)
|
|
2156
|
+
};
|
|
2157
|
+
this.optional = config?.optional ?? true;
|
|
2377
2158
|
}
|
|
2378
|
-
return aliases;
|
|
2379
2159
|
};
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2160
|
+
var Many = class extends Relation {
|
|
2161
|
+
static [entityKind] = "ManyV2";
|
|
2162
|
+
relationType = "many";
|
|
2163
|
+
constructor(tables, targetTable, targetTableName, config) {
|
|
2164
|
+
super(targetTable, targetTableName);
|
|
2165
|
+
this.config = config;
|
|
2166
|
+
this.alias = config?.alias;
|
|
2167
|
+
this.where = config?.where;
|
|
2168
|
+
if (config?.from) this.sourceColumns = (Array.isArray(config.from) ? config.from : [config.from]).map((it) => {
|
|
2169
|
+
this.throughTable ??= it._.through ? tables[it._.through._.tableName] : void 0;
|
|
2170
|
+
this.sourceColumnTableNames.push(it._.tableName);
|
|
2171
|
+
return it._.column;
|
|
2172
|
+
});
|
|
2173
|
+
if (config?.to) this.targetColumns = (Array.isArray(config.to) ? config.to : [config.to]).map((it) => {
|
|
2174
|
+
this.throughTable ??= it._.through ? tables[it._.through._.tableName] : void 0;
|
|
2175
|
+
this.targetColumnTableNames.push(it._.tableName);
|
|
2176
|
+
return it._.column;
|
|
2177
|
+
});
|
|
2178
|
+
if (this.throughTable) this.through = {
|
|
2179
|
+
source: (Array.isArray(config?.from) ? config.from : config?.from ? [config.from] : []).map((c) => c._.through),
|
|
2180
|
+
target: (Array.isArray(config?.to) ? config.to : config?.to ? [config.to] : []).map((c) => c._.through)
|
|
2181
|
+
};
|
|
2182
|
+
}
|
|
2386
2183
|
};
|
|
2387
|
-
|
|
2388
|
-
const
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2184
|
+
function buildRelations(tables, config, strict, defaults) {
|
|
2185
|
+
const tablesConfig = {};
|
|
2186
|
+
for (const [tsName, table] of Object.entries(tables)) tablesConfig[tsName] = {
|
|
2187
|
+
table,
|
|
2188
|
+
name: tsName,
|
|
2189
|
+
polymorphic: table[TablePolymorphic],
|
|
2190
|
+
relations: config[tsName] ?? {},
|
|
2191
|
+
strict,
|
|
2192
|
+
defaults
|
|
2193
|
+
};
|
|
2194
|
+
return processRelations(tablesConfig, tables);
|
|
2195
|
+
}
|
|
2196
|
+
function defineRelations(schema, relations) {
|
|
2197
|
+
const tables = extractTablesFromSchema(schema);
|
|
2198
|
+
const schemaOptions = schema[OrmSchemaOptions];
|
|
2199
|
+
const strict = schemaOptions?.strict ?? true;
|
|
2200
|
+
const defaults = schemaOptions?.defaults;
|
|
2201
|
+
const schemaDefinition = schema[OrmSchemaDefinition];
|
|
2202
|
+
const pluginTableNames = schema[OrmSchemaExtensionTables];
|
|
2203
|
+
const plugins = schema[OrmSchemaExtensions];
|
|
2204
|
+
const tablesConfig = buildRelations(tables, relations ? relations(createRelationsHelper(tables)) : {}, strict, defaults);
|
|
2205
|
+
Object.defineProperty(tablesConfig, OrmSchemaOptions, {
|
|
2206
|
+
value: {
|
|
2207
|
+
strict,
|
|
2208
|
+
defaults
|
|
2209
|
+
},
|
|
2210
|
+
enumerable: false
|
|
2211
|
+
});
|
|
2212
|
+
if (schemaDefinition) Object.defineProperty(tablesConfig, OrmSchemaDefinition, {
|
|
2213
|
+
value: schemaDefinition,
|
|
2214
|
+
enumerable: false
|
|
2215
|
+
});
|
|
2216
|
+
if (pluginTableNames) Object.defineProperty(tablesConfig, OrmSchemaExtensionTables, {
|
|
2217
|
+
value: pluginTableNames,
|
|
2218
|
+
enumerable: false
|
|
2219
|
+
});
|
|
2220
|
+
if (plugins) Object.defineProperty(tablesConfig, OrmSchemaExtensions, {
|
|
2221
|
+
value: plugins,
|
|
2222
|
+
enumerable: false
|
|
2223
|
+
});
|
|
2224
|
+
return tablesConfig;
|
|
2225
|
+
}
|
|
2226
|
+
function processRelations(tablesConfig, tables) {
|
|
2227
|
+
for (const tableConfig of Object.values(tablesConfig)) for (const [relationFieldName, relation] of Object.entries(tableConfig.relations)) {
|
|
2228
|
+
if (!isRelation(relation)) continue;
|
|
2229
|
+
relation.sourceTable = tableConfig.table;
|
|
2230
|
+
relation.fieldName = relationFieldName;
|
|
2231
|
+
}
|
|
2232
|
+
for (const [sourceTableName, tableConfig] of Object.entries(tablesConfig)) for (const [relationFieldName, relation] of Object.entries(tableConfig.relations)) {
|
|
2233
|
+
if (!isRelation(relation)) continue;
|
|
2234
|
+
let reverseRelation;
|
|
2235
|
+
const { targetTableName, alias, sourceColumns, targetColumns, throughTable, sourceTable, through, where, sourceColumnTableNames, targetColumnTableNames } = relation;
|
|
2236
|
+
const relationPrintName = `relations -> ${tableConfig.name}: { ${relationFieldName}: r.${relation.relationType === "one" ? "one" : "many"}.${targetTableName}(...) }`;
|
|
2237
|
+
if (relationFieldName in getTableColumns(tableConfig.table)) throw new Error(`${relationPrintName}: relation name collides with column "${relationFieldName}" of table "${tableConfig.name}"`);
|
|
2238
|
+
if (typeof alias === "string" && !alias) throw new Error(`${relationPrintName}: "alias" cannot be empty`);
|
|
2239
|
+
if (sourceColumns?.length === 0) throw new Error(`${relationPrintName}: "from" cannot be empty`);
|
|
2240
|
+
if (targetColumns?.length === 0) throw new Error(`${relationPrintName}: "to" cannot be empty`);
|
|
2241
|
+
if (sourceColumns && targetColumns) {
|
|
2242
|
+
if (sourceColumns.length !== targetColumns.length && !throughTable) throw new Error(`${relationPrintName}: "from" and "to" must have same length`);
|
|
2243
|
+
for (const sName of sourceColumnTableNames) if (sName !== sourceTableName) throw new Error(`${relationPrintName}: all "from" columns must belong to table "${sourceTableName}", found "${sName}"`);
|
|
2244
|
+
for (const tName of targetColumnTableNames) if (tName !== targetTableName) throw new Error(`${relationPrintName}: all "to" columns must belong to table "${targetTableName}", found "${tName}"`);
|
|
2245
|
+
if (through) {
|
|
2246
|
+
if (through.source.length !== sourceColumns.length || through.target.length !== targetColumns.length) throw new Error(`${relationPrintName}: .through() must be used on all columns in "from" and "to" or none`);
|
|
2247
|
+
for (const column of through.source) if (tables[column._.tableName] !== throughTable) throw new Error(`${relationPrintName}: .through() must use same table for all columns`);
|
|
2248
|
+
for (const column of through.target) if (tables[column._.tableName] !== throughTable) throw new Error(`${relationPrintName}: .through() must use same table for all columns`);
|
|
2249
|
+
}
|
|
2250
|
+
continue;
|
|
2251
|
+
}
|
|
2252
|
+
if (sourceColumns || targetColumns) throw new Error(`${relationPrintName}: relation must have both "from" and "to" or none`);
|
|
2253
|
+
const reverseTableConfig = tablesConfig[targetTableName];
|
|
2254
|
+
if (!reverseTableConfig) throw new Error(`${relationPrintName}: missing "from"/"to" and no reverse relation found for "${targetTableName}"`);
|
|
2255
|
+
if (alias) {
|
|
2256
|
+
const reverseRelations = Object.values(reverseTableConfig.relations).filter((it) => isRelation(it) && it.alias === alias && it !== relation);
|
|
2257
|
+
if (reverseRelations.length > 1) throw new Error(`${relationPrintName}: multiple reverse relations with alias "${alias}" found`);
|
|
2258
|
+
reverseRelation = reverseRelations[0];
|
|
2259
|
+
if (!reverseRelation) throw new Error(`${relationPrintName}: no reverse relation with alias "${alias}" found in "${targetTableName}"`);
|
|
2260
|
+
} else {
|
|
2261
|
+
const reverseRelations = Object.values(reverseTableConfig.relations).filter((it) => isRelation(it) && it.targetTable === sourceTable && !it.alias && it !== relation);
|
|
2262
|
+
if (reverseRelations.length > 1) throw new Error(`${relationPrintName}: multiple relations between "${targetTableName}" and "${sourceTableName}"; use alias`);
|
|
2263
|
+
reverseRelation = reverseRelations[0];
|
|
2264
|
+
if (!reverseRelation) throw new Error(`${relationPrintName}: no reverse relation between "${targetTableName}" and "${sourceTableName}"`);
|
|
2265
|
+
}
|
|
2266
|
+
if (!reverseRelation.sourceColumns || !reverseRelation.targetColumns) throw new Error(`${relationPrintName}: reverse relation "${targetTableName}.${reverseRelation.fieldName}" missing "from"/"to"`);
|
|
2267
|
+
relation.sourceColumns = reverseRelation.targetColumns;
|
|
2268
|
+
relation.targetColumns = reverseRelation.sourceColumns;
|
|
2269
|
+
relation.through = reverseRelation.through ? {
|
|
2270
|
+
source: reverseRelation.through.target,
|
|
2271
|
+
target: reverseRelation.through.source
|
|
2272
|
+
} : void 0;
|
|
2273
|
+
relation.throughTable = reverseRelation.throughTable;
|
|
2274
|
+
relation.isReversed = !where;
|
|
2275
|
+
relation.where = where ?? reverseRelation.where;
|
|
2276
|
+
}
|
|
2277
|
+
return tablesConfig;
|
|
2278
|
+
}
|
|
2279
|
+
function isConvexTable(value) {
|
|
2280
|
+
return typeof value === "object" && value !== null && "tableName" in value && Columns in value;
|
|
2281
|
+
}
|
|
2282
|
+
function isRelation(value) {
|
|
2283
|
+
return value instanceof Relation;
|
|
2284
|
+
}
|
|
2285
|
+
function getTableColumns(table) {
|
|
2286
|
+
const columns = table[Columns];
|
|
2287
|
+
const system = {};
|
|
2288
|
+
if (table.id) system.id = table.id;
|
|
2289
|
+
if (table.createdAt || table._creationTime) system.createdAt = table._creationTime ?? table.createdAt;
|
|
2290
|
+
return {
|
|
2291
|
+
...columns,
|
|
2292
|
+
...system
|
|
2293
|
+
};
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2296
|
+
//#endregion
|
|
2297
|
+
//#region src/orm/triggers.ts
|
|
2298
|
+
function defineTriggers(schema, triggers) {
|
|
2299
|
+
return triggers;
|
|
2300
|
+
}
|
|
2301
|
+
|
|
2302
|
+
//#endregion
|
|
2303
|
+
//#region src/orm/schema.ts
|
|
2304
|
+
const BUILTIN_SCHEMA_EXTENSIONS = [aggregateExtension(), migrationExtension()];
|
|
2305
|
+
function resolveSchemaExtensions(extensions) {
|
|
2306
|
+
const resolved = [...BUILTIN_SCHEMA_EXTENSIONS, ...extensions ?? []];
|
|
2307
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2308
|
+
for (const extension of resolved) {
|
|
2309
|
+
if (seen.has(extension.key)) throw new Error(`defineSchema received duplicate extension '${extension.key}'. Remove duplicate extension registrations.`);
|
|
2310
|
+
seen.add(extension.key);
|
|
2393
2311
|
}
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
const
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
aliases[aliasKey] = path.resolve(baseUrl, aliasTarget);
|
|
2312
|
+
return resolved;
|
|
2313
|
+
}
|
|
2314
|
+
function mergeRelationConfigs(sources) {
|
|
2315
|
+
const merged = {};
|
|
2316
|
+
const relationOrigins = /* @__PURE__ */ new Map();
|
|
2317
|
+
for (const { source, config } of sources) for (const [tableName, relationConfig] of Object.entries(config)) {
|
|
2318
|
+
if (!relationConfig) continue;
|
|
2319
|
+
let tableRelations = merged[tableName];
|
|
2320
|
+
if (!tableRelations) {
|
|
2321
|
+
tableRelations = Object.create(null);
|
|
2322
|
+
merged[tableName] = tableRelations;
|
|
2323
|
+
}
|
|
2324
|
+
for (const [fieldName, relation] of Object.entries(relationConfig)) {
|
|
2325
|
+
const relationKey = `${tableName}.${fieldName}`;
|
|
2326
|
+
const existingSource = relationOrigins.get(relationKey);
|
|
2327
|
+
if (existingSource) throw new Error(`defineSchema relation field '${relationKey}' is defined more than once (${existingSource} and ${source}).`);
|
|
2328
|
+
tableRelations[fieldName] = relation;
|
|
2329
|
+
relationOrigins.set(relationKey, source);
|
|
2330
|
+
}
|
|
2414
2331
|
}
|
|
2415
|
-
return
|
|
2416
|
-
}
|
|
2417
|
-
|
|
2418
|
-
const
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2332
|
+
return merged;
|
|
2333
|
+
}
|
|
2334
|
+
function mergeTriggerConfigs(sources) {
|
|
2335
|
+
const merged = {};
|
|
2336
|
+
const triggerOrigins = /* @__PURE__ */ new Map();
|
|
2337
|
+
for (const { source, config } of sources) for (const [tableName, tableConfig] of Object.entries(config)) {
|
|
2338
|
+
if (!tableConfig || typeof tableConfig !== "object" || Array.isArray(tableConfig)) {
|
|
2339
|
+
const triggerKey = `${tableName}`;
|
|
2340
|
+
const existingSource = triggerOrigins.get(triggerKey);
|
|
2341
|
+
if (existingSource) throw new Error(`defineSchema trigger '${triggerKey}' is defined more than once (${existingSource} and ${source}).`);
|
|
2342
|
+
merged[tableName] = tableConfig;
|
|
2343
|
+
triggerOrigins.set(triggerKey, source);
|
|
2344
|
+
continue;
|
|
2345
|
+
}
|
|
2346
|
+
let mergedTable = merged[tableName];
|
|
2347
|
+
if (!mergedTable) {
|
|
2348
|
+
mergedTable = Object.create(null);
|
|
2349
|
+
merged[tableName] = mergedTable;
|
|
2350
|
+
}
|
|
2351
|
+
for (const [hookKey, hookValue] of Object.entries(tableConfig)) {
|
|
2352
|
+
if ((hookKey === "create" || hookKey === "update" || hookKey === "delete") && hookValue && typeof hookValue === "object" && !Array.isArray(hookValue)) {
|
|
2353
|
+
let mergedOperation = mergedTable[hookKey];
|
|
2354
|
+
if (!mergedOperation || typeof mergedOperation !== "object") {
|
|
2355
|
+
mergedOperation = Object.create(null);
|
|
2356
|
+
mergedTable[hookKey] = mergedOperation;
|
|
2357
|
+
}
|
|
2358
|
+
for (const [operationHookKey, operationHookValue] of Object.entries(hookValue)) {
|
|
2359
|
+
const triggerKey = `${tableName}.${hookKey}.${operationHookKey}`;
|
|
2360
|
+
const existingSource = triggerOrigins.get(triggerKey);
|
|
2361
|
+
if (existingSource) throw new Error(`defineSchema trigger '${triggerKey}' is defined more than once (${existingSource} and ${source}).`);
|
|
2362
|
+
mergedOperation[operationHookKey] = operationHookValue;
|
|
2363
|
+
triggerOrigins.set(triggerKey, source);
|
|
2364
|
+
}
|
|
2365
|
+
continue;
|
|
2366
|
+
}
|
|
2367
|
+
const triggerKey = `${tableName}.${hookKey}`;
|
|
2368
|
+
const existingSource = triggerOrigins.get(triggerKey);
|
|
2369
|
+
if (existingSource) throw new Error(`defineSchema trigger '${triggerKey}' is defined more than once (${existingSource} and ${source}).`);
|
|
2370
|
+
mergedTable[hookKey] = hookValue;
|
|
2371
|
+
triggerOrigins.set(triggerKey, source);
|
|
2372
|
+
}
|
|
2428
2373
|
}
|
|
2429
|
-
|
|
2374
|
+
return merged;
|
|
2375
|
+
}
|
|
2376
|
+
function defineMetadata(target, key, value) {
|
|
2377
|
+
const existing = Object.getOwnPropertyDescriptor(target, key);
|
|
2378
|
+
if (existing && !existing.configurable) return;
|
|
2379
|
+
Object.defineProperty(target, key, {
|
|
2380
|
+
value,
|
|
2381
|
+
enumerable: false,
|
|
2382
|
+
configurable: true
|
|
2383
|
+
});
|
|
2384
|
+
}
|
|
2385
|
+
const OrmSchemaComposerState = Symbol("kitcn:OrmSchemaComposerState");
|
|
2386
|
+
function getSchemaComposerState(schema) {
|
|
2387
|
+
if (!schema || typeof schema !== "object") return;
|
|
2388
|
+
return schema[OrmSchemaComposerState];
|
|
2389
|
+
}
|
|
2390
|
+
function finalizeSchemaMetadata(schema) {
|
|
2391
|
+
if (!schema || typeof schema !== "object") return;
|
|
2392
|
+
const composerState = getSchemaComposerState(schema);
|
|
2393
|
+
if (!composerState) return;
|
|
2394
|
+
const schemaObject = schema;
|
|
2395
|
+
const resolvedExtensions = schemaObject[OrmSchemaExtensions] ?? resolveSchemaExtensions(composerState.extensions);
|
|
2396
|
+
const extensionTableNames = schemaObject[OrmSchemaExtensionTables] ?? Object.freeze([]);
|
|
2397
|
+
const options = schemaObject[OrmSchemaOptions];
|
|
2398
|
+
const extensionRelationFactories = resolvedExtensions.map((extension) => ({
|
|
2399
|
+
key: extension.key,
|
|
2400
|
+
relations: extension[OrmSchemaExtensionRelations]
|
|
2401
|
+
})).filter((entry) => entry.relations !== void 0);
|
|
2402
|
+
const extensionTriggerFactories = resolvedExtensions.map((extension) => ({
|
|
2403
|
+
key: extension.key,
|
|
2404
|
+
triggers: extension[OrmSchemaExtensionTriggers]
|
|
2405
|
+
})).filter((entry) => entry.triggers !== void 0);
|
|
2406
|
+
const hasExtensionRelations = extensionRelationFactories.length > 0;
|
|
2407
|
+
const hasExtensionTriggers = extensionTriggerFactories.length > 0;
|
|
2408
|
+
const shouldBuildRelations = hasExtensionRelations || hasExtensionTriggers || Boolean(composerState.relations) || Boolean(composerState.triggers);
|
|
2409
|
+
let relations = schemaObject[OrmSchemaRelations];
|
|
2410
|
+
if (!relations && shouldBuildRelations) {
|
|
2411
|
+
relations = hasExtensionRelations || composerState.relations ? defineRelations(schema, (helpers) => mergeRelationConfigs([...extensionRelationFactories.map((extension) => ({
|
|
2412
|
+
source: `extension '${extension.key}'`,
|
|
2413
|
+
config: extension.relations(helpers)
|
|
2414
|
+
})), ...composerState.relations ? [{
|
|
2415
|
+
source: "schema.relations()",
|
|
2416
|
+
config: composerState.relations(helpers)
|
|
2417
|
+
}] : []])) : defineRelations(schema);
|
|
2418
|
+
defineMetadata(relations, OrmSchemaDefinition, schema);
|
|
2419
|
+
if (options) defineMetadata(relations, OrmSchemaOptions, options);
|
|
2420
|
+
defineMetadata(relations, OrmSchemaExtensionTables, extensionTableNames);
|
|
2421
|
+
defineMetadata(relations, OrmSchemaExtensions, resolvedExtensions);
|
|
2422
|
+
defineMetadata(schema, OrmSchemaRelations, relations);
|
|
2423
|
+
}
|
|
2424
|
+
if (!relations || schemaObject[OrmSchemaTriggers]) return;
|
|
2425
|
+
const triggerSources = hasExtensionTriggers || composerState.triggers ? [...extensionTriggerFactories.map((extension) => ({
|
|
2426
|
+
source: `extension '${extension.key}'`,
|
|
2427
|
+
config: typeof extension.triggers === "function" ? extension.triggers(relations) : extension.triggers
|
|
2428
|
+
})), ...composerState.triggers ? [{
|
|
2429
|
+
source: "schema.triggers()",
|
|
2430
|
+
config: composerState.triggers
|
|
2431
|
+
}] : []] : [];
|
|
2432
|
+
if (triggerSources.length === 0) return;
|
|
2433
|
+
defineMetadata(schema, OrmSchemaTriggers, defineTriggers(relations, mergeTriggerConfigs(triggerSources)));
|
|
2434
|
+
}
|
|
2435
|
+
function getSchemaRelations(schema) {
|
|
2436
|
+
if (!schema || typeof schema !== "object") return;
|
|
2437
|
+
finalizeSchemaMetadata(schema);
|
|
2438
|
+
return schema[OrmSchemaRelations];
|
|
2439
|
+
}
|
|
2440
|
+
function getSchemaTriggers(schema) {
|
|
2441
|
+
if (!schema || typeof schema !== "object") return;
|
|
2442
|
+
finalizeSchemaMetadata(schema);
|
|
2443
|
+
return schema[OrmSchemaTriggers];
|
|
2444
|
+
}
|
|
2430
2445
|
|
|
2431
2446
|
//#endregion
|
|
2432
2447
|
//#region src/shared/meta-utils.ts
|
|
@@ -2510,6 +2525,56 @@ const logger = {
|
|
|
2510
2525
|
}
|
|
2511
2526
|
};
|
|
2512
2527
|
|
|
2528
|
+
//#endregion
|
|
2529
|
+
//#region src/cli/utils/schema-tables.ts
|
|
2530
|
+
/**
|
|
2531
|
+
* Schema introspection shared by codegen and the backend flows.
|
|
2532
|
+
*
|
|
2533
|
+
* Both need the same answer to "does this app use aggregate/rank indexes?":
|
|
2534
|
+
* codegen registers `aggregateCapability()` on it, and the CLI skips the
|
|
2535
|
+
* backfill flows on it. Two implementations would drift into a state where the
|
|
2536
|
+
* generated ORM refuses a call the CLI still makes.
|
|
2537
|
+
*/
|
|
2538
|
+
/** The `defineSchema(...)` value behind a loaded `schema.ts` module. */
|
|
2539
|
+
function resolveSchemaDefaultExport(schemaModule) {
|
|
2540
|
+
const schemaValue = schemaModule.default !== void 0 ? schemaModule.default : schemaModule;
|
|
2541
|
+
if (!schemaValue || typeof schemaValue !== "object") return null;
|
|
2542
|
+
return schemaValue;
|
|
2543
|
+
}
|
|
2544
|
+
/**
|
|
2545
|
+
* Every live table behind a schema value.
|
|
2546
|
+
*
|
|
2547
|
+
* `SchemaDefinition.tables` is the merged base + plugin-extension map, so auth,
|
|
2548
|
+
* ratelimit and the built-in aggregate/migration bookkeeping tables are all
|
|
2549
|
+
* covered. The relations config is unioned in because a `.relations()` chain
|
|
2550
|
+
* re-materializes the definition, and it is absent for schemas that declare
|
|
2551
|
+
* neither relations nor triggers.
|
|
2552
|
+
*/
|
|
2553
|
+
function collectSchemaTables(schemaValue) {
|
|
2554
|
+
const allTables = /* @__PURE__ */ new Set();
|
|
2555
|
+
const relations = getSchemaRelations(schemaValue);
|
|
2556
|
+
if (relations && typeof relations === "object" && !Array.isArray(relations)) for (const relationConfig of Object.values(relations)) {
|
|
2557
|
+
const table = relationConfig?.table;
|
|
2558
|
+
if (table) allTables.add(table);
|
|
2559
|
+
}
|
|
2560
|
+
const tables = schemaValue.tables;
|
|
2561
|
+
if (tables && typeof tables === "object" && !Array.isArray(tables)) {
|
|
2562
|
+
for (const table of Object.values(tables)) if (table) allTables.add(table);
|
|
2563
|
+
}
|
|
2564
|
+
return [...allTables];
|
|
2565
|
+
}
|
|
2566
|
+
/**
|
|
2567
|
+
* Whether the schema needs the aggregate-index runtime.
|
|
2568
|
+
*
|
|
2569
|
+
* `aggregateIndex(...)` / `rankIndex(...)` register synchronously inside
|
|
2570
|
+
* `convexTable(...)`, so the answer is final once `schema.ts` has evaluated.
|
|
2571
|
+
* Duck-typed rather than routed through `getTableConfig`, which throws on a
|
|
2572
|
+
* raw Convex `defineTable()` in a mixed schema.
|
|
2573
|
+
*/
|
|
2574
|
+
function schemaDeclaresAggregateIndexes(schemaValue) {
|
|
2575
|
+
return collectSchemaTables(schemaValue).some((table) => getAggregateIndexes(table).length > 0 || getRankIndexes(table).length > 0);
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2513
2578
|
//#endregion
|
|
2514
2579
|
//#region src/cli/codegen.ts
|
|
2515
2580
|
/** Valid JS identifier pattern for object keys */
|
|
@@ -2606,49 +2671,51 @@ const GENERATED_ORM_RUNTIME_PROCEDURES = [
|
|
|
2606
2671
|
type: "mutation"
|
|
2607
2672
|
},
|
|
2608
2673
|
{
|
|
2609
|
-
exportName: "
|
|
2674
|
+
exportName: "migrationRun",
|
|
2610
2675
|
internal: true,
|
|
2611
2676
|
type: "mutation"
|
|
2612
2677
|
},
|
|
2613
2678
|
{
|
|
2614
|
-
exportName: "
|
|
2679
|
+
exportName: "migrationRunChunk",
|
|
2615
2680
|
internal: true,
|
|
2616
2681
|
type: "mutation"
|
|
2617
2682
|
},
|
|
2618
2683
|
{
|
|
2619
|
-
exportName: "
|
|
2684
|
+
exportName: "migrationStatus",
|
|
2620
2685
|
internal: true,
|
|
2621
2686
|
type: "mutation"
|
|
2622
2687
|
},
|
|
2623
2688
|
{
|
|
2624
|
-
exportName: "
|
|
2689
|
+
exportName: "migrationCancel",
|
|
2625
2690
|
internal: true,
|
|
2626
2691
|
type: "mutation"
|
|
2627
2692
|
},
|
|
2628
2693
|
{
|
|
2629
|
-
exportName: "
|
|
2694
|
+
exportName: "resetChunk",
|
|
2630
2695
|
internal: true,
|
|
2631
2696
|
type: "mutation"
|
|
2632
2697
|
},
|
|
2633
2698
|
{
|
|
2634
|
-
exportName: "
|
|
2699
|
+
exportName: "reset",
|
|
2635
2700
|
internal: true,
|
|
2636
|
-
type: "
|
|
2637
|
-
}
|
|
2701
|
+
type: "action"
|
|
2702
|
+
}
|
|
2703
|
+
];
|
|
2704
|
+
const GENERATED_AGGREGATE_RUNTIME_PROCEDURES = [
|
|
2638
2705
|
{
|
|
2639
|
-
exportName: "
|
|
2706
|
+
exportName: "aggregateBackfill",
|
|
2640
2707
|
internal: true,
|
|
2641
2708
|
type: "mutation"
|
|
2642
2709
|
},
|
|
2643
2710
|
{
|
|
2644
|
-
exportName: "
|
|
2711
|
+
exportName: "aggregateBackfillChunk",
|
|
2645
2712
|
internal: true,
|
|
2646
2713
|
type: "mutation"
|
|
2647
2714
|
},
|
|
2648
2715
|
{
|
|
2649
|
-
exportName: "
|
|
2716
|
+
exportName: "aggregateBackfillStatus",
|
|
2650
2717
|
internal: true,
|
|
2651
|
-
type: "
|
|
2718
|
+
type: "mutation"
|
|
2652
2719
|
}
|
|
2653
2720
|
];
|
|
2654
2721
|
function listFilesRecursive(cwd, relDir = "") {
|
|
@@ -2848,6 +2915,9 @@ const GENERATED_DIR = "generated";
|
|
|
2848
2915
|
function getGeneratedServerOutputFile(functionsDir) {
|
|
2849
2916
|
return path.join(functionsDir, GENERATED_DIR, "server.ts");
|
|
2850
2917
|
}
|
|
2918
|
+
function getGeneratedAggregateOutputFile(functionsDir) {
|
|
2919
|
+
return path.join(functionsDir, GENERATED_DIR, "aggregate.ts");
|
|
2920
|
+
}
|
|
2851
2921
|
function getGeneratedOrmOutputFile(functionsDir) {
|
|
2852
2922
|
return path.join(functionsDir, GENERATED_DIR, "orm.ts");
|
|
2853
2923
|
}
|
|
@@ -2991,34 +3061,44 @@ function listGeneratedRuntimeFiles(functionsDir) {
|
|
|
2991
3061
|
if (!fs.existsSync(generatedDir)) return [];
|
|
2992
3062
|
return listFilesRecursive(generatedDir).filter((file) => file.endsWith(".runtime.ts")).map((file) => path.join(generatedDir, file));
|
|
2993
3063
|
}
|
|
3064
|
+
const EMPTY_SCHEMA_METADATA = {
|
|
3065
|
+
hasOrmSchema: false,
|
|
3066
|
+
hasAggregateIndexes: false,
|
|
3067
|
+
hasRelations: false,
|
|
3068
|
+
hasTriggers: false
|
|
3069
|
+
};
|
|
2994
3070
|
async function resolveSchemaMetadataForCodegen(functionsDir, debug, createJitiInstance = createProjectJiti) {
|
|
2995
3071
|
const schemaPath = path.join(functionsDir, "schema.ts");
|
|
2996
|
-
if (!fs.existsSync(schemaPath)) return
|
|
2997
|
-
hasOrmSchema: false,
|
|
2998
|
-
hasRelations: false,
|
|
2999
|
-
hasTriggers: false
|
|
3000
|
-
};
|
|
3072
|
+
if (!fs.existsSync(schemaPath)) return EMPTY_SCHEMA_METADATA;
|
|
3001
3073
|
const jitiInstance = createJitiInstance();
|
|
3002
3074
|
try {
|
|
3003
3075
|
const schemaModule = await jitiInstance.import(schemaPath);
|
|
3004
3076
|
const schemaValue = schemaModule && typeof schemaModule === "object" ? schemaModule.default ?? schemaModule : null;
|
|
3005
|
-
if (!schemaValue || typeof schemaValue !== "object") return
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
};
|
|
3077
|
+
if (!schemaValue || typeof schemaValue !== "object") return EMPTY_SCHEMA_METADATA;
|
|
3078
|
+
const hasOrmSchema = OrmSchemaOptions in schemaValue;
|
|
3079
|
+
const hasRelations = Boolean(getSchemaRelations(schemaValue));
|
|
3080
|
+
const hasTriggers = Boolean(getSchemaTriggers(schemaValue));
|
|
3010
3081
|
return {
|
|
3011
|
-
hasOrmSchema
|
|
3012
|
-
|
|
3013
|
-
|
|
3082
|
+
hasOrmSchema,
|
|
3083
|
+
hasAggregateIndexes: hasOrmSchema && resolveHasAggregateIndexes(schemaValue, schemaPath, debug),
|
|
3084
|
+
hasRelations,
|
|
3085
|
+
hasTriggers
|
|
3014
3086
|
};
|
|
3015
3087
|
} catch (error) {
|
|
3016
3088
|
if (debug) logger.warn(`⚠️ Failed to load schema extensions from ${schemaPath}: ${error.message}`);
|
|
3017
|
-
return
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3089
|
+
return EMPTY_SCHEMA_METADATA;
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
/**
|
|
3093
|
+
* Fails open: registering an unused capability only costs bundle size, while
|
|
3094
|
+
* omitting a needed one breaks `createOrm()` for every function in the app.
|
|
3095
|
+
*/
|
|
3096
|
+
function resolveHasAggregateIndexes(schemaValue, schemaPath, debug) {
|
|
3097
|
+
try {
|
|
3098
|
+
return schemaDeclaresAggregateIndexes(schemaValue);
|
|
3099
|
+
} catch (error) {
|
|
3100
|
+
if (debug) logger.warn(`⚠️ Failed to inspect aggregate indexes in ${schemaPath}: ${error.message}`);
|
|
3101
|
+
return true;
|
|
3022
3102
|
}
|
|
3023
3103
|
}
|
|
3024
3104
|
function cleanupGeneratedPluginArtifacts(functionsDir) {
|
|
@@ -3027,7 +3107,7 @@ function cleanupGeneratedPluginArtifacts(functionsDir) {
|
|
|
3027
3107
|
force: true
|
|
3028
3108
|
});
|
|
3029
3109
|
}
|
|
3030
|
-
function emitGeneratedServerFile(outputFile, functionsDir, hasOrmSchema, hasMigrationsManifest, procedureNameLookup) {
|
|
3110
|
+
function emitGeneratedServerFile(outputFile, functionsDir, hasOrmSchema, hasAggregateIndexes, hasMigrationsManifest, procedureNameLookup) {
|
|
3031
3111
|
const asSingleQuotedImport = (importPath) => `'${importPath.replaceAll("'", "\\'")}'`;
|
|
3032
3112
|
const serverTypesImportPath = getServerTypesImportPath(outputFile, functionsDir);
|
|
3033
3113
|
const dataModelImportPath = getDataModelImportPath(outputFile, functionsDir);
|
|
@@ -3039,6 +3119,9 @@ function emitGeneratedServerFile(outputFile, functionsDir, hasOrmSchema, hasMigr
|
|
|
3039
3119
|
const migrationsManifestImportLiteral = asSingleQuotedImport(migrationsManifestImportPath);
|
|
3040
3120
|
const migrationsImportLine = hasMigrationsManifest ? `import { migrations } from ${migrationsManifestImportLiteral};\n` : "";
|
|
3041
3121
|
const migrationsConfigLine = hasMigrationsManifest ? " migrations,\n" : "";
|
|
3122
|
+
const capabilityImportLines = [hasAggregateIndexes ? "import { aggregateCapability } from 'kitcn/orm/aggregate-index';\n" : "", hasMigrationsManifest ? "import { migrationCapability } from 'kitcn/orm/migrations';\n" : ""].join("");
|
|
3123
|
+
const capabilityEntries = [...hasAggregateIndexes ? ["aggregateCapability()"] : [], ...hasMigrationsManifest ? ["migrationCapability()"] : []];
|
|
3124
|
+
const capabilitiesConfigLine = capabilityEntries.length > 0 ? ` capabilities: [${capabilityEntries.join(", ")}],\n` : "";
|
|
3042
3125
|
const functionsDirHint = normalizeImportPath(path.relative(process.cwd(), functionsDir)) || "convex";
|
|
3043
3126
|
const procedureNameLookupLiteral = emitProcedureNameLookupLiteral(procedureNameLookup);
|
|
3044
3127
|
if (!hasOrmSchema) return `// biome-ignore-all format: generated
|
|
@@ -3080,6 +3163,7 @@ export const initCRPC = baseInitCRPC.dataModel<DataModel>().context({
|
|
|
3080
3163
|
export { httpAction, internalMutation };
|
|
3081
3164
|
`;
|
|
3082
3165
|
const moduleNamespace = getModuleNameFromOutputFile(outputFile, functionsDir);
|
|
3166
|
+
const aggregateModuleNamespace = getModuleNameFromOutputFile(getGeneratedAggregateOutputFile(functionsDir), functionsDir);
|
|
3083
3167
|
return `// biome-ignore-all format: generated
|
|
3084
3168
|
// This file is auto-generated by kitcn
|
|
3085
3169
|
// Do not edit manually. Run \`kitcn codegen\` to regenerate.
|
|
@@ -3089,7 +3173,7 @@ import {
|
|
|
3089
3173
|
type GenericOrmCtx,
|
|
3090
3174
|
type OrmFunctions,
|
|
3091
3175
|
} from 'kitcn/orm';
|
|
3092
|
-
import {
|
|
3176
|
+
${capabilityImportLines}import {
|
|
3093
3177
|
createGeneratedFunctionReference,
|
|
3094
3178
|
initCRPC as baseInitCRPC,
|
|
3095
3179
|
registerProcedureNameLookup,
|
|
@@ -3107,7 +3191,7 @@ ${migrationsImportLine}
|
|
|
3107
3191
|
${`const ormFunctions: OrmFunctions = {
|
|
3108
3192
|
scheduledMutationBatch: createGeneratedFunctionReference<"mutation", "internal", unknown>(${JSON.stringify(`${moduleNamespace}:scheduledMutationBatch`)}),
|
|
3109
3193
|
scheduledDelete: createGeneratedFunctionReference<"mutation", "internal", unknown>(${JSON.stringify(`${moduleNamespace}:scheduledDelete`)}),
|
|
3110
|
-
aggregateBackfillChunk: createGeneratedFunctionReference<"mutation", "internal", unknown>(${JSON.stringify(`${
|
|
3194
|
+
aggregateBackfillChunk: createGeneratedFunctionReference<"mutation", "internal", unknown>(${JSON.stringify(`${aggregateModuleNamespace}:aggregateBackfillChunk`)}),
|
|
3111
3195
|
migrationRunChunk: createGeneratedFunctionReference<"mutation", "internal", unknown>(${JSON.stringify(`${moduleNamespace}:migrationRunChunk`)}),
|
|
3112
3196
|
resetChunk: createGeneratedFunctionReference<"mutation", "internal", unknown>(${JSON.stringify(`${moduleNamespace}:resetChunk`)}),
|
|
3113
3197
|
};`}
|
|
@@ -3121,7 +3205,7 @@ registerProcedureNameLookup(
|
|
|
3121
3205
|
export const orm = createOrm({
|
|
3122
3206
|
schema: ormSchema,
|
|
3123
3207
|
ormFunctions,
|
|
3124
|
-
${migrationsConfigLine} internalMutation,
|
|
3208
|
+
${capabilitiesConfigLine}${migrationsConfigLine} internalMutation,
|
|
3125
3209
|
});
|
|
3126
3210
|
|
|
3127
3211
|
export type OrmCtx<Ctx extends ServerQueryCtx | ServerMutationCtx = ServerQueryCtx> = GenericOrmCtx<Ctx, typeof ormSchema>;
|
|
@@ -3144,9 +3228,6 @@ export { httpAction, internalMutation };
|
|
|
3144
3228
|
export const {
|
|
3145
3229
|
scheduledMutationBatch,
|
|
3146
3230
|
scheduledDelete,
|
|
3147
|
-
aggregateBackfill,
|
|
3148
|
-
aggregateBackfillChunk,
|
|
3149
|
-
aggregateBackfillStatus,
|
|
3150
3231
|
migrationRun,
|
|
3151
3232
|
migrationRunChunk,
|
|
3152
3233
|
migrationStatus,
|
|
@@ -3156,6 +3237,41 @@ export const {
|
|
|
3156
3237
|
} = orm.api();
|
|
3157
3238
|
`;
|
|
3158
3239
|
}
|
|
3240
|
+
function emitGeneratedAggregateFile(outputFile, functionsDir) {
|
|
3241
|
+
const schemaImportLiteral = JSON.stringify(getSchemaImportPath(outputFile, functionsDir));
|
|
3242
|
+
const serverTypesImportLiteral = JSON.stringify(getServerTypesImportPath(outputFile, functionsDir));
|
|
3243
|
+
const moduleNamespace = getModuleNameFromOutputFile(outputFile, functionsDir);
|
|
3244
|
+
const serverModuleNamespace = getModuleNameFromOutputFile(getGeneratedServerOutputFile(functionsDir), functionsDir);
|
|
3245
|
+
return `// biome-ignore-all format: generated
|
|
3246
|
+
// This file is auto-generated by kitcn
|
|
3247
|
+
// Do not edit manually. Run \`kitcn codegen\` to regenerate.
|
|
3248
|
+
|
|
3249
|
+
import { createOrm, type OrmFunctions } from 'kitcn/orm';
|
|
3250
|
+
import { aggregateCapability } from 'kitcn/orm/aggregate-index';
|
|
3251
|
+
import { createGeneratedFunctionReference } from 'kitcn/server';
|
|
3252
|
+
import { internalMutation } from ${serverTypesImportLiteral};
|
|
3253
|
+
import schema from ${schemaImportLiteral};
|
|
3254
|
+
|
|
3255
|
+
const ormFunctions: OrmFunctions = {
|
|
3256
|
+
scheduledMutationBatch: createGeneratedFunctionReference<"mutation", "internal", unknown>(${JSON.stringify(`${serverModuleNamespace}:scheduledMutationBatch`)}),
|
|
3257
|
+
scheduledDelete: createGeneratedFunctionReference<"mutation", "internal", unknown>(${JSON.stringify(`${serverModuleNamespace}:scheduledDelete`)}),
|
|
3258
|
+
aggregateBackfillChunk: createGeneratedFunctionReference<"mutation", "internal", unknown>(${JSON.stringify(`${moduleNamespace}:aggregateBackfillChunk`)}),
|
|
3259
|
+
};
|
|
3260
|
+
|
|
3261
|
+
const orm = createOrm({
|
|
3262
|
+
schema,
|
|
3263
|
+
ormFunctions,
|
|
3264
|
+
capabilities: [aggregateCapability()],
|
|
3265
|
+
internalMutation,
|
|
3266
|
+
});
|
|
3267
|
+
|
|
3268
|
+
export const {
|
|
3269
|
+
aggregateBackfill,
|
|
3270
|
+
aggregateBackfillChunk,
|
|
3271
|
+
aggregateBackfillStatus,
|
|
3272
|
+
} = orm.api();
|
|
3273
|
+
`;
|
|
3274
|
+
}
|
|
3159
3275
|
function emitGeneratedAuthFile(outputFile, functionsDir, hasOrmSchema, authContract) {
|
|
3160
3276
|
const asSingleQuotedImport = (importPath) => `'${importPath.replaceAll("'", "\\'")}'`;
|
|
3161
3277
|
const runtimeApiImportPath = getRuntimeApiImportPath(outputFile, functionsDir);
|
|
@@ -3521,6 +3637,13 @@ function buildGeneratedOrmRuntimeProcedureEntries(moduleName) {
|
|
|
3521
3637
|
kind: "dispatch"
|
|
3522
3638
|
}));
|
|
3523
3639
|
}
|
|
3640
|
+
function buildGeneratedAggregateRuntimeProcedureEntries(moduleName) {
|
|
3641
|
+
return GENERATED_AGGREGATE_RUNTIME_PROCEDURES.map((entry) => ({
|
|
3642
|
+
...entry,
|
|
3643
|
+
moduleName,
|
|
3644
|
+
kind: "dispatch"
|
|
3645
|
+
}));
|
|
3646
|
+
}
|
|
3524
3647
|
function partitionRuntimeEntriesForEmission(entries) {
|
|
3525
3648
|
return {
|
|
3526
3649
|
callerEntries: entries,
|
|
@@ -3616,6 +3739,7 @@ async function generateMeta(sharedDir, options) {
|
|
|
3616
3739
|
const startTime = Date.now();
|
|
3617
3740
|
const { functionsDir, outputFile } = getConvexConfig(sharedDir);
|
|
3618
3741
|
const serverOutputFile = getGeneratedServerOutputFile(functionsDir);
|
|
3742
|
+
const aggregateOutputFile = getGeneratedAggregateOutputFile(functionsDir);
|
|
3619
3743
|
const ormOutputFile = getGeneratedOrmOutputFile(functionsDir);
|
|
3620
3744
|
const crpcOutputFile = getGeneratedCrpcOutputFile(functionsDir);
|
|
3621
3745
|
const authOutputFile = getGeneratedAuthOutputFile(functionsDir);
|
|
@@ -3670,6 +3794,7 @@ async function generateMeta(sharedDir, options) {
|
|
|
3670
3794
|
const runtimePlaceholderModules = [...new Set([
|
|
3671
3795
|
...files.map((file) => file.replace(TS_EXTENSION_RE, "")),
|
|
3672
3796
|
...hasOrmSchema ? ["generated/server"] : [],
|
|
3797
|
+
...hasOrmSchema ? ["generated/aggregate"] : [],
|
|
3673
3798
|
...generateAuth ? [generatedAuthModuleName] : []
|
|
3674
3799
|
])];
|
|
3675
3800
|
createdRuntimePlaceholders = ensureGeneratedRuntimePlaceholders(functionsDir, runtimePlaceholderModules, resolveModuleRuntimeExportNames(runtimePlaceholderModules, normalizedTrimSegments));
|
|
@@ -3780,10 +3905,12 @@ ${optionalTypeExports}
|
|
|
3780
3905
|
if (!fs.existsSync(outputDirname)) fs.mkdirSync(outputDirname, { recursive: true });
|
|
3781
3906
|
writeFileIfChanged(outputFile, output);
|
|
3782
3907
|
} else fs.rmSync(outputFile, { force: true });
|
|
3783
|
-
const serverOutput = emitGeneratedServerFile(serverOutputFile, functionsDir, hasOrmSchema, hasMigrationsManifest, procedureNameLookup);
|
|
3908
|
+
const serverOutput = emitGeneratedServerFile(serverOutputFile, functionsDir, hasOrmSchema, schemaMetadata.hasAggregateIndexes, hasMigrationsManifest, procedureNameLookup);
|
|
3784
3909
|
const generatedOutputDirname = path.dirname(serverOutputFile);
|
|
3785
3910
|
if (!fs.existsSync(generatedOutputDirname)) fs.mkdirSync(generatedOutputDirname, { recursive: true });
|
|
3786
3911
|
writeFileIfChanged(serverOutputFile, serverOutput);
|
|
3912
|
+
if (hasOrmSchema) writeFileIfChanged(aggregateOutputFile, emitGeneratedAggregateFile(aggregateOutputFile, functionsDir));
|
|
3913
|
+
else fs.rmSync(aggregateOutputFile, { force: true });
|
|
3787
3914
|
fs.rmSync(ormOutputFile, { force: true });
|
|
3788
3915
|
fs.rmSync(crpcOutputFile, { force: true });
|
|
3789
3916
|
writeFileIfChanged(migrationsHelperOutputFile, emitGeneratedMigrationsFile(migrationsHelperOutputFile, functionsDir, hasRelationsMetadata));
|
|
@@ -3795,6 +3922,7 @@ ${optionalTypeExports}
|
|
|
3795
3922
|
fs.rmSync(getLegacyGeneratedOutputFile(functionsDir), { force: true });
|
|
3796
3923
|
const mergedProcedureEntries = dedupeProcedureEntries([
|
|
3797
3924
|
...hasOrmSchema ? buildGeneratedOrmRuntimeProcedureEntries("generated/server") : [],
|
|
3925
|
+
...hasOrmSchema ? buildGeneratedAggregateRuntimeProcedureEntries("generated/aggregate") : [],
|
|
3798
3926
|
...generateApi ? procedureEntries : [],
|
|
3799
3927
|
...generateAuth && hasAuthDefaultExport ? buildAuthRuntimeProcedureEntries(generatedAuthModuleName) : []
|
|
3800
3928
|
]);
|
|
@@ -3838,6 +3966,8 @@ ${optionalTypeExports}
|
|
|
3838
3966
|
if (generateApi) logger.success(`\nGenerated ${outputFile}`);
|
|
3839
3967
|
else logger.info(`\nRemoved ${outputFile}`);
|
|
3840
3968
|
logger.success(`Generated ${serverOutputFile}`);
|
|
3969
|
+
if (hasOrmSchema) logger.success(`Generated ${aggregateOutputFile}`);
|
|
3970
|
+
else logger.info(`Removed ${aggregateOutputFile}`);
|
|
3841
3971
|
logger.success(`Generated ${migrationsHelperOutputFile}`);
|
|
3842
3972
|
if (generateAuth) logger.success(`Generated ${authOutputFile}`);
|
|
3843
3973
|
else logger.info(`Removed ${authOutputFile}`);
|
|
@@ -4220,4 +4350,4 @@ async function withLocalCodegenEnv(sharedDir, backend, fn) {
|
|
|
4220
4350
|
}
|
|
4221
4351
|
|
|
4222
4352
|
//#endregion
|
|
4223
|
-
export {
|
|
4353
|
+
export { Columns as C, TableName as D, RlsPolicies as E, createSystemFields as O, getRankIndexes as S, OrmSchemaExtensions as T, loadEsbuild as _, generateMeta as a, getAggregateIndexes as b, resolveSchemaDefaultExport as c, PARSE_SNAPSHOT_SUFFIX as d, createProjectJiti as f, loadDotenv as g, loadClackPrompts as h, resolveConfiguredBackend as i, schemaDeclaresAggregateIndexes as l, loadBabelParser as m, withLocalCodegenEnv as n, getConvexConfig as o, CRPC_BUILDER_STUB_SOURCE as p, loadCliConfig as r, collectSchemaTables as s, getLocalBackendEnvVars as t, logger as u, highlighter as v, EnableRLS as w, getIndexes as x, isColorEnabled as y };
|