@gaganref/confect-server 0.0.0-stage → 10.3.1-fork.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2119 -0
- package/LICENSE +7 -0
- package/README.md +16 -2
- package/dist/ActionCtx.d.ts +10 -0
- package/dist/ActionCtx.d.ts.map +1 -0
- package/dist/ActionCtx.js +18 -0
- package/dist/ActionCtx.js.map +1 -0
- package/dist/ActionRunner.d.ts +16 -0
- package/dist/ActionRunner.d.ts.map +1 -0
- package/dist/ActionRunner.js +18 -0
- package/dist/ActionRunner.js.map +1 -0
- package/dist/AiGatewayDecisionClient.d.ts +20 -0
- package/dist/AiGatewayDecisionClient.d.ts.map +1 -0
- package/dist/AiGatewayDecisionClient.js +31 -0
- package/dist/AiGatewayDecisionClient.js.map +1 -0
- package/dist/AiGatewayDecisionModel.d.ts +24 -0
- package/dist/AiGatewayDecisionModel.d.ts.map +1 -0
- package/dist/AiGatewayDecisionModel.js +27 -0
- package/dist/AiGatewayDecisionModel.js.map +1 -0
- package/dist/AiGatewayLanguageClient.d.ts +21 -0
- package/dist/AiGatewayLanguageClient.d.ts.map +1 -0
- package/dist/AiGatewayLanguageClient.js +32 -0
- package/dist/AiGatewayLanguageClient.js.map +1 -0
- package/dist/AiGatewayLanguageModel.d.ts +95 -0
- package/dist/AiGatewayLanguageModel.d.ts.map +1 -0
- package/dist/AiGatewayLanguageModel.js +42 -0
- package/dist/AiGatewayLanguageModel.js.map +1 -0
- package/dist/Auth.d.ts +20 -0
- package/dist/Auth.d.ts.map +1 -0
- package/dist/Auth.js +31 -0
- package/dist/Auth.js.map +1 -0
- package/dist/BlobNotFoundError.d.ts +9 -0
- package/dist/BlobNotFoundError.d.ts.map +1 -0
- package/dist/BlobNotFoundError.js +13 -0
- package/dist/BlobNotFoundError.js.map +1 -0
- package/dist/Callback.d.ts +31 -0
- package/dist/Callback.d.ts.map +1 -0
- package/dist/Callback.js +182 -0
- package/dist/Callback.js.map +1 -0
- package/dist/ComponentCtx.d.ts +71 -0
- package/dist/ComponentCtx.d.ts.map +1 -0
- package/dist/ComponentCtx.js +0 -0
- package/dist/ConvexConfigProvider.d.ts +21 -0
- package/dist/ConvexConfigProvider.d.ts.map +1 -0
- package/dist/ConvexConfigProvider.js +36 -0
- package/dist/ConvexConfigProvider.js.map +1 -0
- package/dist/ConvexFunctionDefinitions.d.ts +25 -0
- package/dist/ConvexFunctionDefinitions.d.ts.map +1 -0
- package/dist/ConvexFunctionDefinitions.js +25 -0
- package/dist/ConvexFunctionDefinitions.js.map +1 -0
- package/dist/ConvexLogger.d.ts +17 -0
- package/dist/ConvexLogger.d.ts.map +1 -0
- package/dist/ConvexLogger.js +33 -0
- package/dist/ConvexLogger.js.map +1 -0
- package/dist/CronJob.d.ts +14 -0
- package/dist/CronJob.d.ts.map +1 -0
- package/dist/CronJob.js +22 -0
- package/dist/CronJob.js.map +1 -0
- package/dist/CronJobs.d.ts +22 -0
- package/dist/CronJobs.d.ts.map +1 -0
- package/dist/CronJobs.js +83 -0
- package/dist/CronJobs.js.map +1 -0
- package/dist/DataModel.d.ts +28 -0
- package/dist/DataModel.d.ts.map +1 -0
- package/dist/DataModel.js +7 -0
- package/dist/DataModel.js.map +1 -0
- package/dist/DatabaseReader.d.ts +26 -0
- package/dist/DatabaseReader.d.ts.map +1 -0
- package/dist/DatabaseReader.js +33 -0
- package/dist/DatabaseReader.js.map +1 -0
- package/dist/DatabaseSchema.d.ts +67 -0
- package/dist/DatabaseSchema.d.ts.map +1 -0
- package/dist/DatabaseSchema.js +61 -0
- package/dist/DatabaseSchema.js.map +1 -0
- package/dist/DatabaseWriter.d.ts +44 -0
- package/dist/DatabaseWriter.d.ts.map +1 -0
- package/dist/DatabaseWriter.js +47 -0
- package/dist/DatabaseWriter.js.map +1 -0
- package/dist/Document.d.ts +37 -0
- package/dist/Document.d.ts.map +1 -0
- package/dist/Document.js +54 -0
- package/dist/Document.js.map +1 -0
- package/dist/ExecutionMetadata.d.ts +13 -0
- package/dist/ExecutionMetadata.d.ts.map +1 -0
- package/dist/ExecutionMetadata.js +19 -0
- package/dist/ExecutionMetadata.js.map +1 -0
- package/dist/FunctionImpl.d.ts +39 -0
- package/dist/FunctionImpl.d.ts.map +1 -0
- package/dist/FunctionImpl.js +46 -0
- package/dist/FunctionImpl.js.map +1 -0
- package/dist/FunctionRegistryItem.d.ts +36 -0
- package/dist/FunctionRegistryItem.d.ts.map +1 -0
- package/dist/FunctionRegistryItem.js +34 -0
- package/dist/FunctionRegistryItem.js.map +1 -0
- package/dist/GroupImpl.d.ts +77 -0
- package/dist/GroupImpl.d.ts.map +1 -0
- package/dist/GroupImpl.js +81 -0
- package/dist/GroupImpl.js.map +1 -0
- package/dist/GroupRegistry.d.ts +8 -0
- package/dist/GroupRegistry.d.ts.map +1 -0
- package/dist/GroupRegistry.js +26 -0
- package/dist/GroupRegistry.js.map +1 -0
- package/dist/Handler.d.ts +47 -0
- package/dist/Handler.d.ts.map +1 -0
- package/dist/Handler.js +7 -0
- package/dist/Handler.js.map +1 -0
- package/dist/HttpRouter.d.ts +80 -0
- package/dist/HttpRouter.d.ts.map +1 -0
- package/dist/HttpRouter.js +195 -0
- package/dist/HttpRouter.js.map +1 -0
- package/dist/MiddlewareImpl.d.ts +79 -0
- package/dist/MiddlewareImpl.d.ts.map +1 -0
- package/dist/MiddlewareImpl.js +58 -0
- package/dist/MiddlewareImpl.js.map +1 -0
- package/dist/MiddlewareRegistryItem.d.ts +12 -0
- package/dist/MiddlewareRegistryItem.d.ts.map +1 -0
- package/dist/MiddlewareRegistryItem.js +15 -0
- package/dist/MiddlewareRegistryItem.js.map +1 -0
- package/dist/MutationCtx.d.ts +10 -0
- package/dist/MutationCtx.d.ts.map +1 -0
- package/dist/MutationCtx.js +18 -0
- package/dist/MutationCtx.js.map +1 -0
- package/dist/MutationRunner.d.ts +27 -0
- package/dist/MutationRunner.d.ts.map +1 -0
- package/dist/MutationRunner.js +28 -0
- package/dist/MutationRunner.js.map +1 -0
- package/dist/MutationTransactionContext.d.ts +17 -0
- package/dist/MutationTransactionContext.d.ts.map +1 -0
- package/dist/MutationTransactionContext.js +20 -0
- package/dist/MutationTransactionContext.js.map +1 -0
- package/dist/OrderedQuery.d.ts +32 -0
- package/dist/OrderedQuery.d.ts.map +1 -0
- package/dist/OrderedQuery.js +36 -0
- package/dist/OrderedQuery.js.map +1 -0
- package/dist/QueryCtx.d.ts +10 -0
- package/dist/QueryCtx.d.ts.map +1 -0
- package/dist/QueryCtx.js +18 -0
- package/dist/QueryCtx.js.map +1 -0
- package/dist/QueryInitializer.d.ts +75 -0
- package/dist/QueryInitializer.d.ts.map +1 -0
- package/dist/QueryInitializer.js +105 -0
- package/dist/QueryInitializer.js.map +1 -0
- package/dist/QueryRunner.d.ts +46 -0
- package/dist/QueryRunner.d.ts.map +1 -0
- package/dist/QueryRunner.js +30 -0
- package/dist/QueryRunner.js.map +1 -0
- package/dist/QueryStream.d.ts +678 -0
- package/dist/QueryStream.d.ts.map +1 -0
- package/dist/QueryStream.js +943 -0
- package/dist/QueryStream.js.map +1 -0
- package/dist/QueryStreamCursor.d.ts +39 -0
- package/dist/QueryStreamCursor.d.ts.map +1 -0
- package/dist/QueryStreamCursor.js +52 -0
- package/dist/QueryStreamCursor.js.map +1 -0
- package/dist/QueryStreamIndexPrefix.d.ts +48 -0
- package/dist/QueryStreamIndexPrefix.d.ts.map +1 -0
- package/dist/QueryStreamIndexPrefix.js +38 -0
- package/dist/QueryStreamIndexPrefix.js.map +1 -0
- package/dist/QueryStreamIndexRange.d.ts +193 -0
- package/dist/QueryStreamIndexRange.d.ts.map +1 -0
- package/dist/QueryStreamIndexRange.js +241 -0
- package/dist/QueryStreamIndexRange.js.map +1 -0
- package/dist/QueryStreamKey.d.ts +64 -0
- package/dist/QueryStreamKey.d.ts.map +1 -0
- package/dist/QueryStreamKey.js +69 -0
- package/dist/QueryStreamKey.js.map +1 -0
- package/dist/QueryStreamKeyBounds.d.ts +136 -0
- package/dist/QueryStreamKeyBounds.d.ts.map +1 -0
- package/dist/QueryStreamKeyBounds.js +208 -0
- package/dist/QueryStreamKeyBounds.js.map +1 -0
- package/dist/QueryStreamKeyLabels.d.ts +27 -0
- package/dist/QueryStreamKeyLabels.d.ts.map +1 -0
- package/dist/QueryStreamKeyLabels.js +22 -0
- package/dist/QueryStreamKeyLabels.js.map +1 -0
- package/dist/QueryStreamKeyLayout.d.ts +179 -0
- package/dist/QueryStreamKeyLayout.d.ts.map +1 -0
- package/dist/QueryStreamKeyLayout.js +167 -0
- package/dist/QueryStreamKeyLayout.js.map +1 -0
- package/dist/QueryStreamKeyValues.d.ts +40 -0
- package/dist/QueryStreamKeyValues.d.ts.map +1 -0
- package/dist/QueryStreamKeyValues.js +57 -0
- package/dist/QueryStreamKeyValues.js.map +1 -0
- package/dist/QueryStreamOrderDirection.d.ts +24 -0
- package/dist/QueryStreamOrderDirection.d.ts.map +1 -0
- package/dist/QueryStreamOrderDirection.js +11 -0
- package/dist/QueryStreamOrderDirection.js.map +1 -0
- package/dist/QueryStreamPagination.d.ts +264 -0
- package/dist/QueryStreamPagination.d.ts.map +1 -0
- package/dist/QueryStreamPagination.js +172 -0
- package/dist/QueryStreamPagination.js.map +1 -0
- package/dist/QueryStreamReadBudget.d.ts +63 -0
- package/dist/QueryStreamReadBudget.d.ts.map +1 -0
- package/dist/QueryStreamReadBudget.js +81 -0
- package/dist/QueryStreamReadBudget.js.map +1 -0
- package/dist/QueryTransactionContext.d.ts +16 -0
- package/dist/QueryTransactionContext.d.ts.map +1 -0
- package/dist/QueryTransactionContext.js +17 -0
- package/dist/QueryTransactionContext.js.map +1 -0
- package/dist/RegisteredConvexFunction.d.ts +359 -0
- package/dist/RegisteredConvexFunction.d.ts.map +1 -0
- package/dist/RegisteredConvexFunction.js +154 -0
- package/dist/RegisteredConvexFunction.js.map +1 -0
- package/dist/RegisteredFunction.d.ts +143 -0
- package/dist/RegisteredFunction.d.ts.map +1 -0
- package/dist/RegisteredFunction.js +125 -0
- package/dist/RegisteredFunction.js.map +1 -0
- package/dist/RegisteredFunctions.d.ts +57 -0
- package/dist/RegisteredFunctions.d.ts.map +1 -0
- package/dist/RegisteredFunctions.js +32 -0
- package/dist/RegisteredFunctions.js.map +1 -0
- package/dist/RegisteredNodeFunction.d.ts +11 -0
- package/dist/RegisteredNodeFunction.d.ts.map +1 -0
- package/dist/RegisteredNodeFunction.js +45 -0
- package/dist/RegisteredNodeFunction.js.map +1 -0
- package/dist/Registry.d.ts +12 -0
- package/dist/Registry.d.ts.map +1 -0
- package/dist/Registry.js +17 -0
- package/dist/Registry.js.map +1 -0
- package/dist/RegistryItems.d.ts +21 -0
- package/dist/RegistryItems.d.ts.map +1 -0
- package/dist/RegistryItems.js +27 -0
- package/dist/RegistryItems.js.map +1 -0
- package/dist/RequestMetadata.d.ts +12 -0
- package/dist/RequestMetadata.d.ts.map +1 -0
- package/dist/RequestMetadata.js +16 -0
- package/dist/RequestMetadata.js.map +1 -0
- package/dist/ResolvedMiddleware.d.ts +24 -0
- package/dist/ResolvedMiddleware.d.ts.map +1 -0
- package/dist/ResolvedMiddleware.js +34 -0
- package/dist/ResolvedMiddleware.js.map +1 -0
- package/dist/Scheduler.d.ts +31 -0
- package/dist/Scheduler.d.ts.map +1 -0
- package/dist/Scheduler.js +35 -0
- package/dist/Scheduler.js.map +1 -0
- package/dist/Storage.d.ts +22 -0
- package/dist/Storage.d.ts.map +1 -0
- package/dist/Storage.js +27 -0
- package/dist/Storage.js.map +1 -0
- package/dist/StorageActionWriter.d.ts +35 -0
- package/dist/StorageActionWriter.d.ts.map +1 -0
- package/dist/StorageActionWriter.js +24 -0
- package/dist/StorageActionWriter.js.map +1 -0
- package/dist/StorageReader.d.ts +26 -0
- package/dist/StorageReader.d.ts.map +1 -0
- package/dist/StorageReader.js +22 -0
- package/dist/StorageReader.js.map +1 -0
- package/dist/StorageWriter.d.ts +29 -0
- package/dist/StorageWriter.d.ts.map +1 -0
- package/dist/StorageWriter.js +24 -0
- package/dist/StorageWriter.js.map +1 -0
- package/dist/Table.d.ts +274 -0
- package/dist/Table.d.ts.map +1 -0
- package/dist/Table.js +70 -0
- package/dist/Table.js.map +1 -0
- package/dist/TableInfo.d.ts +44 -0
- package/dist/TableInfo.d.ts.map +1 -0
- package/dist/TableInfo.js +7 -0
- package/dist/TableInfo.js.map +1 -0
- package/dist/TransactionMetadata.d.ts +12 -0
- package/dist/TransactionMetadata.d.ts.map +1 -0
- package/dist/TransactionMetadata.js +16 -0
- package/dist/TransactionMetadata.js.map +1 -0
- package/dist/VectorSearch.d.ts +23 -0
- package/dist/VectorSearch.d.ts.map +1 -0
- package/dist/VectorSearch.js +17 -0
- package/dist/VectorSearch.js.map +1 -0
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +61 -0
- package/dist/internal/AiGatewayDecisionClient.d.ts +7 -0
- package/dist/internal/AiGatewayDecisionClient.d.ts.map +1 -0
- package/dist/internal/AiGatewayDecisionClient.js +19 -0
- package/dist/internal/AiGatewayDecisionClient.js.map +1 -0
- package/dist/internal/AiGatewayLanguageClient.d.ts +7 -0
- package/dist/internal/AiGatewayLanguageClient.d.ts.map +1 -0
- package/dist/internal/AiGatewayLanguageClient.js +19 -0
- package/dist/internal/AiGatewayLanguageClient.js.map +1 -0
- package/dist/internal/AiGatewayServiceToken.d.ts +46 -0
- package/dist/internal/AiGatewayServiceToken.d.ts.map +1 -0
- package/dist/internal/AiGatewayServiceToken.js +62 -0
- package/dist/internal/AiGatewayServiceToken.js.map +1 -0
- package/dist/internal/constants.d.ts +2 -0
- package/dist/internal/constants.d.ts.map +1 -0
- package/dist/internal/constants.js +6 -0
- package/dist/internal/constants.js.map +1 -0
- package/dist/internal/utils.d.ts +13 -0
- package/dist/internal/utils.d.ts.map +1 -0
- package/dist/internal/utils.js +50 -0
- package/dist/internal/utils.js.map +1 -0
- package/dist/node.d.ts +2 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +1 -0
- package/package.json +68 -3
- package/src/ActionCtx.ts +45 -0
- package/src/ActionRunner.ts +36 -0
- package/src/AiGatewayDecisionClient.ts +40 -0
- package/src/AiGatewayDecisionModel.ts +46 -0
- package/src/AiGatewayLanguageClient.ts +41 -0
- package/src/AiGatewayLanguageModel.ts +61 -0
- package/src/Auth.ts +51 -0
- package/src/BlobNotFoundError.ts +12 -0
- package/src/Callback.ts +313 -0
- package/src/ComponentCtx.ts +192 -0
- package/src/ConvexConfigProvider.ts +39 -0
- package/src/ConvexFunctionDefinitions.ts +91 -0
- package/src/ConvexLogger.ts +43 -0
- package/src/CronJob.ts +42 -0
- package/src/CronJobs.ts +165 -0
- package/src/DataModel.ts +81 -0
- package/src/DatabaseReader.ts +118 -0
- package/src/DatabaseSchema.ts +139 -0
- package/src/DatabaseWriter.ts +232 -0
- package/src/Document.ts +146 -0
- package/src/ExecutionMetadata.ts +26 -0
- package/src/FunctionImpl.ts +96 -0
- package/src/FunctionRegistryItem.ts +85 -0
- package/src/GroupImpl.ts +167 -0
- package/src/GroupRegistry.ts +41 -0
- package/src/Handler.ts +191 -0
- package/src/HttpRouter.ts +388 -0
- package/src/MiddlewareImpl.ts +247 -0
- package/src/MiddlewareRegistryItem.ts +21 -0
- package/src/MutationCtx.ts +45 -0
- package/src/MutationRunner.ts +66 -0
- package/src/MutationTransactionContext.ts +48 -0
- package/src/OrderedQuery.ts +136 -0
- package/src/QueryCtx.ts +45 -0
- package/src/QueryInitializer.ts +537 -0
- package/src/QueryRunner.ts +91 -0
- package/src/QueryStream.ts +2476 -0
- package/src/QueryStreamCursor.ts +99 -0
- package/src/QueryStreamIndexPrefix.ts +79 -0
- package/src/QueryStreamIndexRange.ts +489 -0
- package/src/QueryStreamKey.ts +118 -0
- package/src/QueryStreamKeyBounds.ts +395 -0
- package/src/QueryStreamKeyLabels.ts +56 -0
- package/src/QueryStreamKeyLayout.ts +382 -0
- package/src/QueryStreamKeyValues.ts +112 -0
- package/src/QueryStreamOrderDirection.ts +29 -0
- package/src/QueryStreamPagination.ts +357 -0
- package/src/QueryStreamReadBudget.ts +167 -0
- package/src/QueryTransactionContext.ts +37 -0
- package/src/RegisteredConvexFunction.ts +415 -0
- package/src/RegisteredFunction.ts +391 -0
- package/src/RegisteredFunctions.ts +88 -0
- package/src/RegisteredNodeFunction.ts +120 -0
- package/src/Registry.ts +17 -0
- package/src/RegistryItems.ts +40 -0
- package/src/RequestMetadata.ts +20 -0
- package/src/ResolvedMiddleware.ts +70 -0
- package/src/Scheduler.ts +85 -0
- package/src/Storage.ts +37 -0
- package/src/StorageActionWriter.ts +59 -0
- package/src/StorageReader.ts +59 -0
- package/src/StorageWriter.ts +58 -0
- package/src/Table.ts +156 -0
- package/src/TableInfo.ts +106 -0
- package/src/TransactionMetadata.ts +20 -0
- package/src/VectorSearch.ts +75 -0
- package/src/index.ts +87 -0
- package/src/internal/AiGatewayDecisionClient.ts +16 -0
- package/src/internal/AiGatewayLanguageClient.ts +16 -0
- package/src/internal/AiGatewayServiceToken.ts +129 -0
- package/src/internal/constants.ts +1 -0
- package/src/internal/utils.ts +89 -0
- package/src/node.ts +1 -0
|
@@ -0,0 +1,943 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { decode } from "./Document.js";
|
|
3
|
+
import { make } from "./QueryStreamKeyLabels.js";
|
|
4
|
+
import { Equivalence, concat, format, fromIndex, rename, resolvePrefix, runtimeWidth } from "./QueryStreamKeyLayout.js";
|
|
5
|
+
import { complete, toPrefix, values } from "./QueryStreamKey.js";
|
|
6
|
+
import { Order as Order$1, extract } from "./QueryStreamKeyValues.js";
|
|
7
|
+
import { fromKeyLayout } from "./QueryStreamCursor.js";
|
|
8
|
+
import { flip } from "./QueryStreamOrderDirection.js";
|
|
9
|
+
import { fromStreamKey, keyValues } from "./QueryStreamIndexPrefix.js";
|
|
10
|
+
import { admittedByLower, admittedByUpper, forLayout, fromParsed, intersect, intersectIndexBounds, parse, parseBound, tightestLower, tightestParsedLower, tightestParsedUpper, tightestUpper, toBounds, unbounded } from "./QueryStreamKeyBounds.js";
|
|
11
|
+
import { apply, equalityPrefixLength, fromBounds, toBounds as toBounds$1 } from "./QueryStreamIndexRange.js";
|
|
12
|
+
import { EmptyInitialPageError, InvalidPageSizeError, Range, Start, finish, initial, parseRequest, record } from "./QueryStreamPagination.js";
|
|
13
|
+
import { InvalidReadLimitError, Limits, QueryStreamReadBudget, make as make$1 } from "./QueryStreamReadBudget.js";
|
|
14
|
+
import * as Effect from "effect/Effect";
|
|
15
|
+
import * as Schema from "effect/Schema";
|
|
16
|
+
import { dual, identity, pipe } from "effect/Function";
|
|
17
|
+
import * as Match from "effect/Match";
|
|
18
|
+
import * as Option from "effect/Option";
|
|
19
|
+
import { ConvexError } from "convex/values";
|
|
20
|
+
import * as Array from "effect/Array";
|
|
21
|
+
import * as Order from "effect/Order";
|
|
22
|
+
import * as Predicate from "effect/Predicate";
|
|
23
|
+
import * as Result from "effect/Result";
|
|
24
|
+
import * as Data from "effect/Data";
|
|
25
|
+
import * as Stream from "effect/Stream";
|
|
26
|
+
import { pipeArguments } from "effect/Pipeable";
|
|
27
|
+
import * as Chunk from "effect/Chunk";
|
|
28
|
+
import * as Filter from "effect/Filter";
|
|
29
|
+
import * as Pull from "effect/Pull";
|
|
30
|
+
import * as Sink from "effect/Sink";
|
|
31
|
+
import * as String from "effect/String";
|
|
32
|
+
import * as Tuple from "effect/Tuple";
|
|
33
|
+
//#region src/QueryStream.ts
|
|
34
|
+
var QueryStream_exports = /* @__PURE__ */ __exportAll({
|
|
35
|
+
Element: () => Element,
|
|
36
|
+
EmptyInitialPageError: () => EmptyInitialPageError,
|
|
37
|
+
IncompatibleStreamsError: () => IncompatibleStreamsError,
|
|
38
|
+
InnerStreamLayoutMismatchError: () => InnerStreamLayoutMismatchError,
|
|
39
|
+
InnerStreamOrderMismatchError: () => InnerStreamOrderMismatchError,
|
|
40
|
+
InvalidPageSizeError: () => InvalidPageSizeError,
|
|
41
|
+
InvalidReadLimitError: () => InvalidReadLimitError,
|
|
42
|
+
MissingReversalRecipeError: () => MissingReversalRecipeError,
|
|
43
|
+
NotUniqueError: () => NotUniqueError,
|
|
44
|
+
QueryStream: () => QueryStream,
|
|
45
|
+
ReadBudgetExceededError: () => ReadBudgetExceededError,
|
|
46
|
+
TypeId: () => TypeId,
|
|
47
|
+
distinct: () => distinct,
|
|
48
|
+
empty: () => empty,
|
|
49
|
+
filter: () => filter,
|
|
50
|
+
filterEffect: () => filterEffect,
|
|
51
|
+
flatMap: () => flatMap,
|
|
52
|
+
fromReflection: () => fromReflection,
|
|
53
|
+
isQueryStream: () => isQueryStream,
|
|
54
|
+
map: () => map,
|
|
55
|
+
mapEffect: () => mapEffect,
|
|
56
|
+
merge: () => merge,
|
|
57
|
+
narrow: () => narrow,
|
|
58
|
+
paginate: () => paginate,
|
|
59
|
+
renameKey: () => renameKey,
|
|
60
|
+
reverse: () => reverse,
|
|
61
|
+
unique: () => unique
|
|
62
|
+
});
|
|
63
|
+
/**
|
|
64
|
+
* A page exhausted its read budget without a safe logical continuation.
|
|
65
|
+
*
|
|
66
|
+
* @experimental
|
|
67
|
+
*/
|
|
68
|
+
var ReadBudgetExceededError = class extends Schema.TaggedError()("ReadBudgetExceededError", {
|
|
69
|
+
rowsRead: Schema.Natural,
|
|
70
|
+
bytesRead: Schema.Natural
|
|
71
|
+
}) {
|
|
72
|
+
get message() {
|
|
73
|
+
return "The read budget was exhausted before a safe page boundary; increase the budget or simplify the query";
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Thrown by `merge` when inputs have different directions, visible ordering
|
|
78
|
+
* labels, or implicit-ID positions. The payload identifies the expected and
|
|
79
|
+
* actual direction and layout; map and relabel compatible inputs before
|
|
80
|
+
* merging.
|
|
81
|
+
*
|
|
82
|
+
* @experimental
|
|
83
|
+
*/
|
|
84
|
+
var IncompatibleStreamsError = class extends Data.TaggedError("IncompatibleStreamsError") {
|
|
85
|
+
get message() {
|
|
86
|
+
return `QueryStream.merge: all streams must share an order and key layout (got ${this.expectedOrderDirection} ${format(this.expectedKeyLayout)} and ${this.actualOrderDirection} ${format(this.actualKeyLayout)})`;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* A `flatMap` inner stream runs in a different direction from its outer stream.
|
|
91
|
+
* The join dies with this error when it runs; `expectedOrderDirection` is the
|
|
92
|
+
* outer direction and `actualOrderDirection` is the inner direction.
|
|
93
|
+
*
|
|
94
|
+
* @experimental
|
|
95
|
+
*/
|
|
96
|
+
var InnerStreamOrderMismatchError = class extends Data.TaggedError("InnerStreamOrderMismatchError") {
|
|
97
|
+
get message() {
|
|
98
|
+
return `QueryStream.flatMap: inner stream order (${this.actualOrderDirection}) differs from the outer stream's (${this.expectedOrderDirection})`;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* A `flatMap` inner stream does not match the supplied `innerKeyLayout`,
|
|
103
|
+
* including visible labels and implicit-ID positions. The join dies with this
|
|
104
|
+
* error when it runs; the payload contains the expected and actual layouts.
|
|
105
|
+
*
|
|
106
|
+
* @experimental
|
|
107
|
+
*/
|
|
108
|
+
var InnerStreamLayoutMismatchError = class extends Data.TaggedError("InnerStreamLayoutMismatchError") {
|
|
109
|
+
get message() {
|
|
110
|
+
return `QueryStream.flatMap: inner stream key layout (${format(this.actualKeyLayout)}) differs from innerKeyLayout (${format(this.expectedKeyLayout)})`;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Thrown by `reverse` when a manually constructed stream has no `reverseWith`
|
|
115
|
+
* recipe. Streams created through the table reader and `QueryStream`
|
|
116
|
+
* combinators support reversal.
|
|
117
|
+
*
|
|
118
|
+
* @experimental
|
|
119
|
+
*/
|
|
120
|
+
var MissingReversalRecipeError = class extends Data.TaggedError("MissingReversalRecipeError") {
|
|
121
|
+
get message() {
|
|
122
|
+
return "QueryStream.reverse: this stream cannot be reversed without a reversal recipe";
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Runtime identifier used to distinguish query streams from plain streams.
|
|
127
|
+
*
|
|
128
|
+
* @experimental
|
|
129
|
+
*/
|
|
130
|
+
const TypeId = "~@confect/server/QueryStream";
|
|
131
|
+
/**
|
|
132
|
+
* A decoded value and its stored key. `doc: None` marks an element that was
|
|
133
|
+
* read but filtered out: it emits no value, but still advances cursors. Mapping
|
|
134
|
+
* a value does not recompute its key.
|
|
135
|
+
*
|
|
136
|
+
* @experimental
|
|
137
|
+
*/
|
|
138
|
+
var Element = class extends Data.Class {};
|
|
139
|
+
/**
|
|
140
|
+
* An Effect `Stream` whose stored keys determine its order and pagination
|
|
141
|
+
* boundaries independently of its emitted values.
|
|
142
|
+
*
|
|
143
|
+
* TypeScript normally infers all five parameters from the table's `stream`
|
|
144
|
+
* method and subsequent composition:
|
|
145
|
+
*
|
|
146
|
+
* - `Doc` is the emitted value: a decoded document or a mapped or joined result.
|
|
147
|
+
* - `KeyLabels` is the branded readonly tuple of visible ordering names,
|
|
148
|
+
* initially the index fields not pinned by `eq`. `flatMap` appends labels and
|
|
149
|
+
* `renameKey` replaces them. Implicit ID tiebreakers are omitted from labels
|
|
150
|
+
* but retained in `keyLayout`.
|
|
151
|
+
* - `OrderDirection` is `"asc"` or `"desc"`; the type defaults to their union.
|
|
152
|
+
* - `E` contains typed failures while reading or transforming values. `never`
|
|
153
|
+
* means no typed failures, not no defects.
|
|
154
|
+
* - `R` contains Effect services required to run the stream. The stream retains
|
|
155
|
+
* the database access supplied at creation, so it doesn't require providing
|
|
156
|
+
* `DatabaseReader` again; effectful callbacks can add requirements.
|
|
157
|
+
*
|
|
158
|
+
* Known label or direction mismatches prevent merging at compile time. Runtime
|
|
159
|
+
* checks also compare implicit-ID positions. Reuse a compatible stream's
|
|
160
|
+
* `keyLayout` for `empty` or `flatMap`'s `innerKeyLayout`.
|
|
161
|
+
*
|
|
162
|
+
* Consume with Effect's `Stream.runCollect` or `Stream.runHead`, or use
|
|
163
|
+
* `unique` or `paginate`. A plain `Stream` transform such as `Stream.take`
|
|
164
|
+
* returns an ordinary stream that can be consumed but no longer merged or
|
|
165
|
+
* paginated with this module.
|
|
166
|
+
*
|
|
167
|
+
* @experimental
|
|
168
|
+
*/
|
|
169
|
+
var QueryStream = class {
|
|
170
|
+
orderDirection;
|
|
171
|
+
keyLayout;
|
|
172
|
+
annotated;
|
|
173
|
+
reflection;
|
|
174
|
+
narrowWith;
|
|
175
|
+
reverseWith;
|
|
176
|
+
constructor(orderDirection, keyLayout, annotated, reflection, narrowWith, reverseWith) {
|
|
177
|
+
this.orderDirection = orderDirection;
|
|
178
|
+
this.keyLayout = keyLayout;
|
|
179
|
+
this.annotated = annotated;
|
|
180
|
+
this.reflection = reflection;
|
|
181
|
+
this.narrowWith = narrowWith;
|
|
182
|
+
this.reverseWith = reverseWith;
|
|
183
|
+
if (narrowWith !== void 0) this.narrowWith = (parsedBounds) => narrowWith(Result.getOrThrowWith(forLayout(keyLayout, parsedBounds), identity));
|
|
184
|
+
}
|
|
185
|
+
toStream() {
|
|
186
|
+
return Stream.unwrap(Effect.gen({ self: this }, function* () {
|
|
187
|
+
const readBudget = yield* Effect.serviceOption(QueryStreamReadBudget).pipe(Effect.flatMap(Option.match({
|
|
188
|
+
onNone: () => make$1({
|
|
189
|
+
maximumRowsRead: Option.none(),
|
|
190
|
+
maximumBytesRead: Option.none()
|
|
191
|
+
}),
|
|
192
|
+
onSome: Effect.succeed
|
|
193
|
+
})));
|
|
194
|
+
return Stream.filterMap(this.annotated, Filter.fromPredicateOption(({ doc }) => doc)).pipe(Stream.provideService(QueryStreamReadBudget, readBudget));
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
const streamVariance = {
|
|
199
|
+
_R: identity,
|
|
200
|
+
_E: identity,
|
|
201
|
+
_A: identity
|
|
202
|
+
};
|
|
203
|
+
const queryStreamPrototype = QueryStream.prototype;
|
|
204
|
+
Object.defineProperties(queryStreamPrototype, {
|
|
205
|
+
[TypeId]: { value: TypeId },
|
|
206
|
+
[Stream.TypeId]: { value: streamVariance },
|
|
207
|
+
pipe: { value: function() {
|
|
208
|
+
return pipeArguments(this, arguments);
|
|
209
|
+
} },
|
|
210
|
+
channel: { get() {
|
|
211
|
+
return Stream.toChannel(this.toStream());
|
|
212
|
+
} }
|
|
213
|
+
});
|
|
214
|
+
/**
|
|
215
|
+
* Check whether a value retains query-stream ordering and pagination support.
|
|
216
|
+
* Plain `Stream` transforms return ordinary streams, for which this is false.
|
|
217
|
+
*
|
|
218
|
+
* @experimental
|
|
219
|
+
*/
|
|
220
|
+
const isQueryStream = (u) => Predicate.hasProperty(u, TypeId);
|
|
221
|
+
/**
|
|
222
|
+
* Create a stream with no documents but a known key layout and direction. Use
|
|
223
|
+
* it when a dynamic list of streams is empty, since `merge` requires at least
|
|
224
|
+
* one input. The direction defaults to `"asc"`.
|
|
225
|
+
*
|
|
226
|
+
* Supply the document type in the first call, then reuse a compatible stream's
|
|
227
|
+
* layout: `QueryStream.empty<NotesDoc>()(source.keyLayout, "desc")`. Creating
|
|
228
|
+
* the source solely to obtain its layout does not read documents.
|
|
229
|
+
*
|
|
230
|
+
* @experimental
|
|
231
|
+
*/
|
|
232
|
+
const empty = () => (keyLayout, orderDirection = "asc") => {
|
|
233
|
+
const make = (currentOrderDirection) => new QueryStream(currentOrderDirection, keyLayout, Stream.empty, void 0, () => make(currentOrderDirection), () => make(flip(currentOrderDirection)));
|
|
234
|
+
return make(orderDirection);
|
|
235
|
+
};
|
|
236
|
+
/**
|
|
237
|
+
* Create a reusable query stream from an index-query description. Prefer
|
|
238
|
+
* `reader.table(...).stream(...)` for inferred document and ordering types.
|
|
239
|
+
*
|
|
240
|
+
* Construction does not read documents. Each consuming run rebuilds the index
|
|
241
|
+
* queries within the supplied range and decodes their documents, failing with
|
|
242
|
+
* `DocumentDecodeError` if decoding fails. Key values come from encoded index
|
|
243
|
+
* values before decoding, with equality-pinned fields removed.
|
|
244
|
+
*
|
|
245
|
+
* @experimental
|
|
246
|
+
*/
|
|
247
|
+
const fromReflection = (reflection) => makeLeaf(reflection, reflection.indexBounds === void 0 ? toBounds$1(reflection.indexRange) : intersectIndexBounds(toBounds$1(reflection.indexRange), reflection.indexBounds));
|
|
248
|
+
/**
|
|
249
|
+
* Complete the source index paths with Convex's implicit ID tiebreaker. These
|
|
250
|
+
* paths address the encoded document and are never ordering aliases.
|
|
251
|
+
*/
|
|
252
|
+
const completeFieldPaths = (fieldPaths) => Option.exists(Array.last(fieldPaths), (fieldPath) => fieldPath === "_id") ? fieldPaths : Array.append(fieldPaths, "_id");
|
|
253
|
+
const makeLeaf = (reflection, indexBounds) => {
|
|
254
|
+
const fullFieldPaths = completeFieldPaths(reflection.indexFieldPaths);
|
|
255
|
+
const equalityPrefixLength$1 = equalityPrefixLength(reflection.indexRange);
|
|
256
|
+
const keyLayout = Result.getOrThrowWith(fromIndex(reflection.indexFieldPaths, equalityPrefixLength$1), identity);
|
|
257
|
+
const keyPaths = Array.map(Array.drop(fullFieldPaths, equalityPrefixLength$1), (fieldPath) => String.split(fieldPath, "."));
|
|
258
|
+
const equalityKeyValues = Array.take(indexBounds.lower.keyValues, equalityPrefixLength$1);
|
|
259
|
+
const indexRanges = Result.getOrThrowWith(fromBounds(fullFieldPaths, reflection.orderDirection, indexBounds), identity);
|
|
260
|
+
const encodedDocuments = Stream.fromIterable(indexRanges).pipe(Stream.flatMap((indexRange) => Stream.suspend(() => Stream.fromAsyncIterable(reflection.reader.query(reflection.table.tableName).withIndex(reflection.indexName, (q) => apply(indexRange, q)).order(reflection.orderDirection), identity).pipe(Stream.orDie))));
|
|
261
|
+
const annotated = Stream.unwrap(Effect.map(QueryStreamReadBudget, (readBudget) => readBudget.accountFor(encodedDocuments))).pipe(Stream.mapEffect((encoded) => Effect.map(decode(reflection.table)(encoded), (doc) => new Element({
|
|
262
|
+
doc: Option.some(doc),
|
|
263
|
+
keyValues: extract(encoded, keyPaths)
|
|
264
|
+
}))));
|
|
265
|
+
const toFullKeySpace = (bound) => ({
|
|
266
|
+
keyValues: keyValues(Result.getOrThrowWith(fromStreamKey(fullFieldPaths, equalityKeyValues, bound.key), identity)),
|
|
267
|
+
inclusive: bound.inclusive
|
|
268
|
+
});
|
|
269
|
+
return new QueryStream(reflection.orderDirection, keyLayout, annotated, {
|
|
270
|
+
...reflection,
|
|
271
|
+
indexBounds
|
|
272
|
+
}, (parsedBounds) => makeLeaf(reflection, {
|
|
273
|
+
lower: Option.match(parsedBounds.lower, {
|
|
274
|
+
onNone: () => indexBounds.lower,
|
|
275
|
+
onSome: (bound) => tightestLower(indexBounds.lower, toFullKeySpace(bound))
|
|
276
|
+
}),
|
|
277
|
+
upper: Option.match(parsedBounds.upper, {
|
|
278
|
+
onNone: () => indexBounds.upper,
|
|
279
|
+
onSome: (bound) => tightestUpper(indexBounds.upper, toFullKeySpace(bound))
|
|
280
|
+
})
|
|
281
|
+
}), () => makeLeaf({
|
|
282
|
+
...reflection,
|
|
283
|
+
orderDirection: flip(reflection.orderDirection)
|
|
284
|
+
}, indexBounds));
|
|
285
|
+
};
|
|
286
|
+
const MergeSource = Data.taggedEnum();
|
|
287
|
+
var MergeCandidate = class extends Data.Class {};
|
|
288
|
+
const fillMergeSource = (source) => MergeSource.$match(source, {
|
|
289
|
+
NeedsPull: ({ pull }) => pull.pipe(Effect.map((elements) => MergeSource.Buffered({
|
|
290
|
+
pull,
|
|
291
|
+
head: Array.headNonEmpty(elements),
|
|
292
|
+
tail: Chunk.fromIterable(Array.tailNonEmpty(elements))
|
|
293
|
+
})), Pull.catchDone(() => Effect.gen(function* () {
|
|
294
|
+
return (yield* (yield* QueryStreamReadBudget).isStopped) ? MergeSource.BudgetLimited() : MergeSource.Exhausted();
|
|
295
|
+
}))),
|
|
296
|
+
Buffered: () => Effect.succeed(source),
|
|
297
|
+
Exhausted: () => Effect.succeed(source),
|
|
298
|
+
BudgetLimited: () => Effect.succeed(source)
|
|
299
|
+
});
|
|
300
|
+
/**
|
|
301
|
+
* One step of the k-way merge as a pure unfold: fill every source, emit the
|
|
302
|
+
* earliest head (ties go to the earliest source, keeping the merge stable), and
|
|
303
|
+
* return the sources with that head consumed. `undefined` when every source is
|
|
304
|
+
* exhausted or an input stopped before its next key was known.
|
|
305
|
+
*/
|
|
306
|
+
const mergeStep = (keyOrder) => (sources) => Effect.gen(function* () {
|
|
307
|
+
const readBudget = yield* QueryStreamReadBudget;
|
|
308
|
+
const filled = yield* Effect.forEach(sources, fillMergeSource, { concurrency: (yield* readBudget.isUnlimited) ? "unbounded" : 1 });
|
|
309
|
+
if (filled.some((source) => MergeSource.$is("BudgetLimited")(source))) return void 0;
|
|
310
|
+
const isEarlier = Order.isLessThan(keyOrder);
|
|
311
|
+
const earliest = Array.reduce(filled, Option.none(), (best, source, index) => Option.match(source._tag === "Buffered" ? Option.some(source.head) : Option.none(), {
|
|
312
|
+
onNone: () => best,
|
|
313
|
+
onSome: (head) => Option.match(best, {
|
|
314
|
+
onNone: () => Option.some(new MergeCandidate({
|
|
315
|
+
index,
|
|
316
|
+
element: head
|
|
317
|
+
})),
|
|
318
|
+
onSome: ({ element: bestElement }) => isEarlier(head.keyValues, bestElement.keyValues) ? Option.some(new MergeCandidate({
|
|
319
|
+
index,
|
|
320
|
+
element: head
|
|
321
|
+
})) : best
|
|
322
|
+
})
|
|
323
|
+
}));
|
|
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))));
|
|
332
|
+
});
|
|
333
|
+
/**
|
|
334
|
+
* Combine streams sharing a key layout into one stream in key order. Use this
|
|
335
|
+
* to query several index ranges at once, such as notes by multiple roles.
|
|
336
|
+
*
|
|
337
|
+
* 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.
|
|
341
|
+
*
|
|
342
|
+
* Inputs need compatible document types and identical key layouts and
|
|
343
|
+
* directions. TypeScript rejects known mismatches; runtime mismatches throw
|
|
344
|
+
* `IncompatibleStreamsError` when combined. Map to a common value shape and use
|
|
345
|
+
* `renameKey` when compatible indexes have different field names.
|
|
346
|
+
*
|
|
347
|
+
* @experimental
|
|
348
|
+
*/
|
|
349
|
+
const merge = (streams) => {
|
|
350
|
+
const head = Array.headNonEmpty(streams);
|
|
351
|
+
const incompatible = Array.findFirst(Array.tailNonEmpty(streams), (stream) => stream.orderDirection !== head.orderDirection || !Equivalence(stream.keyLayout, head.keyLayout));
|
|
352
|
+
if (Option.isSome(incompatible)) throw new IncompatibleStreamsError({
|
|
353
|
+
expectedOrderDirection: head.orderDirection,
|
|
354
|
+
actualOrderDirection: incompatible.value.orderDirection,
|
|
355
|
+
expectedKeyLayout: head.keyLayout,
|
|
356
|
+
actualKeyLayout: incompatible.value.keyLayout
|
|
357
|
+
});
|
|
358
|
+
return mergeUnchecked(streams);
|
|
359
|
+
};
|
|
360
|
+
/**
|
|
361
|
+
* `merge` without the compatibility check—for re-merging branches that were
|
|
362
|
+
* validated when the merge was built (narrowing never changes a branch's order
|
|
363
|
+
* or key layout).
|
|
364
|
+
*/
|
|
365
|
+
const mergeUnchecked = (streams) => {
|
|
366
|
+
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))]));
|
|
369
|
+
};
|
|
370
|
+
/**
|
|
371
|
+
* Derive a stream by transforming each present document into `Some` (keep,
|
|
372
|
+
* possibly changed) or `None` (drop). Key values are preserved and dropped
|
|
373
|
+
* elements stay in cursor accounting as read-but-filtered, so the result
|
|
374
|
+
* remains mergeable and paginable; narrowing narrows the input and re-applies
|
|
375
|
+
* the transform, so cursor bounds keep pushing down to leaves.
|
|
376
|
+
*/
|
|
377
|
+
const transform = (self, f) => new QueryStream(self.orderDirection, self.keyLayout, Stream.map(self.annotated, ({ doc, keyValues }) => new Element({
|
|
378
|
+
doc: Option.flatMap(doc, f),
|
|
379
|
+
keyValues
|
|
380
|
+
})), void 0, (parsedBounds) => transform(narrowByParsedBounds(self, parsedBounds), f), () => transform(reverse(self), f));
|
|
381
|
+
/**
|
|
382
|
+
* The effectful {@link transform}.
|
|
383
|
+
*/
|
|
384
|
+
const transformEffect = (self, f, options) => new QueryStream(self.orderDirection, self.keyLayout, Stream.mapEffect(self.annotated, ({ doc, keyValues }) => Option.match(doc, {
|
|
385
|
+
onNone: () => Effect.succeed(new Element({
|
|
386
|
+
doc: Option.none(),
|
|
387
|
+
keyValues
|
|
388
|
+
})),
|
|
389
|
+
onSome: (value) => Effect.map(f(value), (mapped) => new Element({
|
|
390
|
+
doc: mapped,
|
|
391
|
+
keyValues
|
|
392
|
+
}))
|
|
393
|
+
}), { concurrency: options?.concurrency }), void 0, (parsedBounds) => transformEffect(narrowByParsedBounds(self, parsedBounds), f, options), () => transformEffect(reverse(self), f, options));
|
|
394
|
+
/**
|
|
395
|
+
* Keep values that satisfy a pure predicate without changing their stored keys.
|
|
396
|
+
* Like `Stream.filter`, this removes emitted values, but it retains the
|
|
397
|
+
* ordering information needed to merge and paginate the result.
|
|
398
|
+
*
|
|
399
|
+
* Rejected documents still count as reads and advance cursors. Filtering before
|
|
400
|
+
* `distinct` selects each group's first matching document; filtering after it
|
|
401
|
+
* tests only the chosen representative. Use `filterEffect` for predicates that
|
|
402
|
+
* read another table, use a service, or fail with a typed error.
|
|
403
|
+
*
|
|
404
|
+
* @experimental
|
|
405
|
+
*/
|
|
406
|
+
const filter = dual(2, (self, predicate) => transform(self, (doc) => predicate(doc) ? Option.some(doc) : Option.none()));
|
|
407
|
+
/**
|
|
408
|
+
* Keep values that satisfy an effectful predicate, preserving stored keys and
|
|
409
|
+
* cursor progress just like `filter`. The predicate can read other tables or
|
|
410
|
+
* use services; its errors and requirements become part of the stream's types.
|
|
411
|
+
*
|
|
412
|
+
* Effects run one document at a time by default. Pass `{ concurrency }` to run
|
|
413
|
+
* several at once while retaining stream order. Keep the predicate
|
|
414
|
+
* deterministic and read-only, since seeks and pagination can run it again.
|
|
415
|
+
* Separate lookups inside the predicate are not counted by pagination's read
|
|
416
|
+
* budgets.
|
|
417
|
+
*
|
|
418
|
+
* @experimental
|
|
419
|
+
*/
|
|
420
|
+
const filterEffect = dual((args) => isQueryStream(args[0]), (self, predicate, options) => transformEffect(self, (doc) => Effect.map(predicate(doc), (keep) => keep ? Option.some(doc) : Option.none()), options));
|
|
421
|
+
/**
|
|
422
|
+
* Transform emitted values with a pure function while keeping their stored key
|
|
423
|
+
* values. The result retains merge and pagination support, unlike the plain
|
|
424
|
+
* stream returned by `Stream.map`.
|
|
425
|
+
*
|
|
426
|
+
* The mapper may replace any fields, including indexed fields, or return a
|
|
427
|
+
* different shape entirely. It does not recompute keys or sort by the new
|
|
428
|
+
* values: mapping note text to its length still orders by the original text,
|
|
429
|
+
* creation time, and ID, not by length. Preserve the input value rather than
|
|
430
|
+
* mutating it. Use `mapEffect` when the mapper needs an effect.
|
|
431
|
+
*
|
|
432
|
+
* @experimental
|
|
433
|
+
*/
|
|
434
|
+
const map = dual(2, (self, f) => transform(self, (doc) => Option.some(f(doc))));
|
|
435
|
+
/**
|
|
436
|
+
* Transform emitted values with an effect, preserving the original stored keys
|
|
437
|
+
* just like `map`. The mapper may read another table or use services; its
|
|
438
|
+
* errors and requirements become part of the stream's types.
|
|
439
|
+
*
|
|
440
|
+
* Effects run one document at a time by default. Pass `{ concurrency }` for
|
|
441
|
+
* concurrent reads without changing output order. Keep the mapper deterministic
|
|
442
|
+
* and read-only: seeks, reversal, and pagination can reevaluate it. Separate
|
|
443
|
+
* lookups inside the mapper are not counted by pagination's read budgets.
|
|
444
|
+
*
|
|
445
|
+
* @experimental
|
|
446
|
+
*/
|
|
447
|
+
const mapEffect = dual((args) => isQueryStream(args[0]), (self, f, options) => transformEffect(self, (doc) => Effect.asSome(f(doc)), options));
|
|
448
|
+
/**
|
|
449
|
+
* Run an inner query stream for each outer document and concatenate the
|
|
450
|
+
* results. Like sequential `Stream.flatMap`, each inner stream finishes before
|
|
451
|
+
* the next begins. The joined key is the outer key followed by the inner key,
|
|
452
|
+
* retaining both document IDs so equal timestamps remain distinguishable.
|
|
453
|
+
*
|
|
454
|
+
* Supply `options.innerKeyLayout` from a compatible stream's `keyLayout`. For
|
|
455
|
+
* example, comments indexed by `noteId` and pinned to a note have the same
|
|
456
|
+
* layout as comments ordered by creation time: one visible creation-time
|
|
457
|
+
* position followed by an implicit ID. Creating the template reads no
|
|
458
|
+
* documents. For nested joins or renamed streams, apply the same composition or
|
|
459
|
+
* renaming to the template before reusing its layout.
|
|
460
|
+
*
|
|
461
|
+
* The layout is required when constructing the joined stream, before any outer
|
|
462
|
+
* document is available to pass to `f`, including when the outer stream is
|
|
463
|
+
* empty. It determines the joined key positions and the width of empty-inner
|
|
464
|
+
* markers. It contains no query results or direction.
|
|
465
|
+
*
|
|
466
|
+
* Every inner stream must match that layout and the outer direction. TypeScript
|
|
467
|
+
* rejects known mismatches; others die with `InnerStreamOrderMismatchError` or
|
|
468
|
+
* `InnerStreamLayoutMismatchError` when the join runs.
|
|
469
|
+
*
|
|
470
|
+
* An empty inner stream emits no value by default, but leaves a filtered marker
|
|
471
|
+
* that advances cursors past its outer document. Pass `options.onEmpty` to emit
|
|
472
|
+
* `onEmpty(outer)` instead. The element type widens to include the placeholder,
|
|
473
|
+
* so map inner values to a common shape when needed. Its key is the outer key
|
|
474
|
+
* followed by null inner components, and pagination steps past it like any
|
|
475
|
+
* element. Outer documents filtered out before the join remain absent.
|
|
476
|
+
*
|
|
477
|
+
* Keep `f` and `onEmpty` deterministic and read-only. Pagination, narrowing,
|
|
478
|
+
* and reversal can reevaluate them; both outer and inner index reads count
|
|
479
|
+
* toward pagination's budgets.
|
|
480
|
+
*
|
|
481
|
+
* @experimental
|
|
482
|
+
*/
|
|
483
|
+
const flatMap = dual(3, (self, f, options) => {
|
|
484
|
+
return makeFlatMap(self, f, options.innerKeyLayout, Option.fromUndefinedOr(options.onEmpty), {
|
|
485
|
+
lower: Option.none(),
|
|
486
|
+
upper: Option.none()
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
const FlatMapBound = Data.taggedEnum();
|
|
490
|
+
const combineLowerRefinements = (existing, incoming) => Option.orElse(Option.zipWith(existing, incoming, (left, right) => {
|
|
491
|
+
const ordering = Order$1("asc")(left.outerKeyValues, right.outerKeyValues);
|
|
492
|
+
return ordering > 0 ? left : ordering < 0 ? right : {
|
|
493
|
+
outerKeyValues: left.outerKeyValues,
|
|
494
|
+
innerParsedBound: Result.getOrThrowWith(tightestParsedLower(left.innerParsedBound, right.innerParsedBound), identity)
|
|
495
|
+
};
|
|
496
|
+
}), () => Option.orElse(existing, () => incoming));
|
|
497
|
+
const combineUpperRefinements = (existing, incoming) => Option.orElse(Option.zipWith(existing, incoming, (left, right) => {
|
|
498
|
+
const ordering = Order$1("asc")(left.outerKeyValues, right.outerKeyValues);
|
|
499
|
+
return ordering < 0 ? left : ordering > 0 ? right : {
|
|
500
|
+
outerKeyValues: left.outerKeyValues,
|
|
501
|
+
innerParsedBound: Result.getOrThrowWith(tightestParsedUpper(left.innerParsedBound, right.innerParsedBound), identity)
|
|
502
|
+
};
|
|
503
|
+
}), () => Option.orElse(existing, () => incoming));
|
|
504
|
+
const makeFlatMap = (self, f, innerKeyLayout, onEmpty, refinements) => {
|
|
505
|
+
const outerLength = runtimeWidth(self.keyLayout);
|
|
506
|
+
const keyLayout = concat(self.keyLayout, innerKeyLayout);
|
|
507
|
+
const emptyInnerKeyValues = Array.makeBy(runtimeWidth(innerKeyLayout), () => null);
|
|
508
|
+
const validated = (inner) => {
|
|
509
|
+
if (inner.orderDirection !== self.orderDirection) throw new InnerStreamOrderMismatchError({
|
|
510
|
+
expectedOrderDirection: self.orderDirection,
|
|
511
|
+
actualOrderDirection: inner.orderDirection
|
|
512
|
+
});
|
|
513
|
+
if (!Equivalence(inner.keyLayout, innerKeyLayout)) throw new InnerStreamLayoutMismatchError({
|
|
514
|
+
expectedKeyLayout: innerKeyLayout,
|
|
515
|
+
actualKeyLayout: inner.keyLayout
|
|
516
|
+
});
|
|
517
|
+
return inner;
|
|
518
|
+
};
|
|
519
|
+
const innerParsedBoundsFor = (outerKeyValues) => Result.getOrThrowWith(fromParsed(innerKeyLayout, {
|
|
520
|
+
lower: Option.map(Option.filter(refinements.lower, (refinement) => Order$1("asc")(outerKeyValues, refinement.outerKeyValues) === 0), (refinement) => refinement.innerParsedBound),
|
|
521
|
+
upper: Option.map(Option.filter(refinements.upper, (refinement) => Order$1("asc")(outerKeyValues, refinement.outerKeyValues) === 0), (refinement) => refinement.innerParsedBound)
|
|
522
|
+
}), identity);
|
|
523
|
+
const markerStream = (outerKeyValues, innerParsedBounds, doc) => {
|
|
524
|
+
const { aboveLower, belowUpper } = keyPredicates(innerParsedBounds);
|
|
525
|
+
return aboveLower(emptyInnerKeyValues) && belowUpper(emptyInnerKeyValues) ? Stream.succeed(new Element({
|
|
526
|
+
doc,
|
|
527
|
+
keyValues: Array.appendAll(outerKeyValues, emptyInnerKeyValues)
|
|
528
|
+
})) : Stream.empty;
|
|
529
|
+
};
|
|
530
|
+
const annotated = self.annotated.pipe(Stream.flatMap(({ doc: outerDoc, keyValues: outerKeyValues }) => {
|
|
531
|
+
const innerParsedBounds = innerParsedBoundsFor(outerKeyValues);
|
|
532
|
+
return Option.match(outerDoc, {
|
|
533
|
+
onNone: () => markerStream(outerKeyValues, innerParsedBounds, Option.none()),
|
|
534
|
+
onSome: (doc) => {
|
|
535
|
+
const inner = validated(f(doc));
|
|
536
|
+
return narrowByParsedBounds(inner, innerParsedBounds).annotated.pipe(Stream.map(({ doc: innerDoc, keyValues: innerKeyValues }) => new Element({
|
|
537
|
+
doc: innerDoc,
|
|
538
|
+
keyValues: Array.appendAll(outerKeyValues, innerKeyValues)
|
|
539
|
+
})), Stream.orElseIfEmpty(() => Stream.unwrap(Effect.gen(function* () {
|
|
540
|
+
const readBudget = yield* QueryStreamReadBudget;
|
|
541
|
+
if (yield* readBudget.isStopped) return Stream.empty;
|
|
542
|
+
const original = yield* Option.match(Option.gen(function* () {
|
|
543
|
+
yield* onEmpty;
|
|
544
|
+
return yield* Option.orElse(innerParsedBounds.lower, () => innerParsedBounds.upper);
|
|
545
|
+
}), {
|
|
546
|
+
onNone: () => Effect.succeed(Option.none()),
|
|
547
|
+
onSome: () => Stream.runHead(inner.annotated)
|
|
548
|
+
});
|
|
549
|
+
const isStopped = yield* readBudget.isStopped;
|
|
550
|
+
return Option.match(original, {
|
|
551
|
+
onSome: () => Stream.empty,
|
|
552
|
+
onNone: () => isStopped ? Stream.empty : markerStream(outerKeyValues, innerParsedBounds, Option.map(onEmpty, (makeEmpty) => makeEmpty(doc)))
|
|
553
|
+
});
|
|
554
|
+
}))));
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
}));
|
|
558
|
+
const split = (bound) => {
|
|
559
|
+
const keyValues = values(bound.key);
|
|
560
|
+
const { inclusive } = bound;
|
|
561
|
+
return keyValues.length <= outerLength ? FlatMapBound.Outer({
|
|
562
|
+
keyValues,
|
|
563
|
+
inclusive
|
|
564
|
+
}) : FlatMapBound.Inner({
|
|
565
|
+
outerKeyValues: Array.take(keyValues, outerLength),
|
|
566
|
+
innerParsedBound: Result.getOrThrowWith(parseBound(innerKeyLayout, {
|
|
567
|
+
keyValues: Array.drop(keyValues, outerLength),
|
|
568
|
+
inclusive
|
|
569
|
+
}), identity)
|
|
570
|
+
});
|
|
571
|
+
};
|
|
572
|
+
const outerBound = (bound) => FlatMapBound.$match(bound, {
|
|
573
|
+
Outer: ({ keyValues, inclusive }) => ({
|
|
574
|
+
keyValues,
|
|
575
|
+
inclusive
|
|
576
|
+
}),
|
|
577
|
+
Inner: ({ outerKeyValues }) => ({
|
|
578
|
+
keyValues: outerKeyValues,
|
|
579
|
+
inclusive: true
|
|
580
|
+
})
|
|
581
|
+
});
|
|
582
|
+
return new QueryStream(self.orderDirection, keyLayout, annotated, void 0, (parsedBounds) => {
|
|
583
|
+
const lower = Option.map(parsedBounds.lower, split);
|
|
584
|
+
const upper = Option.map(parsedBounds.upper, split);
|
|
585
|
+
return makeFlatMap(narrowByKeyBounds(self, {
|
|
586
|
+
lower: Option.map(lower, outerBound),
|
|
587
|
+
upper: Option.map(upper, outerBound)
|
|
588
|
+
}), f, innerKeyLayout, onEmpty, {
|
|
589
|
+
lower: combineLowerRefinements(refinements.lower, Option.filter(lower, FlatMapBound.$is("Inner"))),
|
|
590
|
+
upper: combineUpperRefinements(refinements.upper, Option.filter(upper, FlatMapBound.$is("Inner")))
|
|
591
|
+
});
|
|
592
|
+
}, () => makeFlatMap(reverse(self), (doc) => reverse(f(doc)), innerKeyLayout, onEmpty, refinements));
|
|
593
|
+
};
|
|
594
|
+
/**
|
|
595
|
+
* Keep the first document for each distinct value of a key prefix. Similar to
|
|
596
|
+
* `Stream.changes` on that prefix, this groups consecutive equal keys, but
|
|
597
|
+
* seeks past the rest of each group instead of scanning it.
|
|
598
|
+
*
|
|
599
|
+
* Pass a prefix of the visible ordering labels, such as `["text"]` for notes
|
|
600
|
+
* ordered by text and creation time. Prefix validity is checked by TypeScript
|
|
601
|
+
* and at runtime. On a `flatMap` result, a prefix reaching into the inner key
|
|
602
|
+
* also groups by the outer document, including its ID.
|
|
603
|
+
*
|
|
604
|
+
* Operation order controls representative selection. Filter before `distinct`
|
|
605
|
+
* to choose the first matching document, or after it to test only the selected
|
|
606
|
+
* representative. `reverse(distinct(q))` keeps those representatives and flips
|
|
607
|
+
* their output order; `distinct(reverse(q))` selects from the other direction.
|
|
608
|
+
*
|
|
609
|
+
* Narrowing after `distinct` filters the original representatives without
|
|
610
|
+
* selecting replacements. Narrowing before it constrains the input and can
|
|
611
|
+
* change the representatives. Preserving representatives may require extra
|
|
612
|
+
* reads outside output bounds and repeated callback evaluation.
|
|
613
|
+
*
|
|
614
|
+
* @experimental
|
|
615
|
+
*/
|
|
616
|
+
const distinct = dual(2, (self, prefixNames) => {
|
|
617
|
+
return makeDistinct(self, Result.getOrThrowWith(resolvePrefix(self.keyLayout, make(prefixNames)), identity), self.orderDirection, unbounded(self.keyLayout));
|
|
618
|
+
});
|
|
619
|
+
/**
|
|
620
|
+
* Relabel visible key positions without changing values or their order. This
|
|
621
|
+
* does not sort or transform emitted documents.
|
|
622
|
+
*
|
|
623
|
+
* Use it to merge indexes or tables ordered by comparable values under
|
|
624
|
+
* different field names. For example, relabel comments ordered by `["body",
|
|
625
|
+
* "_creationTime"]` as `["text", "_creationTime"]` to merge with notes ordered
|
|
626
|
+
* by text. Map both streams to a common value shape first.
|
|
627
|
+
*
|
|
628
|
+
* Supply exactly as many replacement labels as the original visible labels. The
|
|
629
|
+
* values at corresponding positions must be comparable; relabeling does not
|
|
630
|
+
* establish that semantic compatibility for you. Implicit ID tiebreakers keep
|
|
631
|
+
* their positions. For joined streams, replacement labels cover the combined
|
|
632
|
+
* key, outer labels first.
|
|
633
|
+
*
|
|
634
|
+
* @experimental
|
|
635
|
+
*/
|
|
636
|
+
const renameKey = dual(2, (self, replacementNames) => renameKeyImpl(self, make(replacementNames)));
|
|
637
|
+
const renameKeyImpl = (self, replacementKeyLabels) => {
|
|
638
|
+
const keyLayout = Result.getOrThrowWith(rename(self.keyLayout, replacementKeyLabels), identity);
|
|
639
|
+
return new QueryStream(self.orderDirection, keyLayout, self.annotated, void 0, (parsedBounds) => renameKeyImpl(narrowByKeyBounds(self, toBounds(parsedBounds)), replacementKeyLabels), () => renameKeyImpl(reverse(self), replacementKeyLabels));
|
|
640
|
+
};
|
|
641
|
+
const makeDistinct = (self, distinctLength, orderDirection, parsedBounds) => {
|
|
642
|
+
const afterKeyValues = (keyValues) => {
|
|
643
|
+
const pastGroup = {
|
|
644
|
+
keyValues,
|
|
645
|
+
inclusive: false
|
|
646
|
+
};
|
|
647
|
+
return orderDirection === "asc" ? {
|
|
648
|
+
lower: Option.some(pastGroup),
|
|
649
|
+
upper: Option.none()
|
|
650
|
+
} : {
|
|
651
|
+
lower: Option.none(),
|
|
652
|
+
upper: Option.some(pastGroup)
|
|
653
|
+
};
|
|
654
|
+
};
|
|
655
|
+
const groupBound = (bound) => Option.map(bound, ({ inclusive, key }) => ({
|
|
656
|
+
keyValues: Array.take(values(key), distinctLength),
|
|
657
|
+
inclusive: values(key).length > distinctLength || inclusive
|
|
658
|
+
}));
|
|
659
|
+
const { aboveLower, belowUpper } = keyPredicates(parsedBounds);
|
|
660
|
+
const isAdmitted = (keyValues) => aboveLower(keyValues) && belowUpper(keyValues);
|
|
661
|
+
const annotated = Stream.unwrap(Effect.map(QueryStreamReadBudget, (readBudget) => Stream.paginate(narrowByKeyBounds(orderDirection === self.orderDirection ? self : reverse(self), {
|
|
662
|
+
lower: groupBound(parsedBounds.lower),
|
|
663
|
+
upper: groupBound(parsedBounds.upper)
|
|
664
|
+
}), (current) => Effect.gen(function* () {
|
|
665
|
+
if (yield* readBudget.isStopped) return Tuple.make([], Option.none());
|
|
666
|
+
const discovered = yield* Stream.runHead(current.annotated);
|
|
667
|
+
if (Option.isNone(discovered)) return Tuple.make([], Option.none());
|
|
668
|
+
const element = discovered.value;
|
|
669
|
+
const { doc, keyValues } = element;
|
|
670
|
+
const prefixKeyValues = Array.take(keyValues, distinctLength);
|
|
671
|
+
if (orderDirection === self.orderDirection) {
|
|
672
|
+
const nextKeyValues = Option.match(doc, {
|
|
673
|
+
onNone: () => keyValues,
|
|
674
|
+
onSome: () => prefixKeyValues
|
|
675
|
+
});
|
|
676
|
+
return Tuple.make(isAdmitted(keyValues) ? [element] : [], Option.some(narrowByKeyBounds(current, afterKeyValues(nextKeyValues))));
|
|
677
|
+
}
|
|
678
|
+
const next = Option.some(narrowByKeyBounds(current, afterKeyValues(prefixKeyValues)));
|
|
679
|
+
const { firstKeyValues, selected } = yield* narrowByKeyBounds(self, {
|
|
680
|
+
lower: Option.some({
|
|
681
|
+
keyValues: prefixKeyValues,
|
|
682
|
+
inclusive: true
|
|
683
|
+
}),
|
|
684
|
+
upper: Option.some({
|
|
685
|
+
keyValues: prefixKeyValues,
|
|
686
|
+
inclusive: true
|
|
687
|
+
})
|
|
688
|
+
}).annotated.pipe(Stream.run(Sink.fold(() => ({
|
|
689
|
+
firstKeyValues: Option.none(),
|
|
690
|
+
selected: Option.none()
|
|
691
|
+
}), (probe) => Option.isNone(probe.selected), (probe, candidate) => Effect.succeed({
|
|
692
|
+
firstKeyValues: Option.orElse(probe.firstKeyValues, () => Option.some(candidate.keyValues)),
|
|
693
|
+
selected: Option.as(candidate.doc, candidate)
|
|
694
|
+
}))));
|
|
695
|
+
if (Option.isSome(selected)) {
|
|
696
|
+
const representative = selected.value;
|
|
697
|
+
return Tuple.make(isAdmitted(representative.keyValues) ? [representative] : [], next);
|
|
698
|
+
}
|
|
699
|
+
if (yield* readBudget.isStopped) return Tuple.make([], Option.none());
|
|
700
|
+
const checkpointKeyValues = Option.getOrElse(firstKeyValues, () => keyValues);
|
|
701
|
+
return Tuple.make(isAdmitted(checkpointKeyValues) ? [new Element({
|
|
702
|
+
doc: Option.none(),
|
|
703
|
+
keyValues: checkpointKeyValues
|
|
704
|
+
})] : [], next);
|
|
705
|
+
}))));
|
|
706
|
+
return new QueryStream(orderDirection, self.keyLayout, annotated, void 0, (incomingParsedBounds) => makeDistinct(self, distinctLength, orderDirection, Result.getOrThrowWith(intersect(parsedBounds, incomingParsedBounds), identity)), () => makeDistinct(self, distinctLength, flip(orderDirection), parsedBounds));
|
|
707
|
+
};
|
|
708
|
+
/**
|
|
709
|
+
* Run a composed query stream in the opposite direction without changing its
|
|
710
|
+
* key layout. Use this for bidirectional pagination: a reversed feed can load
|
|
711
|
+
* its earlier pages.
|
|
712
|
+
*
|
|
713
|
+
* Reversal uses index scans and seeks rather than collecting and reversing
|
|
714
|
+
* values. Merges, filters, maps, flat-maps (including their inner streams),
|
|
715
|
+
* distinct streams, renamed streams, and empty streams all support it.
|
|
716
|
+
*
|
|
717
|
+
* `reverse(distinct(q))` retains each group's original first representative and
|
|
718
|
+
* reverses their output order. `distinct(reverse(q))` instead chooses the first
|
|
719
|
+
* document from the other direction. Preserving representatives can require
|
|
720
|
+
* extra reads to discover groups and seek their original first documents.
|
|
721
|
+
*
|
|
722
|
+
* A manually constructed stream without `reverseWith` throws
|
|
723
|
+
* `MissingReversalRecipeError`.
|
|
724
|
+
*
|
|
725
|
+
* @experimental
|
|
726
|
+
*/
|
|
727
|
+
const reverse = (self) => {
|
|
728
|
+
if (self.reverseWith === void 0) throw new MissingReversalRecipeError();
|
|
729
|
+
return self.reverseWith();
|
|
730
|
+
};
|
|
731
|
+
/**
|
|
732
|
+
* Restrict a stream to keys between `start` and `end`. Provide at least one
|
|
733
|
+
* endpoint, each with `keyValues` and a required `inclusive` flag; omit the
|
|
734
|
+
* other endpoint to leave that side unbounded.
|
|
735
|
+
*
|
|
736
|
+
* Endpoints follow stream order: `start` is the lower key when ascending and
|
|
737
|
+
* the upper key when descending. Repeated narrowing intersects existing bounds
|
|
738
|
+
* and can only restrict the range further.
|
|
739
|
+
*
|
|
740
|
+
* A key may be a prefix of the full key. An inclusive prefix includes every key
|
|
741
|
+
* extending it; an exclusive prefix excludes the whole group. For example,
|
|
742
|
+
* creation-time bounds can use `[startTime]` and `[endTime]` without trailing
|
|
743
|
+
* IDs. Equal endpoints produce an empty range unless both are inclusive, in
|
|
744
|
+
* which case they select that key or prefix group.
|
|
745
|
+
*
|
|
746
|
+
* Bounds are pushed into index ranges where that preserves the composition's
|
|
747
|
+
* results. On a distinct stream, full-key bounds filter original
|
|
748
|
+
* representatives without choosing replacements; prefix bounds include or
|
|
749
|
+
* exclude whole groups. This can require reads outside the output bounds. Apply
|
|
750
|
+
* bounds before `distinct` instead to constrain which documents can represent a
|
|
751
|
+
* group. Manually constructed streams without `narrowWith` are filtered in
|
|
752
|
+
* memory.
|
|
753
|
+
*
|
|
754
|
+
* For pagination, pass the previous `continueCursor` unchanged to `paginate`;
|
|
755
|
+
* do not decode a cursor into explicit bounds.
|
|
756
|
+
*
|
|
757
|
+
* @experimental
|
|
758
|
+
*/
|
|
759
|
+
const narrow = dual(2, (self, narrowBounds) => {
|
|
760
|
+
const start = Option.fromUndefinedOr(narrowBounds.start);
|
|
761
|
+
const end = Option.fromUndefinedOr(narrowBounds.end);
|
|
762
|
+
const keyBounds = self.orderDirection === "asc" ? {
|
|
763
|
+
lower: start,
|
|
764
|
+
upper: end
|
|
765
|
+
} : {
|
|
766
|
+
lower: end,
|
|
767
|
+
upper: start
|
|
768
|
+
};
|
|
769
|
+
return narrowByKeyBounds(self, keyBounds);
|
|
770
|
+
});
|
|
771
|
+
const narrowByKeyBounds = (self, keyBounds) => {
|
|
772
|
+
const parsedBounds = Result.getOrThrowWith(parse(self.keyLayout, keyBounds), identity);
|
|
773
|
+
return narrowByParsedBounds(self, parsedBounds);
|
|
774
|
+
};
|
|
775
|
+
const narrowByParsedBounds = (self, parsedBounds) => {
|
|
776
|
+
const unbounded = Option.isNone(parsedBounds.lower) && Option.isNone(parsedBounds.upper);
|
|
777
|
+
if (!unbounded && self.narrowWith !== void 0) return self.narrowWith(parsedBounds);
|
|
778
|
+
Result.getOrThrowWith(forLayout(self.keyLayout, parsedBounds), identity);
|
|
779
|
+
return unbounded ? self : narrowInMemory(self, parsedBounds);
|
|
780
|
+
};
|
|
781
|
+
/**
|
|
782
|
+
* The fallback for streams that don't know how to rebuild themselves.
|
|
783
|
+
*/
|
|
784
|
+
const keyPredicates = (parsedBounds) => {
|
|
785
|
+
const complete$1 = (keyValues) => Result.getOrThrowWith(complete(parsedBounds.keyLayout, keyValues), identity);
|
|
786
|
+
return {
|
|
787
|
+
aboveLower: (keyValues) => Result.getOrThrowWith(admittedByLower(parsedBounds)(complete$1(keyValues)), identity),
|
|
788
|
+
belowUpper: (keyValues) => Result.getOrThrowWith(admittedByUpper(parsedBounds)(complete$1(keyValues)), identity)
|
|
789
|
+
};
|
|
790
|
+
};
|
|
791
|
+
const narrowInMemory = (self, parsedBounds) => {
|
|
792
|
+
const { aboveLower, belowUpper } = keyPredicates(parsedBounds);
|
|
793
|
+
const dropOutOfRange = self.orderDirection === "asc" ? Stream.dropWhile(({ keyValues }) => !aboveLower(keyValues)) : Stream.dropWhile(({ keyValues }) => !belowUpper(keyValues));
|
|
794
|
+
const takeInRange = self.orderDirection === "asc" ? Stream.takeWhile(({ keyValues }) => belowUpper(keyValues)) : Stream.takeWhile(({ keyValues }) => aboveLower(keyValues));
|
|
795
|
+
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
|
+
};
|
|
797
|
+
/**
|
|
798
|
+
* `unique` found at least two emitted values. Narrow the query if at most one
|
|
799
|
+
* value was expected, or use another consumer to handle multiple results.
|
|
800
|
+
*
|
|
801
|
+
* @experimental
|
|
802
|
+
*/
|
|
803
|
+
var NotUniqueError = class extends Schema.TaggedError()("NotUniqueError", {}) {
|
|
804
|
+
get message() {
|
|
805
|
+
return "Expected the query stream to contain at most one document";
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
/**
|
|
809
|
+
* Consume a stream expected to emit at most one value. The effect returns
|
|
810
|
+
* `None` for no values and `Some(value)` for one, or fails with
|
|
811
|
+
* `NotUniqueError` for two or more. Like `Stream.runHead` with a uniqueness
|
|
812
|
+
* check, it inspects at most two emitted values; finding them may read
|
|
813
|
+
* additional filtered documents.
|
|
814
|
+
*
|
|
815
|
+
* @experimental
|
|
816
|
+
*/
|
|
817
|
+
const unique = Effect.fn("QueryStream.unique")((self) => self.pipe(Stream.take(2), Stream.runCollect, Effect.flatMap((docs) => docs.length >= 2 ? Effect.fail(new NotUniqueError()) : Effect.succeed(Array.head(docs)))));
|
|
818
|
+
/**
|
|
819
|
+
* Return an effect producing one page of a composed query stream. Start with
|
|
820
|
+
* `cursor: null`, then pass each returned `continueCursor` back unchanged until
|
|
821
|
+
* `isDone`. Cursors record key boundaries, not offsets or the last visible
|
|
822
|
+
* document: filtered elements can advance the cursor without entering `page`.
|
|
823
|
+
*
|
|
824
|
+
* `cursor` is exclusive and `endCursor` is inclusive, in stream order. Without
|
|
825
|
+
* an end boundary, `numItems` limits emitted values. With `endCursor`, the page
|
|
826
|
+
* covers the pinned range regardless of its item count, keeping adjacent
|
|
827
|
+
* reactive pages gap-free as data changes. See `PaginateOptions` for read
|
|
828
|
+
* limits and the special case of a zero-sized request.
|
|
829
|
+
*
|
|
830
|
+
* Bounds are pushed into underlying index queries where possible, but composed
|
|
831
|
+
* streams may read more documents than they return, including documents read on
|
|
832
|
+
* earlier pages. Budgets count filtered documents, distinct-group discovery,
|
|
833
|
+
* repeated seeks, merge prefetch, and both outer and inner query-stream reads
|
|
834
|
+
* in joins. Arbitrary I/O inside callbacks, such as a separate lookup in
|
|
835
|
+
* `mapEffect`, is not tracked.
|
|
836
|
+
*
|
|
837
|
+
* A budget-limited page returns `SplitRequired` at a safe logical boundary. If
|
|
838
|
+
* the budget prevents safe progress or an interior split of a pinned page, the
|
|
839
|
+
* effect fails with `ReadBudgetExceededError` rather than returning a
|
|
840
|
+
* non-advancing cursor. Increase the budget or reduce the reads needed for
|
|
841
|
+
* progress. See `PaginationResult` for split and completion handling.
|
|
842
|
+
*
|
|
843
|
+
* Malformed or incompatible cursors die with a `ConvexError` whose data is `{
|
|
844
|
+
* paginationError: "InvalidCursor" }`; Confect's pagination clients restart
|
|
845
|
+
* from the first page. Restart pagination whenever filters, ordering, or other
|
|
846
|
+
* query semantics change, even if an old cursor is still accepted.
|
|
847
|
+
*
|
|
848
|
+
* 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.
|
|
852
|
+
*
|
|
853
|
+
* Return the whole result to reactive clients. React's
|
|
854
|
+
* `useStreamPaginatedQuery` handles pinning, splitting, and invalid-cursor
|
|
855
|
+
* resets; `usePaginatedQuery` does not support stream pages. Foldkit's
|
|
856
|
+
* `PaginatedQuery` also supports them.
|
|
857
|
+
*
|
|
858
|
+
* @experimental
|
|
859
|
+
*/
|
|
860
|
+
const paginate = dual(2, Effect.fn("QueryStream.paginate")(function* (self, options) {
|
|
861
|
+
const cursorSchema = fromKeyLayout(self.keyLayout);
|
|
862
|
+
const encodeCursor = Schema.encodeEffect(cursorSchema);
|
|
863
|
+
const decodeCursor = Schema.decodeEffect(cursorSchema);
|
|
864
|
+
const complete$2 = (keyValues) => Result.getOrThrowWith(complete(self.keyLayout, keyValues), identity);
|
|
865
|
+
const decoded = yield* Effect.all({
|
|
866
|
+
start: Option.match(Option.fromNullOr(options.cursor), {
|
|
867
|
+
onNone: () => Effect.succeed(Start.Beginning()),
|
|
868
|
+
onSome: (cursor) => Effect.map(decodeCursor(cursor), (key) => Start.After({
|
|
869
|
+
cursor,
|
|
870
|
+
key
|
|
871
|
+
}))
|
|
872
|
+
}),
|
|
873
|
+
range: Option.match(Option.fromNullishOr(options.endCursor), {
|
|
874
|
+
onNone: () => Effect.succeed(Range.Unpinned()),
|
|
875
|
+
onSome: (cursor) => cursor === "[]" ? Effect.succeed(Range.ThroughEnd()) : Effect.map(decodeCursor(cursor), (key) => Range.ThroughKey({ key }))
|
|
876
|
+
})
|
|
877
|
+
}).pipe(Effect.catchTag("SchemaError", () => Effect.die(new ConvexError({ paginationError: "InvalidCursor" }))));
|
|
878
|
+
const request = yield* parseRequest(options.numItems, decoded.start, decoded.range).pipe(Effect.fromResult, Effect.catchTags({
|
|
879
|
+
InvalidPageSizeError: Effect.die,
|
|
880
|
+
EmptyInitialPageError: Effect.die
|
|
881
|
+
}));
|
|
882
|
+
if (request._tag === "Unchanged") return {
|
|
883
|
+
page: [],
|
|
884
|
+
isDone: false,
|
|
885
|
+
continueCursor: request.cursor
|
|
886
|
+
};
|
|
887
|
+
const start = Option.map(request.after, (key) => ({
|
|
888
|
+
key: toPrefix(key),
|
|
889
|
+
inclusive: false
|
|
890
|
+
}));
|
|
891
|
+
const end = Range.$match(request.range, {
|
|
892
|
+
Unpinned: () => Option.none(),
|
|
893
|
+
ThroughEnd: () => Option.none(),
|
|
894
|
+
ThroughKey: ({ key }) => Option.some({
|
|
895
|
+
key: toPrefix(key),
|
|
896
|
+
inclusive: true
|
|
897
|
+
})
|
|
898
|
+
});
|
|
899
|
+
const narrowed = narrowByParsedBounds(self, Result.getOrThrowWith(fromParsed(self.keyLayout, self.orderDirection === "asc" ? {
|
|
900
|
+
lower: start,
|
|
901
|
+
upper: end
|
|
902
|
+
} : {
|
|
903
|
+
lower: end,
|
|
904
|
+
upper: start
|
|
905
|
+
}), identity));
|
|
906
|
+
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))));
|
|
908
|
+
const stopped = yield* readBudget.isStopped;
|
|
909
|
+
const outcome = finish(request, collected, stopped);
|
|
910
|
+
if (Result.isFailure(outcome)) return yield* new ReadBudgetExceededError(yield* readBudget.getReadCounts);
|
|
911
|
+
const encode = (key) => encodeCursor(key).pipe(Effect.catchTag("SchemaError", Effect.die));
|
|
912
|
+
const encodeSplit = (result) => Effect.gen(function* () {
|
|
913
|
+
const continueCursor = yield* Match.value(result.continuation).pipe(Match.tagsExhaustive({
|
|
914
|
+
End: () => Effect.succeed("[]"),
|
|
915
|
+
Key: ({ key }) => encode(key)
|
|
916
|
+
}));
|
|
917
|
+
return {
|
|
918
|
+
page: Array.fromIterable(result.page),
|
|
919
|
+
isDone: false,
|
|
920
|
+
continueCursor,
|
|
921
|
+
pageStatus: result._tag,
|
|
922
|
+
splitCursor: yield* encode(result.splitKey)
|
|
923
|
+
};
|
|
924
|
+
});
|
|
925
|
+
return yield* Match.value(outcome.success).pipe(Match.tagsExhaustive({
|
|
926
|
+
Done: ({ page }) => Effect.succeed({
|
|
927
|
+
page: Array.fromIterable(page),
|
|
928
|
+
isDone: true,
|
|
929
|
+
continueCursor: "[]"
|
|
930
|
+
}),
|
|
931
|
+
Continue: ({ page, key }) => Effect.map(encode(key), (continueCursor) => ({
|
|
932
|
+
page: Array.fromIterable(page),
|
|
933
|
+
isDone: false,
|
|
934
|
+
continueCursor
|
|
935
|
+
})),
|
|
936
|
+
SplitRequired: encodeSplit,
|
|
937
|
+
SplitRecommended: encodeSplit
|
|
938
|
+
}));
|
|
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)))));
|
|
940
|
+
//#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 };
|
|
942
|
+
|
|
943
|
+
//# sourceMappingURL=QueryStream.js.map
|