@abloatai/ablo 0.34.1 → 0.36.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/AGENTS.md +2 -1
- package/CHANGELOG.md +758 -5
- package/README.md +56 -502
- package/bin/ablo.cjs +39 -0
- package/dist/BaseSyncedStore.d.ts +176 -48
- package/dist/BaseSyncedStore.js +346 -214
- package/dist/Database.d.ts +17 -44
- package/dist/Database.js +96 -79
- package/dist/InstanceCache.d.ts +31 -6
- package/dist/InstanceCache.js +65 -30
- package/dist/LazyReferenceCollection.d.ts +3 -3
- package/dist/LazyReferenceCollection.js +4 -4
- package/dist/Model.d.ts +23 -13
- package/dist/Model.js +27 -17
- package/dist/ModelRegistry.d.ts +8 -4
- package/dist/ModelRegistry.js +20 -18
- package/dist/NetworkMonitor.d.ts +3 -1
- package/dist/NetworkMonitor.js +7 -5
- package/dist/{SyncEngineContext.d.ts → RuntimeContext.d.ts} +20 -13
- package/dist/{SyncEngineContext.js → RuntimeContext.js} +11 -12
- package/dist/SyncClient.d.ts +47 -47
- package/dist/SyncClient.js +215 -156
- package/dist/ai-sdk/coordinatedTool.d.ts +2 -2
- package/dist/ai-sdk/coordinatedTool.js +1 -1
- package/dist/ai-sdk/coordinationContext.d.ts +2 -2
- package/dist/ai-sdk/coordinationContext.js +1 -1
- package/dist/ai-sdk/wrap.d.ts +3 -3
- package/dist/ai-sdk/wrap.js +2 -2
- package/dist/auth/index.d.ts +1 -156
- package/dist/auth/index.js +8 -301
- package/dist/client/Ablo.d.ts +42 -287
- package/dist/client/Ablo.js +129 -963
- package/dist/client/abloClient.d.ts +309 -0
- package/dist/client/abloClient.js +13 -0
- package/dist/client/clientPrelude.d.ts +52 -0
- package/dist/client/clientPrelude.js +60 -0
- package/dist/client/consoleLogger.d.ts +2 -2
- package/dist/client/coreClient.d.ts +60 -0
- package/dist/client/coreClient.js +118 -0
- package/dist/client/createInternalComponents.d.ts +8 -4
- package/dist/client/createInternalComponents.js +17 -10
- package/dist/client/createModelProxy.d.ts +98 -373
- package/dist/client/createModelProxy.js +233 -139
- package/dist/client/humans.d.ts +69 -0
- package/dist/client/humans.js +78 -0
- package/dist/client/modelRegistration.d.ts +1 -1
- package/dist/client/modelRegistration.js +9 -9
- package/dist/client/options.d.ts +73 -17
- package/dist/client/reactiveEngine.d.ts +53 -0
- package/dist/client/reactiveEngine.js +688 -0
- package/dist/client/resourceTypes.d.ts +9 -250
- package/dist/client/resourceTypes.js +8 -5
- package/dist/client/schemaConfig.d.ts +4 -4
- package/dist/client/schemaConfig.js +6 -2
- package/dist/client/storeCluster.d.ts +47 -0
- package/dist/client/storeCluster.js +118 -0
- package/dist/client/storeLifecycle.d.ts +61 -0
- package/dist/client/storeLifecycle.js +231 -0
- package/dist/client/validateAbloOptions.d.ts +3 -2
- package/dist/client/validateAbloOptions.js +1 -1
- package/dist/client/wsMutationExecutor.d.ts +3 -3
- package/dist/client/wsMutationExecutor.js +3 -3
- package/dist/context.d.ts +22 -9
- package/dist/context.js +33 -9
- package/dist/coordination/ClaimLog.d.ts +26 -0
- package/dist/coordination/ClaimLog.js +32 -0
- package/dist/coordination/index.d.ts +1 -15
- package/dist/coordination/index.js +8 -31
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.js +2 -2
- package/dist/docs/catalog.d.ts +72 -0
- package/dist/docs/catalog.js +230 -0
- package/dist/docs/index.d.ts +10 -0
- package/dist/docs/index.js +10 -0
- package/dist/environment.d.ts +1 -40
- package/dist/environment.js +8 -37
- package/dist/index.d.ts +44 -36
- package/dist/index.js +30 -22
- package/dist/interfaces/index.d.ts +44 -134
- package/dist/keys/index.d.ts +1 -77
- package/dist/keys/index.js +8 -190
- package/dist/mutators/{RecordingTransaction.d.ts → RecordingMutation.d.ts} +4 -4
- package/dist/mutators/{RecordingTransaction.js → RecordingMutation.js} +2 -2
- package/dist/mutators/Transaction.d.ts +1 -1
- package/dist/mutators/Transaction.js +1 -1
- package/dist/mutators/UndoManager.d.ts +6 -6
- package/dist/mutators/UndoManager.js +5 -5
- package/dist/mutators/defineMutators.d.ts +3 -3
- package/dist/mutators/defineMutators.js +1 -1
- package/dist/mutators/inverseOp.js +2 -2
- package/dist/mutators/mutateActions.d.ts +3 -3
- package/dist/mutators/mutateActions.js +1 -1
- package/dist/mutators/readerActions.d.ts +1 -1
- package/dist/mutators/undoApply.d.ts +1 -1
- package/dist/mutators/undoApply.js +1 -1
- package/dist/policy/index.d.ts +2 -2
- package/dist/policy/index.js +1 -1
- package/dist/query/client.d.ts +5 -2
- package/dist/query/client.js +10 -9
- package/dist/query/types.d.ts +6 -41
- package/dist/query/types.js +2 -2
- package/dist/react/AbloProvider.d.ts +18 -8
- package/dist/react/AbloProvider.js +10 -9
- package/dist/react/context.d.ts +3 -3
- package/dist/react/context.js +1 -1
- package/dist/react/createAbloReact.d.ts +56 -0
- package/dist/react/createAbloReact.js +51 -0
- package/dist/react/index.d.ts +6 -5
- package/dist/react/index.js +6 -3
- package/dist/react/internalContext.d.ts +1 -1
- package/dist/react/useAblo.d.ts +12 -5
- package/dist/react/useAblo.js +26 -8
- package/dist/react/useCurrentUserId.js +1 -1
- package/dist/react/useErrorListener.js +1 -1
- package/dist/react/useMutationFailureListener.d.ts +2 -2
- package/dist/react/useMutationFailureListener.js +1 -1
- package/dist/react/useMutators.d.ts +3 -3
- package/dist/react/useMutators.js +3 -3
- package/dist/react/useUndoScope.d.ts +5 -5
- package/dist/react/useUndoScope.js +1 -1
- package/dist/schema/coordination.d.ts +69 -10
- package/dist/schema/coordination.js +90 -9
- package/dist/schema/ddl.js +2 -2
- package/dist/schema/diff.d.ts +1 -1
- package/dist/schema/generate.js +1 -1
- package/dist/schema/index.d.ts +11 -10
- package/dist/schema/index.js +22 -18
- package/dist/schema/queries.d.ts +27 -27
- package/dist/schema/queries.js +23 -23
- package/dist/schema/select.d.ts +3 -3
- package/dist/schema/select.js +6 -3
- package/dist/schema/serialize.d.ts +15 -6
- package/dist/schema/serialize.js +20 -3
- package/dist/schema/sugar.d.ts +6 -7
- package/dist/schema/sugar.js +9 -12
- package/dist/schema/syncDeltaRow.d.ts +4 -152
- package/dist/schema/syncDeltaRow.js +4 -105
- package/dist/server/adapter.d.ts +18 -1
- package/dist/server/commit.d.ts +10 -16
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/readConfig.d.ts +1 -1
- package/dist/source/adapter.d.ts +7 -5
- package/dist/source/adapter.js +7 -5
- package/dist/source/adapters/drizzle.d.ts +1 -1
- package/dist/source/adapters/drizzle.js +2 -2
- package/dist/source/adapters/kysely.d.ts +1 -1
- package/dist/source/adapters/kysely.js +1 -1
- package/dist/source/adapters/kyselyMutationCore.d.ts +1 -1
- package/dist/source/adapters/kyselyMutationCore.js +2 -2
- package/dist/source/adapters/memory.js +1 -1
- package/dist/source/adapters/prisma.d.ts +8 -3
- package/dist/source/adapters/prisma.js +1 -1
- package/dist/source/connector.js +1 -1
- package/dist/source/connectorProtocol.d.ts +2 -8
- package/dist/source/connectorProtocol.js +3 -2
- package/dist/source/contract.d.ts +29 -17
- package/dist/source/contract.js +27 -22
- package/dist/source/factory.d.ts +1 -1
- package/dist/source/idempotency.js +2 -2
- package/dist/source/index.d.ts +1 -0
- package/dist/source/index.js +3 -0
- package/dist/source/next.d.ts +1 -1
- package/dist/source/signing.d.ts +9 -2
- package/dist/source/signing.js +4 -1
- package/dist/source/types.d.ts +6 -4
- package/dist/source/types.js +1 -1
- package/dist/{core/storeContract.d.ts → storeContract.d.ts} +6 -6
- package/dist/{core → stores}/DatabaseManager.d.ts +3 -1
- package/dist/{core → stores}/DatabaseManager.js +14 -13
- package/dist/stores/ObjectStore.d.ts +1 -1
- package/dist/{core → stores}/StoreManager.d.ts +9 -26
- package/dist/{core → stores}/StoreManager.js +29 -77
- package/dist/stores/SyncActionStore.d.ts +4 -2
- package/dist/stores/SyncActionStore.js +11 -17
- package/dist/stores/syncAction.d.ts +26 -0
- package/dist/stores/syncAction.js +16 -0
- package/dist/surface.d.ts +3 -3
- package/dist/surface.js +6 -4
- package/dist/sync/BootstrapFetcher.d.ts +127 -6
- package/dist/sync/BootstrapFetcher.js +511 -83
- package/dist/sync/ConnectionManager.d.ts +6 -198
- package/dist/sync/ConnectionManager.js +6 -677
- package/dist/sync/OnDemandLoader.d.ts +5 -2
- package/dist/sync/OnDemandLoader.js +61 -21
- package/dist/sync/SubscriptionManager.d.ts +13 -2
- package/dist/sync/SubscriptionManager.js +23 -5
- package/dist/sync/SyncWebSocket.d.ts +27 -510
- package/dist/sync/SyncWebSocket.js +76 -954
- package/dist/sync/awaitClaimGrant.d.ts +4 -44
- package/dist/sync/awaitClaimGrant.js +4 -109
- package/dist/sync/bootstrapApply.d.ts +3 -0
- package/dist/sync/bootstrapApply.js +2 -2
- package/dist/sync/commitFrames.d.ts +6 -40
- package/dist/sync/commitFrames.js +6 -97
- package/dist/sync/contextPorts.d.ts +18 -0
- package/dist/sync/contextPorts.js +31 -0
- package/dist/sync/createClaimStream.d.ts +5 -49
- package/dist/sync/createClaimStream.js +5 -469
- package/dist/sync/createPresenceStream.d.ts +26 -4
- package/dist/sync/createPresenceStream.js +28 -20
- package/dist/sync/createSnapshot.d.ts +2 -2
- package/dist/sync/createSnapshot.js +1 -1
- package/dist/sync/credentialLifecycle.d.ts +5 -173
- package/dist/sync/credentialLifecycle.js +5 -320
- package/dist/sync/deltaPipeline.d.ts +13 -12
- package/dist/sync/deltaPipeline.js +21 -4
- package/dist/sync/groupChange.d.ts +3 -0
- package/dist/sync/groupChange.js +16 -14
- package/dist/sync/participants.d.ts +24 -6
- package/dist/sync/participants.js +32 -23
- package/dist/sync/schemaDrift.d.ts +55 -0
- package/dist/sync/schemaDrift.js +53 -0
- package/dist/sync/schemas.d.ts +23 -33
- package/dist/sync/schemas.js +29 -20
- package/dist/sync/syncPlan.d.ts +3 -3
- package/dist/sync/wsFrameHandlers.d.ts +6 -114
- package/dist/sync/wsFrameHandlers.js +6 -392
- package/dist/syncLog/contract.d.ts +20 -0
- package/dist/syncLog/contract.js +19 -0
- package/dist/syncLog/index.d.ts +1 -0
- package/dist/syncLog/index.js +1 -0
- package/dist/transaction/ablo.d.ts +88 -0
- package/dist/transaction/ablo.js +33 -0
- package/dist/{client/auth.d.ts → transaction/auth/apiKey.d.ts} +43 -8
- package/dist/{client/auth.js → transaction/auth/apiKey.js} +19 -0
- package/dist/transaction/auth/bootstrapScope.d.ts +15 -0
- package/dist/transaction/auth/bootstrapScope.js +1 -0
- package/dist/transaction/auth/capability.d.ts +212 -0
- package/dist/transaction/auth/capability.js +224 -0
- package/dist/{auth → transaction/auth}/credentialSource.d.ts +8 -1
- package/dist/{client → transaction/auth}/identity.d.ts +8 -7
- package/dist/{client → transaction/auth}/identity.js +1 -1
- package/dist/transaction/auth/index.d.ts +162 -0
- package/dist/transaction/auth/index.js +304 -0
- package/dist/{auth → transaction/auth}/schemas.d.ts +1 -1
- package/dist/{auth → transaction/auth}/schemas.js +13 -13
- package/dist/{client → transaction/auth}/sessionMint.d.ts +8 -8
- package/dist/{client → transaction/auth}/sessionMint.js +4 -7
- package/dist/transaction/coordination/awaitClaimGrant.d.ts +56 -0
- package/dist/transaction/coordination/awaitClaimGrant.js +124 -0
- package/dist/{client → transaction/coordination}/claimHeartbeatLoop.d.ts +34 -0
- package/dist/{client → transaction/coordination}/claimHeartbeatLoop.js +20 -0
- package/dist/transaction/coordination/claimMeta.d.ts +49 -0
- package/dist/transaction/coordination/claimMeta.js +52 -0
- package/dist/transaction/coordination/createClaimStream.d.ts +64 -0
- package/dist/transaction/coordination/createClaimStream.js +475 -0
- package/dist/transaction/coordination/events.d.ts +74 -0
- package/dist/transaction/coordination/events.js +7 -0
- package/dist/transaction/coordination/index.d.ts +19 -0
- package/dist/transaction/coordination/index.js +45 -0
- package/dist/transaction/coordination/locator.d.ts +104 -0
- package/dist/transaction/coordination/locator.js +102 -0
- package/dist/transaction/coordination/schema.d.ts +1536 -0
- package/dist/transaction/coordination/schema.js +1177 -0
- package/dist/transaction/coordination/targetConflict.d.ts +2 -0
- package/dist/transaction/coordination/targetConflict.js +107 -0
- package/dist/{coordination → transaction/coordination}/trace.d.ts +7 -18
- package/dist/{coordination → transaction/coordination}/trace.js +18 -25
- package/dist/transaction/durableWrites.d.ts +62 -0
- package/dist/{client → transaction}/durableWrites.js +28 -3
- package/dist/transaction/environment.d.ts +105 -0
- package/dist/transaction/environment.js +108 -0
- package/dist/{errorCodes.d.ts → transaction/errorCodes.d.ts} +12 -12
- package/dist/{errorCodes.js → transaction/errorCodes.js} +45 -18
- package/dist/{errors.d.ts → transaction/errors.d.ts} +37 -13
- package/dist/{errors.js → transaction/errors.js} +85 -16
- package/dist/transaction/footprint.d.ts +111 -0
- package/dist/transaction/footprint.js +0 -0
- package/dist/transaction/index.d.ts +20 -0
- package/dist/transaction/index.js +20 -0
- package/dist/transaction/keys/index.d.ts +87 -0
- package/dist/transaction/keys/index.js +207 -0
- package/dist/transaction/log/syncDeltaRow.d.ts +158 -0
- package/dist/transaction/log/syncDeltaRow.js +95 -0
- package/dist/{sync/syncPosition.d.ts → transaction/logPosition.d.ts} +22 -8
- package/dist/{sync/syncPosition.js → transaction/logPosition.js} +15 -6
- package/dist/transaction/logger.d.ts +16 -0
- package/dist/transaction/logger.js +7 -0
- package/dist/transaction/observability.d.ts +53 -0
- package/dist/transaction/observability.js +19 -0
- package/dist/transaction/plugin.d.ts +285 -0
- package/dist/transaction/plugin.js +106 -0
- package/dist/{policy → transaction/policy}/types.d.ts +3 -3
- package/dist/{policy → transaction/policy}/types.js +2 -0
- package/dist/transaction/resources/httpResources.d.ts +321 -0
- package/dist/transaction/resources/httpResources.js +7 -0
- package/dist/transaction/resources/modelOperations.d.ts +427 -0
- package/dist/transaction/resources/modelOperations.js +12 -0
- package/dist/transaction/resources/mutationOptions.d.ts +66 -0
- package/dist/transaction/resources/mutationOptions.js +9 -0
- package/dist/transaction/resources/where.d.ts +101 -0
- package/dist/transaction/resources/where.js +115 -0
- package/dist/{client → transaction/resources}/writeOptionsSchema.d.ts +2 -6
- package/dist/{client → transaction/resources}/writeOptionsSchema.js +9 -5
- package/dist/{schema → transaction/schema}/field.d.ts +17 -23
- package/dist/{schema → transaction/schema}/field.js +5 -5
- package/dist/transaction/schema/fieldRef.d.ts +38 -0
- package/dist/transaction/schema/fieldRef.js +11 -0
- package/dist/transaction/schema/loadStrategy.d.ts +45 -0
- package/dist/transaction/schema/loadStrategy.js +46 -0
- package/dist/{schema → transaction/schema}/model.d.ts +50 -35
- package/dist/{schema → transaction/schema}/model.js +30 -20
- package/dist/transaction/schema/openapi.d.ts +58 -0
- package/dist/transaction/schema/openapi.js +501 -0
- package/dist/{schema → transaction/schema}/relation.d.ts +21 -16
- package/dist/{schema → transaction/schema}/relation.js +7 -7
- package/dist/{schema → transaction/schema}/residency.d.ts +0 -9
- package/dist/{schema → transaction/schema}/residency.js +0 -5
- package/dist/{schema → transaction/schema}/roles.d.ts +5 -5
- package/dist/{schema → transaction/schema}/roles.js +5 -5
- package/dist/{schema → transaction/schema}/schema.d.ts +39 -10
- package/dist/{schema → transaction/schema}/schema.js +24 -3
- package/dist/{schema → transaction/schema}/tenancy.d.ts +1 -1
- package/dist/{schema → transaction/schema}/tenancy.js +7 -4
- package/dist/transaction/transactionLayer.d.ts +82 -0
- package/dist/transaction/transactionLayer.js +24 -0
- package/dist/{transactions → transaction/transactions/settlement}/commitEnvelope.d.ts +5 -6
- package/dist/{transactions → transaction/transactions/settlement}/commitEnvelope.js +9 -13
- package/dist/{transactions → transaction/transactions/settlement}/httpCommitEnvelope.d.ts +9 -2
- package/dist/{transactions → transaction/transactions/settlement}/httpCommitEnvelope.js +5 -9
- package/dist/{transactions/durableWriteStore.d.ts → transaction/transactions/settlement/pendingWrite.d.ts} +11 -37
- package/dist/transaction/transactions/settlement/pendingWrite.js +20 -0
- package/dist/transaction/transport/commitFrames.d.ts +90 -0
- package/dist/transaction/transport/commitFrames.js +134 -0
- package/dist/transaction/transport/connectionManager.d.ts +215 -0
- package/dist/transaction/transport/connectionManager.js +673 -0
- package/dist/transaction/transport/credentialLifecycle.d.ts +177 -0
- package/dist/transaction/transport/credentialLifecycle.js +324 -0
- package/dist/{sync → transaction/transport}/heartbeat.d.ts +3 -1
- package/dist/{sync → transaction/transport}/heartbeat.js +6 -4
- package/dist/transaction/transport/httpClient.d.ts +131 -0
- package/dist/{client → transaction/transport}/httpClient.js +6 -5
- package/dist/transaction/transport/httpOptions.d.ts +33 -0
- package/dist/transaction/transport/httpOptions.js +12 -0
- package/dist/{client → transaction/transport}/httpTransport.js +295 -97
- package/dist/{sync/NetworkProbe.d.ts → transaction/transport/networkProbe.d.ts} +7 -4
- package/dist/{sync/NetworkProbe.js → transaction/transport/networkProbe.js} +14 -13
- package/dist/transaction/transport/wsFrameHandlers.d.ts +128 -0
- package/dist/transaction/transport/wsFrameHandlers.js +429 -0
- package/dist/transaction/transport/wsTransport.d.ts +574 -0
- package/dist/transaction/transport/wsTransport.js +1023 -0
- package/dist/transaction/types/assertExact.d.ts +17 -0
- package/dist/transaction/types/assertExact.js +1 -0
- package/dist/{types → transaction/types}/global.d.ts +17 -2
- package/dist/{types → transaction/types}/global.js +2 -1
- package/dist/{types → transaction/types}/index.d.ts +14 -46
- package/dist/{types → transaction/types}/index.js +7 -16
- package/dist/{types → transaction/types}/streams.d.ts +73 -45
- package/dist/transaction/utils/duration.d.ts +50 -0
- package/dist/{utils → transaction/utils}/duration.js +32 -0
- package/dist/{utils → transaction/utils}/json.d.ts +18 -0
- package/dist/transaction/utils/json.js +276 -0
- package/dist/transaction/wire/accountResponses.d.ts +420 -0
- package/dist/transaction/wire/accountResponses.js +290 -0
- package/dist/transaction/wire/auth.d.ts +56 -0
- package/dist/transaction/wire/auth.js +63 -0
- package/dist/transaction/wire/claimEvent.d.ts +76 -0
- package/dist/transaction/wire/claimEvent.js +73 -0
- package/dist/transaction/wire/claims.d.ts +530 -0
- package/dist/transaction/wire/claims.js +327 -0
- package/dist/{wire → transaction/wire}/commit.d.ts +125 -193
- package/dist/{wire → transaction/wire}/commit.js +68 -47
- package/dist/{wire → transaction/wire}/delta.d.ts +66 -17
- package/dist/{wire → transaction/wire}/delta.js +37 -13
- package/dist/transaction/wire/errorEnvelope.d.ts +72 -0
- package/dist/{wire → transaction/wire}/errorEnvelope.js +36 -5
- package/dist/transaction/wire/feedCursor.d.ts +60 -0
- package/dist/transaction/wire/feedCursor.js +82 -0
- package/dist/transaction/wire/feedEvent.d.ts +204 -0
- package/dist/transaction/wire/feedEvent.js +65 -0
- package/dist/transaction/wire/frames.d.ts +194 -0
- package/dist/transaction/wire/frames.js +50 -0
- package/dist/transaction/wire/inboundFrames.d.ts +562 -0
- package/dist/transaction/wire/inboundFrames.js +116 -0
- package/dist/transaction/wire/index.d.ts +54 -0
- package/dist/transaction/wire/index.js +83 -0
- package/dist/{wire → transaction/wire}/listEnvelope.d.ts +13 -14
- package/dist/transaction/wire/listEnvelope.js +42 -0
- package/dist/transaction/wire/modelMutations.d.ts +31 -0
- package/dist/transaction/wire/modelMutations.js +52 -0
- package/dist/transaction/wire/modelResponses.d.ts +85 -0
- package/dist/transaction/wire/modelResponses.js +43 -0
- package/dist/transaction/wire/modelShape.d.ts +78 -0
- package/dist/transaction/wire/modelShape.js +74 -0
- package/dist/transactions/{TransactionQueue.d.ts → mutations/MutationQueue.d.ts} +85 -38
- package/dist/transactions/{TransactionQueue.js → mutations/MutationQueue.js} +141 -80
- package/dist/transactions/{TransactionStore.d.ts → mutations/MutationStore.d.ts} +8 -8
- package/dist/transactions/{TransactionStore.js → mutations/MutationStore.js} +2 -2
- package/dist/transactions/{coalesceRules.d.ts → mutations/coalesceRules.d.ts} +10 -10
- package/dist/transactions/{coalesceRules.js → mutations/coalesceRules.js} +1 -1
- package/dist/transactions/mutations/commitLatency.d.ts +52 -0
- package/dist/transactions/mutations/commitLatency.js +130 -0
- package/dist/transactions/{commitOutboxStore.d.ts → mutations/commitOutboxStore.d.ts} +1 -5
- package/dist/transactions/{commitOutboxStore.js → mutations/commitOutboxStore.js} +1 -1
- package/dist/transactions/{commitPayload.d.ts → mutations/commitPayload.d.ts} +18 -16
- package/dist/transactions/{commitPayload.js → mutations/commitPayload.js} +15 -15
- package/dist/transactions/{deltaConfirmation.d.ts → mutations/deltaConfirmation.d.ts} +15 -11
- package/dist/transactions/{deltaConfirmation.js → mutations/deltaConfirmation.js} +14 -12
- package/dist/transactions/mutations/durableWriteStore.d.ts +14 -0
- package/dist/transactions/mutations/durableWriteStore.js +12 -0
- package/dist/transactions/{optimisticApply.d.ts → mutations/optimisticApply.d.ts} +7 -7
- package/dist/transactions/{replayValidation.d.ts → mutations/replayValidation.d.ts} +4 -3
- package/dist/transactions/{replayValidation.js → mutations/replayValidation.js} +7 -5
- package/dist/utils/mobxSetup.d.ts +1 -1
- package/dist/utils/mobxSetup.js +5 -2
- package/dist/{core → views}/QueryView.d.ts +2 -2
- package/dist/{core → views}/QueryView.js +2 -2
- package/dist/{core → views}/ViewRegistry.d.ts +1 -1
- package/dist/{core/queryUtils.d.ts → views/incrementalView.d.ts} +6 -6
- package/dist/{core/queryUtils.js → views/incrementalView.js} +6 -6
- package/dist/webhooks/events.d.ts +2 -2
- package/dist/wire/index.d.ts +1 -34
- package/dist/wire/index.js +8 -49
- package/docs/agent-messaging.md +3 -3
- package/docs/agents.md +20 -13
- package/docs/api-keys.md +14 -10
- package/docs/api.md +27 -61
- package/docs/audit.md +6 -3
- package/docs/cli.md +41 -13
- package/docs/client-behavior.md +11 -9
- package/docs/concurrency-convention.md +49 -57
- package/docs/coordination.md +283 -121
- package/docs/data-sources.md +7 -5
- package/docs/debugging.md +39 -15
- package/docs/deployment.md +267 -0
- package/docs/examples/agent-human.md +49 -42
- package/docs/examples/ai-sdk-tool.md +69 -44
- package/docs/examples/existing-python-backend.md +8 -6
- package/docs/examples/nextjs.md +129 -47
- package/docs/examples/scoped-agent.md +46 -45
- package/docs/examples/server-agent.md +46 -26
- package/docs/groups.md +87 -30
- package/docs/guarantees.md +41 -12
- package/docs/how-it-works.md +38 -12
- package/docs/idempotency.md +126 -0
- package/docs/identity.md +77 -74
- package/docs/index.md +172 -86
- package/docs/integration-guide.md +31 -19
- package/docs/mcp.md +46 -21
- package/docs/migration.md +95 -18
- package/docs/operating-on-your-database.md +3 -1
- package/docs/projects.md +3 -1
- package/docs/quickstart.md +22 -5
- package/docs/react.md +31 -18
- package/docs/schema-contract.md +5 -3
- package/docs/session-settings.md +108 -0
- package/docs/sessions.md +4 -2
- package/docs/webhooks.md +12 -10
- package/llms.txt +48 -18
- package/package.json +21 -26
- package/dist/agent/Agent.d.ts +0 -366
- package/dist/agent/Agent.js +0 -514
- package/dist/agent/index.d.ts +0 -115
- package/dist/agent/index.js +0 -128
- package/dist/agent/session.d.ts +0 -93
- package/dist/agent/session.js +0 -149
- package/dist/agent/types.d.ts +0 -68
- package/dist/agent/types.js +0 -9
- package/dist/cli.cjs +0 -286329
- package/dist/client/durableWrites.d.ts +0 -21
- package/dist/client/httpClient.d.ts +0 -80
- package/dist/coordination/schema.d.ts +0 -722
- package/dist/coordination/schema.js +0 -578
- package/dist/schema/openapi.d.ts +0 -29
- package/dist/schema/openapi.js +0 -124
- package/dist/testing/fixtures/bootstrap.d.ts +0 -49
- package/dist/testing/fixtures/bootstrap.js +0 -59
- package/dist/testing/fixtures/deltas.d.ts +0 -83
- package/dist/testing/fixtures/deltas.js +0 -136
- package/dist/testing/fixtures/models.d.ts +0 -83
- package/dist/testing/fixtures/models.js +0 -272
- package/dist/testing/helpers/reactWrapper.d.ts +0 -69
- package/dist/testing/helpers/reactWrapper.js +0 -67
- package/dist/testing/helpers/syncEngineHarness.d.ts +0 -54
- package/dist/testing/helpers/syncEngineHarness.js +0 -73
- package/dist/testing/helpers/wait.d.ts +0 -30
- package/dist/testing/helpers/wait.js +0 -49
- package/dist/testing/index.d.ts +0 -23
- package/dist/testing/index.js +0 -33
- package/dist/testing/mocks/FakeDatabase.d.ts +0 -18
- package/dist/testing/mocks/FakeDatabase.js +0 -10
- package/dist/testing/mocks/MockMutationExecutor.d.ts +0 -87
- package/dist/testing/mocks/MockMutationExecutor.js +0 -192
- package/dist/testing/mocks/MockNetworkMonitor.d.ts +0 -20
- package/dist/testing/mocks/MockNetworkMonitor.js +0 -46
- package/dist/testing/mocks/MockSyncContext.d.ts +0 -51
- package/dist/testing/mocks/MockSyncContext.js +0 -71
- package/dist/testing/mocks/MockSyncStore.d.ts +0 -88
- package/dist/testing/mocks/MockSyncStore.js +0 -171
- package/dist/testing/mocks/MockWebSocket.d.ts +0 -71
- package/dist/testing/mocks/MockWebSocket.js +0 -118
- package/dist/transactions/durableWriteStore.js +0 -30
- package/dist/utils/duration.d.ts +0 -25
- package/dist/utils/json.js +0 -88
- package/dist/wire/errorEnvelope.d.ts +0 -55
- package/dist/wire/frames.d.ts +0 -197
- package/dist/wire/frames.js +0 -49
- package/dist/wire/listEnvelope.js +0 -18
- package/docs/interaction-model.md +0 -97
- /package/dist/{core → query}/QueryProcessor.d.ts +0 -0
- /package/dist/{core → query}/QueryProcessor.js +0 -0
- /package/dist/{core/storeContract.js → storeContract.js} +0 -0
- /package/dist/{core → stores}/openIDBWithTimeout.d.ts +0 -0
- /package/dist/{core → stores}/openIDBWithTimeout.js +0 -0
- /package/dist/{client → transaction/auth}/credentialEndpoint.d.ts +0 -0
- /package/dist/{client → transaction/auth}/credentialEndpoint.js +0 -0
- /package/dist/{auth → transaction/auth}/credentialPolicy.d.ts +0 -0
- /package/dist/{auth → transaction/auth}/credentialPolicy.js +0 -0
- /package/dist/{auth → transaction/auth}/credentialSource.js +0 -0
- /package/dist/{client → transaction/auth}/hostedEndpoints.d.ts +0 -0
- /package/dist/{client → transaction/auth}/hostedEndpoints.js +0 -0
- /package/dist/{client → transaction}/persistence.d.ts +0 -0
- /package/dist/{client → transaction}/persistence.js +0 -0
- /package/dist/{client → transaction/resources}/functionalUpdate.d.ts +0 -0
- /package/dist/{client → transaction/resources}/functionalUpdate.js +0 -0
- /package/dist/{transactions → transaction/transactions/settlement}/idempotencyKey.d.ts +0 -0
- /package/dist/{transactions → transaction/transactions/settlement}/idempotencyKey.js +0 -0
- /package/dist/{client → transaction/transport}/httpTransport.d.ts +0 -0
- /package/dist/{types → transaction/types}/modelData.d.ts +0 -0
- /package/dist/{types → transaction/types}/modelData.js +0 -0
- /package/dist/{types → transaction/types}/participant.d.ts +0 -0
- /package/dist/{types → transaction/types}/participant.js +0 -0
- /package/dist/{types → transaction/types}/streams.js +0 -0
- /package/dist/{utils → transaction/utils}/asyncIterator.d.ts +0 -0
- /package/dist/{utils → transaction/utils}/asyncIterator.js +0 -0
- /package/dist/{wire → transaction/wire}/bootstrapReason.d.ts +0 -0
- /package/dist/{wire → transaction/wire}/bootstrapReason.js +0 -0
- /package/dist/{wire → transaction/wire}/protocol.d.ts +0 -0
- /package/dist/{wire → transaction/wire}/protocol.js +0 -0
- /package/dist/{wire → transaction/wire}/protocolVersion.d.ts +0 -0
- /package/dist/{wire → transaction/wire}/protocolVersion.js +0 -0
- /package/dist/transactions/{UnconfirmedWrites.d.ts → mutations/UnconfirmedWrites.d.ts} +0 -0
- /package/dist/transactions/{UnconfirmedWrites.js → mutations/UnconfirmedWrites.js} +0 -0
- /package/dist/transactions/{optimisticApply.js → mutations/optimisticApply.js} +0 -0
- /package/dist/{core → views}/ViewRegistry.js +0 -0
|
@@ -37,7 +37,7 @@ import { z } from 'zod';
|
|
|
37
37
|
* error documentation and returned on the `Ablo-Version` response header, so a
|
|
38
38
|
* consumer can detect when its expected contract has drifted from the server's.
|
|
39
39
|
*/
|
|
40
|
-
export const ERROR_CONTRACT_VERSION = '2026-07-
|
|
40
|
+
export const ERROR_CONTRACT_VERSION = '2026-07-23';
|
|
41
41
|
/**
|
|
42
42
|
* A closed classification of how a failure can be recovered from — a level above
|
|
43
43
|
* the raw {@link ErrorCode}. Where a code says what went wrong, a recovery class
|
|
@@ -136,6 +136,7 @@ export const ERROR_CODES = {
|
|
|
136
136
|
database_role_unreadable: wire('permission', 403, false, 'Ablo could not introspect the database role it connects with, so it cannot verify that row-level security is enforced.'),
|
|
137
137
|
database_tables_unforced_rls: wire('permission', 403, false, 'Some synced tables do not have `FORCE ROW LEVEL SECURITY` applied, so the table owner can bypass row isolation. Run `ALTER TABLE ... FORCE ROW LEVEL SECURITY` on each synced table.'),
|
|
138
138
|
database_host_not_allowed: wire('permission', 403, false, "The database host resolves to a private, loopback, or link-local address, which Ablo's servers will not connect to. Use a publicly resolvable host."),
|
|
139
|
+
connected_database_unreachable: wire('tenant', 503, false, 'Ablo could not reach the database connected to this environment: it refused the credentials on file, or did not answer at all. A database often changes after it is connected — a password is rotated, an instance is replaced — while the connection goes on pointing at what used to be there. The error names the host it tried. If the password changed, `ablo connect rotate` re-keys the roles and re-registers them; if the database itself was replaced, `ablo connect apply` sets up the new one and `ablo connect register` hands it over. Run either with a key for this environment.'),
|
|
139
140
|
// Older spellings of the `database_*` codes above, still sent by some servers
|
|
140
141
|
// and kept so they classify identically. Prefer the `database_*` codes.
|
|
141
142
|
byo_role_cannot_enforce_rls: wire('permission', 403, false, 'The direct Postgres connector role cannot enforce row-level security.'),
|
|
@@ -149,11 +150,28 @@ export const ERROR_CODES = {
|
|
|
149
150
|
// only loop. Recovery belongs to the caller: take a claim, which queues fairly
|
|
150
151
|
// behind the holder (`ablo.<model>.claim`), or re-read and rebase.
|
|
151
152
|
claim_conflict: wire('claim', 409, false, 'Another participant holds a claim on this row, so the write was rejected. Take a claim with `ablo.<model>.claim` to queue fairly behind the holder, or re-read and rebase.'),
|
|
152
|
-
claim_lost: wire('claim', 409, false, 'The claim held on this row was lost before the write could apply. Re-acquire
|
|
153
|
+
claim_lost: wire('claim', 409, false, 'The claim held on this row was lost before the write could apply. Re-acquire with `ablo.<model>.claim` and retry from its fresh snapshot.'),
|
|
153
154
|
fence_token_stale: wire('claim', 409, false, 'This write carried a fencing token below the row’s current high-water: a later holder claimed the row, wrote, and moved on while this claim was lapsed, so applying the write would silently overwrite their work. The claim is gone — re-claim the row and retry from the current state.'),
|
|
154
155
|
entity_claimed: wire('claim', 409, false, 'This row is currently claimed by another participant, so the write was blocked. Queue behind the holder with `ablo.<model>.claim`, or wait for the claim to clear.'),
|
|
155
|
-
|
|
156
|
+
// A claim payload that cannot be parsed is a malformed request, not
|
|
157
|
+
// contention — it is filed with `malformed_subscription` below rather than
|
|
158
|
+
// with the 409s above, so the claim category stays purely about a target
|
|
159
|
+
// being held. This is the code for a claim that fails to name its target,
|
|
160
|
+
// over either transport — and for the participant claim behind `join`, which
|
|
161
|
+
// names scopes rather than a row but fails the same way and to the same
|
|
162
|
+
// caller. The copy covers both because the caller sees one word, `claim`, and
|
|
163
|
+
// needs to be told which shape was expected without being handed a lecture on
|
|
164
|
+
// the two frames.
|
|
165
|
+
malformed_claim: wire('validation', 400, false, 'The claim payload could not be parsed. A claim on a row must name the model and the entity it targets; a claim on a scope, which is what `join` opens, must name sync groups spelled `kind:id` or `default`. Check the payload shape and resend.'),
|
|
156
166
|
malformed_subscription: wire('validation', 400, false, 'The `update_subscription` payload was malformed; expected `{ syncGroups: string[] }`.'),
|
|
167
|
+
// The counterpart to the two above, pointing the other way: those are a
|
|
168
|
+
// client sending the server something it cannot read, this is the server
|
|
169
|
+
// sending the client something it cannot read. One code covers both
|
|
170
|
+
// transports — a mis-shaped socket frame and a mis-shaped HTTP body are the
|
|
171
|
+
// same failure, and the message says which arrived. Not retryable: it means
|
|
172
|
+
// the two sides are running different versions of the protocol, and waiting
|
|
173
|
+
// does not change that.
|
|
174
|
+
malformed_response: wire('transport', 502, false, 'The server sent a message this client could not read, so it was declined whole rather than applied in part. Nothing was changed locally. This normally means the client and the server are running different versions; upgrading the client resolves it.'),
|
|
157
175
|
model_claimed: wire('claim', 409, false, 'Another participant holds a claim on this row. Read `claim.state` to see who holds it, or queue behind them with a claim of your own.'),
|
|
158
176
|
model_claimed_timeout: wire('claim', 409, false, 'Another participant held a claim on this row and did not release it in time. Retry, or read `claim.state` to see who holds it.'),
|
|
159
177
|
model_claim_not_configured: client('claim', 'Claiming requires the collaboration runtime, which the standard Ablo({ schema, apiKey }) client wires up for every model automatically — there is no per-model claim configuration to add. This appears only when a model proxy is constructed directly without that runtime (an internal/advanced path).'),
|
|
@@ -163,7 +181,7 @@ export const ERROR_CODES = {
|
|
|
163
181
|
// `readAt`, so resending the identical payload can never succeed. Recovery
|
|
164
182
|
// is a caller-level re-read that produces a NEW request with a fresh
|
|
165
183
|
// watermark — the same shape as `claim_conflict`.
|
|
166
|
-
stale_context: wire('conflict', 409, false, "The row changed after you read it — the write's `readAt` watermark is older than the current row version.
|
|
184
|
+
stale_context: wire('conflict', 409, false, "The row changed after you read it — the write's `readAt` watermark is older than the current row version. Pass a function to `update(id, current => next)` and the SDK re-reads and retries for you; or re-read and retry by hand."),
|
|
167
185
|
// Raised by the functional `update(id, current => next)` form once its
|
|
168
186
|
// internal reconcile budget is exhausted, because the row stayed continuously
|
|
169
187
|
// contended. The SDK has already retried; the caller decides whether to back
|
|
@@ -176,6 +194,7 @@ export const ERROR_CODES = {
|
|
|
176
194
|
idempotency_key_too_long: wire('validation', 400, false, 'The supplied `Idempotency-Key` exceeds the maximum length. Use a shorter key — a UUID works well.'),
|
|
177
195
|
// ── validation (400 / 422) ─────────────────────────────────────────
|
|
178
196
|
write_options_invalid: client('validation', 'The write options (`idempotencyKey` / `label` / `wait` / `readAt` / `onStale` / `claim`) failed validation against the write-options schema.'),
|
|
197
|
+
write_payload_invalid: client('validation', 'A write payload contained a value that cannot be represented safely as JSON. Use plain objects, arrays, finite numbers, strings, booleans, null, or valid dates.'),
|
|
179
198
|
source_operation_id_required: client('validation', 'A data-source operation arrived without the entity `id` it targets.'),
|
|
180
199
|
source_adapter_misconfigured: client('validation', 'The data-source ORM adapter could not map a schema model onto the backing client — the client exposes no matching delegate or model. Check that the adapter and schema agree on model names.'),
|
|
181
200
|
// The server validates every incoming data-source event before appending it
|
|
@@ -186,7 +205,6 @@ export const ERROR_CODES = {
|
|
|
186
205
|
duration_invalid: client('validation', 'A duration value was not a number of seconds or a "500ms" | "30s" | "3m" | "24h" string.'),
|
|
187
206
|
schema_definition_invalid: client('validation', 'A schema definition value was invalid (bad column identifier, non-finite backfill, or unsupported schema-JSON version).'),
|
|
188
207
|
cli_invalid_arguments: client('validation', 'The CLI was invoked with an unknown flag or a malformed flag value.'),
|
|
189
|
-
turn_validation_failed: wire('validation', 422, false, 'The agent turn payload failed server-side validation and was not applied.'),
|
|
190
208
|
commit_operation_required: wire('validation', 400, false, 'A commit must carry `operation` or `operations`.'),
|
|
191
209
|
// Both commit transports — the WebSocket `commit` frame and the HTTP
|
|
192
210
|
// `/v1/commits` endpoint — validate every operation and reject the whole batch
|
|
@@ -247,14 +265,18 @@ export const ERROR_CODES = {
|
|
|
247
265
|
// ── claim / lease (409 / transport) ───────────────────────────────
|
|
248
266
|
claim_lease_unavailable: wire('claim', 503, true, 'The claim-lease coordination subsystem is temporarily unavailable, so the claim could not be processed. Retry shortly.'),
|
|
249
267
|
claim_not_wired: client('claim', 'Claims were used, but this runtime has no claim support wired in. The standard `Ablo({ schema, apiKey })` client wires it up automatically.'),
|
|
250
|
-
claim_queued: wire('claim', 409, true, 'The claim was queued behind the current lease holder and will be granted in turn.
|
|
268
|
+
claim_queued: wire('claim', 409, true, 'The claim was queued behind the current lease holder and will be granted in turn. Poll `claims.retrieve({ claimId })` for the grant — the id rides on the error — or read `claim.queue` to see the line.'),
|
|
251
269
|
claim_wait_aborted: wire('claim', 409, true, 'The wait for this claim lease was aborted before the lease was granted.'),
|
|
252
|
-
|
|
253
|
-
grant_timeout: wire('claim', 504, true, 'The wait for a capability grant timed out before one arrived. Retry the request.'),
|
|
254
|
-
slide_intent_missing_deck_id: wire('claim', 400, false, 'This slide claim is missing the id of the deck it belongs to.'),
|
|
255
|
-
slide_intent_unknown_sibling: wire('claim', 400, false, 'This slide claim references a sibling slide that does not exist in the deck.'),
|
|
270
|
+
grant_timeout: wire('claim', 504, true, 'The wait for the claim grant timed out before your turn arrived (`waitTimeoutMs`). Claim again to rejoin the line, raise the cap, or re-read and proceed without the claim.'),
|
|
256
271
|
// ── bootstrap (transport) ──────────────────────────────────────────
|
|
257
272
|
bootstrap_fetch_timeout: wire('bootstrap', 504, true, 'The initial bootstrap fetch timed out before the server responded. Retry shortly.'),
|
|
273
|
+
// Deliberate cancellation, never a failure of the network. It is what the
|
|
274
|
+
// client hands to `abort()` so the layer above can tell "we stopped this on
|
|
275
|
+
// purpose" apart from "the transfer died" — the two are indistinguishable
|
|
276
|
+
// from a bare abort, and retrying the first re-issues requests that were just
|
|
277
|
+
// killed. Non-retryable by construction: whoever cancelled it either has a
|
|
278
|
+
// newer request in flight or has already given up.
|
|
279
|
+
bootstrap_cancelled: client('bootstrap', 'This bootstrap request was cancelled before it finished, because a newer one replaced it or the bootstrap it belonged to had already failed. It is not retried on its own.'),
|
|
258
280
|
bootstrap_offline: wire('bootstrap', 503, true, 'Bootstrap could not run because the client is offline. It can proceed once the network returns.'),
|
|
259
281
|
bootstrap_offline_no_cache: wire('bootstrap', 503, false, 'The client is offline and no cached snapshot is available to start from, so there is no data to load until the network returns.'),
|
|
260
282
|
bootstrap_response_invalid: wire('bootstrap', 502, true, 'The bootstrap response could not be parsed. Retrying may succeed.'),
|
|
@@ -270,7 +292,7 @@ export const ERROR_CODES = {
|
|
|
270
292
|
commit_failed: wire('transport', 500, true, 'The commit reached the server but failed to apply. Retrying may succeed.'),
|
|
271
293
|
replication_lag_timeout: wire('transport', 504, true, "The data source accepted the write, but its correlated authoritative source delta did not arrive before the confirmation deadline. The write may still materialize; retry with the same idempotency key or wait for source ingestion to recover."),
|
|
272
294
|
commit_offline_grace_expired: wire('transport', 503, false, 'The offline grace window expired before this commit could be sent, so it was not applied. Re-apply the change once the connection returns.'),
|
|
273
|
-
queue_too_deep: wire('transport', 503, true, 'The
|
|
295
|
+
queue_too_deep: wire('transport', 503, true, 'The line is already past its depth limit. For a claim, more participants were waiting than your `maxQueueDepth` allows — claim again without the cap to wait anyway, or work elsewhere and retry later. For a write, the transaction queue is draining — retry shortly.'),
|
|
274
296
|
flush_timeout: wire('transport', 504, true, 'Flushing the transaction queue timed out before every pending write was sent. Retry once connectivity stabilizes.'),
|
|
275
297
|
wait_for_timeout: wire('transport', 504, true, 'A wait-for condition timed out before it was satisfied. Retry, or extend the timeout.'),
|
|
276
298
|
instance_at_capacity: wire('transport', 503, true, 'The server is at connection capacity. Retry shortly — transient and not specific to your credentials.'),
|
|
@@ -294,8 +316,6 @@ export const ERROR_CODES = {
|
|
|
294
316
|
// fail closed, so the request was rejected rather than admitted unchecked. It
|
|
295
317
|
// is retryable: the next attempt re-probes the backend.
|
|
296
318
|
rate_limiter_unavailable: wire('server', 503, true, 'The rate-limiter backend is unavailable and this endpoint is configured to fail closed; retry shortly.'),
|
|
297
|
-
turn_open_failed: wire('server', 500, true, 'The agent turn could not be opened on the server. It is safe to retry.'),
|
|
298
|
-
turn_close_failed: wire('server', 500, true, 'The agent turn could not be closed cleanly on the server. It is safe to retry the close.'),
|
|
299
319
|
// ── client-only invariants (never serialized) ──────────────────────
|
|
300
320
|
invalid_options: client('client', 'The Ablo client was constructed with invalid or incomplete options.'),
|
|
301
321
|
no_ablo_provider: client('client', 'An Ablo hook was used outside of an Ablo provider.'),
|
|
@@ -345,12 +365,14 @@ export const ERROR_CODES = {
|
|
|
345
365
|
forbidden: wire('permission', 403, false, 'The caller lacks permission for this operation.'),
|
|
346
366
|
source_api_key_unresolved: wire('auth', 401, false, 'The API key presented for this data source could not be resolved to a known key. Check the key and its environment.'),
|
|
347
367
|
capability_auth_disabled: wire('server', 503, false, 'Capability authentication is disabled on this server.'),
|
|
368
|
+
capability_rotation_unavailable: wire('server', 500, false, 'This capability could not be rotated, because the environment it belongs to could not be read from it. It is unchanged and still works. Create a replacement capability and retire this one.'),
|
|
348
369
|
provisioner_unavailable: wire('server', 503, false, 'This deployment has no database provisioner configured, so tables cannot be created here.'),
|
|
349
370
|
invalid_model: wire('validation', 400, false, 'The model name in the request is not a valid model identifier.'),
|
|
350
371
|
invalid_id: wire('validation', 400, false, 'The id in the request is not a valid identifier.'),
|
|
351
372
|
unknown_model: wire('tenant', 400, false, 'Named a model the server does not know. Run `ablo push` (or keep `ablo dev` running) to upload `ablo/schema.ts` — the server keeps its own copy of the schema.'),
|
|
352
373
|
model_not_tenant_scoped: wire('tenant', 400, false, 'This model is not tenant-scoped, so it cannot be queried through the tenant-scoped read path.'),
|
|
353
374
|
source_tenancy_not_enforced: wire('tenant', 400, false, "This model is scoped by its connected data source (`policy: { by: 'source' }`), so its tenant is resolved from the source registration rather than a row column. Enforcing that resolution requires the write-through connect path, which is not enabled on this plane yet — so the model cannot be served through the tenant-scoped read or bootstrap path without risking a cross-tenant read. If this model lives on a log plane, scope it with `by: 'column'` or `by: 'parent'` instead."),
|
|
375
|
+
user_scope_not_enforced: wire('tenant', 400, false, "Rows in this model belong to one person rather than to the whole organization, and that boundary is kept where the rows are stored. This plane is currently served from its log, which carries the organization and the project on every row but not the owner — so the boundary cannot be applied here, and serving the read would show one member another member's private records. It was declined whole and nothing was returned. Serve the plane from its tables, or read with a credential that acts for the organization rather than for a person."),
|
|
354
376
|
model_not_provisioned: wire('tenant', 409, false, "This model is in the plane's registered schema, but its table has not been provisioned yet. Registering a schema with `ablo push` records the model; a plane's physical tables are created separately, out of band, so a model can appear in the schema before its table exists. Provision the plane's tables, then retry the read."),
|
|
355
377
|
schema_table_invalid: wire('schema', 500, false, "The model's table identifier is invalid."),
|
|
356
378
|
schema_scope_invalid: wire('schema', 500, false, "The model's scope predicate could not be built."),
|
|
@@ -367,6 +389,7 @@ export const ERROR_CODES = {
|
|
|
367
389
|
protocol_version_unsupported: wire('transport', 426, false, 'The client sync-protocol version is outside the range this server supports — upgrade the SDK (or the server was rolled back mid-fleet).'),
|
|
368
390
|
database_unreachable: wire('validation', 400, false, "Ablo could not reach this database to check that it can stream replication. The connection string may be wrong, the host may not be reachable from Ablo's servers, or the credentials may not be accepted."),
|
|
369
391
|
database_not_replication_ready: wire('validation', 400, false, 'This database is not set up for logical replication yet. Every failing item — wal_level, the publication, the replication grant, a replica identity — is listed in the error details with its exact fix. `ablo connect` prints the one-time setup; `ablo connect check` verifies it.'),
|
|
392
|
+
database_already_connected: wire('conflict', 409, false, 'This database is already connected to another environment in your organization. Ablo streams a database from one environment at a time, so connecting it to a second one would leave the two reading the same change stream, and the newer connection would take the stream over from the older. Give this environment a database of its own — on a branching provider a branch of the same database is the usual answer, and it is what sandbox is for. If you meant to move the connection rather than add one, disconnect it from the environment that holds it with `ablo connect deregister` first.'),
|
|
370
393
|
replication_publication_drift: wire('validation', 400, false, 'Your schema maps to tables that are not members of the replication publication, so their changes silently never stream and the source looks frozen. The missing tables and the exact `ALTER PUBLICATION … ADD TABLE …` to add them are in the error details — Ablo never alters your database for you.'),
|
|
371
394
|
query_unknown_relation: wire('validation', 400, false, 'The query references a relation the model does not define. Check the relation name against the schema.'),
|
|
372
395
|
query_relation_target_unknown: wire('schema', 500, false, 'A relation in the query targets a model the schema does not define.'),
|
|
@@ -384,20 +407,24 @@ export const ERROR_CODES = {
|
|
|
384
407
|
usage_invalid: wire('validation', 400, false, 'The usage request was invalid.'),
|
|
385
408
|
invalid_request: wire('validation', 400, false, 'The request parameters were invalid.'),
|
|
386
409
|
capability_not_found: wire('not_found', 404, false, 'No capability exists with the given id.'),
|
|
410
|
+
claim_not_found: wire('not_found', 404, false, 'No claim of yours exists with the given id. It was released, it expired, or it was never acquired on this plane.'),
|
|
387
411
|
invalid_participant_kind: wire('validation', 400, false, 'The participant kind is not one the server recognizes.'),
|
|
388
412
|
invalid_sync_group: wire('validation', 400, false, 'Sync groups must be `default` or `<namespace>:<id>`.'),
|
|
389
413
|
narrow_scope_required: wire('validation', 400, false, 'This request requires a scope narrowed to specific resources; the presented scope is too broad.'),
|
|
390
414
|
wide_scope_forbidden: wire('permission', 403, false, 'This caller may not use a wide scope. Request a scope narrowed to the resources you need.'),
|
|
391
415
|
capability_required: wire('auth', 401, false, 'This operation requires a capability, and none was presented.'),
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
416
|
+
// A row in an ordered collection can be created at a position stated relative
|
|
417
|
+
// to its neighbours ("before this one"), so the position has to be resolved
|
|
418
|
+
// against the parent before the row lands. These are the two ways that request
|
|
419
|
+
// can fail to describe a real place: no parent to resolve against, and a
|
|
420
|
+
// neighbour that is not in it.
|
|
421
|
+
position_missing_parent: wire('validation', 400, false, 'This row gives a position but not the parent it belongs to, so the position cannot be resolved. Send the parent id alongside the position.'),
|
|
422
|
+
position_unknown_sibling: wire('validation', 400, false, 'This row is positioned relative to another row that is not in the same parent. Check that the neighbouring row id is correct and still present.'),
|
|
397
423
|
schema_too_large: wire('validation', 413, false, 'The submitted schema exceeds the maximum size.'),
|
|
398
424
|
request_too_large: wire('validation', 413, false, 'The request body exceeds the maximum size.'),
|
|
399
425
|
invalid_schema: wire('validation', 400, false, 'The submitted schema could not be parsed.'),
|
|
400
426
|
incompatible_change: wire('conflict', 409, false, 'The schema change is incompatible with the schema currently deployed and cannot be applied as-is.'),
|
|
427
|
+
replication_reset_required: wire('conflict', 409, false, 'A connected log plane has live rows for a mapped model that disappeared. Declare a rename or an explicit drop/reset before advancing the schema.'),
|
|
401
428
|
};
|
|
402
429
|
/** Looks up the {@link ErrorCodeSpec} for a code. Returns `undefined` for the
|
|
403
430
|
* dynamic `policy:*` family and for any newer code this client does not yet
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Both work on every subclass.
|
|
16
16
|
*/
|
|
17
|
+
import { z } from 'zod';
|
|
17
18
|
import type { ErrorCode } from './errorCodes.js';
|
|
18
19
|
import { type WireClaimSummary, type ModelClaim, type ModelTarget, type ParticipantKind } from './coordination/schema.js';
|
|
19
20
|
export type { ErrorCode, WireErrorCode, ErrorCategory, ErrorCodeSpec, RecoveryClass } from './errorCodes.js';
|
|
@@ -274,7 +275,7 @@ export declare function claimedError(target: {
|
|
|
274
275
|
* narrow an existing capability into, one that would pass on retry.
|
|
275
276
|
*/
|
|
276
277
|
export interface RequiredCapability {
|
|
277
|
-
/** The operation or capability scope, for example `"
|
|
278
|
+
/** The operation or capability scope, for example `"report.update"` or
|
|
278
279
|
* `"subscribe"`. */
|
|
279
280
|
readonly scope: string;
|
|
280
281
|
/** The concrete constraints the capability must satisfy — for example
|
|
@@ -314,19 +315,19 @@ export declare class CapabilityError extends AbloPermissionError {
|
|
|
314
315
|
* user should be sent to sign in again rather than offered a generic retry.
|
|
315
316
|
*
|
|
316
317
|
* It extends {@link AbloAuthenticationError}, so it is caught both by code using
|
|
317
|
-
* the {@link
|
|
318
|
+
* the {@link AbloSessionError.isSessionError} check and by code that catches the
|
|
318
319
|
* authentication category in general.
|
|
319
320
|
*/
|
|
320
|
-
export declare class
|
|
321
|
+
export declare class AbloSessionError extends AbloAuthenticationError {
|
|
321
322
|
readonly isSessionError = true;
|
|
322
323
|
readonly statusCode: number;
|
|
323
324
|
constructor(message: string, statusCode?: number);
|
|
324
325
|
/**
|
|
325
|
-
* Returns true when a value is a {@link
|
|
326
|
+
* Returns true when a value is a {@link AbloSessionError}, or any error-like
|
|
326
327
|
* object that reports itself as a session error through an `isSessionError`
|
|
327
328
|
* flag.
|
|
328
329
|
*/
|
|
329
|
-
static isSessionError(error: unknown): error is
|
|
330
|
+
static isSessionError(error: unknown): error is AbloSessionError;
|
|
330
331
|
/**
|
|
331
332
|
* Determines whether an HTTP response means the login session has expired and
|
|
332
333
|
* the user should sign in again. When the body carries a structured Ablo error
|
|
@@ -336,7 +337,17 @@ export declare class SyncSessionError extends AbloAuthenticationError {
|
|
|
336
337
|
static isSessionErrorResponse(status: number, body?: string): boolean;
|
|
337
338
|
}
|
|
338
339
|
/**
|
|
339
|
-
*
|
|
340
|
+
* @deprecated Renamed to {@link AbloSessionError} — the one engine-named class
|
|
341
|
+
* in the Ablo* hierarchy, brought in line with the family. Same class, same
|
|
342
|
+
* `instanceof` identity. Removed in 0.36.0.
|
|
343
|
+
*/
|
|
344
|
+
export declare const SyncSessionError: typeof AbloSessionError;
|
|
345
|
+
/**
|
|
346
|
+
* @deprecated Renamed to {@link AbloSessionError}. Removed in 0.36.0.
|
|
347
|
+
*/
|
|
348
|
+
export type SyncSessionError = AbloSessionError;
|
|
349
|
+
/**
|
|
350
|
+
* The WebSocket-close counterpart to {@link AbloSessionError.isSessionErrorResponse}:
|
|
340
351
|
* returns true for close reasons that mean the short-lived access credential
|
|
341
352
|
* (`ek_` or `rk_`) has expired. The server closes such sockets with code 4001
|
|
342
353
|
* and reason `'credential_expired'`. Because the credential is re-mintable from
|
|
@@ -345,6 +356,24 @@ export declare class SyncSessionError extends AbloAuthenticationError {
|
|
|
345
356
|
* reason, such as a revoked key or a genuinely lost login, stays terminal.
|
|
346
357
|
*/
|
|
347
358
|
export declare function isAccessCredentialExpiryCloseReason(reason: string): boolean;
|
|
359
|
+
/**
|
|
360
|
+
* The one definition of the {@link RequiredCapability} wire shape. Every
|
|
361
|
+
* boundary that carries a denial's capability detail — the error body parsed
|
|
362
|
+
* here, and the rejected commit receipt in `wire/commit.ts` — composes this
|
|
363
|
+
* schema rather than restating the fields, so a new member cannot reach one
|
|
364
|
+
* boundary and miss the other.
|
|
365
|
+
*
|
|
366
|
+
* Unknown keys pass through: the server may describe a capability in more
|
|
367
|
+
* detail than this build knows, and dropping those keys would leave a caller
|
|
368
|
+
* unable to act on a denial it could otherwise satisfy.
|
|
369
|
+
*/
|
|
370
|
+
export declare const requiredCapabilityWireSchema: z.ZodObject<{
|
|
371
|
+
scope: z.ZodString;
|
|
372
|
+
constraints: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodString]>>>;
|
|
373
|
+
issuer: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
|
|
374
|
+
ttlSeconds: z.ZodPreprocess<z.ZodOptional<z.ZodNumber>>;
|
|
375
|
+
nonce: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
|
|
376
|
+
}, z.core.$loose>;
|
|
348
377
|
/**
|
|
349
378
|
* Coerces any thrown value into an {@link AbloError}, so a consumer never catches
|
|
350
379
|
* an untyped error from the SDK. An error that is already an {@link AbloError}
|
|
@@ -377,14 +406,9 @@ export declare function errorFromWire(message: string, opts?: {
|
|
|
377
406
|
requestId?: string;
|
|
378
407
|
requiredCapability?: RequiredCapability;
|
|
379
408
|
claims?: readonly ClaimErrorClaim[];
|
|
409
|
+
/** Everything the producer attached beyond the envelope's own fields. */
|
|
410
|
+
details?: Readonly<Record<string, unknown>>;
|
|
380
411
|
}): AbloError;
|
|
381
|
-
/**
|
|
382
|
-
* Translates an HTTP response into the appropriate typed {@link AbloError}. This
|
|
383
|
-
* is the single mapping every request path routes a non-2xx response through, so
|
|
384
|
-
* the error a consumer sees is always the right subclass. After extracting the
|
|
385
|
-
* code and message from the response body, it delegates the class selection to
|
|
386
|
-
* {@link errorFromWire}, the same logic the frame transports use.
|
|
387
|
-
*/
|
|
388
412
|
export declare function translateHttpError(status: number, body: unknown, requestId?: string): AbloError;
|
|
389
413
|
/**
|
|
390
414
|
* Reports whether an HTTP error body carries a code that {@link translateHttpError}
|
|
@@ -335,27 +335,27 @@ export class CapabilityError extends AbloPermissionError {
|
|
|
335
335
|
* user should be sent to sign in again rather than offered a generic retry.
|
|
336
336
|
*
|
|
337
337
|
* It extends {@link AbloAuthenticationError}, so it is caught both by code using
|
|
338
|
-
* the {@link
|
|
338
|
+
* the {@link AbloSessionError.isSessionError} check and by code that catches the
|
|
339
339
|
* authentication category in general.
|
|
340
340
|
*/
|
|
341
|
-
export class
|
|
341
|
+
export class AbloSessionError extends AbloAuthenticationError {
|
|
342
342
|
isSessionError = true;
|
|
343
343
|
statusCode;
|
|
344
344
|
constructor(message, statusCode = 401) {
|
|
345
345
|
super(message, { httpStatus: statusCode, code: 'session_expired' });
|
|
346
|
-
this.name = '
|
|
346
|
+
this.name = 'AbloSessionError';
|
|
347
347
|
this.statusCode = statusCode;
|
|
348
348
|
if (Error.captureStackTrace) {
|
|
349
|
-
Error.captureStackTrace(this,
|
|
349
|
+
Error.captureStackTrace(this, AbloSessionError);
|
|
350
350
|
}
|
|
351
351
|
}
|
|
352
352
|
/**
|
|
353
|
-
* Returns true when a value is a {@link
|
|
353
|
+
* Returns true when a value is a {@link AbloSessionError}, or any error-like
|
|
354
354
|
* object that reports itself as a session error through an `isSessionError`
|
|
355
355
|
* flag.
|
|
356
356
|
*/
|
|
357
357
|
static isSessionError(error) {
|
|
358
|
-
if (error instanceof
|
|
358
|
+
if (error instanceof AbloSessionError) {
|
|
359
359
|
return true;
|
|
360
360
|
}
|
|
361
361
|
if (error && typeof error === 'object' && 'isSessionError' in error) {
|
|
@@ -392,7 +392,13 @@ export class SyncSessionError extends AbloAuthenticationError {
|
|
|
392
392
|
}
|
|
393
393
|
}
|
|
394
394
|
/**
|
|
395
|
-
*
|
|
395
|
+
* @deprecated Renamed to {@link AbloSessionError} — the one engine-named class
|
|
396
|
+
* in the Ablo* hierarchy, brought in line with the family. Same class, same
|
|
397
|
+
* `instanceof` identity. Removed in 0.36.0.
|
|
398
|
+
*/
|
|
399
|
+
export const SyncSessionError = AbloSessionError;
|
|
400
|
+
/**
|
|
401
|
+
* The WebSocket-close counterpart to {@link AbloSessionError.isSessionErrorResponse}:
|
|
396
402
|
* returns true for close reasons that mean the short-lived access credential
|
|
397
403
|
* (`ek_` or `rk_`) has expired. The server closes such sockets with code 4001
|
|
398
404
|
* and reason `'credential_expired'`. Because the credential is re-mintable from
|
|
@@ -405,7 +411,18 @@ export function isAccessCredentialExpiryCloseReason(reason) {
|
|
|
405
411
|
}
|
|
406
412
|
// ── HTTP → class mapping ──────────────────────────────────────────────
|
|
407
413
|
const OptionalWireStringSchema = z.preprocess((value) => (typeof value === 'string' ? value : undefined), z.string().optional());
|
|
408
|
-
|
|
414
|
+
/**
|
|
415
|
+
* The one definition of the {@link RequiredCapability} wire shape. Every
|
|
416
|
+
* boundary that carries a denial's capability detail — the error body parsed
|
|
417
|
+
* here, and the rejected commit receipt in `wire/commit.ts` — composes this
|
|
418
|
+
* schema rather than restating the fields, so a new member cannot reach one
|
|
419
|
+
* boundary and miss the other.
|
|
420
|
+
*
|
|
421
|
+
* Unknown keys pass through: the server may describe a capability in more
|
|
422
|
+
* detail than this build knows, and dropping those keys would leave a caller
|
|
423
|
+
* unable to act on a denial it could otherwise satisfy.
|
|
424
|
+
*/
|
|
425
|
+
export const requiredCapabilityWireSchema = z
|
|
409
426
|
.object({
|
|
410
427
|
scope: z.string(),
|
|
411
428
|
constraints: z
|
|
@@ -416,19 +433,19 @@ const RequiredCapabilityWireSchema = z
|
|
|
416
433
|
.preprocess((value) => (typeof value === 'number' ? value : undefined), z.number().optional()),
|
|
417
434
|
nonce: OptionalWireStringSchema,
|
|
418
435
|
})
|
|
419
|
-
.
|
|
436
|
+
.loose();
|
|
420
437
|
const NestedErrorShapeSchema = z
|
|
421
438
|
.object({
|
|
422
439
|
code: OptionalWireStringSchema,
|
|
423
440
|
message: OptionalWireStringSchema,
|
|
424
441
|
field: OptionalWireStringSchema,
|
|
425
|
-
requiredCapability:
|
|
442
|
+
requiredCapability: requiredCapabilityWireSchema.optional().catch(undefined),
|
|
426
443
|
heldBy: OptionalWireStringSchema,
|
|
427
444
|
policyReason: OptionalWireStringSchema,
|
|
428
445
|
heldByClaim: wireClaimSummarySchema.optional().catch(undefined),
|
|
429
446
|
claims: z.array(wireClaimSummarySchema).optional().catch(undefined),
|
|
430
447
|
})
|
|
431
|
-
.
|
|
448
|
+
.loose();
|
|
432
449
|
const ErrorFieldSchema = z
|
|
433
450
|
.preprocess((value) => typeof value === 'string' || (typeof value === 'object' && value !== null)
|
|
434
451
|
? value
|
|
@@ -442,13 +459,13 @@ const ErrorBodyShapeSchema = z
|
|
|
442
459
|
code: OptionalWireStringSchema,
|
|
443
460
|
reason: OptionalWireStringSchema,
|
|
444
461
|
message: OptionalWireStringSchema,
|
|
445
|
-
requiredCapability:
|
|
462
|
+
requiredCapability: requiredCapabilityWireSchema.optional().catch(undefined),
|
|
446
463
|
heldBy: OptionalWireStringSchema,
|
|
447
464
|
policyReason: OptionalWireStringSchema,
|
|
448
465
|
heldByClaim: wireClaimSummarySchema.optional().catch(undefined),
|
|
449
466
|
claims: z.array(wireClaimSummarySchema).optional().catch(undefined),
|
|
450
467
|
})
|
|
451
|
-
.
|
|
468
|
+
.loose();
|
|
452
469
|
function parseErrorBodyShape(body) {
|
|
453
470
|
if (typeof body !== 'object' || body === null)
|
|
454
471
|
return {};
|
|
@@ -489,7 +506,7 @@ export function toAbloError(err) {
|
|
|
489
506
|
* subclass, with its `code`, status, and retryability intact.
|
|
490
507
|
*/
|
|
491
508
|
export function errorFromWire(message, opts = {}) {
|
|
492
|
-
const { code, requestId, requiredCapability, claims } = opts;
|
|
509
|
+
const { code, requestId, requiredCapability, claims, details } = opts;
|
|
493
510
|
// Effective status: an explicit HTTP status wins; otherwise fall back to
|
|
494
511
|
// the code's canonical status from the registry (undefined for unknown /
|
|
495
512
|
// forward-compat codes, which then map to the base AbloError).
|
|
@@ -498,7 +515,12 @@ export function errorFromWire(message, opts = {}) {
|
|
|
498
515
|
// may send a code this SDK predates). Cast to ErrorCode here — the one
|
|
499
516
|
// sanctioned crossing — so internal producers stay statically checked.
|
|
500
517
|
const publicCode = (code === 'claim_conflict' ? 'claim_conflict' : code);
|
|
501
|
-
const baseOpts = {
|
|
518
|
+
const baseOpts = {
|
|
519
|
+
code: publicCode,
|
|
520
|
+
httpStatus,
|
|
521
|
+
requestId,
|
|
522
|
+
...(details && Object.keys(details).length > 0 ? { details } : {}),
|
|
523
|
+
};
|
|
502
524
|
// ── Code-first specials (transport-independent) ──────────────────────
|
|
503
525
|
// A scoped credential was denied — route through CapabilityError so callers
|
|
504
526
|
// can read `.requiredCapability` to attenuate-and-retry.
|
|
@@ -519,6 +541,14 @@ export function errorFromWire(message, opts = {}) {
|
|
|
519
541
|
if (code === 'stale_context') {
|
|
520
542
|
return new AbloStaleContextError(message, baseOpts);
|
|
521
543
|
}
|
|
544
|
+
// The database connected to the caller's environment could not be reached.
|
|
545
|
+
// It rides a 503, but the generic 5xx rule below would rebuild it as an
|
|
546
|
+
// `AbloServerError` — telling the reader Ablo is broken and to wait, when the
|
|
547
|
+
// remedy is on their side and the server already classified it as a
|
|
548
|
+
// connection failure. Keep the class the producer chose.
|
|
549
|
+
if (code === 'connected_database_unreachable') {
|
|
550
|
+
return new AbloConnectionError(message, baseOpts);
|
|
551
|
+
}
|
|
522
552
|
// ── Status-driven dispatch (HTTP parity) ─────────────────────────────
|
|
523
553
|
if (httpStatus === 401)
|
|
524
554
|
return new AbloAuthenticationError(message, baseOpts);
|
|
@@ -541,6 +571,38 @@ export function errorFromWire(message, opts = {}) {
|
|
|
541
571
|
* code and message from the response body, it delegates the class selection to
|
|
542
572
|
* {@link errorFromWire}, the same logic the frame transports use.
|
|
543
573
|
*/
|
|
574
|
+
/**
|
|
575
|
+
* The keys the error envelope spends on itself. {@link AbloError.toJSON} spreads
|
|
576
|
+
* `details` FLAT alongside them rather than nesting it, so everything here is
|
|
577
|
+
* the envelope and everything else was `details` — which is how
|
|
578
|
+
* {@link translateHttpError} puts it back.
|
|
579
|
+
*/
|
|
580
|
+
const ENVELOPE_KEYS = new Set([
|
|
581
|
+
'type',
|
|
582
|
+
'code',
|
|
583
|
+
'param',
|
|
584
|
+
'message',
|
|
585
|
+
'doc_url',
|
|
586
|
+
'request_id',
|
|
587
|
+
// Read into typed properties below, so they are not detail as well.
|
|
588
|
+
'error',
|
|
589
|
+
'reason',
|
|
590
|
+
'requiredCapability',
|
|
591
|
+
'claims',
|
|
592
|
+
'heldByClaim',
|
|
593
|
+
]);
|
|
594
|
+
/** The non-envelope remainder of an error body — what `details` was before it
|
|
595
|
+
* was spread. Empty when the producer attached nothing. */
|
|
596
|
+
function detailsFromBody(body) {
|
|
597
|
+
if (typeof body !== 'object' || body === null)
|
|
598
|
+
return undefined;
|
|
599
|
+
const rest = {};
|
|
600
|
+
for (const [key, value] of Object.entries(body)) {
|
|
601
|
+
if (!ENVELOPE_KEYS.has(key))
|
|
602
|
+
rest[key] = value;
|
|
603
|
+
}
|
|
604
|
+
return Object.keys(rest).length > 0 ? rest : undefined;
|
|
605
|
+
}
|
|
544
606
|
export function translateHttpError(status, body, requestId) {
|
|
545
607
|
const parsed = parseErrorBodyShape(body);
|
|
546
608
|
const nested = parsed.error != null && typeof parsed.error === 'object'
|
|
@@ -561,12 +623,19 @@ export function translateHttpError(status, body, requestId) {
|
|
|
561
623
|
: nested?.heldByClaim
|
|
562
624
|
? [nested.heldByClaim]
|
|
563
625
|
: undefined);
|
|
626
|
+
// The envelope carries `request_id`, so a caller that has only the body still
|
|
627
|
+
// gets the correlation id — without this it was dropped unless the caller
|
|
628
|
+
// happened to read the header and pass it in separately.
|
|
629
|
+
const bodyRequestId = typeof body === 'object' && body !== null && 'request_id' in body
|
|
630
|
+
? body.request_id
|
|
631
|
+
: undefined;
|
|
564
632
|
return errorFromWire(message, {
|
|
565
633
|
code,
|
|
566
634
|
httpStatus: status,
|
|
567
|
-
requestId,
|
|
635
|
+
requestId: requestId ?? (typeof bodyRequestId === 'string' ? bodyRequestId : undefined),
|
|
568
636
|
requiredCapability,
|
|
569
637
|
claims,
|
|
638
|
+
details: detailsFromBody(body),
|
|
570
639
|
});
|
|
571
640
|
}
|
|
572
641
|
/**
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The footprint — every object Ablo leaves inside a customer's own database,
|
|
3
|
+
* declared once.
|
|
4
|
+
*
|
|
5
|
+
* Four things need this list and each used to carry its own copy: the setup SQL
|
|
6
|
+
* that creates the objects, the replication runtime that reads them, the audit
|
|
7
|
+
* that tells a customer what Ablo put there, and (eventually) the teardown that
|
|
8
|
+
* removes them. They drifted, as hand-maintained copies do — `ABLO_PUBLICATION`
|
|
9
|
+
* was declared twice, under a comment insisting the literals "MUST equal the
|
|
10
|
+
* CLI's" and pointing at a file that no longer held it, and the audit went on
|
|
11
|
+
* looking for a footprint from an earlier era while seeing none of what Ablo
|
|
12
|
+
* installs today.
|
|
13
|
+
*
|
|
14
|
+
* The audit is the copy that matters most. A customer runs it to answer "what is
|
|
15
|
+
* still in my database", and a stale list answers "nothing" — which reads as
|
|
16
|
+
* clean rather than as unexamined. So the list lives here, every consumer
|
|
17
|
+
* derives from it, and an object added to Ablo's setup is visible to the audit
|
|
18
|
+
* because it could not have been added anywhere else.
|
|
19
|
+
*
|
|
20
|
+
* Retired objects stay in the list, marked {@link FootprintArtifact.retired}.
|
|
21
|
+
* They are no longer installed, but a database connected to an older Ablo still
|
|
22
|
+
* holds them, and those are precisely the ones a customer would never think to
|
|
23
|
+
* look for.
|
|
24
|
+
*/
|
|
25
|
+
/** The Postgres object class, which decides how the audit looks it up. */
|
|
26
|
+
export type FootprintKind = 'publication' | 'slot' | 'role' | 'table' | 'type';
|
|
27
|
+
export interface FootprintArtifact {
|
|
28
|
+
readonly kind: FootprintKind;
|
|
29
|
+
readonly name: string;
|
|
30
|
+
/** What it is for, in the words a customer reading an audit would want. */
|
|
31
|
+
readonly purpose: string;
|
|
32
|
+
/**
|
|
33
|
+
* Installed only for a data source that reports through the signed endpoint
|
|
34
|
+
* rather than the write-ahead log.
|
|
35
|
+
*/
|
|
36
|
+
readonly endpointOnly?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* No longer installed by any current version. A database that once ran an
|
|
39
|
+
* older Ablo still holds it; the audit reports it so it can be cleaned up.
|
|
40
|
+
*/
|
|
41
|
+
readonly retired?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* What it costs to leave behind, when leaving it behind costs something. The
|
|
44
|
+
* audit leads with this, because "you still have an object named X" is not a
|
|
45
|
+
* reason to act and "this one is retaining your write-ahead log" is.
|
|
46
|
+
*/
|
|
47
|
+
readonly hazard?: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The publication Ablo's replication reads from. Constant by design: a
|
|
51
|
+
* publication declares what streams, and several readers are meant to share one
|
|
52
|
+
* (`publication_names` is a per-stream option, not slot state).
|
|
53
|
+
*/
|
|
54
|
+
export declare const ABLO_PUBLICATION = "ablo_publication";
|
|
55
|
+
/**
|
|
56
|
+
* The replication slot that holds Ablo's position in the write-ahead log.
|
|
57
|
+
*
|
|
58
|
+
* Constant, and it should not be — a slot stores ONE position, so two
|
|
59
|
+
* connections sharing this name compete for the same marker. It is why a
|
|
60
|
+
* database can be connected to one plane at a time. ADR 0020 derives it per
|
|
61
|
+
* connection; {@link isValidReplicationSlotName} is the check a derived name
|
|
62
|
+
* has to pass.
|
|
63
|
+
*/
|
|
64
|
+
export declare const ABLO_REPLICATION_SLOT = "ablo_slot";
|
|
65
|
+
/** The least-privilege login Ablo reads with. Per-connection under ADR 0020. */
|
|
66
|
+
export declare const ABLO_REPLICATION_ROLE = "ablo_replicator";
|
|
67
|
+
/** The separate least-privilege login used only for row writes. */
|
|
68
|
+
export declare const ABLO_WRITE_ROLE = "ablo_writer";
|
|
69
|
+
/** The bookkeeping table that makes a retried write land once. */
|
|
70
|
+
export declare const ABLO_IDEMPOTENCY_TABLE = "ablo_idempotency";
|
|
71
|
+
/** The transactional outbox an endpoint source reports through. */
|
|
72
|
+
export declare const ABLO_OUTBOX_TABLE = "ablo_outbox";
|
|
73
|
+
/**
|
|
74
|
+
* Postgres will not accept a replication slot name outside this shape — the
|
|
75
|
+
* check is in `ReplicationSlotValidateNameInternal`, and identifier quoting does
|
|
76
|
+
* not exempt a name from it. Anything derived into a slot name is validated
|
|
77
|
+
* here, at the point it is derived, rather than failing at the point it is used.
|
|
78
|
+
*/
|
|
79
|
+
export declare const REPLICATION_SLOT_NAME: RegExp;
|
|
80
|
+
export declare function isValidReplicationSlotName(name: string): boolean;
|
|
81
|
+
/** The four plane axes a connection is identified by. */
|
|
82
|
+
export interface FootprintPlane {
|
|
83
|
+
readonly organizationId: string;
|
|
84
|
+
readonly environment: string;
|
|
85
|
+
/** '' for the organization-default project. */
|
|
86
|
+
readonly projectId?: string;
|
|
87
|
+
/** '' for the default sandbox. */
|
|
88
|
+
readonly sandboxId?: string;
|
|
89
|
+
}
|
|
90
|
+
/** The names one connection owns. Nothing here is shared with another plane. */
|
|
91
|
+
export interface FootprintNames {
|
|
92
|
+
readonly slot: string;
|
|
93
|
+
readonly publication: string;
|
|
94
|
+
readonly replicationRole: string;
|
|
95
|
+
readonly writeRole: string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The objects this connection owns, named so no other connection can claim them.
|
|
99
|
+
*
|
|
100
|
+
* A slot stores ONE position, so two connections sharing a name compete for the
|
|
101
|
+
* same marker and Postgres reports nothing — which is why the constants these
|
|
102
|
+
* replace made a database connectable to one plane at a time. The publication
|
|
103
|
+
* and roles are derived from the same digest so a database's footprint reads as
|
|
104
|
+
* one set per connection rather than a mix of shared and private objects.
|
|
105
|
+
*
|
|
106
|
+
* Stable: the same plane always derives the same names, so re-running setup is a
|
|
107
|
+
* no-op rather than a second installation.
|
|
108
|
+
*/
|
|
109
|
+
export declare function footprintNamesFor(plane: FootprintPlane): FootprintNames;
|
|
110
|
+
/** Everything Ablo has ever put in a customer's database, current and retired. */
|
|
111
|
+
export declare const ABLO_FOOTPRINT: readonly FootprintArtifact[];
|
|
Binary file
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Ablo settlement core (ADR 0013).
|
|
3
|
+
*
|
|
4
|
+
* The transaction layer that the reactive client is a consumer of, not the
|
|
5
|
+
* other way round. The root barrel is deliberate: it exports the seam —
|
|
6
|
+
* {@link TransactionLayer} — plus exactly the vocabulary its signatures
|
|
7
|
+
* reference and the error hierarchy callers catch. Everything else stays on
|
|
8
|
+
* subpaths; every addition here is a decision
|
|
9
|
+
* (docs/plans/transaction-layer-barrel-design.md).
|
|
10
|
+
*/
|
|
11
|
+
export type { TransactionLayer, ListQuery, CommitReceipt } from './transactionLayer.js';
|
|
12
|
+
export { Ablo } from './ablo.js';
|
|
13
|
+
export type { AbloHttpClient, AbloHttpClientOptions, HttpModelClient, } from './transport/httpClient.js';
|
|
14
|
+
export type { Delta, HeldClaim, HeldLease, ClaimTarget, ClaimLeaseOptions, } from './types/streams.js';
|
|
15
|
+
export type { ModelData } from './types/modelData.js';
|
|
16
|
+
export { LogPosition, logPositionSchema, parseLogPosition } from './logPosition.js';
|
|
17
|
+
export type { LogPositionSnapshot } from './logPosition.js';
|
|
18
|
+
export { AbloError, AbloAuthenticationError, AbloPermissionError, AbloRateLimitError, AbloIdempotencyError, AbloConnectionError, AbloValidationError, AbloNotFoundError, AbloServerError, AbloStaleContextError, AbloContentionError, AbloClaimedError, AbloSessionError, toAbloError, } from './errors.js';
|
|
19
|
+
export * from './errorCodes.js';
|
|
20
|
+
export * from './environment.js';
|