@abloatai/ablo 0.26.0 → 0.28.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 +42 -2
- package/README.md +102 -86
- package/dist/BaseSyncedStore.d.ts +85 -88
- package/dist/BaseSyncedStore.js +134 -151
- package/dist/Database.d.ts +68 -69
- package/dist/Database.js +316 -135
- 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 +54 -52
- package/dist/Model.js +78 -62
- 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 +122 -118
- package/dist/SyncClient.js +541 -245
- package/dist/adapters/alwaysOnline.d.ts +6 -8
- package/dist/adapters/alwaysOnline.js +6 -8
- package/dist/adapters/inMemoryStorage.d.ts +10 -9
- package/dist/adapters/inMemoryStorage.js +21 -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 +173 -121
- package/dist/client/Ablo.d.ts +97 -74
- package/dist/client/Ablo.js +129 -163
- package/dist/client/ApiClient.d.ts +30 -19
- package/dist/client/ApiClient.js +442 -81
- 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 +16 -17
- package/dist/client/createInternalComponents.js +26 -31
- 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 +59 -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 +78 -87
- package/dist/client/options.d.ts +157 -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 +16 -20
- package/dist/client/wsMutationExecutor.js +18 -23
- package/dist/commit/contract.d.ts +493 -0
- package/dist/commit/contract.js +187 -0
- package/dist/commit/index.d.ts +6 -0
- package/dist/commit/index.js +5 -0
- 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 +14 -14
- package/dist/core/StoreManager.js +33 -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 +137 -134
- package/dist/errors.d.ts +160 -166
- package/dist/errors.js +155 -158
- package/dist/index.d.ts +36 -27
- package/dist/index.js +91 -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 +124 -131
- package/dist/mutators/UndoManager.js +177 -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 +28 -25
- package/dist/react/useAblo.js +41 -17
- 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 +3 -3
- 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 +64 -43
- 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 +24 -12
- package/dist/stores/ObjectStore.js +38 -16
- package/dist/stores/ObjectStoreContract.d.ts +14 -15
- package/dist/stores/SyncActionStore.d.ts +7 -11
- package/dist/stores/SyncActionStore.js +13 -17
- package/dist/surface.d.ts +28 -21
- package/dist/surface.js +29 -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 +141 -166
- 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/persistedPrefix.d.ts +12 -0
- package/dist/sync/persistedPrefix.js +22 -0
- 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 +5 -3
- package/dist/testing/index.js +3 -2
- package/dist/testing/mocks/FakeDatabase.d.ts +18 -0
- package/dist/testing/mocks/FakeDatabase.js +10 -0
- 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 +28 -23
- package/dist/testing/mocks/MockWebSocket.js +22 -21
- package/dist/transactions/TransactionQueue.d.ts +244 -181
- package/dist/transactions/TransactionQueue.js +929 -423
- 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/commitEnvelope.d.ts +132 -0
- package/dist/transactions/commitEnvelope.js +139 -0
- package/dist/transactions/commitOutboxStore.d.ts +32 -0
- package/dist/transactions/commitOutboxStore.js +26 -0
- package/dist/transactions/commitPayload.d.ts +63 -52
- package/dist/transactions/commitPayload.js +54 -57
- package/dist/transactions/deltaConfirmation.d.ts +20 -22
- package/dist/transactions/deltaConfirmation.js +37 -45
- package/dist/transactions/httpCommitEnvelope.d.ts +43 -0
- package/dist/transactions/httpCommitEnvelope.js +179 -0
- package/dist/transactions/optimisticApply.d.ts +49 -0
- package/dist/transactions/optimisticApply.js +65 -0
- package/dist/transactions/replayValidation.d.ts +182 -0
- package/dist/transactions/replayValidation.js +156 -0
- 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/wire/bootstrapReason.d.ts +9 -0
- package/dist/wire/bootstrapReason.js +8 -0
- 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 +315 -86
- package/dist/wire/frames.js +47 -33
- package/dist/wire/index.d.ts +18 -14
- package/dist/wire/index.js +32 -27
- 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/api.md +10 -10
- package/docs/coordination.md +59 -0
- package/docs/mcp.md +1 -1
- package/package.json +17 -11
- 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/transactions/persistedReplay.d.ts +0 -93
- package/dist/transactions/persistedReplay.js +0 -105
- package/dist/utils/mobx-setup.d.ts +0 -42
package/dist/client/Ablo.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Ablo —
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* `Ablo` — the one-call entry point to the sync engine client. It hides the
|
|
3
|
+
* internal wiring — the object pool, local database, sync client, WebSocket,
|
|
4
|
+
* bootstrap, and offline queue — behind a single function that returns a typed
|
|
5
|
+
* client with one property per model in your schema.
|
|
6
6
|
*
|
|
7
7
|
* Usage:
|
|
8
8
|
* import { Ablo } from '@abloatai/ablo';
|
|
@@ -18,13 +18,14 @@
|
|
|
18
18
|
* });
|
|
19
19
|
* await sync.reports.delete({ id: reportId });
|
|
20
20
|
*/
|
|
21
|
+
import { durableCommitOperationSchema, } from '../transactions/commitEnvelope.js';
|
|
21
22
|
import { AbloAuthenticationError, AbloConnectionError, AbloValidationError, AbloNotFoundError, translateHttpError, toAbloError, claimedError } from '../errors.js';
|
|
22
23
|
import { descriptionFromMeta } from '../coordination/schema.js';
|
|
23
24
|
import { initSyncEngine } from '../context.js';
|
|
24
25
|
import { noopObservability, browserOnlineStatus, defaultSessionErrorDetector, noopAnalytics, } from '../SyncEngineContext.js';
|
|
25
26
|
import { alwaysOnline } from '../adapters/alwaysOnline.js';
|
|
26
27
|
import { validateAbloOptions } from './validateAbloOptions.js';
|
|
27
|
-
import {
|
|
28
|
+
import { InstanceCache } from '../InstanceCache.js';
|
|
28
29
|
import {} from '../auth/index.js';
|
|
29
30
|
import { mintSession } from './sessionMint.js';
|
|
30
31
|
import { createAuthCredentialSource } from '../auth/credentialSource.js';
|
|
@@ -55,17 +56,18 @@ import { assertWriteOptions } from './writeOptionsSchema.js';
|
|
|
55
56
|
// that read it. Re-exported there for use elsewhere in the file.
|
|
56
57
|
// ── Auth normalization ─────────────────────────────────────────────────────
|
|
57
58
|
/**
|
|
58
|
-
* The
|
|
59
|
-
* or `null` when auth is static
|
|
60
|
-
* refresh
|
|
59
|
+
* The single resolver the credential lifecycle needs: an async
|
|
60
|
+
* `() => token | null`, or `null` when auth is static — a plain long-lived
|
|
61
|
+
* `apiKey` string with no refresh, which is the common case.
|
|
61
62
|
*
|
|
62
|
-
* The short-lived per-user browser path passes a
|
|
63
|
-
*
|
|
64
|
-
* mint-before-connect, the proactive refresh timer
|
|
65
|
-
* and the reactive `credential_stale` re-mint. The
|
|
66
|
-
* `ApiKeySetter` contract end
|
|
67
|
-
* login is gone (terminal
|
|
68
|
-
* transient failure (
|
|
63
|
+
* The short-lived per-user browser path passes a function `apiKey` (an
|
|
64
|
+
* {@link ApiKeySetter}), and the SDK then drives the whole credential lifecycle
|
|
65
|
+
* from it: mint-before-connect, the proactive refresh timer with its
|
|
66
|
+
* wake/online/focus re-mint, and the reactive `credential_stale` re-mint. The
|
|
67
|
+
* resolver follows the `ApiKeySetter` contract end to end: resolve a token,
|
|
68
|
+
* resolve `null` when the login is gone (terminal — surfaces `session_expired`
|
|
69
|
+
* and signs the user out), or throw on a transient failure (backs off, without
|
|
70
|
+
* signing out).
|
|
69
71
|
*/
|
|
70
72
|
function resolveCredentialResolver(apiKey) {
|
|
71
73
|
if (typeof apiKey === 'function')
|
|
@@ -86,10 +88,10 @@ export function Ablo(options) {
|
|
|
86
88
|
const authInput = { options, env };
|
|
87
89
|
const configuredApiKey = resolveApiKey(authInput);
|
|
88
90
|
const configuredAuthToken = resolveAuthToken(authInput);
|
|
89
|
-
// The client
|
|
90
|
-
// drives both the reactive re-mint (
|
|
91
|
-
// refresh timer
|
|
92
|
-
// `apiKey` path
|
|
91
|
+
// The client owns its credential lifecycle (not the React layer): this resolver
|
|
92
|
+
// drives both the reactive re-mint (the connection's `credential_stale` state)
|
|
93
|
+
// and the proactive refresh timer with its wake/online/focus triggers. Null for
|
|
94
|
+
// the common static `apiKey` path, which needs no refresh.
|
|
93
95
|
const credentialResolver = resolveCredentialResolver(configuredApiKey);
|
|
94
96
|
const authCredentials = createAuthCredentialSource(
|
|
95
97
|
// eslint-disable-next-line @typescript-eslint/no-deprecated -- load-bearing on the self-hosted path; server-internal cap-mint (Phase 3) not shipped
|
|
@@ -118,24 +120,19 @@ export function Ablo(options) {
|
|
|
118
120
|
...deriveConfigFromSchema(schema),
|
|
119
121
|
...internalOptions.configOverrides,
|
|
120
122
|
};
|
|
121
|
-
// 2. Create the mutation executor
|
|
123
|
+
// 2. Create the mutation executor and dispatcher.
|
|
122
124
|
//
|
|
123
|
-
// The default executor sends `{ type: 'commit', ... }` over the
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
//
|
|
125
|
+
// The default executor sends `{ type: 'commit', ... }` over the engine's
|
|
126
|
+
// WebSocket. The socket doesn't exist yet at this point (it's created later
|
|
127
|
+
// when the store initializes), so the default takes a lazy getter that
|
|
128
|
+
// resolves the live socket at commit time. `storeHolder` is captured by the
|
|
129
|
+
// closure and assigned below once the store is built — JS closures close
|
|
130
|
+
// over bindings, not values, so by the time the first commit fires the store
|
|
131
|
+
// is live.
|
|
130
132
|
//
|
|
131
|
-
// Caller-supplied executors are still honored for advanced cases
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
// never need to override transport. See Zero's `ClientOptions`
|
|
135
|
-
// (packages/zero-client/src/client/options.ts) and Liveblocks'
|
|
136
|
-
// `ClientOptions` (packages/liveblocks-core/src/client.ts) for the
|
|
137
|
-
// reference shape: URLs + auth + declarative mutators, never a
|
|
138
|
-
// pluggable commit transport.
|
|
133
|
+
// Caller-supplied executors are still honored for advanced cases (test
|
|
134
|
+
// mocks, alternative transports), but apps should almost never need to
|
|
135
|
+
// override the transport.
|
|
139
136
|
// Captured-by-reference binding — assigned below after BaseSyncedStore
|
|
140
137
|
// is constructed. The default executor's `getWs` closure reads it
|
|
141
138
|
// lazily at commit time.
|
|
@@ -195,18 +192,16 @@ export function Ablo(options) {
|
|
|
195
192
|
});
|
|
196
193
|
// Hand the credential lifecycle to the client (refresher + proactive refresh
|
|
197
194
|
// timer + wake/online/focus re-mint). Installed once here so refresh works for
|
|
198
|
-
//
|
|
195
|
+
// any consumer of `Ablo({ auth })`, not only those who render `<AbloProvider>`.
|
|
199
196
|
// The first mint happens in `ready()` so the first connection carries a token.
|
|
200
197
|
//
|
|
201
|
-
// Long-lived server clients also get the pre-roll
|
|
202
|
-
// (`proactiveInNode`): their socket must renew its `rk_
|
|
203
|
-
//
|
|
204
|
-
// qualify — agent
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
//
|
|
208
|
-
// client, kind or no kind). User-kind clients in Node (an SSR/RSC module
|
|
209
|
-
// eval of scaffolded browser code) stay reactive-only.
|
|
198
|
+
// Long-lived server clients also get the pre-roll timer on windowless hosts
|
|
199
|
+
// (`proactiveInNode`): their socket must renew its `rk_` or `ek_` before the
|
|
200
|
+
// server's keepalive reaper closes it (4001 `credential_expired`). Two signals
|
|
201
|
+
// qualify — an agent or system participant, and an absolute endpoint-string
|
|
202
|
+
// `apiKey` (a relative one can't be fetched in Node, so an absolute URL is
|
|
203
|
+
// unambiguously a deliberate server client). User-kind clients in Node (an
|
|
204
|
+
// SSR/RSC module evaluating scaffolded browser code) stay reactive-only.
|
|
210
205
|
if (credentialResolver) {
|
|
211
206
|
const rawEndpoint = internalOptions.authEndpoint ?? internalOptions.apiKey;
|
|
212
207
|
const absoluteEndpoint = typeof rawEndpoint === 'string' && /^https?:\/\//i.test(rawEndpoint);
|
|
@@ -218,27 +213,25 @@ export function Ablo(options) {
|
|
|
218
213
|
/* eslint-enable @typescript-eslint/no-deprecated */
|
|
219
214
|
});
|
|
220
215
|
}
|
|
221
|
-
// Put the lazy-query lane on the
|
|
216
|
+
// Put the lazy-query lane on the same auth-recovery path as the WebSocket probe
|
|
222
217
|
// and the proactive pre-roll: a 401 on `/sync/query` re-mints via the store's
|
|
223
|
-
// single-flight lifecycle and replays once, instead of silently returning
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
// coordinator is constructed before the store exists.
|
|
218
|
+
// single-flight lifecycle and replays once, instead of silently returning empty
|
|
219
|
+
// rows against an expired `ek_` until the next proactive tick. Late-bound
|
|
220
|
+
// because the coordinator is constructed before the store exists.
|
|
227
221
|
hydration.setCredentialRecovery((recovery) => store.recoverFromAuthRejection(recovery));
|
|
228
222
|
// Wire the store back into the default executor's lazy getter (see
|
|
229
223
|
// `storeHolder` above). The executor was constructed before the store
|
|
230
224
|
// existed; this late binding closes the loop so commits dispatch over
|
|
231
225
|
// the engine's WebSocket once it opens.
|
|
232
226
|
storeHolder.store = store;
|
|
233
|
-
// Bind
|
|
234
|
-
//
|
|
235
|
-
// `
|
|
236
|
-
//
|
|
237
|
-
//
|
|
238
|
-
//
|
|
239
|
-
//
|
|
240
|
-
//
|
|
241
|
-
// `ws_not_ready` forever (terminal AgentJob writes hang on retry).
|
|
227
|
+
// Bind this executor to this client's TransactionQueue. Without it, the queue
|
|
228
|
+
// resolves `mutationExecutor` from the module-level `getContext()`, which
|
|
229
|
+
// `initSyncEngine()` overwrites on every client construction. In multi-client
|
|
230
|
+
// flows (for example a worker plus a per-job peer) the second `initSyncEngine()`
|
|
231
|
+
// call would silently redirect the first client's queue through the second
|
|
232
|
+
// client's executor closure — and when the second client disposes, its
|
|
233
|
+
// `storeHolder.store` becomes null, so the first client's commits start throwing
|
|
234
|
+
// `ws_not_ready` forever.
|
|
242
235
|
syncClient.getTransactionQueue().setMutationExecutor(executor);
|
|
243
236
|
// Presence + claim streams — built eagerly so `engine.presence`
|
|
244
237
|
// and `engine.claims` return the same reference for the engine's
|
|
@@ -317,13 +310,13 @@ export function Ablo(options) {
|
|
|
317
310
|
}
|
|
318
311
|
_readyPromise = (async () => {
|
|
319
312
|
try {
|
|
320
|
-
// Mint the
|
|
321
|
-
// WebSocket upgrade
|
|
322
|
-
// connect that has to self-heal). Only when a refreshing resolver is
|
|
323
|
-
//
|
|
324
|
-
//
|
|
325
|
-
// app shows sign-in); a
|
|
326
|
-
// and the lifecycle's online/wake triggers retry).
|
|
313
|
+
// Mint the first access credential before we connect, so the initial
|
|
314
|
+
// WebSocket upgrade and bootstrap carry a valid bearer (no tokenless first
|
|
315
|
+
// connect that has to self-heal). Only when a refreshing resolver is wired
|
|
316
|
+
// and no static credential is already present. Follows the `apiKey`
|
|
317
|
+
// resolver contract: `null` means the login is gone (terminal — fail ready
|
|
318
|
+
// so the app shows sign-in); a throw means transient (rethrown; autoStart
|
|
319
|
+
// swallows it and the lifecycle's online/wake triggers retry).
|
|
327
320
|
if (credentialResolver && !authCredentials.getAuthToken()) {
|
|
328
321
|
const token = await credentialResolver();
|
|
329
322
|
if (!token) {
|
|
@@ -331,7 +324,7 @@ export function Ablo(options) {
|
|
|
331
324
|
}
|
|
332
325
|
authCredentials.setAuthToken(token);
|
|
333
326
|
}
|
|
334
|
-
// Register the caller's own database for write-back
|
|
327
|
+
// Register the caller's own database for write-back before bootstrap, so
|
|
335
328
|
// the server resolves this org's data plane to the customer's DB rather
|
|
336
329
|
// than serving an empty/wrong store. The org is derived server-side from
|
|
337
330
|
// the API key. Idempotent server-side (register-or-update). Skipped when
|
|
@@ -353,15 +346,15 @@ export function Ablo(options) {
|
|
|
353
346
|
url,
|
|
354
347
|
kind,
|
|
355
348
|
configuredApiKey,
|
|
356
|
-
// Resolve identity against the
|
|
357
|
-
// `configuredAuthToken`. Consumers using a function `apiKey`
|
|
358
|
-
//
|
|
359
|
-
//
|
|
360
|
-
//
|
|
361
|
-
//
|
|
362
|
-
//
|
|
363
|
-
//
|
|
364
|
-
//
|
|
349
|
+
// Resolve identity against the live token, not the construction-time
|
|
350
|
+
// `configuredAuthToken`. Consumers using a function `apiKey` never pass
|
|
351
|
+
// `authToken` at construction — the lifecycle mints the first `ek_` or
|
|
352
|
+
// `rk_` and calls `setAuthToken()` before `ready()`, which updates the
|
|
353
|
+
// shared credential source. Reading the frozen `configuredAuthToken`
|
|
354
|
+
// here made `/auth/identity` fire with no bearer (returning
|
|
355
|
+
// `no_matching_provider` / `session_expired`) even though the token was
|
|
356
|
+
// present. This reads the shared credential source, like every other
|
|
357
|
+
// transport.
|
|
365
358
|
configuredAuthToken: authCredentials.getAuthToken() ?? configuredAuthToken,
|
|
366
359
|
bootstrapHelper,
|
|
367
360
|
auth: authCredentials,
|
|
@@ -369,11 +362,11 @@ export function Ablo(options) {
|
|
|
369
362
|
});
|
|
370
363
|
const { userId, accountScope, teamIds, capabilityToken, syncGroups, participantKind, } = resolved;
|
|
371
364
|
// Fail-loud guard: detect the degenerate "no real sync groups
|
|
372
|
-
// resolved" state before opening the
|
|
373
|
-
//
|
|
365
|
+
// resolved" state before opening the socket. It is the same class of bug as
|
|
366
|
+
// a
|
|
374
367
|
// sensible-looking default that's functionally broken: the
|
|
375
368
|
// SDK ends up subscribing only to the server-side
|
|
376
|
-
// `['default']` fallback
|
|
369
|
+
// `['default']` fallback, no
|
|
377
370
|
// delta has that tag, live fan-out silently never delivers.
|
|
378
371
|
// For human users (kind:'user') this is almost certainly a
|
|
379
372
|
// misconfiguration upstream — either the caller didn't pass
|
|
@@ -385,9 +378,9 @@ export function Ablo(options) {
|
|
|
385
378
|
if (participantKind === 'user' &&
|
|
386
379
|
(resolvedSyncGroups.length === 0 ||
|
|
387
380
|
(resolvedSyncGroups.length === 1 && resolvedSyncGroups[0] === 'default'))) {
|
|
388
|
-
// Actionable and
|
|
389
|
-
// kept at warn
|
|
390
|
-
//
|
|
381
|
+
// Actionable and not self-healing (no live updates until fixed):
|
|
382
|
+
// kept at warn level for consumers; the low-level diagnostic
|
|
383
|
+
// fields ride the debug log below.
|
|
391
384
|
logger.warn('This client was started without sync groups, so it will not receive ' +
|
|
392
385
|
'live updates. Pass `syncGroups` (for example ' +
|
|
393
386
|
'`["org:<id>", "user:<id>"]`) or check that your auth provider supplies them.');
|
|
@@ -459,15 +452,15 @@ export function Ablo(options) {
|
|
|
459
452
|
httpStatus: error.httpStatus,
|
|
460
453
|
error: error.message,
|
|
461
454
|
});
|
|
462
|
-
// Clear the memo so a
|
|
463
|
-
// replaying this rejection forever. Bootstrap failures here are
|
|
464
|
-
//
|
|
465
|
-
//
|
|
466
|
-
//
|
|
467
|
-
//
|
|
468
|
-
//
|
|
469
|
-
//
|
|
470
|
-
//
|
|
455
|
+
// Clear the memo so a future `ready()` re-attempts bootstrap instead of
|
|
456
|
+
// replaying this rejection forever. Bootstrap failures here are transient
|
|
457
|
+
// by nature — offline, an IndexedDB open timeout, a bootstrap fetch
|
|
458
|
+
// hiccup — and the early `if (_readyPromise) return _readyPromise` guard
|
|
459
|
+
// would otherwise hand every later caller this same dead promise, bricking
|
|
460
|
+
// the engine until a full page reload. Nulling it lets the provider's
|
|
461
|
+
// online/wake/retry triggers drive a clean re-bootstrap. (The terminal
|
|
462
|
+
// `_validationError` branch above intentionally stays cached — config
|
|
463
|
+
// can't change without recreating the engine.)
|
|
471
464
|
_readyPromise = null;
|
|
472
465
|
throw error;
|
|
473
466
|
}
|
|
@@ -531,7 +524,7 @@ export function Ablo(options) {
|
|
|
531
524
|
}
|
|
532
525
|
const type = op.action.toUpperCase();
|
|
533
526
|
const id = op.id ?? op.target?.id ?? '';
|
|
534
|
-
return {
|
|
527
|
+
return durableCommitOperationSchema.parse({
|
|
535
528
|
type,
|
|
536
529
|
model: model.toLowerCase(),
|
|
537
530
|
id,
|
|
@@ -539,7 +532,7 @@ export function Ablo(options) {
|
|
|
539
532
|
transactionId: op.transactionId ?? undefined,
|
|
540
533
|
readAt: op.readAt ?? defaults.readAt ?? undefined,
|
|
541
534
|
onStale: op.onStale ?? defaults.onStale ?? undefined,
|
|
542
|
-
};
|
|
535
|
+
});
|
|
543
536
|
}
|
|
544
537
|
function normalizeCommitOperations(commitOptions) {
|
|
545
538
|
if (commitOptions.operation && commitOptions.operations) {
|
|
@@ -574,27 +567,6 @@ export function Ablo(options) {
|
|
|
574
567
|
},
|
|
575
568
|
};
|
|
576
569
|
}
|
|
577
|
-
function modelClaimFromQueued(claim) {
|
|
578
|
-
return {
|
|
579
|
-
id: claim.id,
|
|
580
|
-
actor: claim.heldBy ?? "",
|
|
581
|
-
participantKind: claim.participantKind ?? "user",
|
|
582
|
-
reason: claim.reason,
|
|
583
|
-
...(claim.description ? { description: claim.description } : {}),
|
|
584
|
-
field: claim.target.field,
|
|
585
|
-
status: 'queued',
|
|
586
|
-
position: claim.position,
|
|
587
|
-
expiresAt: claim.expiresAt ?? 0,
|
|
588
|
-
target: {
|
|
589
|
-
model: claim.target.type,
|
|
590
|
-
id: claim.target.id,
|
|
591
|
-
path: claim.target.path,
|
|
592
|
-
range: claim.target.range,
|
|
593
|
-
field: claim.target.field,
|
|
594
|
-
meta: claim.target.meta,
|
|
595
|
-
},
|
|
596
|
-
};
|
|
597
|
-
}
|
|
598
570
|
function targetMatchesModel(target, claim) {
|
|
599
571
|
if (target.model &&
|
|
600
572
|
claim.target.type.toLowerCase() !== target.model.toLowerCase()) {
|
|
@@ -611,14 +583,6 @@ export function Ablo(options) {
|
|
|
611
583
|
.filter((claim) => (target ? targetMatchesModel(target, claim) : true))
|
|
612
584
|
.map(modelClaimFromActive);
|
|
613
585
|
}
|
|
614
|
-
function listModelClaimQueue(target) {
|
|
615
|
-
if (!target?.model || !target.id)
|
|
616
|
-
return [];
|
|
617
|
-
return publicClaims
|
|
618
|
-
.queueFor({ type: target.model, id: target.id })
|
|
619
|
-
.filter((claim) => (target.field ? claim.target.field === target.field : true))
|
|
620
|
-
.map(modelClaimFromQueued);
|
|
621
|
-
}
|
|
622
586
|
function waitForModelUnclaimed(target, options) {
|
|
623
587
|
if (listModelClaims(target).length === 0)
|
|
624
588
|
return Promise.resolve();
|
|
@@ -690,6 +654,10 @@ export function Ablo(options) {
|
|
|
690
654
|
waited,
|
|
691
655
|
release,
|
|
692
656
|
revoke: claim.revoke,
|
|
657
|
+
// The lease-control members are forwarded explicitly — this wrapper
|
|
658
|
+
// rebuilds the handle field by field, so anything not named here is
|
|
659
|
+
// silently dropped from the public claim.
|
|
660
|
+
heartbeat: claim.heartbeat,
|
|
693
661
|
[Symbol.asyncDispose]: release,
|
|
694
662
|
};
|
|
695
663
|
}
|
|
@@ -752,7 +720,7 @@ export function Ablo(options) {
|
|
|
752
720
|
createSnapshot: (modelKey, id) => createSnapshot({
|
|
753
721
|
pool: objectPool,
|
|
754
722
|
transport: store.getSyncWebSocket(),
|
|
755
|
-
// `position.readFloor` is
|
|
723
|
+
// `position.readFloor` is the value claims and snapshots stamp as
|
|
756
724
|
// `readAt` (max of the pool-applied cursor and the acked
|
|
757
725
|
// watermark for our own writes — see sync/syncPosition.ts).
|
|
758
726
|
// Stamping a bare stream cursor made a claim taken right after
|
|
@@ -798,19 +766,18 @@ export function Ablo(options) {
|
|
|
798
766
|
selfParticipantKind: kind,
|
|
799
767
|
// Read-interest / write-intent enrolment for the typed surface.
|
|
800
768
|
// `enterScope`/`pinScope` resolve the `{ [schemaKey]: id }` scope
|
|
801
|
-
// through the
|
|
769
|
+
// through the same resolver the claim path uses, landing this client in
|
|
802
770
|
// the entity-scoped group the holder's claim presence fans out on.
|
|
803
|
-
//
|
|
804
|
-
//
|
|
771
|
+
// Returns the store promise so the claim write path can await pinScope
|
|
772
|
+
// before acquiring the lease (closing the subscribe-vs-broadcast race);
|
|
805
773
|
// read-interest callers (`retrieve`/`claim.state`) still `void` it and
|
|
806
|
-
// stay fire-and-forget.
|
|
807
|
-
// errors so read interest never makes a read reject or stall.
|
|
774
|
+
// stay fire-and-forget. It's soft either way — the store swallows
|
|
775
|
+
// reconcile errors so read interest never makes a read reject or stall.
|
|
808
776
|
enterScope: (scope) => store.enterScope(scope),
|
|
809
777
|
pinScope: (scope) => store.pinScope(scope),
|
|
810
|
-
// `ablo.<model>.watch(ids, { ttl })`
|
|
811
|
-
// this model's sync group(s).
|
|
812
|
-
// `
|
|
813
|
-
// only — `join` throws AbloConnectionError if the socket isn't ready.
|
|
778
|
+
// `ablo.<model>.watch(ids, { ttl })` performs a scoped participant join
|
|
779
|
+
// on this model's sync group(s). WebSocket only — `join` throws
|
|
780
|
+
// `AbloConnectionError` if the socket isn't ready.
|
|
814
781
|
createWatch: (modelKey, ids, options) => participantManager.join({
|
|
815
782
|
scope: { [modelKey]: ids },
|
|
816
783
|
...(options?.ttl !== undefined ? { ttlSeconds: options.ttl } : {}),
|
|
@@ -851,7 +818,7 @@ export function Ablo(options) {
|
|
|
851
818
|
// SyncClient we already hold from createInternalComponents —
|
|
852
819
|
// no need to leak an accessor through BaseSyncedStore.
|
|
853
820
|
const queue = syncClient.getTransactionQueue();
|
|
854
|
-
queue.enqueueCommit(clientTxId, operations, {
|
|
821
|
+
await queue.enqueueCommit(clientTxId, operations, {
|
|
855
822
|
...(commitOptions.reads ? { reads: [...commitOptions.reads] } : {}),
|
|
856
823
|
});
|
|
857
824
|
if (wait === 'queued') {
|
|
@@ -960,7 +927,7 @@ export function Ablo(options) {
|
|
|
960
927
|
const id = params.id ?? createModelId();
|
|
961
928
|
await applyClaimedPolicy({ model: name, id }, params);
|
|
962
929
|
// Confirm, then return the authoritative row (with framework defaults;
|
|
963
|
-
// the
|
|
930
|
+
// the existing row on an idempotent re-create) — mirrors the WebSocket client.
|
|
964
931
|
await commits.create({
|
|
965
932
|
claimRef: params.claimRef,
|
|
966
933
|
idempotencyKey: params.idempotencyKey,
|
|
@@ -1005,13 +972,13 @@ export function Ablo(options) {
|
|
|
1005
972
|
};
|
|
1006
973
|
}
|
|
1007
974
|
/**
|
|
1008
|
-
* The
|
|
975
|
+
* The control-plane credential: always the original configured secret key.
|
|
1009
976
|
* Never reads `authCredentials` — that holds the exchanged sync credential
|
|
1010
977
|
* (a wide-scope `rk_` on the hosted path), which control-plane routes
|
|
1011
978
|
* rightly refuse (e.g. the user-session mint is sk_-gated). Counterpart to
|
|
1012
979
|
* `getAuthToken()`, which resolves the sync-plane token.
|
|
1013
980
|
*
|
|
1014
|
-
* The
|
|
981
|
+
* The secret-key-only rule is enforced on the server; the credential-kind taxonomy
|
|
1015
982
|
* (secret/restricted/ephemeral/publishable) lives in `auth/credentialPolicy`.
|
|
1016
983
|
*/
|
|
1017
984
|
async function controlPlaneApiKey() {
|
|
@@ -1019,7 +986,7 @@ export function Ablo(options) {
|
|
|
1019
986
|
}
|
|
1020
987
|
/**
|
|
1021
988
|
* Resolve the control-plane context a session/agent mint needs (sk_ +
|
|
1022
|
-
* bootstrap base URL + the schema-key→typename map the
|
|
989
|
+
* bootstrap base URL + the schema-key→typename map the server gates on).
|
|
1023
990
|
* Shared by `sessions.create` and `agents.create` so the two mint doors
|
|
1024
991
|
* can never drift on how a token is minted. Throws if no `sk_` is present —
|
|
1025
992
|
* minting is a backend-only operation.
|
|
@@ -1036,7 +1003,7 @@ export function Ablo(options) {
|
|
|
1036
1003
|
bootstrapBaseUrl: internalOptions.bootstrapBaseUrl,
|
|
1037
1004
|
}),
|
|
1038
1005
|
...(internalOptions.fetch ? { fetch: internalOptions.fetch } : {}),
|
|
1039
|
-
// Map every `can` schema-key to the wire typename the
|
|
1006
|
+
// Map every `can` schema-key to the wire typename the server gates on, so a
|
|
1040
1007
|
// typename override (`documents` → `Document`) doesn't mint a capability
|
|
1041
1008
|
// the server then denies. See `MintSessionContext`.
|
|
1042
1009
|
modelTypenames: Object.fromEntries(Object.entries(schema.models).map(([key, def]) => [
|
|
@@ -1064,9 +1031,9 @@ export function Ablo(options) {
|
|
|
1064
1031
|
// The live short-lived bearer (set via `setAuthToken` / `apiKey`-resolver refresh)
|
|
1065
1032
|
// is the canonical credential; fall back to a configured API key.
|
|
1066
1033
|
//
|
|
1067
|
-
// This is the
|
|
1034
|
+
// This is the sync-plane token (bootstrap, WebSocket, query HTTP). Control-plane
|
|
1068
1035
|
// calls (sessions.create, datasource registration) never use it — they
|
|
1069
|
-
// present the
|
|
1036
|
+
// present the original secret key via `controlPlaneApiKey()` below. The
|
|
1070
1037
|
// split matters: after the startup exchange this resolver returns the
|
|
1071
1038
|
// derived wide-scope `rk_`, a credential the control-plane routes
|
|
1072
1039
|
// correctly refuse (an agent token must never mint humans).
|
|
@@ -1078,9 +1045,8 @@ export function Ablo(options) {
|
|
|
1078
1045
|
setCredentialRefresher(refresher) {
|
|
1079
1046
|
store.setCredentialRefresher(refresher);
|
|
1080
1047
|
},
|
|
1081
|
-
// The org this client resolved to — null until `ready()` completes.
|
|
1082
|
-
//
|
|
1083
|
-
// agent regex-scraped `ablo status` output); now it's a property.
|
|
1048
|
+
// The org this client resolved to — null until `ready()` completes. Exposed
|
|
1049
|
+
// as a property so integrators can read it programmatically.
|
|
1084
1050
|
get organizationId() {
|
|
1085
1051
|
return _resolvedOrganizationId;
|
|
1086
1052
|
},
|
|
@@ -1088,33 +1054,33 @@ export function Ablo(options) {
|
|
|
1088
1054
|
store.nudgeReconnect();
|
|
1089
1055
|
},
|
|
1090
1056
|
sessions: {
|
|
1091
|
-
//
|
|
1092
|
-
//
|
|
1057
|
+
// A backend (holding `sk_`) mints a short-lived scoped token for one end
|
|
1058
|
+
// user or one agent.
|
|
1093
1059
|
//
|
|
1094
|
-
//
|
|
1095
|
-
//
|
|
1096
|
-
//
|
|
1097
|
-
//
|
|
1098
|
-
//
|
|
1060
|
+
// Both arms authenticate with the original secret key
|
|
1061
|
+
// (`controlPlaneApiKey()`), never the wide-scope `rk_` the startup exchange
|
|
1062
|
+
// installed as the sync credential. A derived agent credential silently
|
|
1063
|
+
// replacing the secret key on control-plane calls is how humans would get
|
|
1064
|
+
// minted as agents — and correct attribution is the point.
|
|
1099
1065
|
async create(params) {
|
|
1100
|
-
// Both mint
|
|
1066
|
+
// Both mint paths (`{ user }` → /auth/ephemeral-keys → `ek_`,
|
|
1101
1067
|
// `{ agent, can }` → /auth/capability → scoped `rk_`) resolve their
|
|
1102
1068
|
// control-plane context through the shared `buildMintContext`, so this
|
|
1103
|
-
// client, `agents.create`, and the stateless HTTP client can
|
|
1104
|
-
//
|
|
1069
|
+
// client, `agents.create`, and the stateless HTTP client can't drift on
|
|
1070
|
+
// how a token is minted.
|
|
1105
1071
|
return mintSession(params, await buildMintContext('sessions.create'));
|
|
1106
1072
|
},
|
|
1107
1073
|
},
|
|
1108
|
-
// Mint a scoped agent
|
|
1109
|
-
//
|
|
1110
|
-
// for agents that run in
|
|
1111
|
-
// fresh uuid per call, so concurrent agents are distinct participants
|
|
1112
|
-
// queue behind each other (even when they share a `name`). Humans don't
|
|
1113
|
-
// a server-built client — ship them a token via `sessions.create({ user })`.
|
|
1074
|
+
// Mint a scoped agent identity and hand back a connected client bound to it —
|
|
1075
|
+
// `sessions.create({ agent })` plus `Ablo({ apiKey })` fused into one call,
|
|
1076
|
+
// for agents that run in this (secret-key-holding) process. Omitting `id`
|
|
1077
|
+
// yields a fresh uuid per call, so concurrent agents are distinct participants
|
|
1078
|
+
// that queue behind each other (even when they share a `name`). Humans don't
|
|
1079
|
+
// get a server-built client — ship them a token via `sessions.create({ user })`.
|
|
1114
1080
|
agents: {
|
|
1115
1081
|
async create(params) {
|
|
1116
1082
|
// Distinct participant by default: omit `id` → a fresh uuid, so even two
|
|
1117
|
-
// agents that share a `name` are
|
|
1083
|
+
// agents that share a `name` are independent participants and queue
|
|
1118
1084
|
// behind one another. `name` is display only (→ userMeta.name); it never
|
|
1119
1085
|
// derives the id. Pass an explicit `id` only to re-attach an agent to
|
|
1120
1086
|
// its own held claims.
|
|
@@ -1128,7 +1094,7 @@ export function Ablo(options) {
|
|
|
1128
1094
|
...(userMeta ? { userMeta } : {}),
|
|
1129
1095
|
};
|
|
1130
1096
|
// Re-mint the `rk_` on every resolver call so a long-lived agent client
|
|
1131
|
-
// never hits token expiry; the `sk_` stays in
|
|
1097
|
+
// never hits token expiry; the `sk_` stays in this process — the child
|
|
1132
1098
|
// only ever sees its own short-lived `rk_`.
|
|
1133
1099
|
const mintToken = async () => (await mintSession(sessionParams, await buildMintContext('agents.create')))
|
|
1134
1100
|
.token;
|
|
@@ -1175,7 +1141,7 @@ export function Ablo(options) {
|
|
|
1175
1141
|
* the session (WebSocket close code 1008/4001/4003 or a session_error
|
|
1176
1142
|
* frame). Multiple subscribers supported; returns an unsubscribe
|
|
1177
1143
|
* function. Consumers typically use this to trigger auth-failed UI
|
|
1178
|
-
* flows (e.g., redirect to sign-in). Does
|
|
1144
|
+
* flows (e.g., redirect to sign-in). Does not automatically purge the
|
|
1179
1145
|
* IndexedDB — call `engine.purge()` from the listener if you need
|
|
1180
1146
|
* that behavior (the SDK's `<AbloProvider>` does this by default).
|
|
1181
1147
|
*/
|
|
@@ -1202,7 +1168,7 @@ export function Ablo(options) {
|
|
|
1202
1168
|
// the pool). Prefixed with _ to signal "internal but stable."
|
|
1203
1169
|
/** The BaseSyncedStore — implements SyncStoreContract for SyncContext.Provider. */
|
|
1204
1170
|
get _store() { return store; },
|
|
1205
|
-
/** The
|
|
1171
|
+
/** The InstanceCache — for demand loaders that need pool.createFromData(). */
|
|
1206
1172
|
get _pool() { return objectPool; },
|
|
1207
1173
|
/** The SyncWebSocket — for collaboration events (slide selection, cursors). */
|
|
1208
1174
|
get _ws() { return store.getSyncWebSocket() ?? null; },
|