@abloatai/ablo 0.26.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/CHANGELOG.md +14 -0
- package/README.md +101 -85
- package/dist/BaseSyncedStore.d.ts +85 -88
- package/dist/BaseSyncedStore.js +131 -147
- package/dist/Database.d.ts +54 -68
- package/dist/Database.js +97 -113
- package/dist/{ObjectPool.d.ts → InstanceCache.d.ts} +10 -13
- package/dist/{ObjectPool.js → InstanceCache.js} +85 -83
- package/dist/LazyReferenceCollection.d.ts +11 -15
- package/dist/LazyReferenceCollection.js +12 -16
- package/dist/Model.d.ts +37 -52
- package/dist/Model.js +46 -61
- package/dist/ModelRegistry.d.ts +21 -19
- package/dist/ModelRegistry.js +23 -27
- package/dist/NetworkMonitor.d.ts +5 -6
- package/dist/NetworkMonitor.js +5 -6
- package/dist/SyncClient.d.ts +112 -112
- package/dist/SyncClient.js +165 -172
- 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 +27 -32
- package/dist/agent/Agent.js +18 -19
- 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} +26 -33
- package/dist/ai-sdk/index.d.ts +25 -22
- package/dist/ai-sdk/index.js +25 -22
- package/dist/ai-sdk/wrap.d.ts +6 -7
- package/dist/ai-sdk/wrap.js +1 -1
- package/dist/auth/credentialPolicy.d.ts +69 -74
- package/dist/auth/credentialPolicy.js +51 -56
- package/dist/auth/credentialSource.d.ts +6 -5
- package/dist/auth/credentialSource.js +9 -10
- package/dist/auth/index.d.ts +59 -58
- package/dist/auth/index.js +31 -37
- 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 +167 -119
- package/dist/client/Ablo.d.ts +73 -73
- package/dist/client/Ablo.js +125 -160
- package/dist/client/ApiClient.d.ts +30 -19
- package/dist/client/ApiClient.js +133 -38
- package/dist/client/auth.d.ts +47 -47
- package/dist/client/auth.js +108 -117
- package/dist/client/claimHeartbeatLoop.d.ts +50 -0
- package/dist/client/claimHeartbeatLoop.js +88 -0
- package/dist/client/consoleLogger.d.ts +5 -6
- package/dist/client/consoleLogger.js +5 -6
- package/dist/client/createInternalComponents.d.ts +14 -17
- package/dist/client/createInternalComponents.js +25 -30
- package/dist/client/createModelProxy.d.ts +130 -120
- package/dist/client/createModelProxy.js +152 -122
- package/dist/client/credentialEndpoint.d.ts +40 -42
- package/dist/client/credentialEndpoint.js +35 -36
- package/dist/client/functionalUpdate.d.ts +29 -27
- package/dist/client/functionalUpdate.js +21 -21
- package/dist/client/hostedEndpoints.d.ts +9 -12
- package/dist/client/hostedEndpoints.js +9 -12
- package/dist/client/httpClient.d.ts +57 -53
- package/dist/client/httpClient.js +29 -31
- package/dist/client/identity.d.ts +15 -20
- package/dist/client/identity.js +47 -58
- package/dist/client/modelRegistration.d.ts +5 -9
- package/dist/client/modelRegistration.js +67 -87
- package/dist/client/options.d.ts +134 -157
- package/dist/client/options.js +3 -7
- package/dist/client/registerDataSource.d.ts +9 -9
- package/dist/client/registerDataSource.js +15 -16
- package/dist/client/resourceTypes.d.ts +64 -75
- package/dist/client/resourceTypes.js +4 -10
- package/dist/client/schemaConfig.d.ts +31 -43
- package/dist/client/schemaConfig.js +38 -50
- package/dist/client/sessionMint.d.ts +16 -12
- package/dist/client/sessionMint.js +26 -31
- package/dist/client/validateAbloOptions.d.ts +12 -14
- package/dist/client/validateAbloOptions.js +8 -9
- package/dist/client/writeOptionsSchema.d.ts +18 -16
- package/dist/client/writeOptionsSchema.js +23 -20
- package/dist/client/wsMutationExecutor.d.ts +15 -20
- package/dist/client/wsMutationExecutor.js +17 -23
- package/dist/context.d.ts +6 -4
- package/dist/context.js +6 -4
- package/dist/coordination/index.d.ts +10 -8
- package/dist/coordination/index.js +14 -12
- package/dist/coordination/schema.d.ts +176 -128
- package/dist/coordination/schema.js +197 -133
- package/dist/coordination/trace.d.ts +9 -10
- package/dist/coordination/trace.js +13 -14
- package/dist/core/DatabaseManager.d.ts +5 -7
- package/dist/core/DatabaseManager.js +15 -19
- package/dist/core/QueryProcessor.d.ts +7 -9
- package/dist/core/QueryProcessor.js +22 -28
- package/dist/core/QueryView.d.ts +8 -8
- package/dist/core/QueryView.js +2 -2
- package/dist/core/StoreManager.d.ts +12 -14
- package/dist/core/StoreManager.js +21 -24
- package/dist/core/ViewRegistry.d.ts +5 -5
- package/dist/core/ViewRegistry.js +4 -4
- package/dist/core/index.d.ts +17 -12
- package/dist/core/index.js +32 -26
- package/dist/core/openIDBWithTimeout.d.ts +38 -36
- package/dist/core/openIDBWithTimeout.js +42 -43
- package/dist/core/queryUtils.d.ts +45 -0
- package/dist/core/queryUtils.js +69 -0
- package/dist/core/storeContract.d.ts +63 -61
- package/dist/core/storeContract.js +8 -12
- package/dist/environment.d.ts +28 -0
- package/dist/environment.js +21 -0
- package/dist/errorCodes.d.ts +107 -99
- package/dist/errorCodes.js +131 -132
- package/dist/errors.d.ts +160 -166
- package/dist/errors.js +155 -158
- package/dist/index.d.ts +30 -27
- package/dist/index.js +89 -86
- package/dist/interfaces/index.d.ts +102 -113
- package/dist/interfaces/index.js +5 -4
- package/dist/keys/index.d.ts +27 -29
- package/dist/keys/index.js +41 -40
- 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 +145 -156
- package/dist/mutators/defineMutators.d.ts +23 -34
- 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 +104 -100
- package/dist/policy/types.js +67 -66
- package/dist/query/client.d.ts +28 -23
- package/dist/query/client.js +45 -43
- package/dist/query/types.d.ts +37 -60
- package/dist/query/types.js +13 -33
- package/dist/react/AbloProvider.d.ts +1 -1
- package/dist/react/AbloProvider.js +2 -2
- package/dist/react/context.d.ts +25 -28
- 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 +16 -14
- 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 +10 -11
- package/dist/react/useMutationFailureListener.d.ts +8 -8
- package/dist/react/useMutationFailureListener.js +8 -8
- package/dist/react/useMutators.d.ts +11 -11
- package/dist/react/useMutators.js +3 -3
- package/dist/react/useReactive.js +2 -2
- package/dist/react/useSyncStatus.d.ts +4 -6
- package/dist/react/useUndoScope.d.ts +7 -9
- package/dist/react/useUndoScope.js +1 -1
- 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 +20 -24
- package/dist/schema/ddlLock.js +18 -23
- 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 +35 -48
- package/dist/schema/generate.d.ts +12 -12
- package/dist/schema/generate.js +12 -12
- package/dist/schema/index.d.ts +2 -2
- package/dist/schema/index.js +21 -23
- package/dist/schema/model.d.ts +118 -143
- package/dist/schema/model.js +22 -33
- package/dist/schema/openapi.d.ts +10 -9
- package/dist/schema/openapi.js +5 -3
- package/dist/schema/queries.d.ts +29 -31
- package/dist/schema/queries.js +23 -25
- package/dist/schema/relation.d.ts +89 -99
- package/dist/schema/relation.js +13 -13
- package/dist/schema/residency.d.ts +16 -13
- package/dist/schema/residency.js +16 -13
- package/dist/schema/roles.d.ts +36 -43
- package/dist/schema/roles.js +31 -37
- package/dist/schema/schema.d.ts +33 -42
- package/dist/schema/schema.js +31 -32
- package/dist/schema/select.d.ts +13 -13
- package/dist/schema/select.js +13 -13
- package/dist/schema/serialize.d.ts +28 -31
- 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} +26 -49
- 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 +30 -25
- package/dist/source/adapter.js +10 -10
- package/dist/source/adapters/drizzle.d.ts +28 -23
- package/dist/source/adapters/drizzle.js +30 -25
- package/dist/source/adapters/kysely.d.ts +27 -25
- package/dist/source/adapters/kysely.js +24 -23
- package/dist/source/adapters/memory.d.ts +8 -7
- package/dist/source/adapters/memory.js +9 -8
- package/dist/source/adapters/prisma.d.ts +13 -12
- package/dist/source/adapters/prisma.js +22 -25
- package/dist/source/conformance.d.ts +18 -11
- package/dist/source/conformance.js +17 -11
- package/dist/source/connector.d.ts +31 -32
- package/dist/source/connector.js +28 -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 +46 -58
- package/dist/source/factory.js +22 -27
- package/dist/source/index.d.ts +7 -9
- package/dist/source/index.js +12 -14
- package/dist/source/migrations.d.ts +9 -9
- package/dist/source/migrations.js +9 -9
- package/dist/source/next.d.ts +9 -10
- package/dist/source/next.js +6 -7
- package/dist/source/pushQueue.d.ts +69 -47
- package/dist/source/pushQueue.js +32 -28
- package/dist/source/signing.d.ts +46 -17
- package/dist/source/signing.js +28 -11
- package/dist/source/types.d.ts +121 -104
- package/dist/source/types.js +13 -14
- package/dist/stores/ObjectStore.d.ts +10 -11
- package/dist/stores/ObjectStore.js +11 -12
- package/dist/stores/ObjectStoreContract.d.ts +12 -15
- package/dist/stores/SyncActionStore.d.ts +7 -11
- package/dist/stores/SyncActionStore.js +13 -17
- package/dist/surface.d.ts +27 -20
- package/dist/surface.js +27 -20
- package/dist/sync/{BootstrapHelper.d.ts → BootstrapFetcher.d.ts} +36 -42
- package/dist/sync/{BootstrapHelper.js → BootstrapFetcher.js} +76 -76
- package/dist/sync/ConnectionManager.d.ts +39 -50
- package/dist/sync/ConnectionManager.js +55 -66
- package/dist/sync/NetworkProbe.d.ts +24 -29
- package/dist/sync/NetworkProbe.js +63 -69
- package/dist/sync/{HydrationCoordinator.d.ts → OnDemandLoader.d.ts} +42 -41
- package/dist/sync/{HydrationCoordinator.js → OnDemandLoader.js} +59 -54
- package/dist/sync/{AreaOfInterestManager.d.ts → SubscriptionManager.d.ts} +43 -57
- package/dist/sync/{AreaOfInterestManager.js → SubscriptionManager.js} +43 -51
- package/dist/sync/SyncWebSocket.d.ts +139 -165
- package/dist/sync/SyncWebSocket.js +191 -223
- package/dist/sync/awaitClaimGrant.d.ts +18 -18
- package/dist/sync/awaitClaimGrant.js +11 -11
- package/dist/sync/bootstrapApply.d.ts +34 -24
- package/dist/sync/bootstrapApply.js +27 -19
- package/dist/sync/commitFrames.d.ts +21 -20
- package/dist/sync/commitFrames.js +18 -18
- package/dist/sync/createClaimStream.d.ts +23 -22
- package/dist/sync/createClaimStream.js +105 -23
- package/dist/sync/createPresenceStream.d.ts +19 -18
- package/dist/sync/createPresenceStream.js +25 -26
- package/dist/sync/createSnapshot.d.ts +12 -14
- package/dist/sync/createSnapshot.js +20 -26
- package/dist/sync/credentialLifecycle.d.ts +104 -104
- package/dist/sync/credentialLifecycle.js +140 -147
- package/dist/sync/deltaPipeline.d.ts +36 -34
- package/dist/sync/deltaPipeline.js +64 -65
- package/dist/sync/groupChange.d.ts +63 -61
- package/dist/sync/groupChange.js +74 -78
- package/dist/sync/heartbeat.d.ts +34 -33
- package/dist/sync/heartbeat.js +31 -31
- package/dist/sync/participants.d.ts +19 -19
- package/dist/sync/schemas.d.ts +3 -2
- package/dist/sync/schemas.js +14 -10
- package/dist/sync/syncCursor.d.ts +17 -21
- package/dist/sync/syncCursor.js +17 -21
- package/dist/sync/syncPlan.d.ts +28 -36
- package/dist/sync/syncPlan.js +18 -19
- package/dist/sync/syncPosition.d.ts +54 -49
- package/dist/sync/syncPosition.js +57 -52
- package/dist/sync/wsFrameHandlers.d.ts +35 -36
- package/dist/sync/wsFrameHandlers.js +63 -67
- package/dist/testing/fixtures/bootstrap.d.ts +12 -6
- package/dist/testing/fixtures/bootstrap.js +12 -6
- package/dist/testing/fixtures/deltas.d.ts +30 -33
- package/dist/testing/fixtures/deltas.js +30 -33
- package/dist/testing/fixtures/models.d.ts +11 -10
- package/dist/testing/fixtures/models.js +11 -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 -15
- package/dist/testing/helpers/{sync-engine-harness.js → syncEngineHarness.js} +12 -10
- package/dist/testing/helpers/wait.d.ts +13 -8
- package/dist/testing/helpers/wait.js +13 -8
- package/dist/testing/index.d.ts +3 -3
- package/dist/testing/index.js +2 -2
- 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 +20 -17
- package/dist/testing/mocks/MockSyncContext.js +15 -13
- package/dist/testing/mocks/MockSyncStore.d.ts +10 -10
- package/dist/testing/mocks/MockSyncStore.js +11 -11
- package/dist/testing/mocks/MockWebSocket.d.ts +26 -22
- package/dist/testing/mocks/MockWebSocket.js +22 -21
- package/dist/transactions/TransactionQueue.d.ts +181 -176
- package/dist/transactions/TransactionQueue.js +338 -350
- package/dist/transactions/TransactionStore.d.ts +6 -4
- package/dist/transactions/TransactionStore.js +6 -4
- package/dist/transactions/UnconfirmedWrites.d.ts +82 -0
- package/dist/transactions/UnconfirmedWrites.js +104 -0
- package/dist/transactions/coalesceRules.d.ts +41 -17
- package/dist/transactions/coalesceRules.js +40 -17
- package/dist/transactions/commitPayload.d.ts +48 -52
- package/dist/transactions/commitPayload.js +48 -57
- package/dist/transactions/deltaConfirmation.d.ts +20 -22
- package/dist/transactions/deltaConfirmation.js +37 -45
- package/dist/transactions/optimisticApply.d.ts +49 -0
- package/dist/transactions/optimisticApply.js +65 -0
- package/dist/transactions/{persistedReplay.d.ts → replayValidation.d.ts} +28 -22
- package/dist/transactions/{persistedReplay.js → replayValidation.js} +33 -27
- package/dist/types/global.d.ts +46 -41
- package/dist/types/global.js +20 -19
- package/dist/types/index.d.ts +71 -77
- package/dist/types/index.js +22 -22
- package/dist/types/modelData.d.ts +6 -8
- package/dist/types/modelData.js +5 -7
- package/dist/types/participant.d.ts +10 -11
- package/dist/types/participant.js +6 -8
- package/dist/types/streams.d.ts +208 -195
- 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} +42 -98
- 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 +30 -31
- package/dist/wire/errorEnvelope.js +34 -40
- package/dist/wire/frames.d.ts +79 -86
- package/dist/wire/frames.js +26 -33
- package/dist/wire/index.d.ts +14 -12
- package/dist/wire/index.js +30 -26
- package/dist/wire/listEnvelope.d.ts +16 -23
- package/dist/wire/listEnvelope.js +7 -6
- package/dist/wire/protocol.d.ts +25 -32
- package/dist/wire/protocol.js +25 -32
- package/dist/wire/protocolVersion.d.ts +44 -40
- package/dist/wire/protocolVersion.js +44 -40
- package/docs/coordination.md +59 -0
- package/package.json +11 -10
- package/dist/ai-sdk/coordinated-tool.d.ts +0 -101
- package/dist/ai-sdk/coordination-context.d.ts +0 -52
- package/dist/core/query-utils.d.ts +0 -34
- package/dist/core/query-utils.js +0 -59
- package/dist/schema/sync-delta-row.js +0 -103
- package/dist/schema/sync-delta-wire.js +0 -102
- 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 -8
- package/dist/server/storage-mode.js +0 -28
- package/dist/source/connector-protocol.d.ts +0 -159
- package/dist/source/connector-protocol.js +0 -161
- package/dist/transactions/OptimisticEchoTracker.d.ts +0 -82
- package/dist/transactions/OptimisticEchoTracker.js +0 -104
- package/dist/transactions/mutation-error-handler.d.ts +0 -5
- package/dist/transactions/mutation-error-handler.js +0 -39
- package/dist/transactions/optimistic.d.ts +0 -24
- package/dist/transactions/optimistic.js +0 -45
- package/dist/utils/mobx-setup.d.ts +0 -42
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.27.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- **Claims can now be held for long-running work by heartbeating — on both transports.** A claim's TTL is crash cleanup, not a work estimate; work that outlives it (a 30-minute report, a long agent run) keeps its lease by beating. `claim({ heartbeat: true })` beats automatically until release; `held.heartbeat()` beats by hand. A lapsed lease answers the next beat with `AbloClaimedError` (`claim_lost`) — for a socketless worker, the failed beat _is_ the loss notification, and any write attempted under the old lease is independently rejected by its `readAt` guard.
|
|
8
|
+
|
|
9
|
+
- **Beats carry progress and pressure.** `heartbeat({ details })` stores lightweight progress as the claim's peer-visible `meta.progress` (last beat wins, via `claim.state`); every beat's answer reports `queueDepth` — how many participants wait in line behind the lease — observable per auto-beat with the new `onHeartbeat` claim option.
|
|
10
|
+
|
|
11
|
+
- **`ablo.claims.heartbeatAll({ ttl })`** extends every lease the credential holds in one request (`POST /v1/claims/heartbeat`) — the stateless twin of the WebSocket keepalive, for workers holding many rows.
|
|
12
|
+
|
|
13
|
+
- **Keepalive renewals extend but never shorten a lease**, so an explicit work-duration `ttl` now survives pings and brief reconnects instead of collapsing to the liveness window.
|
|
14
|
+
|
|
15
|
+
- **README.** The quick start leads with a runnable example, and a new _Background workers_ section shows the enqueue-on-your-own-queue / heartbeat-the-claim pattern end to end.
|
|
16
|
+
|
|
3
17
|
## 0.26.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -39,10 +39,10 @@ Under the hood, you define your data once with a Zod schema and get the same
|
|
|
39
39
|
typed model client for every actor — people, server actions, and agents:
|
|
40
40
|
|
|
41
41
|
```ts
|
|
42
|
-
await ablo.
|
|
43
|
-
await ablo.
|
|
44
|
-
await ablo.
|
|
45
|
-
await using
|
|
42
|
+
await ablo.weatherReports.create({ data }) // create
|
|
43
|
+
await ablo.weatherReports.retrieve({ id }) // read
|
|
44
|
+
await ablo.weatherReports.update({ id, data }) // update
|
|
45
|
+
await using claim = await ablo.weatherReports.claim({ id }) // hold for slow agent work
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
The schema is the public contract. It gives you typed model methods, realtime
|
|
@@ -63,8 +63,9 @@ yet? A **sandbox** `sk_test` key holds throwaway **test data** — like Stripe t
|
|
|
63
63
|
mode — so you can explore before pointing it at your Postgres. Test-mode only; in
|
|
64
64
|
production every row lives in your database.)
|
|
65
65
|
|
|
66
|
-
**Built for** collaborative editors, AI agent workflows,
|
|
67
|
-
|
|
66
|
+
**Built for** collaborative editors, AI agent workflows, background workers on
|
|
67
|
+
your own infrastructure, and internal tools — anywhere people and agents change
|
|
68
|
+
shared state and everyone has to see it live.
|
|
68
69
|
|
|
69
70
|
## Set up
|
|
70
71
|
|
|
@@ -111,62 +112,15 @@ instead of guessing:
|
|
|
111
112
|
|
|
112
113
|
## Quick Start
|
|
113
114
|
|
|
115
|
+
One schema, one client, one write path for humans and agents — this runs as-is
|
|
116
|
+
after `ablo push`:
|
|
117
|
+
|
|
114
118
|
```ts
|
|
115
119
|
import Ablo from '@abloatai/ablo';
|
|
116
120
|
import { defineSchema, model, z } from '@abloatai/ablo/schema';
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
The schema is registered once (init scaffolds `ablo/register.ts` for you), and
|
|
120
|
-
every type is one parameter away — no `typeof schema` re-stating, anywhere:
|
|
121
|
-
|
|
122
|
-
```ts
|
|
123
|
-
// ablo/register.ts — scaffolded by `npx ablo init`, sits beside ablo/schema.ts
|
|
124
|
-
import type { schema } from './schema';
|
|
125
|
-
declare module '@abloatai/ablo' {
|
|
126
|
-
interface Register { Schema: typeof schema }
|
|
127
|
-
}
|
|
128
|
-
export {};
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
It's a regular `.ts` module, not a hand-authored `.d.ts`. The top-level
|
|
132
|
-
`import type { schema }` makes the `declare module` block *merge* into (augment)
|
|
133
|
-
the SDK's `Register` interface instead of colliding with it — the same shape
|
|
134
|
-
[TanStack Router uses in `src/router.tsx`](https://tanstack.com/router/latest/docs/framework/react/guide/type-safety). Any `.ts` file in your
|
|
135
|
-
`tsconfig` `include` works; it never needs to be imported.
|
|
136
|
-
|
|
137
|
-
```ts
|
|
138
|
-
import type { Model } from '@abloatai/ablo/schema';
|
|
139
|
-
|
|
140
|
-
type WeatherReport = Model<'weatherReports'>; // fully typed from YOUR schema
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
(The same `Register` binding types every hook and client — it's the
|
|
144
|
-
TanStack-Router pattern: declare the source of truth once, everything
|
|
145
|
-
infers from it.)
|
|
146
121
|
|
|
147
|
-
### Naming the client type
|
|
148
|
-
|
|
149
|
-
When you need to pass the client around (a function parameter, a context value),
|
|
150
|
-
**infer the type from the value** — `type Sync = typeof sync`:
|
|
151
|
-
|
|
152
|
-
```ts
|
|
153
|
-
export const sync = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
|
|
154
|
-
export type Sync = typeof sync; // fully-typed, schema-aware
|
|
155
|
-
|
|
156
|
-
function persist(client: Sync) { /* ... */ }
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
This is the same idiom as tRPC's `type AppRouter = typeof appRouter` and
|
|
160
|
-
Drizzle's `typeof db` — the factory resolves the typed overload at the call
|
|
161
|
-
site, so `typeof sync` carries your schema. Do **not** write
|
|
162
|
-
`ReturnType<typeof Ablo>`: that collapses to the untyped last overload and
|
|
163
|
-
loses your model types. There is no bespoke client-type generic to import —
|
|
164
|
-
`typeof` your client value is the type.
|
|
165
|
-
|
|
166
|
-
```ts
|
|
167
122
|
const schema = defineSchema({
|
|
168
|
-
//
|
|
169
|
-
// provided automatically — don't declare them.
|
|
123
|
+
// id, createdAt, updatedAt, organizationId, createdBy come free on every model
|
|
170
124
|
weatherReports: model({
|
|
171
125
|
location: z.string(),
|
|
172
126
|
status: z.enum(['pending', 'ready']),
|
|
@@ -174,29 +128,21 @@ const schema = defineSchema({
|
|
|
174
128
|
}),
|
|
175
129
|
});
|
|
176
130
|
|
|
177
|
-
const ablo = Ablo({
|
|
178
|
-
schema,
|
|
179
|
-
apiKey: process.env.ABLO_API_KEY, // written to .env.local by `npx ablo push`
|
|
180
|
-
});
|
|
181
|
-
// Your Postgres is connected once via `npx ablo connect` (logical replication) —
|
|
182
|
-
// not passed here. Ablo tails its WAL; your rows never leave your database.
|
|
183
|
-
|
|
131
|
+
const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
|
|
184
132
|
await ablo.ready();
|
|
185
133
|
|
|
186
134
|
const created = await ablo.weatherReports.create({
|
|
187
|
-
data: {
|
|
188
|
-
location: 'Stockholm',
|
|
189
|
-
status: 'pending',
|
|
190
|
-
},
|
|
135
|
+
data: { location: 'Stockholm', status: 'pending' },
|
|
191
136
|
});
|
|
192
137
|
|
|
193
|
-
//
|
|
194
|
-
// claim is held nobody else can overwrite it; anyone else who tries waits in
|
|
195
|
-
// line and re-reads the result. This is the whole point of Ablo.
|
|
138
|
+
// Claim the row before slow work — anyone else waits in line, then re-reads.
|
|
196
139
|
await using claim = await ablo.weatherReports.claim({ id: created.id });
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
140
|
+
const forecast = await fetchForecast(claim.data.location); // slow: API or LLM call
|
|
141
|
+
await ablo.weatherReports.update({
|
|
142
|
+
id: created.id,
|
|
143
|
+
data: { status: 'ready', forecast },
|
|
144
|
+
claim, // the write completes the claimed work and releases the lease
|
|
145
|
+
});
|
|
200
146
|
|
|
201
147
|
const ready = ablo.weatherReports.get(created.id);
|
|
202
148
|
console.log({ id: ready?.id, status: ready?.status });
|
|
@@ -210,6 +156,38 @@ Expected output:
|
|
|
210
156
|
{ id: '...', status: 'ready' }
|
|
211
157
|
```
|
|
212
158
|
|
|
159
|
+
### TypeScript setup (once, scaffolded for you)
|
|
160
|
+
|
|
161
|
+
`npx ablo init` writes `ablo/register.ts` next to your schema. It binds your
|
|
162
|
+
schema to the SDK's types once — the same declaration-merging shape
|
|
163
|
+
[TanStack Router uses](https://tanstack.com/router/latest/docs/framework/react/guide/type-safety) —
|
|
164
|
+
so every hook and client infers from it:
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
// ablo/register.ts — scaffolded by `npx ablo init`
|
|
168
|
+
import type { schema } from './schema';
|
|
169
|
+
declare module '@abloatai/ablo' {
|
|
170
|
+
interface Register { Schema: typeof schema }
|
|
171
|
+
}
|
|
172
|
+
export {};
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
import type { Model } from '@abloatai/ablo/schema';
|
|
177
|
+
|
|
178
|
+
type WeatherReport = Model<'weatherReports'>; // fully typed from your schema
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
To pass the client around, take the type from the value — the tRPC /
|
|
182
|
+
Drizzle idiom:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
export const sync = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
|
|
186
|
+
export type Sync = typeof sync;
|
|
187
|
+
|
|
188
|
+
function persist(client: Sync) { /* ... */ }
|
|
189
|
+
```
|
|
190
|
+
|
|
213
191
|
## Reading
|
|
214
192
|
|
|
215
193
|
Two ways to read, depending on whether you can wait. `get(id)` / `getAll({ where })`
|
|
@@ -262,7 +240,11 @@ meanwhile, or worse, acts on stale state. `claim` holds the row across that gap:
|
|
|
262
240
|
await using claim = await ablo.weatherReports.claim({ id: 'report_stockholm' });
|
|
263
241
|
const report = claim.data;
|
|
264
242
|
const forecast = await weatherAgent.getWeather(report.location);
|
|
265
|
-
await ablo.weatherReports.update({
|
|
243
|
+
await ablo.weatherReports.update({
|
|
244
|
+
id: report.id,
|
|
245
|
+
data: { forecast, status: 'ready' },
|
|
246
|
+
claim, // attribute the write to the held claim
|
|
247
|
+
});
|
|
266
248
|
```
|
|
267
249
|
|
|
268
250
|
If someone else holds the row, `claim()` waits in a fair queue, then re-reads —
|
|
@@ -311,6 +293,46 @@ try {
|
|
|
311
293
|
See [Coordination](./docs/coordination.md) for the full `claim` / `claim.state` /
|
|
312
294
|
`claim.queue` / `claim.release` reference.
|
|
313
295
|
|
|
296
|
+
## Background workers — jobs that run for minutes, not seconds
|
|
297
|
+
|
|
298
|
+
Your API route enqueues a job on your own queue (SQS, EventBridge, anything);
|
|
299
|
+
a worker on your own infrastructure does the slow part. **Keep that queue —
|
|
300
|
+
Ablo is the worker's data layer.** It covers the two things every queue leaves
|
|
301
|
+
to you: keeping the row safe, and showing progress live.
|
|
302
|
+
|
|
303
|
+
Long work holds its claim by **heartbeating** — the same pattern as an SQS
|
|
304
|
+
visibility heartbeat or a Temporal activity heartbeat:
|
|
305
|
+
|
|
306
|
+
```ts
|
|
307
|
+
// on your worker — stateless HTTP, no socket to hold
|
|
308
|
+
await using claim = await ablo.weatherReports.claim({
|
|
309
|
+
id: msg.reportId,
|
|
310
|
+
ttl: '10m',
|
|
311
|
+
heartbeat: true, // beats automatically until release
|
|
312
|
+
onHeartbeatLost: () => abort(), // the lease is gone → stop working
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
for (const step of steps) {
|
|
316
|
+
await runStep(step);
|
|
317
|
+
// write progress to the row — every subscribed UI updates live
|
|
318
|
+
await ablo.weatherReports.update({ id: msg.reportId, data: { progress: step.pct }, claim });
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
- A worker that **crashes** stops beating; the row frees within one beat and
|
|
323
|
+
the next worker takes over.
|
|
324
|
+
- A worker that **wakes up late** learns it on its next beat
|
|
325
|
+
(`AbloClaimedError`), and the write path rejects its stale writes anyway.
|
|
326
|
+
- **Retries stay on your queue** — the redelivered job claims the now-free
|
|
327
|
+
row, reads its current state, and resumes.
|
|
328
|
+
- A worker holding **many rows** extends them all in one call:
|
|
329
|
+
`ablo.claims.heartbeatAll({ ttl: '5m' })`.
|
|
330
|
+
|
|
331
|
+
SQS's heartbeat protects the *message* from redelivery; Ablo's protects the
|
|
332
|
+
*row* from concurrent and stale writes. SQS is at-least-once, so two workers
|
|
333
|
+
will eventually get the same job — the claim is what keeps that from
|
|
334
|
+
corrupting data.
|
|
335
|
+
|
|
314
336
|
## React
|
|
315
337
|
|
|
316
338
|
In a React app it's the **same `ablo.<model>` API** — just mounted through a
|
|
@@ -445,14 +467,8 @@ connects:
|
|
|
445
467
|
| **Logical replication** (primary) | `npx ablo connect` prints the setup SQL (`wal_level=logical`, a publication, a `REPLICATION` role); `npx ablo connect --register` tells Ablo to replicate it. Ablo tails the WAL — it never runs DDL on, writes to, owns, or migrates your database. | Your database can grant a `REPLICATION` role (most can). |
|
|
446
468
|
| **Signed endpoint** (fallback) | Your app exposes one route built from an ORM adapter (`prismaDataSource` / `drizzleDataSource`); Ablo sends signed requests and your app touches its own database. Needs no database configuration. | Your database **can't** grant a replication role (a locked-down managed DB). |
|
|
447
469
|
|
|
448
|
-
|
|
449
|
-
coordination
|
|
450
|
-
connect your own Postgres. Test-mode only: in production your rows always live in
|
|
451
|
-
your database and Ablo holds just the transaction log, never your data.)
|
|
452
|
-
|
|
453
|
-
Your database is the system of record. The old `databaseUrl` dial-in (Ablo
|
|
454
|
-
holding a read/write connection) is **deprecated and being removed** — connect
|
|
455
|
-
via logical replication instead. See
|
|
470
|
+
Your database is the system of record — Ablo holds the transaction log and
|
|
471
|
+
coordination, never your rows. See
|
|
456
472
|
[Connect Your Database](./docs/data-sources.md).
|
|
457
473
|
|
|
458
474
|
## Configuration
|
|
@@ -466,7 +482,7 @@ Every other option has correct defaults:
|
|
|
466
482
|
| --- | --- | --- | --- |
|
|
467
483
|
| `schema` | `Schema` | — (required) | Typed model proxies (`ablo.<model>.*`) |
|
|
468
484
|
| `apiKey` | `string \| ApiKeySetter \| null` | `process.env.ABLO_API_KEY` | Server key — a string, or an async function for rotation |
|
|
469
|
-
| `databaseUrl` | `string \| null` | `—` |
|
|
485
|
+
| `databaseUrl` | `string \| null` | `—` | Deprecated. Connect via `npx ablo connect` instead — see [Connect Your Database](./docs/data-sources.md). |
|
|
470
486
|
|
|
471
487
|
Keep `apiKey` in trusted server runtimes. In the browser, `<AbloProvider>`
|
|
472
488
|
authenticates with the signed-in user's session; the raw-key path is gated
|
|
@@ -519,7 +535,7 @@ contract; there are no retry or timeout knobs to tune.
|
|
|
519
535
|
- [Guarantees](./docs/guarantees.md) — confirmed writes, stale-write protection, claim coordination, and agent lifecycle.
|
|
520
536
|
- [Integration Guide](./docs/integration-guide.md) — integrate React, your database, multiplayer, and agents.
|
|
521
537
|
- [React](./docs/react.md) — `<AbloProvider>`, `useAblo`, presence, status, and bootstrap gating.
|
|
522
|
-
- [Coordination](./docs/coordination.md) — `claim` / `claim.state` / `claim.queue` / `claim.release` reference: hold a row across slow agent work, and observe the line waiting behind it.
|
|
538
|
+
- [Coordination](./docs/coordination.md) — `claim` / `claim.state` / `claim.queue` / `claim.release` / `heartbeat` reference: hold a row across slow agent work — minutes or hours, via heartbeats — and observe the line waiting behind it.
|
|
523
539
|
- [Client Behavior](./docs/client-behavior.md) — options, errors, retries, timeouts, and public imports.
|
|
524
540
|
- [Connect Your Database](./docs/data-sources.md) — connect your Postgres by logical replication (`npx ablo connect`) or, as a fallback, a signed endpoint; your database is the system of record either way.
|
|
525
541
|
- [Existing Python Backend](./docs/examples/existing-python-backend.md) — migrate existing Python endpoints to multiplayer and agent-safe writes gradually.
|
|
@@ -1,28 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The base class that application-specific sync stores extend. It supplies the
|
|
3
|
+
* shared orchestration for reads, writes, delta processing, and bootstrap, and
|
|
4
|
+
* exports the core types those stores build on.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* This file only contains types and the abstract contract — the actual
|
|
11
|
-
* implementation stays in the app's SyncedStore.ts until we incrementally
|
|
12
|
-
* pull generic methods into this base class.
|
|
6
|
+
* A subclass adds its own domain behavior — lazy-loaded relations,
|
|
7
|
+
* collaboration events, and model enrichment — by overriding the protected
|
|
8
|
+
* extension points defined here. The heavy lifting is delegated to injected
|
|
9
|
+
* collaborators: {@link SyncClient} owns pool writes and the transaction
|
|
10
|
+
* queue, {@link Database} owns local persistence, {@link InstanceCache} holds the
|
|
11
|
+
* in-memory models, and {@link ModelRegistry} holds their metadata.
|
|
13
12
|
*/
|
|
14
13
|
import type { RecoveryClass } from './errorCodes.js';
|
|
15
14
|
import { ConnectionManager } from './sync/ConnectionManager.js';
|
|
16
|
-
import {
|
|
15
|
+
import { SubscriptionManager } from './sync/SubscriptionManager.js';
|
|
17
16
|
import { type ParticipantScope } from './sync/participants.js';
|
|
18
17
|
import type { SyncClient } from './SyncClient.js';
|
|
19
18
|
import type { Database, BootstrapResult } from './Database.js';
|
|
20
|
-
import type {
|
|
19
|
+
import type { InstanceCache } from './InstanceCache.js';
|
|
21
20
|
import { ModelRegistry } from './ModelRegistry.js';
|
|
22
21
|
import { SyncWebSocket, type SyncDelta, type SyncGroupChangePayload, type GroupAddedPayload, type GroupRemovedPayload, type BootstrapHint, type BootstrapDataEvent, type PresenceUpdateEvent, type EventMap, type DefaultCollaborationEvents } from './sync/SyncWebSocket.js';
|
|
23
22
|
import { QueryProcessor } from './core/QueryProcessor.js';
|
|
24
23
|
import { Model } from './Model.js';
|
|
25
|
-
import { ModelScope } from './
|
|
24
|
+
import { ModelScope } from './InstanceCache.js';
|
|
26
25
|
import type { Schema } from './schema/schema.js';
|
|
27
26
|
import type { SyncStatus, LocalMutation } from './core/storeContract.js';
|
|
28
27
|
import type { AuthCredentialSource } from './auth/credentialSource.js';
|
|
@@ -56,7 +55,7 @@ export interface SyncedStoreConfig {
|
|
|
56
55
|
*/
|
|
57
56
|
enrichmentPlan?: readonly EnrichmentPlanEntry[];
|
|
58
57
|
/**
|
|
59
|
-
* Foreign-key indexes to register on the
|
|
58
|
+
* Foreign-key indexes to register on the InstanceCache at construction
|
|
60
59
|
* time. Replaces the subclass override of `registerForeignKeys` for
|
|
61
60
|
* per-model FK registration. Merged with schema-derived entries
|
|
62
61
|
* (relations marked `{ index: true }` on `belongsTo`). Both sets
|
|
@@ -79,8 +78,7 @@ export interface UserContext {
|
|
|
79
78
|
kind?: 'user' | 'agent' | 'system';
|
|
80
79
|
/** Restricted (`rk_`) API key for `kind: 'agent'` — the agent's
|
|
81
80
|
* bearer credential. Sent in the `ablo.bearer.<token>` WebSocket
|
|
82
|
-
* subprotocol, never in the URL.
|
|
83
|
-
* Biscuit→opaque-key migration.) */
|
|
81
|
+
* subprotocol, never in the URL. */
|
|
84
82
|
capabilityToken?: string;
|
|
85
83
|
/** Server-authoritative sync groups, supplied by auth/capability
|
|
86
84
|
* exchange. The SDK does not invent org/user/default groups; app
|
|
@@ -119,16 +117,15 @@ export { ModelScope };
|
|
|
119
117
|
export type { SyncDelta, SyncGroupChangePayload, GroupAddedPayload, GroupRemovedPayload, BootstrapHint, BootstrapDataEvent, PresenceUpdateEvent, };
|
|
120
118
|
export { deriveSyncPlanFromSchema } from './sync/syncPlan.js';
|
|
121
119
|
/**
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* base class incrementally as they are genericized.
|
|
120
|
+
* The abstract base class that application-specific sync stores extend. It
|
|
121
|
+
* carries the injected collaborators, the observable sync status, and the
|
|
122
|
+
* orchestration for initialization, delta processing, bootstrap, and the
|
|
123
|
+
* read and write API. A subclass supplies its own domain behavior by
|
|
124
|
+
* overriding the protected extension points defined here and by typing its
|
|
125
|
+
* collaboration events through the generic parameter.
|
|
129
126
|
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
127
|
+
* A subclass must call `super(dependencies, config)` and then set up its own
|
|
128
|
+
* MobX observables.
|
|
132
129
|
*
|
|
133
130
|
* Generic over `TCollaboration` — an app-defined event map for real-time
|
|
134
131
|
* collaboration events (cursors, selections, presence beyond the core set).
|
|
@@ -150,7 +147,7 @@ export declare class BaseSyncedStore<TCollaboration extends EventMap<TCollaborat
|
|
|
150
147
|
syncStatus: SyncStatus;
|
|
151
148
|
protected readonly syncClient: SyncClient;
|
|
152
149
|
protected readonly database: Database;
|
|
153
|
-
protected readonly objectPool:
|
|
150
|
+
protected readonly objectPool: InstanceCache;
|
|
154
151
|
protected readonly modelRegistry: ModelRegistry;
|
|
155
152
|
protected readonly auth?: AuthCredentialSource;
|
|
156
153
|
/**
|
|
@@ -166,7 +163,7 @@ export declare class BaseSyncedStore<TCollaboration extends EventMap<TCollaborat
|
|
|
166
163
|
* to whichever instance is current, so callers (the React participant
|
|
167
164
|
* hook) never hold a stale reference. Null until `setupWebSocketSync`.
|
|
168
165
|
*/
|
|
169
|
-
protected areaOfInterest:
|
|
166
|
+
protected areaOfInterest: SubscriptionManager | null;
|
|
170
167
|
/** Sync groups whose current state has been backfilled into the pool
|
|
171
168
|
* (hydrate-on-enter). Cleared when the pool is reset on (re)bootstrap. */
|
|
172
169
|
private readonly hydratedGroups;
|
|
@@ -185,22 +182,22 @@ export declare class BaseSyncedStore<TCollaboration extends EventMap<TCollaborat
|
|
|
185
182
|
getSyncWebSocket(): SyncWebSocket<TCollaboration> | null;
|
|
186
183
|
private scopeToGroups;
|
|
187
184
|
/**
|
|
188
|
-
* Bring a scope into view
|
|
189
|
-
* `{ hydrate: true }`,
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
* and the live
|
|
185
|
+
* Bring a scope into view and subscribe to its sync groups. With
|
|
186
|
+
* `{ hydrate: true }`, also backfill the groups' current state into the pool
|
|
187
|
+
* once the subscription is active. The order matters: subscribing first
|
|
188
|
+
* guarantees no live delta is missed in the gap before the snapshot lands.
|
|
189
|
+
* Hydration is best-effort — a failed backfill never rejects `enterScope`,
|
|
190
|
+
* and the live delta stream keeps flowing regardless.
|
|
194
191
|
*/
|
|
195
192
|
enterScope(scope: ParticipantScope, opts?: {
|
|
196
193
|
hydrate?: boolean;
|
|
197
194
|
}): Promise<void>;
|
|
198
195
|
/**
|
|
199
|
-
* Backfill the current state of `syncGroups` into the pool
|
|
200
|
-
* snapshot fetch
|
|
201
|
-
* (skips groups already hydrated) and single-flight (concurrent
|
|
202
|
-
* same group share one fetch).
|
|
203
|
-
*
|
|
196
|
+
* Backfill the current state of `syncGroups` into the pool with a side-effect-free
|
|
197
|
+
* scoped snapshot fetch followed by the version-guarded scoped apply. The call
|
|
198
|
+
* is idempotent (it skips groups already hydrated) and single-flight (concurrent
|
|
199
|
+
* enters of the same group share one fetch). On error the groups are left
|
|
200
|
+
* unmarked, so a later re-enter retries.
|
|
204
201
|
*/
|
|
205
202
|
protected hydrateGroups(syncGroups: readonly string[]): Promise<void>;
|
|
206
203
|
/** Leave a scope → its groups go warm (hysteresis), then drop on sweep. */
|
|
@@ -246,15 +243,14 @@ export declare class BaseSyncedStore<TCollaboration extends EventMap<TCollaborat
|
|
|
246
243
|
constructor(dependencies: {
|
|
247
244
|
syncClient: SyncClient;
|
|
248
245
|
database: Database;
|
|
249
|
-
objectPool:
|
|
246
|
+
objectPool: InstanceCache;
|
|
250
247
|
modelRegistry: ModelRegistry;
|
|
251
248
|
/**
|
|
252
|
-
* Optional schema. When provided,
|
|
253
|
-
* the schema's models
|
|
254
|
-
* the enrichment plan from declarative annotations.
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
* instead.
|
|
249
|
+
* Optional schema. When provided, {@link deriveSyncPlanFromSchema} walks
|
|
250
|
+
* the schema's models and relations to auto-populate foreign-key indexes
|
|
251
|
+
* and the enrichment plan from their declarative annotations. Subclasses
|
|
252
|
+
* that register model classes directly can instead pass explicit
|
|
253
|
+
* `config.foreignKeyIndexes` / `config.enrichmentPlan`.
|
|
258
254
|
*/
|
|
259
255
|
schema?: TSchema;
|
|
260
256
|
/** Sync server URL for WebSocket connection. Converted to wss:// automatically. */
|
|
@@ -263,17 +259,17 @@ export declare class BaseSyncedStore<TCollaboration extends EventMap<TCollaborat
|
|
|
263
259
|
auth?: AuthCredentialSource;
|
|
264
260
|
}, config?: SyncedStoreConfig);
|
|
265
261
|
/**
|
|
266
|
-
* Register foreign
|
|
262
|
+
* Register foreign-key indexes for constant-time lookups.
|
|
267
263
|
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
264
|
+
* This is an override hook. The preferred way to declare a foreign-key
|
|
265
|
+
* index is `config.foreignKeyIndexes` at construction time, or marking the
|
|
266
|
+
* `belongsTo` relation with `{ index: true }` in the schema. The hook fires
|
|
267
|
+
* after the schema-derived and config registrations, so a subclass can
|
|
268
|
+
* layer additional indexes on top.
|
|
273
269
|
*/
|
|
274
270
|
protected registerForeignKeys(): void;
|
|
275
271
|
/**
|
|
276
|
-
* Enrich delta data with related models from the
|
|
272
|
+
* Enrich delta data with related models from the InstanceCache.
|
|
277
273
|
*
|
|
278
274
|
* Base implementation walks `this.enrichmentPlan` — entries populated
|
|
279
275
|
* from the schema's `{ enrich: true }` relations and from
|
|
@@ -390,11 +386,12 @@ export declare class BaseSyncedStore<TCollaboration extends EventMap<TCollaborat
|
|
|
390
386
|
*/
|
|
391
387
|
performCredentialRefresh(): Promise<'refreshed' | 'session_error' | 'network_error'>;
|
|
392
388
|
/**
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
396
|
-
*
|
|
397
|
-
*
|
|
389
|
+
* The authentication-recovery path for HTTP transports, such as the lazy
|
|
390
|
+
* query lane. It runs a single-flight credential re-mint driven by the
|
|
391
|
+
* rejection's recovery class, routing outcomes through the same state
|
|
392
|
+
* machine the WebSocket probe uses. `'retry'` means a fresh credential is
|
|
393
|
+
* now in the credential source and the request should be replayed once.
|
|
394
|
+
* Full contract on {@link CredentialLifecycle.recoverFromAuthRejection}.
|
|
398
395
|
*/
|
|
399
396
|
recoverFromAuthRejection(recovery: RecoveryClass): Promise<'retry' | 'stop'>;
|
|
400
397
|
/**
|
|
@@ -406,11 +403,11 @@ export declare class BaseSyncedStore<TCollaboration extends EventMap<TCollaborat
|
|
|
406
403
|
*/
|
|
407
404
|
nudgeReconnect(): void;
|
|
408
405
|
/**
|
|
409
|
-
* Install the access-credential lifecycle
|
|
410
|
-
*
|
|
411
|
-
*
|
|
412
|
-
*
|
|
413
|
-
*
|
|
406
|
+
* Install the client-owned access-credential lifecycle: register `getToken`
|
|
407
|
+
* as the reactive re-mint hook and arm the browser-only proactive refresh
|
|
408
|
+
* (a refresh timer plus an OS-wake re-mint). Idempotent — a second call
|
|
409
|
+
* replaces the first — and torn down on {@link disconnect}. Full rationale
|
|
410
|
+
* on {@link CredentialLifecycle.start}.
|
|
414
411
|
*/
|
|
415
412
|
startCredentialLifecycle(getToken: CredentialRefresher, opts?: {
|
|
416
413
|
proactiveInNode?: boolean;
|
|
@@ -431,8 +428,9 @@ export declare class BaseSyncedStore<TCollaboration extends EventMap<TCollaborat
|
|
|
431
428
|
*/
|
|
432
429
|
protected handleGroupAdded(payload: GroupAddedPayload, syncId: number): Promise<void>;
|
|
433
430
|
/**
|
|
434
|
-
* Handle an actionType 'S' (GroupRemoved) delta
|
|
435
|
-
* state
|
|
431
|
+
* Handle an actionType 'S' (GroupRemoved) delta: for safety, clear the
|
|
432
|
+
* revoked local state and trigger a full re-bootstrap. See
|
|
433
|
+
* {@link groupChange.handleGroupRemoved}.
|
|
436
434
|
*/
|
|
437
435
|
protected handleGroupRemoved(delta: SyncDelta): Promise<void>;
|
|
438
436
|
/** Compute new sync groups after applying additions and removals */
|
|
@@ -453,8 +451,7 @@ export declare class BaseSyncedStore<TCollaboration extends EventMap<TCollaborat
|
|
|
453
451
|
protected checkSyncGroupShrinkage(): Promise<void>;
|
|
454
452
|
/** Narrow context the bootstrap-apply leaf talks back through. */
|
|
455
453
|
private poolContext;
|
|
456
|
-
/** Apply bootstrap data to the
|
|
457
|
-
/** Apply bootstrap data to the ObjectPool. Delegates pool writes to SyncClient. */
|
|
454
|
+
/** Apply bootstrap data to the {@link InstanceCache}, removing entities that are no longer present (ghost removal). Pool writes are delegated to {@link SyncClient}. */
|
|
458
455
|
protected applyBootstrapToPool(bootstrapResult: BootstrapResult, protectedIds?: ReadonlySet<string>): RehydrationStats;
|
|
459
456
|
/**
|
|
460
457
|
* Initialize the sync engine with user context.
|
|
@@ -553,23 +550,24 @@ export declare class BaseSyncedStore<TCollaboration extends EventMap<TCollaborat
|
|
|
553
550
|
/**
|
|
554
551
|
* Apply a complete, server-delivered delta frame atomically.
|
|
555
552
|
*
|
|
556
|
-
* A `delta_batch`
|
|
557
|
-
* the
|
|
558
|
-
* `processDeltaWithBatching` path re-
|
|
559
|
-
* debounce timer
|
|
560
|
-
*
|
|
561
|
-
*
|
|
562
|
-
*
|
|
553
|
+
* A `delta_batch` WebSocket event (a reconnect or catch-up replay) already
|
|
554
|
+
* carries the full set of missed deltas. Routing it through the per-delta
|
|
555
|
+
* `processDeltaWithBatching` path would re-chunk it via the live-traffic
|
|
556
|
+
* debounce timer and `maxBatchSize` force-flush, so a 300-delta catch-up
|
|
557
|
+
* would fan out into several separate `flushPendingDeltas` cycles — each its
|
|
558
|
+
* own local write, pool mutation, `models:changed` emit, and re-render, so
|
|
559
|
+
* the UI visibly repaints once per chunk.
|
|
563
560
|
*
|
|
564
|
-
*
|
|
565
|
-
* watermark,
|
|
566
|
-
* a flush, then
|
|
567
|
-
*
|
|
568
|
-
*
|
|
561
|
+
* Instead, this runs the per-delta bookkeeping (deduplication, ack, version
|
|
562
|
+
* vector, watermark, group-change routing, delete cascade) for every delta
|
|
563
|
+
* without scheduling a flush, then flushes once — collapsing the whole frame
|
|
564
|
+
* into a single local write, pool mutation, `models:changed` emit, and
|
|
565
|
+
* re-render. The post-bootstrap replay of deltas queued during bootstrap
|
|
566
|
+
* uses the same path.
|
|
569
567
|
*
|
|
570
|
-
*
|
|
571
|
-
* with
|
|
572
|
-
* eventually drives through `flushPendingDeltas`.
|
|
568
|
+
* It is named `applyDeltaFrame`, not `processDeltaBatch`, to avoid confusion
|
|
569
|
+
* with {@link Database.processDeltaBatch} — the lower-level local write this
|
|
570
|
+
* eventually drives through `flushPendingDeltas`.
|
|
573
571
|
*/
|
|
574
572
|
protected applyDeltaFrame(deltas: SyncDelta[]): void;
|
|
575
573
|
/**
|
|
@@ -597,8 +595,7 @@ export declare class BaseSyncedStore<TCollaboration extends EventMap<TCollaborat
|
|
|
597
595
|
* skips both the scan AND the allocation.
|
|
598
596
|
*/
|
|
599
597
|
protected cascadeCancelTransactionsForDeletedParent(parentModelName: string, parentId: string): void;
|
|
600
|
-
/** Flush pending deltas with deduplication
|
|
601
|
-
/** Flush pending deltas with deduplication. Delegates pool writes to SyncClient. */
|
|
598
|
+
/** Flush pending deltas with deduplication. Pool writes are delegated to {@link SyncClient}. */
|
|
602
599
|
protected flushPendingDeltas(): Promise<void>;
|
|
603
600
|
/** Check if a model type is local-only (no sync). Override for domain-specific models. */
|
|
604
601
|
protected isLocalOnlyModel(_modelName: string): boolean;
|
|
@@ -663,10 +660,10 @@ export declare class BaseSyncedStore<TCollaboration extends EventMap<TCollaborat
|
|
|
663
660
|
*/
|
|
664
661
|
create<K extends keyof TSchema['models'] & string>(typename: K, data: Record<string, unknown>): import('./schema/schema.js').InferModel<TSchema, K> | null;
|
|
665
662
|
/**
|
|
666
|
-
*
|
|
667
|
-
*
|
|
668
|
-
*
|
|
669
|
-
*
|
|
663
|
+
* Query entry point for callers that hold a {@link Model} constructor and an
|
|
664
|
+
* options object. It filters, orders, and paginates the matching models from
|
|
665
|
+
* the pool. Prefer the schema-typed read surface (`ablo.<model>.list`) where
|
|
666
|
+
* you can, since it infers concrete row types without a class value or cast.
|
|
670
667
|
*/
|
|
671
668
|
queryByClass(modelClass: ModelConstructor<Model>, options?: {
|
|
672
669
|
predicate?: (model: Model) => boolean;
|
|
@@ -695,7 +692,7 @@ export declare class BaseSyncedStore<TCollaboration extends EventMap<TCollaborat
|
|
|
695
692
|
protected incrementPendingChanges(): void;
|
|
696
693
|
protected decrementPendingChanges(): void;
|
|
697
694
|
protected updateSyncStatus(updates: Partial<SyncStatus>): void;
|
|
698
|
-
get pool():
|
|
695
|
+
get pool(): InstanceCache;
|
|
699
696
|
get lastSyncId(): number;
|
|
700
697
|
get isReady(): boolean;
|
|
701
698
|
get isSyncing(): boolean;
|