@abloatai/ablo 0.35.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -2
- package/CHANGELOG.md +71 -1929
- package/NOTICE +2 -2
- package/README.md +23 -532
- package/assets/banner.png +0 -0
- package/dist/auth.d.ts +2 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +2 -0
- package/dist/auth.js.map +1 -0
- package/dist/client.d.ts +3 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +3 -0
- package/dist/client.js.map +1 -0
- package/dist/coordination.d.ts +2 -0
- package/dist/coordination.d.ts.map +1 -0
- package/dist/coordination.js +2 -0
- package/dist/coordination.js.map +1 -0
- package/dist/index.d.ts +3 -112
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -161
- package/dist/index.js.map +1 -0
- package/dist/react.d.ts +4 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +3 -0
- package/dist/react.js.map +1 -0
- package/dist/schema.d.ts +2 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +2 -0
- package/dist/schema.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +2 -0
- package/dist/server.js.map +1 -0
- package/dist/source-conformance.d.ts +2 -0
- package/dist/source-conformance.d.ts.map +1 -0
- package/dist/source-conformance.js +2 -0
- package/dist/source-conformance.js.map +1 -0
- package/dist/source-drizzle.d.ts +2 -0
- package/dist/source-drizzle.d.ts.map +1 -0
- package/dist/source-drizzle.js +2 -0
- package/dist/source-drizzle.js.map +1 -0
- package/dist/source-kysely.d.ts +2 -0
- package/dist/source-kysely.d.ts.map +1 -0
- package/dist/source-kysely.js +2 -0
- package/dist/source-kysely.js.map +1 -0
- package/dist/source-next.d.ts +2 -0
- package/dist/source-next.d.ts.map +1 -0
- package/dist/source-next.js +2 -0
- package/dist/source-next.js.map +1 -0
- package/dist/source.d.ts +2 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +2 -0
- package/dist/source.js.map +1 -0
- package/dist/wire.d.ts +2 -0
- package/dist/wire.d.ts.map +1 -0
- package/dist/wire.js +2 -0
- package/dist/wire.js.map +1 -0
- package/docs/agents.md +2 -2
- package/docs/api-keys.md +13 -12
- package/docs/api.md +15 -53
- package/docs/audit.md +4 -3
- package/docs/cli.md +11 -11
- package/docs/client-behavior.md +9 -9
- package/docs/concurrency-convention.md +28 -42
- package/docs/coordination.md +228 -86
- package/docs/data-sources.md +5 -5
- package/docs/debugging.md +34 -12
- package/docs/deployment.md +8 -8
- package/docs/examples/agent-human.md +4 -4
- package/docs/examples/ai-sdk-tool.md +1 -1
- package/docs/examples/existing-python-backend.md +15 -4
- package/docs/examples/nextjs.md +27 -6
- package/docs/examples/scoped-agent.md +3 -3
- package/docs/examples/server-agent.md +2 -2
- package/docs/groups.md +57 -3
- package/docs/guarantees.md +37 -10
- package/docs/how-it-works.md +32 -8
- package/docs/idempotency.md +6 -6
- package/docs/identity.md +24 -24
- package/docs/index.md +8 -8
- package/docs/integration-guide.md +38 -16
- package/docs/internal/README.md +18 -0
- package/docs/internal/agent-fleet-coordination-design.md +171 -0
- package/docs/internal/agent-orchestration.md +58 -0
- package/docs/internal/commit-identifiers.md +91 -0
- package/docs/internal/concurrency-open-decisions.md +37 -0
- package/docs/internal/data-source-reverse-channel.md +150 -0
- package/docs/internal/per-field-conflict-detection.md +165 -0
- package/docs/internal/postgres-replication.md +64 -0
- package/docs/internal/serializable-schema.md +119 -0
- package/docs/internal/structure.md +32 -0
- package/docs/mcp.md +9 -9
- package/docs/migration.md +37 -18
- package/docs/projects.md +1 -1
- package/docs/quickstart.md +2 -2
- package/docs/react.md +24 -13
- package/docs/schema-contract.md +3 -3
- package/docs/sessions.md +91 -37
- package/docs/webhooks.md +9 -9
- package/examples/README.md +2 -2
- package/examples/data-source/README.md +1 -1
- package/examples/data-source/ablo-driver.ts +1 -1
- package/examples/data-source/customer-server.ts +1 -1
- package/examples/data-source/run.ts +1 -1
- package/examples/data-source/schema.ts +1 -1
- package/examples/quickstart.ts +2 -2
- package/llms.txt +12 -12
- package/package.json +64 -174
- package/dist/BaseSyncedStore.d.ts +0 -823
- package/dist/BaseSyncedStore.js +0 -1955
- package/dist/Database.d.ts +0 -335
- package/dist/Database.js +0 -1500
- package/dist/InstanceCache.d.ts +0 -233
- package/dist/InstanceCache.js +0 -1164
- package/dist/LazyReferenceCollection.d.ts +0 -177
- package/dist/LazyReferenceCollection.js +0 -461
- package/dist/Model.d.ts +0 -444
- package/dist/Model.js +0 -909
- package/dist/ModelRegistry.d.ts +0 -221
- package/dist/ModelRegistry.js +0 -537
- package/dist/NetworkMonitor.d.ts +0 -26
- package/dist/NetworkMonitor.js +0 -77
- package/dist/RuntimeContext.d.ts +0 -52
- package/dist/RuntimeContext.js +0 -80
- package/dist/SyncClient.d.ts +0 -551
- package/dist/SyncClient.js +0 -2199
- package/dist/adapters/alwaysOnline.d.ts +0 -14
- package/dist/adapters/alwaysOnline.js +0 -17
- package/dist/adapters/inMemoryStorage.d.ts +0 -31
- package/dist/adapters/inMemoryStorage.js +0 -110
- package/dist/ai-sdk/coordinatedTool.d.ts +0 -120
- package/dist/ai-sdk/coordinatedTool.js +0 -134
- package/dist/ai-sdk/coordinationContext.d.ts +0 -46
- package/dist/ai-sdk/coordinationContext.js +0 -106
- package/dist/ai-sdk/index.d.ts +0 -121
- package/dist/ai-sdk/index.js +0 -121
- package/dist/ai-sdk/wrap.d.ts +0 -65
- package/dist/ai-sdk/wrap.js +0 -39
- package/dist/auth/index.d.ts +0 -1
- package/dist/auth/index.js +0 -8
- package/dist/batching/index.d.ts +0 -55
- package/dist/batching/index.js +0 -147
- package/dist/cli.cjs +0 -288600
- package/dist/client/Ablo.d.ts +0 -231
- package/dist/client/Ablo.js +0 -149
- package/dist/client/abloClient.d.ts +0 -309
- package/dist/client/abloClient.js +0 -13
- package/dist/client/clientPrelude.d.ts +0 -52
- package/dist/client/clientPrelude.js +0 -60
- package/dist/client/consoleLogger.d.ts +0 -35
- package/dist/client/consoleLogger.js +0 -44
- package/dist/client/coreClient.d.ts +0 -60
- package/dist/client/coreClient.js +0 -118
- package/dist/client/createInternalComponents.d.ts +0 -46
- package/dist/client/createInternalComponents.js +0 -92
- package/dist/client/createModelProxy.d.ts +0 -228
- package/dist/client/createModelProxy.js +0 -818
- package/dist/client/humans.d.ts +0 -48
- package/dist/client/humans.js +0 -52
- package/dist/client/modelRegistration.d.ts +0 -10
- package/dist/client/modelRegistration.js +0 -312
- package/dist/client/options.d.ts +0 -461
- package/dist/client/options.js +0 -7
- package/dist/client/reactiveEngine.d.ts +0 -48
- package/dist/client/reactiveEngine.js +0 -910
- package/dist/client/resourceTypes.d.ts +0 -12
- package/dist/client/resourceTypes.js +0 -10
- package/dist/client/schemaConfig.d.ts +0 -44
- package/dist/client/schemaConfig.js +0 -185
- package/dist/client/validateAbloOptions.d.ts +0 -42
- package/dist/client/validateAbloOptions.js +0 -43
- package/dist/client/wsMutationExecutor.d.ts +0 -27
- package/dist/client/wsMutationExecutor.js +0 -72
- package/dist/context.d.ts +0 -29
- package/dist/context.js +0 -58
- package/dist/coordination/ClaimLog.d.ts +0 -26
- package/dist/coordination/ClaimLog.js +0 -32
- package/dist/coordination/index.d.ts +0 -1
- package/dist/coordination/index.js +0 -8
- package/dist/core/DatabaseManager.d.ts +0 -105
- package/dist/core/DatabaseManager.js +0 -387
- package/dist/core/QueryProcessor.d.ts +0 -75
- package/dist/core/QueryProcessor.js +0 -255
- package/dist/core/QueryView.d.ts +0 -79
- package/dist/core/QueryView.js +0 -218
- package/dist/core/StoreManager.d.ts +0 -112
- package/dist/core/StoreManager.js +0 -302
- package/dist/core/ViewRegistry.d.ts +0 -20
- package/dist/core/ViewRegistry.js +0 -55
- package/dist/core/index.d.ts +0 -33
- package/dist/core/index.js +0 -48
- package/dist/core/openIDBWithTimeout.d.ts +0 -65
- package/dist/core/openIDBWithTimeout.js +0 -153
- package/dist/core/queryUtils.d.ts +0 -45
- package/dist/core/queryUtils.js +0 -69
- package/dist/core/storeContract.d.ts +0 -145
- package/dist/core/storeContract.js +0 -12
- package/dist/docs/catalog.d.ts +0 -72
- package/dist/docs/catalog.js +0 -227
- package/dist/docs/index.d.ts +0 -10
- package/dist/docs/index.js +0 -10
- package/dist/environment.d.ts +0 -1
- package/dist/environment.js +0 -8
- package/dist/interfaces/index.d.ts +0 -311
- package/dist/interfaces/index.js +0 -9
- package/dist/keys/index.d.ts +0 -1
- package/dist/keys/index.js +0 -8
- package/dist/mutators/RecordingMutation.d.ts +0 -36
- package/dist/mutators/RecordingMutation.js +0 -182
- package/dist/mutators/Transaction.d.ts +0 -40
- package/dist/mutators/Transaction.js +0 -58
- package/dist/mutators/UndoManager.d.ts +0 -258
- package/dist/mutators/UndoManager.js +0 -658
- package/dist/mutators/defineMutators.d.ts +0 -60
- package/dist/mutators/defineMutators.js +0 -18
- package/dist/mutators/inverseOp.d.ts +0 -126
- package/dist/mutators/inverseOp.js +0 -71
- package/dist/mutators/mutateActions.d.ts +0 -45
- package/dist/mutators/mutateActions.js +0 -105
- package/dist/mutators/readerActions.d.ts +0 -33
- package/dist/mutators/readerActions.js +0 -57
- package/dist/mutators/undoApply.d.ts +0 -51
- package/dist/mutators/undoApply.js +0 -117
- package/dist/policy/index.d.ts +0 -21
- package/dist/policy/index.js +0 -20
- package/dist/query/client.d.ts +0 -61
- package/dist/query/client.js +0 -137
- package/dist/query/types.d.ts +0 -85
- package/dist/query/types.js +0 -16
- package/dist/react/AbloProvider.d.ts +0 -230
- package/dist/react/AbloProvider.js +0 -455
- package/dist/react/ClientSideSuspense.d.ts +0 -36
- package/dist/react/ClientSideSuspense.js +0 -17
- package/dist/react/DefaultFallback.d.ts +0 -24
- package/dist/react/DefaultFallback.js +0 -43
- package/dist/react/context.d.ts +0 -55
- package/dist/react/context.js +0 -29
- package/dist/react/index.d.ts +0 -61
- package/dist/react/index.js +0 -66
- package/dist/react/internalContext.d.ts +0 -33
- package/dist/react/internalContext.js +0 -3
- package/dist/react/useAblo.d.ts +0 -75
- package/dist/react/useAblo.js +0 -102
- package/dist/react/useCurrentUserId.d.ts +0 -22
- package/dist/react/useCurrentUserId.js +0 -34
- package/dist/react/useErrorListener.d.ts +0 -20
- package/dist/react/useErrorListener.js +0 -38
- package/dist/react/useMutationFailureListener.d.ts +0 -26
- package/dist/react/useMutationFailureListener.js +0 -38
- package/dist/react/useMutators.d.ts +0 -56
- package/dist/react/useMutators.js +0 -84
- package/dist/react/useReactive.d.ts +0 -35
- package/dist/react/useReactive.js +0 -123
- package/dist/react/useSyncStatus.d.ts +0 -59
- package/dist/react/useSyncStatus.js +0 -76
- package/dist/react/useUndoScope.d.ts +0 -34
- package/dist/react/useUndoScope.js +0 -81
- package/dist/schema/coordination.d.ts +0 -112
- package/dist/schema/coordination.js +0 -129
- package/dist/schema/ddl.d.ts +0 -97
- package/dist/schema/ddl.js +0 -491
- package/dist/schema/ddlLock.d.ts +0 -35
- package/dist/schema/ddlLock.js +0 -46
- package/dist/schema/diff.d.ts +0 -225
- package/dist/schema/diff.js +0 -289
- package/dist/schema/generate.d.ts +0 -19
- package/dist/schema/generate.js +0 -86
- package/dist/schema/index.d.ts +0 -41
- package/dist/schema/index.js +0 -76
- package/dist/schema/queries.d.ts +0 -201
- package/dist/schema/queries.js +0 -144
- package/dist/schema/select.d.ts +0 -40
- package/dist/schema/select.js +0 -87
- package/dist/schema/serialize.d.ts +0 -115
- package/dist/schema/serialize.js +0 -262
- package/dist/schema/sugar.d.ts +0 -109
- package/dist/schema/sugar.js +0 -83
- package/dist/schema/syncDeltaRow.d.ts +0 -6
- package/dist/schema/syncDeltaRow.js +0 -6
- package/dist/server/adapter.d.ts +0 -173
- package/dist/server/adapter.js +0 -18
- package/dist/server/commit.d.ts +0 -107
- package/dist/server/commit.js +0 -1
- package/dist/server/index.d.ts +0 -14
- package/dist/server/index.js +0 -2
- package/dist/server/readConfig.d.ts +0 -80
- package/dist/server/readConfig.js +0 -8
- package/dist/server/storageMode.d.ts +0 -23
- package/dist/server/storageMode.js +0 -17
- package/dist/source/adapter.d.ts +0 -81
- package/dist/source/adapter.js +0 -22
- package/dist/source/adapters/drizzle.d.ts +0 -48
- package/dist/source/adapters/drizzle.js +0 -219
- package/dist/source/adapters/kysely.d.ts +0 -42
- package/dist/source/adapters/kysely.js +0 -205
- package/dist/source/adapters/kyselyMutationCore.d.ts +0 -76
- package/dist/source/adapters/kyselyMutationCore.js +0 -125
- package/dist/source/adapters/memory.d.ts +0 -13
- package/dist/source/adapters/memory.js +0 -130
- package/dist/source/adapters/prisma.d.ts +0 -63
- package/dist/source/adapters/prisma.js +0 -202
- package/dist/source/conformance.d.ts +0 -37
- package/dist/source/conformance.js +0 -215
- package/dist/source/connector.d.ts +0 -95
- package/dist/source/connector.js +0 -266
- package/dist/source/connectorProtocol.d.ts +0 -154
- package/dist/source/connectorProtocol.js +0 -163
- package/dist/source/contract.d.ts +0 -195
- package/dist/source/contract.js +0 -164
- package/dist/source/factory.d.ts +0 -92
- package/dist/source/factory.js +0 -286
- package/dist/source/footprint.d.ts +0 -111
- package/dist/source/footprint.js +0 -0
- package/dist/source/idempotency.d.ts +0 -61
- package/dist/source/idempotency.js +0 -144
- package/dist/source/index.d.ts +0 -23
- package/dist/source/index.js +0 -30
- package/dist/source/migrations.d.ts +0 -21
- package/dist/source/migrations.js +0 -103
- package/dist/source/next.d.ts +0 -32
- package/dist/source/next.js +0 -25
- package/dist/source/pushQueue.d.ts +0 -134
- package/dist/source/pushQueue.js +0 -256
- package/dist/source/signing.d.ts +0 -92
- package/dist/source/signing.js +0 -162
- package/dist/source/types.d.ts +0 -401
- package/dist/source/types.js +0 -59
- package/dist/stores/ObjectStore.d.ts +0 -115
- package/dist/stores/ObjectStore.js +0 -393
- package/dist/stores/ObjectStoreContract.d.ts +0 -38
- package/dist/stores/ObjectStoreContract.js +0 -1
- package/dist/stores/SyncActionStore.d.ts +0 -97
- package/dist/stores/SyncActionStore.js +0 -504
- package/dist/stores/syncAction.d.ts +0 -26
- package/dist/stores/syncAction.js +0 -16
- package/dist/surface.d.ts +0 -36
- package/dist/surface.js +0 -75
- package/dist/sync/BootstrapFetcher.d.ts +0 -280
- package/dist/sync/BootstrapFetcher.js +0 -962
- package/dist/sync/ConnectionManager.d.ts +0 -8
- package/dist/sync/ConnectionManager.js +0 -8
- package/dist/sync/OnDemandLoader.d.ts +0 -228
- package/dist/sync/OnDemandLoader.js +0 -742
- package/dist/sync/SubscriptionManager.d.ts +0 -159
- package/dist/sync/SubscriptionManager.js +0 -243
- package/dist/sync/SyncWebSocket.d.ts +0 -173
- package/dist/sync/SyncWebSocket.js +0 -438
- package/dist/sync/awaitClaimGrant.d.ts +0 -6
- package/dist/sync/awaitClaimGrant.js +0 -6
- package/dist/sync/bootstrapApply.d.ts +0 -70
- package/dist/sync/bootstrapApply.js +0 -73
- package/dist/sync/commitFrames.d.ts +0 -8
- package/dist/sync/commitFrames.js +0 -8
- package/dist/sync/contextPorts.d.ts +0 -18
- package/dist/sync/contextPorts.js +0 -31
- package/dist/sync/createClaimStream.d.ts +0 -7
- package/dist/sync/createClaimStream.js +0 -7
- package/dist/sync/createPresenceStream.d.ts +0 -69
- package/dist/sync/createPresenceStream.js +0 -200
- package/dist/sync/createSnapshot.d.ts +0 -29
- package/dist/sync/createSnapshot.js +0 -118
- package/dist/sync/credentialLifecycle.d.ts +0 -7
- package/dist/sync/credentialLifecycle.js +0 -7
- package/dist/sync/deltaPipeline.d.ts +0 -113
- package/dist/sync/deltaPipeline.js +0 -261
- package/dist/sync/groupChange.d.ts +0 -113
- package/dist/sync/groupChange.js +0 -242
- package/dist/sync/participants.d.ts +0 -115
- package/dist/sync/participants.js +0 -344
- package/dist/sync/persistedPrefix.d.ts +0 -12
- package/dist/sync/persistedPrefix.js +0 -22
- package/dist/sync/schemaDrift.d.ts +0 -55
- package/dist/sync/schemaDrift.js +0 -53
- package/dist/sync/schemas.d.ts +0 -70
- package/dist/sync/schemas.js +0 -94
- package/dist/sync/syncCursor.d.ts +0 -40
- package/dist/sync/syncCursor.js +0 -55
- package/dist/sync/syncPlan.d.ts +0 -54
- package/dist/sync/syncPlan.js +0 -50
- package/dist/sync/wsFrameHandlers.d.ts +0 -8
- package/dist/sync/wsFrameHandlers.js +0 -8
- 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/httpResponses.d.ts +0 -70
- package/dist/testing/fixtures/httpResponses.js +0 -90
- 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 -186
- 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 -72
- 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/transaction/ablo.d.ts +0 -88
- package/dist/transaction/ablo.js +0 -33
- package/dist/transaction/auth/apiKey.d.ts +0 -152
- package/dist/transaction/auth/apiKey.js +0 -419
- package/dist/transaction/auth/bootstrapScope.d.ts +0 -15
- package/dist/transaction/auth/bootstrapScope.js +0 -1
- package/dist/transaction/auth/capability.d.ts +0 -177
- package/dist/transaction/auth/capability.js +0 -199
- package/dist/transaction/auth/credentialEndpoint.d.ts +0 -61
- package/dist/transaction/auth/credentialEndpoint.js +0 -86
- package/dist/transaction/auth/credentialPolicy.d.ts +0 -148
- package/dist/transaction/auth/credentialPolicy.js +0 -125
- package/dist/transaction/auth/credentialSource.d.ts +0 -30
- package/dist/transaction/auth/credentialSource.js +0 -55
- package/dist/transaction/auth/hostedEndpoints.d.ts +0 -21
- package/dist/transaction/auth/hostedEndpoints.js +0 -21
- package/dist/transaction/auth/identity.d.ts +0 -55
- package/dist/transaction/auth/identity.js +0 -210
- package/dist/transaction/auth/index.d.ts +0 -162
- package/dist/transaction/auth/index.js +0 -304
- package/dist/transaction/auth/schemas.d.ts +0 -59
- package/dist/transaction/auth/schemas.js +0 -85
- package/dist/transaction/auth/sessionMint.d.ts +0 -28
- package/dist/transaction/auth/sessionMint.js +0 -85
- package/dist/transaction/coordination/awaitClaimGrant.d.ts +0 -49
- package/dist/transaction/coordination/awaitClaimGrant.js +0 -112
- package/dist/transaction/coordination/claimHeartbeatLoop.d.ts +0 -50
- package/dist/transaction/coordination/claimHeartbeatLoop.js +0 -88
- package/dist/transaction/coordination/claimMeta.d.ts +0 -49
- package/dist/transaction/coordination/claimMeta.js +0 -52
- package/dist/transaction/coordination/createClaimStream.d.ts +0 -64
- package/dist/transaction/coordination/createClaimStream.js +0 -475
- package/dist/transaction/coordination/events.d.ts +0 -74
- package/dist/transaction/coordination/events.js +0 -7
- package/dist/transaction/coordination/index.d.ts +0 -19
- package/dist/transaction/coordination/index.js +0 -44
- package/dist/transaction/coordination/locator.d.ts +0 -83
- package/dist/transaction/coordination/locator.js +0 -82
- package/dist/transaction/coordination/schema.d.ts +0 -1473
- package/dist/transaction/coordination/schema.js +0 -1013
- package/dist/transaction/coordination/targetConflict.d.ts +0 -2
- package/dist/transaction/coordination/targetConflict.js +0 -103
- package/dist/transaction/coordination/trace.d.ts +0 -78
- package/dist/transaction/coordination/trace.js +0 -138
- package/dist/transaction/durableWrites.d.ts +0 -62
- package/dist/transaction/durableWrites.js +0 -71
- package/dist/transaction/environment.d.ts +0 -105
- package/dist/transaction/environment.js +0 -108
- package/dist/transaction/errorCodes.d.ts +0 -403
- package/dist/transaction/errorCodes.js +0 -480
- package/dist/transaction/errors.d.ts +0 -428
- package/dist/transaction/errors.js +0 -686
- package/dist/transaction/index.d.ts +0 -20
- package/dist/transaction/index.js +0 -20
- package/dist/transaction/keys/index.d.ts +0 -87
- package/dist/transaction/keys/index.js +0 -207
- package/dist/transaction/log/syncDeltaRow.d.ts +0 -158
- package/dist/transaction/log/syncDeltaRow.js +0 -95
- package/dist/transaction/logPosition.d.ts +0 -97
- package/dist/transaction/logPosition.js +0 -125
- package/dist/transaction/logger.d.ts +0 -16
- package/dist/transaction/logger.js +0 -7
- package/dist/transaction/observability.d.ts +0 -53
- package/dist/transaction/observability.js +0 -19
- package/dist/transaction/persistence.d.ts +0 -12
- package/dist/transaction/persistence.js +0 -11
- package/dist/transaction/plugin.d.ts +0 -192
- package/dist/transaction/plugin.js +0 -87
- package/dist/transaction/policy/types.d.ts +0 -217
- package/dist/transaction/policy/types.js +0 -126
- package/dist/transaction/resources/functionalUpdate.d.ts +0 -79
- package/dist/transaction/resources/functionalUpdate.js +0 -87
- package/dist/transaction/resources/httpResources.d.ts +0 -266
- package/dist/transaction/resources/httpResources.js +0 -7
- package/dist/transaction/resources/modelOperations.d.ts +0 -319
- package/dist/transaction/resources/modelOperations.js +0 -12
- package/dist/transaction/resources/mutationOptions.d.ts +0 -66
- package/dist/transaction/resources/mutationOptions.js +0 -9
- package/dist/transaction/resources/where.d.ts +0 -85
- package/dist/transaction/resources/where.js +0 -70
- package/dist/transaction/resources/writeOptionsSchema.d.ts +0 -47
- package/dist/transaction/resources/writeOptionsSchema.js +0 -73
- package/dist/transaction/schema/field.d.ts +0 -126
- package/dist/transaction/schema/field.js +0 -265
- package/dist/transaction/schema/loadStrategy.d.ts +0 -45
- package/dist/transaction/schema/loadStrategy.js +0 -46
- package/dist/transaction/schema/model.d.ts +0 -379
- package/dist/transaction/schema/model.js +0 -123
- package/dist/transaction/schema/openapi.d.ts +0 -57
- package/dist/transaction/schema/openapi.js +0 -340
- package/dist/transaction/schema/relation.d.ts +0 -199
- package/dist/transaction/schema/relation.js +0 -104
- package/dist/transaction/schema/residency.d.ts +0 -29
- package/dist/transaction/schema/residency.js +0 -25
- package/dist/transaction/schema/roles.d.ts +0 -249
- package/dist/transaction/schema/roles.js +0 -230
- package/dist/transaction/schema/schema.d.ts +0 -324
- package/dist/transaction/schema/schema.js +0 -305
- package/dist/transaction/schema/tenancy.d.ts +0 -139
- package/dist/transaction/schema/tenancy.js +0 -190
- package/dist/transaction/transactionLayer.d.ts +0 -82
- package/dist/transaction/transactionLayer.js +0 -24
- package/dist/transaction/transactions/settlement/commitEnvelope.d.ts +0 -143
- package/dist/transaction/transactions/settlement/commitEnvelope.js +0 -161
- package/dist/transaction/transactions/settlement/httpCommitEnvelope.d.ts +0 -53
- package/dist/transaction/transactions/settlement/httpCommitEnvelope.js +0 -207
- package/dist/transaction/transactions/settlement/idempotencyKey.d.ts +0 -10
- package/dist/transaction/transactions/settlement/idempotencyKey.js +0 -9
- package/dist/transaction/transactions/settlement/pendingWrite.d.ts +0 -112
- package/dist/transaction/transactions/settlement/pendingWrite.js +0 -20
- package/dist/transaction/transport/commitFrames.d.ts +0 -90
- package/dist/transaction/transport/commitFrames.js +0 -134
- package/dist/transaction/transport/connectionManager.d.ts +0 -215
- package/dist/transaction/transport/connectionManager.js +0 -673
- package/dist/transaction/transport/credentialLifecycle.d.ts +0 -177
- package/dist/transaction/transport/credentialLifecycle.js +0 -324
- package/dist/transaction/transport/heartbeat.d.ts +0 -65
- package/dist/transaction/transport/heartbeat.js +0 -93
- package/dist/transaction/transport/httpClient.d.ts +0 -123
- package/dist/transaction/transport/httpClient.js +0 -145
- package/dist/transaction/transport/httpOptions.d.ts +0 -33
- package/dist/transaction/transport/httpOptions.js +0 -12
- package/dist/transaction/transport/httpTransport.d.ts +0 -8
- package/dist/transaction/transport/httpTransport.js +0 -1276
- package/dist/transaction/transport/networkProbe.d.ts +0 -84
- package/dist/transaction/transport/networkProbe.js +0 -207
- package/dist/transaction/transport/wsFrameHandlers.d.ts +0 -128
- package/dist/transaction/transport/wsFrameHandlers.js +0 -429
- package/dist/transaction/transport/wsTransport.d.ts +0 -576
- package/dist/transaction/transport/wsTransport.js +0 -1017
- package/dist/transaction/types/assertExact.d.ts +0 -17
- package/dist/transaction/types/assertExact.js +0 -1
- package/dist/transaction/types/global.d.ts +0 -107
- package/dist/transaction/types/global.js +0 -40
- package/dist/transaction/types/index.d.ts +0 -205
- package/dist/transaction/types/index.js +0 -56
- package/dist/transaction/types/modelData.d.ts +0 -10
- package/dist/transaction/types/modelData.js +0 -9
- package/dist/transaction/types/participant.d.ts +0 -20
- package/dist/transaction/types/participant.js +0 -10
- package/dist/transaction/types/streams.d.ts +0 -540
- package/dist/transaction/types/streams.js +0 -11
- package/dist/transaction/utils/asyncIterator.d.ts +0 -34
- package/dist/transaction/utils/asyncIterator.js +0 -135
- package/dist/transaction/utils/duration.d.ts +0 -25
- package/dist/transaction/utils/duration.js +0 -45
- package/dist/transaction/utils/json.d.ts +0 -57
- package/dist/transaction/utils/json.js +0 -276
- package/dist/transaction/wire/accountResponses.d.ts +0 -351
- package/dist/transaction/wire/accountResponses.js +0 -255
- package/dist/transaction/wire/auth.d.ts +0 -49
- package/dist/transaction/wire/auth.js +0 -57
- package/dist/transaction/wire/bootstrapReason.d.ts +0 -9
- package/dist/transaction/wire/bootstrapReason.js +0 -8
- package/dist/transaction/wire/claimEvent.d.ts +0 -76
- package/dist/transaction/wire/claimEvent.js +0 -73
- package/dist/transaction/wire/claims.d.ts +0 -463
- package/dist/transaction/wire/claims.js +0 -229
- package/dist/transaction/wire/commit.d.ts +0 -603
- package/dist/transaction/wire/commit.js +0 -321
- package/dist/transaction/wire/delta.d.ts +0 -250
- package/dist/transaction/wire/delta.js +0 -147
- package/dist/transaction/wire/errorEnvelope.d.ts +0 -72
- package/dist/transaction/wire/errorEnvelope.js +0 -123
- package/dist/transaction/wire/feedCursor.d.ts +0 -60
- package/dist/transaction/wire/feedCursor.js +0 -82
- package/dist/transaction/wire/feedEvent.d.ts +0 -177
- package/dist/transaction/wire/feedEvent.js +0 -39
- package/dist/transaction/wire/frames.d.ts +0 -194
- package/dist/transaction/wire/frames.js +0 -50
- package/dist/transaction/wire/inboundFrames.d.ts +0 -552
- package/dist/transaction/wire/inboundFrames.js +0 -116
- package/dist/transaction/wire/index.d.ts +0 -50
- package/dist/transaction/wire/index.js +0 -74
- package/dist/transaction/wire/listEnvelope.d.ts +0 -37
- package/dist/transaction/wire/listEnvelope.js +0 -42
- package/dist/transaction/wire/modelResponses.d.ts +0 -85
- package/dist/transaction/wire/modelResponses.js +0 -43
- package/dist/transaction/wire/protocol.d.ts +0 -38
- package/dist/transaction/wire/protocol.js +0 -38
- package/dist/transaction/wire/protocolVersion.d.ts +0 -73
- package/dist/transaction/wire/protocolVersion.js +0 -83
- package/dist/transactions/mutations/MutationQueue.d.ts +0 -655
- package/dist/transactions/mutations/MutationQueue.js +0 -2797
- package/dist/transactions/mutations/MutationStore.d.ts +0 -20
- package/dist/transactions/mutations/MutationStore.js +0 -53
- package/dist/transactions/mutations/UnconfirmedWrites.d.ts +0 -82
- package/dist/transactions/mutations/UnconfirmedWrites.js +0 -104
- package/dist/transactions/mutations/coalesceRules.d.ts +0 -58
- package/dist/transactions/mutations/coalesceRules.js +0 -140
- package/dist/transactions/mutations/commitLatency.d.ts +0 -52
- package/dist/transactions/mutations/commitLatency.js +0 -130
- package/dist/transactions/mutations/commitOutboxStore.d.ts +0 -28
- package/dist/transactions/mutations/commitOutboxStore.js +0 -26
- package/dist/transactions/mutations/commitPayload.d.ts +0 -164
- package/dist/transactions/mutations/commitPayload.js +0 -152
- package/dist/transactions/mutations/deltaConfirmation.d.ts +0 -59
- package/dist/transactions/mutations/deltaConfirmation.js +0 -233
- package/dist/transactions/mutations/durableWriteStore.d.ts +0 -14
- package/dist/transactions/mutations/durableWriteStore.js +0 -12
- package/dist/transactions/mutations/optimisticApply.d.ts +0 -49
- package/dist/transactions/mutations/optimisticApply.js +0 -65
- package/dist/transactions/mutations/replayValidation.d.ts +0 -186
- package/dist/transactions/mutations/replayValidation.js +0 -163
- package/dist/utils/mobxSetup.d.ts +0 -53
- package/dist/utils/mobxSetup.js +0 -330
- package/dist/webhooks/events.d.ts +0 -43
- package/dist/webhooks/events.js +0 -42
- package/dist/webhooks/index.d.ts +0 -8
- package/dist/webhooks/index.js +0 -8
- package/dist/wire/index.d.ts +0 -1
- package/dist/wire/index.js +0 -8
- package/docs/interaction-model.md +0 -99
package/docs/coordination.md
CHANGED
|
@@ -38,21 +38,35 @@ Reads stay open: reading a claimed row is allowed unless the caller explicitly
|
|
|
38
38
|
asks for claimed gating. A claim carries a TTL so a crashed holder is
|
|
39
39
|
auto-released and the queue advances.
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
>
|
|
47
|
-
>
|
|
48
|
-
>
|
|
49
|
-
>
|
|
50
|
-
>
|
|
51
|
-
>
|
|
52
|
-
>
|
|
53
|
-
>
|
|
54
|
-
|
|
55
|
-
|
|
41
|
+
A claim is also as narrow as you make it. Select one or more `fields` and
|
|
42
|
+
exclusion follows the target: **two claims on different fields of the same row
|
|
43
|
+
are both granted**, and only claims that share a field queue behind each other.
|
|
44
|
+
See [claiming part of a row](#claiming-part-of-a-row).
|
|
45
|
+
|
|
46
|
+
> **Transport: both wait — only the mechanism differs.** `claim({ id })` means
|
|
47
|
+
> "serialize me behind whoever holds this row" on every transport. The
|
|
48
|
+
> realtime client parks the promise on its socket and resolves it on the grant
|
|
49
|
+
> frame. The **stateless HTTP client** (`Ablo({ transport: 'http' })` — the
|
|
50
|
+
> transport server-side agents use) holds the same place in the same
|
|
51
|
+
> server-side FIFO line; under the hood it heartbeats its queued ticket until
|
|
52
|
+
> the line moves, then re-reads the row and resolves to the same held claim.
|
|
53
|
+
> The same snippet works on both.
|
|
54
|
+
>
|
|
55
|
+
> Shape the wait the same way on either transport: cap it with
|
|
56
|
+
> `waitTimeoutMs` (rejects `grant_timeout` and leaves the line), cancel it
|
|
57
|
+
> from outside with `signal` (an `AbortSignal`; rejects
|
|
58
|
+
> `claim_wait_aborted`), bound the line you'll join with `maxQueueDepth`
|
|
59
|
+
> (`queue_too_deep`), or skip waiting entirely with `queue: false` — the
|
|
60
|
+
> try-claim, which resolves `null` when the target is held (a declined try
|
|
61
|
+
> is not an error) and takes no place in line. For
|
|
62
|
+
> callers that manage the wait themselves, the ticket surface remains:
|
|
63
|
+
> `ablo.claims.get({ claimId })` polls a ticket to its grant,
|
|
64
|
+
> `ablo.claims.heartbeat({ claimId })` keeps the slot, and
|
|
65
|
+
> `ablo.claims.release({ claimId })` leaves the line. And contention can be
|
|
66
|
+
> treated as a signal rather than a wait at all — catch the error, re-read
|
|
67
|
+
> fresh, regenerate, retry; see [Errors](#errors) for the loop sketch.
|
|
68
|
+
|
|
69
|
+
This reference opens with [the model](#the-model-three-layers-one-decision) — the
|
|
56
70
|
one answer to "how do two agents not clobber each other" — then covers the
|
|
57
71
|
[claim state object](#the-claim-state-object), the SDK [methods](#methods)
|
|
58
72
|
(`claim` · `claim.state` · `claim.queue` · `claim.release` · [writing under a
|
|
@@ -83,7 +97,7 @@ claim](#writing-under-a-claim)), and the [errors](#errors) you can catch.
|
|
|
83
97
|
|
|
84
98
|
---
|
|
85
99
|
|
|
86
|
-
## The model
|
|
100
|
+
## The model: three layers, one decision
|
|
87
101
|
|
|
88
102
|
Ablo has exactly **three** coordination layers. They are **not** three competing
|
|
89
103
|
answers to the same question — they stack, and only one of them is a decision you
|
|
@@ -91,9 +105,9 @@ make:
|
|
|
91
105
|
|
|
92
106
|
| layer | kind | what it does | enforces? |
|
|
93
107
|
|---|---|---|---|
|
|
94
|
-
| **Presence** (`claim.state`, observers) | observation | Broadcasts who is working where, live. Renders cursors / "agent X is editing." Reading or claiming a row auto-enrolls you in its sync group, so `claim.state({ id })` observes co-participants from any client (browser or Node agent) with no manual subscribe step. | **No.** Advisory only
|
|
95
|
-
| **Claim** (`claim`/`claim.queue`/`claim.release`) | pessimistic | Reserves a row for one participant. Foreign writers are rejected server-side; contenders join a fair FIFO queue. | **Yes**, between participants
|
|
96
|
-
| **Stale-context** (`readAt` + `onStale`) | optimistic (LWW) | On commit, rejects a write whose snapshot is older than the row's latest delta. Last-writer-wins detection. | **Yes**, against time
|
|
108
|
+
| **Presence** (`claim.state`, observers) | observation | Broadcasts who is working where, live. Renders cursors / "agent X is editing." Reading or claiming a row auto-enrolls you in its sync group, so `claim.state({ id })` observes co-participants from any client (browser or Node agent) with no manual subscribe step. | **No.** Advisory only: it never blocks or rejects a write. |
|
|
109
|
+
| **Claim** (`claim`/`claim.queue`/`claim.release`) | pessimistic | Reserves a row for one participant. Foreign writers are rejected server-side; contenders join a fair FIFO queue. | **Yes**, between participants: mutual exclusion. |
|
|
110
|
+
| **Stale-context** (`readAt` + `onStale`) | optimistic (LWW) | On commit, rejects a write whose snapshot is older than the row's latest delta. Last-writer-wins detection. | **Yes**, against time: lost-update detection. |
|
|
97
111
|
|
|
98
112
|
**The one decision: do you hold the row across a slow gap (read → LLM call →
|
|
99
113
|
write)?**
|
|
@@ -205,14 +219,15 @@ a model row. It's what `claim.state()` returns and what observers render.
|
|
|
205
219
|
| field | type | description |
|
|
206
220
|
|---|---|---|
|
|
207
221
|
| `id` | `string` | The claim id (distinct from the target row id). |
|
|
208
|
-
| `status` | `ClaimStatus` | `'active' \| 'queued' \| 'committed' \| 'expired' \| 'canceled'`. `active` = the holder; `queued` = waiting in line behind it. The other three are terminal states you only see on a claim you just finished
|
|
209
|
-
| `target` | `EntityRef` | What is being coordinated (`{ model, id
|
|
210
|
-
| `description` | `string` | Peer-visible description of the work
|
|
222
|
+
| `status` | `ClaimStatus` | `'active' \| 'queued' \| 'committed' \| 'expired' \| 'canceled'`. `active` = the holder; `queued` = waiting in line behind it. The other three are terminal states you only see on a claim you just finished: `committed` (released after a successful write), `expired` (TTL lapsed), `canceled` (released early). |
|
|
223
|
+
| `target` | `EntityRef` | What is being coordinated: the row (`{ model, id }`) plus any field narrowing the holder claimed: `field?`, `fields?`, and opaque `meta?`. A target with no narrowing covers the whole row. |
|
|
224
|
+
| `description` | `string` | Peer-visible description of the work: the sentence another participant reads to decide whether to wait or move on (`'rewriting the risk section'`). Defaults to `'editing'`. |
|
|
211
225
|
| `heldBy` | `string` | Participant holding (or waiting on) it (e.g. `'agent:forecaster'`). |
|
|
212
|
-
| `participantKind` | `'user' \| 'agent' \| 'system'` | Who's behind it
|
|
213
|
-
| `position` | `number?` | 0-based place in the FIFO line
|
|
214
|
-
| `createdAt` | `number?` | Ms-epoch the holder opened it. Optional
|
|
215
|
-
| `expiresAt` | `number` | Ms-epoch the server reclaims it if the holder goes **silent**. Renewed automatically while the holder's connection stays alive
|
|
226
|
+
| `participantKind` | `'user' \| 'agent' \| 'system'` | Who's behind it: a human (`user`), an AI (`agent`), or automated infrastructure (`system`). |
|
|
227
|
+
| `position` | `number?` | 0-based place in the FIFO line: present only when `status: 'queued'` (`0` = next behind the holder). |
|
|
228
|
+
| `createdAt` | `number?` | Ms-epoch the holder opened it. Optional: derived shapes may omit it. |
|
|
229
|
+
| `expiresAt` | `number` | Ms-epoch the server reclaims it if the holder goes **silent**. Renewed automatically while the holder's connection stays alive: a crash-cleanup floor, not a duration you size. |
|
|
230
|
+
| `meta` | `Record<string, unknown>?` | The claim's open metadata bag, as it stands on the wire. A [heartbeat](#heartbeat-holding-a-claim-for-long-running-work) writes its `details` here under `progress`: last beat wins, so an observer can read what a long hold is doing without the holder releasing it. Distinct from `target.meta`, which is the shape your program declared: a declared shape has no member for a key the coordinator wrote. |
|
|
216
231
|
|
|
217
232
|
```jsonc
|
|
218
233
|
{
|
|
@@ -223,23 +238,49 @@ a model row. It's what `claim.state()` returns and what observers render.
|
|
|
223
238
|
"heldBy": "agent:forecaster",
|
|
224
239
|
"participantKind": "agent",
|
|
225
240
|
"createdAt": 1748160000000,
|
|
226
|
-
"expiresAt": 1748160030000
|
|
241
|
+
"expiresAt": 1748160030000,
|
|
242
|
+
"meta": { "progress": { "phase": "writing", "done": 2, "of": 5 } }
|
|
227
243
|
}
|
|
228
244
|
```
|
|
229
245
|
|
|
246
|
+
### Lifecycle
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
claim({ id }) update({ id }) lands
|
|
250
|
+
(free) ───────────▶ active ───────────────────────▶ committed
|
|
251
|
+
│
|
|
252
|
+
┌───────────┴───────────┐
|
|
253
|
+
▼ ▼
|
|
254
|
+
canceled expired
|
|
255
|
+
(release w/o write) (TTL; holder died)
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
A target is free when `ablo.<model>.claim.state({ id })` returns `null`. Terminal
|
|
259
|
+
states drop out of the live stream, so a claim you can see is either `active`
|
|
260
|
+
(the holder) or `queued` (waiting in the FIFO line behind it; see
|
|
261
|
+
[`claim.queue`](#claimqueue)).
|
|
262
|
+
|
|
263
|
+
Reading a holder's progress is the same synchronous read as everything else
|
|
264
|
+
here — no second subscription, and nothing to poll:
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
const held = ablo.documents.claim.state({ id: docId });
|
|
268
|
+
const phase = held?.meta?.progress?.phase ?? 'reading';
|
|
269
|
+
```
|
|
270
|
+
|
|
230
271
|
---
|
|
231
272
|
|
|
232
273
|
## Methods
|
|
233
274
|
|
|
234
275
|
One word — "claim" — names four distinct things; keep them separate as you read:
|
|
235
276
|
|
|
236
|
-
- **the lease (claim handle)
|
|
277
|
+
- **the lease (claim handle):** the *object* returned by `ablo.<model>.claim({ id })`
|
|
237
278
|
(`ClaimHandle`, an `AsyncDisposable` with `.data` and `.release()`).
|
|
238
|
-
- **acquiring a claim/lease
|
|
279
|
+
- **acquiring a claim/lease:** the *verb* `ablo.<model>.claim({ id })`, the call
|
|
239
280
|
that takes the lease.
|
|
240
|
-
- **`claim.state` / `claim.queue
|
|
281
|
+
- **`claim.state` / `claim.queue`:** the *inspection namespace* hanging off the
|
|
241
282
|
model, for reading who holds the row and who's lined up.
|
|
242
|
-
- **the write's `claim` param
|
|
283
|
+
- **the write's `claim` param:** `update({ id, data, claim })`, where you pass a
|
|
243
284
|
lease the proxy didn't take itself.
|
|
244
285
|
|
|
245
286
|
Each method below follows one fixed shape: **signature · what it does ·
|
|
@@ -259,16 +300,38 @@ then re-reads so the claimed snapshot reflects what the previous holder
|
|
|
259
300
|
committed. There's no polling and no race window — the server decides the order,
|
|
260
301
|
so two claimers can't both think they won.
|
|
261
302
|
|
|
262
|
-
**Parameters**
|
|
303
|
+
**Parameters** — every option is flat on the call, and each sits on one of
|
|
304
|
+
four axes. `claim({ id })` alone is a complete call; each axis is opt-in.
|
|
305
|
+
|
|
306
|
+
*What you claim* — the target, narrowed below the row:
|
|
307
|
+
|
|
308
|
+
| name | type | required | description |
|
|
309
|
+
|---|---|---|---|
|
|
310
|
+
| `id` | `string` | yes | The row id: same id as `retrieve` / `update`. |
|
|
311
|
+
| `options.fields` | field selector | no | Claim fields declared by the model's Zod schema instead of the whole row: `fields: (task) => task.status`, or `fields: (task) => [task.status, task.title]` for several. The model supplies its own fields, so autocomplete is exact, a typo does not compile, and a schema rename is a compile error at every use. Two sets conflict where they intersect, so holders of disjoint fields do not wait for each other; see [claiming part of a row](#claiming-part-of-a-row). |
|
|
312
|
+
|
|
313
|
+
*What others see* — the presence half:
|
|
263
314
|
|
|
264
315
|
| name | type | required | description |
|
|
265
316
|
|---|---|---|---|
|
|
266
|
-
| `id` | `string` | yes | The row id — same id as `retrieve` / `update`. |
|
|
267
317
|
| `options.description` | `string` | no | Peer-visible description of the work, shown to observers (default `'editing'`). |
|
|
268
|
-
| `options.
|
|
269
|
-
|
|
318
|
+
| `options.meta` | `object` | no | App-defined structured metadata, carried verbatim to every participant observing the claim. Declare its shape once on `Register`'s `ClaimMeta` slot. |
|
|
319
|
+
|
|
320
|
+
*How you wait* — admission to the line:
|
|
321
|
+
|
|
322
|
+
| name | type | required | description |
|
|
323
|
+
|---|---|---|---|
|
|
324
|
+
| `options.queue` | `boolean` | no | `true` (default) queues and waits for the lease. `false` is the try-claim: if another participant holds the row it resolves `null`: an expected outcome, not an error, so claim-or-skip dedup reads `if (!claim) return` (waiting would double-process). Who holds it stays readable via `claim.state`. A *write* to a held row still rejects `entity_claimed`. |
|
|
270
325
|
| `options.maxQueueDepth` | `number` | no | Backpressure: reject with `AbloClaimedError('queue_too_deep')` instead of joining a line already `>= maxQueueDepth` deep. Omit to wait however deep the queue is. |
|
|
271
|
-
| `options.
|
|
326
|
+
| `options.waitTimeoutMs` | `number` | no | Cap on how long a queued claim waits for its grant before rejecting with `AbloClaimedError('grant_timeout')`. Omit to wait as long as the line takes. Same meaning on both transports; over HTTP a timed-out wait also leaves the line. |
|
|
327
|
+
| `options.signal` | `AbortSignal` | no | Abort a pending wait from outside: a cancelled agent task or an unmounted component takes its queued claim with it. Rejects with `AbloClaimedError('claim_wait_aborted')`; over HTTP the abort also leaves the line. Ignored once the grant has arrived, release a held lease instead. |
|
|
328
|
+
|
|
329
|
+
*How long you hold* — the lease:
|
|
330
|
+
|
|
331
|
+
| name | type | required | description |
|
|
332
|
+
|---|---|---|---|
|
|
333
|
+
| `options.ttl` | `Duration` | no | Crash-cleanup floor. Rarely set: the lease renews while your connection is alive, so it only matters once you go silent. |
|
|
334
|
+
| `options.heartbeat` | `true \| Duration \| { every?, onBeat?, onLost? }` | no | Keep the lease alive for work that outlives the TTL: `true` beats every third of the TTL, a duration sets the cadence, and the structured form carries the cadence and both callbacks in one place: `onBeat` fires after every successful beat (chiefly `queueDepth`, the pressure signal), `onLost` once if a beat learns the lease is gone. The loop stops on release. |
|
|
272
335
|
|
|
273
336
|
The high-level `claim` queues by default, so on contention you either get the row
|
|
274
337
|
when your turn arrives or one of the [queue errors](#errors) (`claim_lost`,
|
|
@@ -300,14 +363,67 @@ committed — pass an idempotency key on the write if you replay the block.) The
|
|
|
300
363
|
lower-level [`claim.release`](#claimrelease) shows the manual `try/finally`
|
|
301
364
|
equivalent for when you hold a claim without `await using`.
|
|
302
365
|
|
|
366
|
+
### Claiming part of a row
|
|
367
|
+
|
|
368
|
+
Name a field and a typo cannot survive — the model is already bound by the call,
|
|
369
|
+
so it hands you its own fields:
|
|
370
|
+
|
|
371
|
+
```ts
|
|
372
|
+
await using mine = await ablo.tasks.claim({ id, fields: (task) => task.status });
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
`task.status` is checked against the model: a field it does not have stops
|
|
376
|
+
compiling, and renaming one is a compile error at every use. Nothing to import,
|
|
377
|
+
nothing to add to your schema file.
|
|
378
|
+
|
|
379
|
+
The selector is the public model API. Quoted field names exist only in the wire
|
|
380
|
+
contract and low-level coordination protocol.
|
|
381
|
+
|
|
382
|
+
A claim covers the whole row only when you name nothing narrower. Select
|
|
383
|
+
`fields` and exclusion follows them: **two claims on different
|
|
384
|
+
fields of the same row are both granted**, and only claims that share a field
|
|
385
|
+
queue behind each other.
|
|
386
|
+
|
|
387
|
+
```ts
|
|
388
|
+
// Two agents on the SAME order at the same time. The pricing agent holds
|
|
389
|
+
// `total` and `discount`; the fulfillment agent holds `status`. Disjoint
|
|
390
|
+
// fields, so both are granted at once and neither waits on the other.
|
|
391
|
+
await using priced = await ablo.orders.claim({
|
|
392
|
+
id: orderId,
|
|
393
|
+
fields: (o) => [o.total, o.discount],
|
|
394
|
+
description: 'repricing',
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
await using shipping = await ablo.orders.claim({
|
|
398
|
+
id: orderId,
|
|
399
|
+
fields: (order) => order.status,
|
|
400
|
+
description: 'marking shipped',
|
|
401
|
+
});
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Overlap is set intersection. Holders on `total` and on `status` proceed
|
|
405
|
+
concurrently; two claims that both name `total` queue; naming no field covers
|
|
406
|
+
every field, so it conflicts with any narrower claim on the row.
|
|
407
|
+
|
|
408
|
+
**Field is the floor.** A claim cannot be finer than a whole field, because
|
|
409
|
+
nothing writes part of a value: whichever holder commits first takes the entire
|
|
410
|
+
field. Sub-field targets, a range of text or a path into a document, are not
|
|
411
|
+
offered. Two writers holding different parts of one field is only safe once
|
|
412
|
+
concurrent edits to that field can be reconciled (operational transformation),
|
|
413
|
+
and that is not solved yet. When it is, sub-field targets return, and they
|
|
414
|
+
return working.
|
|
415
|
+
|
|
416
|
+
A claim with no target is the widest parent: it covers every field of the row
|
|
417
|
+
and conflicts with any narrower claim on it.
|
|
418
|
+
|
|
303
419
|
### Claim-gated reads
|
|
304
420
|
|
|
305
421
|
`claim.state({ id })` always returns immediately. Model reads such as
|
|
306
|
-
`ablo.<model>.local.
|
|
422
|
+
`ablo.<model>.local.get(id)` are local reads and stay available while a claim is
|
|
307
423
|
held. Server/model reads can choose a claimed policy:
|
|
308
424
|
|
|
309
425
|
```ts
|
|
310
|
-
await ablo.weatherReports.
|
|
426
|
+
await ablo.weatherReports.get({
|
|
311
427
|
id: 'report_stockholm',
|
|
312
428
|
ifClaimed: 'fail',
|
|
313
429
|
});
|
|
@@ -347,8 +463,15 @@ whole coordination API.
|
|
|
347
463
|
|---|---|---|---|
|
|
348
464
|
| `id` | `string` | yes | The row id. |
|
|
349
465
|
|
|
350
|
-
**Returns** —
|
|
351
|
-
is free.
|
|
466
|
+
**Returns** — an active [claim state object](#the-claim-state-object) on the row, or
|
|
467
|
+
`null` when the row is free.
|
|
468
|
+
|
|
469
|
+
**One holder, and a row can have several.** This reads a row, not a target, and
|
|
470
|
+
answers with a single claim. That is the whole story for a whole-row claim, and
|
|
471
|
+
only part of it once you [claim parts of a row](#claiming-part-of-a-row): three
|
|
472
|
+
agents holding `total`, `discount`, and `status` are all active at once, and
|
|
473
|
+
this read surfaces one of them. To render every holder, a badge per claimed
|
|
474
|
+
field or a chip per participant, use [`claim.list`](#claimlist).
|
|
352
475
|
|
|
353
476
|
**Example**
|
|
354
477
|
|
|
@@ -371,6 +494,45 @@ Returns the active claim state when the row is held, or `null` when it's free:
|
|
|
371
494
|
}
|
|
372
495
|
```
|
|
373
496
|
|
|
497
|
+
### `claim.list`
|
|
498
|
+
|
|
499
|
+
```ts
|
|
500
|
+
ablo.<model>.claim.list({ id })
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
Every holder of a row. Same synchronous, reactive read as `claim.state` — off
|
|
504
|
+
the same local snapshot, safe to call inline in a render — and the same list
|
|
505
|
+
envelope as [`claim.queue`](#claimqueue).
|
|
506
|
+
|
|
507
|
+
Reach for it whenever a row can be claimed by field. One agent repricing
|
|
508
|
+
`total` while another marks `status` are two active claims on one row, and only
|
|
509
|
+
this read returns both.
|
|
510
|
+
|
|
511
|
+
**Parameters**
|
|
512
|
+
|
|
513
|
+
| name | type | required | description |
|
|
514
|
+
|---|---|---|---|
|
|
515
|
+
| `id` | `string` | yes | The row id. |
|
|
516
|
+
|
|
517
|
+
**Returns** — `{ object: 'list', data: Claim[] }`. Your own claim comes first
|
|
518
|
+
when this client holds one, then the other participants'. Empty `data` when the
|
|
519
|
+
row is free.
|
|
520
|
+
|
|
521
|
+
**Example** — a badge on every claimed field:
|
|
522
|
+
|
|
523
|
+
```tsx
|
|
524
|
+
const { data: holders } = ablo.orders.claim.list({ id: orderId });
|
|
525
|
+
|
|
526
|
+
return FIELDS.map((field) => {
|
|
527
|
+
const held = holders.find((c) => c.target.field === field);
|
|
528
|
+
return <FieldBadge key={field} field={field} by={held?.heldBy} note={held?.description} />;
|
|
529
|
+
});
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
`claim.state({ id })` remains the right read when a row is claimed whole, or
|
|
533
|
+
when all you need is "is anyone working here" — it answers with one claim and
|
|
534
|
+
`null` when the row is free.
|
|
535
|
+
|
|
374
536
|
### `claim.queue`
|
|
375
537
|
|
|
376
538
|
```ts
|
|
@@ -436,7 +598,7 @@ try {
|
|
|
436
598
|
}
|
|
437
599
|
```
|
|
438
600
|
|
|
439
|
-
### `heartbeat
|
|
601
|
+
### `heartbeat`: holding a claim for long-running work
|
|
440
602
|
|
|
441
603
|
```ts
|
|
442
604
|
held.heartbeat(ttl?: Duration): Promise<{ expiresAt: number }>
|
|
@@ -457,8 +619,7 @@ await using claim = await ablo.reports.claim({
|
|
|
457
619
|
id: 'report_q3',
|
|
458
620
|
description: 'generating',
|
|
459
621
|
ttl: '5m',
|
|
460
|
-
heartbeat:
|
|
461
|
-
onHeartbeatLost: () => abortWork(),
|
|
622
|
+
heartbeat: { onLost: () => abortWork() }, // `true` and '2m' are the shorthands
|
|
462
623
|
});
|
|
463
624
|
await runLongGeneration(claim.data); // lease held for the duration
|
|
464
625
|
// scope exit releases; the loop stops with it
|
|
@@ -473,11 +634,11 @@ failures (a connection blip) don't stop the loop — the next tick retries.
|
|
|
473
634
|
|
|
474
635
|
Each beat's answer carries two more things:
|
|
475
636
|
|
|
476
|
-
- **`queueDepth
|
|
637
|
+
- **`queueDepth`:** how many participants wait in line behind the lease.
|
|
477
638
|
This is the cooperative-yield pressure signal: a worker that can checkpoint
|
|
478
639
|
may release early when others wait. Read it from the resolved beat, or pass
|
|
479
|
-
`
|
|
480
|
-
- **progress `details
|
|
640
|
+
`heartbeat: { onBeat }` when claiming to observe every auto-beat.
|
|
641
|
+
- **progress `details`:** `held.heartbeat({ details: { pages: 42, of: 100 } })`
|
|
481
642
|
stores the payload as the claim's peer-visible `meta.progress` (last beat
|
|
482
643
|
wins, via `claim.state`). This is presence, not a checkpoint: it dies with
|
|
483
644
|
the lease. Durable progress belongs in the data itself — write a row, and
|
|
@@ -507,7 +668,7 @@ A stateless worker holding **many** rows beats them all in one round trip:
|
|
|
507
668
|
entry per extended lease. This is the socketless twin of the realtime
|
|
508
669
|
keepalive, which already renews every held lease on each ping.
|
|
509
670
|
|
|
510
|
-
### durability
|
|
671
|
+
### durability: what a claim survives
|
|
511
672
|
|
|
512
673
|
A lease belongs to your **identity** — the participant behind the credential —
|
|
513
674
|
not to the socket it was claimed on; the server keys each lease by participant
|
|
@@ -547,12 +708,12 @@ snapshot.
|
|
|
547
708
|
|
|
548
709
|
| the holder… | what happens to the claim |
|
|
549
710
|
| --- | --- |
|
|
550
|
-
| blips, then reconnects within the window | renewed automatically on reconnect
|
|
711
|
+
| blips, then reconnects within the window | renewed automatically on reconnect: no interruption |
|
|
551
712
|
| crashes or drops for good | released within one keepalive cycle; the queue advances |
|
|
552
|
-
| still has a second live connection | survives
|
|
713
|
+
| still has a second live connection | survives: release fires only on the last connection |
|
|
553
714
|
| loses the server to a restart | rides the TTL in the coordination store; re-announced on reconnect |
|
|
554
715
|
|
|
555
|
-
### `join
|
|
716
|
+
### `join`: presence for a set of rows
|
|
556
717
|
|
|
557
718
|
Reading or claiming a row auto-enrolls you in its sync group, which is enough for
|
|
558
719
|
`claim.state`/`claim.queue` to observe co-participants. When you want to *hold*
|
|
@@ -620,20 +781,20 @@ try {
|
|
|
620
781
|
|
|
621
782
|
## Errors
|
|
622
783
|
|
|
623
|
-
All extend `AbloError` (`packages/
|
|
784
|
+
All extend `AbloError` (`packages/transaction/src/errors.ts`). Catch by `type` or
|
|
624
785
|
inspect the `code`.
|
|
625
786
|
|
|
626
787
|
| error | `code` | thrown when | carries |
|
|
627
788
|
|---|---|---|---|
|
|
628
|
-
| `AbloClaimedError` | `claim_lost` | A held/queued claim was taken away
|
|
629
|
-
| `AbloClaimedError` | `claim_queued` | **HTTP transport only.** A contended `claim` (default `queue: true`) could not block-wait for the lease (no socket), so it rejected immediately instead of queueing. Retryable
|
|
789
|
+
| `AbloClaimedError` | `claim_lost` | A held/queued claim was taken away: the holder disconnected (reaped on the keepalive cycle), went silent past its TTL, was revoked, or was preempted (a privileged reorder, or a configured cumulative-hold ceiling reached while contenders waited), while you were holding or waiting. | `claims?` |
|
|
790
|
+
| `AbloClaimedError` | `claim_queued` | **HTTP transport only.** A contended `claim` (default `queue: true`) could not block-wait for the lease (no socket), so it rejected immediately instead of queueing. Retryable: re-attempt the claim. | `claims?` |
|
|
630
791
|
| `AbloClaimedError` | `grant_timeout` | The optional `timeoutMs` elapsed while you were still queued for a grant. | `claims?` |
|
|
631
|
-
| `AbloClaimedError` | `queue_too_deep` | `claim` was passed `maxQueueDepth` and the wait line was already that deep when you tried to join
|
|
632
|
-
| `AbloClaimedError` | `claim_conflict` | An `update`/`delete` targets a row another participant holds
|
|
633
|
-
| `AbloClaimedError` | `entity_claimed` | Same conflict, from the commit guard backstop.
|
|
634
|
-
| `AbloStaleContextError`
|
|
635
|
-
| `AbloValidationError` | `model_claim_not_configured` | `claim` called on a model proxy built without the collaboration runtime
|
|
636
|
-
| `AbloValidationError` | `entity_not_found` | The row id doesn't exist locally or on load.
|
|
792
|
+
| `AbloClaimedError` | `queue_too_deep` | `claim` was passed `maxQueueDepth` and the wait line was already that deep when you tried to join: fail-fast instead of waiting. | `claims?` |
|
|
793
|
+
| `AbloClaimedError` | `claim_conflict` | An `update`/`delete` targets a row another participant holds: the server's pre-commit check rejected it. |: |
|
|
794
|
+
| `AbloClaimedError` | `entity_claimed` | Same conflict, from the commit guard backstop. |: |
|
|
795
|
+
| `AbloStaleContextError` |: | A guarded `update` (under a claim, or any write carrying `readAt`) targets a row that received deltas since the snapshot: your reasoning is stale. | `readAt`, `conflicts[]` |
|
|
796
|
+
| `AbloValidationError` | `model_claim_not_configured` | `claim` called on a model proxy built without the collaboration runtime: an internal/advanced construction path. The standard `Ablo({ schema, apiKey })` client enables claiming for **every** model; there is no per-model claim config to add. |: |
|
|
797
|
+
| `AbloValidationError` | `entity_not_found` | The row id doesn't exist locally or on load. |: |
|
|
637
798
|
|
|
638
799
|
`AbloStaleContextError.conflicts` lists the `(model, id, observedSyncId)` rows
|
|
639
800
|
that moved during your generation window — use it for selective regeneration
|
|
@@ -741,9 +902,9 @@ state**, which is the shape that races. The other two aren't read-modify-write:
|
|
|
741
902
|
|
|
742
903
|
| Verb | Functional form? | Why | Its "just works" property |
|
|
743
904
|
| --- | --- | --- | --- |
|
|
744
|
-
| `update` | **yes
|
|
745
|
-
| `create` | no
|
|
746
|
-
| `delete` | no
|
|
905
|
+
| `update` | **yes**: `update(id, current => next)` | next value depends on the current one (lost-update hazard) | compare-and-swap + reconcile |
|
|
906
|
+
| `create` | no: `create({ data, id? })` | no prior state to read; the hazard is *id collision*, a terminal `unique_violation`, not a lost update | **idempotency**: stable id / `idempotencyKey` makes a retried create safe |
|
|
907
|
+
| `delete` | no: `delete({ id })` | no resulting state to compute; "make it not exist" is unchanged by concurrent edits, and delete is idempotent | naturally idempotent |
|
|
747
908
|
|
|
748
909
|
The same reason React has `setState(prev => next)` but no functional mount /
|
|
749
910
|
unmount. A *conditional* delete ("only if unchanged since I read it") is the one
|
|
@@ -752,38 +913,19 @@ not a function.
|
|
|
752
913
|
|
|
753
914
|
---
|
|
754
915
|
|
|
755
|
-
## Observability
|
|
916
|
+
## Observability
|
|
756
917
|
|
|
757
918
|
Coordination you can't see is coordination you can't debug. Pass an
|
|
758
919
|
`observability` provider to `Ablo({ ... })` and the client reports every claim
|
|
759
920
|
lifecycle event and stale-write collision it sees. The batteries-included
|
|
760
|
-
provider is `ClaimLog
|
|
921
|
+
provider is `ClaimLog`, and `collisions()` is the eval primitive:
|
|
761
922
|
|
|
762
923
|
```ts
|
|
763
|
-
import Ablo, { ClaimLog } from '@abloatai/ablo';
|
|
764
|
-
|
|
765
924
|
const log = new ClaimLog();
|
|
766
925
|
const ablo = Ablo({ schema, apiKey, observability: log });
|
|
767
|
-
// …run your agents…
|
|
768
926
|
|
|
769
|
-
log.
|
|
770
|
-
log.collisions() // just the collisions: rejected/lost claims + stale writes
|
|
771
|
-
log.toString() // pretty, greppable timeline to print
|
|
772
|
-
log.onChange(fn) // reactive subscribe → drive a live activity feed / useSyncExternalStore
|
|
927
|
+
expect(log.collisions()).toHaveLength(0); // no one stepped on anyone
|
|
773
928
|
```
|
|
774
929
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
`conflict: tx … — 1 row(s) changed underneath: reports/r1`.
|
|
778
|
-
|
|
779
|
-
`ClaimLog` implements the full `SyncObservabilityProvider`, so it drops straight
|
|
780
|
-
into the `observability` slot; spread `noopObservability` if you only want to
|
|
781
|
-
override a few hooks (e.g. forward to Sentry/OTel). Exports: `ClaimLog`,
|
|
782
|
-
`formatClaim`, `formatConflict`, `noopObservability`, and the types `ClaimEvent`,
|
|
783
|
-
`ConflictEvent`, `ClaimLogEntry`, `SyncObservabilityProvider`.
|
|
784
|
-
|
|
785
|
-
> **Both transports, from 0.21.0.** Observability fires on the WebSocket **and**
|
|
786
|
-
> the stateless HTTP transport (claim acquired + coordination-conflict
|
|
787
|
-
> rejections, on every write door). Before 0.21.0 only WebSocket emitted, so a
|
|
788
|
-
> `ClaimLog` on an HTTP client — e.g. a headless server-agent eval — stayed
|
|
789
|
-
> silent even though coordination still worked.
|
|
930
|
+
See [Debugging & Logs](./debugging.md) for the setup, the event shapes, a
|
|
931
|
+
reactive activity feed, and routing events to your own backend.
|
package/docs/data-sources.md
CHANGED
|
@@ -75,7 +75,7 @@ Run it against your database as a superuser or the DB owner. It creates:
|
|
|
75
75
|
|
|
76
76
|
Scope it to a subset with `npx ablo connect --tables a,b,c`.
|
|
77
77
|
|
|
78
|
-
- **A replication role
|
|
78
|
+
- **A replication role:** it streams the WAL and `SELECT`s, nothing more. This is
|
|
79
79
|
the role Ablo reads and confirms through. You choose the password; it never
|
|
80
80
|
passes through Ablo's CLI or servers:
|
|
81
81
|
|
|
@@ -87,7 +87,7 @@ Run it against your database as a superuser or the DB owner. It creates:
|
|
|
87
87
|
On Amazon RDS the `REPLICATION` attribute is granted, not set directly:
|
|
88
88
|
`GRANT rds_replication TO "ablo_replicator";`.
|
|
89
89
|
|
|
90
|
-
- **A scoped writer role
|
|
90
|
+
- **A scoped writer role:** the role Ablo writes your rows through. It gets row
|
|
91
91
|
DML (`SELECT, INSERT, UPDATE, DELETE`) and the sync ledger, and nothing else: no
|
|
92
92
|
`REPLICATION`, no schema `CREATE`, `NOSUPERUSER NOBYPASSRLS`, row security on. It
|
|
93
93
|
can change rows in your tables; it cannot change your database:
|
|
@@ -182,14 +182,14 @@ await ablo.weatherReports.update({ id: 'report_stockholm', data: { high: 21 } })
|
|
|
182
182
|
await ablo.weatherReports.update({ id: 'report_stockholm', data: { high: 21 }, wait: 'confirmed' });
|
|
183
183
|
|
|
184
184
|
// Reads are live off the same stream.
|
|
185
|
-
const report = ablo.weatherReports.local.
|
|
185
|
+
const report = ablo.weatherReports.local.get('report_stockholm');
|
|
186
186
|
```
|
|
187
187
|
|
|
188
188
|
A commit is accepted the moment Ablo takes it (`queued`); it becomes `confirmed`
|
|
189
189
|
once the row appears on your WAL. See [Guarantees](./guarantees.md) for what each
|
|
190
190
|
state means and when to wait.
|
|
191
191
|
|
|
192
|
-
## What Ablo touches in your database
|
|
192
|
+
## What Ablo touches in your database: the honest footprint
|
|
193
193
|
|
|
194
194
|
This is the complete list. Nothing else.
|
|
195
195
|
|
|
@@ -197,7 +197,7 @@ This is the complete list. Nothing else.
|
|
|
197
197
|
|---|---|---|
|
|
198
198
|
| `ablo_publication` | A publication naming the tables Ablo reads and confirms against. | You create it (step 2). |
|
|
199
199
|
| `ablo_replicator` role | A `REPLICATION` + `SELECT` role Ablo reads and confirms through. | You create it (step 2). |
|
|
200
|
-
| `ablo_writer` role | A scoped DML role Ablo writes your rows through
|
|
200
|
+
| `ablo_writer` role | A scoped DML role Ablo writes your rows through: row DML + ledger, nothing more. | You create it (step 2). |
|
|
201
201
|
| Replication slot | A logical slot Ablo subscribes through to track its WAL position. | Ablo's runtime creates it on first connect. |
|
|
202
202
|
| `wal_level = logical` | A server setting that **requires a restart**. | You set it (step 1). |
|
|
203
203
|
|
package/docs/debugging.md
CHANGED
|
@@ -13,6 +13,17 @@ import { schema } from './ablo/schema';
|
|
|
13
13
|
const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY, debug: true });
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
## CLI environment and target
|
|
17
|
+
|
|
18
|
+
The CLI checks an explicit credential in this order: exported `ABLO_API_KEY`,
|
|
19
|
+
`.env.local`, `.env`, then the key saved by `ablo login`. An exported value wins
|
|
20
|
+
over project files. If a stale shell export (for example `OPENAI_API_KEY`) is
|
|
21
|
+
shadowing a value in `.env.local`, restart the shell or unset the stale variable.
|
|
22
|
+
|
|
23
|
+
When a command appears to use the wrong plane or project, run `ablo status`.
|
|
24
|
+
It reports the credential source and, when reachable, the server-confirmed
|
|
25
|
+
project and environment; that confirmed target is authoritative.
|
|
26
|
+
|
|
16
27
|
`debug: true` is the simple switch. For finer control use `logLevel`, or set it without touching code via the `ABLO_LOG_LEVEL` environment variable.
|
|
17
28
|
|
|
18
29
|
```ts
|
|
@@ -31,15 +42,15 @@ ABLO_LOG_LEVEL=debug npm run dev # same, from the environment
|
|
|
31
42
|
|---|---|
|
|
32
43
|
| `silent` | nothing |
|
|
33
44
|
| `error` | failures only |
|
|
34
|
-
| `warn` | **default
|
|
45
|
+
| `warn` | **default**: warnings + errors |
|
|
35
46
|
| `info` | the above + the **coordination trace** (claims, grants, queueing) + connection state |
|
|
36
|
-
| `debug` | the above + internal lifecycle (per-model registration, store hydration)
|
|
47
|
+
| `debug` | the above + internal lifecycle (per-model registration, store hydration): the full firehose |
|
|
37
48
|
|
|
38
49
|
Precedence: an explicit `logLevel` wins, then `debug: true` (⇒ `debug`), then `ABLO_LOG_LEVEL`, then the `warn` default. `debug: false` (or omitting it) just means "don't raise the level."
|
|
39
50
|
|
|
40
51
|
> For watching coordination, **`logLevel: 'info'` is the sweet spot** — you get the claim trace without the per-model registration chatter that `debug` adds.
|
|
41
52
|
|
|
42
|
-
## What you'll see
|
|
53
|
+
## What you'll see: the coordination trace
|
|
43
54
|
|
|
44
55
|
These lines (all at `info`) let you watch the handover you built:
|
|
45
56
|
|
|
@@ -54,12 +65,12 @@ These lines (all at `info`) let you watch the handover you built:
|
|
|
54
65
|
|
|
55
66
|
Read it as the lifecycle of one claim:
|
|
56
67
|
|
|
57
|
-
- **`requesting
|
|
58
|
-
- **`queued … position N of M
|
|
59
|
-
- **`granted … your turn
|
|
60
|
-
- **`rejected … held by <who
|
|
61
|
-
- **`lost
|
|
62
|
-
- **`released
|
|
68
|
+
- **`requesting`:** your code (or an agent) called `ablo.<model>.claim(...)`. `(will queue if contended)` appears when you passed `{ queue: true }`.
|
|
69
|
+
- **`queued … position N of M`:** the row was held, so you're waiting in the FIFO line. This is the "an agent is waiting behind a claim" moment; it re-logs only when your position changes, so you can watch it advance.
|
|
70
|
+
- **`granted … your turn`:** you reached the head of the line; the lease is now yours and the row may have changed while you waited.
|
|
71
|
+
- **`rejected … held by <who>`:** your claim was refused because someone else holds it (and the model's policy didn't let you in).
|
|
72
|
+
- **`lost`:** you held the lease and it was taken (preempted by a higher-priority writer, or it expired).
|
|
73
|
+
- **`released`:** you (or `await using`'s scope exit) gave the lease back.
|
|
63
74
|
|
|
64
75
|
## Where the logs run
|
|
65
76
|
|
|
@@ -82,7 +93,7 @@ Ablo({
|
|
|
82
93
|
});
|
|
83
94
|
```
|
|
84
95
|
|
|
85
|
-
## Read the coordination in code
|
|
96
|
+
## Read the coordination in code: the activity log
|
|
86
97
|
|
|
87
98
|
The console trace above is for *you*, at a terminal. To put the same activity **inside your app** — an activity feed, a "who's editing" badge, a Sentry breadcrumb trail — read it programmatically. Same events, three layers; pick by audience:
|
|
88
99
|
|
|
@@ -114,7 +125,7 @@ interface ConflictEvent {
|
|
|
114
125
|
|
|
115
126
|
`phase` is past-tense — the state the claim just entered — and maps one-to-one to what arrives on the wire.
|
|
116
127
|
|
|
117
|
-
### Collect them
|
|
128
|
+
### Collect them: `ClaimLog`
|
|
118
129
|
|
|
119
130
|
`ClaimLog` records both into an ordered list. Hand it to `observability`, then read it back:
|
|
120
131
|
|
|
@@ -136,7 +147,7 @@ It's also the simplest way to **assert** coordination in a test — no log scrap
|
|
|
136
147
|
expect(log.collisions()).toHaveLength(0); // no one stepped on anyone
|
|
137
148
|
```
|
|
138
149
|
|
|
139
|
-
### Show it on a page
|
|
150
|
+
### Show it on a page: reactive
|
|
140
151
|
|
|
141
152
|
`ClaimLog.onChange` fires on every event and returns an unsubscribe — the exact shape `useSyncExternalStore` wants, so a live feed is a few lines:
|
|
142
153
|
|
|
@@ -185,6 +196,17 @@ const ablo = Ablo({
|
|
|
185
196
|
});
|
|
186
197
|
```
|
|
187
198
|
|
|
199
|
+
`ClaimLog` implements the full `SyncObservabilityProvider`, so it drops straight
|
|
200
|
+
into the `observability` slot. The surface exports `ClaimLog`, `formatClaim`,
|
|
201
|
+
`formatConflict`, and `noopObservability`, plus the types `ClaimEvent`,
|
|
202
|
+
`ConflictEvent`, `ClaimLogEntry`, and `SyncObservabilityProvider`.
|
|
203
|
+
|
|
204
|
+
> **Both transports, from 0.21.0.** Observability fires on the WebSocket and on
|
|
205
|
+
> the stateless HTTP transport (claim acquired, plus coordination-conflict
|
|
206
|
+
> rejections on every write door). Before 0.21.0 only WebSocket emitted, so a
|
|
207
|
+
> `ClaimLog` on an HTTP client, such as a headless server-agent eval, stayed
|
|
208
|
+
> silent even though coordination still worked.
|
|
209
|
+
|
|
188
210
|
## Errors
|
|
189
211
|
|
|
190
212
|
Ablo's thrown errors are typed and self-describing — `String(err)` (or logging it) yields one clean line, never a stack dump:
|