@abloatai/ablo 0.34.1 → 0.35.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 +674 -5
- package/README.md +39 -22
- package/dist/BaseSyncedStore.d.ts +152 -44
- package/dist/BaseSyncedStore.js +300 -184
- package/dist/Database.d.ts +9 -24
- package/dist/Database.js +37 -22
- package/dist/InstanceCache.d.ts +25 -4
- package/dist/InstanceCache.js +48 -15
- package/dist/LazyReferenceCollection.d.ts +3 -3
- package/dist/LazyReferenceCollection.js +4 -4
- package/dist/Model.d.ts +6 -6
- package/dist/Model.js +10 -10
- package/dist/ModelRegistry.d.ts +4 -4
- package/dist/ModelRegistry.js +3 -3
- package/dist/{SyncEngineContext.d.ts → RuntimeContext.d.ts} +20 -13
- package/dist/{SyncEngineContext.js → RuntimeContext.js} +11 -12
- package/dist/SyncClient.d.ts +42 -32
- package/dist/SyncClient.js +166 -110
- 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/cli.cjs +3344 -1073
- package/dist/client/Ablo.d.ts +42 -287
- package/dist/client/Ablo.js +118 -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 +4 -4
- package/dist/client/createInternalComponents.js +9 -8
- package/dist/client/createModelProxy.d.ts +78 -373
- package/dist/client/createModelProxy.js +114 -86
- package/dist/client/humans.d.ts +48 -0
- package/dist/client/humans.js +52 -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 +48 -0
- package/dist/client/reactiveEngine.js +910 -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/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 +9 -9
- package/dist/context.js +10 -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/DatabaseManager.js +1 -1
- package/dist/core/QueryView.d.ts +1 -1
- package/dist/core/QueryView.js +1 -1
- package/dist/core/StoreManager.d.ts +4 -23
- package/dist/core/StoreManager.js +5 -55
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +2 -2
- package/dist/core/storeContract.d.ts +2 -2
- package/dist/docs/catalog.d.ts +72 -0
- package/dist/docs/catalog.js +227 -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 +40 -34
- package/dist/index.js +26 -20
- 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 +2 -2
- package/dist/query/client.js +4 -4
- package/dist/query/types.d.ts +6 -41
- package/dist/query/types.js +2 -2
- package/dist/react/AbloProvider.d.ts +6 -8
- package/dist/react/AbloProvider.js +5 -7
- package/dist/react/context.d.ts +1 -1
- package/dist/react/context.js +1 -1
- package/dist/react/index.d.ts +5 -5
- package/dist/react/index.js +3 -3
- package/dist/react/internalContext.d.ts +1 -1
- package/dist/react/useAblo.d.ts +3 -3
- package/dist/react/useAblo.js +1 -1
- 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 +86 -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 +10 -10
- package/dist/schema/index.js +18 -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 +3 -3
- package/dist/schema/serialize.d.ts +15 -6
- package/dist/schema/serialize.js +17 -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/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/footprint.d.ts +111 -0
- package/dist/source/footprint.js +0 -0
- 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/stores/ObjectStore.d.ts +1 -1
- package/dist/stores/SyncActionStore.d.ts +1 -1
- package/dist/stores/SyncActionStore.js +2 -10
- 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 +123 -6
- package/dist/sync/BootstrapFetcher.js +492 -66
- package/dist/sync/ConnectionManager.d.ts +6 -198
- package/dist/sync/ConnectionManager.js +6 -677
- package/dist/sync/OnDemandLoader.d.ts +2 -2
- package/dist/sync/OnDemandLoader.js +60 -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/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 +1 -1
- package/dist/sync/participants.d.ts +5 -4
- package/dist/sync/participants.js +29 -22
- package/dist/sync/schemaDrift.d.ts +55 -0
- package/dist/sync/schemaDrift.js +53 -0
- package/dist/sync/schemas.d.ts +21 -32
- package/dist/sync/schemas.js +26 -17
- 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/testing/fixtures/bootstrap.d.ts +1 -1
- package/dist/testing/fixtures/deltas.d.ts +1 -1
- package/dist/testing/fixtures/httpResponses.d.ts +70 -0
- package/dist/testing/fixtures/httpResponses.js +90 -0
- package/dist/testing/fixtures/models.js +1 -1
- package/dist/testing/helpers/wait.js +1 -1
- package/dist/testing/mocks/MockMutationExecutor.d.ts +2 -2
- package/dist/testing/mocks/MockMutationExecutor.js +8 -14
- package/dist/testing/mocks/MockSyncContext.d.ts +11 -11
- package/dist/testing/mocks/MockSyncContext.js +10 -9
- package/dist/testing/mocks/MockSyncStore.js +1 -1
- package/dist/testing/mocks/MockWebSocket.d.ts +2 -2
- 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 +177 -0
- package/dist/transaction/auth/capability.js +199 -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 +49 -0
- package/dist/transaction/coordination/awaitClaimGrant.js +112 -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 +44 -0
- package/dist/transaction/coordination/locator.d.ts +83 -0
- package/dist/transaction/coordination/locator.js +82 -0
- package/dist/transaction/coordination/schema.d.ts +1473 -0
- package/dist/{coordination → transaction/coordination}/schema.js +490 -55
- package/dist/transaction/coordination/targetConflict.d.ts +2 -0
- package/dist/transaction/coordination/targetConflict.js +103 -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} +11 -11
- package/dist/{errorCodes.js → transaction/errorCodes.js} +35 -12
- package/dist/{errors.d.ts → transaction/errors.d.ts} +37 -13
- package/dist/{errors.js → transaction/errors.js} +85 -16
- 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 +192 -0
- package/dist/transaction/plugin.js +87 -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 +266 -0
- package/dist/transaction/resources/httpResources.js +7 -0
- package/dist/transaction/resources/modelOperations.d.ts +319 -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 +85 -0
- package/dist/transaction/resources/where.js +70 -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 +5 -5
- package/dist/{schema → transaction/schema}/field.js +5 -5
- 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 +57 -0
- package/dist/transaction/schema/openapi.js +340 -0
- package/dist/{schema → transaction/schema}/relation.d.ts +14 -14
- 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 +12 -10
- package/dist/{schema → transaction/schema}/schema.js +4 -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 +4 -5
- 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} +10 -36
- 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/{client → transaction/transport}/httpClient.d.ts +59 -16
- package/dist/{client → transaction/transport}/httpClient.js +5 -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 +171 -85
- 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 +576 -0
- package/dist/transaction/transport/wsTransport.js +1017 -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 +63 -45
- 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 +351 -0
- package/dist/transaction/wire/accountResponses.js +255 -0
- package/dist/transaction/wire/auth.d.ts +49 -0
- package/dist/transaction/wire/auth.js +57 -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 +463 -0
- package/dist/transaction/wire/claims.js +229 -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 +177 -0
- package/dist/transaction/wire/feedEvent.js +39 -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 +552 -0
- package/dist/transaction/wire/inboundFrames.js +116 -0
- package/dist/transaction/wire/index.d.ts +50 -0
- package/dist/transaction/wire/index.js +74 -0
- package/dist/{wire → transaction/wire}/listEnvelope.d.ts +13 -14
- package/dist/transaction/wire/listEnvelope.js +42 -0
- package/dist/transaction/wire/modelResponses.d.ts +85 -0
- package/dist/transaction/wire/modelResponses.js +43 -0
- package/dist/transactions/{TransactionQueue.d.ts → mutations/MutationQueue.d.ts} +79 -38
- package/dist/transactions/{TransactionQueue.js → mutations/MutationQueue.js} +110 -59
- 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} +16 -15
- package/dist/transactions/{commitPayload.js → mutations/commitPayload.js} +12 -12
- package/dist/transactions/{deltaConfirmation.d.ts → mutations/deltaConfirmation.d.ts} +11 -11
- package/dist/transactions/{deltaConfirmation.js → mutations/deltaConfirmation.js} +7 -7
- 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} +3 -3
- package/dist/transactions/{replayValidation.js → mutations/replayValidation.js} +4 -3
- package/dist/utils/mobxSetup.d.ts +1 -1
- package/dist/utils/mobxSetup.js +5 -2
- 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 +19 -12
- package/docs/api-keys.md +8 -4
- package/docs/api.md +22 -18
- package/docs/audit.md +2 -0
- package/docs/cli.md +31 -3
- package/docs/client-behavior.md +8 -6
- package/docs/concurrency-convention.md +30 -24
- package/docs/coordination.md +48 -38
- package/docs/data-sources.md +3 -1
- package/docs/debugging.md +5 -3
- 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 +45 -44
- package/docs/examples/server-agent.md +46 -26
- package/docs/groups.md +32 -29
- package/docs/guarantees.md +4 -2
- package/docs/how-it-works.md +9 -7
- package/docs/idempotency.md +126 -0
- package/docs/identity.md +58 -54
- package/docs/index.md +172 -86
- package/docs/integration-guide.md +17 -16
- package/docs/interaction-model.md +6 -4
- package/docs/mcp.md +41 -16
- package/docs/migration.md +63 -5
- package/docs/operating-on-your-database.md +3 -1
- package/docs/projects.md +2 -0
- package/docs/quickstart.md +22 -5
- package/docs/react.md +12 -10
- package/docs/schema-contract.md +5 -3
- package/docs/session-settings.md +108 -0
- package/docs/sessions.md +3 -1
- package/docs/webhooks.md +3 -1
- package/llms.txt +47 -17
- package/package.json +10 -8
- 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/client/durableWrites.d.ts +0 -21
- package/dist/coordination/schema.d.ts +0 -722
- package/dist/schema/openapi.d.ts +0 -29
- package/dist/schema/openapi.js +0 -124
- package/dist/transactions/durableWriteStore.js +0 -30
- 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/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/coordination}/claimHeartbeatLoop.d.ts +0 -0
- /package/dist/{client → transaction/coordination}/claimHeartbeatLoop.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/{utils → transaction/utils}/duration.d.ts +0 -0
- /package/dist/{utils → transaction/utils}/duration.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
|
@@ -7,13 +7,16 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { AbloClaimedError, AbloAuthenticationError, AbloConnectionError, AbloIdempotencyError, AbloValidationError, AbloNotFoundError, claimedError, translateHttpError, } from '../errors.js';
|
|
9
9
|
import { v5 as uuidv5 } from 'uuid';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import { reconcileFunctionalUpdate, } from '../resources/functionalUpdate.js';
|
|
12
|
+
import { assertBrowserSafety, readProcessEnv, resolveApiKey, resolveApiKeyValue, resolveAuthToken, resolveBaseURL, resolveBootstrapBaseUrl, rejectRemovedDatabaseUrlOption, warnIfCliKeyMismatch, } from '../auth/apiKey.js';
|
|
12
13
|
import { PROTOCOL_VERSION, PROTOCOL_VERSION_HEADER } from '../wire/protocolVersion.js';
|
|
13
14
|
import { commitReceiptSchema } from '../wire/commit.js';
|
|
15
|
+
import { claimAcquireResponseSchema, claimHeartbeatReplySchema, claimListResponseSchema, } from '../wire/claims.js';
|
|
16
|
+
import { modelListResponseSchema, modelReadResponseSchema, } from '../wire/modelResponses.js';
|
|
14
17
|
import { toMs } from '../utils/duration.js';
|
|
15
|
-
import { heartbeatCadenceMs, resolveHeartbeatOptions, startClaimHeartbeatLoop, } from '
|
|
16
|
-
import { mintSession } from '
|
|
18
|
+
import { heartbeatCadenceMs, resolveHeartbeatOptions, startClaimHeartbeatLoop, } from '../coordination/claimHeartbeatLoop.js';
|
|
19
|
+
import { mintSession } from '../auth/sessionMint.js';
|
|
17
20
|
import { parseIdentityResolveResponse } from '../auth/schemas.js';
|
|
18
21
|
/**
|
|
19
22
|
* Interpret a heartbeat reply for a lease this handle HOLDS: anything other
|
|
@@ -31,12 +34,20 @@ function heldHeartbeatReply(reply, label) {
|
|
|
31
34
|
}
|
|
32
35
|
throw new AbloClaimedError(`The lease behind ${label} is no longer held — it expired or was granted onward. Re-acquire the claim and retry; a write attempted under the old lease is rejected by its \`readAt\` guard.`, { code: 'claim_lost' });
|
|
33
36
|
}
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
+
import { claimDescription } from '../coordination/schema.js';
|
|
38
|
+
import { subTarget, streamTarget } from '../coordination/locator.js';
|
|
39
|
+
import { declaredMeta, wireMeta } from '../coordination/claimMeta.js';
|
|
40
|
+
import { assertWriteOptions } from '../resources/writeOptionsSchema.js';
|
|
41
|
+
import { createDurableHttpCommitEnvelope, canonicalHttpCommitBody, durableHttpCommitEnvelopeSchema, httpCommitEnvelopeRecordId, isHttpCommitReplayExpired, } from '../transactions/settlement/httpCommitEnvelope.js';
|
|
42
|
+
import { resolveDurableWrites } from '../durableWrites.js';
|
|
37
43
|
/** @internal Default per-request deadline for the private HTTP transport. */
|
|
38
44
|
export const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
|
|
39
45
|
const HTTP_CONFIRMATION_POLL_INTERVAL_MS = 250;
|
|
46
|
+
/**
|
|
47
|
+
* The server's acquire window, mirrored here as the client-side default for a
|
|
48
|
+
* claim that names no `ttl` — it sets the auto-heartbeat cadence.
|
|
49
|
+
*/
|
|
50
|
+
const DEFAULT_CLAIM_TTL_MS = 60_000;
|
|
40
51
|
function parseSuccessfulCommitResponse(value, idempotencyKey) {
|
|
41
52
|
const parsed = commitReceiptSchema.safeParse(value);
|
|
42
53
|
if (!parsed.success || parsed.data.clientTxId !== idempotencyKey) {
|
|
@@ -51,6 +62,10 @@ function parseSuccessfulCommitResponse(value, idempotencyKey) {
|
|
|
51
62
|
}
|
|
52
63
|
/** Decode the HTTP claim DTO into the one public Claim shape. */
|
|
53
64
|
function claimFromModelClaim(claim) {
|
|
65
|
+
// The handle a caller reads back is a public claim, so its `meta` is the
|
|
66
|
+
// declared shape; the rest of the sub-entity locator crosses whole rather
|
|
67
|
+
// than member by member, which is how `fields` used to die on this hop.
|
|
68
|
+
const { meta, ...details } = subTarget(claim.target);
|
|
54
69
|
return {
|
|
55
70
|
object: 'claim',
|
|
56
71
|
id: claim.id,
|
|
@@ -62,12 +77,9 @@ function claimFromModelClaim(claim) {
|
|
|
62
77
|
expiresAt: claim.expiresAt,
|
|
63
78
|
...(claim.position !== undefined ? { position: claim.position } : {}),
|
|
64
79
|
target: {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
...(
|
|
68
|
-
...(claim.target.range ? { range: claim.target.range } : {}),
|
|
69
|
-
...(claim.target.field ? { field: claim.target.field } : {}),
|
|
70
|
-
...(claim.target.meta ? { meta: claim.target.meta } : {}),
|
|
80
|
+
...streamTarget(claim.target),
|
|
81
|
+
...details,
|
|
82
|
+
...(meta !== undefined ? { meta: declaredMeta(meta) } : {}),
|
|
71
83
|
},
|
|
72
84
|
};
|
|
73
85
|
}
|
|
@@ -159,7 +171,7 @@ export function createHttpTransport(options) {
|
|
|
159
171
|
}
|
|
160
172
|
: undefined;
|
|
161
173
|
if (!scope) {
|
|
162
|
-
const rawIdentity = await
|
|
174
|
+
const rawIdentity = await requestRaw('/auth/identity', { method: 'GET' }, true);
|
|
163
175
|
const identity = parseIdentityResolveResponse(rawIdentity);
|
|
164
176
|
scope = {
|
|
165
177
|
organizationId: identity.accountScope,
|
|
@@ -226,7 +238,15 @@ export function createHttpTransport(options) {
|
|
|
226
238
|
return target.toString();
|
|
227
239
|
}
|
|
228
240
|
const requestTimeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
229
|
-
|
|
241
|
+
/**
|
|
242
|
+
* Issues one request and returns its decoded body without a contract.
|
|
243
|
+
*
|
|
244
|
+
* Use this only where the response has no shape worth checking — a release
|
|
245
|
+
* that answers `{}` — or where the caller runs a richer check of its own, as
|
|
246
|
+
* the commit paths do with their receipt schema. Everywhere else, go through
|
|
247
|
+
* {@link requestJson}, which will not let a response past unvalidated.
|
|
248
|
+
*/
|
|
249
|
+
async function requestRaw(path, init, skipReady = false) {
|
|
230
250
|
if (!skipReady)
|
|
231
251
|
await ready();
|
|
232
252
|
const { idempotencyKey, sealedProtocolVersion, ...requestInit } = init;
|
|
@@ -295,6 +315,22 @@ export function createHttpTransport(options) {
|
|
|
295
315
|
}
|
|
296
316
|
return body;
|
|
297
317
|
}
|
|
318
|
+
/**
|
|
319
|
+
* Issues one request and validates its body against the route's schema.
|
|
320
|
+
*
|
|
321
|
+
* The schema is the route's response contract, declared once in `wire/` and
|
|
322
|
+
* shared with the server that produces it. A body that does not match is a
|
|
323
|
+
* version disagreement between the two, so it is refused whole rather than
|
|
324
|
+
* read field by field and half-trusted.
|
|
325
|
+
*/
|
|
326
|
+
async function requestJson(path, init, responseSchema, skipReady = false) {
|
|
327
|
+
const body = await requestRaw(path, init, skipReady);
|
|
328
|
+
const parsed = responseSchema.safeParse(body);
|
|
329
|
+
if (!parsed.success) {
|
|
330
|
+
throw new AbloConnectionError(`The Ablo API returned a response for ${init.method ?? 'GET'} ${path} that this client could not read; nothing was applied.`, { code: 'malformed_response', cause: parsed.error });
|
|
331
|
+
}
|
|
332
|
+
return parsed.data;
|
|
333
|
+
}
|
|
298
334
|
function isDefinitiveHttpRejection(error) {
|
|
299
335
|
if (typeof error !== 'object' || error === null)
|
|
300
336
|
return false;
|
|
@@ -388,7 +424,7 @@ export function createHttpTransport(options) {
|
|
|
388
424
|
}, remaining)
|
|
389
425
|
: null;
|
|
390
426
|
try {
|
|
391
|
-
const raw = await
|
|
427
|
+
const raw = await requestRaw(request.path, {
|
|
392
428
|
method: request.method,
|
|
393
429
|
idempotencyKey: request.idempotencyKey,
|
|
394
430
|
...(request.sealedProtocolVersion !== undefined
|
|
@@ -456,7 +492,7 @@ export function createHttpTransport(options) {
|
|
|
456
492
|
a.id.localeCompare(b.id));
|
|
457
493
|
for (const envelope of envelopes) {
|
|
458
494
|
try {
|
|
459
|
-
const raw = await
|
|
495
|
+
const raw = await requestRaw(envelope.request.path, {
|
|
460
496
|
method: envelope.request.method,
|
|
461
497
|
idempotencyKey: envelope.idempotencyKey,
|
|
462
498
|
sealedProtocolVersion: envelope.protocolVersion,
|
|
@@ -613,7 +649,7 @@ export function createHttpTransport(options) {
|
|
|
613
649
|
};
|
|
614
650
|
let response;
|
|
615
651
|
try {
|
|
616
|
-
const raw = await
|
|
652
|
+
const raw = await requestRaw(exactRequest.path, {
|
|
617
653
|
method: exactRequest.method,
|
|
618
654
|
idempotencyKey: exactRequest.idempotencyKey,
|
|
619
655
|
...(exactRequest.sealedProtocolVersion !== undefined
|
|
@@ -654,11 +690,6 @@ export function createHttpTransport(options) {
|
|
|
654
690
|
? crypto.randomUUID()
|
|
655
691
|
: `tx_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
656
692
|
}
|
|
657
|
-
function createClaimId() {
|
|
658
|
-
return typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
|
|
659
|
-
? `int_${crypto.randomUUID()}`
|
|
660
|
-
: `int_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
661
|
-
}
|
|
662
693
|
function createModelId(modelName, idempotencyKey) {
|
|
663
694
|
if (idempotencyKey) {
|
|
664
695
|
return uuidv5(`${modelName}:${idempotencyKey}`, 'aa4ba6d4-bf0b-5b38-9c45-116f79a6e548');
|
|
@@ -698,13 +729,8 @@ export function createHttpTransport(options) {
|
|
|
698
729
|
if (target?.field)
|
|
699
730
|
params.set('field', target.field);
|
|
700
731
|
const suffix = params.toString();
|
|
701
|
-
const body = await requestJson(`/v1/claims${suffix ? `?${suffix}` : ''}`, {
|
|
702
|
-
|
|
703
|
-
});
|
|
704
|
-
return {
|
|
705
|
-
active: body.claims ?? [],
|
|
706
|
-
queue: body.queue ?? [],
|
|
707
|
-
};
|
|
732
|
+
const body = await requestJson(`/v1/claims${suffix ? `?${suffix}` : ''}`, { method: 'GET' }, claimListResponseSchema);
|
|
733
|
+
return { active: body.claims, queue: body.queue };
|
|
708
734
|
}
|
|
709
735
|
async function applyClaimedPolicy(target, options, defaultPolicy = 'return') {
|
|
710
736
|
const policy = options?.ifClaimed ?? defaultPolicy;
|
|
@@ -761,14 +787,14 @@ export function createHttpTransport(options) {
|
|
|
761
787
|
})));
|
|
762
788
|
throw error;
|
|
763
789
|
}
|
|
764
|
-
// `requestJson` throws via `translateHttpError` on any non-2xx,
|
|
765
|
-
//
|
|
766
|
-
//
|
|
767
|
-
//
|
|
768
|
-
|
|
790
|
+
// `requestJson` throws via `translateHttpError` on any non-2xx, so
|
|
791
|
+
// reaching here implies success and `body` is already the success-only
|
|
792
|
+
// receipt union — a rejection is a separate type that never arrives here.
|
|
793
|
+
// The settlement status therefore passes through verbatim: no branch may
|
|
794
|
+
// collapse a state the server reported into a different one.
|
|
769
795
|
return {
|
|
770
796
|
id: body.id ?? body.clientTxId,
|
|
771
|
-
status,
|
|
797
|
+
status: body.status,
|
|
772
798
|
lastSyncId: body.lastSyncId,
|
|
773
799
|
...(body.notifications && body.notifications.length > 0
|
|
774
800
|
? { notifications: body.notifications }
|
|
@@ -799,26 +825,26 @@ export function createHttpTransport(options) {
|
|
|
799
825
|
}
|
|
800
826
|
}
|
|
801
827
|
const qs = params.toString();
|
|
802
|
-
const res = await requestJson(`/v1/models/${encodeURIComponent(modelName)}${qs ? `?${qs}` : ''}`, { method: 'GET' });
|
|
803
|
-
|
|
828
|
+
const res = await requestJson(`/v1/models/${encodeURIComponent(modelName)}${qs ? `?${qs}` : ''}`, { method: 'GET' }, modelListResponseSchema);
|
|
829
|
+
// The envelope is checked; the rows are not, and cannot be here. This
|
|
830
|
+
// transport is schema-agnostic — it moves rows for whatever schema the
|
|
831
|
+
// caller declared, and `T` is that declaration. Row validation belongs to
|
|
832
|
+
// the typed facade above, which holds the model's schema.
|
|
833
|
+
return res.data;
|
|
804
834
|
}
|
|
805
835
|
async function retrieveModel(modelName, params) {
|
|
806
836
|
await applyClaimedPolicy({ model: modelName, id: params.id }, params);
|
|
807
|
-
const query = await requestJson(`/v1/models/${encodeURIComponent(modelName)}/${encodeURIComponent(params.id)}`, {
|
|
808
|
-
method: 'GET',
|
|
809
|
-
});
|
|
837
|
+
const query = await requestJson(`/v1/models/${encodeURIComponent(modelName)}/${encodeURIComponent(params.id)}`, { method: 'GET' }, modelReadResponseSchema);
|
|
810
838
|
// A miss is `data: undefined`, not a thrown error. The WebSocket client's
|
|
811
839
|
// `retrieve` returns `T | undefined` for a missing row; throwing only here
|
|
812
840
|
// made the obvious read ("does this row exist?") a hard edge that an agent
|
|
813
841
|
// had to wrap in try/catch. Both transports agree: an absent row means absent
|
|
814
842
|
// data. Callers branch on `.data` (the documented `.data?.x` usage).
|
|
815
843
|
// Normalize a miss to `undefined` (the server may send `null` or omit it).
|
|
844
|
+
// The row itself is the caller's declared type — see the note in `listModel`
|
|
845
|
+
// on why this transport validates the envelope and not the row.
|
|
816
846
|
const data = (query.data ?? undefined);
|
|
817
|
-
return {
|
|
818
|
-
data,
|
|
819
|
-
stamp: query.stamp ?? 0,
|
|
820
|
-
claims: query.claims ?? [],
|
|
821
|
-
};
|
|
847
|
+
return { data, stamp: query.stamp, claims: query.claims };
|
|
822
848
|
}
|
|
823
849
|
/**
|
|
824
850
|
* A single-operation mutation over the model-scoped routes — the canonical
|
|
@@ -884,13 +910,12 @@ export function createHttpTransport(options) {
|
|
|
884
910
|
recordCoordinationConflict(error, clientTxId, [{ model: modelName, id }]);
|
|
885
911
|
throw error;
|
|
886
912
|
}
|
|
887
|
-
//
|
|
888
|
-
//
|
|
889
|
-
//
|
|
890
|
-
const status = body.status === 'queued' ? 'queued' : 'confirmed';
|
|
913
|
+
// Same contract as `commits.create` above: a non-2xx already threw, so
|
|
914
|
+
// `body` is the success-only receipt union and its settlement status passes
|
|
915
|
+
// through verbatim rather than through a catch-all branch.
|
|
891
916
|
return {
|
|
892
917
|
id: body.serverTxId,
|
|
893
|
-
status,
|
|
918
|
+
status: body.status,
|
|
894
919
|
lastSyncId: body.lastSyncId,
|
|
895
920
|
};
|
|
896
921
|
}
|
|
@@ -904,30 +929,56 @@ export function createHttpTransport(options) {
|
|
|
904
929
|
value.object === 'claim' &&
|
|
905
930
|
typeof value.id === 'string' &&
|
|
906
931
|
typeof value.release === 'function';
|
|
907
|
-
const claimMeta = (options) => options?.meta;
|
|
908
932
|
const acquireClaim = async (params) => {
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
933
|
+
// The row is named by the URL, so `target` carries only the narrowing a
|
|
934
|
+
// claim adds below it. Sending it is what makes a field-scoped claim
|
|
935
|
+
// actually field-scoped: the server's conflict rule reads `path`,
|
|
936
|
+
// `range`, and `field`, so a claim that keeps them client-side takes a
|
|
937
|
+
// lease on the whole row while its handle says otherwise.
|
|
938
|
+
// Projected in one move rather than member by member. The member-by-member
|
|
939
|
+
// version is how `field` came to be sent while `fields` was not, which
|
|
940
|
+
// left a set-scoped claim silently holding the whole row.
|
|
941
|
+
const narrowing = subTarget(params);
|
|
942
|
+
// Typed as the request contract rather than a bare literal — the omission
|
|
943
|
+
// above was invisible for exactly as long as this was an untyped object.
|
|
944
|
+
const request = {
|
|
945
|
+
description: claimDescription(params),
|
|
946
|
+
...(params.ttl !== undefined ? { ttl: params.ttl } : {}),
|
|
947
|
+
// The caller's `meta` is the declared shape; the body is wire-shaped,
|
|
948
|
+
// so it crosses through the same conversion `subTarget` used above.
|
|
949
|
+
...(params.meta !== undefined ? { meta: wireMeta(params.meta) } : {}),
|
|
950
|
+
...(Object.keys(narrowing).length > 0 ? { target: narrowing } : {}),
|
|
951
|
+
// `queue` (default true) → queue behind the holder; false → fail-fast
|
|
952
|
+
// with AbloClaimedError (work-distribution dedup).
|
|
953
|
+
queue: params.queue ?? true,
|
|
954
|
+
};
|
|
955
|
+
const body = await requestJson(claimPath(params.id), { method: 'POST', body: JSON.stringify(request) }, claimAcquireResponseSchema);
|
|
956
|
+
// HELD BY ADR 0018 (`docs/decisions/0018-the-premise-is-the-missing-structure.md`).
|
|
957
|
+
// Raising a successful outcome as an exception is wrong, and this stays
|
|
958
|
+
// wrong on purpose: it is the symptom the ADR is derived from, so removing
|
|
959
|
+
// the smell without the structure would hide the reason for the work.
|
|
960
|
+
//
|
|
961
|
+
// Being queued is not a transport failure — it is "I cannot establish my
|
|
962
|
+
// premise yet." Premise is the structure the codebase approximates six
|
|
963
|
+
// ways and has never modelled, which is why this was never solvable here.
|
|
964
|
+
// A client-side polling loop is specifically ruled out: it would become a
|
|
965
|
+
// shape callers build against, and the better it worked the harder it
|
|
966
|
+
// would be to replace with the status this should be.
|
|
967
|
+
//
|
|
968
|
+
// A caller that must wait today can poll `GET /v1/claims/{claimId}`, which
|
|
969
|
+
// is served and published. That primitive is safe to build on; a blessed
|
|
970
|
+
// wait algorithm here is not.
|
|
971
|
+
// The two arms are told apart by `status`, which only the queued reply
|
|
972
|
+
// carries — see `claimAcquireResponseSchema` for why they cannot be a
|
|
973
|
+
// discriminated union.
|
|
974
|
+
if ('status' in body) {
|
|
975
|
+
throw new AbloClaimedError(`Target ${name}/${params.id} is held; queued at position ${body.position}. ` +
|
|
976
|
+
`Poll \`GET /v1/claims/{claimId}\` for the grant — the HTTP client does not await it.`, { code: 'claim_queued' });
|
|
923
977
|
}
|
|
924
|
-
|
|
925
|
-
// the other id shapes the response may carry.
|
|
926
|
-
const id = body.claim?.id ?? body.id ?? body.claimId ?? createClaimId();
|
|
927
|
-
const fenceToken = body.claim?.fenceToken;
|
|
978
|
+
const { id, fenceToken } = body.claim;
|
|
928
979
|
return fenceToken !== undefined ? { id, fenceToken } : { id };
|
|
929
980
|
};
|
|
930
|
-
const releaseClaim = (params) =>
|
|
981
|
+
const releaseClaim = (params) => requestRaw(claimPath(isClaimHandle(params) ? params.target.id : params.id), {
|
|
931
982
|
method: 'DELETE',
|
|
932
983
|
}).then(() => undefined);
|
|
933
984
|
// One beat on the held lease. A lapsed lease answers `claim_lost`
|
|
@@ -941,7 +992,7 @@ export function createHttpTransport(options) {
|
|
|
941
992
|
...(options.ttl !== undefined ? { ttl: options.ttl } : {}),
|
|
942
993
|
...(options.details !== undefined ? { details: options.details } : {}),
|
|
943
994
|
}),
|
|
944
|
-
});
|
|
995
|
+
}, claimHeartbeatReplySchema);
|
|
945
996
|
return heldHeartbeatReply(reply, `claim ${claimId} on ${name}/${id}`);
|
|
946
997
|
};
|
|
947
998
|
async function claimImpl(params) {
|
|
@@ -952,7 +1003,7 @@ export function createHttpTransport(options) {
|
|
|
952
1003
|
model: name,
|
|
953
1004
|
id: params.id,
|
|
954
1005
|
...(params.field ? { field: params.field } : {}),
|
|
955
|
-
|
|
1006
|
+
description: claimDescription(params),
|
|
956
1007
|
});
|
|
957
1008
|
const { data, stamp } = await retrieveModel(name, { id: params.id });
|
|
958
1009
|
// A held claim hands back a snapshot; the typed `HeldClaim.data` is `T`.
|
|
@@ -971,12 +1022,12 @@ export function createHttpTransport(options) {
|
|
|
971
1022
|
return beat;
|
|
972
1023
|
};
|
|
973
1024
|
// Opt-in auto-heartbeat — the background-worker cadence. The stateless
|
|
974
|
-
// HTTP claim defaults to the server's
|
|
1025
|
+
// HTTP claim defaults to the server's acquire window when no TTL
|
|
975
1026
|
// was requested, so the default cadence lands at 20s beats.
|
|
976
1027
|
const stopHeartbeatLoop = params.heartbeat
|
|
977
1028
|
? startClaimHeartbeatLoop({
|
|
978
1029
|
beat: () => heartbeat(),
|
|
979
|
-
intervalMs: heartbeatCadenceMs(params.ttl !== undefined ? toMs(params.ttl) :
|
|
1030
|
+
intervalMs: heartbeatCadenceMs(params.ttl !== undefined ? toMs(params.ttl) : DEFAULT_CLAIM_TTL_MS, params.heartbeat),
|
|
980
1031
|
...(params.onHeartbeatLost ? { onLost: params.onHeartbeatLost } : {}),
|
|
981
1032
|
})
|
|
982
1033
|
: undefined;
|
|
@@ -984,20 +1035,22 @@ export function createHttpTransport(options) {
|
|
|
984
1035
|
stopHeartbeatLoop?.();
|
|
985
1036
|
return releaseClaim(params);
|
|
986
1037
|
};
|
|
1038
|
+
// The handle handed back is a public claim, so its `meta` is the declared
|
|
1039
|
+
// shape — the same crossing the two decodes above make, spelled the same
|
|
1040
|
+
// way. `subTarget` is wire-shaped by contract, including here, where the
|
|
1041
|
+
// value happens to have started out declared.
|
|
1042
|
+
const { meta, ...narrowed } = subTarget(params);
|
|
987
1043
|
return {
|
|
988
1044
|
object: 'claim',
|
|
989
1045
|
id: claimId,
|
|
990
1046
|
readAt: stamp,
|
|
991
1047
|
...(fenceToken !== undefined ? { fenceToken } : {}),
|
|
992
1048
|
target: {
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
...(
|
|
996
|
-
...(params.path ? { path: params.path } : {}),
|
|
997
|
-
...(params.range ? { range: params.range } : {}),
|
|
998
|
-
...(claimMeta(params) ? { meta: claimMeta(params) } : {}),
|
|
1049
|
+
...streamTarget({ model: name, id: params.id }),
|
|
1050
|
+
...narrowed,
|
|
1051
|
+
...(meta !== undefined ? { meta: declaredMeta(meta) } : {}),
|
|
999
1052
|
},
|
|
1000
|
-
description: params
|
|
1053
|
+
description: claimDescription(params),
|
|
1001
1054
|
data,
|
|
1002
1055
|
release,
|
|
1003
1056
|
revoke: () => {
|
|
@@ -1007,7 +1060,7 @@ export function createHttpTransport(options) {
|
|
|
1007
1060
|
[Symbol.asyncDispose]: release,
|
|
1008
1061
|
};
|
|
1009
1062
|
}
|
|
1010
|
-
const claimsForEntity =
|
|
1063
|
+
const claimsForEntity = (params) => requestJson(`/v1/claims?model=${encodeURIComponent(name)}&id=${encodeURIComponent(params.id)}${params.field ? `&field=${encodeURIComponent(params.field)}` : ''}`, { method: 'GET' }, claimListResponseSchema);
|
|
1011
1064
|
const claim = Object.assign(claimImpl, {
|
|
1012
1065
|
release: releaseClaim,
|
|
1013
1066
|
state: async (params) => {
|
|
@@ -1023,7 +1076,7 @@ export function createHttpTransport(options) {
|
|
|
1023
1076
|
};
|
|
1024
1077
|
},
|
|
1025
1078
|
reorder: async (params) => {
|
|
1026
|
-
await
|
|
1079
|
+
await requestRaw(`${claimPath(params.id)}/reorder`, {
|
|
1027
1080
|
method: 'POST',
|
|
1028
1081
|
// The reorder route's payload is `{ heldBy, claimId }[]` — a Claim's id
|
|
1029
1082
|
// is the claimId.
|
|
@@ -1136,6 +1189,29 @@ export function createHttpTransport(options) {
|
|
|
1136
1189
|
return mutateModel('delete', name, params.id, undefined, options);
|
|
1137
1190
|
});
|
|
1138
1191
|
},
|
|
1192
|
+
async track(params) {
|
|
1193
|
+
const dependency = {
|
|
1194
|
+
model: name.toLowerCase(),
|
|
1195
|
+
id: params.id,
|
|
1196
|
+
...(params.readAt !== undefined ? { readAt: params.readAt } : {}),
|
|
1197
|
+
};
|
|
1198
|
+
// A track carries no write, so it rides the commit lane as a
|
|
1199
|
+
// zero-operation body — the shape `/v1/commits` accepts for registering
|
|
1200
|
+
// a premise without one. Going through the same durable lane as every
|
|
1201
|
+
// other commit means a disconnect replays the registration rather than
|
|
1202
|
+
// dropping it, and a notification that had already fired is not lost to
|
|
1203
|
+
// a retry.
|
|
1204
|
+
const body = await dispatchHttpCommit({
|
|
1205
|
+
path: '/v1/commits',
|
|
1206
|
+
method: 'POST',
|
|
1207
|
+
idempotencyKey: createClientTxId(),
|
|
1208
|
+
body: { track: [dependency] },
|
|
1209
|
+
wait: 'confirmed',
|
|
1210
|
+
});
|
|
1211
|
+
return body.notifications && body.notifications.length > 0
|
|
1212
|
+
? { notifications: body.notifications }
|
|
1213
|
+
: {};
|
|
1214
|
+
},
|
|
1139
1215
|
};
|
|
1140
1216
|
}
|
|
1141
1217
|
return {
|
|
@@ -1158,11 +1234,21 @@ export function createHttpTransport(options) {
|
|
|
1158
1234
|
if (!apiKey) {
|
|
1159
1235
|
throw new AbloAuthenticationError('sessions.create requires a secret (sk_) API key — call it from your backend, not the browser.', { code: 'apikey_missing' });
|
|
1160
1236
|
}
|
|
1237
|
+
// A transport built without a schema has no way to translate `can`'s
|
|
1238
|
+
// schema keys into the type names the server gates on. Minting anyway
|
|
1239
|
+
// would spell every override wrong and surface as
|
|
1240
|
+
// `capability_scope_denied` on the agent's first write, so refuse here
|
|
1241
|
+
// instead of guessing.
|
|
1242
|
+
if (!options.modelTypenames) {
|
|
1243
|
+
throw new AbloValidationError('sessions.create needs the schema this client is bound to. Construct it ' +
|
|
1244
|
+
"through Ablo({ schema, apiKey, transport: 'http' }) rather than the " +
|
|
1245
|
+
'bare transport.', { code: 'invalid_options', param: 'schema' });
|
|
1246
|
+
}
|
|
1161
1247
|
return mintSession(params, {
|
|
1162
1248
|
apiKey,
|
|
1163
1249
|
baseUrl: apiBaseUrl,
|
|
1250
|
+
modelTypenames: options.modelTypenames,
|
|
1164
1251
|
...(options.fetch ? { fetch: options.fetch } : {}),
|
|
1165
|
-
...(options.modelTypenames ? { modelTypenames: options.modelTypenames } : {}),
|
|
1166
1252
|
});
|
|
1167
1253
|
},
|
|
1168
1254
|
},
|
|
@@ -23,11 +23,12 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import { z } from 'zod';
|
|
25
25
|
import { type AuthTokenGetter } from '../auth/credentialSource.js';
|
|
26
|
+
import { type Logger } from '../logger.js';
|
|
26
27
|
/**
|
|
27
28
|
* The complete set of probe outcomes. Each value carries both reachability and
|
|
28
|
-
* credential state, so
|
|
29
|
+
* credential state, so the connection state machine can branch on one exhaustive
|
|
29
30
|
* discriminant instead of piecing the situation together from several booleans.
|
|
30
|
-
* It mirrors the
|
|
31
|
+
* It mirrors the `RecoveryClass` taxonomy at the connectivity layer.
|
|
31
32
|
*/
|
|
32
33
|
export declare const PROBE_OUTCOMES: readonly ["reachable", "unreachable", "session_expired", "credential_stale", "auth_blocked"];
|
|
33
34
|
/** Zod enum derived from {@link PROBE_OUTCOMES}. */
|
|
@@ -69,11 +70,13 @@ export interface NetworkProbeOptions {
|
|
|
69
70
|
getAuthToken?: AuthTokenGetter;
|
|
70
71
|
/** Compatibility fallback for callers with a copied token string. */
|
|
71
72
|
authToken?: string | null;
|
|
73
|
+
/** Where probe outcomes are logged. Defaults to silent. */
|
|
74
|
+
logger?: Logger;
|
|
72
75
|
}
|
|
73
76
|
/**
|
|
74
77
|
* Probes the sync server with a lightweight HEAD request, returning both
|
|
75
|
-
* reachability and session status in a single call so
|
|
76
|
-
* can pick the right
|
|
78
|
+
* reachability and session status in a single call so the connection state
|
|
79
|
+
* machine can pick the right transition without guessing.
|
|
77
80
|
*
|
|
78
81
|
* @param input The sync-server base URL (an HTTP or WS scheme is accepted), or
|
|
79
82
|
* an options bag. A bare string is also accepted.
|
|
@@ -22,15 +22,15 @@
|
|
|
22
22
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine
|
|
23
23
|
*/
|
|
24
24
|
import { z } from 'zod';
|
|
25
|
-
import { getContext } from '../context.js';
|
|
26
25
|
import { classifyRecovery } from '../errors.js';
|
|
27
26
|
import { withAuthHeaders } from '../auth/credentialSource.js';
|
|
28
|
-
import { ABLO_DEFAULT_BASE_URL } from '../
|
|
27
|
+
import { ABLO_DEFAULT_BASE_URL } from '../auth/hostedEndpoints.js';
|
|
28
|
+
import { noopLogger } from '../logger.js';
|
|
29
29
|
/**
|
|
30
30
|
* The complete set of probe outcomes. Each value carries both reachability and
|
|
31
|
-
* credential state, so
|
|
31
|
+
* credential state, so the connection state machine can branch on one exhaustive
|
|
32
32
|
* discriminant instead of piecing the situation together from several booleans.
|
|
33
|
-
* It mirrors the
|
|
33
|
+
* It mirrors the `RecoveryClass` taxonomy at the connectivity layer.
|
|
34
34
|
*/
|
|
35
35
|
export const PROBE_OUTCOMES = [
|
|
36
36
|
/** Server reachable and the access credential is currently valid. */
|
|
@@ -72,8 +72,8 @@ function resolveProbeUrl(baseUrl) {
|
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
74
|
* Probes the sync server with a lightweight HEAD request, returning both
|
|
75
|
-
* reachability and session status in a single call so
|
|
76
|
-
* can pick the right
|
|
75
|
+
* reachability and session status in a single call so the connection state
|
|
76
|
+
* machine can pick the right transition without guessing.
|
|
77
77
|
*
|
|
78
78
|
* @param input The sync-server base URL (an HTTP or WS scheme is accepted), or
|
|
79
79
|
* an options bag. A bare string is also accepted.
|
|
@@ -82,6 +82,7 @@ export async function probeNetwork(input) {
|
|
|
82
82
|
const baseUrl = typeof input === 'string' ? input : input?.baseUrl;
|
|
83
83
|
const getAuthToken = typeof input === 'string' ? undefined : input?.getAuthToken;
|
|
84
84
|
const authToken = typeof input === 'string' ? undefined : input?.authToken;
|
|
85
|
+
const logger = (typeof input === 'string' ? undefined : input?.logger) ?? noopLogger;
|
|
85
86
|
const url = resolveProbeUrl(baseUrl);
|
|
86
87
|
// Fast-fail: if navigator.onLine is false, skip the probe entirely. This is
|
|
87
88
|
// the one case where navigator.onLine is reliable (MDN: "false means
|
|
@@ -115,14 +116,14 @@ export async function probeNetwork(input) {
|
|
|
115
116
|
const recovery = classifyRecovery(authFailure);
|
|
116
117
|
switch (recovery) {
|
|
117
118
|
case 'session_expiry':
|
|
118
|
-
|
|
119
|
+
logger.info('[NetworkProbe] Server reachable, login expired', {
|
|
119
120
|
status: response.status,
|
|
120
121
|
code: authFailure,
|
|
121
122
|
latencyMs,
|
|
122
123
|
});
|
|
123
124
|
return { outcome: 'session_expired', latencyMs };
|
|
124
125
|
case 'access_credential_expiry':
|
|
125
|
-
|
|
126
|
+
logger.info('[NetworkProbe] Server reachable, access key stale — will re-mint', {
|
|
126
127
|
status: response.status,
|
|
127
128
|
code: authFailure,
|
|
128
129
|
latencyMs,
|
|
@@ -136,7 +137,7 @@ export async function probeNetwork(input) {
|
|
|
136
137
|
// Re-authenticating re-mints the same rejected credential and
|
|
137
138
|
// retrying will not help, so stop rather than reconnect-loop or sign
|
|
138
139
|
// the user out.
|
|
139
|
-
|
|
140
|
+
logger.debug('[NetworkProbe] Reachable but auth-blocked (non-retryable, non-expiry)', {
|
|
140
141
|
status: response.status,
|
|
141
142
|
code: authFailure,
|
|
142
143
|
recovery,
|
|
@@ -168,7 +169,7 @@ export async function probeNetwork(input) {
|
|
|
168
169
|
// counter falls through to `auth_blocked` (stop) — still never a spurious
|
|
169
170
|
// logout. The invariant holds: null is the only terminal path, never a
|
|
170
171
|
// bare 401.
|
|
171
|
-
|
|
172
|
+
logger.info('[NetworkProbe] Server reachable, bare 401 — re-mint (not sign-out)', {
|
|
172
173
|
latencyMs,
|
|
173
174
|
});
|
|
174
175
|
return { outcome: 'credential_stale', latencyMs };
|
|
@@ -178,14 +179,14 @@ export async function probeNetwork(input) {
|
|
|
178
179
|
// expected 204; log a warning so misconfigurations surface instead of
|
|
179
180
|
// silently passing.
|
|
180
181
|
if (response.status < 200 || response.status >= 300) {
|
|
181
|
-
|
|
182
|
+
logger.debug('[NetworkProbe] Unexpected probe response', {
|
|
182
183
|
status: response.status,
|
|
183
184
|
url,
|
|
184
185
|
latencyMs,
|
|
185
186
|
});
|
|
186
187
|
}
|
|
187
188
|
else {
|
|
188
|
-
|
|
189
|
+
logger.debug('[NetworkProbe] Server reachable, credential valid', {
|
|
189
190
|
status: response.status,
|
|
190
191
|
latencyMs,
|
|
191
192
|
});
|
|
@@ -195,7 +196,7 @@ export async function probeNetwork(input) {
|
|
|
195
196
|
catch (error) {
|
|
196
197
|
clearTimeout(timeout);
|
|
197
198
|
const isAbort = error instanceof DOMException && error.name === 'AbortError';
|
|
198
|
-
|
|
199
|
+
logger.info('[NetworkProbe] Probe failed', {
|
|
199
200
|
reason: isAbort ? 'timeout' : error.message,
|
|
200
201
|
});
|
|
201
202
|
return { outcome: 'unreachable', latencyMs: null };
|