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,1518 @@
1
+ import { compareValues, convexToJson, jsonToConvex } from "convex/values";
2
+
3
+ //#region src/orm/filter-expression.ts
4
+ /**
5
+ * Unique symbol for FilterExpression brand
6
+ * Prevents structural typing - only expressions created by factory functions are valid
7
+ */
8
+ const FilterExpressionBrand = Symbol("FilterExpression");
9
+ /**
10
+ * Create a field reference
11
+ * Used internally by operator functions
12
+ */
13
+ function fieldRef(fieldName) {
14
+ return {
15
+ __brand: "FieldReference",
16
+ fieldName
17
+ };
18
+ }
19
+ /**
20
+ * Type guard for FieldReference
21
+ */
22
+ function isFieldReference(value) {
23
+ return value && typeof value === "object" && value.__brand === "FieldReference";
24
+ }
25
+ /**
26
+ * Create a column wrapper
27
+ * Used internally by query builder's _createColumnProxies
28
+ */
29
+ function column(builder, columnName) {
30
+ return {
31
+ builder,
32
+ columnName
33
+ };
34
+ }
35
+ function resolveColumn(col) {
36
+ if (col && typeof col === "object" && "columnName" in col) return col;
37
+ if (isFieldReference(col)) return {
38
+ builder: col,
39
+ columnName: col.fieldName
40
+ };
41
+ const builder = col;
42
+ const columnName = builder?.config?.name;
43
+ if (!columnName) throw new Error("Column builder is missing a column name");
44
+ return column(builder, columnName);
45
+ }
46
+ /**
47
+ * Internal binary expression implementation
48
+ * Private class - only accessible via factory functions
49
+ */
50
+ var BinaryExpressionImpl = class {
51
+ [FilterExpressionBrand] = true;
52
+ type = "binary";
53
+ constructor(operator, operands) {
54
+ this.operator = operator;
55
+ this.operands = operands;
56
+ }
57
+ accept(visitor) {
58
+ return visitor.visitBinary(this);
59
+ }
60
+ };
61
+ /**
62
+ * Internal logical expression implementation
63
+ * Private class - only accessible via factory functions
64
+ */
65
+ var LogicalExpressionImpl = class {
66
+ [FilterExpressionBrand] = true;
67
+ type = "logical";
68
+ constructor(operator, operands) {
69
+ this.operator = operator;
70
+ this.operands = operands;
71
+ }
72
+ accept(visitor) {
73
+ return visitor.visitLogical(this);
74
+ }
75
+ };
76
+ /**
77
+ * Internal unary expression implementation
78
+ * Private class - only accessible via factory functions
79
+ */
80
+ var UnaryExpressionImpl = class {
81
+ [FilterExpressionBrand] = true;
82
+ type = "unary";
83
+ constructor(operator, operands) {
84
+ this.operator = operator;
85
+ this.operands = operands;
86
+ }
87
+ accept(visitor) {
88
+ return visitor.visitUnary(this);
89
+ }
90
+ };
91
+ /**
92
+ * Equality operator: field == value
93
+ *
94
+ * @example
95
+ * const filter = eq(cols.name, 'Alice');
96
+ */
97
+ function eq(col, value) {
98
+ return new BinaryExpressionImpl("eq", [fieldRef(resolveColumn(col).columnName), value]);
99
+ }
100
+ /**
101
+ * Not equal operator: field != value
102
+ */
103
+ function ne(col, value) {
104
+ return new BinaryExpressionImpl("ne", [fieldRef(resolveColumn(col).columnName), value]);
105
+ }
106
+ /**
107
+ * Greater than operator: field > value
108
+ */
109
+ function gt(col, value) {
110
+ return new BinaryExpressionImpl("gt", [fieldRef(resolveColumn(col).columnName), value]);
111
+ }
112
+ /**
113
+ * Greater than or equal operator: field >= value
114
+ */
115
+ function gte(col, value) {
116
+ return new BinaryExpressionImpl("gte", [fieldRef(resolveColumn(col).columnName), value]);
117
+ }
118
+ /**
119
+ * Less than operator: field < value
120
+ */
121
+ function lt(col, value) {
122
+ return new BinaryExpressionImpl("lt", [fieldRef(resolveColumn(col).columnName), value]);
123
+ }
124
+ /**
125
+ * Less than or equal operator: field <= value
126
+ */
127
+ function lte(col, value) {
128
+ return new BinaryExpressionImpl("lte", [fieldRef(resolveColumn(col).columnName), value]);
129
+ }
130
+ /**
131
+ * Between operator: field BETWEEN min AND max (inclusive)
132
+ *
133
+ * Sugar for and(gte(field, min), lte(field, max)).
134
+ */
135
+ function between(col, min, max) {
136
+ return and(gte(col, min), lte(col, max));
137
+ }
138
+ /**
139
+ * Not between operator: field < min OR field > max
140
+ *
141
+ * Sugar for or(lt(field, min), gt(field, max)).
142
+ */
143
+ function notBetween(col, min, max) {
144
+ return or(lt(col, min), gt(col, max));
145
+ }
146
+ /**
147
+ * LIKE operator: SQL-style pattern matching with % wildcards
148
+ * Note: Implemented as post-filter (Convex has no native LIKE)
149
+ *
150
+ * @example
151
+ * const users = await db.query.users.findMany({
152
+ * where: like(users.name, '%alice%'),
153
+ * });
154
+ */
155
+ function like(col, pattern) {
156
+ return new BinaryExpressionImpl("like", [fieldRef(resolveColumn(col).columnName), pattern]);
157
+ }
158
+ /**
159
+ * ILIKE operator: Case-insensitive LIKE
160
+ * Note: Implemented as post-filter (Convex has no native LIKE)
161
+ *
162
+ * @example
163
+ * const users = await db.query.users.findMany({
164
+ * where: ilike(users.name, '%ALICE%'),
165
+ * });
166
+ */
167
+ function ilike(col, pattern) {
168
+ return new BinaryExpressionImpl("ilike", [fieldRef(resolveColumn(col).columnName), pattern]);
169
+ }
170
+ /**
171
+ * NOT LIKE operator: Negated LIKE pattern
172
+ */
173
+ function notLike(col, pattern) {
174
+ return new BinaryExpressionImpl("notLike", [fieldRef(resolveColumn(col).columnName), pattern]);
175
+ }
176
+ /**
177
+ * NOT ILIKE operator: Negated case-insensitive LIKE
178
+ */
179
+ function notIlike(col, pattern) {
180
+ return new BinaryExpressionImpl("notIlike", [fieldRef(resolveColumn(col).columnName), pattern]);
181
+ }
182
+ /**
183
+ * startsWith operator: Check if string starts with prefix
184
+ * Optimized for prefix matching
185
+ *
186
+ * @example
187
+ * const users = await db.query.users.findMany({
188
+ * where: startsWith(users.email, 'admin@'),
189
+ * });
190
+ */
191
+ function startsWith(col, prefix) {
192
+ return new BinaryExpressionImpl("startsWith", [fieldRef(resolveColumn(col).columnName), prefix]);
193
+ }
194
+ /**
195
+ * endsWith operator: Check if string ends with suffix
196
+ *
197
+ * @example
198
+ * const users = await db.query.users.findMany({
199
+ * where: endsWith(users.email, '@example.com'),
200
+ * });
201
+ */
202
+ function endsWith(col, suffix) {
203
+ return new BinaryExpressionImpl("endsWith", [fieldRef(resolveColumn(col).columnName), suffix]);
204
+ }
205
+ /**
206
+ * contains operator: Check if string contains substring
207
+ * Can use search index for optimization when available
208
+ *
209
+ * @example
210
+ * const posts = await db.query.posts.findMany({
211
+ * where: contains(posts.title, 'javascript'),
212
+ * });
213
+ */
214
+ function contains(col, substring) {
215
+ return new BinaryExpressionImpl("contains", [fieldRef(resolveColumn(col).columnName), substring]);
216
+ }
217
+ /**
218
+ * Logical AND: all expressions must be true
219
+ * Filters out undefined expressions (following Drizzle pattern)
220
+ *
221
+ * @example
222
+ * const filter = and(
223
+ * eq(fieldRef('age'), 25),
224
+ * eq(fieldRef('name'), 'Alice')
225
+ * );
226
+ */
227
+ function and(...expressions) {
228
+ const defined = expressions.filter((expr) => expr !== void 0);
229
+ if (defined.length === 0) return;
230
+ if (defined.length === 1) return defined[0];
231
+ return new LogicalExpressionImpl("and", defined);
232
+ }
233
+ /**
234
+ * Logical OR: at least one expression must be true
235
+ * Filters out undefined expressions (following Drizzle pattern)
236
+ *
237
+ * @example
238
+ * const filter = or(
239
+ * eq(fieldRef('status'), 'active'),
240
+ * eq(fieldRef('status'), 'pending')
241
+ * );
242
+ */
243
+ function or(...expressions) {
244
+ const defined = expressions.filter((expr) => expr !== void 0);
245
+ if (defined.length === 0) return;
246
+ if (defined.length === 1) return defined[0];
247
+ return new LogicalExpressionImpl("or", defined);
248
+ }
249
+ /**
250
+ * Logical NOT: negates expression
251
+ *
252
+ * @example
253
+ * const filter = not(eq(fieldRef('isDeleted'), true));
254
+ */
255
+ function not(expression) {
256
+ return new UnaryExpressionImpl("not", [expression]);
257
+ }
258
+ /**
259
+ * Array membership operator: field IN array
260
+ *
261
+ * @example
262
+ * const filter = inArray(cols.status, ['active', 'pending']);
263
+ */
264
+ function inArray(col, values) {
265
+ return new BinaryExpressionImpl("inArray", [fieldRef(resolveColumn(col).columnName), values]);
266
+ }
267
+ /**
268
+ * Array exclusion operator: field NOT IN array
269
+ * Validates array is non-empty at construction time
270
+ *
271
+ * @example
272
+ * const filter = notInArray(cols.role, ['admin', 'moderator']);
273
+ */
274
+ function notInArray(col, values) {
275
+ if (!Array.isArray(values) || values.length === 0) throw new Error("notInArray requires a non-empty array of values");
276
+ return new BinaryExpressionImpl("notInArray", [fieldRef(resolveColumn(col).columnName), values]);
277
+ }
278
+ /**
279
+ * Array contains operator: field @> array
280
+ */
281
+ function arrayContains(col, values) {
282
+ return new BinaryExpressionImpl("arrayContains", [fieldRef(resolveColumn(col).columnName), values]);
283
+ }
284
+ /**
285
+ * Array contained operator: field <@ array
286
+ */
287
+ function arrayContained(col, values) {
288
+ return new BinaryExpressionImpl("arrayContained", [fieldRef(resolveColumn(col).columnName), values]);
289
+ }
290
+ /**
291
+ * Array overlaps operator: field && array
292
+ */
293
+ function arrayOverlaps(col, values) {
294
+ return new BinaryExpressionImpl("arrayOverlaps", [fieldRef(resolveColumn(col).columnName), values]);
295
+ }
296
+ /**
297
+ * Null check operator: field IS NULL
298
+ * Type validation: Only works with nullable fields
299
+ *
300
+ * @example
301
+ * const filter = isNull(cols.deletedAt);
302
+ */
303
+ function isNull(col) {
304
+ return new UnaryExpressionImpl("isNull", [fieldRef(resolveColumn(col).columnName)]);
305
+ }
306
+ /**
307
+ * Not null check operator: field IS NOT NULL
308
+ * Type validation: Only works with nullable fields
309
+ *
310
+ * @example
311
+ * const filter = isNotNull(cols.deletedAt);
312
+ */
313
+ function isNotNull(col) {
314
+ return new UnaryExpressionImpl("isNotNull", [fieldRef(resolveColumn(col).columnName)]);
315
+ }
316
+
317
+ //#endregion
318
+ //#region src/orm/unset-token.ts
319
+ const unsetToken = Symbol.for("kitcn/orm/unsetToken");
320
+ function isUnsetToken(value) {
321
+ return value === unsetToken;
322
+ }
323
+
324
+ //#endregion
325
+ //#region src/orm/stream.ts
326
+ function makeExclusive(boundType) {
327
+ if (boundType === "gt" || boundType === "gte") return "gt";
328
+ return "lt";
329
+ }
330
+ /** Split a range query between two index keys into a series of range queries
331
+ * that should be executed in sequence. This is necessary because Convex only
332
+ * supports range queries of the form
333
+ * q.eq("f1", v).eq("f2", v).lt("f3", v).gt("f3", v).
334
+ * i.e. all fields must be equal except for the last field, which can have
335
+ * two inequalities.
336
+ *
337
+ * For example, the range from >[1, 2, 3] to <=[1, 3, 2] would be split into
338
+ * the following queries:
339
+ * 1. q.eq("f1", 1).eq("f2", 2).gt("f3", 3)
340
+ * 2. q.eq("f1", 1).gt("f2", 2).lt("f2", 3)
341
+ * 3. q.eq("f1", 1).eq("f2", 3).lte("f3", 2)
342
+ */
343
+ function splitRange(indexFields, order, startBound, endBound, startBoundType, endBoundType) {
344
+ const commonPrefix = [];
345
+ let workingIndexFields = indexFields.slice();
346
+ let workingStartBound = startBound.slice();
347
+ let workingEndBound = endBound.slice();
348
+ let workingStartBoundType = startBoundType;
349
+ let workingEndBoundType = endBoundType;
350
+ while (workingStartBound.length > 0 && workingEndBound.length > 0 && compareValues(workingStartBound[0], workingEndBound[0]) === 0) {
351
+ const indexField = workingIndexFields[0];
352
+ workingIndexFields = workingIndexFields.slice(1);
353
+ const eqBound = workingStartBound[0];
354
+ workingStartBound = workingStartBound.slice(1);
355
+ workingEndBound = workingEndBound.slice(1);
356
+ commonPrefix.push([
357
+ "eq",
358
+ indexField,
359
+ eqBound
360
+ ]);
361
+ }
362
+ const makeCompare = (boundType, key) => {
363
+ const range = commonPrefix.slice();
364
+ let i = 0;
365
+ for (; i < key.length - 1; i++) range.push([
366
+ "eq",
367
+ workingIndexFields[i],
368
+ key[i]
369
+ ]);
370
+ if (i < key.length) range.push([
371
+ boundType,
372
+ workingIndexFields[i],
373
+ key[i]
374
+ ]);
375
+ return range;
376
+ };
377
+ const startRanges = [];
378
+ while (workingStartBound.length > 1) {
379
+ startRanges.push(makeCompare(workingStartBoundType, workingStartBound));
380
+ workingStartBoundType = makeExclusive(workingStartBoundType);
381
+ workingStartBound = workingStartBound.slice(0, -1);
382
+ }
383
+ const endRanges = [];
384
+ while (workingEndBound.length > 1) {
385
+ endRanges.push(makeCompare(workingEndBoundType, workingEndBound));
386
+ workingEndBoundType = makeExclusive(workingEndBoundType);
387
+ workingEndBound = workingEndBound.slice(0, -1);
388
+ }
389
+ endRanges.reverse();
390
+ let middleRange;
391
+ if (workingEndBound.length === 0) middleRange = makeCompare(workingStartBoundType, workingStartBound);
392
+ else if (workingStartBound.length === 0) middleRange = makeCompare(workingEndBoundType, workingEndBound);
393
+ else {
394
+ const startValue = workingStartBound[0];
395
+ const endValue = workingEndBound[0];
396
+ middleRange = commonPrefix.slice();
397
+ middleRange.push([
398
+ workingStartBoundType,
399
+ workingIndexFields[0],
400
+ startValue
401
+ ]);
402
+ middleRange.push([
403
+ workingEndBoundType,
404
+ workingIndexFields[0],
405
+ endValue
406
+ ]);
407
+ }
408
+ const ranges = [
409
+ ...startRanges,
410
+ middleRange,
411
+ ...endRanges
412
+ ];
413
+ if (order === "desc") ranges.reverse();
414
+ return ranges;
415
+ }
416
+ function rangeToQuery(range) {
417
+ return (q) => {
418
+ let query = q;
419
+ for (const [boundType, field, value] of range) query = query[boundType](field, value);
420
+ return query;
421
+ };
422
+ }
423
+ /**
424
+ * Get the ordered list of fields for a given table's index based on the schema.
425
+ *
426
+ * - For "by_creation_time", returns ["_creationTime", "_id"].
427
+ * - For "by_id", returns ["_id"].
428
+ * - Otherwise, looks up the named index in the schema and returns its fields
429
+ * followed by ["_creationTime", "_id"].
430
+ * e.g. for an index defined like `.index("abc", ["a", "b"])`,
431
+ * returns ["a", "b", "_creationTime", "_id"].
432
+ */
433
+ function getIndexFields(table, index, schema) {
434
+ const indexDescriptor = String(index ?? "by_creation_time");
435
+ if (indexDescriptor === "by_creation_time") return ["_creationTime", "_id"];
436
+ if (indexDescriptor === "by_id") return ["_id"];
437
+ if (!schema) throw new Error("schema is required to infer index fields");
438
+ const indexInfo = schema.tables[table].indexes.find((index) => index.indexDescriptor === indexDescriptor);
439
+ if (!indexInfo) throw new Error(`Index ${indexDescriptor} not found in table ${table}`);
440
+ const fields = indexInfo.fields.slice();
441
+ fields.push("_creationTime");
442
+ fields.push("_id");
443
+ return fields;
444
+ }
445
+ function getIndexKey(doc, indexFields) {
446
+ const key = [];
447
+ for (const field of indexFields) {
448
+ let obj = doc;
449
+ for (const subfield of field.split(".")) obj = obj[subfield];
450
+ key.push(obj);
451
+ }
452
+ return key;
453
+ }
454
+ /**
455
+ * A "stream" is an async iterable of query results, ordered by an index on a table.
456
+ *
457
+ * Use it as you would use `ctx.db`.
458
+ * If using pagination in a reactive query, see the warnings on the `paginator`
459
+ * function. TL;DR: you need to pass in `endCursor` to prevent holes or overlaps
460
+ * between pages.
461
+ *
462
+ * Once you have a stream, you can use `mergeStreams` or `filterStream` to make
463
+ * more streams. Then use `queryStream` to convert it into an OrderedQuery,
464
+ * so you can call `.paginate()`, `.collect()`, etc.
465
+ */
466
+ function stream(db, schema) {
467
+ return new StreamDatabaseReader(db, schema);
468
+ }
469
+ /**
470
+ * A "QueryStream" is an async iterable of query results, ordered by indexed fields.
471
+ */
472
+ var QueryStream = class {
473
+ /**
474
+ * Create a new stream with a TypeScript filter applied.
475
+ *
476
+ * This is similar to `db.query(tableName).filter(predicate)`, but it's more
477
+ * general because it can call arbitrary TypeScript code, including more
478
+ * database queries.
479
+ *
480
+ * All documents filtered out are still considered "read" from the database;
481
+ * they are just excluded from the output stream.
482
+ *
483
+ * In contrast to `filter` from legacy helper filtering, this filterWith
484
+ * is applied *before* any pagination. That means if the filter excludes a lot
485
+ * of documents, the `.paginate()` method will read a lot of documents until
486
+ * it gets as many documents as it wants. If you run into issues with reading
487
+ * too much data, you can pass `maxScan` to `paginate()`.
488
+ */
489
+ filterWith(predicate) {
490
+ const order = this.getOrder();
491
+ return new FlatMapStream(this, async (doc) => {
492
+ return new SingletonStream(await predicate(doc) ? doc : null, order, [], [], []);
493
+ }, []);
494
+ }
495
+ /**
496
+ * Create a new stream where each element is the result of applying the mapper
497
+ * function to the elements of the original stream.
498
+ *
499
+ * Similar to how [1, 2, 3].map(x => x * 2) => [2, 4, 6]
500
+ */
501
+ map(mapper) {
502
+ const order = this.getOrder();
503
+ return new FlatMapStream(this, async (doc) => {
504
+ return new SingletonStream(await mapper(doc), order, [], [], []);
505
+ }, []);
506
+ }
507
+ /**
508
+ * Similar to flatMap on an array, but iterate over a stream, and the for each
509
+ * element, iterate over the stream created by the mapper function.
510
+ *
511
+ * Ordered by the original stream order, then the mapped stream. Similar to
512
+ * how ["a", "b"].flatMap(letter => [letter, letter]) => ["a", "a", "b", "b"]
513
+ *
514
+ * @param mapper A function that takes a document and returns a new stream.
515
+ * @param mappedIndexFields The index fields of the streams created by mapper.
516
+ * @returns A stream of documents returned by the mapper streams,
517
+ * grouped by the documents in the original stream.
518
+ */
519
+ flatMap(mapper, mappedIndexFields) {
520
+ normalizeIndexFields(mappedIndexFields);
521
+ return new FlatMapStream(this, mapper, mappedIndexFields);
522
+ }
523
+ /**
524
+ * Get the first item from the original stream for each distinct value of the
525
+ * selected index fields.
526
+ *
527
+ * e.g. if the stream has an equality filter on `a`, and index fields `[a, b, c]`,
528
+ * we can do `stream.distinct(["b"])` to get a stream of the first item for
529
+ * each distinct value of `b`.
530
+ * Similarly, you could do `stream.distinct(["a", "b"])` with the same result,
531
+ * or `stream.distinct(["a", "b", "c"])` to get the original stream.
532
+ *
533
+ * This stream efficiently skips past items with the same value for the selected
534
+ * distinct index fields.
535
+ *
536
+ * This can be used to perform a loose index scan.
537
+ */
538
+ distinct(distinctIndexFields) {
539
+ return new DistinctStream(this, distinctIndexFields);
540
+ }
541
+ filter(_predicate) {
542
+ throw new Error("Cannot call .filter() directly on a query stream. Use .filterWith() for filtering or .collect() if you want to convert the stream to an array first.");
543
+ }
544
+ async paginate(opts) {
545
+ if (opts.limit === 0) {
546
+ if (opts.cursor === null) throw new Error(".paginate called with cursor of null and 0 for limit. This is not supported, as null is not a valid continueCursor. Advice: avoid calling paginate entirely in these cases.");
547
+ return {
548
+ page: [],
549
+ isDone: false,
550
+ continueCursor: opts.cursor
551
+ };
552
+ }
553
+ const order = this.getOrder();
554
+ let newStartKey = {
555
+ key: [],
556
+ inclusive: true
557
+ };
558
+ if (opts.cursor !== null) newStartKey = {
559
+ key: deserializeCursor(opts.cursor),
560
+ inclusive: false
561
+ };
562
+ let newEndKey = {
563
+ key: [],
564
+ inclusive: true
565
+ };
566
+ const maxRowsToRead = opts.maxScan;
567
+ const softMaxRowsToRead = opts.limit + 1;
568
+ let maxRows = opts.limit;
569
+ if (opts.endCursor) {
570
+ newEndKey = {
571
+ key: deserializeCursor(opts.endCursor),
572
+ inclusive: true
573
+ };
574
+ maxRows = void 0;
575
+ }
576
+ const newLowerBound = order === "asc" ? newStartKey : newEndKey;
577
+ const newUpperBound = order === "asc" ? newEndKey : newStartKey;
578
+ const narrowStream = this.narrow({
579
+ lowerBound: newLowerBound.key,
580
+ lowerBoundInclusive: newLowerBound.inclusive,
581
+ upperBound: newUpperBound.key,
582
+ upperBoundInclusive: newUpperBound.inclusive
583
+ });
584
+ const page = [];
585
+ const indexKeys = [];
586
+ let hasMore = opts.endCursor && opts.endCursor !== "[]";
587
+ let continueCursor = opts.endCursor ?? "[]";
588
+ for await (const [doc, indexKey] of narrowStream.iterWithKeys()) {
589
+ if (doc !== null) page.push(doc);
590
+ indexKeys.push(indexKey);
591
+ if (maxRows !== void 0 && page.length >= maxRows || maxRowsToRead !== void 0 && indexKeys.length >= maxRowsToRead) {
592
+ hasMore = true;
593
+ continueCursor = serializeCursor(indexKey);
594
+ break;
595
+ }
596
+ }
597
+ let pageStatus;
598
+ let splitCursor;
599
+ if (indexKeys.length === maxRowsToRead) {
600
+ pageStatus = "SplitRequired";
601
+ splitCursor = indexKeys[Math.floor((indexKeys.length - 1) / 2)];
602
+ } else if (indexKeys.length >= softMaxRowsToRead) {
603
+ pageStatus = "SplitRecommended";
604
+ splitCursor = indexKeys[Math.floor((indexKeys.length - 1) / 2)];
605
+ }
606
+ return {
607
+ page,
608
+ isDone: !hasMore,
609
+ continueCursor,
610
+ pageStatus,
611
+ splitCursor: splitCursor ? serializeCursor(splitCursor) : void 0
612
+ };
613
+ }
614
+ async collect() {
615
+ return await this.take(Number.POSITIVE_INFINITY);
616
+ }
617
+ async take(n) {
618
+ const results = [];
619
+ for await (const [doc, _] of this.iterWithKeys()) {
620
+ if (doc === null) continue;
621
+ results.push(doc);
622
+ if (results.length === n) break;
623
+ }
624
+ return results;
625
+ }
626
+ async unique() {
627
+ const docs = await this.take(2);
628
+ if (docs.length === 2) throw new Error("Query is not unique");
629
+ return docs[0] ?? null;
630
+ }
631
+ async uniqueOrThrow() {
632
+ const doc = await this.unique();
633
+ if (doc === null) throw new Error("Query returned no results");
634
+ return doc;
635
+ }
636
+ async first() {
637
+ return (await this.take(1))[0] ?? null;
638
+ }
639
+ async firstOrThrow() {
640
+ const doc = await this.first();
641
+ if (doc === null) throw new Error("Query returned no results");
642
+ return doc;
643
+ }
644
+ [Symbol.asyncIterator]() {
645
+ const iterator = this.iterWithKeys()[Symbol.asyncIterator]();
646
+ return { async next() {
647
+ const result = await iterator.next();
648
+ if (result.done) return {
649
+ done: true,
650
+ value: void 0
651
+ };
652
+ return {
653
+ done: false,
654
+ value: result.value[0]
655
+ };
656
+ } };
657
+ }
658
+ };
659
+ var StreamDatabaseReader = class {
660
+ system;
661
+ db;
662
+ schema;
663
+ constructor(db, schema) {
664
+ this.db = db;
665
+ this.schema = schema;
666
+ this.system = db.system;
667
+ }
668
+ query(tableName) {
669
+ return new StreamQueryInitializer(this, tableName);
670
+ }
671
+ get(_id) {
672
+ throw new Error("get() not supported for `paginator`");
673
+ }
674
+ normalizeId(_tableName, _id) {
675
+ throw new Error("normalizeId() not supported for `paginator`.");
676
+ }
677
+ };
678
+ var StreamableQuery = class extends QueryStream {};
679
+ var StreamQueryInitializer = class extends StreamableQuery {
680
+ parent;
681
+ table;
682
+ constructor(parent, table) {
683
+ super();
684
+ this.parent = parent;
685
+ this.table = table;
686
+ }
687
+ fullTableScan() {
688
+ return this.withIndex("by_creation_time");
689
+ }
690
+ withIndex(indexName, indexRange) {
691
+ const q = new ReflectIndexRange(getIndexFields(this.table, indexName, this.parent.schema));
692
+ if (indexRange) indexRange(q);
693
+ return new StreamQuery(this, indexName, q, indexRange);
694
+ }
695
+ withSearchIndex(_indexName, _searchFilter) {
696
+ throw new Error("Cannot paginate withSearchIndex");
697
+ }
698
+ inner() {
699
+ return this.fullTableScan();
700
+ }
701
+ order(order) {
702
+ return this.inner().order(order);
703
+ }
704
+ reflect() {
705
+ return this.inner().reflect();
706
+ }
707
+ iterWithKeys() {
708
+ return this.inner().iterWithKeys();
709
+ }
710
+ getOrder() {
711
+ return this.inner().getOrder();
712
+ }
713
+ getEqualityIndexFilter() {
714
+ return this.inner().getEqualityIndexFilter();
715
+ }
716
+ getIndexFields() {
717
+ return this.inner().getIndexFields();
718
+ }
719
+ narrow(indexBounds) {
720
+ return this.inner().narrow(indexBounds);
721
+ }
722
+ };
723
+ var StreamQuery = class extends StreamableQuery {
724
+ parent;
725
+ index;
726
+ q;
727
+ indexRange;
728
+ constructor(parent, index, q, indexRange) {
729
+ super();
730
+ this.parent = parent;
731
+ this.index = index;
732
+ this.q = q;
733
+ this.indexRange = indexRange;
734
+ }
735
+ order(order) {
736
+ return new OrderedStreamQuery(this, order);
737
+ }
738
+ inner() {
739
+ return this.order("asc");
740
+ }
741
+ reflect() {
742
+ return this.inner().reflect();
743
+ }
744
+ iterWithKeys() {
745
+ return this.inner().iterWithKeys();
746
+ }
747
+ getOrder() {
748
+ return this.inner().getOrder();
749
+ }
750
+ getEqualityIndexFilter() {
751
+ return this.inner().getEqualityIndexFilter();
752
+ }
753
+ getIndexFields() {
754
+ return this.inner().getIndexFields();
755
+ }
756
+ narrow(indexBounds) {
757
+ return this.inner().narrow(indexBounds);
758
+ }
759
+ };
760
+ var OrderedStreamQuery = class extends StreamableQuery {
761
+ parent;
762
+ order;
763
+ constructor(parent, order) {
764
+ super();
765
+ this.parent = parent;
766
+ this.order = order;
767
+ }
768
+ reflect() {
769
+ return {
770
+ db: this.parent.parent.parent.db,
771
+ schema: this.parent.parent.parent.schema,
772
+ table: this.parent.parent.table,
773
+ index: this.parent.index,
774
+ indexFields: this.parent.q.indexFields,
775
+ order: this.order,
776
+ bounds: {
777
+ lowerBound: this.parent.q.lowerBoundIndexKey ?? [],
778
+ lowerBoundInclusive: this.parent.q.lowerBoundInclusive,
779
+ upperBound: this.parent.q.upperBoundIndexKey ?? [],
780
+ upperBoundInclusive: this.parent.q.upperBoundInclusive
781
+ },
782
+ indexRange: this.parent.indexRange
783
+ };
784
+ }
785
+ /**
786
+ * inner() is as if you had used ctx.db to construct the query.
787
+ */
788
+ inner() {
789
+ const { db, table, index, order, indexRange } = this.reflect();
790
+ return db.query(table).withIndex(index, indexRange).order(order);
791
+ }
792
+ iterWithKeys() {
793
+ const { indexFields } = this.reflect();
794
+ const iterable = this.inner();
795
+ return { [Symbol.asyncIterator]() {
796
+ const iterator = iterable[Symbol.asyncIterator]();
797
+ return { async next() {
798
+ const result = await iterator.next();
799
+ if (result.done) return {
800
+ done: true,
801
+ value: void 0
802
+ };
803
+ return {
804
+ done: false,
805
+ value: [result.value, getIndexKey(result.value, indexFields)]
806
+ };
807
+ } };
808
+ } };
809
+ }
810
+ getOrder() {
811
+ return this.order;
812
+ }
813
+ getEqualityIndexFilter() {
814
+ return this.parent.q.equalityIndexFilter;
815
+ }
816
+ getIndexFields() {
817
+ return this.parent.q.indexFields;
818
+ }
819
+ narrow(indexBounds) {
820
+ const { db, table, index, order, bounds, schema } = this.reflect();
821
+ let maxLowerBound = bounds.lowerBound;
822
+ let maxLowerBoundInclusive = bounds.lowerBoundInclusive;
823
+ if (compareKeys({
824
+ value: indexBounds.lowerBound,
825
+ kind: indexBounds.lowerBoundInclusive ? "predecessor" : "successor"
826
+ }, {
827
+ value: bounds.lowerBound,
828
+ kind: bounds.lowerBoundInclusive ? "predecessor" : "successor"
829
+ }) > 0) {
830
+ maxLowerBound = indexBounds.lowerBound;
831
+ maxLowerBoundInclusive = indexBounds.lowerBoundInclusive;
832
+ }
833
+ let minUpperBound = bounds.upperBound;
834
+ let minUpperBoundInclusive = bounds.upperBoundInclusive;
835
+ if (compareKeys({
836
+ value: indexBounds.upperBound,
837
+ kind: indexBounds.upperBoundInclusive ? "successor" : "predecessor"
838
+ }, {
839
+ value: bounds.upperBound,
840
+ kind: bounds.upperBoundInclusive ? "successor" : "predecessor"
841
+ }) < 0) {
842
+ minUpperBound = indexBounds.upperBound;
843
+ minUpperBoundInclusive = indexBounds.upperBoundInclusive;
844
+ }
845
+ return streamIndexRange(db, schema, table, index, {
846
+ lowerBound: maxLowerBound,
847
+ lowerBoundInclusive: maxLowerBoundInclusive,
848
+ upperBound: minUpperBound,
849
+ upperBoundInclusive: minUpperBoundInclusive
850
+ }, order);
851
+ }
852
+ };
853
+ /**
854
+ * Create a stream of documents using the given index and bounds.
855
+ */
856
+ function streamIndexRange(db, schema, table, index, bounds, order) {
857
+ return new ConcatStreams(...splitRange(getIndexFields(table, index, schema), order, bounds.lowerBound, bounds.upperBound, bounds.lowerBoundInclusive ? "gte" : "gt", bounds.upperBoundInclusive ? "lte" : "lt").map((splitBound) => stream(db, schema).query(table).withIndex(index, rangeToQuery(splitBound)).order(order)));
858
+ }
859
+ var ReflectIndexRange = class {
860
+ #hasSuffix = false;
861
+ lowerBoundIndexKey = void 0;
862
+ lowerBoundInclusive = true;
863
+ upperBoundIndexKey = void 0;
864
+ upperBoundInclusive = true;
865
+ equalityIndexFilter = [];
866
+ indexFields;
867
+ constructor(indexFields) {
868
+ this.indexFields = indexFields;
869
+ }
870
+ eq(field, value) {
871
+ if (!this.#canLowerBound(field) || !this.#canUpperBound(field)) throw new Error(`Cannot use eq on field '${field}'`);
872
+ this.lowerBoundIndexKey = this.lowerBoundIndexKey ?? [];
873
+ this.lowerBoundIndexKey.push(value);
874
+ this.upperBoundIndexKey = this.upperBoundIndexKey ?? [];
875
+ this.upperBoundIndexKey.push(value);
876
+ this.equalityIndexFilter.push(value);
877
+ return this;
878
+ }
879
+ lt(field, value) {
880
+ if (!this.#canUpperBound(field)) throw new Error(`Cannot use lt on field '${field}'`);
881
+ this.upperBoundIndexKey = this.upperBoundIndexKey ?? [];
882
+ this.upperBoundIndexKey.push(value);
883
+ this.upperBoundInclusive = false;
884
+ this.#hasSuffix = true;
885
+ return this;
886
+ }
887
+ lte(field, value) {
888
+ if (!this.#canUpperBound(field)) throw new Error(`Cannot use lte on field '${field}'`);
889
+ this.upperBoundIndexKey = this.upperBoundIndexKey ?? [];
890
+ this.upperBoundIndexKey.push(value);
891
+ this.#hasSuffix = true;
892
+ return this;
893
+ }
894
+ gt(field, value) {
895
+ if (!this.#canLowerBound(field)) throw new Error(`Cannot use gt on field '${field}'`);
896
+ this.lowerBoundIndexKey = this.lowerBoundIndexKey ?? [];
897
+ this.lowerBoundIndexKey.push(value);
898
+ this.lowerBoundInclusive = false;
899
+ this.#hasSuffix = true;
900
+ return this;
901
+ }
902
+ gte(field, value) {
903
+ if (!this.#canLowerBound(field)) throw new Error(`Cannot use gte on field '${field}'`);
904
+ this.lowerBoundIndexKey = this.lowerBoundIndexKey ?? [];
905
+ this.lowerBoundIndexKey.push(value);
906
+ this.#hasSuffix = true;
907
+ return this;
908
+ }
909
+ #canLowerBound(field) {
910
+ const currentLowerBoundLength = this.lowerBoundIndexKey?.length ?? 0;
911
+ const currentUpperBoundLength = this.upperBoundIndexKey?.length ?? 0;
912
+ if (currentLowerBoundLength > currentUpperBoundLength) return false;
913
+ if (currentLowerBoundLength === currentUpperBoundLength && this.#hasSuffix) return false;
914
+ return currentLowerBoundLength < this.indexFields.length && this.indexFields[currentLowerBoundLength] === field;
915
+ }
916
+ #canUpperBound(field) {
917
+ const currentLowerBoundLength = this.lowerBoundIndexKey?.length ?? 0;
918
+ const currentUpperBoundLength = this.upperBoundIndexKey?.length ?? 0;
919
+ if (currentUpperBoundLength > currentLowerBoundLength) return false;
920
+ if (currentLowerBoundLength === currentUpperBoundLength && this.#hasSuffix) return false;
921
+ return currentUpperBoundLength < this.indexFields.length && this.indexFields[currentUpperBoundLength] === field;
922
+ }
923
+ };
924
+ /**
925
+ * Merge multiple streams, provided in any order, into a single stream.
926
+ *
927
+ * The streams will be merged into a stream of documents ordered by the index keys,
928
+ * i.e. by "author" (then by the implicit "_creationTime").
929
+ *
930
+ * e.g. ```ts
931
+ * mergedStream([
932
+ * stream(db, schema).query("messages").withIndex("by_author", q => q.eq("author", "user3")),
933
+ * stream(db, schema).query("messages").withIndex("by_author", q => q.eq("author", "user1")),
934
+ * stream(db, schema).query("messages").withIndex("by_author", q => q.eq("author", "user2")),
935
+ * ], ["author"])
936
+ * ```
937
+ *
938
+ * returns a stream of messages for user1, then user2, then user3.
939
+ *
940
+ * You can also use `orderByIndexFields` to change the indexed fields before merging, which changes the order of the merged stream.
941
+ * This only works if the streams are already ordered by `orderByIndexFields`,
942
+ * which happens if each does a .eq(field, value) on all index fields before `orderByIndexFields`.
943
+ *
944
+ * e.g. if the "by_author" index is defined as being ordered by ["author", "_creationTime"],
945
+ * and each query does an equality lookup on "author", each individual query before merging is in fact ordered by "_creationTime".
946
+ *
947
+ * e.g. ```ts
948
+ * mergedStream([
949
+ * stream(db, schema).query("messages").withIndex("by_author", q => q.eq("author", "user3")),
950
+ * stream(db, schema).query("messages").withIndex("by_author", q => q.eq("author", "user1")),
951
+ * stream(db, schema).query("messages").withIndex("by_author", q => q.eq("author", "user2")),
952
+ * ], ["_creationTime"])
953
+ * ```
954
+ *
955
+ * This returns a stream of messages from all three users, sorted by creation time.
956
+ */
957
+ function mergedStream(streams, orderByIndexFields) {
958
+ return new MergedStream(streams, orderByIndexFields);
959
+ }
960
+ var MergedStream = class MergedStream extends QueryStream {
961
+ #order;
962
+ #streams;
963
+ #equalityIndexFilter;
964
+ #indexFields;
965
+ constructor(streams, orderByIndexFields) {
966
+ super();
967
+ if (streams.length === 0) throw new Error("Cannot union empty array of streams");
968
+ this.#order = allSame(streams.map((stream) => stream.getOrder()), "Cannot merge streams with different orders");
969
+ this.#streams = streams.map((stream) => new OrderByStream(stream, orderByIndexFields));
970
+ this.#indexFields = allSame(this.#streams.map((stream) => stream.getIndexFields()), "Cannot merge streams with different index fields. Consider using .orderBy()");
971
+ this.#equalityIndexFilter = commonPrefix(this.#streams.map((stream) => stream.getEqualityIndexFilter()));
972
+ }
973
+ iterWithKeys() {
974
+ const iterables = this.#streams.map((stream) => stream.iterWithKeys());
975
+ const comparisonInversion = this.#order === "asc" ? 1 : -1;
976
+ return { [Symbol.asyncIterator]() {
977
+ const iterators = iterables.map((iterable) => iterable[Symbol.asyncIterator]());
978
+ const results = Array.from({ length: iterators.length }, () => ({
979
+ done: false,
980
+ value: void 0
981
+ }));
982
+ return { async next() {
983
+ await Promise.all(iterators.map(async (iterator, i) => {
984
+ if (!results[i].done && !results[i].value) results[i] = await iterator.next();
985
+ }));
986
+ let minIndexKeyAndIndex;
987
+ for (let i = 0; i < results.length; i++) {
988
+ const result = results[i];
989
+ if (result.done || !result.value) continue;
990
+ const [_, resultIndexKey] = result.value;
991
+ if (minIndexKeyAndIndex === void 0) {
992
+ minIndexKeyAndIndex = [resultIndexKey, i];
993
+ continue;
994
+ }
995
+ const [prevMin, _prevMinIndex] = minIndexKeyAndIndex;
996
+ if (compareKeys({
997
+ value: resultIndexKey,
998
+ kind: "exact"
999
+ }, {
1000
+ value: prevMin,
1001
+ kind: "exact"
1002
+ }) * comparisonInversion < 0) minIndexKeyAndIndex = [resultIndexKey, i];
1003
+ }
1004
+ if (minIndexKeyAndIndex === void 0) return {
1005
+ done: true,
1006
+ value: void 0
1007
+ };
1008
+ const [_, minIndex] = minIndexKeyAndIndex;
1009
+ const result = results[minIndex].value;
1010
+ results[minIndex].value = void 0;
1011
+ return {
1012
+ done: false,
1013
+ value: result
1014
+ };
1015
+ } };
1016
+ } };
1017
+ }
1018
+ getOrder() {
1019
+ return this.#order;
1020
+ }
1021
+ getEqualityIndexFilter() {
1022
+ return this.#equalityIndexFilter;
1023
+ }
1024
+ getIndexFields() {
1025
+ return this.#indexFields;
1026
+ }
1027
+ narrow(indexBounds) {
1028
+ return new MergedStream(this.#streams.map((stream) => stream.narrow(indexBounds)), this.#indexFields);
1029
+ }
1030
+ };
1031
+ function allSame(values, errorMessage) {
1032
+ const first = values[0];
1033
+ for (const value of values) if (compareValues(value, first)) throw new Error(errorMessage);
1034
+ return first;
1035
+ }
1036
+ function commonPrefix(values) {
1037
+ let commonPrefix = values[0];
1038
+ for (const value of values) for (let i = 0; i < commonPrefix.length; i++) if (i >= value.length || compareValues(commonPrefix[i], value[i])) {
1039
+ commonPrefix = commonPrefix.slice(0, i);
1040
+ break;
1041
+ }
1042
+ return commonPrefix;
1043
+ }
1044
+ /**
1045
+ * Concatenate multiple streams into a single stream.
1046
+ * This assumes that the streams correspond to disjoint index ranges,
1047
+ * and are provided in the same order as the index ranges.
1048
+ *
1049
+ * e.g. ```ts
1050
+ * new ConcatStreams(
1051
+ * stream(db, schema).query("messages").withIndex("by_author", q => q.eq("author", "user1")),
1052
+ * stream(db, schema).query("messages").withIndex("by_author", q => q.eq("author", "user2")),
1053
+ * )
1054
+ * ```
1055
+ *
1056
+ * is valid, but if the stream arguments were reversed, or the queries were
1057
+ * `.order("desc")`, it would be invalid.
1058
+ *
1059
+ * It's not recommended to use `ConcatStreams` directly, since it has the same
1060
+ * behavior as `MergedStream`, but with fewer runtime checks.
1061
+ */
1062
+ var ConcatStreams = class ConcatStreams extends QueryStream {
1063
+ #order;
1064
+ #streams;
1065
+ #equalityIndexFilter;
1066
+ #indexFields;
1067
+ constructor(...streams) {
1068
+ super();
1069
+ this.#streams = streams;
1070
+ if (streams.length === 0) throw new Error("Cannot concat empty array of streams");
1071
+ this.#order = allSame(streams.map((stream) => stream.getOrder()), "Cannot concat streams with different orders. Consider using .orderBy()");
1072
+ this.#indexFields = allSame(streams.map((stream) => stream.getIndexFields()), "Cannot concat streams with different index fields. Consider using .orderBy()");
1073
+ this.#equalityIndexFilter = commonPrefix(streams.map((stream) => stream.getEqualityIndexFilter()));
1074
+ }
1075
+ iterWithKeys() {
1076
+ const iterables = this.#streams.map((stream) => stream.iterWithKeys());
1077
+ const comparisonInversion = this.#order === "asc" ? 1 : -1;
1078
+ let previousIndexKey;
1079
+ return { [Symbol.asyncIterator]() {
1080
+ const iterators = iterables.map((iterable) => iterable[Symbol.asyncIterator]());
1081
+ return { async next() {
1082
+ while (iterators.length > 0) {
1083
+ const result = await iterators[0].next();
1084
+ if (result.done) iterators.shift();
1085
+ else {
1086
+ const [_, indexKey] = result.value;
1087
+ if (previousIndexKey !== void 0 && compareKeys({
1088
+ value: previousIndexKey,
1089
+ kind: "exact"
1090
+ }, {
1091
+ value: indexKey,
1092
+ kind: "exact"
1093
+ }) * comparisonInversion > 0) throw new Error(`ConcatStreams in wrong order: ${JSON.stringify(previousIndexKey)}, ${JSON.stringify(indexKey)}`);
1094
+ previousIndexKey = indexKey;
1095
+ return result;
1096
+ }
1097
+ }
1098
+ return {
1099
+ done: true,
1100
+ value: void 0
1101
+ };
1102
+ } };
1103
+ } };
1104
+ }
1105
+ getOrder() {
1106
+ return this.#order;
1107
+ }
1108
+ getEqualityIndexFilter() {
1109
+ return this.#equalityIndexFilter;
1110
+ }
1111
+ getIndexFields() {
1112
+ return this.#indexFields;
1113
+ }
1114
+ narrow(indexBounds) {
1115
+ return new ConcatStreams(...this.#streams.map((stream) => stream.narrow(indexBounds)));
1116
+ }
1117
+ };
1118
+ var FlatMapStreamIterator = class {
1119
+ #outerStream;
1120
+ #outerIterator;
1121
+ #currentOuterItem = null;
1122
+ #mapper;
1123
+ #mappedIndexFields;
1124
+ constructor(outerStream, mapper, mappedIndexFields) {
1125
+ this.#outerIterator = outerStream.iterWithKeys()[Symbol.asyncIterator]();
1126
+ this.#outerStream = outerStream;
1127
+ this.#mapper = mapper;
1128
+ this.#mappedIndexFields = mappedIndexFields;
1129
+ }
1130
+ singletonSkipInnerStream() {
1131
+ const indexKey = this.#mappedIndexFields.map(() => null);
1132
+ return new SingletonStream(null, this.#outerStream.getOrder(), this.#mappedIndexFields, indexKey, indexKey);
1133
+ }
1134
+ async setCurrentOuterItem(item) {
1135
+ const [t, indexKey] = item;
1136
+ let innerStream;
1137
+ if (t === null) innerStream = this.singletonSkipInnerStream();
1138
+ else {
1139
+ innerStream = await this.#mapper(t);
1140
+ if (!equalIndexFields(innerStream.getIndexFields(), this.#mappedIndexFields)) throw new Error(`FlatMapStream: inner stream has different index fields than expected: ${JSON.stringify(innerStream.getIndexFields())} vs ${JSON.stringify(this.#mappedIndexFields)}`);
1141
+ if (innerStream.getOrder() !== this.#outerStream.getOrder()) throw new Error(`FlatMapStream: inner stream has different order than outer stream: ${innerStream.getOrder()} vs ${this.#outerStream.getOrder()}`);
1142
+ }
1143
+ this.#currentOuterItem = {
1144
+ t,
1145
+ indexKey,
1146
+ innerIterator: innerStream.iterWithKeys()[Symbol.asyncIterator](),
1147
+ count: 0
1148
+ };
1149
+ }
1150
+ async next() {
1151
+ if (this.#currentOuterItem === null) {
1152
+ const result = await this.#outerIterator.next();
1153
+ if (result.done) return {
1154
+ done: true,
1155
+ value: void 0
1156
+ };
1157
+ await this.setCurrentOuterItem(result.value);
1158
+ return await this.next();
1159
+ }
1160
+ const result = await this.#currentOuterItem.innerIterator.next();
1161
+ if (result.done) {
1162
+ if (this.#currentOuterItem.count > 0) this.#currentOuterItem = null;
1163
+ else this.#currentOuterItem.innerIterator = this.singletonSkipInnerStream().iterWithKeys()[Symbol.asyncIterator]();
1164
+ return await this.next();
1165
+ }
1166
+ const [u, indexKey] = result.value;
1167
+ this.#currentOuterItem.count++;
1168
+ return {
1169
+ done: false,
1170
+ value: [u, [...this.#currentOuterItem.indexKey, ...indexKey]]
1171
+ };
1172
+ }
1173
+ };
1174
+ var FlatMapStream = class FlatMapStream extends QueryStream {
1175
+ #stream;
1176
+ #mapper;
1177
+ #mappedIndexFields;
1178
+ constructor(stream, mapper, mappedIndexFields) {
1179
+ super();
1180
+ this.#stream = stream;
1181
+ this.#mapper = mapper;
1182
+ this.#mappedIndexFields = mappedIndexFields;
1183
+ }
1184
+ iterWithKeys() {
1185
+ const outerStream = this.#stream;
1186
+ const mapper = this.#mapper;
1187
+ const mappedIndexFields = this.#mappedIndexFields;
1188
+ return { [Symbol.asyncIterator]() {
1189
+ return new FlatMapStreamIterator(outerStream, mapper, mappedIndexFields);
1190
+ } };
1191
+ }
1192
+ getOrder() {
1193
+ return this.#stream.getOrder();
1194
+ }
1195
+ getEqualityIndexFilter() {
1196
+ return this.#stream.getEqualityIndexFilter();
1197
+ }
1198
+ getIndexFields() {
1199
+ return [...this.#stream.getIndexFields(), ...this.#mappedIndexFields];
1200
+ }
1201
+ narrow(indexBounds) {
1202
+ const outerLength = this.#stream.getIndexFields().length;
1203
+ const outerLowerBound = indexBounds.lowerBound.slice(0, outerLength);
1204
+ const outerUpperBound = indexBounds.upperBound.slice(0, outerLength);
1205
+ const innerLowerBound = indexBounds.lowerBound.slice(outerLength);
1206
+ const innerUpperBound = indexBounds.upperBound.slice(outerLength);
1207
+ const outerIndexBounds = {
1208
+ lowerBound: outerLowerBound,
1209
+ lowerBoundInclusive: innerLowerBound.length === 0 ? indexBounds.lowerBoundInclusive : true,
1210
+ upperBound: outerUpperBound,
1211
+ upperBoundInclusive: innerUpperBound.length === 0 ? indexBounds.upperBoundInclusive : true
1212
+ };
1213
+ const innerIndexBounds = {
1214
+ lowerBound: innerLowerBound,
1215
+ lowerBoundInclusive: innerLowerBound.length === 0 ? true : indexBounds.lowerBoundInclusive,
1216
+ upperBound: innerUpperBound,
1217
+ upperBoundInclusive: innerUpperBound.length === 0 ? true : indexBounds.upperBoundInclusive
1218
+ };
1219
+ return new FlatMapStream(this.#stream.narrow(outerIndexBounds), async (t) => {
1220
+ return (await this.#mapper(t)).narrow(innerIndexBounds);
1221
+ }, this.#mappedIndexFields);
1222
+ }
1223
+ };
1224
+ var SingletonStream = class SingletonStream extends QueryStream {
1225
+ #value;
1226
+ #order;
1227
+ #indexFields;
1228
+ #indexKey;
1229
+ #equalityIndexFilter;
1230
+ constructor(value, order, indexFields, indexKey, equalityIndexFilter) {
1231
+ super();
1232
+ this.#value = value;
1233
+ this.#order = order;
1234
+ this.#indexFields = indexFields;
1235
+ this.#indexKey = indexKey;
1236
+ this.#equalityIndexFilter = equalityIndexFilter;
1237
+ if (indexKey.length !== indexFields.length) throw new Error(`indexKey must have the same length as indexFields: ${JSON.stringify(indexKey)} vs ${JSON.stringify(indexFields)}`);
1238
+ }
1239
+ iterWithKeys() {
1240
+ const value = this.#value;
1241
+ const indexKey = this.#indexKey;
1242
+ return { [Symbol.asyncIterator]() {
1243
+ let sent = false;
1244
+ return { async next() {
1245
+ if (sent) return {
1246
+ done: true,
1247
+ value: void 0
1248
+ };
1249
+ sent = true;
1250
+ return {
1251
+ done: false,
1252
+ value: [value, indexKey]
1253
+ };
1254
+ } };
1255
+ } };
1256
+ }
1257
+ getOrder() {
1258
+ return this.#order;
1259
+ }
1260
+ getIndexFields() {
1261
+ return this.#indexFields;
1262
+ }
1263
+ getEqualityIndexFilter() {
1264
+ return this.#equalityIndexFilter;
1265
+ }
1266
+ narrow(indexBounds) {
1267
+ const compareLowerBound = compareKeys({
1268
+ value: indexBounds.lowerBound,
1269
+ kind: indexBounds.lowerBoundInclusive ? "exact" : "successor"
1270
+ }, {
1271
+ value: this.#indexKey,
1272
+ kind: "exact"
1273
+ });
1274
+ const compareUpperBound = compareKeys({
1275
+ value: this.#indexKey,
1276
+ kind: "exact"
1277
+ }, {
1278
+ value: indexBounds.upperBound,
1279
+ kind: indexBounds.upperBoundInclusive ? "exact" : "predecessor"
1280
+ });
1281
+ if (compareLowerBound <= 0 && compareUpperBound <= 0) return new SingletonStream(this.#value, this.#order, this.#indexFields, this.#indexKey, this.#equalityIndexFilter);
1282
+ return new EmptyStream(this.#order, this.#indexFields);
1283
+ }
1284
+ };
1285
+ /**
1286
+ * This is a completely empty stream that yields no values, and in particular
1287
+ * does not count towards maxScan.
1288
+ * Compare to SingletonStream(null, ...), which yields no values but does count
1289
+ * towards maxScan.
1290
+ */
1291
+ var EmptyStream = class extends QueryStream {
1292
+ #order;
1293
+ #indexFields;
1294
+ constructor(order, indexFields) {
1295
+ super();
1296
+ this.#order = order;
1297
+ this.#indexFields = indexFields;
1298
+ }
1299
+ iterWithKeys() {
1300
+ return { [Symbol.asyncIterator]() {
1301
+ return { async next() {
1302
+ return {
1303
+ done: true,
1304
+ value: void 0
1305
+ };
1306
+ } };
1307
+ } };
1308
+ }
1309
+ getOrder() {
1310
+ return this.#order;
1311
+ }
1312
+ getIndexFields() {
1313
+ return this.#indexFields;
1314
+ }
1315
+ getEqualityIndexFilter() {
1316
+ return [];
1317
+ }
1318
+ narrow(_indexBounds) {
1319
+ return this;
1320
+ }
1321
+ };
1322
+ function normalizeIndexFields(indexFields) {
1323
+ if (!indexFields.includes("_creationTime") && (indexFields.length !== 1 || indexFields[0] !== "_id")) indexFields.push("_creationTime");
1324
+ if (!indexFields.includes("_id")) indexFields.push("_id");
1325
+ }
1326
+ function* getOrderingIndexFields(stream) {
1327
+ const streamEqualityIndexLength = stream.getEqualityIndexFilter().length;
1328
+ const streamIndexFields = stream.getIndexFields();
1329
+ for (let i = 0; i <= streamEqualityIndexLength; i++) yield streamIndexFields.slice(i);
1330
+ }
1331
+ var OrderByStream = class OrderByStream extends QueryStream {
1332
+ #staticFilter;
1333
+ #stream;
1334
+ #indexFields;
1335
+ constructor(stream, indexFields) {
1336
+ super();
1337
+ this.#stream = stream;
1338
+ this.#indexFields = indexFields;
1339
+ normalizeIndexFields(this.#indexFields);
1340
+ const streamIndexFields = stream.getIndexFields();
1341
+ if (!Array.from(getOrderingIndexFields(stream)).some((orderingIndexFields) => equalIndexFields(orderingIndexFields, indexFields))) throw new Error(`indexFields must be some sequence of fields the stream is ordered by: ${JSON.stringify(indexFields)}, ${JSON.stringify(streamIndexFields)} (${stream.getEqualityIndexFilter().length} equality fields)`);
1342
+ this.#staticFilter = stream.getEqualityIndexFilter().slice(0, streamIndexFields.length - indexFields.length);
1343
+ }
1344
+ getOrder() {
1345
+ return this.#stream.getOrder();
1346
+ }
1347
+ getEqualityIndexFilter() {
1348
+ return this.#stream.getEqualityIndexFilter().slice(this.#staticFilter.length);
1349
+ }
1350
+ getIndexFields() {
1351
+ return this.#indexFields;
1352
+ }
1353
+ iterWithKeys() {
1354
+ const iterable = this.#stream.iterWithKeys();
1355
+ const staticFilter = this.#staticFilter;
1356
+ return { [Symbol.asyncIterator]() {
1357
+ const iterator = iterable[Symbol.asyncIterator]();
1358
+ return { async next() {
1359
+ const result = await iterator.next();
1360
+ if (result.done) return result;
1361
+ const [doc, indexKey] = result.value;
1362
+ return {
1363
+ done: false,
1364
+ value: [doc, indexKey.slice(staticFilter.length)]
1365
+ };
1366
+ } };
1367
+ } };
1368
+ }
1369
+ narrow(indexBounds) {
1370
+ return new OrderByStream(this.#stream.narrow({
1371
+ lowerBound: [...this.#staticFilter, ...indexBounds.lowerBound],
1372
+ lowerBoundInclusive: indexBounds.lowerBoundInclusive,
1373
+ upperBound: [...this.#staticFilter, ...indexBounds.upperBound],
1374
+ upperBoundInclusive: indexBounds.upperBoundInclusive
1375
+ }), this.#indexFields);
1376
+ }
1377
+ };
1378
+ var DistinctStream = class DistinctStream extends QueryStream {
1379
+ #distinctIndexFieldsLength;
1380
+ #stream;
1381
+ #distinctIndexFields;
1382
+ constructor(stream, distinctIndexFields) {
1383
+ super();
1384
+ this.#stream = stream;
1385
+ this.#distinctIndexFields = distinctIndexFields;
1386
+ let distinctIndexFieldsLength;
1387
+ for (const orderingIndexFields of getOrderingIndexFields(stream)) if (equalIndexFields(orderingIndexFields.slice(0, distinctIndexFields.length), distinctIndexFields)) {
1388
+ distinctIndexFieldsLength = stream.getIndexFields().length - orderingIndexFields.length + distinctIndexFields.length;
1389
+ break;
1390
+ }
1391
+ if (distinctIndexFieldsLength === void 0) throw new Error(`distinctIndexFields must be a prefix of the stream's ordering index fields: ${JSON.stringify(distinctIndexFields)}, ${JSON.stringify(stream.getIndexFields())} (${stream.getEqualityIndexFilter().length} equality fields)`);
1392
+ this.#distinctIndexFieldsLength = distinctIndexFieldsLength;
1393
+ }
1394
+ iterWithKeys() {
1395
+ const stream = this.#stream;
1396
+ const distinctIndexFieldsLength = this.#distinctIndexFieldsLength;
1397
+ return { [Symbol.asyncIterator]() {
1398
+ let currentStream = stream;
1399
+ let currentIterator = currentStream.iterWithKeys()[Symbol.asyncIterator]();
1400
+ return { async next() {
1401
+ const result = await currentIterator.next();
1402
+ if (result.done) return {
1403
+ done: true,
1404
+ value: void 0
1405
+ };
1406
+ const [doc, indexKey] = result.value;
1407
+ if (doc === null) return {
1408
+ done: false,
1409
+ value: [null, indexKey]
1410
+ };
1411
+ const distinctIndexKey = indexKey.slice(0, distinctIndexFieldsLength);
1412
+ if (stream.getOrder() === "asc") currentStream = currentStream.narrow({
1413
+ lowerBound: distinctIndexKey,
1414
+ lowerBoundInclusive: false,
1415
+ upperBound: [],
1416
+ upperBoundInclusive: true
1417
+ });
1418
+ else currentStream = currentStream.narrow({
1419
+ lowerBound: [],
1420
+ lowerBoundInclusive: true,
1421
+ upperBound: distinctIndexKey,
1422
+ upperBoundInclusive: false
1423
+ });
1424
+ currentIterator = currentStream.iterWithKeys()[Symbol.asyncIterator]();
1425
+ return result;
1426
+ } };
1427
+ } };
1428
+ }
1429
+ narrow(indexBounds) {
1430
+ const indexBoundsPrefix = {
1431
+ ...indexBounds,
1432
+ lowerBound: indexBounds.lowerBound.slice(0, this.#distinctIndexFieldsLength),
1433
+ upperBound: indexBounds.upperBound.slice(0, this.#distinctIndexFieldsLength)
1434
+ };
1435
+ return new DistinctStream(this.#stream.narrow(indexBoundsPrefix), this.#distinctIndexFields);
1436
+ }
1437
+ getOrder() {
1438
+ return this.#stream.getOrder();
1439
+ }
1440
+ getIndexFields() {
1441
+ return this.#stream.getIndexFields();
1442
+ }
1443
+ getEqualityIndexFilter() {
1444
+ return this.#stream.getEqualityIndexFilter();
1445
+ }
1446
+ };
1447
+ function equalIndexFields(indexFields1, indexFields2) {
1448
+ if (indexFields1.length !== indexFields2.length) return false;
1449
+ for (let i = 0; i < indexFields1.length; i++) if (indexFields1[i] !== indexFields2[i]) return false;
1450
+ return true;
1451
+ }
1452
+ function getValueAtIndex(v, index) {
1453
+ if (index >= v.length) return;
1454
+ return {
1455
+ kind: "found",
1456
+ value: v[index]
1457
+ };
1458
+ }
1459
+ function compareDanglingSuffix(shorterKeyKind, longerKeyKind, shorterKey, longerKey) {
1460
+ if (shorterKeyKind === "exact" && longerKeyKind === "exact") throw new Error(`Exact keys are not the same length: ${JSON.stringify(shorterKey.value)}, ${JSON.stringify(longerKey.value)}`);
1461
+ if (shorterKeyKind === "exact") throw new Error(`Exact key is shorter than prefix: ${JSON.stringify(shorterKey.value)}, ${JSON.stringify(longerKey.value)}`);
1462
+ if (shorterKeyKind === "predecessor" && longerKeyKind === "successor") return -1;
1463
+ if (shorterKeyKind === "successor" && longerKeyKind === "predecessor") return 1;
1464
+ if (shorterKeyKind === "predecessor" && longerKeyKind === "predecessor") return -1;
1465
+ if (shorterKeyKind === "successor" && longerKeyKind === "successor") return 1;
1466
+ if (shorterKeyKind === "predecessor" && longerKeyKind === "exact") return -1;
1467
+ if (shorterKeyKind === "successor" && longerKeyKind === "exact") return 1;
1468
+ throw new Error(`Unexpected key kinds: ${shorterKeyKind}, ${longerKeyKind}`);
1469
+ }
1470
+ function compareKeys(key1, key2) {
1471
+ let i = 0;
1472
+ while (i < Math.max(key1.value.length, key2.value.length)) {
1473
+ const v1 = getValueAtIndex(key1.value, i);
1474
+ const v2 = getValueAtIndex(key2.value, i);
1475
+ if (v1 === void 0) return compareDanglingSuffix(key1.kind, key2.kind, key1, key2);
1476
+ if (v2 === void 0) return -1 * compareDanglingSuffix(key2.kind, key1.kind, key2, key1);
1477
+ const result = compareValues(v1.value, v2.value);
1478
+ if (result !== 0) return result;
1479
+ i++;
1480
+ }
1481
+ if (key1.kind === key2.kind) return 0;
1482
+ if (key1.kind === "exact") {
1483
+ if (key2.kind === "successor") return -1;
1484
+ return 1;
1485
+ }
1486
+ if (key1.kind === "predecessor") return -1;
1487
+ if (key1.kind === "successor") return 1;
1488
+ throw new Error(`Unexpected key kind: ${key1.kind}`);
1489
+ }
1490
+ function serializeCursor(key) {
1491
+ return JSON.stringify(convexToJson(key.map((v) => v === void 0 ? "undefined" : typeof v === "string" && v.endsWith("undefined") ? `_${v}` : v)));
1492
+ }
1493
+ function deserializeCursor(cursor) {
1494
+ let parsed;
1495
+ try {
1496
+ parsed = JSON.parse(cursor);
1497
+ } catch {
1498
+ throw new Error("Invalid pagination cursor for stream-backed pagination. Use the continueCursor returned by the same findMany query shape.");
1499
+ }
1500
+ return jsonToConvex(parsed).map((v) => {
1501
+ if (typeof v === "string") {
1502
+ if (v === "undefined") return;
1503
+ if (v.endsWith("undefined")) return v.slice(1);
1504
+ }
1505
+ return v;
1506
+ });
1507
+ }
1508
+
1509
+ //#endregion
1510
+ //#region src/orm/query-context.ts
1511
+ async function getByIdWithOrmQueryFallback(ctx, tableName, id) {
1512
+ const ormTableQuery = ctx.orm?.query?.[tableName];
1513
+ if (ormTableQuery?.findFirst) return await ormTableQuery.findFirst({ where: { id } });
1514
+ return await ctx.db.get(id);
1515
+ }
1516
+
1517
+ //#endregion
1518
+ export { ne as A, inArray as C, like as D, isNull as E, notLike as F, or as I, startsWith as L, notBetween as M, notIlike as N, lt as O, notInArray as P, ilike as S, isNotNull as T, endsWith as _, mergedStream as a, gt as b, isUnsetToken as c, arrayContained as d, arrayContains as f, contains as g, column as h, getIndexFields as i, not as j, lte as k, unsetToken as l, between as m, EmptyStream as n, stream as o, arrayOverlaps as p, QueryStream as r, streamIndexRange as s, getByIdWithOrmQueryFallback as t, and as u, eq as v, isFieldReference as w, gte as x, fieldRef as y };