@abloatai/humans 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -0
- package/dist/Ablo.d.ts +208 -0
- package/dist/Ablo.js +120 -0
- package/dist/client.d.ts +317 -0
- package/dist/client.js +13 -0
- package/dist/core.d.ts +35 -0
- package/dist/core.js +48 -0
- package/dist/humans.d.ts +28 -0
- package/dist/humans.js +34 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +6 -0
- package/dist/local/BaseSyncedStore.d.ts +807 -0
- package/dist/local/BaseSyncedStore.js +1516 -0
- package/dist/local/Database.d.ts +322 -0
- package/dist/local/Database.js +1589 -0
- package/dist/local/InstanceCache.d.ts +255 -0
- package/dist/local/InstanceCache.js +1263 -0
- package/dist/local/LazyReferenceCollection.d.ts +177 -0
- package/dist/local/LazyReferenceCollection.js +461 -0
- package/dist/local/Model.d.ts +475 -0
- package/dist/local/Model.js +950 -0
- package/dist/local/ModelRegistry.d.ts +225 -0
- package/dist/local/ModelRegistry.js +539 -0
- package/dist/local/NetworkMonitor.d.ts +28 -0
- package/dist/local/NetworkMonitor.js +79 -0
- package/dist/local/RuntimeContext.d.ts +52 -0
- package/dist/local/RuntimeContext.js +80 -0
- package/dist/local/SyncClient.d.ts +516 -0
- package/dist/local/SyncClient.js +1754 -0
- package/dist/local/adapters/alwaysOnline.d.ts +14 -0
- package/dist/local/adapters/alwaysOnline.js +17 -0
- package/dist/local/adapters/inMemoryStorage.d.ts +37 -0
- package/dist/local/adapters/inMemoryStorage.js +122 -0
- package/dist/local/client/clientPrelude.d.ts +52 -0
- package/dist/local/client/clientPrelude.js +60 -0
- package/dist/local/client/consoleLogger.d.ts +35 -0
- package/dist/local/client/consoleLogger.js +44 -0
- package/dist/local/client/createInternalComponents.d.ts +50 -0
- package/dist/local/client/createInternalComponents.js +98 -0
- package/dist/local/client/createModelProxy.d.ts +248 -0
- package/dist/local/client/createModelProxy.js +880 -0
- package/dist/local/client/modelRegistration.d.ts +10 -0
- package/dist/local/client/modelRegistration.js +316 -0
- package/dist/local/client/options.d.ts +440 -0
- package/dist/local/client/options.js +7 -0
- package/dist/local/client/reactiveEngine.d.ts +53 -0
- package/dist/local/client/reactiveEngine.js +705 -0
- package/dist/local/client/resourceTypes.d.ts +12 -0
- package/dist/local/client/resourceTypes.js +10 -0
- package/dist/local/client/schemaConfig.d.ts +44 -0
- package/dist/local/client/schemaConfig.js +185 -0
- package/dist/local/client/storeCluster.d.ts +46 -0
- package/dist/local/client/storeCluster.js +133 -0
- package/dist/local/client/storeLifecycle.d.ts +61 -0
- package/dist/local/client/storeLifecycle.js +236 -0
- package/dist/local/client/validateAbloOptions.d.ts +42 -0
- package/dist/local/client/validateAbloOptions.js +43 -0
- package/dist/local/client/wsMutationExecutor.d.ts +27 -0
- package/dist/local/client/wsMutationExecutor.js +72 -0
- package/dist/local/context.d.ts +42 -0
- package/dist/local/context.js +81 -0
- package/dist/local/coordination/ClaimLog.d.ts +26 -0
- package/dist/local/coordination/ClaimLog.js +32 -0
- package/dist/local/interfaces/index.d.ts +311 -0
- package/dist/local/interfaces/index.js +9 -0
- package/dist/local/localModelContract.d.ts +14 -0
- package/dist/local/localModelContract.js +1 -0
- package/dist/local/logPosition.d.ts +31 -0
- package/dist/local/logPosition.js +53 -0
- package/dist/local/mutationPersistence.d.ts +6 -0
- package/dist/local/mutationPersistence.js +1 -0
- package/dist/local/mutators/RecordingMutation.d.ts +36 -0
- package/dist/local/mutators/RecordingMutation.js +182 -0
- package/dist/local/mutators/Transaction.d.ts +40 -0
- package/dist/local/mutators/Transaction.js +58 -0
- package/dist/local/mutators/UndoManager.d.ts +258 -0
- package/dist/local/mutators/UndoManager.js +665 -0
- package/dist/local/mutators/defineMutators.d.ts +60 -0
- package/dist/local/mutators/defineMutators.js +18 -0
- package/dist/local/mutators/inverseOp.d.ts +126 -0
- package/dist/local/mutators/inverseOp.js +71 -0
- package/dist/local/mutators/mutateActions.d.ts +45 -0
- package/dist/local/mutators/mutateActions.js +105 -0
- package/dist/local/mutators/readerActions.d.ts +33 -0
- package/dist/local/mutators/readerActions.js +57 -0
- package/dist/local/mutators/undoApply.d.ts +51 -0
- package/dist/local/mutators/undoApply.js +117 -0
- package/dist/local/persistence.d.ts +7 -0
- package/dist/local/persistence.js +9 -0
- package/dist/local/query/QueryProcessor.d.ts +75 -0
- package/dist/local/query/QueryProcessor.js +255 -0
- package/dist/local/query/client.d.ts +64 -0
- package/dist/local/query/client.js +138 -0
- package/dist/local/query/types.d.ts +85 -0
- package/dist/local/query/types.js +16 -0
- package/dist/local/schema/serialize.d.ts +1 -0
- package/dist/local/schema/serialize.js +1 -0
- package/dist/local/store/queryApi.d.ts +13 -0
- package/dist/local/store/queryApi.js +35 -0
- package/dist/local/storeContract.d.ts +145 -0
- package/dist/local/storeContract.js +12 -0
- package/dist/local/stores/DatabaseManager.d.ts +112 -0
- package/dist/local/stores/DatabaseManager.js +400 -0
- package/dist/local/stores/ObjectStore.d.ts +115 -0
- package/dist/local/stores/ObjectStore.js +393 -0
- package/dist/local/stores/ObjectStoreContract.d.ts +38 -0
- package/dist/local/stores/ObjectStoreContract.js +1 -0
- package/dist/local/stores/StoreManager.d.ts +114 -0
- package/dist/local/stores/StoreManager.js +304 -0
- package/dist/local/stores/SyncActionStore.d.ts +99 -0
- package/dist/local/stores/SyncActionStore.js +506 -0
- package/dist/local/stores/openIDBWithTimeout.d.ts +65 -0
- package/dist/local/stores/openIDBWithTimeout.js +153 -0
- package/dist/local/stores/persistenceCleanup.d.ts +7 -0
- package/dist/local/stores/persistenceCleanup.js +26 -0
- package/dist/local/stores/persistenceIdentity.d.ts +27 -0
- package/dist/local/stores/persistenceIdentity.js +38 -0
- package/dist/local/stores/syncAction.d.ts +26 -0
- package/dist/local/stores/syncAction.js +16 -0
- package/dist/local/stores/v1PersistenceDeletion.d.ts +8 -0
- package/dist/local/stores/v1PersistenceDeletion.js +16 -0
- package/dist/local/sync/BootstrapFetcher.d.ts +284 -0
- package/dist/local/sync/BootstrapFetcher.js +964 -0
- package/dist/local/sync/ConnectionManager.d.ts +8 -0
- package/dist/local/sync/ConnectionManager.js +8 -0
- package/dist/local/sync/OnDemandLoader.d.ts +231 -0
- package/dist/local/sync/OnDemandLoader.js +743 -0
- package/dist/local/sync/SubscriptionManager.d.ts +159 -0
- package/dist/local/sync/SubscriptionManager.js +243 -0
- package/dist/local/sync/SyncWebSocket.d.ts +173 -0
- package/dist/local/sync/SyncWebSocket.js +438 -0
- package/dist/local/sync/bootstrapApply.d.ts +73 -0
- package/dist/local/sync/bootstrapApply.js +73 -0
- package/dist/local/sync/commitFrames.d.ts +8 -0
- package/dist/local/sync/commitFrames.js +8 -0
- package/dist/local/sync/connectionManagerLifecycle.d.ts +23 -0
- package/dist/local/sync/connectionManagerLifecycle.js +126 -0
- package/dist/local/sync/contextPorts.d.ts +18 -0
- package/dist/local/sync/contextPorts.js +31 -0
- package/dist/local/sync/createClaimStream.d.ts +64 -0
- package/dist/local/sync/createClaimStream.js +475 -0
- package/dist/local/sync/createSnapshot.d.ts +29 -0
- package/dist/local/sync/createSnapshot.js +116 -0
- package/dist/local/sync/credentialLifecycle.d.ts +7 -0
- package/dist/local/sync/credentialLifecycle.js +7 -0
- package/dist/local/sync/deltaPipeline.d.ts +116 -0
- package/dist/local/sync/deltaPipeline.js +357 -0
- package/dist/local/sync/groupChange.d.ts +116 -0
- package/dist/local/sync/groupChange.js +244 -0
- package/dist/local/sync/initialize.d.ts +27 -0
- package/dist/local/sync/initialize.js +137 -0
- package/dist/local/sync/participants.d.ts +132 -0
- package/dist/local/sync/participants.js +342 -0
- package/dist/local/sync/persistedPrefix.d.ts +12 -0
- package/dist/local/sync/persistedPrefix.js +22 -0
- package/dist/local/sync/reconnect.d.ts +23 -0
- package/dist/local/sync/reconnect.js +55 -0
- package/dist/local/sync/schemaDrift.d.ts +55 -0
- package/dist/local/sync/schemaDrift.js +53 -0
- package/dist/local/sync/schemas.d.ts +71 -0
- package/dist/local/sync/schemas.js +94 -0
- package/dist/local/sync/socketEventWiring.d.ts +31 -0
- package/dist/local/sync/socketEventWiring.js +130 -0
- package/dist/local/sync/syncCursor.d.ts +40 -0
- package/dist/local/sync/syncCursor.js +55 -0
- package/dist/local/sync/syncPlan.d.ts +54 -0
- package/dist/local/sync/syncPlan.js +50 -0
- package/dist/local/sync/terminalSessionLifecycle.d.ts +20 -0
- package/dist/local/sync/terminalSessionLifecycle.js +50 -0
- package/dist/local/sync/wsFrameHandlers.d.ts +8 -0
- package/dist/local/sync/wsFrameHandlers.js +8 -0
- package/dist/local/transactions/databaseCommitOutbox.d.ts +15 -0
- package/dist/local/transactions/databaseCommitOutbox.js +16 -0
- package/dist/local/transactions/localMutation.d.ts +10 -0
- package/dist/local/transactions/localMutation.js +37 -0
- package/dist/local/transactions/mutations/MutationQueue.d.ts +511 -0
- package/dist/local/transactions/mutations/MutationQueue.js +1498 -0
- package/dist/local/transactions/mutations/MutationStore.d.ts +20 -0
- package/dist/local/transactions/mutations/MutationStore.js +53 -0
- package/dist/local/transactions/mutations/UnconfirmedWrites.d.ts +82 -0
- package/dist/local/transactions/mutations/UnconfirmedWrites.js +104 -0
- package/dist/local/transactions/mutations/batchProcessing.d.ts +64 -0
- package/dist/local/transactions/mutations/batchProcessing.js +349 -0
- package/dist/local/transactions/mutations/coalesceRules.d.ts +58 -0
- package/dist/local/transactions/mutations/coalesceRules.js +140 -0
- package/dist/local/transactions/mutations/commitApi.d.ts +19 -0
- package/dist/local/transactions/mutations/commitApi.js +74 -0
- package/dist/local/transactions/mutations/commitLane.d.ts +70 -0
- package/dist/local/transactions/mutations/commitLane.js +140 -0
- package/dist/local/transactions/mutations/commitLatency.d.ts +52 -0
- package/dist/local/transactions/mutations/commitLatency.js +130 -0
- package/dist/local/transactions/mutations/commitPayload.d.ts +165 -0
- package/dist/local/transactions/mutations/commitPayload.js +152 -0
- package/dist/local/transactions/mutations/commitTransport.d.ts +36 -0
- package/dist/local/transactions/mutations/commitTransport.js +104 -0
- package/dist/local/transactions/mutations/deltaConfirmation.d.ts +63 -0
- package/dist/local/transactions/mutations/deltaConfirmation.js +235 -0
- package/dist/local/transactions/mutations/durableCommitRestore.d.ts +17 -0
- package/dist/local/transactions/mutations/durableCommitRestore.js +95 -0
- package/dist/local/transactions/mutations/durableWriteStore.d.ts +14 -0
- package/dist/local/transactions/mutations/durableWriteStore.js +12 -0
- package/dist/local/transactions/mutations/executionSelection.d.ts +6 -0
- package/dist/local/transactions/mutations/executionSelection.js +42 -0
- package/dist/local/transactions/mutations/failureHandling.d.ts +16 -0
- package/dist/local/transactions/mutations/failureHandling.js +130 -0
- package/dist/local/transactions/mutations/failurePolicy.d.ts +13 -0
- package/dist/local/transactions/mutations/failurePolicy.js +48 -0
- package/dist/local/transactions/mutations/localMutation.d.ts +58 -0
- package/dist/local/transactions/mutations/localMutation.js +75 -0
- package/dist/local/transactions/mutations/modelOperations.d.ts +44 -0
- package/dist/local/transactions/mutations/modelOperations.js +144 -0
- package/dist/local/transactions/mutations/mutationPersistence.d.ts +25 -0
- package/dist/local/transactions/mutations/mutationPersistence.js +188 -0
- package/dist/local/transactions/mutations/pendingDrain.d.ts +33 -0
- package/dist/local/transactions/mutations/pendingDrain.js +112 -0
- package/dist/local/transactions/mutations/processingScheduler.d.ts +14 -0
- package/dist/local/transactions/mutations/processingScheduler.js +24 -0
- package/dist/local/transactions/mutations/queueCoalescing.d.ts +13 -0
- package/dist/local/transactions/mutations/queueCoalescing.js +35 -0
- package/dist/local/transactions/mutations/replayValidation.d.ts +187 -0
- package/dist/local/transactions/mutations/replayValidation.js +164 -0
- package/dist/local/transactions/reconnectDrain.d.ts +11 -0
- package/dist/local/transactions/reconnectDrain.js +13 -0
- package/dist/local/utils/mobxSetup.d.ts +53 -0
- package/dist/local/utils/mobxSetup.js +330 -0
- package/dist/local/views/QueryView.d.ts +79 -0
- package/dist/local/views/QueryView.js +218 -0
- package/dist/local/views/ViewRegistry.d.ts +20 -0
- package/dist/local/views/ViewRegistry.js +57 -0
- package/dist/local/views/incrementalView.d.ts +45 -0
- package/dist/local/views/incrementalView.js +69 -0
- package/dist/plugin.d.ts +285 -0
- package/dist/plugin.js +106 -0
- package/dist/presenceStream.d.ts +69 -0
- package/dist/presenceStream.js +200 -0
- package/dist/react/AbloProvider.d.ts +242 -0
- package/dist/react/AbloProvider.js +456 -0
- package/dist/react/ClientSideSuspense.d.ts +36 -0
- package/dist/react/ClientSideSuspense.js +17 -0
- package/dist/react/DefaultFallback.d.ts +24 -0
- package/dist/react/DefaultFallback.js +43 -0
- package/dist/react/context.d.ts +55 -0
- package/dist/react/context.js +29 -0
- package/dist/react/createAbloReact.d.ts +50 -0
- package/dist/react/createAbloReact.js +48 -0
- package/dist/react/internalContext.d.ts +33 -0
- package/dist/react/internalContext.js +3 -0
- package/dist/react/useAblo.d.ts +96 -0
- package/dist/react/useAblo.js +120 -0
- package/dist/react/useCurrentUserId.d.ts +2 -0
- package/dist/react/useCurrentUserId.js +12 -0
- package/dist/react/useErrorListener.d.ts +2 -0
- package/dist/react/useErrorListener.js +14 -0
- package/dist/react/useMutationFailureListener.d.ts +8 -0
- package/dist/react/useMutationFailureListener.js +19 -0
- package/dist/react/useMutators.d.ts +56 -0
- package/dist/react/useMutators.js +84 -0
- package/dist/react/useSyncStatus.d.ts +19 -0
- package/dist/react/useSyncStatus.js +37 -0
- package/dist/react/useUndoScope.d.ts +34 -0
- package/dist/react/useUndoScope.js +73 -0
- package/dist/react.d.ts +18 -0
- package/dist/react.js +14 -0
- package/dist/reactRuntime.d.ts +4 -0
- package/dist/reactRuntime.js +2 -0
- package/dist/surface.d.ts +36 -0
- package/dist/surface.js +77 -0
- package/dist/useReactive.d.ts +6 -0
- package/dist/useReactive.js +43 -0
- package/package.json +119 -0
- package/src/Ablo.ts +456 -0
- package/src/client.ts +374 -0
- package/src/core.ts +104 -0
- package/src/humans.ts +61 -0
- package/src/index.ts +40 -0
- package/src/local/BaseSyncedStore.ts +1991 -0
- package/src/local/Database.ts +2052 -0
- package/src/local/InstanceCache.ts +1503 -0
- package/src/local/LazyReferenceCollection.ts +563 -0
- package/src/local/Model.ts +1124 -0
- package/src/local/ModelRegistry.ts +762 -0
- package/src/local/NetworkMonitor.ts +88 -0
- package/src/local/RuntimeContext.ts +141 -0
- package/src/local/SyncClient.ts +2131 -0
- package/src/local/adapters/alwaysOnline.ts +20 -0
- package/src/local/adapters/inMemoryStorage.ts +141 -0
- package/src/local/client/clientPrelude.ts +112 -0
- package/src/local/client/consoleLogger.ts +60 -0
- package/src/local/client/createInternalComponents.ts +163 -0
- package/src/local/client/createModelProxy.ts +1390 -0
- package/src/local/client/modelRegistration.ts +350 -0
- package/src/local/client/options.ts +526 -0
- package/src/local/client/reactiveEngine.ts +935 -0
- package/src/local/client/resourceTypes.ts +37 -0
- package/src/local/client/schemaConfig.ts +194 -0
- package/src/local/client/storeCluster.ts +172 -0
- package/src/local/client/storeLifecycle.ts +343 -0
- package/src/local/client/validateAbloOptions.ts +95 -0
- package/src/local/client/wsMutationExecutor.ts +110 -0
- package/src/local/context.ts +96 -0
- package/src/local/coordination/ClaimLog.ts +39 -0
- package/src/local/interfaces/index.ts +468 -0
- package/src/local/localModelContract.ts +15 -0
- package/src/local/logPosition.ts +84 -0
- package/src/local/mutationPersistence.ts +7 -0
- package/src/local/mutators/RecordingMutation.ts +222 -0
- package/src/local/mutators/Transaction.ts +97 -0
- package/src/local/mutators/UndoManager.ts +741 -0
- package/src/local/mutators/defineMutators.ts +76 -0
- package/src/local/mutators/inverseOp.ts +83 -0
- package/src/local/mutators/mutateActions.ts +167 -0
- package/src/local/mutators/readerActions.ts +99 -0
- package/src/local/mutators/undoApply.ts +141 -0
- package/src/local/persistence.ts +16 -0
- package/src/local/query/QueryProcessor.ts +347 -0
- package/src/local/query/client.ts +197 -0
- package/src/local/query/types.ts +102 -0
- package/src/local/schema/serialize.ts +1 -0
- package/src/local/store/queryApi.ts +56 -0
- package/src/local/storeContract.ts +146 -0
- package/src/local/stores/DatabaseManager.ts +507 -0
- package/src/local/stores/ObjectStore.ts +449 -0
- package/src/local/stores/ObjectStoreContract.ts +48 -0
- package/src/local/stores/StoreManager.ts +388 -0
- package/src/local/stores/SyncActionStore.ts +579 -0
- package/src/local/stores/openIDBWithTimeout.ts +195 -0
- package/src/local/stores/persistenceCleanup.ts +43 -0
- package/src/local/stores/persistenceIdentity.ts +83 -0
- package/src/local/stores/syncAction.ts +21 -0
- package/src/local/stores/v1PersistenceDeletion.ts +21 -0
- package/src/local/sync/BootstrapFetcher.ts +1224 -0
- package/src/local/sync/ConnectionManager.ts +15 -0
- package/src/local/sync/OnDemandLoader.ts +927 -0
- package/src/local/sync/SubscriptionManager.ts +300 -0
- package/src/local/sync/SyncWebSocket.ts +584 -0
- package/src/local/sync/bootstrapApply.ts +130 -0
- package/src/local/sync/commitFrames.ts +16 -0
- package/src/local/sync/connectionManagerLifecycle.ts +158 -0
- package/src/local/sync/contextPorts.ts +37 -0
- package/src/local/sync/createClaimStream.ts +668 -0
- package/src/local/sync/createSnapshot.ts +160 -0
- package/src/local/sync/credentialLifecycle.ts +18 -0
- package/src/local/sync/deltaPipeline.ts +473 -0
- package/src/local/sync/groupChange.ts +343 -0
- package/src/local/sync/initialize.ts +205 -0
- package/src/local/sync/participants.ts +564 -0
- package/src/local/sync/persistedPrefix.ts +27 -0
- package/src/local/sync/reconnect.ts +88 -0
- package/src/local/sync/schemaDrift.ts +86 -0
- package/src/local/sync/schemas.ts +118 -0
- package/src/local/sync/socketEventWiring.ts +196 -0
- package/src/local/sync/syncCursor.ts +62 -0
- package/src/local/sync/syncPlan.ts +89 -0
- package/src/local/sync/terminalSessionLifecycle.ts +66 -0
- package/src/local/sync/wsFrameHandlers.ts +20 -0
- package/src/local/transactions/databaseCommitOutbox.ts +33 -0
- package/src/local/transactions/localMutation.ts +58 -0
- package/src/local/transactions/mutations/MutationQueue.ts +1998 -0
- package/src/local/transactions/mutations/MutationStore.ts +65 -0
- package/src/local/transactions/mutations/UnconfirmedWrites.ts +133 -0
- package/src/local/transactions/mutations/batchProcessing.ts +469 -0
- package/src/local/transactions/mutations/coalesceRules.ts +192 -0
- package/src/local/transactions/mutations/commitApi.ts +97 -0
- package/src/local/transactions/mutations/commitLane.ts +191 -0
- package/src/local/transactions/mutations/commitLatency.ts +164 -0
- package/src/local/transactions/mutations/commitPayload.ts +281 -0
- package/src/local/transactions/mutations/commitTransport.ts +174 -0
- package/src/local/transactions/mutations/deltaConfirmation.ts +298 -0
- package/src/local/transactions/mutations/durableCommitRestore.ts +128 -0
- package/src/local/transactions/mutations/durableWriteStore.ts +21 -0
- package/src/local/transactions/mutations/executionSelection.ts +42 -0
- package/src/local/transactions/mutations/failureHandling.ts +154 -0
- package/src/local/transactions/mutations/failurePolicy.ts +61 -0
- package/src/local/transactions/mutations/localMutation.ts +135 -0
- package/src/local/transactions/mutations/modelOperations.ts +210 -0
- package/src/local/transactions/mutations/mutationPersistence.ts +231 -0
- package/src/local/transactions/mutations/pendingDrain.ts +160 -0
- package/src/local/transactions/mutations/processingScheduler.ts +35 -0
- package/src/local/transactions/mutations/queueCoalescing.ts +45 -0
- package/src/local/transactions/mutations/replayValidation.ts +192 -0
- package/src/local/transactions/reconnectDrain.ts +24 -0
- package/src/local/utils/mobxSetup.ts +388 -0
- package/src/local/views/QueryView.ts +311 -0
- package/src/local/views/ViewRegistry.ts +61 -0
- package/src/local/views/incrementalView.ts +92 -0
- package/src/plugin.ts +396 -0
- package/src/presenceStream.ts +279 -0
- package/src/react/AbloProvider.tsx +744 -0
- package/src/react/ClientSideSuspense.tsx +57 -0
- package/src/react/DefaultFallback.tsx +60 -0
- package/src/react/context.ts +89 -0
- package/src/react/createAbloReact.ts +116 -0
- package/src/react/internalContext.ts +38 -0
- package/src/react/useAblo.ts +280 -0
- package/src/react/useCurrentUserId.ts +17 -0
- package/src/react/useErrorListener.ts +22 -0
- package/src/react/useMutationFailureListener.ts +34 -0
- package/src/react/useMutators.ts +184 -0
- package/src/react/useSyncStatus.ts +42 -0
- package/src/react/useUndoScope.ts +143 -0
- package/src/react.ts +69 -0
- package/src/reactRuntime.ts +10 -0
- package/src/surface.ts +106 -0
- package/src/useReactive.ts +51 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs in-memory queries over a set of models — filtering by predicate,
|
|
3
|
+
* sorting, paginating, and caching the results. A caller hands
|
|
4
|
+
* {@link QueryProcessor} the candidate models and the query options, and it
|
|
5
|
+
* returns a {@link QueryResult}. It keeps two caches: a string-keyed cache for
|
|
6
|
+
* plain queries, and a structural-identity cache for predicate queries, whose
|
|
7
|
+
* closures cannot be turned into a stable string key.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { Model } from '../Model.js';
|
|
11
|
+
import type { ModelScope } from '../InstanceCache.js';
|
|
12
|
+
|
|
13
|
+
export interface QueryOptions<T extends Model> {
|
|
14
|
+
predicate?: (model: T) => boolean;
|
|
15
|
+
/** Stable key to distinguish different predicates for the same model type.
|
|
16
|
+
* Required when multiple predicate queries exist for the same model — without this,
|
|
17
|
+
* they share a cache key and thrash each other's cached result every render. */
|
|
18
|
+
predicateKey?: string;
|
|
19
|
+
scope?: ModelScope;
|
|
20
|
+
orderBy?: keyof T;
|
|
21
|
+
order?: 'asc' | 'desc';
|
|
22
|
+
limit?: number;
|
|
23
|
+
offset?: number;
|
|
24
|
+
skipCache?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface QueryResult<T extends Model> {
|
|
28
|
+
data: T[];
|
|
29
|
+
total: number;
|
|
30
|
+
hasMore: boolean;
|
|
31
|
+
fromCache?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Simple cache interface for query results
|
|
36
|
+
*/
|
|
37
|
+
interface QueryCache {
|
|
38
|
+
get<T>(key: string): T | undefined;
|
|
39
|
+
set(key: string, data: unknown): void;
|
|
40
|
+
invalidate(pattern?: string): void;
|
|
41
|
+
clear(): void;
|
|
42
|
+
/** Number of entries currently cached. */
|
|
43
|
+
readonly size: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The default in-memory cache. It keeps a reverse index from model type to the
|
|
48
|
+
* cache keys for that type, so invalidating one model type costs work
|
|
49
|
+
* proportional to that type's keys rather than a scan of the whole cache. A
|
|
50
|
+
* regex fallback still covers the rare pattern that is not a plain model-type
|
|
51
|
+
* match.
|
|
52
|
+
*/
|
|
53
|
+
class BasicQueryCache implements QueryCache {
|
|
54
|
+
private cache = new Map<string, unknown>();
|
|
55
|
+
// Reverse index: model type -> set of cache keys for that type
|
|
56
|
+
private modelTypeIndex = new Map<string, Set<string>>();
|
|
57
|
+
|
|
58
|
+
get<T>(key: string): T | undefined {
|
|
59
|
+
return this.cache.get(key) as T | undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get size(): number {
|
|
63
|
+
return this.cache.size;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
set(key: string, data: unknown): void {
|
|
67
|
+
this.cache.set(key, data);
|
|
68
|
+
|
|
69
|
+
// Extract model type from cache key (format: "operation:ModelType:options")
|
|
70
|
+
const modelType = this.extractModelType(key);
|
|
71
|
+
if (modelType) {
|
|
72
|
+
if (!this.modelTypeIndex.has(modelType)) {
|
|
73
|
+
this.modelTypeIndex.set(modelType, new Set());
|
|
74
|
+
}
|
|
75
|
+
this.modelTypeIndex.get(modelType)!.add(key);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Optimized invalidation - O(k) where k = keys for model type
|
|
81
|
+
* Supports both exact model type names and regex patterns (fallback)
|
|
82
|
+
*/
|
|
83
|
+
invalidate(pattern?: string): void {
|
|
84
|
+
if (!pattern) {
|
|
85
|
+
this.cache.clear();
|
|
86
|
+
this.modelTypeIndex.clear();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Fast path: Check if pattern is a simple model type match like ".*ModelType.*"
|
|
91
|
+
const modelType = /^\.\*(\w+)\.\*$/.exec(pattern)?.[1];
|
|
92
|
+
if (modelType !== undefined) {
|
|
93
|
+
const keysToDelete = this.modelTypeIndex.get(modelType);
|
|
94
|
+
if (keysToDelete) {
|
|
95
|
+
for (const key of keysToDelete) {
|
|
96
|
+
this.cache.delete(key);
|
|
97
|
+
}
|
|
98
|
+
this.modelTypeIndex.delete(modelType);
|
|
99
|
+
}
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Slow path fallback: regex matching for complex patterns
|
|
104
|
+
// This should rarely be needed with proper model type patterns
|
|
105
|
+
const regex = new RegExp(pattern);
|
|
106
|
+
const keysToDelete: string[] = [];
|
|
107
|
+
|
|
108
|
+
for (const key of this.cache.keys()) {
|
|
109
|
+
if (regex.test(key)) {
|
|
110
|
+
keysToDelete.push(key);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Batch delete to avoid iterator invalidation
|
|
115
|
+
for (const key of keysToDelete) {
|
|
116
|
+
this.cache.delete(key);
|
|
117
|
+
// Clean up index
|
|
118
|
+
const modelType = this.extractModelType(key);
|
|
119
|
+
if (modelType) {
|
|
120
|
+
this.modelTypeIndex.get(modelType)?.delete(key);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
clear(): void {
|
|
126
|
+
this.cache.clear();
|
|
127
|
+
this.modelTypeIndex.clear();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Extract model type from cache key
|
|
132
|
+
* Cache key format: "operation:ModelType:options"
|
|
133
|
+
*/
|
|
134
|
+
private extractModelType(key: string): string | null {
|
|
135
|
+
// A missing second segment reads as undefined, so `?? null` yields null.
|
|
136
|
+
return key.split(':')[1] ?? null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export class QueryProcessor {
|
|
141
|
+
private cache: QueryCache;
|
|
142
|
+
private enableCache: boolean;
|
|
143
|
+
|
|
144
|
+
// Stable-reference cache for predicate queries. A string cache key cannot
|
|
145
|
+
// capture a closure, so predicate queries use a separate identity-based
|
|
146
|
+
// cache keyed on the deterministic part of the query (model name plus
|
|
147
|
+
// serializable options). Each entry stores the previous result and a
|
|
148
|
+
// fingerprint of its model ids; when the ids are unchanged, the processor
|
|
149
|
+
// returns the previous array reference so observers do not re-render.
|
|
150
|
+
private predicateResultCache = new Map<string, { ids: string; result: QueryResult<Model> }>();
|
|
151
|
+
|
|
152
|
+
constructor(config: { enableCache?: boolean } = {}) {
|
|
153
|
+
this.enableCache = config.enableCache ?? true;
|
|
154
|
+
this.cache = new BasicQueryCache();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Process query with filtering, sorting, and pagination
|
|
159
|
+
*/
|
|
160
|
+
processQuery<T extends Model>(
|
|
161
|
+
models: T[],
|
|
162
|
+
modelName: string,
|
|
163
|
+
options: QueryOptions<T> = {}
|
|
164
|
+
): QueryResult<T> {
|
|
165
|
+
// Generate cache key
|
|
166
|
+
const cacheKey = this.generateCacheKey('query', modelName, options);
|
|
167
|
+
|
|
168
|
+
// Check string-based cache (non-predicate queries only)
|
|
169
|
+
if (!options.predicate && !options.skipCache && this.enableCache) {
|
|
170
|
+
const cached = this.cache.get<QueryResult<T>>(cacheKey);
|
|
171
|
+
if (cached) {
|
|
172
|
+
return { ...cached, fromCache: true };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Apply predicate filter
|
|
177
|
+
let filtered = options.predicate ? models.filter(options.predicate) : models;
|
|
178
|
+
|
|
179
|
+
// Sort models
|
|
180
|
+
if (options.orderBy) {
|
|
181
|
+
filtered = this.sortModels(filtered, options.orderBy, options.order);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const total = filtered.length;
|
|
185
|
+
|
|
186
|
+
// Apply pagination
|
|
187
|
+
const offset = options.offset || 0;
|
|
188
|
+
const limit = options.limit || filtered.length;
|
|
189
|
+
const data = filtered.slice(offset, offset + limit);
|
|
190
|
+
|
|
191
|
+
const result: QueryResult<T> = {
|
|
192
|
+
data,
|
|
193
|
+
total,
|
|
194
|
+
hasMore: offset + limit < total,
|
|
195
|
+
fromCache: false,
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// For predicate queries: compare by structural identity. Return the
|
|
199
|
+
// previous array reference when the model ids are unchanged, so observers
|
|
200
|
+
// don't re-render on a structurally identical result.
|
|
201
|
+
if (options.predicate && this.enableCache) {
|
|
202
|
+
const ids = data.map((m) => m.id).join(',');
|
|
203
|
+
const cached = this.predicateResultCache.get(cacheKey);
|
|
204
|
+
|
|
205
|
+
if (cached && cached.ids === ids) {
|
|
206
|
+
// Structural match — return previous reference for stability
|
|
207
|
+
return { ...(cached.result as QueryResult<T>), fromCache: true };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// New result — store for future comparison
|
|
211
|
+
this.predicateResultCache.set(cacheKey, { ids, result });
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// For non-predicate queries: use the string-based cache.
|
|
215
|
+
if (!options.predicate && this.enableCache) {
|
|
216
|
+
this.cache.set(cacheKey, result);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return result;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Sort models by field
|
|
224
|
+
*/
|
|
225
|
+
private sortModels<T extends Model>(
|
|
226
|
+
models: T[],
|
|
227
|
+
field: keyof T,
|
|
228
|
+
order: 'asc' | 'desc' = 'asc'
|
|
229
|
+
): T[] {
|
|
230
|
+
return [...models].sort((a, b) => {
|
|
231
|
+
const aVal = a[field];
|
|
232
|
+
const bVal = b[field];
|
|
233
|
+
|
|
234
|
+
if (aVal === bVal) return 0;
|
|
235
|
+
if (aVal === null || aVal === undefined) return 1;
|
|
236
|
+
if (bVal === null || bVal === undefined) return -1;
|
|
237
|
+
|
|
238
|
+
const comparison = aVal < bVal ? -1 : 1;
|
|
239
|
+
return order === 'asc' ? comparison : -comparison;
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Find first matching model with predicate
|
|
245
|
+
*/
|
|
246
|
+
findFirst<T extends Model>(
|
|
247
|
+
models: Generator<T, void, unknown>,
|
|
248
|
+
predicate: (model: T) => boolean
|
|
249
|
+
): T | undefined {
|
|
250
|
+
for (const model of models) {
|
|
251
|
+
if (predicate(model)) {
|
|
252
|
+
return model;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return undefined;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Count models with optional predicate
|
|
260
|
+
*/
|
|
261
|
+
countModels<T extends Model>(
|
|
262
|
+
models: Generator<T, void, unknown>,
|
|
263
|
+
predicate?: (model: T) => boolean
|
|
264
|
+
): number {
|
|
265
|
+
let count = 0;
|
|
266
|
+
|
|
267
|
+
for (const model of models) {
|
|
268
|
+
if (!predicate || predicate(model)) {
|
|
269
|
+
count++;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return count;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Generate a deterministic cache key from query parameters.
|
|
278
|
+
* Functions (predicates) are excluded from the key — predicate queries use
|
|
279
|
+
* the structural identity cache (predicateResultCache) instead of the
|
|
280
|
+
* string-based cache.
|
|
281
|
+
*/
|
|
282
|
+
private generateCacheKey<T extends Model>(
|
|
283
|
+
operation: string,
|
|
284
|
+
modelName: string,
|
|
285
|
+
options: QueryOptions<T>
|
|
286
|
+
): string {
|
|
287
|
+
const serializableOptions: Record<string, unknown> = {};
|
|
288
|
+
let hasPredicate = false;
|
|
289
|
+
|
|
290
|
+
for (const [key, value] of Object.entries(options)) {
|
|
291
|
+
if (typeof value === 'function') {
|
|
292
|
+
hasPredicate = true;
|
|
293
|
+
// Mark that this query has a predicate, but use a stable marker
|
|
294
|
+
// (not Math.random). The actual caching for predicate queries
|
|
295
|
+
// is handled by predicateResultCache using ID comparison.
|
|
296
|
+
serializableOptions[key] = `__predicate__`;
|
|
297
|
+
} else {
|
|
298
|
+
serializableOptions[key] = value;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const sortedOptions = JSON.stringify(
|
|
303
|
+
serializableOptions,
|
|
304
|
+
Object.keys(serializableOptions).sort()
|
|
305
|
+
);
|
|
306
|
+
const key = `${operation}:${modelName}:${sortedOptions}`;
|
|
307
|
+
return hasPredicate ? `pred:${key}` : key;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Invalidate cache by pattern
|
|
312
|
+
*/
|
|
313
|
+
invalidateCache(pattern?: string): void {
|
|
314
|
+
this.cache.invalidate(pattern);
|
|
315
|
+
// Also invalidate predicate result cache for this model type
|
|
316
|
+
if (pattern) {
|
|
317
|
+
const modelType = /^\.\*(\w+)\.\*$/.exec(pattern)?.[1];
|
|
318
|
+
if (modelType !== undefined) {
|
|
319
|
+
for (const key of this.predicateResultCache.keys()) {
|
|
320
|
+
if (key.includes(modelType)) {
|
|
321
|
+
this.predicateResultCache.delete(key);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
this.predicateResultCache.clear();
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Clear all cache
|
|
332
|
+
*/
|
|
333
|
+
clearCache(): void {
|
|
334
|
+
this.cache.clear();
|
|
335
|
+
this.predicateResultCache.clear();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Get cache stats for debugging
|
|
340
|
+
*/
|
|
341
|
+
getCacheStats(): { size: number; enabled: boolean } {
|
|
342
|
+
return {
|
|
343
|
+
size: this.cache.size,
|
|
344
|
+
enabled: this.enableCache,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The HTTP client for the sync query endpoint.
|
|
3
|
+
*
|
|
4
|
+
* {@link postQuery} is a small wrapper over `fetch` that POSTs a
|
|
5
|
+
* {@link QueryBatch} as JSON to `/sync/query`, attaches the bearer credential
|
|
6
|
+
* as an `Authorization` header, and parses the response into a typed
|
|
7
|
+
* {@link QueryBatchResult}. An HTTP failure is not thrown: it is logged, and
|
|
8
|
+
* every query in the batch comes back with an empty result, so a
|
|
9
|
+
* fire-and-forget caller cannot crash on an unhandled rejection.
|
|
10
|
+
*
|
|
11
|
+
* Higher-level query helpers build on this to issue structured queries without
|
|
12
|
+
* repeating the fetch and error-handling boilerplate.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { z } from 'zod';
|
|
16
|
+
import type { QueryBatch, QueryBatchResult } from './types.js';
|
|
17
|
+
import { translateHttpError } from '@abloatai/transaction/errors';
|
|
18
|
+
import { classifyRecovery, type RecoveryClass } from '@abloatai/transaction/errorCodes';
|
|
19
|
+
import { withAuthHeaders, type AuthTokenGetter } from '@abloatai/transaction/auth/credentialSource';
|
|
20
|
+
import { globalRuntime } from '../context.js';
|
|
21
|
+
import type { RuntimeContext } from '../RuntimeContext.js';
|
|
22
|
+
|
|
23
|
+
// ── Response validation ─────────────────────────────────────────────────
|
|
24
|
+
//
|
|
25
|
+
// Each result slot is an array of rows, or an object for a bundled response.
|
|
26
|
+
// A per-query failure on the server surfaces here as an empty array rather
|
|
27
|
+
// than an error, so emptiness alone does not distinguish "no rows" from
|
|
28
|
+
// "the query failed." Parsing through Zod normalizes a `null` slot into an
|
|
29
|
+
// empty array, so callers never receive a raw null.
|
|
30
|
+
const QueryResultSchema = z
|
|
31
|
+
.union([z.array(z.unknown()), z.record(z.string(), z.unknown()), z.null()])
|
|
32
|
+
.transform((val): unknown[] | Record<string, unknown> => {
|
|
33
|
+
if (val === null) return [];
|
|
34
|
+
return val;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const QueryBatchResultSchema = z
|
|
38
|
+
.object({
|
|
39
|
+
results: z.array(QueryResultSchema),
|
|
40
|
+
})
|
|
41
|
+
.loose();
|
|
42
|
+
|
|
43
|
+
export interface PostQueryOptions {
|
|
44
|
+
/**
|
|
45
|
+
* Full base URL of the sync server including the `/api` prefix.
|
|
46
|
+
* The query endpoint is appended as `/sync/query`, so the final
|
|
47
|
+
* request hits `${baseUrl}/sync/query`.
|
|
48
|
+
*/
|
|
49
|
+
baseUrl: string;
|
|
50
|
+
|
|
51
|
+
/** Timeout in ms for the fetch request. Default: 30000. */
|
|
52
|
+
fetchTimeout?: number;
|
|
53
|
+
|
|
54
|
+
/** The owning client's runtime. Defaults to the module-global bridge. */
|
|
55
|
+
runtime?: RuntimeContext;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Live bearer credential getter. Preferred over `capabilityToken` because it
|
|
59
|
+
* is read per request, so token refreshes propagate without reconstructing
|
|
60
|
+
* query helpers.
|
|
61
|
+
*/
|
|
62
|
+
getAuthToken?: AuthTokenGetter;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* A fixed credential string, for callers that hold only a copied token.
|
|
66
|
+
* Prefer `getAuthToken`, which is re-read on each request so a refresh takes
|
|
67
|
+
* effect without rebuilding the client.
|
|
68
|
+
*/
|
|
69
|
+
capabilityToken?: string;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* An optional hook that tries to recover from a rejected credential. When a
|
|
73
|
+
* query comes back with a 401, its {@link RecoveryClass} is passed here: a
|
|
74
|
+
* return of `'retry'` means a fresh credential has been obtained and the
|
|
75
|
+
* request is replayed exactly once, while `'stop'` ends the attempt. Because
|
|
76
|
+
* the replay happens at most once, a wedged credential cannot cause a retry
|
|
77
|
+
* loop. When this hook is absent, a 401 is logged and returns empty results
|
|
78
|
+
* like any other failure.
|
|
79
|
+
*/
|
|
80
|
+
recoverCredential?: (recovery: RecoveryClass) => Promise<'retry' | 'stop'>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Sends a batch of queries to `/sync/query` and returns the parsed
|
|
85
|
+
* {@link QueryBatchResult}. An HTTP failure is not thrown: it is logged, and
|
|
86
|
+
* every query in the batch comes back with an empty result, which keeps a
|
|
87
|
+
* fire-and-forget caller from crashing on an unhandled rejection. A 401 may
|
|
88
|
+
* first be handed to {@link PostQueryOptions.recoverCredential} for a single
|
|
89
|
+
* retry.
|
|
90
|
+
*
|
|
91
|
+
* The response preserves order: `results[i]` corresponds to the query at
|
|
92
|
+
* `queries[i]`, so callers can rely on index alignment to pull typed results
|
|
93
|
+
* out of a multi-query batch.
|
|
94
|
+
*/
|
|
95
|
+
export async function postQuery(
|
|
96
|
+
options: PostQueryOptions,
|
|
97
|
+
batch: QueryBatch,
|
|
98
|
+
): Promise<QueryBatchResult> {
|
|
99
|
+
const url = `${options.baseUrl}/sync/query`;
|
|
100
|
+
const timeout = options.fetchTimeout ?? 30_000;
|
|
101
|
+
const runtime = options.runtime ?? globalRuntime;
|
|
102
|
+
|
|
103
|
+
// At most two attempts: the original request, plus one replay after a
|
|
104
|
+
// successful credential recovery (see `recoverCredential`). A second auth
|
|
105
|
+
// rejection falls through to the log-and-empty path, so a wedged credential
|
|
106
|
+
// can never retry-loop.
|
|
107
|
+
for (let attempt = 0; ; attempt++) {
|
|
108
|
+
// Race the fetch against a timeout so hung requests don't block
|
|
109
|
+
// the calling helper indefinitely. Fresh controller per attempt.
|
|
110
|
+
const controller = new AbortController();
|
|
111
|
+
const timer = setTimeout(() => { controller.abort(); }, timeout);
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
// Recomputed per attempt: `withAuthHeaders` reads the live credential
|
|
115
|
+
// source, so a replay after recovery carries the freshly-minted key.
|
|
116
|
+
const headers = withAuthHeaders(
|
|
117
|
+
options.getAuthToken,
|
|
118
|
+
{ 'Content-Type': 'application/json' },
|
|
119
|
+
options.capabilityToken,
|
|
120
|
+
);
|
|
121
|
+
const response = await fetch(url, {
|
|
122
|
+
method: 'POST',
|
|
123
|
+
headers,
|
|
124
|
+
body: JSON.stringify(batch),
|
|
125
|
+
signal: controller.signal,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
if (!response.ok) {
|
|
129
|
+
// Build the typed AbloError for this HTTP failure (the same
|
|
130
|
+
// code-to-class map the throwing paths use) so the log carries a
|
|
131
|
+
// registry `code` — for example an authentication error with
|
|
132
|
+
// `session_expired` on a 401 — rather than a bare status. This path
|
|
133
|
+
// deliberately does not throw, since a fire-and-forget caller could
|
|
134
|
+
// crash on an unhandled rejection. It returns empty slots while still
|
|
135
|
+
// logging a legible Ablo error.
|
|
136
|
+
let body: unknown = null;
|
|
137
|
+
try {
|
|
138
|
+
body = await response.clone().json();
|
|
139
|
+
} catch {
|
|
140
|
+
// non-JSON error page — translateHttpError falls back to status text
|
|
141
|
+
}
|
|
142
|
+
const err = translateHttpError(response.status, body);
|
|
143
|
+
|
|
144
|
+
// On a 401, hand the failure to the recovery hook once. The recovery
|
|
145
|
+
// class routes the outcome: an expired access credential is re-minted
|
|
146
|
+
// and the request replays; a lost session is terminal and stops here;
|
|
147
|
+
// anything else stops. A bare 401 with no readable code is treated as
|
|
148
|
+
// an expired access credential, so the only terminal path is the
|
|
149
|
+
// re-mint itself coming back empty, never an ambiguous status.
|
|
150
|
+
if (attempt === 0 && response.status === 401 && options.recoverCredential) {
|
|
151
|
+
const recovery: RecoveryClass =
|
|
152
|
+
typeof err.code === 'string'
|
|
153
|
+
? classifyRecovery(err.code)
|
|
154
|
+
: 'access_credential_expiry';
|
|
155
|
+
const outcome = await options.recoverCredential(recovery);
|
|
156
|
+
if (outcome === 'retry') {
|
|
157
|
+
runtime.logger.debug('[postQuery] credential recovered — replaying query once', {
|
|
158
|
+
code: err.code ?? response.status,
|
|
159
|
+
});
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Logged through the level-gated logger, so it honors ABLO_LOG_LEVEL:
|
|
165
|
+
// a `warn` line the app developer can read (the models, the typed
|
|
166
|
+
// message, and a wire `code`), with the forensic detail on a companion
|
|
167
|
+
// `debug` line. The read stays empty until the underlying cause, such
|
|
168
|
+
// as auth or network, is resolved.
|
|
169
|
+
const models = batch.queries.map((q) => q.model).join(', ');
|
|
170
|
+
runtime.logger.warn(
|
|
171
|
+
`Could not load ${models} — ${err.message} (code: ${err.code ?? response.status}). No results were returned.`,
|
|
172
|
+
);
|
|
173
|
+
runtime.logger.debug('[postQuery.error] query http failure', {
|
|
174
|
+
type: err.type,
|
|
175
|
+
code: err.code ?? response.status,
|
|
176
|
+
models,
|
|
177
|
+
message: err.message,
|
|
178
|
+
});
|
|
179
|
+
return { results: batch.queries.map(() => []) };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const raw: unknown = await response.json();
|
|
183
|
+
const parsed = QueryBatchResultSchema.safeParse(raw);
|
|
184
|
+
if (!parsed.success) {
|
|
185
|
+
// A malformed server response isn't something the consumer can act on
|
|
186
|
+
// (server/protocol issue) → debug, gated like everything else.
|
|
187
|
+
runtime.logger.debug('[postQuery.error] malformed response', {
|
|
188
|
+
issues: parsed.error.issues,
|
|
189
|
+
});
|
|
190
|
+
return { results: batch.queries.map(() => []) };
|
|
191
|
+
}
|
|
192
|
+
return parsed.data;
|
|
193
|
+
} finally {
|
|
194
|
+
clearTimeout(timer);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured query types for the `/sync/query` endpoint.
|
|
3
|
+
*
|
|
4
|
+
* A query describes a filtered read in a compact wire format. `where` is a flat
|
|
5
|
+
* list of `[column, operator, value]` conditions combined with AND, and
|
|
6
|
+
* `related` names the schema relations to fetch alongside each row. The server
|
|
7
|
+
* compiles a query against your schema: it reads the model's relation metadata
|
|
8
|
+
* to turn `related: ['blocks']` into the right join, and turns each condition
|
|
9
|
+
* into a WHERE fragment. The protocol carries no model-specific logic, so
|
|
10
|
+
* adding a model or relation is a schema change rather than a server change.
|
|
11
|
+
*
|
|
12
|
+
* The `IN` operator lets you batch a read by any column, including a foreign
|
|
13
|
+
* key — for example, fetching every block whose `sectionId` falls in a set of
|
|
14
|
+
* ids.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// The where grammar describes the *request*, not any local copy of the rows it
|
|
18
|
+
// returns, so it lives in the settlement core (ADR 0016). Re-exported here so
|
|
19
|
+
// the existing `query/types` import path keeps resolving.
|
|
20
|
+
export type {
|
|
21
|
+
WherePrimitive,
|
|
22
|
+
WhereOp,
|
|
23
|
+
WhereClause,
|
|
24
|
+
LoadWhere,
|
|
25
|
+
} from '@abloatai/transaction/resources/where';
|
|
26
|
+
import type { WhereClause } from '@abloatai/transaction/resources/where';
|
|
27
|
+
|
|
28
|
+
/** A single structured fetch request. */
|
|
29
|
+
export interface Query {
|
|
30
|
+
/**
|
|
31
|
+
* Client-facing model name (e.g. "File", "Block", "Message").
|
|
32
|
+
* The server's adapter maps this to the actual database table.
|
|
33
|
+
*/
|
|
34
|
+
model: string;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The conditions to filter by, combined with AND. Empty or omitted returns
|
|
38
|
+
* every row the caller may see; reads are still scoped to the caller's
|
|
39
|
+
* organization on the server. Use `['col', 'IN', values]` to batch a read by
|
|
40
|
+
* any column.
|
|
41
|
+
*/
|
|
42
|
+
where?: readonly WhereClause[];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The relations to fetch with each row, named as they are declared on this
|
|
46
|
+
* model in the schema. The server resolves each name from the schema's
|
|
47
|
+
* relation metadata and joins the related rows in. They come back nested
|
|
48
|
+
* under the relation key:
|
|
49
|
+
*
|
|
50
|
+
* { __typename: 'Section', id: '…', blocks: [{ __typename: 'Block', … }] }
|
|
51
|
+
*/
|
|
52
|
+
related?: readonly string[];
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Row limit. Applied after where + JOIN, before related nesting.
|
|
56
|
+
* Omit for no limit.
|
|
57
|
+
*/
|
|
58
|
+
limit?: number;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Column to order by. For stable pagination. Omit for unordered.
|
|
62
|
+
*/
|
|
63
|
+
orderBy?: string;
|
|
64
|
+
|
|
65
|
+
/** Order direction. Defaults to `'asc'`. */
|
|
66
|
+
order?: 'asc' | 'desc';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Request body for POST /sync/query. */
|
|
70
|
+
export interface QueryBatch {
|
|
71
|
+
/**
|
|
72
|
+
* Batch of queries to execute in one round trip. Results are
|
|
73
|
+
* returned in request order at the same indices. Keep batches
|
|
74
|
+
* small — the server caps at 16 queries per batch by default.
|
|
75
|
+
*/
|
|
76
|
+
queries: Query[];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Response body from POST /sync/query. */
|
|
80
|
+
export interface QueryBatchResult {
|
|
81
|
+
/**
|
|
82
|
+
* The result of each query, in request order: `results[i]` corresponds to
|
|
83
|
+
* `queries[i]`. Each element is an array of rows, or an object bundling
|
|
84
|
+
* several named row collections when the source returns more than one. Every
|
|
85
|
+
* row carries a `__typename` field naming its model, so callers can dispatch
|
|
86
|
+
* on it, along with any requested `related` rows nested under their relation
|
|
87
|
+
* key.
|
|
88
|
+
*
|
|
89
|
+
* A query that fails on the server comes back as an empty array, which is
|
|
90
|
+
* indistinguishable from a query that simply matched nothing — treat an empty
|
|
91
|
+
* result as "no rows", not as proof the query succeeded. The element type is
|
|
92
|
+
* `unknown` because one batch can mix row shapes, so narrow each result
|
|
93
|
+
* before use.
|
|
94
|
+
*/
|
|
95
|
+
results: unknown[];
|
|
96
|
+
/**
|
|
97
|
+
* The server watermark observed after the batch ran. Model reads expose this
|
|
98
|
+
* as `stamp`, and callers thread it into `commits.create({ readAt })` so the
|
|
99
|
+
* server can reject a write built on stale data.
|
|
100
|
+
*/
|
|
101
|
+
lastSyncId?: number;
|
|
102
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@abloatai/transaction/schema/serialize';
|