@abloatai/ablo 0.25.0 → 0.27.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 +5 -3
- package/CHANGELOG.md +34 -0
- package/README.md +104 -88
- package/dist/BaseSyncedStore.d.ts +140 -266
- package/dist/BaseSyncedStore.js +338 -739
- package/dist/Database.d.ts +62 -77
- package/dist/Database.js +106 -127
- package/dist/{ObjectPool.d.ts → InstanceCache.d.ts} +10 -13
- package/dist/{ObjectPool.js → InstanceCache.js} +91 -83
- package/dist/LazyReferenceCollection.d.ts +11 -15
- package/dist/LazyReferenceCollection.js +16 -15
- package/dist/Model.d.ts +37 -52
- package/dist/Model.js +52 -69
- package/dist/ModelRegistry.d.ts +46 -25
- package/dist/ModelRegistry.js +32 -30
- package/dist/NetworkMonitor.d.ts +5 -6
- package/dist/NetworkMonitor.js +6 -7
- package/dist/SyncClient.d.ts +119 -109
- package/dist/SyncClient.js +303 -224
- package/dist/SyncEngineContext.d.ts +1 -3
- package/dist/SyncEngineContext.js +1 -2
- package/dist/adapters/alwaysOnline.d.ts +6 -8
- package/dist/adapters/alwaysOnline.js +6 -8
- package/dist/adapters/inMemoryStorage.d.ts +9 -9
- package/dist/adapters/inMemoryStorage.js +9 -9
- package/dist/agent/Agent.d.ts +39 -31
- package/dist/agent/Agent.js +35 -23
- package/dist/agent/index.d.ts +4 -4
- package/dist/agent/index.js +5 -5
- package/dist/agent/session.d.ts +47 -44
- package/dist/agent/session.js +37 -48
- package/dist/agent/types.d.ts +26 -31
- package/dist/agent/types.js +6 -7
- package/dist/ai-sdk/coordinatedTool.d.ts +108 -0
- package/dist/ai-sdk/{coordinated-tool.js → coordinatedTool.js} +44 -38
- package/dist/ai-sdk/coordinationContext.d.ts +46 -0
- package/dist/ai-sdk/{coordination-context.js → coordinationContext.js} +30 -31
- package/dist/ai-sdk/index.d.ts +25 -22
- package/dist/ai-sdk/index.js +25 -22
- package/dist/ai-sdk/wrap.d.ts +7 -8
- package/dist/ai-sdk/wrap.js +2 -2
- package/dist/auth/credentialPolicy.d.ts +74 -71
- package/dist/auth/credentialPolicy.js +51 -56
- package/dist/auth/credentialSource.d.ts +7 -18
- package/dist/auth/credentialSource.js +10 -18
- package/dist/auth/index.d.ts +59 -58
- package/dist/auth/index.js +34 -40
- package/dist/auth/schemas.d.ts +5 -4
- package/dist/auth/schemas.js +5 -4
- package/dist/batching/index.d.ts +19 -21
- package/dist/batching/index.js +14 -17
- package/dist/cli.cjs +483 -369
- package/dist/client/Ablo.d.ts +107 -836
- package/dist/client/Ablo.js +174 -833
- package/dist/client/ApiClient.d.ts +44 -20
- package/dist/client/ApiClient.js +193 -44
- package/dist/client/auth.d.ts +51 -60
- package/dist/client/auth.js +137 -110
- package/dist/client/claimHeartbeatLoop.d.ts +50 -0
- package/dist/client/claimHeartbeatLoop.js +88 -0
- package/dist/client/consoleLogger.d.ts +35 -0
- package/dist/client/consoleLogger.js +44 -0
- package/dist/client/createInternalComponents.d.ts +14 -17
- package/dist/client/createInternalComponents.js +26 -31
- package/dist/client/createModelProxy.d.ts +130 -120
- package/dist/client/createModelProxy.js +158 -124
- package/dist/client/credentialEndpoint.d.ts +61 -0
- package/dist/client/credentialEndpoint.js +86 -0
- package/dist/client/functionalUpdate.d.ts +29 -27
- package/dist/client/functionalUpdate.js +21 -21
- package/dist/client/hostedEndpoints.d.ts +21 -0
- package/dist/client/hostedEndpoints.js +21 -0
- package/dist/client/httpClient.d.ts +58 -54
- package/dist/client/httpClient.js +29 -31
- package/dist/client/identity.d.ts +15 -20
- package/dist/client/identity.js +49 -59
- package/dist/client/modelRegistration.d.ts +10 -0
- package/dist/client/modelRegistration.js +301 -0
- package/dist/client/options.d.ts +373 -0
- package/dist/client/options.js +6 -0
- package/dist/client/registerDataSource.d.ts +9 -9
- package/dist/client/registerDataSource.js +15 -16
- package/dist/client/resourceTypes.d.ts +333 -0
- package/dist/client/resourceTypes.js +7 -0
- package/dist/client/schemaConfig.d.ts +44 -0
- package/dist/client/schemaConfig.js +176 -0
- package/dist/client/sessionMint.d.ts +17 -13
- package/dist/client/sessionMint.js +26 -31
- package/dist/client/validateAbloOptions.d.ts +12 -14
- package/dist/client/validateAbloOptions.js +9 -10
- package/dist/client/writeOptionsSchema.d.ts +18 -16
- package/dist/client/writeOptionsSchema.js +23 -20
- package/dist/client/wsMutationExecutor.d.ts +28 -0
- package/dist/client/wsMutationExecutor.js +71 -0
- package/dist/context.d.ts +6 -4
- package/dist/context.js +6 -7
- package/dist/coordination/index.d.ts +13 -4
- package/dist/coordination/index.js +29 -4
- package/dist/coordination/schema.d.ts +176 -128
- package/dist/coordination/schema.js +197 -133
- package/dist/coordination/trace.d.ts +9 -11
- package/dist/coordination/trace.js +13 -15
- package/dist/core/DatabaseManager.d.ts +5 -8
- package/dist/core/DatabaseManager.js +38 -40
- package/dist/core/QueryProcessor.d.ts +7 -9
- package/dist/core/QueryProcessor.js +27 -34
- package/dist/core/QueryView.d.ts +17 -5
- package/dist/core/QueryView.js +6 -7
- package/dist/core/StoreManager.d.ts +14 -16
- package/dist/core/StoreManager.js +26 -25
- package/dist/core/ViewRegistry.d.ts +5 -5
- package/dist/core/ViewRegistry.js +4 -4
- package/dist/core/index.d.ts +18 -13
- package/dist/core/index.js +32 -26
- package/dist/core/openIDBWithTimeout.d.ts +38 -36
- package/dist/core/openIDBWithTimeout.js +57 -54
- package/dist/core/queryUtils.d.ts +45 -0
- package/dist/core/queryUtils.js +69 -0
- package/dist/core/storeContract.d.ts +145 -0
- package/dist/core/storeContract.js +12 -0
- package/dist/environment.d.ts +28 -0
- package/dist/environment.js +21 -0
- package/dist/errorCodes.d.ts +118 -101
- package/dist/errorCodes.js +277 -260
- package/dist/errors.d.ts +170 -165
- package/dist/errors.js +161 -151
- package/dist/index.d.ts +30 -27
- package/dist/index.js +90 -82
- package/dist/interfaces/index.d.ts +108 -133
- package/dist/interfaces/index.js +5 -4
- package/dist/keys/index.d.ts +27 -29
- package/dist/keys/index.js +59 -49
- package/dist/mutators/RecordingTransaction.d.ts +16 -16
- package/dist/mutators/RecordingTransaction.js +31 -37
- package/dist/mutators/Transaction.d.ts +18 -26
- package/dist/mutators/Transaction.js +14 -20
- package/dist/mutators/UndoManager.d.ts +122 -131
- package/dist/mutators/UndoManager.js +149 -155
- package/dist/mutators/defineMutators.d.ts +24 -37
- package/dist/mutators/defineMutators.js +14 -20
- package/dist/mutators/inverseOp.d.ts +12 -15
- package/dist/mutators/inverseOp.js +12 -15
- package/dist/mutators/mutateActions.d.ts +10 -9
- package/dist/mutators/mutateActions.js +1 -1
- package/dist/mutators/readerActions.d.ts +9 -8
- package/dist/mutators/readerActions.js +2 -2
- package/dist/mutators/undoApply.d.ts +31 -27
- package/dist/mutators/undoApply.js +26 -24
- package/dist/policy/index.d.ts +5 -3
- package/dist/policy/index.js +5 -3
- package/dist/policy/types.d.ts +105 -101
- package/dist/policy/types.js +67 -66
- package/dist/query/client.d.ts +32 -16
- package/dist/query/client.js +103 -72
- package/dist/query/types.d.ts +37 -60
- package/dist/query/types.js +13 -33
- package/dist/react/AbloProvider.d.ts +7 -11
- package/dist/react/AbloProvider.js +24 -17
- package/dist/react/context.d.ts +27 -146
- package/dist/react/context.js +9 -10
- package/dist/react/index.d.ts +41 -42
- package/dist/react/index.js +37 -38
- package/dist/react/internalContext.d.ts +17 -19
- package/dist/react/useAblo.d.ts +23 -22
- package/dist/react/useAblo.js +17 -15
- package/dist/react/useCurrentUserId.d.ts +8 -7
- package/dist/react/useCurrentUserId.js +8 -7
- package/dist/react/useErrorListener.d.ts +7 -7
- package/dist/react/useErrorListener.js +11 -12
- package/dist/react/useMutationFailureListener.d.ts +8 -8
- package/dist/react/useMutationFailureListener.js +9 -9
- package/dist/react/useMutators.d.ts +11 -11
- package/dist/react/useMutators.js +10 -4
- package/dist/react/useReactive.js +2 -3
- package/dist/react/useSyncStatus.d.ts +4 -6
- package/dist/react/useUndoScope.d.ts +7 -9
- package/dist/react/useUndoScope.js +3 -3
- package/dist/schema/coordination.d.ts +21 -25
- package/dist/schema/coordination.js +21 -25
- package/dist/schema/ddl.d.ts +43 -39
- package/dist/schema/ddl.js +75 -68
- package/dist/schema/ddlLock.d.ts +35 -0
- package/dist/schema/ddlLock.js +46 -0
- package/dist/schema/diff.d.ts +99 -61
- package/dist/schema/diff.js +43 -34
- package/dist/schema/field.d.ts +37 -42
- package/dist/schema/field.js +36 -49
- package/dist/schema/generate.d.ts +12 -12
- package/dist/schema/generate.js +12 -12
- package/dist/schema/index.d.ts +5 -4
- package/dist/schema/index.js +29 -21
- package/dist/schema/model.d.ts +121 -146
- package/dist/schema/model.js +24 -35
- package/dist/schema/openapi.d.ts +10 -9
- package/dist/schema/openapi.js +7 -1
- package/dist/schema/queries.d.ts +30 -32
- package/dist/schema/queries.js +24 -25
- package/dist/schema/relation.d.ts +89 -99
- package/dist/schema/relation.js +13 -13
- package/dist/schema/residency.d.ts +38 -0
- package/dist/schema/residency.js +30 -0
- package/dist/schema/roles.d.ts +45 -27
- package/dist/schema/roles.js +52 -21
- package/dist/schema/schema.d.ts +36 -45
- package/dist/schema/schema.js +42 -39
- package/dist/schema/select.d.ts +13 -13
- package/dist/schema/select.js +13 -13
- package/dist/schema/serialize.d.ts +36 -39
- package/dist/schema/serialize.js +27 -31
- package/dist/schema/sugar.d.ts +17 -32
- package/dist/schema/sugar.js +14 -29
- package/dist/schema/{sync-delta-row.d.ts → syncDeltaRow.d.ts} +27 -50
- package/dist/schema/syncDeltaRow.js +89 -0
- package/dist/schema/tenancy.d.ts +44 -46
- package/dist/schema/tenancy.js +46 -48
- package/dist/server/adapter.d.ts +58 -58
- package/dist/server/adapter.js +13 -14
- package/dist/server/commit.d.ts +60 -64
- package/dist/server/index.d.ts +9 -10
- package/dist/server/index.js +1 -1
- package/dist/server/readConfig.d.ts +70 -0
- package/dist/server/readConfig.js +8 -0
- package/dist/server/storageMode.d.ts +23 -0
- package/dist/server/storageMode.js +17 -0
- package/dist/source/adapter.d.ts +31 -26
- package/dist/source/adapter.js +10 -10
- package/dist/source/adapters/drizzle.d.ts +28 -23
- package/dist/source/adapters/drizzle.js +34 -28
- package/dist/source/adapters/kysely.d.ts +27 -25
- package/dist/source/adapters/kysely.js +28 -26
- package/dist/source/adapters/memory.d.ts +8 -7
- package/dist/source/adapters/memory.js +10 -9
- package/dist/source/adapters/prisma.d.ts +13 -12
- package/dist/source/adapters/prisma.js +27 -29
- package/dist/source/conformance.d.ts +18 -11
- package/dist/source/conformance.js +27 -19
- package/dist/source/connector.d.ts +31 -32
- package/dist/source/connector.js +30 -28
- package/dist/source/connectorProtocol.d.ts +160 -0
- package/dist/source/connectorProtocol.js +162 -0
- package/dist/source/contract.d.ts +26 -27
- package/dist/source/contract.js +28 -29
- package/dist/source/factory.d.ts +94 -0
- package/dist/source/factory.js +268 -0
- package/dist/source/index.d.ts +10 -462
- package/dist/source/index.js +17 -421
- package/dist/source/migrations.d.ts +9 -9
- package/dist/source/migrations.js +9 -9
- package/dist/source/next.d.ts +10 -11
- package/dist/source/next.js +7 -8
- package/dist/source/pushQueue.d.ts +70 -48
- package/dist/source/pushQueue.js +36 -29
- package/dist/source/signing.d.ts +88 -0
- package/dist/source/signing.js +159 -0
- package/dist/source/types.d.ts +351 -0
- package/dist/source/types.js +43 -0
- package/dist/stores/ObjectStore.d.ts +11 -12
- package/dist/stores/ObjectStore.js +34 -35
- package/dist/stores/ObjectStoreContract.d.ts +12 -15
- package/dist/stores/SyncActionStore.d.ts +8 -12
- package/dist/stores/SyncActionStore.js +77 -46
- package/dist/surface.d.ts +28 -21
- package/dist/surface.js +28 -20
- package/dist/sync/{BootstrapHelper.d.ts → BootstrapFetcher.d.ts} +37 -45
- package/dist/sync/{BootstrapHelper.js → BootstrapFetcher.js} +101 -80
- package/dist/sync/ConnectionManager.d.ts +47 -50
- package/dist/sync/ConnectionManager.js +74 -70
- package/dist/sync/NetworkProbe.d.ts +27 -31
- package/dist/sync/NetworkProbe.js +67 -72
- package/dist/sync/{HydrationCoordinator.d.ts → OnDemandLoader.d.ts} +49 -36
- package/dist/sync/{HydrationCoordinator.js → OnDemandLoader.js} +79 -54
- package/dist/sync/{AreaOfInterestManager.d.ts → SubscriptionManager.d.ts} +45 -59
- package/dist/sync/{AreaOfInterestManager.js → SubscriptionManager.js} +44 -52
- package/dist/sync/SyncWebSocket.d.ts +175 -250
- package/dist/sync/SyncWebSocket.js +431 -769
- package/dist/sync/awaitClaimGrant.d.ts +18 -18
- package/dist/sync/awaitClaimGrant.js +38 -30
- package/dist/sync/bootstrapApply.d.ts +70 -0
- package/dist/sync/bootstrapApply.js +73 -0
- package/dist/sync/commitFrames.d.ts +44 -0
- package/dist/sync/commitFrames.js +94 -0
- package/dist/sync/createClaimStream.d.ts +23 -22
- package/dist/sync/createClaimStream.js +108 -25
- package/dist/sync/createPresenceStream.d.ts +19 -18
- package/dist/sync/createPresenceStream.js +25 -26
- package/dist/sync/createSnapshot.d.ts +13 -17
- package/dist/sync/createSnapshot.js +20 -26
- package/dist/sync/credentialLifecycle.d.ts +175 -0
- package/dist/sync/credentialLifecycle.js +322 -0
- package/dist/sync/deltaPipeline.d.ts +113 -0
- package/dist/sync/deltaPipeline.js +261 -0
- package/dist/sync/groupChange.d.ts +113 -0
- package/dist/sync/groupChange.js +242 -0
- package/dist/sync/heartbeat.d.ts +63 -0
- package/dist/sync/heartbeat.js +91 -0
- package/dist/sync/participants.d.ts +27 -27
- package/dist/sync/schemas.d.ts +3 -2
- package/dist/sync/schemas.js +14 -10
- package/dist/sync/syncCursor.d.ts +40 -0
- package/dist/sync/syncCursor.js +55 -0
- package/dist/sync/syncPlan.d.ts +54 -0
- package/dist/sync/syncPlan.js +50 -0
- package/dist/sync/syncPosition.d.ts +54 -49
- package/dist/sync/syncPosition.js +57 -52
- package/dist/sync/wsFrameHandlers.d.ts +116 -0
- package/dist/sync/wsFrameHandlers.js +374 -0
- package/dist/testing/fixtures/bootstrap.d.ts +21 -17
- package/dist/testing/fixtures/bootstrap.js +12 -6
- package/dist/testing/fixtures/deltas.d.ts +31 -34
- package/dist/testing/fixtures/deltas.js +30 -33
- package/dist/testing/fixtures/models.d.ts +11 -10
- package/dist/testing/fixtures/models.js +12 -10
- package/dist/testing/helpers/{react-wrapper.d.ts → reactWrapper.d.ts} +13 -10
- package/dist/testing/helpers/{react-wrapper.js → reactWrapper.js} +15 -12
- package/dist/testing/helpers/{sync-engine-harness.d.ts → syncEngineHarness.d.ts} +17 -18
- package/dist/testing/helpers/{sync-engine-harness.js → syncEngineHarness.js} +14 -11
- package/dist/testing/helpers/wait.d.ts +13 -8
- package/dist/testing/helpers/wait.js +13 -8
- package/dist/testing/index.d.ts +4 -4
- package/dist/testing/index.js +3 -3
- package/dist/testing/mocks/MockMutationExecutor.d.ts +18 -17
- package/dist/testing/mocks/MockMutationExecutor.js +15 -14
- package/dist/testing/mocks/MockNetworkMonitor.d.ts +8 -8
- package/dist/testing/mocks/MockNetworkMonitor.js +8 -8
- package/dist/testing/mocks/MockSyncContext.d.ts +21 -34
- package/dist/testing/mocks/MockSyncContext.js +16 -45
- package/dist/testing/mocks/MockSyncStore.d.ts +14 -14
- package/dist/testing/mocks/MockSyncStore.js +11 -11
- package/dist/testing/mocks/MockWebSocket.d.ts +28 -24
- package/dist/testing/mocks/MockWebSocket.js +22 -21
- package/dist/transactions/TransactionQueue.d.ts +190 -221
- package/dist/transactions/TransactionQueue.js +424 -822
- package/dist/transactions/TransactionStore.d.ts +20 -0
- package/dist/transactions/TransactionStore.js +53 -0
- package/dist/transactions/UnconfirmedWrites.d.ts +82 -0
- package/dist/transactions/UnconfirmedWrites.js +104 -0
- package/dist/transactions/coalesceRules.d.ts +58 -0
- package/dist/transactions/coalesceRules.js +140 -0
- package/dist/transactions/commitPayload.d.ts +130 -0
- package/dist/transactions/commitPayload.js +143 -0
- package/dist/transactions/deltaConfirmation.d.ts +58 -0
- package/dist/transactions/deltaConfirmation.js +215 -0
- package/dist/transactions/optimisticApply.d.ts +49 -0
- package/dist/transactions/optimisticApply.js +65 -0
- package/dist/transactions/replayValidation.d.ts +99 -0
- package/dist/transactions/replayValidation.js +111 -0
- package/dist/types/global.d.ts +46 -41
- package/dist/types/global.js +20 -19
- package/dist/types/index.d.ts +74 -80
- package/dist/types/index.js +22 -27
- package/dist/types/modelData.d.ts +10 -0
- package/dist/types/modelData.js +9 -0
- package/dist/types/participant.d.ts +20 -0
- package/dist/types/participant.js +10 -0
- package/dist/types/streams.d.ts +216 -209
- package/dist/types/streams.js +7 -7
- package/dist/utils/asyncIterator.d.ts +25 -32
- package/dist/utils/asyncIterator.js +25 -32
- package/dist/utils/duration.d.ts +12 -15
- package/dist/utils/duration.js +12 -15
- package/dist/utils/mobxSetup.d.ts +53 -0
- package/dist/utils/{mobx-setup.js → mobxSetup.js} +44 -100
- package/dist/webhooks/events.d.ts +21 -16
- package/dist/webhooks/events.js +10 -8
- package/dist/webhooks/index.d.ts +5 -7
- package/dist/webhooks/index.js +5 -7
- package/dist/{schema/sync-delta-wire.d.ts → wire/delta.d.ts} +58 -41
- package/dist/wire/delta.js +114 -0
- package/dist/wire/errorEnvelope.d.ts +35 -27
- package/dist/wire/errorEnvelope.js +38 -32
- package/dist/wire/frames.d.ts +150 -67
- package/dist/wire/frames.js +48 -1
- package/dist/wire/index.d.ts +18 -13
- package/dist/wire/index.js +36 -13
- package/dist/wire/listEnvelope.d.ts +16 -23
- package/dist/wire/listEnvelope.js +7 -6
- package/dist/wire/protocol.d.ts +38 -0
- package/dist/wire/protocol.js +38 -0
- package/dist/wire/protocolVersion.d.ts +60 -0
- package/dist/wire/protocolVersion.js +67 -0
- package/docs/api-keys.md +4 -3
- package/docs/coordination.md +59 -0
- package/docs/examples/existing-python-backend.md +3 -3
- package/docs/identity.md +4 -4
- package/docs/integration-guide.md +1 -1
- package/docs/react.md +1 -1
- package/docs/sessions.md +5 -7
- package/package.json +24 -21
- package/dist/ai-sdk/coordinated-tool.d.ts +0 -101
- package/dist/ai-sdk/coordination-context.d.ts +0 -52
- package/dist/client/index.d.ts +0 -36
- package/dist/client/index.js +0 -33
- package/dist/config/index.d.ts +0 -10
- package/dist/config/index.js +0 -12
- package/dist/core/query-utils.d.ts +0 -34
- package/dist/core/query-utils.js +0 -59
- package/dist/interfaces/headless.d.ts +0 -95
- package/dist/interfaces/headless.js +0 -41
- package/dist/query/index.d.ts +0 -6
- package/dist/query/index.js +0 -5
- package/dist/realtime/index.d.ts +0 -10
- package/dist/realtime/index.js +0 -9
- package/dist/schema/plane.d.ts +0 -23
- package/dist/schema/plane.js +0 -19
- package/dist/schema/sync-delta-row.js +0 -103
- package/dist/schema/sync-delta-wire.js +0 -102
- package/dist/server/next.d.ts +0 -51
- package/dist/server/next.js +0 -47
- package/dist/server/read-config.d.ts +0 -67
- package/dist/server/read-config.js +0 -8
- package/dist/server/storage-mode.d.ts +0 -1
- package/dist/server/storage-mode.js +0 -18
- package/dist/source/connector-protocol.d.ts +0 -159
- package/dist/source/connector-protocol.js +0 -161
- package/dist/sync/OfflineFlush.d.ts +0 -9
- package/dist/sync/OfflineFlush.js +0 -22
- package/dist/sync/OfflineTransactionStore.d.ts +0 -37
- package/dist/sync/OfflineTransactionStore.js +0 -263
- package/dist/transactions/OptimisticEchoTracker.d.ts +0 -82
- package/dist/transactions/OptimisticEchoTracker.js +0 -104
- package/dist/transactions/index.d.ts +0 -16
- package/dist/transactions/index.js +0 -7
- package/dist/transactions/mutation-error-handler.d.ts +0 -5
- package/dist/transactions/mutation-error-handler.js +0 -39
- package/dist/utils/mobx-setup.d.ts +0 -42
|
@@ -1,29 +1,53 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* nouns directly to HTTP routes on sync-server.
|
|
2
|
+
* The stateless API client behind `Ablo({ apiKey })`. It carries no schema,
|
|
3
|
+
* object pool, local database, or WebSocket, and maps the public Model, Claim,
|
|
4
|
+
* and Commit nouns directly to HTTP routes on the server. This is the transport
|
|
5
|
+
* used for server-side agents, workers, and serverless code.
|
|
7
6
|
*/
|
|
8
|
-
import type { AbloOptions
|
|
7
|
+
import type { AbloOptions } from './options.js';
|
|
8
|
+
import type { CommitResource, ClaimCreateOptions, ClaimWaitOptions, ModelClient, ModelClaim, ModelTarget, CreateSessionParams, AbloSession } from './resourceTypes.js';
|
|
9
9
|
import type { SchemaRecord } from '../schema/schema.js';
|
|
10
10
|
import type { Duration } from '../utils/duration.js';
|
|
11
|
-
import type { Claim } from '../types/streams.js';
|
|
11
|
+
import type { Claim, ClaimHeartbeat } from '../types/streams.js';
|
|
12
12
|
import type { SyncObservabilityProvider } from '../interfaces/index.js';
|
|
13
13
|
export type AbloApiClientOptions = Omit<AbloOptions, 'schema'> & {
|
|
14
14
|
readonly schema?: null | undefined;
|
|
15
15
|
readonly bootstrapBaseUrl?: string | undefined;
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
* transport emits the same claim
|
|
19
|
-
* `ClaimLog` works identically for headless
|
|
17
|
+
* The observability provider forwarded from `Ablo({ observability })`. The HTTP
|
|
18
|
+
* transport emits the same claim and conflict events as the WebSocket transport,
|
|
19
|
+
* so a `ClaimLog` works identically for headless server-agent evaluations.
|
|
20
20
|
*/
|
|
21
21
|
readonly observability?: SyncObservabilityProvider;
|
|
22
|
+
/**
|
|
23
|
+
* Per-request deadline in milliseconds for the stateless HTTP transport.
|
|
24
|
+
* Every request this client issues is aborted after this long and surfaces
|
|
25
|
+
* as a retryable connection error — without it a black-holed server hangs
|
|
26
|
+
* a headless agent forever (browsers never time fetch out on their own).
|
|
27
|
+
* Pass `0` to disable the deadline.
|
|
28
|
+
*
|
|
29
|
+
* @default 30_000
|
|
30
|
+
*/
|
|
31
|
+
readonly timeoutMs?: number;
|
|
22
32
|
};
|
|
33
|
+
/** Default per-request deadline for the stateless HTTP transport. */
|
|
34
|
+
export declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
23
35
|
export interface AbloApiClaims {
|
|
24
36
|
create(options: ClaimCreateOptions): Promise<Claim>;
|
|
25
37
|
list(target?: Partial<ModelTarget>): Promise<readonly ModelClaim[]>;
|
|
26
38
|
waitFor(target: Partial<ModelTarget>, options?: ClaimWaitOptions): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* The batched beat — extend every lease this credential holds in one
|
|
41
|
+
* request (`POST /v1/claims/heartbeat`), the stateless twin of the
|
|
42
|
+
* WebSocket keepalive. One round trip per cadence for a worker holding
|
|
43
|
+
* many rows. Returns one {@link ClaimHeartbeat} per extended lease,
|
|
44
|
+
* tagged with its claim id — no separate result type to learn.
|
|
45
|
+
*/
|
|
46
|
+
heartbeatAll(options?: {
|
|
47
|
+
ttl?: Duration;
|
|
48
|
+
}): Promise<readonly (ClaimHeartbeat & {
|
|
49
|
+
readonly claimId: string;
|
|
50
|
+
})[]>;
|
|
27
51
|
}
|
|
28
52
|
export type CapabilityParticipantKind = 'agent' | 'system';
|
|
29
53
|
export interface CapabilityCreateBaseOptions {
|
|
@@ -84,14 +108,14 @@ export interface CapabilityRevocation {
|
|
|
84
108
|
}
|
|
85
109
|
export interface CapabilityRotateOptions {
|
|
86
110
|
/**
|
|
87
|
-
*
|
|
88
|
-
* rotation, so you can deploy the replacement with zero downtime.
|
|
89
|
-
* 24h server
|
|
111
|
+
* The overlap window — the old token keeps authenticating for this long after
|
|
112
|
+
* rotation, so you can deploy the replacement with zero downtime. Defaults to
|
|
113
|
+
* 24h on the server.
|
|
90
114
|
*/
|
|
91
115
|
readonly grace?: Duration;
|
|
92
116
|
readonly graceSeconds?: number;
|
|
93
117
|
/**
|
|
94
|
-
*
|
|
118
|
+
* The lifetime of the replacement capability. Omit to inherit the original's
|
|
95
119
|
* lifetime.
|
|
96
120
|
*/
|
|
97
121
|
readonly lease?: Duration;
|
|
@@ -113,9 +137,9 @@ export interface CapabilityResource {
|
|
|
113
137
|
retrieve(id: string): Promise<CapabilityRecord>;
|
|
114
138
|
revoke(id: string): Promise<CapabilityRevocation>;
|
|
115
139
|
/**
|
|
116
|
-
* Rotate with overlap
|
|
117
|
-
*
|
|
118
|
-
*
|
|
140
|
+
* Rotate with overlap: mint a fresh capability that carries the same scope, and
|
|
141
|
+
* keep the old token working for a grace window so you can roll out the
|
|
142
|
+
* replacement without downtime.
|
|
119
143
|
*/
|
|
120
144
|
rotate(id: string, options?: CapabilityRotateOptions): Promise<RotatedCapability>;
|
|
121
145
|
/**
|
|
@@ -142,9 +166,9 @@ export interface AbloApi {
|
|
|
142
166
|
*/
|
|
143
167
|
getAuthToken(): Promise<string | null>;
|
|
144
168
|
/**
|
|
145
|
-
* Mint a short-lived scoped session
|
|
146
|
-
*
|
|
147
|
-
*
|
|
169
|
+
* Mint a short-lived scoped session. Minting is a control-plane HTTP call (no
|
|
170
|
+
* socket), so it lives on this stateless client too, not only the realtime one.
|
|
171
|
+
* `{ user }` mints an `ek_`; `{ agent, can }` mints an `rk_`.
|
|
148
172
|
*/
|
|
149
173
|
readonly sessions: {
|
|
150
174
|
create(params: CreateSessionParams<SchemaRecord>): Promise<AbloSession>;
|
package/dist/client/ApiClient.js
CHANGED
|
@@ -1,17 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* nouns directly to HTTP routes on sync-server.
|
|
2
|
+
* The stateless API client behind `Ablo({ apiKey })`. It carries no schema,
|
|
3
|
+
* object pool, local database, or WebSocket, and maps the public Model, Claim,
|
|
4
|
+
* and Commit nouns directly to HTTP routes on the server. This is the transport
|
|
5
|
+
* used for server-side agents, workers, and serverless code.
|
|
7
6
|
*/
|
|
8
7
|
import { AbloClaimedError, AbloAuthenticationError, AbloConnectionError, AbloValidationError, AbloNotFoundError, claimedError, translateHttpError, } from '../errors.js';
|
|
9
8
|
import { reconcileFunctionalUpdate, } from './functionalUpdate.js';
|
|
10
9
|
import { assertBrowserSafety, readProcessEnv, resolveApiKey, resolveApiKeyValue, resolveAuthToken, resolveBaseURL, resolveBootstrapBaseUrl, resolveDatabaseUrl, warnIfCliKeyMismatch, warnIfDatabaseUrlEnvIgnored, warnIfDatabaseUrlDeprecated, } from './auth.js';
|
|
11
10
|
import { registerDataSource } from './registerDataSource.js';
|
|
12
|
-
import {
|
|
11
|
+
import { PROTOCOL_VERSION, PROTOCOL_VERSION_HEADER } from '../wire/protocolVersion.js';
|
|
12
|
+
import { toMs, toSeconds } from '../utils/duration.js';
|
|
13
|
+
import { heartbeatCadenceMs, resolveHeartbeatOptions, startClaimHeartbeatLoop, } from './claimHeartbeatLoop.js';
|
|
13
14
|
import { mintSession } from './sessionMint.js';
|
|
15
|
+
/**
|
|
16
|
+
* Interpret a heartbeat reply for a lease this handle HOLDS: anything other
|
|
17
|
+
* than `held` means the lease is no longer ours (a holder cannot be `queued`;
|
|
18
|
+
* `lost` rides a 409 that the wire error mapping already surfaces as
|
|
19
|
+
* AbloClaimedError before reaching here). The thrown loss is the definitive
|
|
20
|
+
* signal that stops the auto-heartbeat loop.
|
|
21
|
+
*/
|
|
22
|
+
function heldHeartbeatReply(reply, label) {
|
|
23
|
+
if (reply.status === 'held' && typeof reply.expiresAt === 'number') {
|
|
24
|
+
return {
|
|
25
|
+
expiresAt: reply.expiresAt,
|
|
26
|
+
...(reply.queueDepth !== undefined ? { queueDepth: reply.queueDepth } : {}),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
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' });
|
|
30
|
+
}
|
|
14
31
|
import { assertWriteOptions } from './writeOptionsSchema.js';
|
|
32
|
+
/** Default per-request deadline for the stateless HTTP transport. */
|
|
33
|
+
export const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
|
|
15
34
|
const DEFAULT_AGENT_LEASE = '10m';
|
|
16
35
|
export function createProtocolClient(options) {
|
|
17
36
|
const env = readProcessEnv();
|
|
@@ -29,11 +48,12 @@ export function createProtocolClient(options) {
|
|
|
29
48
|
databaseUrl: configuredDatabaseUrl,
|
|
30
49
|
dangerouslyAllowBrowser: options.dangerouslyAllowBrowser,
|
|
31
50
|
});
|
|
32
|
-
// Observability
|
|
33
|
-
// claim
|
|
51
|
+
// Observability hook for the stateless HTTP transport. The WebSocket transport
|
|
52
|
+
// emits claim and conflict events; the HTTP path (server-side agents,
|
|
34
53
|
// `transport: 'http'`) emitted nothing, so a `ClaimLog` handed to a headless
|
|
35
|
-
// agent
|
|
36
|
-
// coordination-conflict rejection.
|
|
54
|
+
// agent evaluation stayed empty. This mirrors the two WebSocket events here:
|
|
55
|
+
// claim acquired and coordination-conflict rejection. A no-op when no provider
|
|
56
|
+
// is configured.
|
|
37
57
|
const observability = options.observability;
|
|
38
58
|
// Shared by the two HTTP write doors (`commits.create` + per-model
|
|
39
59
|
// `mutateModel`): a rejected write whose code is a coordination conflict is
|
|
@@ -101,6 +121,9 @@ export function createProtocolClient(options) {
|
|
|
101
121
|
const headers = {
|
|
102
122
|
'Content-Type': 'application/json',
|
|
103
123
|
Authorization: `Bearer ${token}`,
|
|
124
|
+
// Protocol handshake for the HTTP transport (wire/protocolVersion.ts):
|
|
125
|
+
// the server answers an out-of-range version with a typed 426.
|
|
126
|
+
[PROTOCOL_VERSION_HEADER]: String(PROTOCOL_VERSION),
|
|
104
127
|
};
|
|
105
128
|
for (const [key, value] of Object.entries(options.defaultHeaders ?? {})) {
|
|
106
129
|
if (value == null) {
|
|
@@ -120,20 +143,66 @@ export function createProtocolClient(options) {
|
|
|
120
143
|
}
|
|
121
144
|
return target.toString();
|
|
122
145
|
}
|
|
146
|
+
const requestTimeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
123
147
|
async function requestJson(path, init) {
|
|
124
148
|
await ready();
|
|
125
149
|
const { idempotencyKey, ...requestInit } = init;
|
|
126
150
|
const headers = await authHeaders();
|
|
127
151
|
if (idempotencyKey)
|
|
128
152
|
headers['Idempotency-Key'] = idempotencyKey;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const
|
|
153
|
+
// Deadline: abort the request after `timeoutMs` so a black-holed server
|
|
154
|
+
// can't hang the caller forever (fetch has NO default timeout in browsers,
|
|
155
|
+
// and only undici's generous defaults in Node). A caller-supplied signal
|
|
156
|
+
// is combined with the deadline via a shared controller — the portable
|
|
157
|
+
// equivalent of `AbortSignal.any([caller, AbortSignal.timeout(t)])`,
|
|
158
|
+
// which older runtimes (and the jsdom test env) don't implement. The
|
|
159
|
+
// same pattern already guards `query/client.ts` and `BootstrapFetcher`.
|
|
160
|
+
const callerSignal = requestInit.signal ?? undefined;
|
|
161
|
+
const controller = new AbortController();
|
|
162
|
+
const onCallerAbort = () => { controller.abort(callerSignal?.reason); };
|
|
163
|
+
if (callerSignal) {
|
|
164
|
+
if (callerSignal.aborted)
|
|
165
|
+
onCallerAbort();
|
|
166
|
+
else
|
|
167
|
+
callerSignal.addEventListener('abort', onCallerAbort, { once: true });
|
|
168
|
+
}
|
|
169
|
+
let timedOut = false;
|
|
170
|
+
const deadline = requestTimeoutMs > 0
|
|
171
|
+
? setTimeout(() => {
|
|
172
|
+
timedOut = true;
|
|
173
|
+
controller.abort();
|
|
174
|
+
}, requestTimeoutMs)
|
|
175
|
+
: null;
|
|
176
|
+
let res;
|
|
177
|
+
let bodyText;
|
|
178
|
+
try {
|
|
179
|
+
res = await fetchImpl(endpoint(path), {
|
|
180
|
+
...requestInit,
|
|
181
|
+
signal: controller.signal,
|
|
182
|
+
headers: {
|
|
183
|
+
...headers,
|
|
184
|
+
...requestInit.headers,
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
// Keep the deadline armed while the body streams — a server that sends
|
|
188
|
+
// headers then stalls the body is the same hang with better manners.
|
|
189
|
+
bodyText = await res.text();
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
if (timedOut) {
|
|
193
|
+
// Retryable by contract: `wait_for_timeout` is a registered transient
|
|
194
|
+
// transport code, so `isRetryableCode` steers callers to retry.
|
|
195
|
+
throw new AbloConnectionError(`The Ablo API did not respond within ${requestTimeoutMs}ms ` +
|
|
196
|
+
`(${requestInit.method ?? 'GET'} ${path}). The request was aborted; ` +
|
|
197
|
+
'it is safe to retry.', { code: 'wait_for_timeout', cause: error });
|
|
198
|
+
}
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
finally {
|
|
202
|
+
if (deadline)
|
|
203
|
+
clearTimeout(deadline);
|
|
204
|
+
callerSignal?.removeEventListener('abort', onCallerAbort);
|
|
205
|
+
}
|
|
137
206
|
const body = parseBody(bodyText);
|
|
138
207
|
if (!res.ok) {
|
|
139
208
|
throw translateHttpError(res.status, body ?? `Ablo API request failed: ${res.status} ${res.statusText}`, res.headers.get('x-request-id') ?? undefined);
|
|
@@ -430,11 +499,11 @@ export function createProtocolClient(options) {
|
|
|
430
499
|
queue: claimOptions.queue,
|
|
431
500
|
}),
|
|
432
501
|
});
|
|
433
|
-
// The fair-queue grant is
|
|
434
|
-
//
|
|
435
|
-
//
|
|
436
|
-
//
|
|
437
|
-
//
|
|
502
|
+
// The fair-queue grant is pushed over a WebSocket (`claim_granted`), which
|
|
503
|
+
// this stateless HTTP client doesn't hold. Returning a handle here would be
|
|
504
|
+
// a phantom holder — a lease we can't confirm is ours. So a queued response
|
|
505
|
+
// is surfaced as a typed claimed signal; callers that need to wait in line
|
|
506
|
+
// use the realtime (WebSocket-backed) `ablo.<model>.claim`.
|
|
438
507
|
if (body.status === 'queued') {
|
|
439
508
|
throw new AbloClaimedError(`Target ${claimOptions.target.model}/${claimOptions.target.id} is held; ` +
|
|
440
509
|
`queued at position ${body.position ?? 0}. The HTTP client can't await ` +
|
|
@@ -448,9 +517,24 @@ export function createProtocolClient(options) {
|
|
|
448
517
|
released = true;
|
|
449
518
|
await requestJson(`/v1/claims/${encodeURIComponent(id)}`, { method: 'DELETE' });
|
|
450
519
|
};
|
|
520
|
+
// The by-id twin of the model-scoped heartbeat — same reply contract.
|
|
521
|
+
const heartbeat = async (beatOptions) => {
|
|
522
|
+
const resolved = resolveHeartbeatOptions(beatOptions);
|
|
523
|
+
const reply = await requestJson(`/v1/claims/${encodeURIComponent(id)}/heartbeat`, {
|
|
524
|
+
method: 'POST',
|
|
525
|
+
body: JSON.stringify({
|
|
526
|
+
...(resolved.ttl !== undefined ? { ttl: resolved.ttl } : {}),
|
|
527
|
+
...(resolved.details !== undefined
|
|
528
|
+
? { details: resolved.details }
|
|
529
|
+
: {}),
|
|
530
|
+
}),
|
|
531
|
+
});
|
|
532
|
+
return heldHeartbeatReply(reply, `claim ${id}`);
|
|
533
|
+
};
|
|
451
534
|
return {
|
|
452
535
|
object: 'claim',
|
|
453
536
|
id,
|
|
537
|
+
heartbeat,
|
|
454
538
|
reason: claimOptions.reason,
|
|
455
539
|
target: {
|
|
456
540
|
type: claimOptions.target.model,
|
|
@@ -471,6 +555,23 @@ export function createProtocolClient(options) {
|
|
|
471
555
|
waitFor(target, options) {
|
|
472
556
|
return waitForNoClaims(target, options);
|
|
473
557
|
},
|
|
558
|
+
async heartbeatAll(options) {
|
|
559
|
+
const reply = await requestJson('/v1/claims/heartbeat', {
|
|
560
|
+
method: 'POST',
|
|
561
|
+
body: JSON.stringify(options?.ttl !== undefined ? { ttl: options.ttl } : {}),
|
|
562
|
+
});
|
|
563
|
+
return (reply.results ?? []).flatMap((entry) => typeof entry.claimId === 'string' && typeof entry.expiresAt === 'number'
|
|
564
|
+
? [
|
|
565
|
+
{
|
|
566
|
+
claimId: entry.claimId,
|
|
567
|
+
expiresAt: entry.expiresAt,
|
|
568
|
+
...(entry.queueDepth !== undefined
|
|
569
|
+
? { queueDepth: entry.queueDepth }
|
|
570
|
+
: {}),
|
|
571
|
+
},
|
|
572
|
+
]
|
|
573
|
+
: []);
|
|
574
|
+
},
|
|
474
575
|
};
|
|
475
576
|
async function listModel(modelName, options) {
|
|
476
577
|
const params = new URLSearchParams();
|
|
@@ -502,11 +603,11 @@ export function createProtocolClient(options) {
|
|
|
502
603
|
const query = await requestJson(`/v1/models/${encodeURIComponent(modelName)}/${encodeURIComponent(params.id)}`, {
|
|
503
604
|
method: 'GET',
|
|
504
605
|
});
|
|
505
|
-
//
|
|
606
|
+
// A miss is `data: undefined`, not a thrown error. The WebSocket client's
|
|
506
607
|
// `retrieve` returns `T | undefined` for a missing row; throwing only here
|
|
507
608
|
// made the obvious read ("does this row exist?") a hard edge that an agent
|
|
508
|
-
// had to wrap in try/catch. Both transports
|
|
509
|
-
// data. Callers branch on `.data` (
|
|
609
|
+
// had to wrap in try/catch. Both transports agree: an absent row means absent
|
|
610
|
+
// data. Callers branch on `.data` (the documented `.data?.x` usage).
|
|
510
611
|
// Normalize a miss to `undefined` (the server may send `null` or omit it).
|
|
511
612
|
const data = (query.data ?? undefined);
|
|
512
613
|
return {
|
|
@@ -516,16 +617,16 @@ export function createProtocolClient(options) {
|
|
|
516
617
|
};
|
|
517
618
|
}
|
|
518
619
|
/**
|
|
519
|
-
*
|
|
520
|
-
* that mirrors `ablo.<model>.create/update/delete`:
|
|
620
|
+
* A single-operation mutation over the model-scoped routes — the canonical
|
|
621
|
+
* surface that mirrors `ablo.<model>.create/update/delete`:
|
|
521
622
|
*
|
|
522
623
|
* POST /v1/models/:model create
|
|
523
624
|
* PATCH /v1/models/:model/:id update
|
|
524
625
|
* DELETE /v1/models/:model/:id delete
|
|
525
626
|
*
|
|
526
|
-
*
|
|
527
|
-
*
|
|
528
|
-
*
|
|
627
|
+
* The `commits.create(...)` resource remains the path for atomic
|
|
628
|
+
* multi-operation envelopes; this helper handles the one-operation,
|
|
629
|
+
* one-record case.
|
|
529
630
|
*/
|
|
530
631
|
async function mutateModel(action, modelName, id, data, options) {
|
|
531
632
|
assertWriteOptions(options && {
|
|
@@ -569,9 +670,8 @@ export function createProtocolClient(options) {
|
|
|
569
670
|
});
|
|
570
671
|
}
|
|
571
672
|
catch (error) {
|
|
572
|
-
//
|
|
573
|
-
//
|
|
574
|
-
// collisions here too; this single row IS the fallback target.
|
|
673
|
+
// The per-model write door (`ablo.<model>.update/create/delete`). Capture
|
|
674
|
+
// coordination collisions here too; this single row is the fallback target.
|
|
575
675
|
recordCoordinationConflict(error, clientTxId, [{ model: modelName, id }]);
|
|
576
676
|
throw error;
|
|
577
677
|
}
|
|
@@ -617,9 +717,25 @@ export function createProtocolClient(options) {
|
|
|
617
717
|
throw new AbloClaimedError(`Target ${name}/${params.id} is held; queued at position ${body.position ?? 0}. ` +
|
|
618
718
|
`The HTTP client cannot await the grant without a WebSocket.`, { code: 'claim_queued' });
|
|
619
719
|
}
|
|
620
|
-
|
|
720
|
+
// `claimId` is the field name the queued response uses; check it alongside
|
|
721
|
+
// the other id shapes the response may carry.
|
|
722
|
+
return body.claim?.id ?? body.id ?? body.claimId ?? createClaimId();
|
|
621
723
|
};
|
|
622
724
|
const releaseClaim = (params) => requestJson(claimPath(isClaimHandle(params) ? params.target.id : params.id), { method: 'DELETE' }).then(() => undefined);
|
|
725
|
+
// One beat on the held lease. A lapsed lease answers `claim_lost`
|
|
726
|
+
// (409), which the wire error mapping surfaces as AbloClaimedError —
|
|
727
|
+
// the definitive signal that stops the auto-heartbeat loop.
|
|
728
|
+
const heartbeatClaim = async (id, claimId, options) => {
|
|
729
|
+
const reply = await requestJson(`${claimPath(id)}/heartbeat`, {
|
|
730
|
+
method: 'POST',
|
|
731
|
+
body: JSON.stringify({
|
|
732
|
+
claimId,
|
|
733
|
+
...(options.ttl !== undefined ? { ttl: options.ttl } : {}),
|
|
734
|
+
...(options.details !== undefined ? { details: options.details } : {}),
|
|
735
|
+
}),
|
|
736
|
+
});
|
|
737
|
+
return heldHeartbeatReply(reply, `claim ${claimId} on ${name}/${id}`);
|
|
738
|
+
};
|
|
623
739
|
async function claimImpl(params) {
|
|
624
740
|
const claimId = await acquireClaim(params);
|
|
625
741
|
observability?.captureClaim({
|
|
@@ -637,7 +753,33 @@ export function createProtocolClient(options) {
|
|
|
637
753
|
if (data === undefined) {
|
|
638
754
|
throw new AbloNotFoundError(`Cannot claim ${name}/${params.id}: it does not exist (or is outside this credential's scope).`, [params.id]);
|
|
639
755
|
}
|
|
640
|
-
const
|
|
756
|
+
const heartbeat = async (beatOptions) => {
|
|
757
|
+
const resolved = resolveHeartbeatOptions(beatOptions);
|
|
758
|
+
const beat = await heartbeatClaim(params.id, claimId, {
|
|
759
|
+
ttl: resolved.ttl ?? params.ttl,
|
|
760
|
+
...(resolved.details !== undefined
|
|
761
|
+
? { details: resolved.details }
|
|
762
|
+
: {}),
|
|
763
|
+
});
|
|
764
|
+
params.onHeartbeat?.(beat);
|
|
765
|
+
return beat;
|
|
766
|
+
};
|
|
767
|
+
// Opt-in auto-heartbeat — the background-worker cadence. The stateless
|
|
768
|
+
// HTTP claim defaults to the server's 60s acquire window when no TTL
|
|
769
|
+
// was requested, so the default cadence lands at 20s beats.
|
|
770
|
+
const stopHeartbeatLoop = params.heartbeat
|
|
771
|
+
? startClaimHeartbeatLoop({
|
|
772
|
+
beat: () => heartbeat(),
|
|
773
|
+
intervalMs: heartbeatCadenceMs(params.ttl !== undefined ? toMs(params.ttl) : 60_000, params.heartbeat),
|
|
774
|
+
...(params.onHeartbeatLost
|
|
775
|
+
? { onLost: params.onHeartbeatLost }
|
|
776
|
+
: {}),
|
|
777
|
+
})
|
|
778
|
+
: undefined;
|
|
779
|
+
const release = () => {
|
|
780
|
+
stopHeartbeatLoop?.();
|
|
781
|
+
return releaseClaim(params);
|
|
782
|
+
};
|
|
641
783
|
return {
|
|
642
784
|
object: 'claim',
|
|
643
785
|
id: claimId,
|
|
@@ -657,6 +799,7 @@ export function createProtocolClient(options) {
|
|
|
657
799
|
revoke: () => {
|
|
658
800
|
void release().catch(() => { });
|
|
659
801
|
},
|
|
802
|
+
heartbeat,
|
|
660
803
|
[Symbol.asyncDispose]: release,
|
|
661
804
|
};
|
|
662
805
|
}
|
|
@@ -675,8 +818,8 @@ export function createProtocolClient(options) {
|
|
|
675
818
|
reorder: async (params) => {
|
|
676
819
|
await requestJson(`${claimPath(params.id)}/reorder`, {
|
|
677
820
|
method: 'POST',
|
|
678
|
-
// The reorder route's payload is `{ heldBy, claimId }[]` — Claim's id
|
|
679
|
-
//
|
|
821
|
+
// The reorder route's payload is `{ heldBy, claimId }[]` — a Claim's id
|
|
822
|
+
// is the claimId.
|
|
680
823
|
body: JSON.stringify({ order: params.order.map((i) => ({ heldBy: i.heldBy, claimId: i.id })) }),
|
|
681
824
|
});
|
|
682
825
|
},
|
|
@@ -688,7 +831,13 @@ export function createProtocolClient(options) {
|
|
|
688
831
|
if (isClaimHandle(claimInput)) {
|
|
689
832
|
return run({ ...input, claimRef: { id: claimInput.id }, claim: undefined });
|
|
690
833
|
}
|
|
691
|
-
|
|
834
|
+
// `isClaimHandle` ruled out the handle form above; the generic mismatch
|
|
835
|
+
// (the union carries `Claim`, the guard narrows `Claim<T>`) keeps the
|
|
836
|
+
// compiler from subtracting it, so narrow to the inline-options form.
|
|
837
|
+
const claimId = await acquireClaim({
|
|
838
|
+
id,
|
|
839
|
+
...claimInput,
|
|
840
|
+
});
|
|
692
841
|
try {
|
|
693
842
|
return await run({ ...input, claimRef: { id: claimId }, claim: undefined });
|
|
694
843
|
}
|
|
@@ -742,8 +891,8 @@ export function createProtocolClient(options) {
|
|
|
742
891
|
// Confirm the write, then return the row — the obvious expectation of
|
|
743
892
|
// "create" (the WebSocket client already returns the row). The read-
|
|
744
893
|
// back is the authoritative server row, so it carries the framework
|
|
745
|
-
// defaults (createdAt
|
|
746
|
-
// an existing id, the
|
|
894
|
+
// defaults (createdAt, createdBy, …) and, for an idempotent re-create of
|
|
895
|
+
// an existing id, the existing row rather than the caller's input.
|
|
747
896
|
await mutateModel('create', name, id, params.data, {
|
|
748
897
|
...options,
|
|
749
898
|
wait: options?.wait ?? 'confirmed',
|
|
@@ -775,9 +924,9 @@ export function createProtocolClient(options) {
|
|
|
775
924
|
model,
|
|
776
925
|
sessions: {
|
|
777
926
|
async create(params) {
|
|
778
|
-
// Stateless mint: the configured key
|
|
779
|
-
// (no startup `rk_` exchange runs on this client).
|
|
780
|
-
// URL
|
|
927
|
+
// Stateless mint: the configured key is the control-plane credential here
|
|
928
|
+
// (no startup `rk_` exchange runs on this client). It reuses the resolved
|
|
929
|
+
// base URL and fetch; the shared `mintSession` handles the two server routes.
|
|
781
930
|
const apiKey = await resolveApiKeyValue(configuredApiKey);
|
|
782
931
|
if (!apiKey) {
|
|
783
932
|
throw new AbloAuthenticationError('sessions.create requires a secret (sk_) API key — call it from your backend, not the browser.', { code: 'apikey_missing' });
|