@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/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';
|
|
@@ -24,7 +24,7 @@ import { initSyncEngine } from '../context.js';
|
|
|
24
24
|
import { noopObservability, browserOnlineStatus, defaultSessionErrorDetector, noopAnalytics, } from '../SyncEngineContext.js';
|
|
25
25
|
import { alwaysOnline } from '../adapters/alwaysOnline.js';
|
|
26
26
|
import { validateAbloOptions } from './validateAbloOptions.js';
|
|
27
|
-
import {
|
|
27
|
+
import { InstanceCache } from '../InstanceCache.js';
|
|
28
28
|
import {} from '../auth/index.js';
|
|
29
29
|
import { mintSession } from './sessionMint.js';
|
|
30
30
|
import { createAuthCredentialSource } from '../auth/credentialSource.js';
|
|
@@ -55,17 +55,18 @@ import { assertWriteOptions } from './writeOptionsSchema.js';
|
|
|
55
55
|
// that read it. Re-exported there for use elsewhere in the file.
|
|
56
56
|
// ── Auth normalization ─────────────────────────────────────────────────────
|
|
57
57
|
/**
|
|
58
|
-
* The
|
|
59
|
-
* or `null` when auth is static
|
|
60
|
-
* refresh
|
|
58
|
+
* The single resolver the credential lifecycle needs: an async
|
|
59
|
+
* `() => token | null`, or `null` when auth is static — a plain long-lived
|
|
60
|
+
* `apiKey` string with no refresh, which is the common case.
|
|
61
61
|
*
|
|
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 (
|
|
62
|
+
* The short-lived per-user browser path passes a function `apiKey` (an
|
|
63
|
+
* {@link ApiKeySetter}), and the SDK then drives the whole credential lifecycle
|
|
64
|
+
* from it: mint-before-connect, the proactive refresh timer with its
|
|
65
|
+
* wake/online/focus re-mint, and the reactive `credential_stale` re-mint. The
|
|
66
|
+
* resolver follows the `ApiKeySetter` contract end to end: resolve a token,
|
|
67
|
+
* resolve `null` when the login is gone (terminal — surfaces `session_expired`
|
|
68
|
+
* and signs the user out), or throw on a transient failure (backs off, without
|
|
69
|
+
* signing out).
|
|
69
70
|
*/
|
|
70
71
|
function resolveCredentialResolver(apiKey) {
|
|
71
72
|
if (typeof apiKey === 'function')
|
|
@@ -86,10 +87,10 @@ export function Ablo(options) {
|
|
|
86
87
|
const authInput = { options, env };
|
|
87
88
|
const configuredApiKey = resolveApiKey(authInput);
|
|
88
89
|
const configuredAuthToken = resolveAuthToken(authInput);
|
|
89
|
-
// The client
|
|
90
|
-
// drives both the reactive re-mint (
|
|
91
|
-
// refresh timer
|
|
92
|
-
// `apiKey` path
|
|
90
|
+
// The client owns its credential lifecycle (not the React layer): this resolver
|
|
91
|
+
// drives both the reactive re-mint (the connection's `credential_stale` state)
|
|
92
|
+
// and the proactive refresh timer with its wake/online/focus triggers. Null for
|
|
93
|
+
// the common static `apiKey` path, which needs no refresh.
|
|
93
94
|
const credentialResolver = resolveCredentialResolver(configuredApiKey);
|
|
94
95
|
const authCredentials = createAuthCredentialSource(
|
|
95
96
|
// 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 +119,19 @@ export function Ablo(options) {
|
|
|
118
119
|
...deriveConfigFromSchema(schema),
|
|
119
120
|
...internalOptions.configOverrides,
|
|
120
121
|
};
|
|
121
|
-
// 2. Create the mutation executor
|
|
122
|
+
// 2. Create the mutation executor and dispatcher.
|
|
122
123
|
//
|
|
123
|
-
// The default executor sends `{ type: 'commit', ... }` over the
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
//
|
|
124
|
+
// The default executor sends `{ type: 'commit', ... }` over the engine's
|
|
125
|
+
// WebSocket. The socket doesn't exist yet at this point (it's created later
|
|
126
|
+
// when the store initializes), so the default takes a lazy getter that
|
|
127
|
+
// resolves the live socket at commit time. `storeHolder` is captured by the
|
|
128
|
+
// closure and assigned below once the store is built — JS closures close
|
|
129
|
+
// over bindings, not values, so by the time the first commit fires the store
|
|
130
|
+
// is live.
|
|
130
131
|
//
|
|
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.
|
|
132
|
+
// Caller-supplied executors are still honored for advanced cases (test
|
|
133
|
+
// mocks, alternative transports), but apps should almost never need to
|
|
134
|
+
// override the transport.
|
|
139
135
|
// Captured-by-reference binding — assigned below after BaseSyncedStore
|
|
140
136
|
// is constructed. The default executor's `getWs` closure reads it
|
|
141
137
|
// lazily at commit time.
|
|
@@ -195,18 +191,16 @@ export function Ablo(options) {
|
|
|
195
191
|
});
|
|
196
192
|
// Hand the credential lifecycle to the client (refresher + proactive refresh
|
|
197
193
|
// timer + wake/online/focus re-mint). Installed once here so refresh works for
|
|
198
|
-
//
|
|
194
|
+
// any consumer of `Ablo({ auth })`, not only those who render `<AbloProvider>`.
|
|
199
195
|
// The first mint happens in `ready()` so the first connection carries a token.
|
|
200
196
|
//
|
|
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.
|
|
197
|
+
// Long-lived server clients also get the pre-roll timer on windowless hosts
|
|
198
|
+
// (`proactiveInNode`): their socket must renew its `rk_` or `ek_` before the
|
|
199
|
+
// server's keepalive reaper closes it (4001 `credential_expired`). Two signals
|
|
200
|
+
// qualify — an agent or system participant, and an absolute endpoint-string
|
|
201
|
+
// `apiKey` (a relative one can't be fetched in Node, so an absolute URL is
|
|
202
|
+
// unambiguously a deliberate server client). User-kind clients in Node (an
|
|
203
|
+
// SSR/RSC module evaluating scaffolded browser code) stay reactive-only.
|
|
210
204
|
if (credentialResolver) {
|
|
211
205
|
const rawEndpoint = internalOptions.authEndpoint ?? internalOptions.apiKey;
|
|
212
206
|
const absoluteEndpoint = typeof rawEndpoint === 'string' && /^https?:\/\//i.test(rawEndpoint);
|
|
@@ -218,27 +212,25 @@ export function Ablo(options) {
|
|
|
218
212
|
/* eslint-enable @typescript-eslint/no-deprecated */
|
|
219
213
|
});
|
|
220
214
|
}
|
|
221
|
-
// Put the lazy-query lane on the
|
|
215
|
+
// Put the lazy-query lane on the same auth-recovery path as the WebSocket probe
|
|
222
216
|
// 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.
|
|
217
|
+
// single-flight lifecycle and replays once, instead of silently returning empty
|
|
218
|
+
// rows against an expired `ek_` until the next proactive tick. Late-bound
|
|
219
|
+
// because the coordinator is constructed before the store exists.
|
|
227
220
|
hydration.setCredentialRecovery((recovery) => store.recoverFromAuthRejection(recovery));
|
|
228
221
|
// Wire the store back into the default executor's lazy getter (see
|
|
229
222
|
// `storeHolder` above). The executor was constructed before the store
|
|
230
223
|
// existed; this late binding closes the loop so commits dispatch over
|
|
231
224
|
// the engine's WebSocket once it opens.
|
|
232
225
|
storeHolder.store = store;
|
|
233
|
-
// Bind
|
|
234
|
-
//
|
|
235
|
-
// `
|
|
236
|
-
//
|
|
237
|
-
//
|
|
238
|
-
//
|
|
239
|
-
//
|
|
240
|
-
//
|
|
241
|
-
// `ws_not_ready` forever (terminal AgentJob writes hang on retry).
|
|
226
|
+
// Bind this executor to this client's TransactionQueue. Without it, the queue
|
|
227
|
+
// resolves `mutationExecutor` from the module-level `getContext()`, which
|
|
228
|
+
// `initSyncEngine()` overwrites on every client construction. In multi-client
|
|
229
|
+
// flows (for example a worker plus a per-job peer) the second `initSyncEngine()`
|
|
230
|
+
// call would silently redirect the first client's queue through the second
|
|
231
|
+
// client's executor closure — and when the second client disposes, its
|
|
232
|
+
// `storeHolder.store` becomes null, so the first client's commits start throwing
|
|
233
|
+
// `ws_not_ready` forever.
|
|
242
234
|
syncClient.getTransactionQueue().setMutationExecutor(executor);
|
|
243
235
|
// Presence + claim streams — built eagerly so `engine.presence`
|
|
244
236
|
// and `engine.claims` return the same reference for the engine's
|
|
@@ -317,13 +309,13 @@ export function Ablo(options) {
|
|
|
317
309
|
}
|
|
318
310
|
_readyPromise = (async () => {
|
|
319
311
|
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).
|
|
312
|
+
// Mint the first access credential before we connect, so the initial
|
|
313
|
+
// WebSocket upgrade and bootstrap carry a valid bearer (no tokenless first
|
|
314
|
+
// connect that has to self-heal). Only when a refreshing resolver is wired
|
|
315
|
+
// and no static credential is already present. Follows the `apiKey`
|
|
316
|
+
// resolver contract: `null` means the login is gone (terminal — fail ready
|
|
317
|
+
// so the app shows sign-in); a throw means transient (rethrown; autoStart
|
|
318
|
+
// swallows it and the lifecycle's online/wake triggers retry).
|
|
327
319
|
if (credentialResolver && !authCredentials.getAuthToken()) {
|
|
328
320
|
const token = await credentialResolver();
|
|
329
321
|
if (!token) {
|
|
@@ -331,7 +323,7 @@ export function Ablo(options) {
|
|
|
331
323
|
}
|
|
332
324
|
authCredentials.setAuthToken(token);
|
|
333
325
|
}
|
|
334
|
-
// Register the caller's own database for write-back
|
|
326
|
+
// Register the caller's own database for write-back before bootstrap, so
|
|
335
327
|
// the server resolves this org's data plane to the customer's DB rather
|
|
336
328
|
// than serving an empty/wrong store. The org is derived server-side from
|
|
337
329
|
// the API key. Idempotent server-side (register-or-update). Skipped when
|
|
@@ -353,15 +345,15 @@ export function Ablo(options) {
|
|
|
353
345
|
url,
|
|
354
346
|
kind,
|
|
355
347
|
configuredApiKey,
|
|
356
|
-
// Resolve identity against the
|
|
357
|
-
// `configuredAuthToken`. Consumers using a function `apiKey`
|
|
358
|
-
//
|
|
359
|
-
//
|
|
360
|
-
//
|
|
361
|
-
//
|
|
362
|
-
//
|
|
363
|
-
//
|
|
364
|
-
//
|
|
348
|
+
// Resolve identity against the live token, not the construction-time
|
|
349
|
+
// `configuredAuthToken`. Consumers using a function `apiKey` never pass
|
|
350
|
+
// `authToken` at construction — the lifecycle mints the first `ek_` or
|
|
351
|
+
// `rk_` and calls `setAuthToken()` before `ready()`, which updates the
|
|
352
|
+
// shared credential source. Reading the frozen `configuredAuthToken`
|
|
353
|
+
// here made `/auth/identity` fire with no bearer (returning
|
|
354
|
+
// `no_matching_provider` / `session_expired`) even though the token was
|
|
355
|
+
// present. This reads the shared credential source, like every other
|
|
356
|
+
// transport.
|
|
365
357
|
configuredAuthToken: authCredentials.getAuthToken() ?? configuredAuthToken,
|
|
366
358
|
bootstrapHelper,
|
|
367
359
|
auth: authCredentials,
|
|
@@ -369,11 +361,11 @@ export function Ablo(options) {
|
|
|
369
361
|
});
|
|
370
362
|
const { userId, accountScope, teamIds, capabilityToken, syncGroups, participantKind, } = resolved;
|
|
371
363
|
// Fail-loud guard: detect the degenerate "no real sync groups
|
|
372
|
-
// resolved" state before opening the
|
|
373
|
-
//
|
|
364
|
+
// resolved" state before opening the socket. It is the same class of bug as
|
|
365
|
+
// a
|
|
374
366
|
// sensible-looking default that's functionally broken: the
|
|
375
367
|
// SDK ends up subscribing only to the server-side
|
|
376
|
-
// `['default']` fallback
|
|
368
|
+
// `['default']` fallback, no
|
|
377
369
|
// delta has that tag, live fan-out silently never delivers.
|
|
378
370
|
// For human users (kind:'user') this is almost certainly a
|
|
379
371
|
// misconfiguration upstream — either the caller didn't pass
|
|
@@ -385,9 +377,9 @@ export function Ablo(options) {
|
|
|
385
377
|
if (participantKind === 'user' &&
|
|
386
378
|
(resolvedSyncGroups.length === 0 ||
|
|
387
379
|
(resolvedSyncGroups.length === 1 && resolvedSyncGroups[0] === 'default'))) {
|
|
388
|
-
// Actionable and
|
|
389
|
-
// kept at warn
|
|
390
|
-
//
|
|
380
|
+
// Actionable and not self-healing (no live updates until fixed):
|
|
381
|
+
// kept at warn level for consumers; the low-level diagnostic
|
|
382
|
+
// fields ride the debug log below.
|
|
391
383
|
logger.warn('This client was started without sync groups, so it will not receive ' +
|
|
392
384
|
'live updates. Pass `syncGroups` (for example ' +
|
|
393
385
|
'`["org:<id>", "user:<id>"]`) or check that your auth provider supplies them.');
|
|
@@ -459,15 +451,15 @@ export function Ablo(options) {
|
|
|
459
451
|
httpStatus: error.httpStatus,
|
|
460
452
|
error: error.message,
|
|
461
453
|
});
|
|
462
|
-
// Clear the memo so a
|
|
463
|
-
// replaying this rejection forever. Bootstrap failures here are
|
|
464
|
-
//
|
|
465
|
-
//
|
|
466
|
-
//
|
|
467
|
-
//
|
|
468
|
-
//
|
|
469
|
-
//
|
|
470
|
-
//
|
|
454
|
+
// Clear the memo so a future `ready()` re-attempts bootstrap instead of
|
|
455
|
+
// replaying this rejection forever. Bootstrap failures here are transient
|
|
456
|
+
// by nature — offline, an IndexedDB open timeout, a bootstrap fetch
|
|
457
|
+
// hiccup — and the early `if (_readyPromise) return _readyPromise` guard
|
|
458
|
+
// would otherwise hand every later caller this same dead promise, bricking
|
|
459
|
+
// the engine until a full page reload. Nulling it lets the provider's
|
|
460
|
+
// online/wake/retry triggers drive a clean re-bootstrap. (The terminal
|
|
461
|
+
// `_validationError` branch above intentionally stays cached — config
|
|
462
|
+
// can't change without recreating the engine.)
|
|
471
463
|
_readyPromise = null;
|
|
472
464
|
throw error;
|
|
473
465
|
}
|
|
@@ -574,27 +566,6 @@ export function Ablo(options) {
|
|
|
574
566
|
},
|
|
575
567
|
};
|
|
576
568
|
}
|
|
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
569
|
function targetMatchesModel(target, claim) {
|
|
599
570
|
if (target.model &&
|
|
600
571
|
claim.target.type.toLowerCase() !== target.model.toLowerCase()) {
|
|
@@ -611,14 +582,6 @@ export function Ablo(options) {
|
|
|
611
582
|
.filter((claim) => (target ? targetMatchesModel(target, claim) : true))
|
|
612
583
|
.map(modelClaimFromActive);
|
|
613
584
|
}
|
|
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
585
|
function waitForModelUnclaimed(target, options) {
|
|
623
586
|
if (listModelClaims(target).length === 0)
|
|
624
587
|
return Promise.resolve();
|
|
@@ -690,6 +653,10 @@ export function Ablo(options) {
|
|
|
690
653
|
waited,
|
|
691
654
|
release,
|
|
692
655
|
revoke: claim.revoke,
|
|
656
|
+
// The lease-control members are forwarded explicitly — this wrapper
|
|
657
|
+
// rebuilds the handle field by field, so anything not named here is
|
|
658
|
+
// silently dropped from the public claim.
|
|
659
|
+
heartbeat: claim.heartbeat,
|
|
693
660
|
[Symbol.asyncDispose]: release,
|
|
694
661
|
};
|
|
695
662
|
}
|
|
@@ -752,7 +719,7 @@ export function Ablo(options) {
|
|
|
752
719
|
createSnapshot: (modelKey, id) => createSnapshot({
|
|
753
720
|
pool: objectPool,
|
|
754
721
|
transport: store.getSyncWebSocket(),
|
|
755
|
-
// `position.readFloor` is
|
|
722
|
+
// `position.readFloor` is the value claims and snapshots stamp as
|
|
756
723
|
// `readAt` (max of the pool-applied cursor and the acked
|
|
757
724
|
// watermark for our own writes — see sync/syncPosition.ts).
|
|
758
725
|
// Stamping a bare stream cursor made a claim taken right after
|
|
@@ -798,19 +765,18 @@ export function Ablo(options) {
|
|
|
798
765
|
selfParticipantKind: kind,
|
|
799
766
|
// Read-interest / write-intent enrolment for the typed surface.
|
|
800
767
|
// `enterScope`/`pinScope` resolve the `{ [schemaKey]: id }` scope
|
|
801
|
-
// through the
|
|
768
|
+
// through the same resolver the claim path uses, landing this client in
|
|
802
769
|
// the entity-scoped group the holder's claim presence fans out on.
|
|
803
|
-
//
|
|
804
|
-
//
|
|
770
|
+
// Returns the store promise so the claim write path can await pinScope
|
|
771
|
+
// before acquiring the lease (closing the subscribe-vs-broadcast race);
|
|
805
772
|
// 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.
|
|
773
|
+
// stay fire-and-forget. It's soft either way — the store swallows
|
|
774
|
+
// reconcile errors so read interest never makes a read reject or stall.
|
|
808
775
|
enterScope: (scope) => store.enterScope(scope),
|
|
809
776
|
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.
|
|
777
|
+
// `ablo.<model>.watch(ids, { ttl })` performs a scoped participant join
|
|
778
|
+
// on this model's sync group(s). WebSocket only — `join` throws
|
|
779
|
+
// `AbloConnectionError` if the socket isn't ready.
|
|
814
780
|
createWatch: (modelKey, ids, options) => participantManager.join({
|
|
815
781
|
scope: { [modelKey]: ids },
|
|
816
782
|
...(options?.ttl !== undefined ? { ttlSeconds: options.ttl } : {}),
|
|
@@ -960,7 +926,7 @@ export function Ablo(options) {
|
|
|
960
926
|
const id = params.id ?? createModelId();
|
|
961
927
|
await applyClaimedPolicy({ model: name, id }, params);
|
|
962
928
|
// Confirm, then return the authoritative row (with framework defaults;
|
|
963
|
-
// the
|
|
929
|
+
// the existing row on an idempotent re-create) — mirrors the WebSocket client.
|
|
964
930
|
await commits.create({
|
|
965
931
|
claimRef: params.claimRef,
|
|
966
932
|
idempotencyKey: params.idempotencyKey,
|
|
@@ -1005,13 +971,13 @@ export function Ablo(options) {
|
|
|
1005
971
|
};
|
|
1006
972
|
}
|
|
1007
973
|
/**
|
|
1008
|
-
* The
|
|
974
|
+
* The control-plane credential: always the original configured secret key.
|
|
1009
975
|
* Never reads `authCredentials` — that holds the exchanged sync credential
|
|
1010
976
|
* (a wide-scope `rk_` on the hosted path), which control-plane routes
|
|
1011
977
|
* rightly refuse (e.g. the user-session mint is sk_-gated). Counterpart to
|
|
1012
978
|
* `getAuthToken()`, which resolves the sync-plane token.
|
|
1013
979
|
*
|
|
1014
|
-
* The
|
|
980
|
+
* The secret-key-only rule is enforced on the server; the credential-kind taxonomy
|
|
1015
981
|
* (secret/restricted/ephemeral/publishable) lives in `auth/credentialPolicy`.
|
|
1016
982
|
*/
|
|
1017
983
|
async function controlPlaneApiKey() {
|
|
@@ -1019,7 +985,7 @@ export function Ablo(options) {
|
|
|
1019
985
|
}
|
|
1020
986
|
/**
|
|
1021
987
|
* Resolve the control-plane context a session/agent mint needs (sk_ +
|
|
1022
|
-
* bootstrap base URL + the schema-key→typename map the
|
|
988
|
+
* bootstrap base URL + the schema-key→typename map the server gates on).
|
|
1023
989
|
* Shared by `sessions.create` and `agents.create` so the two mint doors
|
|
1024
990
|
* can never drift on how a token is minted. Throws if no `sk_` is present —
|
|
1025
991
|
* minting is a backend-only operation.
|
|
@@ -1036,7 +1002,7 @@ export function Ablo(options) {
|
|
|
1036
1002
|
bootstrapBaseUrl: internalOptions.bootstrapBaseUrl,
|
|
1037
1003
|
}),
|
|
1038
1004
|
...(internalOptions.fetch ? { fetch: internalOptions.fetch } : {}),
|
|
1039
|
-
// Map every `can` schema-key to the wire typename the
|
|
1005
|
+
// Map every `can` schema-key to the wire typename the server gates on, so a
|
|
1040
1006
|
// typename override (`documents` → `Document`) doesn't mint a capability
|
|
1041
1007
|
// the server then denies. See `MintSessionContext`.
|
|
1042
1008
|
modelTypenames: Object.fromEntries(Object.entries(schema.models).map(([key, def]) => [
|
|
@@ -1064,9 +1030,9 @@ export function Ablo(options) {
|
|
|
1064
1030
|
// The live short-lived bearer (set via `setAuthToken` / `apiKey`-resolver refresh)
|
|
1065
1031
|
// is the canonical credential; fall back to a configured API key.
|
|
1066
1032
|
//
|
|
1067
|
-
// This is the
|
|
1033
|
+
// This is the sync-plane token (bootstrap, WebSocket, query HTTP). Control-plane
|
|
1068
1034
|
// calls (sessions.create, datasource registration) never use it — they
|
|
1069
|
-
// present the
|
|
1035
|
+
// present the original secret key via `controlPlaneApiKey()` below. The
|
|
1070
1036
|
// split matters: after the startup exchange this resolver returns the
|
|
1071
1037
|
// derived wide-scope `rk_`, a credential the control-plane routes
|
|
1072
1038
|
// correctly refuse (an agent token must never mint humans).
|
|
@@ -1078,9 +1044,8 @@ export function Ablo(options) {
|
|
|
1078
1044
|
setCredentialRefresher(refresher) {
|
|
1079
1045
|
store.setCredentialRefresher(refresher);
|
|
1080
1046
|
},
|
|
1081
|
-
// The org this client resolved to — null until `ready()` completes.
|
|
1082
|
-
//
|
|
1083
|
-
// agent regex-scraped `ablo status` output); now it's a property.
|
|
1047
|
+
// The org this client resolved to — null until `ready()` completes. Exposed
|
|
1048
|
+
// as a property so integrators can read it programmatically.
|
|
1084
1049
|
get organizationId() {
|
|
1085
1050
|
return _resolvedOrganizationId;
|
|
1086
1051
|
},
|
|
@@ -1088,33 +1053,33 @@ export function Ablo(options) {
|
|
|
1088
1053
|
store.nudgeReconnect();
|
|
1089
1054
|
},
|
|
1090
1055
|
sessions: {
|
|
1091
|
-
//
|
|
1092
|
-
//
|
|
1056
|
+
// A backend (holding `sk_`) mints a short-lived scoped token for one end
|
|
1057
|
+
// user or one agent.
|
|
1093
1058
|
//
|
|
1094
|
-
//
|
|
1095
|
-
//
|
|
1096
|
-
//
|
|
1097
|
-
//
|
|
1098
|
-
//
|
|
1059
|
+
// Both arms authenticate with the original secret key
|
|
1060
|
+
// (`controlPlaneApiKey()`), never the wide-scope `rk_` the startup exchange
|
|
1061
|
+
// installed as the sync credential. A derived agent credential silently
|
|
1062
|
+
// replacing the secret key on control-plane calls is how humans would get
|
|
1063
|
+
// minted as agents — and correct attribution is the point.
|
|
1099
1064
|
async create(params) {
|
|
1100
|
-
// Both mint
|
|
1065
|
+
// Both mint paths (`{ user }` → /auth/ephemeral-keys → `ek_`,
|
|
1101
1066
|
// `{ agent, can }` → /auth/capability → scoped `rk_`) resolve their
|
|
1102
1067
|
// control-plane context through the shared `buildMintContext`, so this
|
|
1103
|
-
// client, `agents.create`, and the stateless HTTP client can
|
|
1104
|
-
//
|
|
1068
|
+
// client, `agents.create`, and the stateless HTTP client can't drift on
|
|
1069
|
+
// how a token is minted.
|
|
1105
1070
|
return mintSession(params, await buildMintContext('sessions.create'));
|
|
1106
1071
|
},
|
|
1107
1072
|
},
|
|
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 })`.
|
|
1073
|
+
// Mint a scoped agent identity and hand back a connected client bound to it —
|
|
1074
|
+
// `sessions.create({ agent })` plus `Ablo({ apiKey })` fused into one call,
|
|
1075
|
+
// for agents that run in this (secret-key-holding) process. Omitting `id`
|
|
1076
|
+
// yields a fresh uuid per call, so concurrent agents are distinct participants
|
|
1077
|
+
// that queue behind each other (even when they share a `name`). Humans don't
|
|
1078
|
+
// get a server-built client — ship them a token via `sessions.create({ user })`.
|
|
1114
1079
|
agents: {
|
|
1115
1080
|
async create(params) {
|
|
1116
1081
|
// Distinct participant by default: omit `id` → a fresh uuid, so even two
|
|
1117
|
-
// agents that share a `name` are
|
|
1082
|
+
// agents that share a `name` are independent participants and queue
|
|
1118
1083
|
// behind one another. `name` is display only (→ userMeta.name); it never
|
|
1119
1084
|
// derives the id. Pass an explicit `id` only to re-attach an agent to
|
|
1120
1085
|
// its own held claims.
|
|
@@ -1128,7 +1093,7 @@ export function Ablo(options) {
|
|
|
1128
1093
|
...(userMeta ? { userMeta } : {}),
|
|
1129
1094
|
};
|
|
1130
1095
|
// Re-mint the `rk_` on every resolver call so a long-lived agent client
|
|
1131
|
-
// never hits token expiry; the `sk_` stays in
|
|
1096
|
+
// never hits token expiry; the `sk_` stays in this process — the child
|
|
1132
1097
|
// only ever sees its own short-lived `rk_`.
|
|
1133
1098
|
const mintToken = async () => (await mintSession(sessionParams, await buildMintContext('agents.create')))
|
|
1134
1099
|
.token;
|
|
@@ -1175,7 +1140,7 @@ export function Ablo(options) {
|
|
|
1175
1140
|
* the session (WebSocket close code 1008/4001/4003 or a session_error
|
|
1176
1141
|
* frame). Multiple subscribers supported; returns an unsubscribe
|
|
1177
1142
|
* function. Consumers typically use this to trigger auth-failed UI
|
|
1178
|
-
* flows (e.g., redirect to sign-in). Does
|
|
1143
|
+
* flows (e.g., redirect to sign-in). Does not automatically purge the
|
|
1179
1144
|
* IndexedDB — call `engine.purge()` from the listener if you need
|
|
1180
1145
|
* that behavior (the SDK's `<AbloProvider>` does this by default).
|
|
1181
1146
|
*/
|
|
@@ -1202,7 +1167,7 @@ export function Ablo(options) {
|
|
|
1202
1167
|
// the pool). Prefixed with _ to signal "internal but stable."
|
|
1203
1168
|
/** The BaseSyncedStore — implements SyncStoreContract for SyncContext.Provider. */
|
|
1204
1169
|
get _store() { return store; },
|
|
1205
|
-
/** The
|
|
1170
|
+
/** The InstanceCache — for demand loaders that need pool.createFromData(). */
|
|
1206
1171
|
get _pool() { return objectPool; },
|
|
1207
1172
|
/** The SyncWebSocket — for collaboration events (slide selection, cursors). */
|
|
1208
1173
|
get _ws() { return store.getSyncWebSocket() ?? null; },
|
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* nouns directly to HTTP routes on sync-server.
|
|
2
|
+
* The stateless API client behind `Ablo({ apiKey })`. It carries no schema,
|
|
3
|
+
* object pool, local database, or WebSocket, and maps the public Model, Claim,
|
|
4
|
+
* and Commit nouns directly to HTTP routes on the server. This is the transport
|
|
5
|
+
* used for server-side agents, workers, and serverless code.
|
|
7
6
|
*/
|
|
8
7
|
import type { AbloOptions } from './options.js';
|
|
9
8
|
import type { CommitResource, ClaimCreateOptions, ClaimWaitOptions, ModelClient, ModelClaim, ModelTarget, CreateSessionParams, AbloSession } from './resourceTypes.js';
|
|
10
9
|
import type { SchemaRecord } from '../schema/schema.js';
|
|
11
10
|
import type { Duration } from '../utils/duration.js';
|
|
12
|
-
import type { Claim } from '../types/streams.js';
|
|
11
|
+
import type { Claim, ClaimHeartbeat } from '../types/streams.js';
|
|
13
12
|
import type { SyncObservabilityProvider } from '../interfaces/index.js';
|
|
14
13
|
export type AbloApiClientOptions = Omit<AbloOptions, 'schema'> & {
|
|
15
14
|
readonly schema?: null | undefined;
|
|
16
15
|
readonly bootstrapBaseUrl?: string | undefined;
|
|
17
16
|
/**
|
|
18
|
-
*
|
|
19
|
-
* transport emits the same claim
|
|
20
|
-
* `ClaimLog` works identically for headless
|
|
17
|
+
* The observability provider forwarded from `Ablo({ observability })`. The HTTP
|
|
18
|
+
* transport emits the same claim and conflict events as the WebSocket transport,
|
|
19
|
+
* so a `ClaimLog` works identically for headless server-agent evaluations.
|
|
21
20
|
*/
|
|
22
21
|
readonly observability?: SyncObservabilityProvider;
|
|
23
22
|
/**
|
|
@@ -37,6 +36,18 @@ export interface AbloApiClaims {
|
|
|
37
36
|
create(options: ClaimCreateOptions): Promise<Claim>;
|
|
38
37
|
list(target?: Partial<ModelTarget>): Promise<readonly ModelClaim[]>;
|
|
39
38
|
waitFor(target: Partial<ModelTarget>, options?: ClaimWaitOptions): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* The batched beat — extend every lease this credential holds in one
|
|
41
|
+
* request (`POST /v1/claims/heartbeat`), the stateless twin of the
|
|
42
|
+
* WebSocket keepalive. One round trip per cadence for a worker holding
|
|
43
|
+
* many rows. Returns one {@link ClaimHeartbeat} per extended lease,
|
|
44
|
+
* tagged with its claim id — no separate result type to learn.
|
|
45
|
+
*/
|
|
46
|
+
heartbeatAll(options?: {
|
|
47
|
+
ttl?: Duration;
|
|
48
|
+
}): Promise<readonly (ClaimHeartbeat & {
|
|
49
|
+
readonly claimId: string;
|
|
50
|
+
})[]>;
|
|
40
51
|
}
|
|
41
52
|
export type CapabilityParticipantKind = 'agent' | 'system';
|
|
42
53
|
export interface CapabilityCreateBaseOptions {
|
|
@@ -97,14 +108,14 @@ export interface CapabilityRevocation {
|
|
|
97
108
|
}
|
|
98
109
|
export interface CapabilityRotateOptions {
|
|
99
110
|
/**
|
|
100
|
-
*
|
|
101
|
-
* rotation, so you can deploy the replacement with zero downtime.
|
|
102
|
-
* 24h server
|
|
111
|
+
* The overlap window — the old token keeps authenticating for this long after
|
|
112
|
+
* rotation, so you can deploy the replacement with zero downtime. Defaults to
|
|
113
|
+
* 24h on the server.
|
|
103
114
|
*/
|
|
104
115
|
readonly grace?: Duration;
|
|
105
116
|
readonly graceSeconds?: number;
|
|
106
117
|
/**
|
|
107
|
-
*
|
|
118
|
+
* The lifetime of the replacement capability. Omit to inherit the original's
|
|
108
119
|
* lifetime.
|
|
109
120
|
*/
|
|
110
121
|
readonly lease?: Duration;
|
|
@@ -126,9 +137,9 @@ export interface CapabilityResource {
|
|
|
126
137
|
retrieve(id: string): Promise<CapabilityRecord>;
|
|
127
138
|
revoke(id: string): Promise<CapabilityRevocation>;
|
|
128
139
|
/**
|
|
129
|
-
* Rotate with overlap
|
|
130
|
-
*
|
|
131
|
-
*
|
|
140
|
+
* Rotate with overlap: mint a fresh capability that carries the same scope, and
|
|
141
|
+
* keep the old token working for a grace window so you can roll out the
|
|
142
|
+
* replacement without downtime.
|
|
132
143
|
*/
|
|
133
144
|
rotate(id: string, options?: CapabilityRotateOptions): Promise<RotatedCapability>;
|
|
134
145
|
/**
|
|
@@ -155,9 +166,9 @@ export interface AbloApi {
|
|
|
155
166
|
*/
|
|
156
167
|
getAuthToken(): Promise<string | null>;
|
|
157
168
|
/**
|
|
158
|
-
* Mint a short-lived scoped session
|
|
159
|
-
*
|
|
160
|
-
*
|
|
169
|
+
* Mint a short-lived scoped session. Minting is a control-plane HTTP call (no
|
|
170
|
+
* socket), so it lives on this stateless client too, not only the realtime one.
|
|
171
|
+
* `{ user }` mints an `ek_`; `{ agent, can }` mints an `rk_`.
|
|
161
172
|
*/
|
|
162
173
|
readonly sessions: {
|
|
163
174
|
create(params: CreateSessionParams<SchemaRecord>): Promise<AbloSession>;
|