kitcn 0.0.1 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/bin/intent.js +3 -0
  2. package/dist/aggregate/index.d.ts +388 -0
  3. package/dist/aggregate/index.js +37 -0
  4. package/dist/api-entry-BckXqaLb.js +66 -0
  5. package/dist/auth/client/index.d.ts +37 -0
  6. package/dist/auth/client/index.js +217 -0
  7. package/dist/auth/config/index.d.ts +45 -0
  8. package/dist/auth/config/index.js +24 -0
  9. package/dist/auth/generated/index.d.ts +2 -0
  10. package/dist/auth/generated/index.js +3 -0
  11. package/dist/auth/http/index.d.ts +64 -0
  12. package/dist/auth/http/index.js +461 -0
  13. package/dist/auth/index.d.ts +221 -0
  14. package/dist/auth/index.js +1398 -0
  15. package/dist/auth/nextjs/index.d.ts +50 -0
  16. package/dist/auth/nextjs/index.js +81 -0
  17. package/dist/auth-store-Cljlmdmi.js +197 -0
  18. package/dist/builder-CBdG5W6A.js +1974 -0
  19. package/dist/caller-factory-cTXNvYdz.js +216 -0
  20. package/dist/cli.mjs +13264 -0
  21. package/dist/codegen-lF80HSWu.mjs +3416 -0
  22. package/dist/context-utils-HPC5nXzx.d.ts +17 -0
  23. package/dist/create-schema-odyF4kCy.js +156 -0
  24. package/dist/create-schema-orm-DOyiNDCx.js +246 -0
  25. package/dist/crpc/index.d.ts +105 -0
  26. package/dist/crpc/index.js +169 -0
  27. package/dist/customFunctions-C0voKmtx.js +144 -0
  28. package/dist/error-BZEnI7Sq.js +41 -0
  29. package/dist/generated-contract-disabled-Cih4eITO.js +50 -0
  30. package/dist/generated-contract-disabled-D-sOFy92.d.ts +354 -0
  31. package/dist/http-types-DqJubRPJ.d.ts +292 -0
  32. package/dist/meta-utils-0Pu0Nrap.js +117 -0
  33. package/dist/middleware-BUybuv9n.d.ts +34 -0
  34. package/dist/middleware-C2qTZ3V7.js +84 -0
  35. package/dist/orm/index.d.ts +17 -0
  36. package/dist/orm/index.js +10713 -0
  37. package/dist/plugins/index.d.ts +2 -0
  38. package/dist/plugins/index.js +3 -0
  39. package/dist/procedure-caller-DtxLmGwA.d.ts +1467 -0
  40. package/dist/procedure-caller-MWcxhQDv.js +349 -0
  41. package/dist/query-context-B8o6-8kC.js +1518 -0
  42. package/dist/query-context-CFZqIvD7.d.ts +42 -0
  43. package/dist/query-options-Dw7cOyXl.js +121 -0
  44. package/dist/ratelimit/index.d.ts +269 -0
  45. package/dist/ratelimit/index.js +856 -0
  46. package/dist/ratelimit/react/index.d.ts +76 -0
  47. package/dist/ratelimit/react/index.js +183 -0
  48. package/dist/react/index.d.ts +1284 -0
  49. package/dist/react/index.js +2526 -0
  50. package/dist/rsc/index.d.ts +276 -0
  51. package/dist/rsc/index.js +233 -0
  52. package/dist/runtime-CtvJPkur.js +2453 -0
  53. package/dist/server/index.d.ts +5 -0
  54. package/dist/server/index.js +6 -0
  55. package/dist/solid/index.d.ts +1221 -0
  56. package/dist/solid/index.js +2940 -0
  57. package/dist/transformer-DtDhR3Lc.js +194 -0
  58. package/dist/types-BTb_4BaU.d.ts +42 -0
  59. package/dist/types-BiJE7qxR.d.ts +4 -0
  60. package/dist/types-DEJpkIhw.d.ts +88 -0
  61. package/dist/types-HhO_R6pd.d.ts +213 -0
  62. package/dist/validators-B7oIJCAp.js +279 -0
  63. package/dist/validators-vzRKjBJC.d.ts +88 -0
  64. package/dist/watcher.mjs +96 -0
  65. package/dist/where-clause-compiler-DdjN63Io.d.ts +4756 -0
  66. package/package.json +107 -34
  67. package/skills/convex/SKILL.md +486 -0
  68. package/skills/convex/references/features/aggregates.md +353 -0
  69. package/skills/convex/references/features/auth-admin.md +446 -0
  70. package/skills/convex/references/features/auth-organizations.md +1141 -0
  71. package/skills/convex/references/features/auth-polar.md +579 -0
  72. package/skills/convex/references/features/auth.md +470 -0
  73. package/skills/convex/references/features/create-plugins.md +153 -0
  74. package/skills/convex/references/features/http.md +676 -0
  75. package/skills/convex/references/features/migrations.md +162 -0
  76. package/skills/convex/references/features/orm.md +1166 -0
  77. package/skills/convex/references/features/react.md +657 -0
  78. package/skills/convex/references/features/scheduling.md +267 -0
  79. package/skills/convex/references/features/testing.md +209 -0
  80. package/skills/convex/references/setup/auth.md +501 -0
  81. package/skills/convex/references/setup/biome.md +190 -0
  82. package/skills/convex/references/setup/doc-guidelines.md +145 -0
  83. package/skills/convex/references/setup/index.md +761 -0
  84. package/skills/convex/references/setup/next.md +116 -0
  85. package/skills/convex/references/setup/react.md +175 -0
  86. package/skills/convex/references/setup/server.md +473 -0
  87. package/skills/convex/references/setup/start.md +67 -0
  88. package/LICENSE +0 -21
  89. package/README.md +0 -0
  90. package/dist/index.d.mts +0 -5
  91. package/dist/index.d.mts.map +0 -1
  92. package/dist/index.mjs +0 -6
  93. package/dist/index.mjs.map +0 -1
@@ -0,0 +1,2453 @@
1
+ import { o as vRequired } from "./validators-B7oIJCAp.js";
2
+ import { ConvexError, convexToJson, jsonToConvex, v } from "convex/values";
3
+
4
+ //#region src/aggregate-core/compare.ts
5
+ function compareValues$1(k1, k2) {
6
+ return compareAsTuples(makeComparable(k1), makeComparable(k2));
7
+ }
8
+ function compareAsTuples(a, b) {
9
+ if (a[0] === b[0]) return compareSameTypeValues(a[1], b[1]);
10
+ if (a[0] < b[0]) return -1;
11
+ return 1;
12
+ }
13
+ function compareSameTypeValues(v1, v2) {
14
+ if (v1 === void 0 || v1 === null) return 0;
15
+ if (typeof v1 === "bigint" || typeof v1 === "number" || typeof v1 === "boolean" || typeof v1 === "string") return v1 < v2 ? -1 : v1 === v2 ? 0 : 1;
16
+ if (!Array.isArray(v1) || !Array.isArray(v2)) throw new Error(`Unexpected type ${v1}`);
17
+ for (let i = 0; i < v1.length && i < v2.length; i++) {
18
+ const cmp = compareAsTuples(v1[i], v2[i]);
19
+ if (cmp !== 0) return cmp;
20
+ }
21
+ if (v1.length < v2.length) return -1;
22
+ if (v1.length > v2.length) return 1;
23
+ return 0;
24
+ }
25
+ function makeComparable(v) {
26
+ if (v === void 0) return [0, void 0];
27
+ if (v === null) return [1, null];
28
+ if (typeof v === "bigint") return [2, v];
29
+ if (typeof v === "number") {
30
+ if (Number.isNaN(v)) return [3.5, 0];
31
+ return [3, v];
32
+ }
33
+ if (typeof v === "boolean") return [4, v];
34
+ if (typeof v === "string") return [5, v];
35
+ if (v instanceof ArrayBuffer) return [6, Array.from(new Uint8Array(v)).map(makeComparable)];
36
+ if (Array.isArray(v)) return [7, v.map(makeComparable)];
37
+ return [8, Object.keys(v).sort().map((k) => [k, v[k]]).map(makeComparable)];
38
+ }
39
+
40
+ //#endregion
41
+ //#region src/orm/builders/column-builder.ts
42
+ /**
43
+ * entityKind symbol for runtime type checking
44
+ * Following Drizzle's pattern for type guards
45
+ */
46
+ const entityKind = Symbol.for("kitcn:entityKind");
47
+ /**
48
+ * Base ColumnBuilder abstract class
49
+ *
50
+ * All column builders inherit from this class.
51
+ * Implements chaining methods and stores runtime config.
52
+ */
53
+ var ColumnBuilder = class {
54
+ static [entityKind] = "ColumnBuilder";
55
+ [entityKind] = "ColumnBuilder";
56
+ /**
57
+ * Runtime configuration - actual mutable state
58
+ */
59
+ config;
60
+ constructor(name, dataType, columnType) {
61
+ this.config = {
62
+ name,
63
+ notNull: false,
64
+ default: void 0,
65
+ hasDefault: false,
66
+ primaryKey: false,
67
+ isUnique: false,
68
+ uniqueName: void 0,
69
+ uniqueNulls: void 0,
70
+ foreignKeyConfigs: [],
71
+ dataType,
72
+ columnType
73
+ };
74
+ }
75
+ /**
76
+ * Mark column as NOT NULL
77
+ * Returns type-branded instance with notNull: true
78
+ */
79
+ notNull() {
80
+ this.config.notNull = true;
81
+ return this;
82
+ }
83
+ /**
84
+ * Override the TypeScript type for this column.
85
+ * Mirrors Drizzle's $type() (type-only, no runtime validation changes).
86
+ */
87
+ $type() {
88
+ return this;
89
+ }
90
+ /**
91
+ * Set default value for column
92
+ * Makes field optional on insert
93
+ */
94
+ default(value) {
95
+ this.config.default = value;
96
+ this.config.hasDefault = true;
97
+ return this;
98
+ }
99
+ /**
100
+ * Set default function for column (runtime evaluated on insert).
101
+ * Mirrors Drizzle's $defaultFn() / $default().
102
+ */
103
+ $defaultFn(fn) {
104
+ this.config.defaultFn = fn;
105
+ return this;
106
+ }
107
+ /**
108
+ * Alias of $defaultFn for Drizzle parity.
109
+ */
110
+ $default(fn) {
111
+ return this.$defaultFn(fn);
112
+ }
113
+ /**
114
+ * Set on-update function for column (runtime evaluated on update).
115
+ * Mirrors Drizzle's $onUpdateFn() / $onUpdate().
116
+ */
117
+ $onUpdateFn(fn) {
118
+ this.config.onUpdateFn = fn;
119
+ return this;
120
+ }
121
+ /**
122
+ * Alias of $onUpdateFn for Drizzle parity.
123
+ */
124
+ $onUpdate(fn) {
125
+ return this.$onUpdateFn(fn);
126
+ }
127
+ /**
128
+ * Mark column as primary key
129
+ * Implies NOT NULL
130
+ */
131
+ primaryKey() {
132
+ this.config.primaryKey = true;
133
+ this.config.notNull = true;
134
+ return this;
135
+ }
136
+ /**
137
+ * Mark column as UNIQUE
138
+ * Mirrors Drizzle column unique API
139
+ */
140
+ unique(name, config) {
141
+ this.config.isUnique = true;
142
+ this.config.uniqueName = name;
143
+ this.config.uniqueNulls = config?.nulls;
144
+ return this;
145
+ }
146
+ /**
147
+ * Define a foreign key reference
148
+ * Mirrors Drizzle column references() API
149
+ */
150
+ references(ref, config = {}) {
151
+ this.config.foreignKeyConfigs.push({
152
+ ref,
153
+ config
154
+ });
155
+ return this;
156
+ }
157
+ };
158
+
159
+ //#endregion
160
+ //#region src/orm/builders/convex-column-builder.ts
161
+ /**
162
+ * Convex-specific column builder base class
163
+ *
164
+ * All Convex column builders (ConvexTextBuilder, ConvexIntegerBuilder, etc.)
165
+ * inherit from this class.
166
+ */
167
+ var ConvexColumnBuilder = class extends ColumnBuilder {
168
+ static [entityKind] = "ConvexColumnBuilder";
169
+ };
170
+
171
+ //#endregion
172
+ //#region src/orm/builders/custom.ts
173
+ function isRecord$1(value) {
174
+ return typeof value === "object" && value !== null && !Array.isArray(value);
175
+ }
176
+ function isValidator(value) {
177
+ return isRecord$1(value) && typeof value.kind === "string" && typeof value.isOptional === "string";
178
+ }
179
+ function isColumnBuilder$1(value) {
180
+ return isRecord$1(value) && value[entityKind] === "ColumnBuilder";
181
+ }
182
+ function toRequiredValidator(validator) {
183
+ return validator.isOptional === "optional" ? vRequired(validator) : validator;
184
+ }
185
+ function toRequiredBuilderValidator(validator) {
186
+ const requiredValidator = toRequiredValidator(validator);
187
+ if (requiredValidator.kind !== "union") return requiredValidator;
188
+ const nonNullMembers = requiredValidator.members.filter((member) => member.kind !== "null");
189
+ if (nonNullMembers.length !== 1) return requiredValidator;
190
+ const [member] = nonNullMembers;
191
+ if (member.kind === "object" || member.kind === "array") return member;
192
+ return requiredValidator;
193
+ }
194
+ function formatInvalidInput(path, value) {
195
+ return `${path} expected a column builder, Convex validator, or nested object shape. Got ${Array.isArray(value) ? "array" : value === null ? "null" : typeof value}.`;
196
+ }
197
+ function objectShapeToValidator(shape, path) {
198
+ const fields = {};
199
+ for (const [key, value] of Object.entries(shape)) fields[key] = nestedInputToValidator(value, `${path}.${key}`);
200
+ return v.object(fields);
201
+ }
202
+ function nestedInputToValidator(input, path) {
203
+ if (isColumnBuilder$1(input)) return toRequiredBuilderValidator(input.convexValidator);
204
+ if (isValidator(input)) return toRequiredValidator(input);
205
+ if (isRecord$1(input)) return objectShapeToValidator(input, path);
206
+ throw new Error(formatInvalidInput(path, input));
207
+ }
208
+ var ConvexCustomBuilder = class extends ConvexColumnBuilder {
209
+ static [entityKind] = "ConvexCustomBuilder";
210
+ constructor(name, validator) {
211
+ super(name, "any", "ConvexCustom");
212
+ this.config.validator = validator;
213
+ }
214
+ get convexValidator() {
215
+ const validator = this.config.validator;
216
+ if (this.config.notNull) return validator;
217
+ return v.optional(v.union(v.null(), validator));
218
+ }
219
+ build() {
220
+ return this.convexValidator;
221
+ }
222
+ };
223
+ function custom(a, b) {
224
+ if (b !== void 0) return new ConvexCustomBuilder(a, b);
225
+ return new ConvexCustomBuilder("", a);
226
+ }
227
+ /**
228
+ * Creates an array column from a nested validator or builder.
229
+ *
230
+ * Values in nested arrays are always compiled as required validators.
231
+ */
232
+ function arrayOf(element) {
233
+ return custom(v.array(nestedInputToValidator(element, "arrayOf(element)"))).$type();
234
+ }
235
+ /**
236
+ * Creates a union column from validators/builders without dropping to `v.union(...)`.
237
+ */
238
+ function unionOf(...members) {
239
+ const validators = members.map((member, index) => nestedInputToValidator(member, `unionOf(members[${index}])`));
240
+ return custom(v.union(...validators)).$type();
241
+ }
242
+ /**
243
+ * Creates an object column from either:
244
+ * - a nested shape of validators/builders, or
245
+ * - a validator/builder describing homogeneous record values
246
+ *
247
+ * Fields in nested objects are always compiled as required validators.
248
+ */
249
+ function objectOf(input) {
250
+ if (isColumnBuilder$1(input) || isValidator(input)) return custom(v.record(v.string(), nestedInputToValidator(input, "objectOf(value)"))).$type();
251
+ if (!isRecord$1(input)) throw new Error(formatInvalidInput("objectOf(shape)", input));
252
+ return custom(objectShapeToValidator(input, "objectOf(shape)")).$type();
253
+ }
254
+ /**
255
+ * Convenience wrapper for Convex "JSON" values.
256
+ *
257
+ * Note: This is Convex JSON (runtime `v.any()`), not SQL JSON/JSONB.
258
+ */
259
+ function json() {
260
+ return custom(v.any()).$type();
261
+ }
262
+
263
+ //#endregion
264
+ //#region src/orm/builders/id.ts
265
+ /**
266
+ * ID column builder class
267
+ * Compiles to v.id(tableName) or v.optional(v.id(tableName))
268
+ */
269
+ var ConvexIdBuilder = class extends ConvexColumnBuilder {
270
+ static [entityKind] = "ConvexIdBuilder";
271
+ constructor(name, tableName) {
272
+ super(name, "string", "ConvexId");
273
+ this.tableName = tableName;
274
+ this.config.referenceTable = tableName;
275
+ }
276
+ /**
277
+ * Expose Convex validator for schema integration
278
+ */
279
+ get convexValidator() {
280
+ if (this.config.notNull) return v.id(this.tableName);
281
+ return v.optional(v.union(v.null(), v.id(this.tableName)));
282
+ }
283
+ /**
284
+ * Compile to Convex validator
285
+ * .notNull() → v.id(tableName)
286
+ * nullable → v.optional(v.id(tableName))
287
+ */
288
+ build() {
289
+ return this.convexValidator;
290
+ }
291
+ };
292
+ function id(tableName) {
293
+ return new ConvexIdBuilder("", tableName);
294
+ }
295
+
296
+ //#endregion
297
+ //#region src/orm/builders/number.ts
298
+ /**
299
+ * Number column builder class
300
+ * Compiles to v.number() or v.optional(v.number())
301
+ */
302
+ var ConvexNumberBuilder = class extends ConvexColumnBuilder {
303
+ static [entityKind] = "ConvexNumberBuilder";
304
+ constructor(name) {
305
+ super(name, "number", "ConvexNumber");
306
+ }
307
+ /**
308
+ * Expose Convex validator for schema integration
309
+ */
310
+ get convexValidator() {
311
+ if (this.config.notNull) return v.number();
312
+ return v.optional(v.union(v.null(), v.number()));
313
+ }
314
+ /**
315
+ * Compile to Convex validator
316
+ * .notNull() → v.number()
317
+ * nullable → v.optional(v.number())
318
+ */
319
+ build() {
320
+ return this.convexValidator;
321
+ }
322
+ };
323
+ function integer(name) {
324
+ return new ConvexNumberBuilder(name ?? "");
325
+ }
326
+
327
+ //#endregion
328
+ //#region src/orm/builders/system-fields.ts
329
+ /**
330
+ * System Fields - Convex-provided fields available on all documents
331
+ *
332
+ * id: Document ID (string, backed by internal Convex _id)
333
+ * createdAt: Creation timestamp alias (backed by internal Convex _creationTime)
334
+ *
335
+ * These are automatically added to every Convex table.
336
+ */
337
+ var ConvexSystemIdBuilder = class extends ColumnBuilder {
338
+ static [entityKind] = "ConvexSystemIdBuilder";
339
+ [entityKind] = "ConvexSystemIdBuilder";
340
+ constructor() {
341
+ super("_id", "string", "ConvexSystemId");
342
+ this.config.notNull = true;
343
+ }
344
+ build() {
345
+ return v.string();
346
+ }
347
+ /**
348
+ * Convex validator - runtime access
349
+ * System fields use v.string() for _id
350
+ */
351
+ get convexValidator() {
352
+ return this.build();
353
+ }
354
+ };
355
+ var ConvexSystemCreationTimeBuilder = class extends ColumnBuilder {
356
+ static [entityKind] = "ConvexSystemCreationTimeBuilder";
357
+ [entityKind] = "ConvexSystemCreationTimeBuilder";
358
+ constructor() {
359
+ super("_creationTime", "number", "ConvexSystemCreationTime");
360
+ this.config.notNull = true;
361
+ }
362
+ build() {
363
+ return v.number();
364
+ }
365
+ /**
366
+ * Convex validator - runtime access
367
+ * System fields use v.number() for _creationTime
368
+ */
369
+ get convexValidator() {
370
+ return this.build();
371
+ }
372
+ };
373
+ var ConvexSystemCreatedAtBuilder = class extends ColumnBuilder {
374
+ static [entityKind] = "ConvexSystemCreatedAtBuilder";
375
+ [entityKind] = "ConvexSystemCreatedAtBuilder";
376
+ constructor() {
377
+ super("_creationTime", "number", "ConvexSystemCreatedAt");
378
+ this.config.notNull = true;
379
+ }
380
+ build() {
381
+ return v.number();
382
+ }
383
+ get convexValidator() {
384
+ return this.build();
385
+ }
386
+ };
387
+ function createSystemFields(tableName) {
388
+ const id = new ConvexSystemIdBuilder();
389
+ const creationTime = new ConvexSystemCreationTimeBuilder();
390
+ const createdAt = new ConvexSystemCreatedAtBuilder();
391
+ id.config.tableName = tableName;
392
+ creationTime.config.tableName = tableName;
393
+ createdAt.config.tableName = tableName;
394
+ return {
395
+ id,
396
+ _creationTime: creationTime,
397
+ createdAt
398
+ };
399
+ }
400
+
401
+ //#endregion
402
+ //#region src/orm/builders/text.ts
403
+ /**
404
+ * Text column builder class
405
+ * Compiles to v.string() or v.optional(v.string())
406
+ */
407
+ var ConvexTextBuilder = class extends ConvexColumnBuilder {
408
+ static [entityKind] = "ConvexTextBuilder";
409
+ constructor(name) {
410
+ super(name, "string", "ConvexText");
411
+ }
412
+ /**
413
+ * Expose Convex validator for schema integration
414
+ */
415
+ get convexValidator() {
416
+ if (this.config.notNull) return v.string();
417
+ return v.optional(v.union(v.null(), v.string()));
418
+ }
419
+ /**
420
+ * Compile to Convex validator
421
+ * .notNull() → v.string()
422
+ * nullable → v.optional(v.string())
423
+ */
424
+ build() {
425
+ return this.convexValidator;
426
+ }
427
+ };
428
+ function text(name) {
429
+ return new ConvexTextBuilder(name ?? "");
430
+ }
431
+
432
+ //#endregion
433
+ //#region src/orm/indexes.ts
434
+ var ConvexIndexBuilderOn = class {
435
+ static [entityKind] = "ConvexIndexBuilderOn";
436
+ [entityKind] = "ConvexIndexBuilderOn";
437
+ constructor(name, unique) {
438
+ this.name = name;
439
+ this.unique = unique;
440
+ }
441
+ on(...columns) {
442
+ return new ConvexIndexBuilder(this.name, columns, this.unique);
443
+ }
444
+ };
445
+ var ConvexIndexBuilder = class {
446
+ static [entityKind] = "ConvexIndexBuilder";
447
+ [entityKind] = "ConvexIndexBuilder";
448
+ config;
449
+ constructor(name, columns, unique) {
450
+ this.config = {
451
+ name,
452
+ columns,
453
+ unique,
454
+ where: void 0
455
+ };
456
+ }
457
+ /**
458
+ * Partial index conditions are not supported in Convex.
459
+ * This method is kept for Drizzle API parity.
460
+ */
461
+ where(condition) {
462
+ this.config.where = condition;
463
+ return this;
464
+ }
465
+ };
466
+ var ConvexSearchIndexBuilderOn = class {
467
+ static [entityKind] = "ConvexSearchIndexBuilderOn";
468
+ [entityKind] = "ConvexSearchIndexBuilderOn";
469
+ constructor(name) {
470
+ this.name = name;
471
+ }
472
+ on(searchField) {
473
+ return new ConvexSearchIndexBuilder(this.name, searchField);
474
+ }
475
+ };
476
+ var ConvexSearchIndexBuilder = class {
477
+ static [entityKind] = "ConvexSearchIndexBuilder";
478
+ [entityKind] = "ConvexSearchIndexBuilder";
479
+ config;
480
+ constructor(name, searchField) {
481
+ this.config = {
482
+ name,
483
+ searchField,
484
+ filterFields: [],
485
+ staged: false
486
+ };
487
+ }
488
+ filter(...fields) {
489
+ this.config.filterFields = fields;
490
+ return this;
491
+ }
492
+ staged() {
493
+ this.config.staged = true;
494
+ return this;
495
+ }
496
+ };
497
+ var ConvexVectorIndexBuilderOn = class {
498
+ static [entityKind] = "ConvexVectorIndexBuilderOn";
499
+ [entityKind] = "ConvexVectorIndexBuilderOn";
500
+ constructor(name) {
501
+ this.name = name;
502
+ }
503
+ on(vectorField) {
504
+ return new ConvexVectorIndexBuilder(this.name, vectorField);
505
+ }
506
+ };
507
+ var ConvexAggregateIndexBuilderOn = class {
508
+ static [entityKind] = "ConvexAggregateIndexBuilderOn";
509
+ [entityKind] = "ConvexAggregateIndexBuilderOn";
510
+ constructor(name) {
511
+ this.name = name;
512
+ }
513
+ on(...columns) {
514
+ return new ConvexAggregateIndexBuilder(this.name, columns);
515
+ }
516
+ all() {
517
+ return new ConvexAggregateIndexBuilder(this.name, []);
518
+ }
519
+ };
520
+ var ConvexAggregateIndexBuilder = class {
521
+ static [entityKind] = "ConvexAggregateIndexBuilder";
522
+ [entityKind] = "ConvexAggregateIndexBuilder";
523
+ config;
524
+ constructor(name, columns) {
525
+ this.config = {
526
+ name,
527
+ columns,
528
+ countFields: [],
529
+ sumFields: [],
530
+ avgFields: [],
531
+ minFields: [],
532
+ maxFields: []
533
+ };
534
+ }
535
+ count(...fields) {
536
+ this.config.countFields = [...this.config.countFields, ...fields];
537
+ return this;
538
+ }
539
+ sum(...fields) {
540
+ this.config.sumFields = [...this.config.sumFields, ...fields];
541
+ return this;
542
+ }
543
+ avg(...fields) {
544
+ this.config.avgFields = [...this.config.avgFields, ...fields];
545
+ return this;
546
+ }
547
+ min(...fields) {
548
+ this.config.minFields = [...this.config.minFields, ...fields];
549
+ return this;
550
+ }
551
+ max(...fields) {
552
+ this.config.maxFields = [...this.config.maxFields, ...fields];
553
+ return this;
554
+ }
555
+ };
556
+ var ConvexRankIndexBuilderOn = class {
557
+ static [entityKind] = "ConvexRankIndexBuilderOn";
558
+ [entityKind] = "ConvexRankIndexBuilderOn";
559
+ constructor(name) {
560
+ this.name = name;
561
+ }
562
+ partitionBy(...columns) {
563
+ return new ConvexRankIndexBuilder(this.name, columns, []);
564
+ }
565
+ all() {
566
+ return new ConvexRankIndexBuilder(this.name, [], []);
567
+ }
568
+ };
569
+ var ConvexRankIndexBuilder = class {
570
+ static [entityKind] = "ConvexRankIndexBuilder";
571
+ [entityKind] = "ConvexRankIndexBuilder";
572
+ config;
573
+ constructor(name, partitionColumns, orderColumns) {
574
+ this.config = {
575
+ name,
576
+ partitionColumns,
577
+ orderColumns,
578
+ sumField: void 0
579
+ };
580
+ }
581
+ orderBy(...columns) {
582
+ this.config.orderColumns = columns.map((entry) => {
583
+ if (entry && typeof entry === "object" && "column" in entry && "direction" in entry) {
584
+ const builder = entry.column?.builder;
585
+ if (!builder) throw new Error("rankIndex orderBy() expected a column builder.");
586
+ return {
587
+ column: builder,
588
+ direction: entry.direction
589
+ };
590
+ }
591
+ return {
592
+ column: entry,
593
+ direction: "asc"
594
+ };
595
+ });
596
+ return this;
597
+ }
598
+ sum(field) {
599
+ this.config.sumField = field;
600
+ return this;
601
+ }
602
+ };
603
+ var ConvexVectorIndexBuilder = class {
604
+ static [entityKind] = "ConvexVectorIndexBuilder";
605
+ [entityKind] = "ConvexVectorIndexBuilder";
606
+ config;
607
+ constructor(name, vectorField) {
608
+ this.config = {
609
+ name,
610
+ vectorField,
611
+ dimensions: void 0,
612
+ filterFields: [],
613
+ staged: false
614
+ };
615
+ }
616
+ dimensions(dimensions) {
617
+ if (!Number.isInteger(dimensions)) throw new Error(`Vector index '${this.config.name}' dimensions must be an integer, got ${dimensions}`);
618
+ if (dimensions <= 0) throw new Error(`Vector index '${this.config.name}' dimensions must be positive, got ${dimensions}`);
619
+ if (dimensions > 1e4) console.warn(`Vector index '${this.config.name}' has unusually large dimensions (${dimensions}). Common values: 768, 1536, 3072`);
620
+ this.config.dimensions = dimensions;
621
+ return this;
622
+ }
623
+ filter(...fields) {
624
+ this.config.filterFields = fields;
625
+ return this;
626
+ }
627
+ staged() {
628
+ this.config.staged = true;
629
+ return this;
630
+ }
631
+ };
632
+ function index(name) {
633
+ return new ConvexIndexBuilderOn(name, false);
634
+ }
635
+ function uniqueIndex(name) {
636
+ return new ConvexIndexBuilderOn(name, true);
637
+ }
638
+ function searchIndex(name) {
639
+ return new ConvexSearchIndexBuilderOn(name);
640
+ }
641
+ function vectorIndex(name) {
642
+ return new ConvexVectorIndexBuilderOn(name);
643
+ }
644
+ function aggregateIndex(name) {
645
+ return new ConvexAggregateIndexBuilderOn(name);
646
+ }
647
+ function rankIndex(name) {
648
+ return new ConvexRankIndexBuilderOn(name);
649
+ }
650
+
651
+ //#endregion
652
+ //#region src/orm/rls/policies.ts
653
+ var RlsPolicy = class {
654
+ static [entityKind] = "RlsPolicy";
655
+ [entityKind] = "RlsPolicy";
656
+ as;
657
+ for;
658
+ to;
659
+ using;
660
+ withCheck;
661
+ /** @internal */
662
+ _linkedTable;
663
+ constructor(name, config) {
664
+ this.name = name;
665
+ if (config) {
666
+ this.as = config.as;
667
+ this.for = config.for;
668
+ this.to = config.to;
669
+ this.using = config.using;
670
+ this.withCheck = config.withCheck;
671
+ }
672
+ }
673
+ link(table) {
674
+ this._linkedTable = table;
675
+ return this;
676
+ }
677
+ };
678
+ function rlsPolicy(name, config) {
679
+ return new RlsPolicy(name, config);
680
+ }
681
+ function isRlsPolicy(value) {
682
+ return !!value && typeof value === "object" && value[entityKind] === "RlsPolicy";
683
+ }
684
+
685
+ //#endregion
686
+ //#region src/orm/symbols.ts
687
+ const TableName = Symbol.for("kitcn:TableName");
688
+ const Columns = Symbol.for("kitcn:Columns");
689
+ const Brand = Symbol.for("kitcn:Brand");
690
+ const Relations = Symbol.for("kitcn:Relations");
691
+ const OrmContext = Symbol.for("kitcn:OrmContext");
692
+ const RlsPolicies = Symbol.for("kitcn:RlsPolicies");
693
+ const EnableRLS = Symbol.for("kitcn:EnableRLS");
694
+ const TableDeleteConfig = Symbol.for("kitcn:TableDeleteConfig");
695
+ const TablePolymorphic = Symbol.for("kitcn:TablePolymorphic");
696
+ const OrmSchemaOptions = Symbol.for("kitcn:OrmSchemaOptions");
697
+ const OrmSchemaDefinition = Symbol.for("kitcn:OrmSchemaDefinition");
698
+ const OrmSchemaExtensionTables = Symbol.for("kitcn:OrmSchemaExtensionTables");
699
+ const OrmSchemaExtensions = Symbol.for("kitcn:OrmSchemaExtensions");
700
+ const OrmSchemaExtensionRelations = Symbol.for("kitcn:OrmSchemaExtensionRelations");
701
+ const OrmSchemaExtensionTriggers = Symbol.for("kitcn:OrmSchemaExtensionTriggers");
702
+ const OrmSchemaRelations = Symbol.for("kitcn:OrmSchemaRelations");
703
+ const OrmSchemaTriggers = Symbol.for("kitcn:OrmSchemaTriggers");
704
+
705
+ //#endregion
706
+ //#region src/orm/table.ts
707
+ /**
708
+ * Reserved Convex system table names that cannot be used
709
+ */
710
+ const RESERVED_TABLES = new Set(["_storage", "_scheduled_functions"]);
711
+ const RESERVED_COLUMN_NAMES = new Set([
712
+ "id",
713
+ "_id",
714
+ "_creationTime"
715
+ ]);
716
+ const DEFAULT_POLYMORPHIC_ALIAS = "details";
717
+ const CONVEX_TABLE_FIELD_LIMIT = 1024;
718
+ /**
719
+ * Valid table name pattern: starts with letter/underscore, contains only alphanumeric and underscore
720
+ */
721
+ const TABLE_NAME_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
722
+ /**
723
+ * Validate table name against Convex constraints
724
+ */
725
+ function validateTableName(name) {
726
+ if (RESERVED_TABLES.has(name)) throw new Error(`Table name '${name}' is reserved. System tables cannot be redefined.`);
727
+ if (!TABLE_NAME_REGEX.test(name)) throw new Error(`Invalid table name '${name}'. Must start with letter, contain only alphanumeric and underscore.`);
728
+ }
729
+ /**
730
+ * Create a Convex object validator from column builders
731
+ *
732
+ * Extracts .convexValidator from each column and creates v.object({...})
733
+ * This is the core factory that bridges ORM columns to Convex validators.
734
+ *
735
+ * @param columns - Record of column name to column builder
736
+ * @returns Convex object validator
737
+ */
738
+ function createValidatorFromColumns(columns) {
739
+ const validatorFields = Object.fromEntries(Object.entries(columns).map(([key, builder]) => [key, builder.convexValidator]));
740
+ return v.object(validatorFields);
741
+ }
742
+ function discriminator(config) {
743
+ if (!config || typeof config !== "object") throw new Error("discriminator(...) requires a config object.");
744
+ if (!config.variants || typeof config.variants !== "object" || Object.keys(config.variants).length === 0) throw new Error("discriminator(...).variants must contain at least one case.");
745
+ if (config.as !== void 0 && (typeof config.as !== "string" || config.as.length === 0)) throw new Error("discriminator(...).as must be a non-empty string when set.");
746
+ const builder = text().notNull();
747
+ builder.__polymorphic = {
748
+ as: config.as ?? DEFAULT_POLYMORPHIC_ALIAS,
749
+ variants: config.variants
750
+ };
751
+ builder.config.discriminator = {
752
+ as: config.as,
753
+ variants: config.variants
754
+ };
755
+ return builder;
756
+ }
757
+ var ConvexDeletionBuilder = class {
758
+ static [entityKind] = "ConvexDeletionBuilder";
759
+ [entityKind] = "ConvexDeletionBuilder";
760
+ constructor(config) {
761
+ this.config = config;
762
+ }
763
+ };
764
+ function deletion(mode, options) {
765
+ if (options?.delayMs !== void 0) {
766
+ if (mode !== "scheduled") throw new Error("deletion() delayMs is only supported for 'scheduled'.");
767
+ if (!Number.isInteger(options.delayMs) || options.delayMs < 0) throw new Error("deletion() delayMs must be a non-negative integer when mode is 'scheduled'.");
768
+ }
769
+ return new ConvexDeletionBuilder({
770
+ mode,
771
+ delayMs: options?.delayMs
772
+ });
773
+ }
774
+ function isConvexIndexBuilder(value) {
775
+ return typeof value === "object" && value !== null && value[entityKind] === "ConvexIndexBuilder";
776
+ }
777
+ function isConvexIndexBuilderOn(value) {
778
+ return typeof value === "object" && value !== null && value[entityKind] === "ConvexIndexBuilderOn";
779
+ }
780
+ function isConvexAggregateIndexBuilder(value) {
781
+ return typeof value === "object" && value !== null && value[entityKind] === "ConvexAggregateIndexBuilder";
782
+ }
783
+ function isConvexAggregateIndexBuilderOn(value) {
784
+ return typeof value === "object" && value !== null && value[entityKind] === "ConvexAggregateIndexBuilderOn";
785
+ }
786
+ function isConvexRankIndexBuilderOn(value) {
787
+ return typeof value === "object" && value !== null && value[entityKind] === "ConvexRankIndexBuilderOn";
788
+ }
789
+ function isConvexRankIndexBuilder(value) {
790
+ return typeof value === "object" && value !== null && value[entityKind] === "ConvexRankIndexBuilder";
791
+ }
792
+ function isConvexUniqueConstraintBuilderOn(value) {
793
+ return typeof value === "object" && value !== null && value[entityKind] === "ConvexUniqueConstraintBuilderOn";
794
+ }
795
+ function isConvexForeignKeyBuilder(value) {
796
+ return typeof value === "object" && value !== null && value[entityKind] === "ConvexForeignKeyBuilder";
797
+ }
798
+ function isConvexCheckBuilder(value) {
799
+ return typeof value === "object" && value !== null && value[entityKind] === "ConvexCheckBuilder";
800
+ }
801
+ function isConvexSearchIndexBuilderOn(value) {
802
+ return typeof value === "object" && value !== null && value[entityKind] === "ConvexSearchIndexBuilderOn";
803
+ }
804
+ function isConvexUniqueConstraintBuilder(value) {
805
+ return typeof value === "object" && value !== null && value[entityKind] === "ConvexUniqueConstraintBuilder";
806
+ }
807
+ function isConvexSearchIndexBuilder(value) {
808
+ return typeof value === "object" && value !== null && value[entityKind] === "ConvexSearchIndexBuilder";
809
+ }
810
+ function isConvexVectorIndexBuilderOn(value) {
811
+ return typeof value === "object" && value !== null && value[entityKind] === "ConvexVectorIndexBuilderOn";
812
+ }
813
+ function isConvexVectorIndexBuilder(value) {
814
+ return typeof value === "object" && value !== null && value[entityKind] === "ConvexVectorIndexBuilder";
815
+ }
816
+ function isConvexDeletionBuilder(value) {
817
+ return typeof value === "object" && value !== null && value[entityKind] === "ConvexDeletionBuilder";
818
+ }
819
+ function isConvexLifecycleBuilder(value) {
820
+ return typeof value === "object" && value !== null && value[entityKind] === "ConvexLifecycleBuilder";
821
+ }
822
+ function getColumnName(column) {
823
+ const config = column.config;
824
+ if (!config?.name) throw new Error("Invalid index column: expected a convexTable column builder.");
825
+ return config.name;
826
+ }
827
+ function getColumnType(column) {
828
+ return column.config?.columnType;
829
+ }
830
+ function getColumnDimensions(column) {
831
+ return column.config?.dimensions;
832
+ }
833
+ function getColumnTableName(column) {
834
+ const config = column.config;
835
+ return config?.tableName ?? config?.referenceTable;
836
+ }
837
+ function getColumnTable(column) {
838
+ return column.config?.table;
839
+ }
840
+ function getUniqueIndexName(tableName, fields, explicitName) {
841
+ if (explicitName) return explicitName;
842
+ return `${tableName}_${fields.join("_")}_unique`;
843
+ }
844
+ function assertColumnInTable(column, expectedTable, context) {
845
+ const tableName = getColumnTableName(column);
846
+ if (tableName && tableName !== expectedTable) throw new Error(`${context} references column from '${tableName}', but belongs to '${expectedTable}'.`);
847
+ return getColumnName(column);
848
+ }
849
+ function assertNoReservedCreatedAtIndexFields(fields, context) {
850
+ if (fields.includes("createdAt")) throw new Error(`${context} cannot use 'createdAt'. 'createdAt' is reserved and maps to internal '_creationTime'.`);
851
+ }
852
+ function assertSearchFieldType(column, indexName) {
853
+ const columnType = getColumnType(column) ?? "unknown";
854
+ if (columnType !== "ConvexText") throw new Error(`Search index '${indexName}' only supports text() columns. Field '${getColumnName(column)}' is type '${columnType}'.`);
855
+ }
856
+ function assertVectorFieldType(column, indexName) {
857
+ const columnType = getColumnType(column) ?? "unknown";
858
+ if (columnType !== "ConvexVector") throw new Error(`Vector index '${indexName}' requires a vector() column. Field '${getColumnName(column)}' is type '${columnType}'.`);
859
+ }
860
+ function assertAggregateSumFieldType(column, indexName) {
861
+ const columnType = getColumnType(column) ?? "unknown";
862
+ if (!["ConvexNumber", "ConvexTimestamp"].includes(columnType)) throw new Error(`aggregateIndex '${indexName}' sum() supports integer()/timestamp() columns only. Field '${getColumnName(column)}' is type '${columnType}'.`);
863
+ }
864
+ function assertAggregateAvgFieldType(column, indexName) {
865
+ const columnType = getColumnType(column) ?? "unknown";
866
+ if (!["ConvexNumber", "ConvexTimestamp"].includes(columnType)) throw new Error(`aggregateIndex '${indexName}' avg() supports integer()/timestamp() columns only. Field '${getColumnName(column)}' is type '${columnType}'.`);
867
+ }
868
+ function assertAggregateComparableFieldType(column, indexName, method) {
869
+ const columnType = getColumnType(column) ?? "unknown";
870
+ if (![
871
+ "ConvexNumber",
872
+ "ConvexTimestamp",
873
+ "ConvexDate",
874
+ "ConvexText",
875
+ "ConvexBoolean",
876
+ "ConvexId"
877
+ ].includes(columnType)) throw new Error(`aggregateIndex '${indexName}' ${method}() does not support column type '${columnType}' on '${getColumnName(column)}'.`);
878
+ }
879
+ function assertRankOrderFieldType(column, indexName) {
880
+ const columnType = getColumnType(column) ?? "unknown";
881
+ if (![
882
+ "ConvexNumber",
883
+ "ConvexTimestamp",
884
+ "ConvexDate"
885
+ ].includes(columnType)) throw new Error(`rankIndex '${indexName}' orderBy() supports integer()/timestamp()/date() columns only. Field '${getColumnName(column)}' is type '${columnType}'.`);
886
+ }
887
+ const dedupeFieldNames = (fields) => [...new Set(fields)];
888
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
889
+ const isColumnBuilder = (value) => isRecord(value) && typeof value.build === "function";
890
+ const getDiscriminatorConfig = (value) => {
891
+ if (!isColumnBuilder(value)) return;
892
+ const discriminator = value.config?.discriminator;
893
+ if (!discriminator) return;
894
+ return discriminator;
895
+ };
896
+ const getPolymorphicFieldSignature = (column) => {
897
+ const validator = column.convexValidator ?? column.build();
898
+ return JSON.stringify({
899
+ columnType: column.config?.columnType,
900
+ validator: validator?.json
901
+ });
902
+ };
903
+ function resolveTableColumns(tableName, columns) {
904
+ const resolvedColumns = {};
905
+ const pendingPolymorphic = [];
906
+ for (const [columnName, rawBuilder] of Object.entries(columns)) {
907
+ if (!isColumnBuilder(rawBuilder)) throw new Error(`Column '${columnName}' on '${tableName}' must be a column builder.`);
908
+ resolvedColumns[columnName] = rawBuilder;
909
+ const discriminatorConfig = getDiscriminatorConfig(rawBuilder);
910
+ if (!discriminatorConfig) continue;
911
+ if (!isRecord(discriminatorConfig.variants) || Object.keys(discriminatorConfig.variants).length === 0) throw new Error(`discriminator('${tableName}.${columnName}') requires at least one variant.`);
912
+ const alias = discriminatorConfig.as === void 0 ? DEFAULT_POLYMORPHIC_ALIAS : discriminatorConfig.as;
913
+ if (typeof alias !== "string" || alias.length === 0) throw new Error(`discriminator('${tableName}.${columnName}').as must be a non-empty string.`);
914
+ pendingPolymorphic.push({
915
+ discriminator: columnName,
916
+ alias,
917
+ variants: discriminatorConfig.variants
918
+ });
919
+ }
920
+ if (pendingPolymorphic.length > 1) throw new Error(`Only one discriminator(...) column is currently supported on '${tableName}'.`);
921
+ const polymorphicConfigs = [];
922
+ for (const pending of pendingPolymorphic) {
923
+ if (pending.alias in resolvedColumns) throw new Error(`discriminator('${tableName}.${pending.discriminator}') alias '${pending.alias}' collides with an existing column.`);
924
+ const generatedFieldMap = /* @__PURE__ */ new Map();
925
+ const variantRuntime = {};
926
+ for (const [variantKey, rawVariantColumns] of Object.entries(pending.variants)) {
927
+ if (!isRecord(rawVariantColumns)) throw new Error(`discriminator('${tableName}.${pending.discriminator}') variant '${variantKey}' must be an object.`);
928
+ const fieldNames = [];
929
+ const requiredFieldNames = [];
930
+ for (const [fieldName, rawFieldBuilder] of Object.entries(rawVariantColumns)) {
931
+ if (!isColumnBuilder(rawFieldBuilder)) throw new Error(`discriminator('${tableName}.${pending.discriminator}').variants.${variantKey}.${fieldName} must be a column builder.`);
932
+ if (fieldName in resolvedColumns) throw new Error(`discriminator('${tableName}.${pending.discriminator}').variants.${variantKey}.${fieldName} collides with an existing table column.`);
933
+ const fieldBuilder = rawFieldBuilder;
934
+ const fieldConfig = fieldBuilder.config;
935
+ const isRequiredForVariant = fieldConfig?.notNull === true && fieldConfig.hasDefault !== true && typeof fieldConfig.defaultFn !== "function";
936
+ const signature = getPolymorphicFieldSignature(fieldBuilder);
937
+ const existing = generatedFieldMap.get(fieldName);
938
+ if (existing && existing.signature !== signature) throw new Error(`discriminator('${tableName}.${pending.discriminator}') field '${fieldName}' has conflicting builder signatures across variants.`);
939
+ if (!existing) {
940
+ if (fieldConfig) fieldConfig.notNull = false;
941
+ generatedFieldMap.set(fieldName, {
942
+ builder: fieldBuilder,
943
+ signature
944
+ });
945
+ }
946
+ fieldNames.push(fieldName);
947
+ if (isRequiredForVariant) requiredFieldNames.push(fieldName);
948
+ }
949
+ variantRuntime[variantKey] = {
950
+ fieldNames,
951
+ requiredFieldNames
952
+ };
953
+ }
954
+ for (const [fieldName, { builder }] of generatedFieldMap.entries()) resolvedColumns[fieldName] = builder;
955
+ polymorphicConfigs.push({
956
+ discriminator: pending.discriminator,
957
+ alias: pending.alias,
958
+ generatedFieldNames: Object.freeze([...generatedFieldMap.keys()]),
959
+ variants: Object.freeze(variantRuntime)
960
+ });
961
+ }
962
+ 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.`);
963
+ return {
964
+ columns: resolvedColumns,
965
+ polymorphicConfigs
966
+ };
967
+ }
968
+ function applyExtraConfig(table, config) {
969
+ if (!config) return;
970
+ const entries = Array.isArray(config) ? config : Object.values(config);
971
+ for (const entry of entries) {
972
+ if (isConvexIndexBuilderOn(entry)) throw new Error(`Invalid index definition on '${table.tableName}'. Did you forget to call .on(...)?`);
973
+ if (isConvexUniqueConstraintBuilderOn(entry)) throw new Error(`Invalid unique constraint definition on '${table.tableName}'. Did you forget to call .on(...)?`);
974
+ if (isConvexAggregateIndexBuilderOn(entry)) throw new Error(`Invalid aggregate index definition on '${table.tableName}'. Did you forget to call .on(...) or .all()?`);
975
+ if (isConvexRankIndexBuilderOn(entry)) throw new Error(`Invalid rank index definition on '${table.tableName}'. Did you forget to call .partitionBy(...) or .all()?`);
976
+ if (isConvexSearchIndexBuilderOn(entry)) throw new Error(`Invalid search index definition on '${table.tableName}'. Did you forget to call .on(...)?`);
977
+ if (isConvexVectorIndexBuilderOn(entry)) throw new Error(`Invalid vector index definition on '${table.tableName}'. Did you forget to call .on(...)?`);
978
+ if (isRlsPolicy(entry)) {
979
+ const target = entry._linkedTable ?? table;
980
+ if (typeof target.addRlsPolicy === "function") target.addRlsPolicy(entry);
981
+ else {
982
+ const policies = target[RlsPolicies] ?? [];
983
+ policies.push(entry);
984
+ target[RlsPolicies] = policies;
985
+ target[EnableRLS] = true;
986
+ }
987
+ continue;
988
+ }
989
+ if (isConvexDeletionBuilder(entry)) {
990
+ if (table[TableDeleteConfig]) throw new Error(`Only one deletion(...) config can be defined for '${table.tableName}'.`);
991
+ table[TableDeleteConfig] = {
992
+ mode: entry.config.mode,
993
+ delayMs: entry.config.delayMs
994
+ };
995
+ continue;
996
+ }
997
+ 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.`);
998
+ if (isConvexIndexBuilder(entry)) {
999
+ const { name, columns, unique, where } = entry.config;
1000
+ if (where) throw new Error(`Convex does not support partial indexes. Remove .where(...) from index '${name}'.`);
1001
+ if (unique) {}
1002
+ const fields = columns.map((column) => assertColumnInTable(column, table.tableName, `Index '${name}'`));
1003
+ assertNoReservedCreatedAtIndexFields(fields, `Index '${name}'`);
1004
+ table.addIndex(name, fields);
1005
+ if (unique) table.addUniqueIndex(name, fields, false);
1006
+ continue;
1007
+ }
1008
+ if (isConvexAggregateIndexBuilder(entry)) {
1009
+ const { name, columns, countFields, sumFields, avgFields, minFields, maxFields } = entry.config;
1010
+ const fields = columns.map((column) => assertColumnInTable(column, table.tableName, `Aggregate index '${name}'`));
1011
+ assertNoReservedCreatedAtIndexFields(fields, `Aggregate index '${name}'`);
1012
+ const resolvedCountFields = dedupeFieldNames(countFields.map((column) => assertColumnInTable(column, table.tableName, `Aggregate index '${name}' count`)));
1013
+ assertNoReservedCreatedAtIndexFields(resolvedCountFields, `Aggregate index '${name}' count`);
1014
+ const resolvedSumFields = dedupeFieldNames(sumFields.map((column) => {
1015
+ const field = assertColumnInTable(column, table.tableName, `Aggregate index '${name}' sum`);
1016
+ assertAggregateSumFieldType(column, name);
1017
+ return field;
1018
+ }));
1019
+ assertNoReservedCreatedAtIndexFields(resolvedSumFields, `Aggregate index '${name}' sum`);
1020
+ const resolvedAvgFields = dedupeFieldNames(avgFields.map((column) => {
1021
+ const field = assertColumnInTable(column, table.tableName, `Aggregate index '${name}' avg`);
1022
+ assertAggregateAvgFieldType(column, name);
1023
+ return field;
1024
+ }));
1025
+ assertNoReservedCreatedAtIndexFields(resolvedAvgFields, `Aggregate index '${name}' avg`);
1026
+ const resolvedMinFields = dedupeFieldNames(minFields.map((column) => {
1027
+ const field = assertColumnInTable(column, table.tableName, `Aggregate index '${name}' min`);
1028
+ assertAggregateComparableFieldType(column, name, "min");
1029
+ return field;
1030
+ }));
1031
+ assertNoReservedCreatedAtIndexFields(resolvedMinFields, `Aggregate index '${name}' min`);
1032
+ const resolvedMaxFields = dedupeFieldNames(maxFields.map((column) => {
1033
+ const field = assertColumnInTable(column, table.tableName, `Aggregate index '${name}' max`);
1034
+ assertAggregateComparableFieldType(column, name, "max");
1035
+ return field;
1036
+ }));
1037
+ assertNoReservedCreatedAtIndexFields(resolvedMaxFields, `Aggregate index '${name}' max`);
1038
+ table.addAggregateIndex(name, {
1039
+ fields,
1040
+ countFields: resolvedCountFields,
1041
+ sumFields: resolvedSumFields,
1042
+ avgFields: resolvedAvgFields,
1043
+ minFields: resolvedMinFields,
1044
+ maxFields: resolvedMaxFields
1045
+ });
1046
+ continue;
1047
+ }
1048
+ if (isConvexRankIndexBuilder(entry)) {
1049
+ const { name, partitionColumns, orderColumns, sumField } = entry.config;
1050
+ if (!orderColumns.length) throw new Error(`rankIndex '${name}' on '${table.tableName}' must declare at least one orderBy(...) column.`);
1051
+ const resolvedPartitionFields = dedupeFieldNames(partitionColumns.map((column) => assertColumnInTable(column, table.tableName, `rankIndex '${name}'`)));
1052
+ assertNoReservedCreatedAtIndexFields(resolvedPartitionFields, `rankIndex '${name}' partitionBy`);
1053
+ const resolvedOrderFields = orderColumns.map((entry) => {
1054
+ const field = assertColumnInTable(entry.column, table.tableName, `rankIndex '${name}' orderBy`);
1055
+ assertNoReservedCreatedAtIndexFields([field], `rankIndex '${name}'`);
1056
+ assertRankOrderFieldType(entry.column, name);
1057
+ return {
1058
+ field,
1059
+ direction: entry.direction
1060
+ };
1061
+ });
1062
+ const resolvedSumField = sumField ? (() => {
1063
+ const field = assertColumnInTable(sumField, table.tableName, `rankIndex '${name}' sum`);
1064
+ assertNoReservedCreatedAtIndexFields([field], `rankIndex '${name}'`);
1065
+ assertAggregateSumFieldType(sumField, name);
1066
+ return field;
1067
+ })() : void 0;
1068
+ table.addRankIndex(name, {
1069
+ partitionFields: resolvedPartitionFields,
1070
+ orderFields: resolvedOrderFields,
1071
+ sumField: resolvedSumField
1072
+ });
1073
+ continue;
1074
+ }
1075
+ if (isConvexUniqueConstraintBuilder(entry)) {
1076
+ const { name, columns, nullsNotDistinct } = entry.config;
1077
+ const fields = columns.map((column) => assertColumnInTable(column, table.tableName, "Unique constraint"));
1078
+ assertNoReservedCreatedAtIndexFields(fields, "Unique constraint");
1079
+ const indexName = getUniqueIndexName(table.tableName, fields, name);
1080
+ table.addIndex(indexName, fields);
1081
+ table.addUniqueIndex(indexName, fields, nullsNotDistinct);
1082
+ continue;
1083
+ }
1084
+ if (isConvexForeignKeyBuilder(entry)) {
1085
+ const { name, columns, foreignColumns, onDelete, onUpdate } = entry.config;
1086
+ if (columns.length === 0 || foreignColumns.length === 0) throw new Error(`Foreign key on '${table.tableName}' requires at least one column.`);
1087
+ if (columns.length !== foreignColumns.length) throw new Error(`Foreign key on '${table.tableName}' must specify matching columns and foreignColumns.`);
1088
+ const localFields = columns.map((column) => assertColumnInTable(column, table.tableName, "Foreign key"));
1089
+ const foreignTableName = getColumnTableName(foreignColumns[0]);
1090
+ if (!foreignTableName) throw new Error(`Foreign key on '${table.tableName}' references a column without a table.`);
1091
+ const foreignTable = getColumnTable(foreignColumns[0]);
1092
+ const foreignFields = foreignColumns.map((column) => {
1093
+ const tableName = getColumnTableName(column);
1094
+ if (tableName && tableName !== foreignTableName) throw new Error(`Foreign key on '${table.tableName}' mixes foreign columns from '${foreignTableName}' and '${tableName}'.`);
1095
+ return getColumnName(column);
1096
+ });
1097
+ table.addForeignKey({
1098
+ name,
1099
+ columns: localFields,
1100
+ foreignTableName,
1101
+ foreignTable,
1102
+ foreignColumns: foreignFields,
1103
+ onDelete,
1104
+ onUpdate
1105
+ });
1106
+ continue;
1107
+ }
1108
+ if (isConvexCheckBuilder(entry)) {
1109
+ const { name, expression } = entry.config;
1110
+ table.addCheck(name, expression);
1111
+ continue;
1112
+ }
1113
+ if (isConvexSearchIndexBuilder(entry)) {
1114
+ const { name, searchField, filterFields, staged } = entry.config;
1115
+ const searchFieldName = assertColumnInTable(searchField, table.tableName, `Search index '${name}'`);
1116
+ assertNoReservedCreatedAtIndexFields([searchFieldName], `Search index '${name}'`);
1117
+ assertSearchFieldType(searchField, name);
1118
+ const filterFieldNames = filterFields.map((field) => assertColumnInTable(field, table.tableName, `Search index '${name}'`));
1119
+ assertNoReservedCreatedAtIndexFields(filterFieldNames, `Search index '${name}'`);
1120
+ table.addSearchIndex(name, {
1121
+ searchField: searchFieldName,
1122
+ filterFields: filterFieldNames,
1123
+ staged
1124
+ });
1125
+ continue;
1126
+ }
1127
+ if (isConvexVectorIndexBuilder(entry)) {
1128
+ const { name, vectorField, dimensions, filterFields, staged } = entry.config;
1129
+ if (dimensions === void 0) throw new Error(`Vector index '${name}' is missing dimensions. Call .dimensions(n) before using.`);
1130
+ const vectorFieldName = assertColumnInTable(vectorField, table.tableName, `Vector index '${name}'`);
1131
+ assertNoReservedCreatedAtIndexFields([vectorFieldName], `Vector index '${name}'`);
1132
+ assertVectorFieldType(vectorField, name);
1133
+ const columnDimensions = getColumnDimensions(vectorField);
1134
+ if (columnDimensions !== void 0 && columnDimensions !== dimensions) throw new Error(`Vector index '${name}' dimensions (${dimensions}) do not match vector column '${vectorFieldName}' dimensions (${columnDimensions}).`);
1135
+ const filterFieldNames = filterFields.map((field) => assertColumnInTable(field, table.tableName, `Vector index '${name}'`));
1136
+ assertNoReservedCreatedAtIndexFields(filterFieldNames, `Vector index '${name}'`);
1137
+ table.addVectorIndex(name, {
1138
+ vectorField: vectorFieldName,
1139
+ dimensions,
1140
+ filterFields: filterFieldNames,
1141
+ staged
1142
+ });
1143
+ continue;
1144
+ }
1145
+ throw new Error(`Unsupported extra config value in convexTable('${table.tableName}').`);
1146
+ }
1147
+ }
1148
+ /**
1149
+ * ConvexTable implementation class
1150
+ * Provides all properties required by Convex's TableDefinition
1151
+ *
1152
+ * Following convex-ents pattern:
1153
+ * - Private fields for indexes (matches TableDefinition structure)
1154
+ * - Duck typing (defineSchema only checks object shape)
1155
+ * - Direct validator storage (no re-wrapping)
1156
+ */
1157
+ var ConvexTableImpl = class {
1158
+ /**
1159
+ * Required by TableDefinition
1160
+ * Public validator property containing v.object({...}) with all column validators
1161
+ */
1162
+ validator;
1163
+ /**
1164
+ * TableDefinition private fields
1165
+ * These satisfy structural typing requirements for defineSchema()
1166
+ */
1167
+ indexes = [];
1168
+ uniqueIndexes = [];
1169
+ aggregateIndexes = [];
1170
+ rankIndexes = [];
1171
+ foreignKeys = [];
1172
+ deferredForeignKeys = [];
1173
+ deferredForeignKeysResolved = false;
1174
+ stagedDbIndexes = [];
1175
+ searchIndexes = [];
1176
+ stagedSearchIndexes = [];
1177
+ vectorIndexes = [];
1178
+ stagedVectorIndexes = [];
1179
+ checks = [];
1180
+ /**
1181
+ * Symbol-based metadata storage
1182
+ */
1183
+ [TableName];
1184
+ [Columns];
1185
+ [Brand] = "ConvexTable";
1186
+ [EnableRLS] = false;
1187
+ [RlsPolicies] = [];
1188
+ [TableDeleteConfig];
1189
+ [TablePolymorphic];
1190
+ /**
1191
+ * Public tableName for convenience
1192
+ */
1193
+ tableName;
1194
+ constructor(name, columns, polymorphicConfigs) {
1195
+ validateTableName(name);
1196
+ 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.`);
1197
+ this[TableName] = name;
1198
+ const namedColumns = Object.fromEntries(Object.entries(columns).map(([columnName, builder]) => {
1199
+ builder.config.name = columnName;
1200
+ builder.config.tableName = name;
1201
+ builder.config.table = this;
1202
+ return [columnName, builder];
1203
+ }));
1204
+ this[Columns] = namedColumns;
1205
+ this.tableName = name;
1206
+ if (polymorphicConfigs && polymorphicConfigs.length > 0) this[TablePolymorphic] = polymorphicConfigs;
1207
+ this.validator = createValidatorFromColumns(namedColumns);
1208
+ for (const [columnName, builder] of Object.entries(namedColumns)) {
1209
+ const config = builder.config;
1210
+ if (config?.isUnique) {
1211
+ const indexName = getUniqueIndexName(name, [columnName], config.uniqueName);
1212
+ const nullsNotDistinct = config.uniqueNulls === "not distinct";
1213
+ this.addIndex(indexName, [columnName]);
1214
+ this.addUniqueIndex(indexName, [columnName], nullsNotDistinct);
1215
+ }
1216
+ if (config?.referenceTable && (!config.foreignKeyConfigs || config.foreignKeyConfigs.length === 0)) this.addForeignKey({
1217
+ name: void 0,
1218
+ columns: [columnName],
1219
+ foreignTableName: config.referenceTable,
1220
+ foreignColumns: ["_id"]
1221
+ });
1222
+ if (config?.foreignKeyConfigs?.length) for (const foreignConfig of config.foreignKeyConfigs) this.deferredForeignKeys.push({
1223
+ localColumnName: columnName,
1224
+ ref: foreignConfig.ref,
1225
+ config: foreignConfig.config
1226
+ });
1227
+ }
1228
+ }
1229
+ getPolymorphicConfigs() {
1230
+ return this[TablePolymorphic];
1231
+ }
1232
+ /**
1233
+ * Internal: add index to table from builder extraConfig
1234
+ *
1235
+ */
1236
+ addIndex(name, fields) {
1237
+ this.indexes.push({
1238
+ indexDescriptor: name,
1239
+ fields
1240
+ });
1241
+ }
1242
+ /**
1243
+ * Internal: add unique index metadata for runtime enforcement
1244
+ */
1245
+ addUniqueIndex(name, fields, nullsNotDistinct) {
1246
+ this.uniqueIndexes.push({
1247
+ name,
1248
+ fields,
1249
+ nullsNotDistinct
1250
+ });
1251
+ }
1252
+ addAggregateIndex(name, config) {
1253
+ 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}'.`);
1254
+ this.aggregateIndexes.push({
1255
+ name,
1256
+ fields: config.fields,
1257
+ countFields: config.countFields,
1258
+ sumFields: config.sumFields,
1259
+ avgFields: config.avgFields,
1260
+ minFields: config.minFields,
1261
+ maxFields: config.maxFields
1262
+ });
1263
+ }
1264
+ addRankIndex(name, config) {
1265
+ 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}'.`);
1266
+ this.rankIndexes.push({
1267
+ name,
1268
+ partitionFields: config.partitionFields,
1269
+ orderFields: config.orderFields,
1270
+ sumField: config.sumField
1271
+ });
1272
+ }
1273
+ getAggregateIndexes() {
1274
+ return [...this.aggregateIndexes];
1275
+ }
1276
+ getRankIndexes() {
1277
+ return [...this.rankIndexes];
1278
+ }
1279
+ /**
1280
+ * Internal: expose unique index metadata for mutation enforcement
1281
+ */
1282
+ getUniqueIndexes() {
1283
+ return this.uniqueIndexes;
1284
+ }
1285
+ /**
1286
+ * Internal: expose index metadata for runtime enforcement
1287
+ */
1288
+ getIndexes() {
1289
+ return this.indexes.map((entry) => ({
1290
+ name: entry.indexDescriptor,
1291
+ fields: entry.fields
1292
+ }));
1293
+ }
1294
+ /**
1295
+ * Internal: expose search index metadata for runtime query execution
1296
+ */
1297
+ getSearchIndexes() {
1298
+ return this.searchIndexes.map((entry) => ({
1299
+ name: entry.indexDescriptor,
1300
+ searchField: entry.searchField,
1301
+ filterFields: entry.filterFields
1302
+ }));
1303
+ }
1304
+ /**
1305
+ * Internal: expose vector index metadata for runtime query execution
1306
+ */
1307
+ getVectorIndexes() {
1308
+ return this.vectorIndexes.map((entry) => ({
1309
+ name: entry.indexDescriptor,
1310
+ vectorField: entry.vectorField,
1311
+ dimensions: entry.dimensions,
1312
+ filterFields: entry.filterFields
1313
+ }));
1314
+ }
1315
+ /**
1316
+ * Internal: attach an RLS policy to this table
1317
+ */
1318
+ addRlsPolicy(policy) {
1319
+ this[RlsPolicies].push(policy);
1320
+ this[EnableRLS] = true;
1321
+ }
1322
+ /**
1323
+ * Internal: return attached RLS policies
1324
+ */
1325
+ getRlsPolicies() {
1326
+ return this[RlsPolicies];
1327
+ }
1328
+ /**
1329
+ * Internal: check if RLS is enabled on this table
1330
+ */
1331
+ isRlsEnabled() {
1332
+ return this[EnableRLS];
1333
+ }
1334
+ /**
1335
+ * Internal: add foreign key metadata for runtime enforcement
1336
+ */
1337
+ addForeignKey(definition) {
1338
+ const matches = (existing) => {
1339
+ if (existing.foreignTableName !== definition.foreignTableName) return false;
1340
+ if (existing.columns.length !== definition.columns.length) return false;
1341
+ if (existing.foreignColumns.length !== definition.foreignColumns.length) return false;
1342
+ for (let i = 0; i < existing.columns.length; i++) if (existing.columns[i] !== definition.columns[i]) return false;
1343
+ for (let i = 0; i < existing.foreignColumns.length; i++) if (existing.foreignColumns[i] !== definition.foreignColumns[i]) return false;
1344
+ return true;
1345
+ };
1346
+ this.foreignKeys = this.foreignKeys.filter((existing) => !matches(existing));
1347
+ this.foreignKeys.push(definition);
1348
+ }
1349
+ resolveDeferredForeignKeys() {
1350
+ if (this.deferredForeignKeysResolved) return;
1351
+ this.deferredForeignKeysResolved = true;
1352
+ for (const deferred of this.deferredForeignKeys) {
1353
+ let foreignColumn;
1354
+ try {
1355
+ foreignColumn = deferred.ref();
1356
+ } catch (error) {
1357
+ const reason = error instanceof Error ? ` ${error.message}` : "";
1358
+ throw new Error(`Failed to resolve foreign key reference for '${this.tableName}.${deferred.localColumnName}'. Use references(() => targetTable.column) after both tables are declared.${reason}`);
1359
+ }
1360
+ const foreignTableName = getColumnTableName(foreignColumn);
1361
+ if (!foreignTableName) throw new Error(`Foreign key on '${this.tableName}.${deferred.localColumnName}' references a column without a table. Use references(() => targetTable.column).`);
1362
+ const foreignTable = getColumnTable(foreignColumn);
1363
+ 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).`);
1364
+ const foreignColumnName = getColumnName(foreignColumn);
1365
+ this.addForeignKey({
1366
+ name: deferred.config.name,
1367
+ columns: [deferred.localColumnName],
1368
+ foreignTableName,
1369
+ foreignTable,
1370
+ foreignColumns: [foreignColumnName],
1371
+ onDelete: deferred.config.onDelete,
1372
+ onUpdate: deferred.config.onUpdate
1373
+ });
1374
+ }
1375
+ this.deferredForeignKeys = [];
1376
+ }
1377
+ /**
1378
+ * Internal: expose foreign key metadata for mutation enforcement
1379
+ */
1380
+ getForeignKeys() {
1381
+ this.resolveDeferredForeignKeys();
1382
+ return this.foreignKeys;
1383
+ }
1384
+ addCheck(name, expression) {
1385
+ this.checks.push({
1386
+ name,
1387
+ expression
1388
+ });
1389
+ }
1390
+ getChecks() {
1391
+ return this.checks;
1392
+ }
1393
+ /**
1394
+ * Internal: add search index to table from builder extraConfig
1395
+ */
1396
+ addSearchIndex(name, config) {
1397
+ const entry = {
1398
+ indexDescriptor: name,
1399
+ searchField: config.searchField,
1400
+ filterFields: config.filterFields ?? []
1401
+ };
1402
+ if (config.staged) this.stagedSearchIndexes.push(entry);
1403
+ else this.searchIndexes.push(entry);
1404
+ }
1405
+ /**
1406
+ * Internal: add vector index to table from builder extraConfig
1407
+ */
1408
+ addVectorIndex(name, config) {
1409
+ const entry = {
1410
+ indexDescriptor: name,
1411
+ vectorField: config.vectorField,
1412
+ dimensions: config.dimensions,
1413
+ filterFields: config.filterFields ?? []
1414
+ };
1415
+ if (config.staged) this.stagedVectorIndexes.push(entry);
1416
+ else this.vectorIndexes.push(entry);
1417
+ }
1418
+ /**
1419
+ * Export the contents of this definition for Convex schema tooling.
1420
+ * Mirrors convex/server TableDefinition.export().
1421
+ */
1422
+ export() {
1423
+ const documentType = this.validator.json;
1424
+ 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)");
1425
+ return {
1426
+ indexes: this.indexes,
1427
+ stagedDbIndexes: this.stagedDbIndexes,
1428
+ searchIndexes: this.searchIndexes,
1429
+ stagedSearchIndexes: this.stagedSearchIndexes,
1430
+ vectorIndexes: this.vectorIndexes,
1431
+ stagedVectorIndexes: this.stagedVectorIndexes,
1432
+ documentType
1433
+ };
1434
+ }
1435
+ };
1436
+ const convexTableInternal = (name, columns, extraConfig) => {
1437
+ const expanded = resolveTableColumns(name, columns);
1438
+ const rawTable = new ConvexTableImpl(name, expanded.columns, expanded.polymorphicConfigs);
1439
+ const systemFields = createSystemFields(name);
1440
+ for (const builder of Object.values(systemFields)) builder.config.table = rawTable;
1441
+ const table = Object.assign(rawTable, systemFields, rawTable[Columns]);
1442
+ const internalCreationTime = systemFields._creationTime;
1443
+ if (Object.hasOwn(table, "_creationTime")) table._creationTime = void 0;
1444
+ Object.defineProperty(table, "_creationTime", {
1445
+ value: internalCreationTime,
1446
+ enumerable: false,
1447
+ configurable: true,
1448
+ writable: false
1449
+ });
1450
+ Object.defineProperty(table, "_id", {
1451
+ value: systemFields.id,
1452
+ enumerable: false,
1453
+ configurable: true,
1454
+ writable: false
1455
+ });
1456
+ applyExtraConfig(rawTable, extraConfig?.(table));
1457
+ return table;
1458
+ };
1459
+ const convexTableWithRLS = (name, columns, extraConfig) => {
1460
+ const table = convexTableInternal(name, columns, extraConfig);
1461
+ table[EnableRLS] = true;
1462
+ return table;
1463
+ };
1464
+ const convexTable = Object.assign(convexTableInternal, { withRLS: convexTableWithRLS });
1465
+
1466
+ //#endregion
1467
+ //#region src/aggregate-core/schema.ts
1468
+ const AGGREGATE_TREE_TABLE = "aggregate_rank_tree";
1469
+ const AGGREGATE_NODE_TABLE = "aggregate_rank_node";
1470
+ const aggregateCounterValidator = v.object({
1471
+ count: v.number(),
1472
+ sum: v.number()
1473
+ });
1474
+ const aggregateItemValidator = v.object({
1475
+ k: v.any(),
1476
+ v: v.any(),
1477
+ s: v.number()
1478
+ });
1479
+ const aggregateTreeTable = convexTable(AGGREGATE_TREE_TABLE, {
1480
+ aggregateName: text().notNull(),
1481
+ maxNodeSize: integer().notNull(),
1482
+ namespace: custom(v.any()),
1483
+ root: id(AGGREGATE_NODE_TABLE).notNull()
1484
+ }, (tree) => [index("by_namespace").on(tree.namespace), index("by_aggregate_name").on(tree.aggregateName)]);
1485
+ const aggregateNodeTable = convexTable(AGGREGATE_NODE_TABLE, {
1486
+ aggregate: custom(aggregateCounterValidator),
1487
+ items: custom(v.array(aggregateItemValidator)).notNull(),
1488
+ subtrees: custom(v.array(v.string())).notNull()
1489
+ });
1490
+ const aggregateStorageTables = {
1491
+ [AGGREGATE_NODE_TABLE]: aggregateNodeTable,
1492
+ [AGGREGATE_TREE_TABLE]: aggregateTreeTable
1493
+ };
1494
+
1495
+ //#endregion
1496
+ //#region src/aggregate-core/btree.ts
1497
+ const DEFAULT_MAX_NODE_SIZE = 16;
1498
+ const LEGACY_AGGREGATE_NAME = "__legacy__";
1499
+ function p(v) {
1500
+ try {
1501
+ return JSON.stringify(v);
1502
+ } catch {
1503
+ return String(v);
1504
+ }
1505
+ }
1506
+ function log(s) {}
1507
+ function aggregateNameFromNamespace(namespace) {
1508
+ if (Array.isArray(namespace) && namespace.length === 3 && typeof namespace[0] === "string" && (namespace[2] === 0 || namespace[2] === 1)) return namespace[0];
1509
+ return LEGACY_AGGREGATE_NAME;
1510
+ }
1511
+ async function insertHandler(ctx, args) {
1512
+ const tree = await getOrCreateTree(ctx.db, args.namespace, DEFAULT_MAX_NODE_SIZE, true);
1513
+ const summand = args.summand ?? 0;
1514
+ const pushUp = await insertIntoNode(ctx, args.namespace, tree.root, {
1515
+ k: args.key,
1516
+ v: args.value,
1517
+ s: summand
1518
+ });
1519
+ if (pushUp) {
1520
+ const total = pushUp.leftSubtreeCount && pushUp.rightSubtreeCount && add(add(pushUp.leftSubtreeCount, pushUp.rightSubtreeCount), itemAggregate(pushUp.item));
1521
+ const newRoot = await ctx.db.insert(AGGREGATE_NODE_TABLE, {
1522
+ items: [pushUp.item],
1523
+ subtrees: [pushUp.leftSubtree, pushUp.rightSubtree],
1524
+ aggregate: total
1525
+ });
1526
+ await ctx.db.patch(tree._id, { root: newRoot });
1527
+ }
1528
+ }
1529
+ async function deleteHandler(ctx, args) {
1530
+ const tree = await getOrCreateTree(ctx.db, args.namespace, DEFAULT_MAX_NODE_SIZE, true);
1531
+ await deleteFromNode(ctx, args.namespace, tree.root, args.key);
1532
+ const root = await ctx.db.get(tree.root);
1533
+ if (root.items.length === 0 && root.subtrees.length === 1) {
1534
+ log(`collapsing root ${root._id} because its only child is ${root.subtrees[0]}`);
1535
+ await ctx.db.patch(tree._id, { root: root.subtrees[0] });
1536
+ if (root.aggregate === void 0) await ctx.db.patch(root.subtrees[0], { aggregate: void 0 });
1537
+ await ctx.db.delete(root._id);
1538
+ }
1539
+ }
1540
+ async function MAX_NODE_SIZE(ctx, namespace) {
1541
+ return (await mustGetTree(ctx.db, namespace)).maxNodeSize;
1542
+ }
1543
+ async function MIN_NODE_SIZE(ctx, namespace) {
1544
+ const max = await MAX_NODE_SIZE(ctx, namespace);
1545
+ if (max % 2 !== 0 || max < 4) throw new Error("MAX_NODE_SIZE must be even and at least 4");
1546
+ return max / 2;
1547
+ }
1548
+ async function aggregateBetweenHandler(ctx, args) {
1549
+ const tree = await getTree(ctx.db, args.namespace);
1550
+ if (tree === null) return {
1551
+ count: 0,
1552
+ sum: 0
1553
+ };
1554
+ return await aggregateBetweenInNode(ctx.db, tree.root, args.k1, args.k2);
1555
+ }
1556
+ async function filterBetween(db, node, k1, k2) {
1557
+ const n = await db.get(node);
1558
+ const included = [];
1559
+ function includeSubtree(i, unboundedRight) {
1560
+ const unboundedLeft = k1 === void 0 || included.length > 0;
1561
+ if (unboundedLeft && unboundedRight) included.push({
1562
+ type: "subtree",
1563
+ subtree: n.subtrees[i]
1564
+ });
1565
+ else included.push(filterBetween(db, n.subtrees[i], unboundedLeft ? void 0 : k1, unboundedRight ? void 0 : k2));
1566
+ }
1567
+ let done = false;
1568
+ for (let i = 0; i < n.items.length; i++) {
1569
+ const k1IsLeft = k1 === void 0 || compareKeys(k1, n.items[i].k) === -1;
1570
+ const k2IsRight = k2 === void 0 || compareKeys(k2, n.items[i].k) === 1;
1571
+ if (k1IsLeft && n.subtrees.length > 0) includeSubtree(i, k2IsRight);
1572
+ if (!k2IsRight) {
1573
+ done = true;
1574
+ break;
1575
+ }
1576
+ if (k1IsLeft) included.push({
1577
+ type: "item",
1578
+ item: n.items[i]
1579
+ });
1580
+ }
1581
+ if (!done && n.subtrees.length > 0) includeSubtree(n.subtrees.length - 1, k2 === void 0);
1582
+ return (await Promise.all(included)).flat(1);
1583
+ }
1584
+ async function aggregateBetweenInNode(db, node, k1, k2) {
1585
+ const filtered = await filterBetween(db, node, k1, k2);
1586
+ const counts = await Promise.all(filtered.map(async (included) => {
1587
+ if (included.type === "item") return itemAggregate(included.item);
1588
+ return await nodeAggregate(db, await db.get(included.subtree));
1589
+ }));
1590
+ let count = {
1591
+ count: 0,
1592
+ sum: 0
1593
+ };
1594
+ for (const c of counts) count = add(count, c);
1595
+ return count;
1596
+ }
1597
+ async function atOffsetHandler(ctx, args) {
1598
+ if (args.offset < 0) throw new Error("offset must be non-negative");
1599
+ if (!Number.isInteger(args.offset)) throw new Error("offset must be an integer");
1600
+ const tree = await getTree(ctx.db, args.namespace);
1601
+ if (tree === null) throw new ConvexError("tree is empty");
1602
+ return await atOffsetInNode(ctx.db, tree.root, args.offset, args.k1, args.k2);
1603
+ }
1604
+ async function atNegativeOffsetHandler(ctx, args) {
1605
+ if (args.offset < 0) throw new Error("offset must be non-negative");
1606
+ if (!Number.isInteger(args.offset)) throw new Error("offset must be an integer");
1607
+ const tree = await getTree(ctx.db, args.namespace);
1608
+ if (tree === null) throw new ConvexError("tree is empty");
1609
+ return await negativeOffsetInNode(ctx.db, tree.root, args.offset, args.k1, args.k2);
1610
+ }
1611
+ async function offsetHandler(ctx, args) {
1612
+ return (await aggregateBetweenHandler(ctx, {
1613
+ k1: args.k1,
1614
+ k2: args.key,
1615
+ namespace: args.namespace
1616
+ })).count;
1617
+ }
1618
+ async function offsetUntilHandler(ctx, args) {
1619
+ return (await aggregateBetweenHandler(ctx, {
1620
+ k1: args.key,
1621
+ k2: args.k2,
1622
+ namespace: args.namespace
1623
+ })).count;
1624
+ }
1625
+ async function deleteFromNode(ctx, namespace, node, key) {
1626
+ let n = await ctx.db.get(node);
1627
+ let foundItem = null;
1628
+ let i = 0;
1629
+ for (; i < n.items.length; i++) {
1630
+ const compare = compareKeys(key, n.items[i].k);
1631
+ if (compare === -1) break;
1632
+ if (compare === 0) {
1633
+ log(`found key ${p(key)} in node ${n._id}`);
1634
+ if (n.subtrees.length === 0) {
1635
+ await ctx.db.patch(node, {
1636
+ items: [...n.items.slice(0, i), ...n.items.slice(i + 1)],
1637
+ aggregate: n.aggregate && sub(n.aggregate, itemAggregate(n.items[i]))
1638
+ });
1639
+ return n.items[i];
1640
+ }
1641
+ const predecessor = await negativeOffsetInNode(ctx.db, n.subtrees[i], 0);
1642
+ log(`replacing ${p(key)} with predecessor ${p(predecessor.k)}`);
1643
+ foundItem = n.items[i];
1644
+ await ctx.db.patch(node, {
1645
+ items: [
1646
+ ...n.items.slice(0, i),
1647
+ predecessor,
1648
+ ...n.items.slice(i + 1)
1649
+ ],
1650
+ aggregate: n.aggregate && sub(add(n.aggregate, itemAggregate(predecessor)), itemAggregate(n.items[i]))
1651
+ });
1652
+ n = await ctx.db.get(node);
1653
+ key = predecessor.k;
1654
+ break;
1655
+ }
1656
+ }
1657
+ if (n.subtrees.length === 0) throw new ConvexError({
1658
+ code: "DELETE_MISSING_KEY",
1659
+ message: `key ${p(key)} not found in node ${n._id}`
1660
+ });
1661
+ const deleted = await deleteFromNode(ctx, namespace, n.subtrees[i], key);
1662
+ if (!deleted) return null;
1663
+ if (!foundItem) foundItem = deleted;
1664
+ const newAggregate = n.aggregate && sub(n.aggregate, itemAggregate(deleted));
1665
+ if (newAggregate) await ctx.db.patch(node, { aggregate: newAggregate });
1666
+ const deficientSubtree = await ctx.db.get(n.subtrees[i]);
1667
+ const minNodeSize = await MIN_NODE_SIZE(ctx, namespace);
1668
+ if (deficientSubtree.items.length < minNodeSize) {
1669
+ log(`deficient subtree ${deficientSubtree._id}`);
1670
+ if (i > 0) {
1671
+ const leftSibling = await ctx.db.get(n.subtrees[i - 1]);
1672
+ if (leftSibling.items.length > minNodeSize) {
1673
+ log(`rotating right with left sibling ${leftSibling._id}`);
1674
+ const grandchild = leftSibling.subtrees.length ? await ctx.db.get(leftSibling.subtrees[leftSibling.subtrees.length - 1]) : null;
1675
+ const grandchildCount = grandchild ? grandchild.aggregate : {
1676
+ count: 0,
1677
+ sum: 0
1678
+ };
1679
+ await ctx.db.patch(deficientSubtree._id, {
1680
+ items: [n.items[i - 1], ...deficientSubtree.items],
1681
+ subtrees: grandchild ? [grandchild._id, ...deficientSubtree.subtrees] : [],
1682
+ aggregate: deficientSubtree.aggregate && grandchildCount && add(add(deficientSubtree.aggregate, grandchildCount), itemAggregate(n.items[i - 1]))
1683
+ });
1684
+ await ctx.db.patch(leftSibling._id, {
1685
+ items: leftSibling.items.slice(0, leftSibling.items.length - 1),
1686
+ subtrees: grandchild ? leftSibling.subtrees.slice(0, leftSibling.subtrees.length - 1) : [],
1687
+ aggregate: leftSibling.aggregate && grandchildCount && sub(sub(leftSibling.aggregate, grandchildCount), itemAggregate(leftSibling.items[leftSibling.items.length - 1]))
1688
+ });
1689
+ await ctx.db.patch(node, { items: [
1690
+ ...n.items.slice(0, i - 1),
1691
+ leftSibling.items[leftSibling.items.length - 1],
1692
+ ...n.items.slice(i)
1693
+ ] });
1694
+ return foundItem;
1695
+ }
1696
+ }
1697
+ if (i < n.subtrees.length - 1) {
1698
+ const rightSibling = await ctx.db.get(n.subtrees[i + 1]);
1699
+ if (rightSibling.items.length > minNodeSize) {
1700
+ log(`rotating left with right sibling ${rightSibling._id}`);
1701
+ const grandchild = rightSibling.subtrees.length ? await ctx.db.get(rightSibling.subtrees[0]) : null;
1702
+ const grandchildCount = grandchild ? grandchild.aggregate : {
1703
+ count: 0,
1704
+ sum: 0
1705
+ };
1706
+ await ctx.db.patch(deficientSubtree._id, {
1707
+ items: [...deficientSubtree.items, n.items[i]],
1708
+ subtrees: grandchild ? [...deficientSubtree.subtrees, grandchild._id] : [],
1709
+ aggregate: deficientSubtree.aggregate && grandchildCount && add(add(deficientSubtree.aggregate, grandchildCount), itemAggregate(n.items[i]))
1710
+ });
1711
+ await ctx.db.patch(rightSibling._id, {
1712
+ items: rightSibling.items.slice(1),
1713
+ subtrees: grandchild ? rightSibling.subtrees.slice(1) : [],
1714
+ aggregate: rightSibling.aggregate && grandchildCount && sub(sub(rightSibling.aggregate, grandchildCount), itemAggregate(rightSibling.items[0]))
1715
+ });
1716
+ await ctx.db.patch(node, { items: [
1717
+ ...n.items.slice(0, i),
1718
+ rightSibling.items[0],
1719
+ ...n.items.slice(i + 1)
1720
+ ] });
1721
+ return foundItem;
1722
+ }
1723
+ }
1724
+ if (i > 0) {
1725
+ log("merging with left sibling");
1726
+ await mergeNodes(ctx.db, n, i - 1);
1727
+ } else {
1728
+ log("merging with right sibling");
1729
+ await mergeNodes(ctx.db, n, i);
1730
+ }
1731
+ }
1732
+ return foundItem;
1733
+ }
1734
+ async function mergeNodes(db, parent, leftIndex) {
1735
+ const left = await db.get(parent.subtrees[leftIndex]);
1736
+ const right = await db.get(parent.subtrees[leftIndex + 1]);
1737
+ log(`merging ${right._id} into ${left._id}`);
1738
+ await db.patch(left._id, {
1739
+ items: [
1740
+ ...left.items,
1741
+ parent.items[leftIndex],
1742
+ ...right.items
1743
+ ],
1744
+ subtrees: [...left.subtrees, ...right.subtrees],
1745
+ aggregate: left.aggregate && right.aggregate && add(add(left.aggregate, right.aggregate), itemAggregate(parent.items[leftIndex]))
1746
+ });
1747
+ await db.patch(parent._id, {
1748
+ items: [...parent.items.slice(0, leftIndex), ...parent.items.slice(leftIndex + 1)],
1749
+ subtrees: [...parent.subtrees.slice(0, leftIndex + 1), ...parent.subtrees.slice(leftIndex + 2)]
1750
+ });
1751
+ await db.delete(right._id);
1752
+ }
1753
+ async function negativeOffsetInNode(db, node, index, k1, k2) {
1754
+ const filtered = await filterBetween(db, node, k1, k2);
1755
+ for (const included of filtered.reverse()) if (included.type === "item") {
1756
+ if (index === 0) return included.item;
1757
+ index -= 1;
1758
+ } else {
1759
+ const subtreeCount = (await nodeAggregate(db, await db.get(included.subtree))).count;
1760
+ if (index < subtreeCount) return await negativeOffsetInNode(db, included.subtree, index);
1761
+ index -= subtreeCount;
1762
+ }
1763
+ throw new ConvexError(`negative offset exceeded count by ${index} (in node ${node})`);
1764
+ }
1765
+ async function atOffsetInNode(db, node, index, k1, k2) {
1766
+ const filtered = await filterBetween(db, node, k1, k2);
1767
+ for (const included of filtered) if (included.type === "item") {
1768
+ if (index === 0) return included.item;
1769
+ index -= 1;
1770
+ } else {
1771
+ const subtreeCount = (await nodeAggregate(db, await db.get(included.subtree))).count;
1772
+ if (index < subtreeCount) return await atOffsetInNode(db, included.subtree, index);
1773
+ index -= subtreeCount;
1774
+ }
1775
+ throw new ConvexError(`offset exceeded count by ${index} (in node ${node})`);
1776
+ }
1777
+ function itemAggregate(item) {
1778
+ return {
1779
+ count: 1,
1780
+ sum: item.s
1781
+ };
1782
+ }
1783
+ function nodeCounts(node) {
1784
+ return node.items.map(itemAggregate);
1785
+ }
1786
+ async function subtreeCounts(db, node) {
1787
+ return await Promise.all(node.subtrees.map(async (subtree) => {
1788
+ return nodeAggregate(db, await db.get(subtree));
1789
+ }));
1790
+ }
1791
+ async function nodeAggregate(db, node) {
1792
+ if (node.aggregate !== void 0) return node.aggregate;
1793
+ const subCounts = await subtreeCounts(db, node);
1794
+ return add(accumulate(nodeCounts(node)), accumulate(subCounts));
1795
+ }
1796
+ function add(a, b) {
1797
+ return {
1798
+ count: a.count + b.count,
1799
+ sum: a.sum + b.sum
1800
+ };
1801
+ }
1802
+ function sub(a, b) {
1803
+ return {
1804
+ count: a.count - b.count,
1805
+ sum: a.sum - b.sum
1806
+ };
1807
+ }
1808
+ function accumulate(nums) {
1809
+ return nums.reduce(add, {
1810
+ count: 0,
1811
+ sum: 0
1812
+ });
1813
+ }
1814
+ async function insertIntoNode(ctx, namespace, node, item) {
1815
+ const n = await ctx.db.get(node);
1816
+ let i = 0;
1817
+ for (; i < n.items.length; i++) {
1818
+ const compare = compareKeys(item.k, n.items[i].k);
1819
+ if (compare === -1) break;
1820
+ if (compare === 0) throw new ConvexError(`key ${p(item.k)} already exists in node ${n._id}`);
1821
+ }
1822
+ if (n.subtrees.length > 0) {
1823
+ const pushUp = await insertIntoNode(ctx, namespace, n.subtrees[i], item);
1824
+ if (pushUp) await ctx.db.patch(node, {
1825
+ items: [
1826
+ ...n.items.slice(0, i),
1827
+ pushUp.item,
1828
+ ...n.items.slice(i)
1829
+ ],
1830
+ subtrees: [
1831
+ ...n.subtrees.slice(0, i),
1832
+ pushUp.leftSubtree,
1833
+ pushUp.rightSubtree,
1834
+ ...n.subtrees.slice(i + 1)
1835
+ ]
1836
+ });
1837
+ } else await ctx.db.patch(node, { items: [
1838
+ ...n.items.slice(0, i),
1839
+ item,
1840
+ ...n.items.slice(i)
1841
+ ] });
1842
+ const newAggregate = n.aggregate && add(n.aggregate, itemAggregate(item));
1843
+ if (newAggregate) await ctx.db.patch(node, { aggregate: newAggregate });
1844
+ const newN = await ctx.db.get(node);
1845
+ const maxNodeSize = await MAX_NODE_SIZE(ctx, namespace);
1846
+ const minNodeSize = await MIN_NODE_SIZE(ctx, namespace);
1847
+ if (newN.items.length > maxNodeSize) {
1848
+ if (newN.items.length !== maxNodeSize + 1 || newN.items.length !== 2 * minNodeSize + 1) throw new Error(`bad ${newN.items.length}`);
1849
+ log(`splitting node ${newN._id} at ${newN.items[minNodeSize].k}`);
1850
+ const topLevel = nodeCounts(newN);
1851
+ const subCounts = await subtreeCounts(ctx.db, newN);
1852
+ const leftCount = add(accumulate(topLevel.slice(0, minNodeSize)), accumulate(subCounts.length ? subCounts.slice(0, minNodeSize + 1) : []));
1853
+ const rightCount = add(accumulate(topLevel.slice(minNodeSize + 1)), accumulate(subCounts.length ? subCounts.slice(minNodeSize + 1) : []));
1854
+ if (newN.aggregate && leftCount.count + rightCount.count + 1 !== newN.aggregate.count) throw new Error(`bad count split ${leftCount.count} ${rightCount.count} ${newN.aggregate.count}`);
1855
+ if (newN.aggregate && Math.abs(leftCount.sum + rightCount.sum + newN.items[minNodeSize].s - newN.aggregate.sum) > 1e-5) throw new Error(`bad sum split ${leftCount.sum} ${rightCount.sum} ${newN.items[minNodeSize].s} ${newN.aggregate.sum}`);
1856
+ await ctx.db.patch(node, {
1857
+ items: newN.items.slice(0, minNodeSize),
1858
+ subtrees: newN.subtrees.length ? newN.subtrees.slice(0, minNodeSize + 1) : [],
1859
+ aggregate: leftCount
1860
+ });
1861
+ const splitN = await ctx.db.insert(AGGREGATE_NODE_TABLE, {
1862
+ items: newN.items.slice(minNodeSize + 1),
1863
+ subtrees: newN.subtrees.length ? newN.subtrees.slice(minNodeSize + 1) : [],
1864
+ aggregate: rightCount
1865
+ });
1866
+ return {
1867
+ item: newN.items[minNodeSize],
1868
+ leftSubtree: node,
1869
+ rightSubtree: splitN,
1870
+ leftSubtreeCount: newN.aggregate && leftCount,
1871
+ rightSubtreeCount: newN.aggregate && rightCount
1872
+ };
1873
+ }
1874
+ return null;
1875
+ }
1876
+ function compareKeys(k1, k2) {
1877
+ return compareValues$1(k1, k2);
1878
+ }
1879
+ async function getTree(db, namespace) {
1880
+ return await db.query(AGGREGATE_TREE_TABLE).withIndex("by_namespace", (q) => q.eq("namespace", namespace)).unique();
1881
+ }
1882
+ async function mustGetTree(db, namespace) {
1883
+ const tree = await getTree(db, namespace);
1884
+ if (!tree) throw new Error("btree not initialized");
1885
+ return tree;
1886
+ }
1887
+ async function getOrCreateTree(db, namespace, maxNodeSize, rootLazy) {
1888
+ const originalTree = await getTree(db, namespace);
1889
+ const aggregateName = aggregateNameFromNamespace(namespace);
1890
+ if (originalTree) {
1891
+ if (originalTree.aggregateName !== aggregateName) {
1892
+ await db.patch(originalTree._id, { aggregateName });
1893
+ return {
1894
+ ...originalTree,
1895
+ aggregateName
1896
+ };
1897
+ }
1898
+ return originalTree;
1899
+ }
1900
+ const root = await db.insert(AGGREGATE_NODE_TABLE, {
1901
+ items: [],
1902
+ subtrees: [],
1903
+ aggregate: {
1904
+ count: 0,
1905
+ sum: 0
1906
+ }
1907
+ });
1908
+ const effectiveMaxNodeSize = maxNodeSize ?? await MAX_NODE_SIZE({ db }, void 0) ?? DEFAULT_MAX_NODE_SIZE;
1909
+ const effectiveRootLazy = rootLazy ?? await isRootLazy(db, void 0) ?? true;
1910
+ const id = await db.insert(AGGREGATE_TREE_TABLE, {
1911
+ aggregateName,
1912
+ root,
1913
+ maxNodeSize: effectiveMaxNodeSize,
1914
+ namespace
1915
+ });
1916
+ const newTree = await db.get(id);
1917
+ await MIN_NODE_SIZE({ db }, namespace);
1918
+ if (effectiveRootLazy) await db.patch(root, { aggregate: void 0 });
1919
+ return newTree;
1920
+ }
1921
+ async function isRootLazy(db, namespace) {
1922
+ const tree = await getTree(db, namespace);
1923
+ if (!tree) return true;
1924
+ return (await db.get(tree.root))?.aggregate === void 0;
1925
+ }
1926
+ async function deleteTreeNodes(db, node) {
1927
+ const current = await db.get(node);
1928
+ if (!current) return;
1929
+ for (const subtree of current.subtrees) await deleteTreeNodes(db, subtree);
1930
+ await db.delete(node);
1931
+ }
1932
+ async function clearTree(db, args) {
1933
+ const tree = await getTree(db, args.namespace);
1934
+ let existingRootLazy = true;
1935
+ let existingMaxNodeSize = DEFAULT_MAX_NODE_SIZE;
1936
+ if (tree) {
1937
+ await db.delete(tree._id);
1938
+ const root = await db.get(tree.root);
1939
+ if (root) {
1940
+ existingRootLazy = root.aggregate === void 0;
1941
+ await deleteTreeNodes(db, tree.root);
1942
+ }
1943
+ existingMaxNodeSize = tree.maxNodeSize;
1944
+ }
1945
+ await getOrCreateTree(db, args.namespace, args.maxNodeSize ?? existingMaxNodeSize, args.rootLazy ?? existingRootLazy);
1946
+ }
1947
+ async function paginateHandler(ctx, args) {
1948
+ const tree = await getTree(ctx.db, args.namespace);
1949
+ if (tree === null) return {
1950
+ page: [],
1951
+ cursor: "",
1952
+ isDone: true
1953
+ };
1954
+ return await paginateInNode(ctx.db, tree.root, args.limit, args.order, args.cursor, args.k1, args.k2);
1955
+ }
1956
+ async function paginateInNode(db, node, limit, order, cursor, k1, k2) {
1957
+ if (limit <= 0) throw new ConvexError("limit must be positive");
1958
+ if (cursor !== void 0 && cursor.length === 0) return {
1959
+ page: [],
1960
+ cursor: "",
1961
+ isDone: true
1962
+ };
1963
+ const items = [];
1964
+ const filtered = await filterBetween(db, node, cursor === void 0 || order === "desc" ? k1 : jsonToConvex(JSON.parse(cursor)), cursor === void 0 || order === "asc" ? k2 : jsonToConvex(JSON.parse(cursor)));
1965
+ if (order === "desc") filtered.reverse();
1966
+ for (const included of filtered) {
1967
+ if (items.length >= limit) return {
1968
+ page: items,
1969
+ cursor: JSON.stringify(convexToJson(items[items.length - 1].k)),
1970
+ isDone: false
1971
+ };
1972
+ if (included.type === "item") items.push(included.item);
1973
+ else {
1974
+ const { page, cursor: newCursor, isDone } = await paginateInNode(db, included.subtree, limit - items.length, order);
1975
+ items.push(...page);
1976
+ if (!isDone) return {
1977
+ page: items,
1978
+ cursor: newCursor,
1979
+ isDone: false
1980
+ };
1981
+ }
1982
+ }
1983
+ return {
1984
+ page: items,
1985
+ cursor: "",
1986
+ isDone: true
1987
+ };
1988
+ }
1989
+ async function paginateNamespacesHandler(ctx, args) {
1990
+ if (args.cursor === "endcursor") return {
1991
+ page: [],
1992
+ cursor: "endcursor",
1993
+ isDone: true
1994
+ };
1995
+ const { page: trees, continueCursor, isDone } = await (args.aggregateName === void 0 ? ctx.db.query(AGGREGATE_TREE_TABLE) : ctx.db.query(AGGREGATE_TREE_TABLE).withIndex("by_aggregate_name", (q) => q.eq("aggregateName", args.aggregateName))).paginate({
1996
+ cursor: args.cursor ?? null,
1997
+ numItems: args.limit
1998
+ });
1999
+ return {
2000
+ page: trees.map((t) => t.namespace ?? null),
2001
+ cursor: isDone ? "endcursor" : continueCursor ?? "endcursor",
2002
+ isDone
2003
+ };
2004
+ }
2005
+ async function aggregateBetweenBatchHandler(ctx, args) {
2006
+ return await Promise.all(args.queries.map((query) => aggregateBetweenHandler(ctx, query)));
2007
+ }
2008
+ async function atOffsetBatchHandler(ctx, args) {
2009
+ return await Promise.all(args.queries.map((query) => query.offset >= 0 ? atOffsetHandler(ctx, query) : atNegativeOffsetHandler(ctx, {
2010
+ ...query,
2011
+ offset: -query.offset - 1
2012
+ })));
2013
+ }
2014
+
2015
+ //#endregion
2016
+ //#region src/aggregate-core/positions.ts
2017
+ const BEFORE_ALL_IDS = null;
2018
+ const AFTER_ALL_IDS = [];
2019
+ function explodeKey(key) {
2020
+ if (Array.isArray(key)) {
2021
+ const exploded = [""];
2022
+ for (const item of key) {
2023
+ exploded.push(item);
2024
+ exploded.push("");
2025
+ }
2026
+ return exploded;
2027
+ }
2028
+ return key;
2029
+ }
2030
+ function implodeKey(k) {
2031
+ if (Array.isArray(k)) {
2032
+ const imploded = [];
2033
+ for (let i = 1; i < k.length; i += 2) imploded.push(k[i]);
2034
+ return imploded;
2035
+ }
2036
+ return k;
2037
+ }
2038
+ function keyToPosition(key, id) {
2039
+ return [
2040
+ explodeKey(key),
2041
+ id,
2042
+ ""
2043
+ ];
2044
+ }
2045
+ function positionToKey(position) {
2046
+ return {
2047
+ key: implodeKey(position[0]),
2048
+ id: position[1]
2049
+ };
2050
+ }
2051
+ function boundsToPositions(bounds) {
2052
+ if (bounds === void 0) return {};
2053
+ if ("eq" in bounds) return {
2054
+ k1: boundToPosition("lower", {
2055
+ key: bounds.eq,
2056
+ inclusive: true
2057
+ }),
2058
+ k2: boundToPosition("upper", {
2059
+ key: bounds.eq,
2060
+ inclusive: true
2061
+ })
2062
+ };
2063
+ if ("prefix" in bounds) {
2064
+ const prefix = bounds.prefix;
2065
+ const exploded = [];
2066
+ for (const item of prefix) {
2067
+ exploded.push("");
2068
+ exploded.push(item);
2069
+ }
2070
+ return {
2071
+ k1: [
2072
+ exploded.concat([BEFORE_ALL_IDS]),
2073
+ BEFORE_ALL_IDS,
2074
+ BEFORE_ALL_IDS
2075
+ ],
2076
+ k2: [
2077
+ exploded.concat([AFTER_ALL_IDS]),
2078
+ AFTER_ALL_IDS,
2079
+ AFTER_ALL_IDS
2080
+ ]
2081
+ };
2082
+ }
2083
+ return {
2084
+ k1: boundToPosition("lower", bounds.lower),
2085
+ k2: boundToPosition("upper", bounds.upper)
2086
+ };
2087
+ }
2088
+ function boundToPosition(direction, bound) {
2089
+ if (bound === void 0) return;
2090
+ if (direction === "lower") return [
2091
+ explodeKey(bound.key),
2092
+ bound.id ?? (bound.inclusive ? BEFORE_ALL_IDS : AFTER_ALL_IDS),
2093
+ bound.inclusive ? BEFORE_ALL_IDS : AFTER_ALL_IDS
2094
+ ];
2095
+ return [
2096
+ explodeKey(bound.key),
2097
+ bound.id ?? (bound.inclusive ? AFTER_ALL_IDS : BEFORE_ALL_IDS),
2098
+ bound.inclusive ? AFTER_ALL_IDS : BEFORE_ALL_IDS
2099
+ ];
2100
+ }
2101
+
2102
+ //#endregion
2103
+ //#region src/aggregate-core/runtime.ts
2104
+ const INTERNAL_NAMESPACE_MARKER_MISSING = 0;
2105
+ const INTERNAL_NAMESPACE_MARKER_PRESENT = 1;
2106
+ const encodeNamespace = (aggregateName, namespace) => [
2107
+ aggregateName,
2108
+ namespace === void 0 ? null : namespace,
2109
+ namespace === void 0 ? INTERNAL_NAMESPACE_MARKER_MISSING : INTERNAL_NAMESPACE_MARKER_PRESENT
2110
+ ];
2111
+ const isInternalNamespace = (value) => Array.isArray(value) && value.length === 3 && typeof value[0] === "string" && (value[2] === INTERNAL_NAMESPACE_MARKER_MISSING || value[2] === INTERNAL_NAMESPACE_MARKER_PRESENT);
2112
+ const decodeNamespace = (namespace) => {
2113
+ if (namespace[2] === INTERNAL_NAMESPACE_MARKER_MISSING) return;
2114
+ return namespace[1];
2115
+ };
2116
+ const namespaceForOpts = (aggregateName, opts) => encodeNamespace(aggregateName, namespaceFromOpts(opts));
2117
+ const namespaceForArg = (aggregateName, args) => encodeNamespace(aggregateName, namespaceFromArg(args));
2118
+ /**
2119
+ * Write data to be aggregated, and read aggregated data.
2120
+ */
2121
+ var Aggregate = class {
2122
+ constructor(aggregateName) {
2123
+ this.aggregateName = aggregateName;
2124
+ }
2125
+ async count(ctx, ...opts) {
2126
+ return (await aggregateBetweenHandler({ db: ctx.db }, {
2127
+ ...boundsToPositions(opts[0]?.bounds),
2128
+ namespace: namespaceForOpts(this.aggregateName, opts)
2129
+ })).count;
2130
+ }
2131
+ async countBatch(ctx, queries) {
2132
+ return (await aggregateBetweenBatchHandler({ db: ctx.db }, { queries: queries.map((query) => {
2133
+ if (!query) throw new Error("You must pass bounds and/or namespace");
2134
+ return {
2135
+ ...boundsToPositions(query.bounds),
2136
+ namespace: namespaceForArg(this.aggregateName, query)
2137
+ };
2138
+ }) })).map((result) => result.count);
2139
+ }
2140
+ async sum(ctx, ...opts) {
2141
+ return (await aggregateBetweenHandler({ db: ctx.db }, {
2142
+ ...boundsToPositions(opts[0]?.bounds),
2143
+ namespace: namespaceForOpts(this.aggregateName, opts)
2144
+ })).sum;
2145
+ }
2146
+ async sumBatch(ctx, queries) {
2147
+ return (await aggregateBetweenBatchHandler({ db: ctx.db }, { queries: queries.map((query) => {
2148
+ if (!query) throw new Error("You must pass bounds and/or namespace");
2149
+ return {
2150
+ ...boundsToPositions(query.bounds),
2151
+ namespace: namespaceForArg(this.aggregateName, query)
2152
+ };
2153
+ }) })).map((result) => result.sum);
2154
+ }
2155
+ async at(ctx, offset, ...opts) {
2156
+ const encodedNamespace = namespaceForOpts(this.aggregateName, opts);
2157
+ return btreeItemToAggregateItem(offset < 0 ? await atNegativeOffsetHandler({ db: ctx.db }, {
2158
+ ...boundsToPositions(opts[0]?.bounds),
2159
+ namespace: encodedNamespace,
2160
+ offset: -offset - 1
2161
+ }) : await atOffsetHandler({ db: ctx.db }, {
2162
+ ...boundsToPositions(opts[0]?.bounds),
2163
+ namespace: encodedNamespace,
2164
+ offset
2165
+ }));
2166
+ }
2167
+ async atBatch(ctx, queries) {
2168
+ return (await atOffsetBatchHandler({ db: ctx.db }, { queries: queries.map((query) => ({
2169
+ ...boundsToPositions(query.bounds),
2170
+ namespace: namespaceForArg(this.aggregateName, query),
2171
+ offset: query.offset
2172
+ })) })).map(btreeItemToAggregateItem);
2173
+ }
2174
+ async indexOf(ctx, key, ...opts) {
2175
+ const { k1, k2 } = boundsToPositions(opts[0]?.bounds);
2176
+ const namespace = namespaceForOpts(this.aggregateName, opts);
2177
+ if (opts[0]?.order === "desc") return offsetUntilHandler({ db: ctx.db }, {
2178
+ k2,
2179
+ key: boundToPosition("upper", {
2180
+ id: opts[0]?.id,
2181
+ inclusive: true,
2182
+ key
2183
+ }),
2184
+ namespace
2185
+ });
2186
+ return offsetHandler({ db: ctx.db }, {
2187
+ k1,
2188
+ key: boundToPosition("lower", {
2189
+ id: opts[0]?.id,
2190
+ inclusive: true,
2191
+ key
2192
+ }),
2193
+ namespace
2194
+ });
2195
+ }
2196
+ async offsetOf(ctx, key, namespace, id, bounds) {
2197
+ return this.indexOf(ctx, key, {
2198
+ bounds,
2199
+ id,
2200
+ namespace,
2201
+ order: "asc"
2202
+ });
2203
+ }
2204
+ async offsetUntil(ctx, key, namespace, id, bounds) {
2205
+ return this.indexOf(ctx, key, {
2206
+ bounds,
2207
+ id,
2208
+ namespace,
2209
+ order: "desc"
2210
+ });
2211
+ }
2212
+ async min(ctx, ...opts) {
2213
+ const { page } = await this.paginate(ctx, {
2214
+ bounds: opts[0]?.bounds,
2215
+ namespace: namespaceFromOpts(opts),
2216
+ order: "asc",
2217
+ pageSize: 1
2218
+ });
2219
+ return page[0] ?? null;
2220
+ }
2221
+ async max(ctx, ...opts) {
2222
+ const { page } = await this.paginate(ctx, {
2223
+ bounds: opts[0]?.bounds,
2224
+ namespace: namespaceFromOpts(opts),
2225
+ order: "desc",
2226
+ pageSize: 1
2227
+ });
2228
+ return page[0] ?? null;
2229
+ }
2230
+ async random(ctx, ...opts) {
2231
+ const count = await this.count(ctx, ...opts);
2232
+ if (count === 0) return null;
2233
+ return this.at(ctx, Math.floor(Math.random() * count), ...opts);
2234
+ }
2235
+ async paginate(ctx, ...opts) {
2236
+ const result = await paginateHandler({ db: ctx.db }, {
2237
+ ...boundsToPositions(opts[0]?.bounds),
2238
+ cursor: opts[0]?.cursor,
2239
+ limit: opts[0]?.pageSize ?? 100,
2240
+ namespace: namespaceForOpts(this.aggregateName, opts),
2241
+ order: opts[0]?.order ?? "asc"
2242
+ });
2243
+ return {
2244
+ cursor: result.cursor,
2245
+ isDone: result.isDone,
2246
+ page: result.page.map(btreeItemToAggregateItem)
2247
+ };
2248
+ }
2249
+ async *iter(ctx, ...opts) {
2250
+ const bounds = opts[0]?.bounds;
2251
+ const namespace = namespaceFromOpts(opts);
2252
+ const order = opts[0]?.order ?? "asc";
2253
+ const pageSize = opts[0]?.pageSize ?? 100;
2254
+ let cursor;
2255
+ let isDone = false;
2256
+ while (!isDone) {
2257
+ const page = await this.paginate(ctx, {
2258
+ bounds,
2259
+ cursor,
2260
+ namespace,
2261
+ order,
2262
+ pageSize
2263
+ });
2264
+ for (const item of page.page) yield item;
2265
+ cursor = page.cursor;
2266
+ isDone = page.isDone;
2267
+ }
2268
+ }
2269
+ async _insert(ctx, namespace, key, id, summand) {
2270
+ await insertHandler({ db: ctx.db }, {
2271
+ key: keyToPosition(key, id),
2272
+ namespace: namespaceForArg(this.aggregateName, { namespace }),
2273
+ summand,
2274
+ value: id
2275
+ });
2276
+ }
2277
+ async _delete(ctx, namespace, key, id) {
2278
+ await deleteHandler({ db: ctx.db }, {
2279
+ key: keyToPosition(key, id),
2280
+ namespace: namespaceForArg(this.aggregateName, { namespace })
2281
+ });
2282
+ }
2283
+ async _replace(ctx, currentNamespace, currentKey, newNamespace, newKey, id, summand) {
2284
+ await deleteHandler({ db: ctx.db }, {
2285
+ key: keyToPosition(currentKey, id),
2286
+ namespace: namespaceForArg(this.aggregateName, { namespace: currentNamespace })
2287
+ });
2288
+ await insertHandler({ db: ctx.db }, {
2289
+ key: keyToPosition(newKey, id),
2290
+ namespace: namespaceForArg(this.aggregateName, { namespace: newNamespace }),
2291
+ summand,
2292
+ value: id
2293
+ });
2294
+ }
2295
+ async _insertIfDoesNotExist(ctx, namespace, key, id, summand) {
2296
+ await this._replaceOrInsert(ctx, namespace, key, namespace, key, id, summand);
2297
+ }
2298
+ async _deleteIfExists(ctx, namespace, key, id) {
2299
+ try {
2300
+ await this._delete(ctx, namespace, key, id);
2301
+ } catch (error) {
2302
+ if (error instanceof ConvexError && error.data?.code === "DELETE_MISSING_KEY") return;
2303
+ throw error;
2304
+ }
2305
+ }
2306
+ async _replaceOrInsert(ctx, currentNamespace, currentKey, newNamespace, newKey, id, summand) {
2307
+ try {
2308
+ await this._delete(ctx, currentNamespace, currentKey, id);
2309
+ } catch (error) {
2310
+ if (!(error instanceof ConvexError && error.data?.code === "DELETE_MISSING_KEY")) throw error;
2311
+ }
2312
+ await this._insert(ctx, newNamespace, newKey, id, summand);
2313
+ }
2314
+ async clear(ctx, ...opts) {
2315
+ await clearTree(ctx.db, {
2316
+ maxNodeSize: opts[0]?.maxNodeSize,
2317
+ namespace: namespaceForOpts(this.aggregateName, opts),
2318
+ rootLazy: opts[0]?.rootLazy
2319
+ });
2320
+ }
2321
+ async makeRootLazy(ctx, namespace) {
2322
+ const tree = await getOrCreateTree(ctx.db, namespaceForArg(this.aggregateName, { namespace }));
2323
+ await ctx.db.patch(tree.root, { aggregate: void 0 });
2324
+ }
2325
+ async paginateNamespaces(ctx, cursor, pageSize = 100) {
2326
+ const result = await paginateNamespacesHandler({ db: ctx.db }, {
2327
+ aggregateName: this.aggregateName,
2328
+ cursor,
2329
+ limit: pageSize
2330
+ });
2331
+ const page = [];
2332
+ for (const namespace of result.page) {
2333
+ if (!isInternalNamespace(namespace)) continue;
2334
+ if (namespace[0] !== this.aggregateName) continue;
2335
+ page.push(decodeNamespace(namespace));
2336
+ }
2337
+ return {
2338
+ cursor: result.cursor,
2339
+ isDone: result.isDone,
2340
+ page
2341
+ };
2342
+ }
2343
+ async *iterNamespaces(ctx, pageSize = 100) {
2344
+ let cursor;
2345
+ let isDone = false;
2346
+ while (!isDone) {
2347
+ const page = await this.paginateNamespaces(ctx, cursor, pageSize);
2348
+ for (const namespace of page.page) yield namespace;
2349
+ cursor = page.cursor;
2350
+ isDone = page.isDone;
2351
+ }
2352
+ }
2353
+ async clearAll(ctx, opts) {
2354
+ for await (const namespace of this.iterNamespaces(ctx)) await this.clear(ctx, {
2355
+ ...opts,
2356
+ namespace
2357
+ });
2358
+ await this.clear(ctx, {
2359
+ ...opts,
2360
+ namespace: void 0
2361
+ });
2362
+ }
2363
+ async makeAllRootsLazy(ctx) {
2364
+ for await (const namespace of this.iterNamespaces(ctx)) await this.makeRootLazy(ctx, namespace);
2365
+ }
2366
+ };
2367
+ var DirectAggregate = class extends Aggregate {
2368
+ constructor(config) {
2369
+ super(config.name);
2370
+ }
2371
+ async insert(ctx, args) {
2372
+ await this._insert(ctx, namespaceFromArg(args), args.key, args.id, args.sumValue);
2373
+ }
2374
+ async delete(ctx, args) {
2375
+ await this._delete(ctx, namespaceFromArg(args), args.key, args.id);
2376
+ }
2377
+ async replace(ctx, currentItem, newItem) {
2378
+ await this._replace(ctx, namespaceFromArg(currentItem), currentItem.key, namespaceFromArg(newItem), newItem.key, currentItem.id, newItem.sumValue);
2379
+ }
2380
+ async insertIfDoesNotExist(ctx, args) {
2381
+ await this._insertIfDoesNotExist(ctx, namespaceFromArg(args), args.key, args.id, args.sumValue);
2382
+ }
2383
+ async deleteIfExists(ctx, args) {
2384
+ await this._deleteIfExists(ctx, namespaceFromArg(args), args.key, args.id);
2385
+ }
2386
+ async replaceOrInsert(ctx, currentItem, newItem) {
2387
+ await this._replaceOrInsert(ctx, namespaceFromArg(currentItem), currentItem.key, namespaceFromArg(newItem), newItem.key, currentItem.id, newItem.sumValue);
2388
+ }
2389
+ };
2390
+ var TableAggregate = class extends Aggregate {
2391
+ constructor(options) {
2392
+ super(options.name);
2393
+ this.options = options;
2394
+ }
2395
+ options;
2396
+ async insert(ctx, doc) {
2397
+ await this._insert(ctx, this.options.namespace?.(doc), this.options.sortKey(doc), doc._id, this.options.sumValue?.(doc));
2398
+ }
2399
+ async delete(ctx, doc) {
2400
+ await this._delete(ctx, this.options.namespace?.(doc), this.options.sortKey(doc), doc._id);
2401
+ }
2402
+ async replace(ctx, oldDoc, newDoc) {
2403
+ await this._replace(ctx, this.options.namespace?.(oldDoc), this.options.sortKey(oldDoc), this.options.namespace?.(newDoc), this.options.sortKey(newDoc), newDoc._id, this.options.sumValue?.(newDoc));
2404
+ }
2405
+ async insertIfDoesNotExist(ctx, doc) {
2406
+ await this._insertIfDoesNotExist(ctx, this.options.namespace?.(doc), this.options.sortKey(doc), doc._id, this.options.sumValue?.(doc));
2407
+ }
2408
+ async deleteIfExists(ctx, doc) {
2409
+ await this._deleteIfExists(ctx, this.options.namespace?.(doc), this.options.sortKey(doc), doc._id);
2410
+ }
2411
+ async replaceOrInsert(ctx, oldDoc, newDoc) {
2412
+ await this._replaceOrInsert(ctx, this.options.namespace?.(oldDoc), this.options.sortKey(oldDoc), this.options.namespace?.(newDoc), this.options.sortKey(newDoc), newDoc._id, this.options.sumValue?.(newDoc));
2413
+ }
2414
+ async indexOfDoc(ctx, doc, opts) {
2415
+ return this.indexOf(ctx, this.options.sortKey(doc), {
2416
+ namespace: this.options.namespace?.(doc),
2417
+ ...opts
2418
+ });
2419
+ }
2420
+ trigger() {
2421
+ return async (ctx, change) => {
2422
+ if (change.operation === "insert") await this.insert(ctx, change.newDoc);
2423
+ else if (change.operation === "update") await this.replace(ctx, change.oldDoc, change.newDoc);
2424
+ else if (change.operation === "delete") await this.delete(ctx, change.oldDoc);
2425
+ };
2426
+ }
2427
+ idempotentTrigger() {
2428
+ return async (ctx, change) => {
2429
+ if (change.operation === "insert") await this.insertIfDoesNotExist(ctx, change.newDoc);
2430
+ else if (change.operation === "update") await this.replaceOrInsert(ctx, change.oldDoc, change.newDoc);
2431
+ else if (change.operation === "delete") await this.deleteIfExists(ctx, change.oldDoc);
2432
+ };
2433
+ }
2434
+ };
2435
+ function btreeItemToAggregateItem({ k, s }) {
2436
+ const { key, id } = positionToKey(k);
2437
+ return {
2438
+ id,
2439
+ key,
2440
+ sumValue: s
2441
+ };
2442
+ }
2443
+ function namespaceFromArg(args) {
2444
+ if ("namespace" in args) return args.namespace;
2445
+ }
2446
+ function namespaceFromOpts(opts) {
2447
+ if (opts.length === 0) return;
2448
+ const [{ namespace }] = opts;
2449
+ return namespace;
2450
+ }
2451
+
2452
+ //#endregion
2453
+ export { vectorIndex as A, ConvexColumnBuilder as B, RlsPolicy as C, rankIndex as D, index as E, arrayOf as F, custom as I, json as L, createSystemFields as M, integer as N, searchIndex as O, id as P, objectOf as R, TablePolymorphic as S, aggregateIndex as T, entityKind as V, OrmSchemaRelations as _, deletion as a, TableDeleteConfig as b, Columns as c, OrmSchemaDefinition as d, OrmSchemaExtensionRelations as f, OrmSchemaOptions as g, OrmSchemaExtensions as h, convexTable as i, text as j, uniqueIndex as k, EnableRLS as l, OrmSchemaExtensionTriggers as m, TableAggregate as n, discriminator as o, OrmSchemaExtensionTables as p, aggregateStorageTables as r, Brand as s, DirectAggregate as t, OrmContext as u, OrmSchemaTriggers as v, rlsPolicy as w, TableName as x, RlsPolicies as y, unionOf as z };