@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
|
@@ -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, claimHeartbeatBatchReplySchema, claimHeartbeatReplySchema, claimListResponseSchema, claimStateSchema, } 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, resolveHeartbeatPlan, 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, partName } from '../coordination/schema.js';
|
|
38
|
+
import { subTarget, streamTarget, batchFence, fenceTokenFor, } 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');
|
|
@@ -667,7 +698,7 @@ export function createHttpTransport(options) {
|
|
|
667
698
|
? crypto.randomUUID()
|
|
668
699
|
: `id_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
669
700
|
}
|
|
670
|
-
function normalizeCommitOperation(op, defaults,
|
|
701
|
+
function normalizeCommitOperation(op, defaults, fence) {
|
|
671
702
|
return {
|
|
672
703
|
action: op.action,
|
|
673
704
|
model: op.model,
|
|
@@ -676,18 +707,16 @@ export function createHttpTransport(options) {
|
|
|
676
707
|
transactionId: op.transactionId ?? null,
|
|
677
708
|
readAt: op.readAt ?? defaults.readAt ?? null,
|
|
678
709
|
onStale: op.onStale ?? defaults.onStale ?? null,
|
|
679
|
-
|
|
680
|
-
// how it supplies the batch `readAt`.
|
|
681
|
-
fenceToken: op.fenceToken ?? fenceToken ?? null,
|
|
710
|
+
fenceToken: op.fenceToken ?? fenceTokenFor(fence, op.model, op.id ?? null),
|
|
682
711
|
};
|
|
683
712
|
}
|
|
684
|
-
function normalizeCommitOperations(commitOptions,
|
|
713
|
+
function normalizeCommitOperations(commitOptions, fence) {
|
|
685
714
|
if (commitOptions.operations.length === 0) {
|
|
686
715
|
throw new AbloValidationError('Commit requires a non-empty `operations` array.', {
|
|
687
716
|
code: 'commit_operation_required',
|
|
688
717
|
});
|
|
689
718
|
}
|
|
690
|
-
return commitOptions.operations.map((op) => normalizeCommitOperation(op, commitOptions,
|
|
719
|
+
return commitOptions.operations.map((op) => normalizeCommitOperation(op, commitOptions, fence));
|
|
691
720
|
}
|
|
692
721
|
async function listClaimState(target) {
|
|
693
722
|
const params = new URLSearchParams();
|
|
@@ -698,14 +727,107 @@ export function createHttpTransport(options) {
|
|
|
698
727
|
if (target?.field)
|
|
699
728
|
params.set('field', target.field);
|
|
700
729
|
const suffix = params.toString();
|
|
701
|
-
const body = await requestJson(`/v1/claims${suffix ? `?${suffix}` : ''}`, {
|
|
702
|
-
|
|
703
|
-
|
|
730
|
+
const body = await requestJson(`/v1/claims${suffix ? `?${suffix}` : ''}`, { method: 'GET' }, claimListResponseSchema);
|
|
731
|
+
// One list, one resource in two states: holders and waiters are told
|
|
732
|
+
// apart by each entry's `status`, not by bespoke envelope members.
|
|
704
733
|
return {
|
|
705
|
-
active: body.
|
|
706
|
-
queue: body.
|
|
734
|
+
active: body.data.filter((row) => row.status !== 'queued'),
|
|
735
|
+
queue: body.data.filter((row) => row.status === 'queued'),
|
|
707
736
|
};
|
|
708
737
|
}
|
|
738
|
+
// The claim-ticket surface: everything a caller does holding only a
|
|
739
|
+
// `claimId` — which is all a queued acquire leaves in its hand. Each method
|
|
740
|
+
// is a thin cast of its route; the reply schemas are the wire's own, so the
|
|
741
|
+
// surface cannot describe a response the server does not send.
|
|
742
|
+
const claims = {
|
|
743
|
+
retrieve({ claimId }) {
|
|
744
|
+
return requestJson(`/v1/claims/${encodeURIComponent(claimId)}`, { method: 'GET' }, claimStateSchema);
|
|
745
|
+
},
|
|
746
|
+
heartbeat({ claimId, ttl }) {
|
|
747
|
+
return requestJson(`/v1/claims/${encodeURIComponent(claimId)}/heartbeat`, {
|
|
748
|
+
method: 'POST',
|
|
749
|
+
body: JSON.stringify(ttl !== undefined ? { ttl } : {}),
|
|
750
|
+
}, claimHeartbeatReplySchema);
|
|
751
|
+
},
|
|
752
|
+
async heartbeatAll(options) {
|
|
753
|
+
const reply = await requestJson('/v1/claims/heartbeat', {
|
|
754
|
+
method: 'POST',
|
|
755
|
+
body: JSON.stringify(options?.ttl !== undefined ? { ttl: options.ttl } : {}),
|
|
756
|
+
}, claimHeartbeatBatchReplySchema);
|
|
757
|
+
return reply.results;
|
|
758
|
+
},
|
|
759
|
+
async release({ claimId }) {
|
|
760
|
+
await requestRaw(`/v1/claims/${encodeURIComponent(claimId)}`, {
|
|
761
|
+
method: 'DELETE',
|
|
762
|
+
});
|
|
763
|
+
},
|
|
764
|
+
};
|
|
765
|
+
// How the stateless client waits its turn. The queued slot is real server
|
|
766
|
+
// state, so one heartbeat per tick does both jobs: it refreshes this
|
|
767
|
+
// waiter's slot and reports the line's answer — `queued` (still waiting) or
|
|
768
|
+
// `held` (granted). The first check comes quickly because most holds are a
|
|
769
|
+
// short claim→write→release; after that the cadence relaxes, with jitter so
|
|
770
|
+
// a fleet of waiters doesn't beat in step.
|
|
771
|
+
const GRANT_POLL_FIRST_MS = 250;
|
|
772
|
+
const GRANT_POLL_INTERVAL_MS = 1_000;
|
|
773
|
+
// An abort cuts the sleep short so the wait ends within a tick of the
|
|
774
|
+
// signal, not at the next scheduled beat.
|
|
775
|
+
const sleep = (ms, signal) => new Promise((resolve) => {
|
|
776
|
+
const done = () => {
|
|
777
|
+
clearTimeout(timer);
|
|
778
|
+
signal?.removeEventListener('abort', done);
|
|
779
|
+
resolve();
|
|
780
|
+
};
|
|
781
|
+
const timer = setTimeout(done, ms);
|
|
782
|
+
signal?.addEventListener('abort', done, { once: true });
|
|
783
|
+
});
|
|
784
|
+
async function awaitGrantOverHttp(targetLabel, queued, options) {
|
|
785
|
+
// The queued reply is a claim resource in its waiting state, so the
|
|
786
|
+
// handle is its `id` — same rule as the 201 and the poll.
|
|
787
|
+
const claimId = queued.id;
|
|
788
|
+
const { signal } = options;
|
|
789
|
+
// Leave the line before rejecting: an abandoned slot would otherwise sit
|
|
790
|
+
// in the queue until its TTL lapses, stalling every waiter behind it.
|
|
791
|
+
const rejectAndLeave = async (error) => {
|
|
792
|
+
await claims.release({ claimId }).catch(() => { });
|
|
793
|
+
throw error;
|
|
794
|
+
};
|
|
795
|
+
if (options.maxQueueDepth !== undefined && queued.position >= options.maxQueueDepth) {
|
|
796
|
+
return rejectAndLeave(new AbloClaimedError(`Claim queue for ${targetLabel} is ${queued.position} deep (max ${options.maxQueueDepth}).`, { code: 'queue_too_deep' }));
|
|
797
|
+
}
|
|
798
|
+
const deadline = options.waitTimeoutMs !== undefined ? Date.now() + options.waitTimeoutMs : undefined;
|
|
799
|
+
let delay = GRANT_POLL_FIRST_MS;
|
|
800
|
+
for (;;) {
|
|
801
|
+
if (signal?.aborted) {
|
|
802
|
+
return rejectAndLeave(new AbloClaimedError(`The wait for the claim on ${targetLabel} was aborted before the grant arrived.`, { code: 'claim_wait_aborted' }));
|
|
803
|
+
}
|
|
804
|
+
if (deadline !== undefined && Date.now() >= deadline) {
|
|
805
|
+
return rejectAndLeave(new AbloClaimedError(`Timed out after ${options.waitTimeoutMs}ms waiting for the queue grant on ${targetLabel}.`, { code: 'grant_timeout' }));
|
|
806
|
+
}
|
|
807
|
+
await sleep(deadline !== undefined ? Math.min(delay, Math.max(0, deadline - Date.now())) : delay, signal);
|
|
808
|
+
if (signal?.aborted) {
|
|
809
|
+
return rejectAndLeave(new AbloClaimedError(`The wait for the claim on ${targetLabel} was aborted before the grant arrived.`, { code: 'claim_wait_aborted' }));
|
|
810
|
+
}
|
|
811
|
+
delay = GRANT_POLL_INTERVAL_MS * (0.85 + Math.random() * 0.3);
|
|
812
|
+
// A lease that ended answers the beat with 409 `claim_lost`, which the
|
|
813
|
+
// wire error mapping raises as AbloClaimedError before this reads
|
|
814
|
+
// anything — the wait fails with the loss, as the socket wait does.
|
|
815
|
+
const beat = await claims.heartbeat({ claimId });
|
|
816
|
+
if (beat.status !== 'held')
|
|
817
|
+
continue;
|
|
818
|
+
// Granted. The heartbeat ack does not carry the fence token — the claim
|
|
819
|
+
// state does, server-stamped at grant.
|
|
820
|
+
const state = await claims.retrieve({ claimId });
|
|
821
|
+
if (state.status !== 'active') {
|
|
822
|
+
return rejectAndLeave(new AbloClaimedError(`Claim lost while queued for ${targetLabel}.`, {
|
|
823
|
+
code: 'claim_lost',
|
|
824
|
+
}));
|
|
825
|
+
}
|
|
826
|
+
return state.fenceToken !== undefined
|
|
827
|
+
? { id: claimId, fenceToken: state.fenceToken }
|
|
828
|
+
: { id: claimId };
|
|
829
|
+
}
|
|
830
|
+
}
|
|
709
831
|
async function applyClaimedPolicy(target, options, defaultPolicy = 'return') {
|
|
710
832
|
const policy = options?.ifClaimed ?? defaultPolicy;
|
|
711
833
|
if (policy === 'return')
|
|
@@ -734,7 +856,7 @@ export function createHttpTransport(options) {
|
|
|
734
856
|
...commitOptions,
|
|
735
857
|
readAt: commitOptions.readAt ?? claim?.readAt ?? null,
|
|
736
858
|
onStale: commitOptions.onStale ?? (claim?.readAt !== undefined ? 'reject' : null),
|
|
737
|
-
}, claim?.fenceToken
|
|
859
|
+
}, batchFence(claim?.target, claim?.fenceToken));
|
|
738
860
|
const requestBody = {
|
|
739
861
|
operations,
|
|
740
862
|
reads: commitOptions.reads,
|
|
@@ -761,14 +883,14 @@ export function createHttpTransport(options) {
|
|
|
761
883
|
})));
|
|
762
884
|
throw error;
|
|
763
885
|
}
|
|
764
|
-
// `requestJson` throws via `translateHttpError` on any non-2xx,
|
|
765
|
-
//
|
|
766
|
-
//
|
|
767
|
-
//
|
|
768
|
-
|
|
886
|
+
// `requestJson` throws via `translateHttpError` on any non-2xx, so
|
|
887
|
+
// reaching here implies success and `body` is already the success-only
|
|
888
|
+
// receipt union — a rejection is a separate type that never arrives here.
|
|
889
|
+
// The settlement status therefore passes through verbatim: no branch may
|
|
890
|
+
// collapse a state the server reported into a different one.
|
|
769
891
|
return {
|
|
770
892
|
id: body.id ?? body.clientTxId,
|
|
771
|
-
status,
|
|
893
|
+
status: body.status,
|
|
772
894
|
lastSyncId: body.lastSyncId,
|
|
773
895
|
...(body.notifications && body.notifications.length > 0
|
|
774
896
|
? { notifications: body.notifications }
|
|
@@ -799,26 +921,26 @@ export function createHttpTransport(options) {
|
|
|
799
921
|
}
|
|
800
922
|
}
|
|
801
923
|
const qs = params.toString();
|
|
802
|
-
const res = await requestJson(`/v1/models/${encodeURIComponent(modelName)}${qs ? `?${qs}` : ''}`, { method: 'GET' });
|
|
803
|
-
|
|
924
|
+
const res = await requestJson(`/v1/models/${encodeURIComponent(modelName)}${qs ? `?${qs}` : ''}`, { method: 'GET' }, modelListResponseSchema);
|
|
925
|
+
// The envelope is checked; the rows are not, and cannot be here. This
|
|
926
|
+
// transport is schema-agnostic — it moves rows for whatever schema the
|
|
927
|
+
// caller declared, and `T` is that declaration. Row validation belongs to
|
|
928
|
+
// the typed facade above, which holds the model's schema.
|
|
929
|
+
return res.data;
|
|
804
930
|
}
|
|
805
931
|
async function retrieveModel(modelName, params) {
|
|
806
932
|
await applyClaimedPolicy({ model: modelName, id: params.id }, params);
|
|
807
|
-
const query = await requestJson(`/v1/models/${encodeURIComponent(modelName)}/${encodeURIComponent(params.id)}`, {
|
|
808
|
-
method: 'GET',
|
|
809
|
-
});
|
|
933
|
+
const query = await requestJson(`/v1/models/${encodeURIComponent(modelName)}/${encodeURIComponent(params.id)}`, { method: 'GET' }, modelReadResponseSchema);
|
|
810
934
|
// A miss is `data: undefined`, not a thrown error. The WebSocket client's
|
|
811
935
|
// `retrieve` returns `T | undefined` for a missing row; throwing only here
|
|
812
936
|
// made the obvious read ("does this row exist?") a hard edge that an agent
|
|
813
937
|
// had to wrap in try/catch. Both transports agree: an absent row means absent
|
|
814
938
|
// data. Callers branch on `.data` (the documented `.data?.x` usage).
|
|
815
939
|
// Normalize a miss to `undefined` (the server may send `null` or omit it).
|
|
940
|
+
// The row itself is the caller's declared type — see the note in `listModel`
|
|
941
|
+
// on why this transport validates the envelope and not the row.
|
|
816
942
|
const data = (query.data ?? undefined);
|
|
817
|
-
return {
|
|
818
|
-
data,
|
|
819
|
-
stamp: query.stamp ?? 0,
|
|
820
|
-
claims: query.claims ?? [],
|
|
821
|
-
};
|
|
943
|
+
return { data, stamp: query.stamp, claims: query.claims };
|
|
822
944
|
}
|
|
823
945
|
/**
|
|
824
946
|
* A single-operation mutation over the model-scoped routes — the canonical
|
|
@@ -884,13 +1006,12 @@ export function createHttpTransport(options) {
|
|
|
884
1006
|
recordCoordinationConflict(error, clientTxId, [{ model: modelName, id }]);
|
|
885
1007
|
throw error;
|
|
886
1008
|
}
|
|
887
|
-
//
|
|
888
|
-
//
|
|
889
|
-
//
|
|
890
|
-
const status = body.status === 'queued' ? 'queued' : 'confirmed';
|
|
1009
|
+
// Same contract as `commits.create` above: a non-2xx already threw, so
|
|
1010
|
+
// `body` is the success-only receipt union and its settlement status passes
|
|
1011
|
+
// through verbatim rather than through a catch-all branch.
|
|
891
1012
|
return {
|
|
892
1013
|
id: body.serverTxId,
|
|
893
|
-
status,
|
|
1014
|
+
status: body.status,
|
|
894
1015
|
lastSyncId: body.lastSyncId,
|
|
895
1016
|
};
|
|
896
1017
|
}
|
|
@@ -904,30 +1025,47 @@ export function createHttpTransport(options) {
|
|
|
904
1025
|
value.object === 'claim' &&
|
|
905
1026
|
typeof value.id === 'string' &&
|
|
906
1027
|
typeof value.release === 'function';
|
|
907
|
-
const claimMeta = (options) => options?.meta;
|
|
908
1028
|
const acquireClaim = async (params) => {
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
1029
|
+
// The row is named by the URL, so `target` carries only the narrowing a
|
|
1030
|
+
// claim adds below it. Sending it is what makes a field-scoped claim
|
|
1031
|
+
// actually field-scoped: the server's conflict rule reads `path`,
|
|
1032
|
+
// `range`, and `field`, so a claim that keeps them client-side takes a
|
|
1033
|
+
// lease on the whole row while its handle says otherwise.
|
|
1034
|
+
// Projected in one move rather than member by member. The member-by-member
|
|
1035
|
+
// version is how `field` came to be sent while `fields` was not, which
|
|
1036
|
+
// left a set-scoped claim silently holding the whole row.
|
|
1037
|
+
const narrowing = subTarget(params);
|
|
1038
|
+
// Typed as the request contract rather than a bare literal — the omission
|
|
1039
|
+
// above was invisible for exactly as long as this was an untyped object.
|
|
1040
|
+
const request = {
|
|
1041
|
+
description: claimDescription(params),
|
|
1042
|
+
...(params.ttl !== undefined ? { ttl: params.ttl } : {}),
|
|
1043
|
+
// The caller's `meta` is the declared shape; the body is wire-shaped,
|
|
1044
|
+
// so it crosses through the same conversion `subTarget` used above.
|
|
1045
|
+
...(params.meta !== undefined ? { meta: wireMeta(params.meta) } : {}),
|
|
1046
|
+
...(Object.keys(narrowing).length > 0 ? { target: narrowing } : {}),
|
|
1047
|
+
// `queue` (default true) → queue behind the holder; false → fail-fast
|
|
1048
|
+
// with AbloClaimedError (work-distribution dedup).
|
|
1049
|
+
queue: params.queue ?? true,
|
|
1050
|
+
};
|
|
1051
|
+
const body = await requestJson(claimPath(params.id), { method: 'POST', body: JSON.stringify(request) }, claimAcquireResponseSchema);
|
|
1052
|
+
// One resource, two states, discriminated by `status`. The queued arm
|
|
1053
|
+
// WAITS, exactly as the socket client does: `claim({ id })` means
|
|
1054
|
+
// "serialize me behind the holder" on every transport, and the grant
|
|
1055
|
+
// machinery is the SDK's to own, not a loop each caller re-derives.
|
|
1056
|
+
// (Being queued is still not an error — ADR 0018 — which is precisely
|
|
1057
|
+
// why it no longer surfaces as one here. The `claims` namespace remains
|
|
1058
|
+
// the manual ticket surface.)
|
|
920
1059
|
if (body.status === 'queued') {
|
|
921
|
-
|
|
922
|
-
`The HTTP client cannot await the grant without a WebSocket.`, { code: 'claim_queued' });
|
|
1060
|
+
return awaitGrantOverHttp(`${name}/${params.id}`, body, params);
|
|
923
1061
|
}
|
|
924
|
-
//
|
|
925
|
-
// the
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1062
|
+
// The lease's own fields are mirrored at the top level, the same place
|
|
1063
|
+
// the poll puts them — one reader for both answers.
|
|
1064
|
+
return body.fenceToken !== undefined
|
|
1065
|
+
? { id: body.id, fenceToken: body.fenceToken }
|
|
1066
|
+
: { id: body.id };
|
|
929
1067
|
};
|
|
930
|
-
const releaseClaim = (params) =>
|
|
1068
|
+
const releaseClaim = (params) => requestRaw(claimPath(isClaimHandle(params) ? params.target.id : params.id), {
|
|
931
1069
|
method: 'DELETE',
|
|
932
1070
|
}).then(() => undefined);
|
|
933
1071
|
// One beat on the held lease. A lapsed lease answers `claim_lost`
|
|
@@ -941,18 +1079,35 @@ export function createHttpTransport(options) {
|
|
|
941
1079
|
...(options.ttl !== undefined ? { ttl: options.ttl } : {}),
|
|
942
1080
|
...(options.details !== undefined ? { details: options.details } : {}),
|
|
943
1081
|
}),
|
|
944
|
-
});
|
|
1082
|
+
}, claimHeartbeatReplySchema);
|
|
945
1083
|
return heldHeartbeatReply(reply, `claim ${claimId} on ${name}/${id}`);
|
|
946
1084
|
};
|
|
947
1085
|
async function claimImpl(params) {
|
|
948
|
-
|
|
1086
|
+
let acquired;
|
|
1087
|
+
try {
|
|
1088
|
+
acquired = await acquireClaim(params);
|
|
1089
|
+
}
|
|
1090
|
+
catch (error) {
|
|
1091
|
+
// The try-claim: a held target is an expected outcome of `queue:
|
|
1092
|
+
// false`, not an error — resolve `null` and let the caller move on.
|
|
1093
|
+
// Every other failure (auth, validation, network) stays a rejection,
|
|
1094
|
+
// and the write-site claim path calls `acquireClaim` directly, so a
|
|
1095
|
+
// write that could not claim still fails loudly.
|
|
1096
|
+
if (params.queue === false &&
|
|
1097
|
+
error instanceof AbloClaimedError &&
|
|
1098
|
+
(error.code === 'entity_claimed' || error.code === 'claim_conflict')) {
|
|
1099
|
+
return null;
|
|
1100
|
+
}
|
|
1101
|
+
throw error;
|
|
1102
|
+
}
|
|
1103
|
+
const { id: claimId, fenceToken } = acquired;
|
|
949
1104
|
observability?.captureClaim({
|
|
950
1105
|
phase: 'acquired',
|
|
951
1106
|
claimId,
|
|
952
1107
|
model: name,
|
|
953
1108
|
id: params.id,
|
|
954
|
-
...(params.field ? { field: params.field } : {}),
|
|
955
|
-
|
|
1109
|
+
...(params.field ? { field: partName(params.field) } : {}),
|
|
1110
|
+
description: claimDescription(params),
|
|
956
1111
|
});
|
|
957
1112
|
const { data, stamp } = await retrieveModel(name, { id: params.id });
|
|
958
1113
|
// A held claim hands back a snapshot; the typed `HeldClaim.data` is `T`.
|
|
@@ -961,43 +1116,49 @@ export function createHttpTransport(options) {
|
|
|
961
1116
|
if (data === undefined) {
|
|
962
1117
|
throw new AbloNotFoundError(`Cannot claim ${name}/${params.id}: it does not exist (or is outside this credential's scope).`, [params.id]);
|
|
963
1118
|
}
|
|
1119
|
+
// One reading of the heartbeat options — cadence and callbacks from
|
|
1120
|
+
// whichever spelling the caller used (plan object, shorthand, or the
|
|
1121
|
+
// deprecated flat callbacks).
|
|
1122
|
+
const plan = resolveHeartbeatPlan(params);
|
|
964
1123
|
const heartbeat = async (beatOptions) => {
|
|
965
1124
|
const resolved = resolveHeartbeatOptions(beatOptions);
|
|
966
1125
|
const beat = await heartbeatClaim(params.id, claimId, {
|
|
967
1126
|
ttl: resolved.ttl ?? params.ttl,
|
|
968
1127
|
...(resolved.details !== undefined ? { details: resolved.details } : {}),
|
|
969
1128
|
});
|
|
970
|
-
|
|
1129
|
+
plan.onBeat?.(beat);
|
|
971
1130
|
return beat;
|
|
972
1131
|
};
|
|
973
1132
|
// Opt-in auto-heartbeat — the background-worker cadence. The stateless
|
|
974
|
-
// HTTP claim defaults to the server's
|
|
1133
|
+
// HTTP claim defaults to the server's acquire window when no TTL
|
|
975
1134
|
// was requested, so the default cadence lands at 20s beats.
|
|
976
|
-
const stopHeartbeatLoop =
|
|
1135
|
+
const stopHeartbeatLoop = plan.loop
|
|
977
1136
|
? startClaimHeartbeatLoop({
|
|
978
1137
|
beat: () => heartbeat(),
|
|
979
|
-
intervalMs: heartbeatCadenceMs(params.ttl !== undefined ? toMs(params.ttl) :
|
|
980
|
-
...(
|
|
1138
|
+
intervalMs: heartbeatCadenceMs(params.ttl !== undefined ? toMs(params.ttl) : DEFAULT_CLAIM_TTL_MS, plan.cadence),
|
|
1139
|
+
...(plan.onLost ? { onLost: plan.onLost } : {}),
|
|
981
1140
|
})
|
|
982
1141
|
: undefined;
|
|
983
1142
|
const release = () => {
|
|
984
1143
|
stopHeartbeatLoop?.();
|
|
985
1144
|
return releaseClaim(params);
|
|
986
1145
|
};
|
|
1146
|
+
// The handle handed back is a public claim, so its `meta` is the declared
|
|
1147
|
+
// shape — the same crossing the two decodes above make, spelled the same
|
|
1148
|
+
// way. `subTarget` is wire-shaped by contract, including here, where the
|
|
1149
|
+
// value happens to have started out declared.
|
|
1150
|
+
const { meta, ...narrowed } = subTarget(params);
|
|
987
1151
|
return {
|
|
988
1152
|
object: 'claim',
|
|
989
1153
|
id: claimId,
|
|
990
1154
|
readAt: stamp,
|
|
991
1155
|
...(fenceToken !== undefined ? { fenceToken } : {}),
|
|
992
1156
|
target: {
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
...(
|
|
996
|
-
...(params.path ? { path: params.path } : {}),
|
|
997
|
-
...(params.range ? { range: params.range } : {}),
|
|
998
|
-
...(claimMeta(params) ? { meta: claimMeta(params) } : {}),
|
|
1157
|
+
...streamTarget({ model: name, id: params.id }),
|
|
1158
|
+
...narrowed,
|
|
1159
|
+
...(meta !== undefined ? { meta: declaredMeta(meta) } : {}),
|
|
999
1160
|
},
|
|
1000
|
-
description: params
|
|
1161
|
+
description: claimDescription(params),
|
|
1001
1162
|
data,
|
|
1002
1163
|
release,
|
|
1003
1164
|
revoke: () => {
|
|
@@ -1007,23 +1168,26 @@ export function createHttpTransport(options) {
|
|
|
1007
1168
|
[Symbol.asyncDispose]: release,
|
|
1008
1169
|
};
|
|
1009
1170
|
}
|
|
1010
|
-
const claimsForEntity =
|
|
1171
|
+
const claimsForEntity = (params) => requestJson(`/v1/claims?model=${encodeURIComponent(name)}&id=${encodeURIComponent(params.id)}${params.field ? `&field=${encodeURIComponent(partName(params.field))}` : ''}`, { method: 'GET' }, claimListResponseSchema);
|
|
1011
1172
|
const claim = Object.assign(claimImpl, {
|
|
1012
1173
|
release: releaseClaim,
|
|
1013
1174
|
state: async (params) => {
|
|
1014
1175
|
const res = await claimsForEntity(params);
|
|
1015
|
-
|
|
1176
|
+
// Holders come first in the one list; a `queued` entry is a waiter.
|
|
1177
|
+
const first = res.data.find((row) => row.status !== 'queued');
|
|
1016
1178
|
return first ? claimFromModelClaim(first) : null;
|
|
1017
1179
|
},
|
|
1018
1180
|
queue: async (params) => {
|
|
1019
1181
|
const res = await claimsForEntity(params);
|
|
1020
1182
|
return {
|
|
1021
1183
|
object: 'list',
|
|
1022
|
-
data:
|
|
1184
|
+
data: res.data
|
|
1185
|
+
.filter((row) => row.status === 'queued')
|
|
1186
|
+
.map(claimFromModelClaim),
|
|
1023
1187
|
};
|
|
1024
1188
|
},
|
|
1025
1189
|
reorder: async (params) => {
|
|
1026
|
-
await
|
|
1190
|
+
await requestRaw(`${claimPath(params.id)}/reorder`, {
|
|
1027
1191
|
method: 'POST',
|
|
1028
1192
|
// The reorder route's payload is `{ heldBy, claimId }[]` — a Claim's id
|
|
1029
1193
|
// is the claimId.
|
|
@@ -1136,6 +1300,29 @@ export function createHttpTransport(options) {
|
|
|
1136
1300
|
return mutateModel('delete', name, params.id, undefined, options);
|
|
1137
1301
|
});
|
|
1138
1302
|
},
|
|
1303
|
+
async track(params) {
|
|
1304
|
+
const dependency = {
|
|
1305
|
+
model: name.toLowerCase(),
|
|
1306
|
+
id: params.id,
|
|
1307
|
+
...(params.readAt !== undefined ? { readAt: params.readAt } : {}),
|
|
1308
|
+
};
|
|
1309
|
+
// A track carries no write, so it rides the commit lane as a
|
|
1310
|
+
// zero-operation body — the shape `/v1/commits` accepts for registering
|
|
1311
|
+
// a premise without one. Going through the same durable lane as every
|
|
1312
|
+
// other commit means a disconnect replays the registration rather than
|
|
1313
|
+
// dropping it, and a notification that had already fired is not lost to
|
|
1314
|
+
// a retry.
|
|
1315
|
+
const body = await dispatchHttpCommit({
|
|
1316
|
+
path: '/v1/commits',
|
|
1317
|
+
method: 'POST',
|
|
1318
|
+
idempotencyKey: createClientTxId(),
|
|
1319
|
+
body: { track: [dependency] },
|
|
1320
|
+
wait: 'confirmed',
|
|
1321
|
+
});
|
|
1322
|
+
return body.notifications && body.notifications.length > 0
|
|
1323
|
+
? { notifications: body.notifications }
|
|
1324
|
+
: {};
|
|
1325
|
+
},
|
|
1139
1326
|
};
|
|
1140
1327
|
}
|
|
1141
1328
|
return {
|
|
@@ -1148,6 +1335,7 @@ export function createHttpTransport(options) {
|
|
|
1148
1335
|
async dispose() { },
|
|
1149
1336
|
async purge() { },
|
|
1150
1337
|
commits,
|
|
1338
|
+
claims,
|
|
1151
1339
|
model,
|
|
1152
1340
|
sessions: {
|
|
1153
1341
|
async create(params) {
|
|
@@ -1158,11 +1346,21 @@ export function createHttpTransport(options) {
|
|
|
1158
1346
|
if (!apiKey) {
|
|
1159
1347
|
throw new AbloAuthenticationError('sessions.create requires a secret (sk_) API key — call it from your backend, not the browser.', { code: 'apikey_missing' });
|
|
1160
1348
|
}
|
|
1349
|
+
// A transport built without a schema has no way to translate `can`'s
|
|
1350
|
+
// schema keys into the type names the server gates on. Minting anyway
|
|
1351
|
+
// would spell every override wrong and surface as
|
|
1352
|
+
// `capability_scope_denied` on the agent's first write, so refuse here
|
|
1353
|
+
// instead of guessing.
|
|
1354
|
+
if (!options.modelTypenames) {
|
|
1355
|
+
throw new AbloValidationError('sessions.create needs the schema this client is bound to. Construct it ' +
|
|
1356
|
+
"through Ablo({ schema, apiKey, transport: 'http' }) rather than the " +
|
|
1357
|
+
'bare transport.', { code: 'invalid_options', param: 'schema' });
|
|
1358
|
+
}
|
|
1161
1359
|
return mintSession(params, {
|
|
1162
1360
|
apiKey,
|
|
1163
1361
|
baseUrl: apiBaseUrl,
|
|
1362
|
+
modelTypenames: options.modelTypenames,
|
|
1164
1363
|
...(options.fetch ? { fetch: options.fetch } : {}),
|
|
1165
|
-
...(options.modelTypenames ? { modelTypenames: options.modelTypenames } : {}),
|
|
1166
1364
|
});
|
|
1167
1365
|
},
|
|
1168
1366
|
},
|