@gaganref/confect-server 10.3.1-fork.8 → 10.3.1-fork.80

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 (180) hide show
  1. package/dist/ActionCall.d.ts +30 -0
  2. package/dist/ActionCall.d.ts.map +1 -0
  3. package/dist/ActionCall.js +41 -0
  4. package/dist/ActionCall.js.map +1 -0
  5. package/dist/ActionRunner.d.ts +1 -1
  6. package/dist/ActionRunner.d.ts.map +1 -1
  7. package/dist/ActionRunner.js +3 -3
  8. package/dist/ActionRunner.js.map +1 -1
  9. package/dist/AiGatewayDecisionClient.js +1 -1
  10. package/dist/AiGatewayLanguageClient.js +1 -1
  11. package/dist/Auth.d.ts +31 -2
  12. package/dist/Auth.d.ts.map +1 -1
  13. package/dist/Auth.js +23 -3
  14. package/dist/Auth.js.map +1 -1
  15. package/dist/Callback.d.ts +1 -1
  16. package/dist/Callback.d.ts.map +1 -1
  17. package/dist/Callback.js +6 -6
  18. package/dist/Callback.js.map +1 -1
  19. package/dist/ConvexConfigProvider.js +2 -2
  20. package/dist/ConvexFunctionDefinitions.d.ts.map +1 -1
  21. package/dist/ConvexFunctionDefinitions.js +4 -4
  22. package/dist/ConvexFunctionDefinitions.js.map +1 -1
  23. package/dist/ConvexLogger.js +1 -1
  24. package/dist/CronJobs.d.ts.map +1 -1
  25. package/dist/CronJobs.js +7 -6
  26. package/dist/CronJobs.js.map +1 -1
  27. package/dist/DatabaseSchema.js +2 -2
  28. package/dist/DatabaseWriter.d.ts +36 -2
  29. package/dist/DatabaseWriter.d.ts.map +1 -1
  30. package/dist/DatabaseWriter.js +55 -8
  31. package/dist/DatabaseWriter.js.map +1 -1
  32. package/dist/ExecutionMetadata.js +1 -1
  33. package/dist/FunctionImpl.js +2 -2
  34. package/dist/FunctionRegistryItem.d.ts +1 -0
  35. package/dist/FunctionRegistryItem.d.ts.map +1 -1
  36. package/dist/FunctionRegistryItem.js +2 -1
  37. package/dist/FunctionRegistryItem.js.map +1 -1
  38. package/dist/GroupImpl.d.ts +21 -2
  39. package/dist/GroupImpl.d.ts.map +1 -1
  40. package/dist/GroupImpl.js +21 -22
  41. package/dist/GroupImpl.js.map +1 -1
  42. package/dist/GroupRegistry.d.ts +1 -0
  43. package/dist/GroupRegistry.d.ts.map +1 -1
  44. package/dist/GroupRegistry.js +10 -3
  45. package/dist/GroupRegistry.js.map +1 -1
  46. package/dist/Handler.d.ts +8 -4
  47. package/dist/Handler.d.ts.map +1 -1
  48. package/dist/Handler.js.map +1 -1
  49. package/dist/HttpRouter.d.ts +3 -0
  50. package/dist/HttpRouter.d.ts.map +1 -1
  51. package/dist/HttpRouter.js +29 -10
  52. package/dist/HttpRouter.js.map +1 -1
  53. package/dist/MiddlewareImpl.js +1 -1
  54. package/dist/MountInit.d.ts +31 -0
  55. package/dist/MountInit.d.ts.map +1 -0
  56. package/dist/MountInit.js +49 -0
  57. package/dist/MountInit.js.map +1 -0
  58. package/dist/MutationRunner.d.ts +1 -1
  59. package/dist/MutationRunner.d.ts.map +1 -1
  60. package/dist/MutationRunner.js +3 -3
  61. package/dist/MutationRunner.js.map +1 -1
  62. package/dist/MutationTransactionContext.d.ts.map +1 -1
  63. package/dist/MutationTransactionContext.js +4 -3
  64. package/dist/MutationTransactionContext.js.map +1 -1
  65. package/dist/OfficialComponent.d.ts +60 -0
  66. package/dist/OfficialComponent.d.ts.map +1 -0
  67. package/dist/OfficialComponent.js +72 -0
  68. package/dist/OfficialComponent.js.map +1 -0
  69. package/dist/OrderedQuery.d.ts +4 -5
  70. package/dist/OrderedQuery.d.ts.map +1 -1
  71. package/dist/OrderedQuery.js.map +1 -1
  72. package/dist/QueryInitializer.js +2 -2
  73. package/dist/QueryRunner.js +2 -2
  74. package/dist/QueryStream.d.ts +120 -12
  75. package/dist/QueryStream.d.ts.map +1 -1
  76. package/dist/QueryStream.js +143 -41
  77. package/dist/QueryStream.js.map +1 -1
  78. package/dist/QueryStreamCursor.js +1 -1
  79. package/dist/QueryStreamIndexPrefix.js +1 -1
  80. package/dist/QueryStreamIndexRange.js +2 -2
  81. package/dist/QueryStreamKey.js +1 -1
  82. package/dist/QueryStreamKeyBounds.js +2 -2
  83. package/dist/QueryStreamKeyLayout.js +2 -2
  84. package/dist/QueryStreamKeyValues.js +2 -2
  85. package/dist/QueryStreamPagination.js +1 -1
  86. package/dist/QueryStreamReadBudget.d.ts +3 -2
  87. package/dist/QueryStreamReadBudget.d.ts.map +1 -1
  88. package/dist/QueryStreamReadBudget.js +8 -5
  89. package/dist/QueryStreamReadBudget.js.map +1 -1
  90. package/dist/QueryTransactionContext.js +2 -2
  91. package/dist/RegisteredConvexFunction.d.ts +10 -5
  92. package/dist/RegisteredConvexFunction.d.ts.map +1 -1
  93. package/dist/RegisteredConvexFunction.js +45 -29
  94. package/dist/RegisteredConvexFunction.js.map +1 -1
  95. package/dist/RegisteredFunction.d.ts +39 -22
  96. package/dist/RegisteredFunction.d.ts.map +1 -1
  97. package/dist/RegisteredFunction.js +155 -42
  98. package/dist/RegisteredFunction.js.map +1 -1
  99. package/dist/RegisteredNodeFunction.d.ts +1 -1
  100. package/dist/RegisteredNodeFunction.d.ts.map +1 -1
  101. package/dist/RegisteredNodeFunction.js +10 -6
  102. package/dist/RegisteredNodeFunction.js.map +1 -1
  103. package/dist/RegistryItems.d.ts +7 -0
  104. package/dist/RegistryItems.d.ts.map +1 -1
  105. package/dist/RegistryItems.js +8 -3
  106. package/dist/RegistryItems.js.map +1 -1
  107. package/dist/RequestMetadata.js +1 -1
  108. package/dist/Scheduler.d.ts +1 -1
  109. package/dist/Scheduler.d.ts.map +1 -1
  110. package/dist/Scheduler.js +14 -13
  111. package/dist/Scheduler.js.map +1 -1
  112. package/dist/Storage.js +1 -1
  113. package/dist/StorageActionWriter.js +1 -1
  114. package/dist/StorageReader.js +1 -1
  115. package/dist/StorageWriter.js +1 -1
  116. package/dist/Table.d.ts +6 -6
  117. package/dist/Table.d.ts.map +1 -1
  118. package/dist/TableInfo.d.ts +1 -1
  119. package/dist/TableInfo.d.ts.map +1 -1
  120. package/dist/TableInfo.js.map +1 -1
  121. package/dist/TransactionMetadata.js +1 -1
  122. package/dist/VectorSearch.js +1 -1
  123. package/dist/index.d.ts +3 -0
  124. package/dist/index.d.ts.map +1 -1
  125. package/dist/index.js +21 -18
  126. package/dist/internal/AiGatewayDecisionClient.js +1 -1
  127. package/dist/internal/AiGatewayLanguageClient.js +1 -1
  128. package/dist/internal/AiGatewayServiceToken.js +1 -1
  129. package/dist/internal/DocumentPatch.d.ts +16 -0
  130. package/dist/internal/DocumentPatch.d.ts.map +1 -0
  131. package/dist/internal/DocumentPatch.js +36 -0
  132. package/dist/internal/DocumentPatch.js.map +1 -0
  133. package/dist/internal/httpBody.d.ts +13 -0
  134. package/dist/internal/httpBody.d.ts.map +1 -0
  135. package/dist/internal/httpBody.js +47 -0
  136. package/dist/internal/httpBody.js.map +1 -0
  137. package/dist/internal/httpOptions.d.ts +4 -0
  138. package/dist/internal/httpOptions.d.ts.map +1 -0
  139. package/dist/internal/httpOptions.js +83 -0
  140. package/dist/internal/httpOptions.js.map +1 -0
  141. package/dist/internal/observability.d.ts +5 -0
  142. package/dist/internal/observability.d.ts.map +1 -0
  143. package/dist/internal/observability.js +21 -0
  144. package/dist/internal/observability.js.map +1 -0
  145. package/dist/internal/reservedArgument.d.ts +8 -0
  146. package/dist/internal/reservedArgument.d.ts.map +1 -0
  147. package/dist/internal/reservedArgument.js +30 -0
  148. package/dist/internal/reservedArgument.js.map +1 -0
  149. package/package.json +5 -5
  150. package/src/ActionCall.ts +83 -0
  151. package/src/ActionRunner.ts +3 -2
  152. package/src/Auth.ts +49 -1
  153. package/src/Callback.ts +3 -2
  154. package/src/ConvexFunctionDefinitions.ts +2 -1
  155. package/src/CronJobs.ts +2 -1
  156. package/src/DatabaseWriter.ts +183 -25
  157. package/src/FunctionRegistryItem.ts +4 -0
  158. package/src/GroupImpl.ts +61 -8
  159. package/src/GroupRegistry.ts +7 -3
  160. package/src/Handler.ts +21 -5
  161. package/src/HttpRouter.ts +69 -5
  162. package/src/MountInit.ts +80 -0
  163. package/src/MutationRunner.ts +3 -2
  164. package/src/MutationTransactionContext.ts +2 -1
  165. package/src/OfficialComponent.ts +168 -0
  166. package/src/OrderedQuery.ts +4 -5
  167. package/src/QueryStream.ts +391 -61
  168. package/src/QueryStreamReadBudget.ts +19 -6
  169. package/src/RegisteredConvexFunction.ts +35 -2
  170. package/src/RegisteredFunction.ts +251 -45
  171. package/src/RegisteredNodeFunction.ts +11 -1
  172. package/src/RegistryItems.ts +31 -0
  173. package/src/Scheduler.ts +17 -19
  174. package/src/TableInfo.ts +2 -1
  175. package/src/index.ts +3 -0
  176. package/src/internal/DocumentPatch.ts +87 -0
  177. package/src/internal/httpBody.ts +72 -0
  178. package/src/internal/httpOptions.ts +154 -0
  179. package/src/internal/observability.ts +27 -0
  180. package/src/internal/reservedArgument.ts +56 -0
@@ -11,17 +11,17 @@ import { admittedByLower, admittedByUpper, forLayout, fromParsed, intersect, int
11
11
  import { apply, equalityPrefixLength, fromBounds, toBounds as toBounds$1 } from "./QueryStreamIndexRange.js";
12
12
  import { EmptyInitialPageError, InvalidPageSizeError, Range, Start, finish, initial, parseRequest, record } from "./QueryStreamPagination.js";
13
13
  import { InvalidReadLimitError, Limits, QueryStreamReadBudget, make as make$1 } from "./QueryStreamReadBudget.js";
14
+ import { ConvexError } from "convex/values";
14
15
  import * as Effect from "effect/Effect";
16
+ import * as Data from "effect/Data";
17
+ import * as Predicate from "effect/Predicate";
18
+ import * as Array from "effect/Array";
15
19
  import * as Schema from "effect/Schema";
16
20
  import { dual, identity, pipe } from "effect/Function";
17
21
  import * as Match from "effect/Match";
18
22
  import * as Option from "effect/Option";
19
- import { ConvexError } from "convex/values";
20
- import * as Array from "effect/Array";
21
23
  import * as Order from "effect/Order";
22
- import * as Predicate from "effect/Predicate";
23
24
  import * as Result from "effect/Result";
24
- import * as Data from "effect/Data";
25
25
  import * as Stream from "effect/Stream";
26
26
  import { pipeArguments } from "effect/Pipeable";
27
27
  import * as Chunk from "effect/Chunk";
@@ -32,6 +32,8 @@ import * as String from "effect/String";
32
32
  import * as Tuple from "effect/Tuple";
33
33
  //#region src/QueryStream.ts
34
34
  var QueryStream_exports = /* @__PURE__ */ __exportAll({
35
+ DEFAULT_MAXIMUM_ROWS_READ: () => DEFAULT_MAXIMUM_ROWS_READ,
36
+ DeferredStreamMismatchError: () => DeferredStreamMismatchError,
35
37
  Element: () => Element,
36
38
  EmptyInitialPageError: () => EmptyInitialPageError,
37
39
  IncompatibleStreamsError: () => IncompatibleStreamsError,
@@ -44,6 +46,8 @@ var QueryStream_exports = /* @__PURE__ */ __exportAll({
44
46
  QueryStream: () => QueryStream,
45
47
  ReadBudgetExceededError: () => ReadBudgetExceededError,
46
48
  TypeId: () => TypeId,
49
+ collect: () => collect,
50
+ defer: () => defer,
47
51
  distinct: () => distinct,
48
52
  empty: () => empty,
49
53
  filter: () => filter,
@@ -61,7 +65,15 @@ var QueryStream_exports = /* @__PURE__ */ __exportAll({
61
65
  unique: () => unique
62
66
  });
63
67
  /**
64
- * A page exhausted its read budget without a safe logical continuation.
68
+ * Default physical row budget for `paginate` and `collect`. Supply an explicit
69
+ * `maximumRowsRead` to override it. Plain Effect Stream consumers use their
70
+ * enclosing budget and remain unlimited when none is provided.
71
+ *
72
+ * @experimental
73
+ */
74
+ const DEFAULT_MAXIMUM_ROWS_READ = 2e3;
75
+ /**
76
+ * A query stream exhausted its read budget before its consumer could finish.
65
77
  *
66
78
  * @experimental
67
79
  */
@@ -70,7 +82,7 @@ var ReadBudgetExceededError = class extends Schema.TaggedError()("ReadBudgetExce
70
82
  bytesRead: Schema.Natural
71
83
  }) {
72
84
  get message() {
73
- return "The read budget was exhausted before a safe page boundary; increase the budget or simplify the query";
85
+ return "The read budget was exhausted before the query stream completed; increase the budget or simplify the query";
74
86
  }
75
87
  };
76
88
  /**
@@ -234,6 +246,40 @@ const empty = () => (keyLayout, orderDirection = "asc") => {
234
246
  return make(orderDirection);
235
247
  };
236
248
  /**
249
+ * A deferred stream's resolved direction or layout differs from its
250
+ * declaration. The check runs on consumption, before the resolved source is
251
+ * pulled.
252
+ *
253
+ * @experimental
254
+ */
255
+ var DeferredStreamMismatchError = class extends Data.TaggedError("DeferredStreamMismatchError") {};
256
+ /**
257
+ * Defer an effect that checks access or constructs a query stream until it is
258
+ * pulled. Supply its layout and direction without reading documents. This lets
259
+ * `flatMap` build and narrow an inner stream before running its checks.
260
+ *
261
+ * The effect runs afresh on each consumption, seek, or reversal. Failures and
262
+ * service requirements are preserved. Its stream must match the declared layout
263
+ * and direction, or consumption dies with `DeferredStreamMismatchError`.
264
+ *
265
+ * @experimental
266
+ */
267
+ const defer = (options, make) => {
268
+ const checked = Effect.map(make, (stream) => {
269
+ if (stream.orderDirection !== options.orderDirection || !Equivalence(stream.keyLayout, options.keyLayout)) throw new DeferredStreamMismatchError({
270
+ expectedOrderDirection: options.orderDirection,
271
+ actualOrderDirection: stream.orderDirection,
272
+ expectedKeyLayout: options.keyLayout,
273
+ actualKeyLayout: stream.keyLayout
274
+ });
275
+ return stream;
276
+ });
277
+ return new QueryStream(options.orderDirection, options.keyLayout, Stream.unwrap(Effect.map(checked, (stream) => stream.annotated)), void 0, (bounds) => defer(options, Effect.map(checked, (stream) => narrowByParsedBounds(stream, bounds))), () => defer({
278
+ ...options,
279
+ orderDirection: flip(options.orderDirection)
280
+ }, Effect.map(checked, reverse)));
281
+ };
282
+ /**
237
283
  * Create a reusable query stream from an index-query description. Prefer
238
284
  * `reader.table(...).stream(...)` for inferred document and ordering types.
239
285
  *
@@ -297,13 +343,21 @@ const fillMergeSource = (source) => MergeSource.$match(source, {
297
343
  Exhausted: () => Effect.succeed(source),
298
344
  BudgetLimited: () => Effect.succeed(source)
299
345
  });
346
+ const consumeMergeHead = (source) => source._tag === "Buffered" ? Option.match(Chunk.head(source.tail), {
347
+ onNone: () => MergeSource.NeedsPull({ pull: source.pull }),
348
+ onSome: (head) => MergeSource.Buffered({
349
+ pull: source.pull,
350
+ head,
351
+ tail: Chunk.drop(source.tail, 1)
352
+ })
353
+ }) : source;
300
354
  /**
301
355
  * One step of the k-way merge as a pure unfold: fill every source, emit the
302
356
  * earliest head (ties go to the earliest source, keeping the merge stable), and
303
357
  * return the sources with that head consumed. `undefined` when every source is
304
358
  * exhausted or an input stopped before its next key was known.
305
359
  */
306
- const mergeStep = (keyOrder) => (sources) => Effect.gen(function* () {
360
+ const mergeStep = (keyOrder, dedupe) => (sources) => Effect.gen(function* () {
307
361
  const readBudget = yield* QueryStreamReadBudget;
308
362
  const filled = yield* Effect.forEach(sources, fillMergeSource, { concurrency: (yield* readBudget.isUnlimited) ? "unbounded" : 1 });
309
363
  if (filled.some((source) => MergeSource.$is("BudgetLimited")(source))) return void 0;
@@ -321,23 +375,35 @@ const mergeStep = (keyOrder) => (sources) => Effect.gen(function* () {
321
375
  })) : best
322
376
  })
323
377
  }));
324
- return Option.getOrUndefined(Option.map(earliest, ({ index, element }) => Tuple.make(element, Array.map(filled, (source, sourceIndex) => sourceIndex === index && source._tag === "Buffered" ? Option.match(Chunk.head(source.tail), {
325
- onNone: () => MergeSource.NeedsPull({ pull: source.pull }),
326
- onSome: (head) => MergeSource.Buffered({
327
- pull: source.pull,
328
- head,
329
- tail: Chunk.drop(source.tail, 1)
330
- })
331
- }) : source))));
378
+ if (Option.isNone(earliest)) return void 0;
379
+ if (!dedupe) {
380
+ const { index, element } = earliest.value;
381
+ return Tuple.make(element, Array.map(filled, (source, sourceIndex) => sourceIndex === index ? consumeMergeHead(source) : source));
382
+ }
383
+ const keyValues = earliest.value.element.keyValues;
384
+ const matches = (source) => source._tag === "Buffered" && keyOrder(source.head.keyValues, keyValues) === 0;
385
+ let representative = earliest.value;
386
+ let remaining = filled;
387
+ for (;;) {
388
+ representative = Array.reduce(remaining, representative, (best, source, index) => source._tag === "Buffered" && matches(source) && Option.isSome(source.head.doc) && (Option.isNone(best.element.doc) || index < best.index) ? new MergeCandidate({
389
+ index,
390
+ element: source.head
391
+ }) : best);
392
+ remaining = yield* Effect.forEach(Array.map(remaining, (source) => matches(source) ? consumeMergeHead(source) : source), fillMergeSource, { concurrency: (yield* readBudget.isUnlimited) ? "unbounded" : 1 });
393
+ if (remaining.some((source) => MergeSource.$is("BudgetLimited")(source))) return void 0;
394
+ if (!remaining.some(matches)) return Tuple.make(representative.element, remaining);
395
+ }
332
396
  });
333
397
  /**
334
398
  * Combine streams sharing a key layout into one stream in key order. Use this
335
399
  * to query several index ranges at once, such as notes by multiple roles.
336
400
  *
337
401
  * The merge emits the smallest next key when ascending and the largest when
338
- * descending. Unlike `Stream.merge`, it does not interleave by arrival time.
339
- * Overlapping inputs are not deduplicated: a document matched twice appears
340
- * twice. Supply at least one input; use `empty` for an empty dynamic list.
402
+ * descending. Unlike `Stream.merge`, it does not interleave by arrival time. By
403
+ * default, a document matched twice appears twice. Pass `{ dedupe: true }` to
404
+ * coalesce equal complete keys, including ID tiebreakers. Resolving a group can
405
+ * prefetch the next key from each input; all such reads count against the
406
+ * shared budget. Supply at least one input; use `empty` for an empty list.
341
407
  *
342
408
  * Inputs need compatible document types and identical key layouts and
343
409
  * directions. TypeScript rejects known mismatches; runtime mismatches throw
@@ -346,7 +412,7 @@ const mergeStep = (keyOrder) => (sources) => Effect.gen(function* () {
346
412
  *
347
413
  * @experimental
348
414
  */
349
- const merge = (streams) => {
415
+ const merge = (streams, options) => {
350
416
  const head = Array.headNonEmpty(streams);
351
417
  const incompatible = Array.findFirst(Array.tailNonEmpty(streams), (stream) => stream.orderDirection !== head.orderDirection || !Equivalence(stream.keyLayout, head.keyLayout));
352
418
  if (Option.isSome(incompatible)) throw new IncompatibleStreamsError({
@@ -355,17 +421,17 @@ const merge = (streams) => {
355
421
  expectedKeyLayout: head.keyLayout,
356
422
  actualKeyLayout: incompatible.value.keyLayout
357
423
  });
358
- return mergeUnchecked(streams);
424
+ return mergeUnchecked(streams, options);
359
425
  };
360
426
  /**
361
427
  * `merge` without the compatibility check—for re-merging branches that were
362
428
  * validated when the merge was built (narrowing never changes a branch's order
363
429
  * or key layout).
364
430
  */
365
- const mergeUnchecked = (streams) => {
431
+ const mergeUnchecked = (streams, options) => {
366
432
  const head = Array.headNonEmpty(streams);
367
- const annotated = Stream.unwrap(Effect.map(Effect.forEach(streams, (stream) => Stream.toPull(stream.annotated)), (pulls) => Stream.unfold(Array.map(pulls, (pull) => MergeSource.NeedsPull({ pull })), mergeStep(Order$1(head.orderDirection)))));
368
- return new QueryStream(head.orderDirection, head.keyLayout, annotated, void 0, (parsedBounds) => mergeUnchecked([narrowByParsedBounds(head, parsedBounds), ...Array.map(Array.tailNonEmpty(streams), (stream) => narrowByParsedBounds(stream, parsedBounds))]), () => mergeUnchecked([reverse(head), ...Array.map(Array.tailNonEmpty(streams), (stream) => reverse(stream))]));
433
+ const annotated = Stream.unwrap(Effect.map(Effect.forEach(streams, (stream) => Stream.toPull(stream.annotated)), (pulls) => Stream.unfold(Array.map(pulls, (pull) => MergeSource.NeedsPull({ pull })), mergeStep(Order$1(head.orderDirection), options?.dedupe === true))));
434
+ return new QueryStream(head.orderDirection, head.keyLayout, annotated, void 0, (parsedBounds) => mergeUnchecked([narrowByParsedBounds(head, parsedBounds), ...Array.map(Array.tailNonEmpty(streams), (stream) => narrowByParsedBounds(stream, parsedBounds))], options), () => mergeUnchecked([reverse(head), ...Array.map(Array.tailNonEmpty(streams), (stream) => reverse(stream))], options));
369
435
  };
370
436
  /**
371
437
  * Derive a stream by transforming each present document into `Some` (keep,
@@ -782,10 +848,10 @@ const narrowByParsedBounds = (self, parsedBounds) => {
782
848
  * The fallback for streams that don't know how to rebuild themselves.
783
849
  */
784
850
  const keyPredicates = (parsedBounds) => {
785
- const complete$1 = (keyValues) => Result.getOrThrowWith(complete(parsedBounds.keyLayout, keyValues), identity);
851
+ const complete$2 = (keyValues) => Result.getOrThrowWith(complete(parsedBounds.keyLayout, keyValues), identity);
786
852
  return {
787
- aboveLower: (keyValues) => Result.getOrThrowWith(admittedByLower(parsedBounds)(complete$1(keyValues)), identity),
788
- belowUpper: (keyValues) => Result.getOrThrowWith(admittedByUpper(parsedBounds)(complete$1(keyValues)), identity)
853
+ aboveLower: (keyValues) => Result.getOrThrowWith(admittedByLower(parsedBounds)(complete$2(keyValues)), identity),
854
+ belowUpper: (keyValues) => Result.getOrThrowWith(admittedByUpper(parsedBounds)(complete$2(keyValues)), identity)
789
855
  };
790
856
  };
791
857
  const narrowInMemory = (self, parsedBounds) => {
@@ -795,6 +861,30 @@ const narrowInMemory = (self, parsedBounds) => {
795
861
  return new QueryStream(self.orderDirection, self.keyLayout, pipe(self.annotated, dropOutOfRange, takeInRange), void 0, void 0, self.reverseWith === void 0 ? void 0 : () => narrowInMemory(reverse(self), parsedBounds));
796
862
  };
797
863
  /**
864
+ * Collect the whole query stream or fail with `ReadBudgetExceededError` when a
865
+ * read budget stops it. No partial array reaches the caller. Arbitrary I/O in
866
+ * callbacks is not counted. Use `paginate` for data that can grow without
867
+ * bound.
868
+ *
869
+ * With explicit options, each run gets a fresh budget. Without options, use the
870
+ * enclosing budget if present, or the default row budget.
871
+ *
872
+ * @experimental
873
+ */
874
+ const collect = dual((args) => isQueryStream(args[0]), Effect.fn("QueryStream.collect")(function* (self, options) {
875
+ const makeBudget = Schema.decodeEffect(Limits)({
876
+ maximumRowsRead: DEFAULT_MAXIMUM_ROWS_READ,
877
+ ...options
878
+ }).pipe(Effect.catchTag("SchemaError", (cause) => Effect.die(new InvalidReadLimitError({ cause }))), Effect.flatMap(make$1));
879
+ const budget = yield* options === void 0 ? Effect.serviceOption(QueryStreamReadBudget).pipe(Effect.flatMap(Option.match({
880
+ onNone: () => makeBudget,
881
+ onSome: Effect.succeed
882
+ }))) : makeBudget;
883
+ const rows = yield* Stream.runCollect(self.toStream()).pipe(Effect.provideService(QueryStreamReadBudget, budget));
884
+ if (yield* budget.isStopped) return yield* new ReadBudgetExceededError(yield* budget.getReadCounts);
885
+ return rows;
886
+ }));
887
+ /**
798
888
  * `unique` found at least two emitted values. Narrow the query if at most one
799
889
  * value was expected, or use another consumer to handle multiple results.
800
890
  *
@@ -846,9 +936,9 @@ const unique = Effect.fn("QueryStream.unique")((self) => self.pipe(Stream.take(2
846
936
  * query semantics change, even if an old cursor is still accepted.
847
937
  *
848
938
  * Cursors expose boundary field names and key values, including document IDs.
849
- * They are neither opaque nor signed: clients can read or craft them. Do not
850
- * publicly paginate over sensitive indexed fields unless pinned with `eq`.
851
- * Never construct cursors from documents or decode them into `narrow` bounds.
939
+ * Without a `cursorEnvelope`, clients can read or craft them. Do not publicly
940
+ * paginate over sensitive indexed fields unless pinned with `eq`. Never
941
+ * construct cursors from documents or decode them into `narrow` bounds.
852
942
  *
853
943
  * Return the whole result to reactive clients. React's
854
944
  * `useStreamPaginatedQuery` handles pinning, splitting, and invalid-cursor
@@ -858,19 +948,28 @@ const unique = Effect.fn("QueryStream.unique")((self) => self.pipe(Stream.take(2
858
948
  * @experimental
859
949
  */
860
950
  const paginate = dual(2, Effect.fn("QueryStream.paginate")(function* (self, options) {
951
+ const seal = (cursor) => options.cursorEnvelope === void 0 ? Effect.succeed(cursor) : options.cursorEnvelope.seal(cursor);
952
+ const open = (cursor) => options.cursorEnvelope === void 0 ? Effect.succeed(cursor) : options.cursorEnvelope.open(cursor).pipe(Effect.flatMap(Option.match({
953
+ onNone: () => Effect.die(new ConvexError({ paginationError: "InvalidCursor" })),
954
+ onSome: Effect.succeed
955
+ })));
956
+ const opened = yield* Effect.all({
957
+ start: options.cursor === null ? Effect.succeed(null) : open(options.cursor),
958
+ end: options.endCursor == null ? Effect.succeed(null) : open(options.endCursor)
959
+ });
861
960
  const cursorSchema = fromKeyLayout(self.keyLayout);
862
961
  const encodeCursor = Schema.encodeEffect(cursorSchema);
863
962
  const decodeCursor = Schema.decodeEffect(cursorSchema);
864
- const complete$2 = (keyValues) => Result.getOrThrowWith(complete(self.keyLayout, keyValues), identity);
963
+ const complete$1 = (keyValues) => Result.getOrThrowWith(complete(self.keyLayout, keyValues), identity);
865
964
  const decoded = yield* Effect.all({
866
- start: Option.match(Option.fromNullOr(options.cursor), {
965
+ start: Option.match(Option.fromNullOr(opened.start), {
867
966
  onNone: () => Effect.succeed(Start.Beginning()),
868
967
  onSome: (cursor) => Effect.map(decodeCursor(cursor), (key) => Start.After({
869
968
  cursor,
870
969
  key
871
970
  }))
872
971
  }),
873
- range: Option.match(Option.fromNullishOr(options.endCursor), {
972
+ range: Option.match(Option.fromNullishOr(opened.end), {
874
973
  onNone: () => Effect.succeed(Range.Unpinned()),
875
974
  onSome: (cursor) => cursor === "[]" ? Effect.succeed(Range.ThroughEnd()) : Effect.map(decodeCursor(cursor), (key) => Range.ThroughKey({ key }))
876
975
  })
@@ -882,7 +981,7 @@ const paginate = dual(2, Effect.fn("QueryStream.paginate")(function* (self, opti
882
981
  if (request._tag === "Unchanged") return {
883
982
  page: [],
884
983
  isDone: false,
885
- continueCursor: request.cursor
984
+ continueCursor: yield* seal(request.cursor)
886
985
  };
887
986
  const start = Option.map(request.after, (key) => ({
888
987
  key: toPrefix(key),
@@ -904,14 +1003,14 @@ const paginate = dual(2, Effect.fn("QueryStream.paginate")(function* (self, opti
904
1003
  upper: start
905
1004
  }), identity));
906
1005
  const readBudget = yield* QueryStreamReadBudget;
907
- const collected = yield* Stream.run(narrowed.annotated, Sink.fold(() => initial(), (state) => state._tag === "Reading", (state, { doc, keyValues }) => Effect.map(readBudget.isExhausted, (hitLimit) => record(request, state, doc, complete$2(keyValues), hitLimit))));
1006
+ const collected = yield* Stream.run(narrowed.annotated, Sink.fold(() => initial(), (state) => state._tag === "Reading", (state, { doc, keyValues }) => Effect.map(readBudget.isStopped, (hitLimit) => record(request, state, doc, complete$1(keyValues), hitLimit))));
908
1007
  const stopped = yield* readBudget.isStopped;
909
1008
  const outcome = finish(request, collected, stopped);
910
1009
  if (Result.isFailure(outcome)) return yield* new ReadBudgetExceededError(yield* readBudget.getReadCounts);
911
- const encode = (key) => encodeCursor(key).pipe(Effect.catchTag("SchemaError", Effect.die));
1010
+ const encode = (key) => encodeCursor(key).pipe(Effect.catchTag("SchemaError", Effect.die), Effect.flatMap(seal));
912
1011
  const encodeSplit = (result) => Effect.gen(function* () {
913
1012
  const continueCursor = yield* Match.value(result.continuation).pipe(Match.tagsExhaustive({
914
- End: () => Effect.succeed("[]"),
1013
+ End: () => seal("[]"),
915
1014
  Key: ({ key }) => encode(key)
916
1015
  }));
917
1016
  return {
@@ -923,11 +1022,11 @@ const paginate = dual(2, Effect.fn("QueryStream.paginate")(function* (self, opti
923
1022
  };
924
1023
  });
925
1024
  return yield* Match.value(outcome.success).pipe(Match.tagsExhaustive({
926
- Done: ({ page }) => Effect.succeed({
1025
+ Done: ({ page }) => Effect.map(seal("[]"), (continueCursor) => ({
927
1026
  page: Array.fromIterable(page),
928
1027
  isDone: true,
929
- continueCursor: "[]"
930
- }),
1028
+ continueCursor
1029
+ })),
931
1030
  Continue: ({ page, key }) => Effect.map(encode(key), (continueCursor) => ({
932
1031
  page: Array.fromIterable(page),
933
1032
  isDone: false,
@@ -936,8 +1035,11 @@ const paginate = dual(2, Effect.fn("QueryStream.paginate")(function* (self, opti
936
1035
  SplitRequired: encodeSplit,
937
1036
  SplitRecommended: encodeSplit
938
1037
  }));
939
- }, (effect, _self, options) => Effect.provideServiceEffect(effect, QueryStreamReadBudget, Schema.decodeEffect(Limits)(options).pipe(Effect.catchTag("SchemaError", (cause) => Effect.die(new InvalidReadLimitError({ cause }))), Effect.flatMap(make$1)))));
1038
+ }, (effect, _self, options) => Effect.provideServiceEffect(effect, QueryStreamReadBudget, Schema.decodeEffect(Limits)({
1039
+ maximumRowsRead: DEFAULT_MAXIMUM_ROWS_READ,
1040
+ ...options
1041
+ }).pipe(Effect.catchTag("SchemaError", (cause) => Effect.die(new InvalidReadLimitError({ cause }))), Effect.flatMap(make$1)))));
940
1042
  //#endregion
941
- export { Element, EmptyInitialPageError, IncompatibleStreamsError, InnerStreamLayoutMismatchError, InnerStreamOrderMismatchError, InvalidPageSizeError, InvalidReadLimitError, MissingReversalRecipeError, NotUniqueError, QueryStream, QueryStream_exports, ReadBudgetExceededError, TypeId, distinct, empty, filter, filterEffect, flatMap, fromReflection, isQueryStream, map, mapEffect, merge, narrow, paginate, renameKey, reverse, unique };
1043
+ export { DEFAULT_MAXIMUM_ROWS_READ, DeferredStreamMismatchError, Element, EmptyInitialPageError, IncompatibleStreamsError, InnerStreamLayoutMismatchError, InnerStreamOrderMismatchError, InvalidPageSizeError, InvalidReadLimitError, MissingReversalRecipeError, NotUniqueError, QueryStream, QueryStream_exports, ReadBudgetExceededError, TypeId, collect, defer, distinct, empty, filter, filterEffect, flatMap, fromReflection, isQueryStream, map, mapEffect, merge, narrow, paginate, renameKey, reverse, unique };
942
1044
 
943
1045
  //# sourceMappingURL=QueryStream.js.map