@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/auth.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Authentication and URL resolution for the `Ablo()` client.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* sequence of named decisions rather than a stream of `??`-chains.
|
|
4
|
+
* Each function here makes one decision: it resolves a configuration value with
|
|
5
|
+
* the right precedence, or fails with an actionable message. Together they let
|
|
6
|
+
* the client constructor read as a sequence of named steps rather than a chain
|
|
7
|
+
* of fallbacks.
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* explicit
|
|
9
|
+
* The environment surface is deliberately small: `ABLO_API_KEY` is the only
|
|
10
|
+
* value read from the environment. Every other routing or authentication
|
|
11
|
+
* override is an explicit option, so an app never picks up hidden behavior from
|
|
12
|
+
* a stray environment variable.
|
|
13
13
|
*/
|
|
14
14
|
import { AbloAuthenticationError, AbloValidationError } from '../errors.js';
|
|
15
15
|
import { classifyCredentialKind } from '../auth/credentialPolicy.js';
|
|
@@ -25,13 +25,12 @@ export function readProcessEnv() {
|
|
|
25
25
|
return maybeGlobal.process?.env ?? {};
|
|
26
26
|
}
|
|
27
27
|
export function resolveApiKey(input) {
|
|
28
|
-
// `authEndpoint`
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
// renewal off it.
|
|
28
|
+
// `authEndpoint` is the option that names a session-mint route: a URL the
|
|
29
|
+
// client exchanges for a short-lived token, or an async resolver for custom
|
|
30
|
+
// exchanges. It is resolved into an `ApiKeySetter` here — the single point
|
|
31
|
+
// shared by every client variant (WebSocket, HTTP, and protocol clients) — so
|
|
32
|
+
// every downstream consumer sees the same resolver and the credential
|
|
33
|
+
// lifecycle drives renewal off it.
|
|
35
34
|
const endpoint = input.options.authEndpoint;
|
|
36
35
|
const configured = input.options.apiKey;
|
|
37
36
|
if (endpoint != null) {
|
|
@@ -48,10 +47,10 @@ export function resolveApiKey(input) {
|
|
|
48
47
|
}
|
|
49
48
|
return createEndpointCredentialResolver(endpoint);
|
|
50
49
|
}
|
|
51
|
-
// `apiKey` also accepts the endpoint-string
|
|
52
|
-
// key strings are `sk_`/`ek_`/`rk_
|
|
53
|
-
//
|
|
54
|
-
// always a literal key, never endpoint
|
|
50
|
+
// `apiKey` also accepts the endpoint-string form directly, detected the same
|
|
51
|
+
// way — key strings are prefixed (`sk_`/`ek_`/`rk_`), so the two shapes never
|
|
52
|
+
// collide. Only the explicit option is treated this way: an `ABLO_API_KEY`
|
|
53
|
+
// environment value is always a literal key, never an endpoint.
|
|
55
54
|
if (typeof configured === 'string' && isCredentialEndpoint(configured)) {
|
|
56
55
|
return createEndpointCredentialResolver(configured);
|
|
57
56
|
}
|
|
@@ -63,7 +62,7 @@ export function resolveAuthToken(input) {
|
|
|
63
62
|
function keyPrefix(key) {
|
|
64
63
|
return `${key.slice(0, 12)}…`;
|
|
65
64
|
}
|
|
66
|
-
/** Infer sandbox
|
|
65
|
+
/** Infer the sandbox or production mode from an Ablo key's prefix. */
|
|
67
66
|
export function modeFromApiKey(key) {
|
|
68
67
|
if (/^(sk|rk)_test_/.test(key))
|
|
69
68
|
return 'sandbox';
|
|
@@ -225,39 +224,38 @@ export function describeCliKeyMismatch(configured, cli) {
|
|
|
225
224
|
return null;
|
|
226
225
|
}
|
|
227
226
|
/**
|
|
228
|
-
*
|
|
227
|
+
* Resolves the Postgres connection string for the direct-connection option, or
|
|
228
|
+
* `null` when none was given.
|
|
229
229
|
*
|
|
230
|
-
* `databaseUrl` is
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
230
|
+
* `databaseUrl` is opt-in: the client registers a dedicated database only when
|
|
231
|
+
* the caller passes it explicitly. It is never read from
|
|
232
|
+
* `process.env.DATABASE_URL`, because this module treats `ABLO_API_KEY` as the
|
|
233
|
+
* one environment fallback — an app's `DATABASE_URL`, commonly set for other
|
|
234
|
+
* tools, must not silently switch the client into connection-string mode. The
|
|
235
|
+
* default path leaves `DATABASE_URL` untouched and reads through `dataSource(...)`
|
|
236
|
+
* instead, so this returns `null`. {@link warnIfDatabaseUrlEnvIgnored} nudges a
|
|
237
|
+
* caller who set the environment variable but omitted the option.
|
|
238
238
|
*/
|
|
239
239
|
export function resolveDatabaseUrl(input) {
|
|
240
240
|
return input.options.databaseUrl ?? null;
|
|
241
241
|
}
|
|
242
242
|
/**
|
|
243
|
-
*
|
|
243
|
+
* Warns once when `DATABASE_URL` is set in the environment but `databaseUrl` was
|
|
244
|
+
* not passed as an option.
|
|
244
245
|
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
* (the `transport: 'api'` client has none).
|
|
246
|
+
* The client does not adopt `process.env.DATABASE_URL` on its own, because that
|
|
247
|
+
* value is commonly set for other tools and switching the client into
|
|
248
|
+
* connection-string mode behind the caller's back is surprising — and on
|
|
249
|
+
* localhost it would try to register a database the hosted service cannot reach.
|
|
250
|
+
* This warning points the developer at the explicit option instead. It fires at
|
|
251
|
+
* most once per process and falls back to `console.warn` when no logger is
|
|
252
|
+
* supplied.
|
|
253
253
|
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
* false positive. This is the first-party hosted app's exact shape, where the
|
|
260
|
-
* stray nudge otherwise reaches end-user desktop logs.
|
|
254
|
+
* The warning is skipped entirely when an `apiKey` resolves (from the option or
|
|
255
|
+
* `ABLO_API_KEY`): that caller has chosen the hosted, token-based transport,
|
|
256
|
+
* which is separate from the direct `databaseUrl` connection. A `DATABASE_URL`
|
|
257
|
+
* present in that environment belongs to unrelated infrastructure, not an omitted
|
|
258
|
+
* option, so warning would be a false positive.
|
|
261
259
|
*/
|
|
262
260
|
let warnedDatabaseUrlEnvIgnored = false;
|
|
263
261
|
export function warnIfDatabaseUrlEnvIgnored(input, warn) {
|
|
@@ -282,19 +280,18 @@ export function warnIfDatabaseUrlEnvIgnored(input, warn) {
|
|
|
282
280
|
console.warn('[Ablo]', message);
|
|
283
281
|
}
|
|
284
282
|
/**
|
|
285
|
-
*
|
|
283
|
+
* Warns once when the deprecated `databaseUrl` option is used.
|
|
286
284
|
*
|
|
287
|
-
* `databaseUrl`
|
|
288
|
-
*
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
* credentials never leave it. See docs/plans/stripe-shaped-storage-posture.md.
|
|
285
|
+
* Passing `databaseUrl` opens a connection pool directly into your Postgres and
|
|
286
|
+
* writes to it. That option is deprecated. Ablo is designed to host only the
|
|
287
|
+
* ordered transaction log (the `sync_deltas` table) and coordination state,
|
|
288
|
+
* never your rows — your data stays in your own database. The supported path is
|
|
289
|
+
* a signed data-source endpoint (`dataSource(...)`), where your app owns the
|
|
290
|
+
* write and your database credentials never leave it.
|
|
294
291
|
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
* logger is supplied
|
|
292
|
+
* The option still works at runtime so existing integrations keep running. This
|
|
293
|
+
* warning fires at most once per process and falls back to `console.warn` when
|
|
294
|
+
* no logger is supplied.
|
|
298
295
|
*/
|
|
299
296
|
let warnedDatabaseUrlDeprecated = false;
|
|
300
297
|
export function warnIfDatabaseUrlDeprecated(input, warn) {
|
|
@@ -335,8 +332,8 @@ export async function warnIfCliKeyMismatch(input, warn) {
|
|
|
335
332
|
else if (typeof console !== 'undefined')
|
|
336
333
|
console.warn('[Ablo]', mismatch.message);
|
|
337
334
|
}
|
|
338
|
-
// Declared in
|
|
339
|
-
//
|
|
335
|
+
// Declared in `./hostedEndpoints`, the single source of the hosted domain, and
|
|
336
|
+
// re-exported here so existing import paths keep working.
|
|
340
337
|
export { ABLO_HOSTED_API_DOMAIN, ABLO_HOSTED_HTTP_BASE_URL, ABLO_DEFAULT_BASE_URL } from './hostedEndpoints.js';
|
|
341
338
|
const LEGACY_HOSTED_API_HOSTS = new Set([
|
|
342
339
|
'mesh.ablo.finance',
|
|
@@ -345,29 +342,29 @@ const LEGACY_HOSTED_API_HOSTS = new Set([
|
|
|
345
342
|
'sync-staging.ablo.finance',
|
|
346
343
|
]);
|
|
347
344
|
/**
|
|
348
|
-
*
|
|
349
|
-
* URLs pass through unchanged; only
|
|
345
|
+
* Normalizes older hosted host names to the current public API domain.
|
|
346
|
+
* Self-hosted or custom URLs pass through unchanged; only the retired
|
|
347
|
+
* first-party host names are rewritten.
|
|
350
348
|
*/
|
|
351
349
|
export function normalizeAbloHostedBaseUrl(rawUrl) {
|
|
352
350
|
const trimmed = rawUrl.trim();
|
|
353
351
|
if (!trimmed)
|
|
354
352
|
return trimmed;
|
|
355
|
-
// A scheme-less value (e.g. `api-staging.abloatai.com`) is
|
|
356
|
-
// `new URL()` throws on it, and
|
|
357
|
-
// the current page — producing
|
|
358
|
-
//
|
|
359
|
-
//
|
|
360
|
-
//
|
|
361
|
-
// preserved untouched.
|
|
353
|
+
// A scheme-less value (e.g. `api-staging.abloatai.com`) is treated as a
|
|
354
|
+
// relative URL: `new URL()` throws on it, and a later `fetch` would resolve it
|
|
355
|
+
// against the current page — producing a 404 from the app's own origin.
|
|
356
|
+
// Prepending a scheme makes the base absolute. `https` matches
|
|
357
|
+
// {@link ABLO_HOSTED_HTTP_BASE_URL}; the socket layer derives `wss` from it.
|
|
358
|
+
// An existing scheme (ws, wss, http, or https) is preserved untouched.
|
|
362
359
|
const schemed = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
363
360
|
try {
|
|
364
361
|
const url = new URL(schemed);
|
|
365
|
-
// Canonicalize the scheme to the HTTP family
|
|
366
|
-
//
|
|
367
|
-
//
|
|
368
|
-
//
|
|
369
|
-
//
|
|
370
|
-
//
|
|
362
|
+
// Canonicalize the scheme to the HTTP family: accept all four schemes
|
|
363
|
+
// (http, https, ws, wss), normalize at this single entry point, and let
|
|
364
|
+
// each layer derive its own protocol (the socket layer maps http to ws and
|
|
365
|
+
// https to wss; fetch uses the URL as-is). Without this, a `ws://` base URL
|
|
366
|
+
// reaches HTTP consumers un-normalized and the client fails at startup
|
|
367
|
+
// instead of connecting.
|
|
371
368
|
if (url.protocol === 'ws:')
|
|
372
369
|
url.protocol = 'http:';
|
|
373
370
|
if (url.protocol === 'wss:')
|
|
@@ -388,11 +385,12 @@ export function resolveBaseURL(input) {
|
|
|
388
385
|
return normalizeAbloHostedBaseUrl(input.options.baseURL ?? ABLO_DEFAULT_BASE_URL);
|
|
389
386
|
}
|
|
390
387
|
/**
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
388
|
+
* Guards against using a secret `apiKey` in a browser. A secret key is
|
|
389
|
+
* server-side only by default: shipping an `sk_live_...` key to a browser would
|
|
390
|
+
* expose it in every visitor's network tab. Callers opt in explicitly when the
|
|
391
|
+
* browser instead holds a minted session token (`ek_`/`rk_`) or routes through a
|
|
392
|
+
* server proxy. Throws {@link AbloAuthenticationError} when a secret key is
|
|
393
|
+
* detected in a browser without opt-in.
|
|
396
394
|
*/
|
|
397
395
|
export function assertBrowserSafety(input) {
|
|
398
396
|
const inBrowser = typeof window !== 'undefined';
|
|
@@ -407,9 +405,9 @@ export function assertBrowserSafety(input) {
|
|
|
407
405
|
'`dangerouslyAllowBrowser` option to `true`, e.g.,\n\n' +
|
|
408
406
|
' Ablo({ schema, apiKey, dangerouslyAllowBrowser: true });\n', { code: 'browser_apikey_blocked' });
|
|
409
407
|
}
|
|
410
|
-
// `databaseUrl` carries
|
|
411
|
-
// `dangerouslyAllowBrowser` does not override
|
|
412
|
-
// a server-side runtime.
|
|
408
|
+
// `databaseUrl` carries database credentials and is never browser-safe, so
|
|
409
|
+
// `dangerouslyAllowBrowser` does not override this check. Register your
|
|
410
|
+
// database from a server-side runtime.
|
|
413
411
|
if (inBrowser && typeof input.databaseUrl === 'string' && input.databaseUrl.length > 0) {
|
|
414
412
|
throw new AbloAuthenticationError('Ablo `databaseUrl` cannot be used in a browser-like environment — it ' +
|
|
415
413
|
'carries your database credentials. Initialize the client with ' +
|
|
@@ -417,12 +415,10 @@ export function assertBrowserSafety(input) {
|
|
|
417
415
|
}
|
|
418
416
|
}
|
|
419
417
|
/**
|
|
420
|
-
*
|
|
421
|
-
*
|
|
422
|
-
* between requests. Returns `null` when no key
|
|
423
|
-
*
|
|
424
|
-
* Mirrors Anthropic's pattern of supporting both a static string and
|
|
425
|
-
* a callable for credential rotation.
|
|
418
|
+
* Resolves an {@link ApiKeySetter} callable to its current string value, or
|
|
419
|
+
* returns a plain string key as-is. Called at request time so a rotating
|
|
420
|
+
* credential picks up new values between requests. Returns `null` when no key
|
|
421
|
+
* was configured.
|
|
426
422
|
*/
|
|
427
423
|
export async function resolveApiKeyValue(apiKey) {
|
|
428
424
|
if (apiKey == null)
|
|
@@ -432,44 +428,39 @@ export async function resolveApiKeyValue(apiKey) {
|
|
|
432
428
|
return apiKey;
|
|
433
429
|
}
|
|
434
430
|
/**
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
* so it lives here as a single source of truth.
|
|
431
|
+
* Translates a WebSocket URL into the matching HTTP API base URL, defaulting to
|
|
432
|
+
* `${url}/api` when the caller has not overridden `bootstrapBaseUrl`. The
|
|
433
|
+
* bootstrap helper, the hydration coordinator, the credential-exchange flow, and
|
|
434
|
+
* the identity flow all derive their base URL through this one function, so the
|
|
435
|
+
* derivation stays consistent across them.
|
|
441
436
|
*
|
|
442
|
-
*
|
|
443
|
-
*
|
|
437
|
+
* When both `wss://` and `https://` are valid, the ws-to-http rewrite preserves
|
|
438
|
+
* the protocol family: ws becomes http and wss becomes https.
|
|
444
439
|
*/
|
|
445
440
|
export function resolveBootstrapBaseUrl(input) {
|
|
446
441
|
if (input.bootstrapBaseUrl) {
|
|
447
|
-
// Coerce ws/wss
|
|
448
|
-
// used for HTTP fetches (identity
|
|
449
|
-
// the browser `fetch` rejects ws
|
|
450
|
-
//
|
|
451
|
-
//
|
|
452
|
-
//
|
|
453
|
-
// faceplanting at fetch time. The derive branch below already does this;
|
|
454
|
-
// the override branch silently skipped it.
|
|
442
|
+
// Coerce ws/wss to http/https on the override path as well. This base URL is
|
|
443
|
+
// used for HTTP fetches (identity resolution, credential exchange, and
|
|
444
|
+
// bootstrap), and the browser `fetch` rejects ws and wss schemes outright.
|
|
445
|
+
// The override can legitimately arrive with a WebSocket scheme when a caller
|
|
446
|
+
// derives it as `${baseUrl}/api` from a WebSocket base URL, so normalize it
|
|
447
|
+
// here rather than failing at fetch time.
|
|
455
448
|
return ensureApiSuffix(normalizeAbloHostedBaseUrl(input.bootstrapBaseUrl).replace(/^ws/, 'http'));
|
|
456
449
|
}
|
|
457
450
|
const url = normalizeAbloHostedBaseUrl(input.url);
|
|
458
451
|
return ensureApiSuffix(url.replace(/^ws/, 'http'));
|
|
459
452
|
}
|
|
460
453
|
/**
|
|
461
|
-
*
|
|
462
|
-
*
|
|
454
|
+
* Ensures the HTTP base ends in the `/api` route segment that every endpoint is
|
|
455
|
+
* mounted under.
|
|
463
456
|
*
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
*
|
|
467
|
-
*
|
|
468
|
-
*
|
|
469
|
-
*
|
|
470
|
-
*
|
|
471
|
-
* to a single trailing `/api` here is always correct — and idempotent for
|
|
472
|
-
* callers who already include it.
|
|
457
|
+
* A hosted deployment that sets a custom `baseURL` or `bootstrapBaseUrl` (a
|
|
458
|
+
* custom subdomain, a staging host, and so on) without the `/api` suffix would
|
|
459
|
+
* send every credential exchange to `…/auth/capability` instead of
|
|
460
|
+
* `…/api/auth/capability`, producing a 404 that surfaces as `exchange_failed`.
|
|
461
|
+
* Since the client builds routes relative to this base and no valid deployment
|
|
462
|
+
* serves them from the root, appending a single trailing `/api` here is always
|
|
463
|
+
* correct, and it is idempotent for callers who already include it.
|
|
473
464
|
*/
|
|
474
465
|
function ensureApiSuffix(httpBase) {
|
|
475
466
|
const trimmed = httpBase.replace(/\/+$/, '');
|
|
@@ -482,8 +473,8 @@ function ensureApiSuffix(httpBase) {
|
|
|
482
473
|
return u.toString().replace(/\/+$/, '');
|
|
483
474
|
}
|
|
484
475
|
catch {
|
|
485
|
-
// Should be unreachable
|
|
486
|
-
// absolute URL
|
|
476
|
+
// Should be unreachable after `normalizeAbloHostedBaseUrl`, which yields an
|
|
477
|
+
// absolute URL, but fall back to a string check rather than throwing.
|
|
487
478
|
return trimmed.endsWith("/api") ? trimmed : `${trimmed}/api`;
|
|
488
479
|
}
|
|
489
480
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The auto-heartbeat loop behind `claim({ id, heartbeat: true })` — one
|
|
3
|
+
* implementation shared by both transports (the WebSocket claim stream and the
|
|
4
|
+
* HTTP `ApiClient`), so the cadence and failure semantics cannot drift.
|
|
5
|
+
*
|
|
6
|
+
* A beat is the "still working" signal that keeps a lease alive for the
|
|
7
|
+
* duration of real work. The loop's failure handling follows the lease-system
|
|
8
|
+
* convention (SQS visibility heartbeats, Kubernetes leases): a beat that fails
|
|
9
|
+
* for a transient reason — the network blipped, the server was briefly
|
|
10
|
+
* unavailable — is simply retried on the next tick, because the lease has
|
|
11
|
+
* runway to spare by construction (the default cadence is a third of the TTL,
|
|
12
|
+
* so two consecutive beats can fail before the lease is even at risk). Only a
|
|
13
|
+
* definitive answer from the server — the lease lapsed and may have been
|
|
14
|
+
* granted to the next in line ({@link AbloClaimedError}) — stops the loop and
|
|
15
|
+
* surfaces the loss, because for a caller with no push channel the failed beat
|
|
16
|
+
* IS the loss notification.
|
|
17
|
+
*/
|
|
18
|
+
import { AbloClaimedError } from '../errors.js';
|
|
19
|
+
import type { ClaimHeartbeat, ClaimHeartbeatOptions, Duration } from '../types/streams.js';
|
|
20
|
+
/**
|
|
21
|
+
* Normalize the public `heartbeat(options?)` argument — a bare Duration is
|
|
22
|
+
* shorthand for `{ ttl }`. Shared by both transports' handle assembly so the
|
|
23
|
+
* shorthand cannot drift.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveHeartbeatOptions(input: Duration | ClaimHeartbeatOptions | undefined): ClaimHeartbeatOptions;
|
|
26
|
+
/**
|
|
27
|
+
* The beat cadence for a lease of `ttlMs`: an explicit duration when the
|
|
28
|
+
* caller set one, otherwise a third of the TTL (floored at 1s) — the
|
|
29
|
+
* DynamoDB-lock-client rule, leaving two missed beats of runway before the
|
|
30
|
+
* lease is at risk while keeping crash recovery within one beat window.
|
|
31
|
+
*/
|
|
32
|
+
export declare function heartbeatCadenceMs(ttlMs: number, heartbeat: true | Duration): number;
|
|
33
|
+
export interface ClaimHeartbeatLoopOptions {
|
|
34
|
+
/** Send one beat; resolves while the lease is still ours. */
|
|
35
|
+
beat(): Promise<ClaimHeartbeat>;
|
|
36
|
+
/** Cadence between beats. Callers default this to a third of the TTL. */
|
|
37
|
+
intervalMs: number;
|
|
38
|
+
/**
|
|
39
|
+
* Called once when a beat comes back with a definitive loss — the lease
|
|
40
|
+
* expired or was taken. The loop has already stopped by the time this runs.
|
|
41
|
+
*/
|
|
42
|
+
onLost?(error: AbloClaimedError): void;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Start beating. Returns a stop function; callers stop the loop when the
|
|
46
|
+
* claim is released (all held-claim assembly sites tie this to `release`).
|
|
47
|
+
* Beats never overlap: a tick that fires while the previous beat is still
|
|
48
|
+
* in flight is skipped rather than stacked.
|
|
49
|
+
*/
|
|
50
|
+
export declare function startClaimHeartbeatLoop(options: ClaimHeartbeatLoopOptions): () => void;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The auto-heartbeat loop behind `claim({ id, heartbeat: true })` — one
|
|
3
|
+
* implementation shared by both transports (the WebSocket claim stream and the
|
|
4
|
+
* HTTP `ApiClient`), so the cadence and failure semantics cannot drift.
|
|
5
|
+
*
|
|
6
|
+
* A beat is the "still working" signal that keeps a lease alive for the
|
|
7
|
+
* duration of real work. The loop's failure handling follows the lease-system
|
|
8
|
+
* convention (SQS visibility heartbeats, Kubernetes leases): a beat that fails
|
|
9
|
+
* for a transient reason — the network blipped, the server was briefly
|
|
10
|
+
* unavailable — is simply retried on the next tick, because the lease has
|
|
11
|
+
* runway to spare by construction (the default cadence is a third of the TTL,
|
|
12
|
+
* so two consecutive beats can fail before the lease is even at risk). Only a
|
|
13
|
+
* definitive answer from the server — the lease lapsed and may have been
|
|
14
|
+
* granted to the next in line ({@link AbloClaimedError}) — stops the loop and
|
|
15
|
+
* surfaces the loss, because for a caller with no push channel the failed beat
|
|
16
|
+
* IS the loss notification.
|
|
17
|
+
*/
|
|
18
|
+
import { AbloClaimedError } from '../errors.js';
|
|
19
|
+
import { toMs } from '../utils/duration.js';
|
|
20
|
+
/**
|
|
21
|
+
* Normalize the public `heartbeat(options?)` argument — a bare Duration is
|
|
22
|
+
* shorthand for `{ ttl }`. Shared by both transports' handle assembly so the
|
|
23
|
+
* shorthand cannot drift.
|
|
24
|
+
*/
|
|
25
|
+
export function resolveHeartbeatOptions(input) {
|
|
26
|
+
if (input === undefined)
|
|
27
|
+
return {};
|
|
28
|
+
if (typeof input === 'string' || typeof input === 'number') {
|
|
29
|
+
return { ttl: input };
|
|
30
|
+
}
|
|
31
|
+
return input;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The beat cadence for a lease of `ttlMs`: an explicit duration when the
|
|
35
|
+
* caller set one, otherwise a third of the TTL (floored at 1s) — the
|
|
36
|
+
* DynamoDB-lock-client rule, leaving two missed beats of runway before the
|
|
37
|
+
* lease is at risk while keeping crash recovery within one beat window.
|
|
38
|
+
*/
|
|
39
|
+
export function heartbeatCadenceMs(ttlMs, heartbeat) {
|
|
40
|
+
if (heartbeat !== true)
|
|
41
|
+
return toMs(heartbeat);
|
|
42
|
+
return Math.max(Math.floor(ttlMs / 3), 1_000);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Start beating. Returns a stop function; callers stop the loop when the
|
|
46
|
+
* claim is released (all held-claim assembly sites tie this to `release`).
|
|
47
|
+
* Beats never overlap: a tick that fires while the previous beat is still
|
|
48
|
+
* in flight is skipped rather than stacked.
|
|
49
|
+
*/
|
|
50
|
+
export function startClaimHeartbeatLoop(options) {
|
|
51
|
+
let stopped = false;
|
|
52
|
+
let inFlight = false;
|
|
53
|
+
const stop = () => {
|
|
54
|
+
if (stopped)
|
|
55
|
+
return;
|
|
56
|
+
stopped = true;
|
|
57
|
+
clearInterval(timer);
|
|
58
|
+
};
|
|
59
|
+
const timer = setInterval(() => {
|
|
60
|
+
if (stopped || inFlight)
|
|
61
|
+
return;
|
|
62
|
+
inFlight = true;
|
|
63
|
+
options
|
|
64
|
+
.beat()
|
|
65
|
+
.catch((error) => {
|
|
66
|
+
if (stopped)
|
|
67
|
+
return;
|
|
68
|
+
if (error instanceof AbloClaimedError) {
|
|
69
|
+
stop();
|
|
70
|
+
options.onLost?.(error);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// Transient (connection, brief server unavailability): the next tick
|
|
74
|
+
// is the retry — the ttl/3 cadence leaves runway for missed beats.
|
|
75
|
+
})
|
|
76
|
+
.finally(() => {
|
|
77
|
+
inFlight = false;
|
|
78
|
+
});
|
|
79
|
+
}, options.intervalMs);
|
|
80
|
+
// The loop must never be what keeps a Node process alive — the held work
|
|
81
|
+
// is. A worker that finishes without releasing exits anyway, and the lease
|
|
82
|
+
// lapses on schedule. No-op in browsers (where setInterval returns a number
|
|
83
|
+
// without `unref`), which is why the call is optional even though the Node
|
|
84
|
+
// timer type always has it.
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
86
|
+
timer.unref?.();
|
|
87
|
+
return stop;
|
|
88
|
+
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The default `[Ablo]` console logger
|
|
2
|
+
* The default `[Ablo]` console logger and its level resolution.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* of hand-rolling a console shim.
|
|
4
|
+
* These helpers are exported so any entry point that needs a default logger can
|
|
5
|
+
* reuse the same level gating rather than hand-rolling a console wrapper.
|
|
7
6
|
*/
|
|
8
7
|
import type { SyncLogger } from '../interfaces/index.js';
|
|
9
8
|
/**
|
|
@@ -30,7 +29,7 @@ export declare function resolveLogLevel(opts?: {
|
|
|
30
29
|
logLevel?: LogLevel;
|
|
31
30
|
}): LogLevel;
|
|
32
31
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
32
|
+
* Builds the default logger, gated at `level` and prefixed `[Ablo]` so its lines
|
|
33
|
+
* stand out in a console full of other tools' output.
|
|
35
34
|
*/
|
|
36
35
|
export declare function createConsoleLogger(level: LogLevel): SyncLogger;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The default `[Ablo]` console logger
|
|
2
|
+
* The default `[Ablo]` console logger and its level resolution.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* of hand-rolling a console shim.
|
|
4
|
+
* These helpers are exported so any entry point that needs a default logger can
|
|
5
|
+
* reuse the same level gating rather than hand-rolling a console wrapper.
|
|
7
6
|
*/
|
|
8
7
|
const LOG_LEVEL_RANK = { debug: 10, info: 20, warn: 30, error: 40, silent: 99 };
|
|
9
8
|
/**
|
|
@@ -26,8 +25,8 @@ export function resolveLogLevel(opts) {
|
|
|
26
25
|
return 'warn';
|
|
27
26
|
}
|
|
28
27
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
28
|
+
* Builds the default logger, gated at `level` and prefixed `[Ablo]` so its lines
|
|
29
|
+
* stand out in a console full of other tools' output.
|
|
31
30
|
*/
|
|
32
31
|
export function createConsoleLogger(level) {
|
|
33
32
|
const threshold = LOG_LEVEL_RANK[level];
|
|
@@ -1,28 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Builds the internal component graph the client runs on.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Mirrors the pattern Anthropic uses: their client constructor wires
|
|
11
|
-
* endpoint modules. Ours wires the sync-engine components instead.
|
|
4
|
+
* From the caller's options and schema this wires together the model registry,
|
|
5
|
+
* object pool, bootstrap helper, database, sync client, and hydration
|
|
6
|
+
* coordinator. Each component depends on the one before it, so construction
|
|
7
|
+
* order matters; keeping it here means the client constructor does not have to
|
|
8
|
+
* know that order.
|
|
12
9
|
*/
|
|
13
10
|
import { Database } from '../Database.js';
|
|
14
11
|
import { ModelRegistry } from '../ModelRegistry.js';
|
|
15
|
-
import {
|
|
12
|
+
import { InstanceCache } from '../InstanceCache.js';
|
|
16
13
|
import { SyncClient } from '../SyncClient.js';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
14
|
+
import { OnDemandLoader } from '../sync/OnDemandLoader.js';
|
|
15
|
+
import { BootstrapFetcher } from '../sync/BootstrapFetcher.js';
|
|
19
16
|
import type { AuthCredentialSource } from '../auth/credentialSource.js';
|
|
20
17
|
import type { Schema, SchemaRecord } from '../schema/schema.js';
|
|
21
18
|
import { type AbloPersistence } from './persistence.js';
|
|
22
19
|
export interface InternalComponentsInput<S extends SchemaRecord> {
|
|
23
20
|
readonly schema: Schema<S>;
|
|
24
|
-
/** WebSocket URL
|
|
25
|
-
* caller
|
|
21
|
+
/** The WebSocket URL. Used to derive the bootstrap HTTP base URL when the
|
|
22
|
+
* caller has not overridden `bootstrapBaseUrl`. */
|
|
26
23
|
readonly url: string;
|
|
27
24
|
readonly options: {
|
|
28
25
|
readonly maxPoolSize?: number;
|
|
@@ -36,10 +33,10 @@ export interface InternalComponentsInput<S extends SchemaRecord> {
|
|
|
36
33
|
}
|
|
37
34
|
export interface InternalComponents {
|
|
38
35
|
readonly modelRegistry: ModelRegistry;
|
|
39
|
-
readonly objectPool:
|
|
40
|
-
readonly bootstrapHelper:
|
|
36
|
+
readonly objectPool: InstanceCache;
|
|
37
|
+
readonly bootstrapHelper: BootstrapFetcher;
|
|
41
38
|
readonly database: Database;
|
|
42
39
|
readonly syncClient: SyncClient;
|
|
43
|
-
readonly hydration:
|
|
40
|
+
readonly hydration: OnDemandLoader;
|
|
44
41
|
}
|
|
45
42
|
export declare function createInternalComponents<S extends SchemaRecord>(input: InternalComponentsInput<S>): InternalComponents;
|