@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/errorCodes.js
CHANGED
|
@@ -1,68 +1,70 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The
|
|
3
|
-
*
|
|
2
|
+
* The registry of every stable error code Ablo can produce. Error handling has
|
|
3
|
+
* two levels, and this file defines the finer one.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* - The `type` is the coarse category, and each one corresponds to an
|
|
6
|
+
* {@link AbloError} subclass such as `AbloPermissionError` or
|
|
7
|
+
* `AbloValidationError`. Catching by `instanceof` is equivalent to switching
|
|
8
|
+
* on `error.type`.
|
|
9
|
+
* - The `code` is the fine-grained, machine-readable identifier defined here,
|
|
10
|
+
* written in `snake_case` (for example `entity_claimed` or `queue_too_deep`).
|
|
11
|
+
* This is what you switch on to handle a specific situation, and what the
|
|
12
|
+
* documentation link for an error is built from.
|
|
6
13
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* - **`code`** — the fine-grained, machine-readable identifier in this
|
|
11
|
-
* file. `snake_case`, ordered noun→state (`entity_claimed`) or
|
|
12
|
-
* condition→constraint (`queue_too_deep`). This is what callers
|
|
13
|
-
* `switch` on for specific handling, and what `doc_url` is derived from.
|
|
14
|
+
* The client, the server, and the tool-calling boundary all speak this same
|
|
15
|
+
* vocabulary, which makes the registry part of the API contract. Two things
|
|
16
|
+
* follow from that:
|
|
14
17
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* 'client'` codes are local SDK invariants (you forgot to open the DB,
|
|
28
|
-
* a model isn't registered) — never sent over the network, so they
|
|
29
|
-
* carry no `httpStatus`, exactly as Stripe omits client-side
|
|
30
|
-
* programmer errors from its published code list.
|
|
18
|
+
* 1. {@link ErrorCode} is a closed set — plus the dynamic `policy:${string}`
|
|
19
|
+
* family — so producing a code that is not registered here is a
|
|
20
|
+
* compile-time error. The {@link AbloError} constructor accepts only a
|
|
21
|
+
* registered code. The one place an arbitrary string is accepted as a code
|
|
22
|
+
* is where an incoming response is parsed, which lets an older client
|
|
23
|
+
* tolerate a code from a newer server it does not yet recognize.
|
|
24
|
+
* 2. The codes marked `surface: 'wire'` are the ones that cross the network
|
|
25
|
+
* and are mapped at the HTTP and tool-calling boundaries; the public error
|
|
26
|
+
* documentation is generated from them. Codes marked `surface: 'client'`
|
|
27
|
+
* describe local mistakes — accessing the database before opening it, or
|
|
28
|
+
* writing to a model that was never registered — and are never sent over
|
|
29
|
+
* the network, so they carry no HTTP status.
|
|
31
30
|
*/
|
|
32
31
|
import { z } from 'zod';
|
|
33
32
|
/**
|
|
34
|
-
*
|
|
35
|
-
* their
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* and on the `Ablo-Version` response header so a
|
|
33
|
+
* The version of the error contract: the envelope shape together with the set of
|
|
34
|
+
* codes and their meanings. It is date-based, and changes only when the contract
|
|
35
|
+
* changes in a way a consumer can observe — a code added or removed, an HTTP
|
|
36
|
+
* status changed, or an envelope field changed. It is emitted in the generated
|
|
37
|
+
* error documentation and returned on the `Ablo-Version` response header, so a
|
|
38
|
+
* consumer can detect when its expected contract has drifted from the server's.
|
|
39
39
|
*/
|
|
40
40
|
export const ERROR_CONTRACT_VERSION = '2026-07-03';
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* compile-time completeness instead of ad-hoc `if (!isRetryableCode(...))`
|
|
49
|
-
* chains.
|
|
42
|
+
* A closed classification of how a failure can be recovered from — a level above
|
|
43
|
+
* the raw {@link ErrorCode}. Where a code says what went wrong, a recovery class
|
|
44
|
+
* says what a client should do about it, which is exactly the distinction the
|
|
45
|
+
* connection layer needs to decide between retrying, re-minting a credential, and
|
|
46
|
+
* signing the user out. Every code maps to one of these, and the set is
|
|
47
|
+
* validated at runtime.
|
|
50
48
|
*
|
|
51
|
-
* - `access_credential_expiry` — the
|
|
52
|
-
*
|
|
53
|
-
* is
|
|
54
|
-
* and retry the same request. This
|
|
55
|
-
*
|
|
56
|
-
* is routine
|
|
57
|
-
* - `session_expiry` — the
|
|
49
|
+
* - `access_credential_expiry` — the short-lived access credential the client
|
|
50
|
+
* presents (its ephemeral `ek_` or `rk_` key) has expired, while the
|
|
51
|
+
* underlying login is still valid. The remedy is to mint a fresh key from the
|
|
52
|
+
* session and retry the same request. This does not sign the user out; a
|
|
53
|
+
* short-lived key expiring — for example after a laptop resumes from sleep —
|
|
54
|
+
* is routine.
|
|
55
|
+
* - `session_expiry` — the long-lived login itself is gone. This is terminal:
|
|
58
56
|
* sign out and route to re-authentication.
|
|
59
|
-
* - `auth_blocked` — reachable
|
|
60
|
-
* (wrong key
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* - `
|
|
64
|
-
*
|
|
65
|
-
*
|
|
57
|
+
* - `auth_blocked` — the server was reachable but rejected the kind or
|
|
58
|
+
* configuration of the credential (wrong key type, untrusted issuer, no
|
|
59
|
+
* organization). Re-authenticating would present the same rejected credential
|
|
60
|
+
* and loop, so the client should stop rather than reconnect or sign out.
|
|
61
|
+
* - `permission` — an authorization denial (403) based on scope, role, or
|
|
62
|
+
* membership.
|
|
63
|
+
* - `transient` — a temporary failure, such as a server error or lease
|
|
64
|
+
* contention, that may succeed if the same request is retried unchanged.
|
|
65
|
+
* - `none` — not a recoverable authentication condition: validation errors,
|
|
66
|
+
* not-found, local invariants, and any code an older client does not
|
|
67
|
+
* recognize.
|
|
66
68
|
*/
|
|
67
69
|
export const RECOVERY_CLASSES = [
|
|
68
70
|
'access_credential_expiry',
|
|
@@ -72,25 +74,26 @@ export const RECOVERY_CLASSES = [
|
|
|
72
74
|
'transient',
|
|
73
75
|
'none',
|
|
74
76
|
];
|
|
75
|
-
/** Zod enum
|
|
76
|
-
*
|
|
77
|
+
/** A Zod enum over {@link RECOVERY_CLASSES}, for validating a recovery class at
|
|
78
|
+
* runtime. */
|
|
77
79
|
export const recoveryClassSchema = z.enum(RECOVERY_CLASSES);
|
|
78
80
|
const wire = (category, httpStatus, retryable, message, recovery) => ({ category, surface: 'wire', httpStatus, retryable, message, recovery });
|
|
79
81
|
const client = (category, message) => ({ category, surface: 'client', retryable: false, message });
|
|
80
82
|
/**
|
|
81
|
-
* The
|
|
82
|
-
*
|
|
83
|
+
* The complete set of stable error codes, keyed by code. A code must be added
|
|
84
|
+
* here before it can be thrown, since the {@link AbloError} constructor accepts
|
|
85
|
+
* only codes from this set.
|
|
83
86
|
*/
|
|
84
87
|
export const ERROR_CODES = {
|
|
85
88
|
// ── auth (401) ─────────────────────────────────────────────────────
|
|
86
89
|
apikey_invalid: wire('auth', 401, false, "This API key isn't one Ablo recognizes — it may be mistyped, truncated, or belong to a different environment. Check the key and try again."),
|
|
87
90
|
apikey_revoked: wire('auth', 401, false, 'This API key has been revoked and can no longer be used. Mint a new key from the dashboard.'),
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
//
|
|
91
|
+
// The short-lived access credential — the ephemeral key (`ek_` for users,
|
|
92
|
+
// `rk_` for agents) minted from the login and presented as a bearer token.
|
|
93
|
+
// Its expiry is routine and re-mintable: get a fresh key from the still-valid
|
|
94
|
+
// session and retry, rather than signing out. An agent's expired `rk_` must
|
|
95
|
+
// not sign a human out either. This is the one code on the silent re-mint
|
|
96
|
+
// path; see the `access_credential_expiry` recovery class.
|
|
94
97
|
apikey_expired: wire('auth', 401, false, 'This ephemeral API key has expired. Mint a fresh key from your still-valid session and retry the request.', 'access_credential_expiry'),
|
|
95
98
|
apikey_missing: wire('auth', 401, false, 'The request arrived without an API key. Send one as `Authorization: Bearer <key>`.'),
|
|
96
99
|
api_key_required: wire('auth', 401, false, 'This operation requires an API key, and none was presented. Send one as `Authorization: Bearer <key>`.'),
|
|
@@ -99,12 +102,13 @@ export const ERROR_CODES = {
|
|
|
99
102
|
identity_resolve_failed: wire('auth', 401, false, 'The server could not resolve an identity for this credential — the identity lookup was rejected. Check that the credential is still valid.'),
|
|
100
103
|
auth_no_credentials: wire('auth', 401, false, 'No recognized authentication credential was presented — no API key and no bearer JWT. Send `Authorization: Bearer <token>`.'),
|
|
101
104
|
identity_missing_organization: wire('auth', 401, false, 'Authentication succeeded, but the credential resolves to no organization, so requests cannot be scoped. Check that the key or token carries an organization.'),
|
|
102
|
-
// The long-lived login is gone
|
|
105
|
+
// The long-lived login is gone; this is terminal and drives sign-out and
|
|
106
|
+
// re-authentication.
|
|
103
107
|
session_expired: wire('auth', 401, false, 'Your session has expired or is no longer valid. Sign in again to continue.', 'session_expiry'),
|
|
104
|
-
// `jwt_invalid` is the
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
+
// `jwt_invalid` is the general fallback; the codes below it split out specific
|
|
109
|
+
// failure modes, so an integrator can tell a wrong JWKS registration from a
|
|
110
|
+
// token with no organization claim from a wrong audience, instead of getting
|
|
111
|
+
// one opaque code for all of them.
|
|
108
112
|
jwt_invalid: wire('auth', 401, false, "The bearer JWT failed validation for a reason the server could not classify further. Check the token's issuer, signature, audience, and expiry."),
|
|
109
113
|
jwt_malformed: wire('auth', 401, false, 'The bearer token is not a well-formed JWT and could not be decoded. Check that the full, unmodified token was sent.'),
|
|
110
114
|
jwt_missing_issuer: wire('auth', 401, false, 'The bearer JWT has no `iss` (issuer) claim, so it cannot be routed to a trusted issuer.'),
|
|
@@ -113,11 +117,10 @@ export const ERROR_CODES = {
|
|
|
113
117
|
jwt_audience_mismatch: wire('auth', 401, false, "The bearer JWT's `aud` (audience) claim does not match the audience this issuer is registered with."),
|
|
114
118
|
jwt_missing_subject: wire('auth', 401, false, 'The bearer JWT has no `sub` (subject) claim to identify the user.'),
|
|
115
119
|
jwt_missing_organization: wire('auth', 401, false, 'The bearer JWT carries no organization context — neither a fixed org for the issuer nor the configured organization claim.'),
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
// (which also keeps `isSessionErrorResponse` behaviour unchanged).
|
|
120
|
+
// Applies only to the trusted-issuer path, where a customer authenticates with
|
|
121
|
+
// a JWT from their own identity provider. When such a token expires, the
|
|
122
|
+
// remedy is to re-authenticate against that provider, so it classifies as a
|
|
123
|
+
// session expiry.
|
|
121
124
|
jwt_expired: wire('auth', 401, false, 'The bearer JWT has expired. Obtain a fresh token from your identity provider and retry.', 'session_expiry'),
|
|
122
125
|
jwt_org_membership_denied: wire('auth', 403, false, "The bearer JWT's subject is not an active member of the organization in its `org_id` claim (removed, suspended, or the claim does not match a membership)."),
|
|
123
126
|
file_upload_auth_required: wire('auth', 401, false, 'File uploads require an authenticated session. Sign in and retry.'),
|
|
@@ -136,22 +139,18 @@ export const ERROR_CODES = {
|
|
|
136
139
|
database_role_unreadable: wire('permission', 403, false, 'Ablo could not introspect the database role it connects with, so it cannot verify that row-level security is enforced.'),
|
|
137
140
|
database_tables_unforced_rls: wire('permission', 403, false, 'Some synced tables do not have `FORCE ROW LEVEL SECURITY` applied, so the table owner can bypass row isolation. Run `ALTER TABLE ... FORCE ROW LEVEL SECURITY` on each synced table.'),
|
|
138
141
|
database_host_not_allowed: wire('permission', 403, false, "The database host resolves to a private, loopback, or link-local address, which Ablo's servers will not connect to. Use a publicly resolvable host."),
|
|
139
|
-
//
|
|
140
|
-
//
|
|
142
|
+
// Older spellings of the `database_*` codes above, still sent by some servers
|
|
143
|
+
// and kept so they classify identically. Prefer the `database_*` codes.
|
|
141
144
|
byo_role_cannot_enforce_rls: wire('permission', 403, false, 'The direct Postgres connector role cannot enforce row-level security.'),
|
|
142
145
|
byo_role_unreadable: wire('permission', 403, false, 'The direct Postgres connector role could not be introspected.'),
|
|
143
146
|
byo_tenant_tables_unforced_rls: wire('permission', 403, false, 'Tenant tables do not have RLS forced under the direct Postgres connector role.'),
|
|
144
147
|
byo_host_not_allowed: wire('permission', 403, false, 'The direct Postgres connector host resolves to a private, loopback, or link-local address and cannot be used.'),
|
|
145
148
|
// ── claim / claim conflict (409) ──────────────────────────────────
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
// lives at the CALLER: take a claim (`ablo.<model>.claim` queues fairly
|
|
152
|
-
// behind the holder) or re-read and rebase. `retryable: true` here turned
|
|
153
|
-
// every cross-client claim conflict into an infinite client resend loop
|
|
154
|
-
// (~150ms storm — found by the claims journey, 2026-06-10).
|
|
149
|
+
// A rejection because another participant holds a claim is not retryable.
|
|
150
|
+
// Re-sending the same write cannot succeed while the claim is held, and a
|
|
151
|
+
// claim can outlive any reasonable retry budget, so an automatic retry would
|
|
152
|
+
// only loop. Recovery belongs to the caller: take a claim, which queues fairly
|
|
153
|
+
// behind the holder (`ablo.<model>.claim`), or re-read and rebase.
|
|
155
154
|
claim_conflict: wire('claim', 409, false, 'Another participant holds a claim on this row, so the write was rejected. Take a claim with `ablo.<model>.claim` to queue fairly behind the holder, or re-read and rebase.'),
|
|
156
155
|
claim_lost: wire('claim', 409, false, 'The claim held on this row was lost before the write could apply. Re-acquire the claim and retry.'),
|
|
157
156
|
entity_claimed: wire('claim', 409, false, 'This row is currently claimed by another participant, so the write was blocked. Queue behind the holder with `ablo.<model>.claim`, or wait for the claim to clear.'),
|
|
@@ -164,9 +163,9 @@ export const ERROR_CODES = {
|
|
|
164
163
|
// ── stale context / idempotency (409) ──────────────────────────────
|
|
165
164
|
stale_context: wire('conflict', 409, true, "The row changed after you read it — the write's `readAt` watermark is older than the current row version. Re-read the row and retry."),
|
|
166
165
|
// Raised by the functional `update(id, current => next)` form once its
|
|
167
|
-
// internal reconcile budget is exhausted
|
|
168
|
-
// contended.
|
|
169
|
-
//
|
|
166
|
+
// internal reconcile budget is exhausted, because the row stayed continuously
|
|
167
|
+
// contended. The SDK has already retried; the caller decides whether to back
|
|
168
|
+
// off, raise `retries`, or move the row to the WebSocket transport.
|
|
170
169
|
contention_exhausted: client('conflict', 'A functional update kept losing to concurrent writes and exhausted its reconcile budget. Back off and retry, raise `retries`, or move the row to the WebSocket transport.'),
|
|
171
170
|
update_aborted: client('conflict', 'The functional update was aborted via its `AbortSignal` before the write landed; nothing was written.'),
|
|
172
171
|
idempotency_conflict: wire('conflict', 409, false, 'This `Idempotency-Key` was already used with a different request body. Reuse a key only to retry an identical request; otherwise generate a new one.'),
|
|
@@ -175,21 +174,20 @@ export const ERROR_CODES = {
|
|
|
175
174
|
write_options_invalid: client('validation', 'The write options (`idempotencyKey` / `label` / `wait` / `readAt` / `onStale` / `claim`) failed validation against the write-options schema.'),
|
|
176
175
|
source_operation_id_required: client('validation', 'A data-source operation arrived without the entity `id` it targets.'),
|
|
177
176
|
source_adapter_misconfigured: client('validation', 'The data-source ORM adapter could not map a schema model onto the backing client — the client exposes no matching delegate or model. Check that the adapter and schema agree on model names.'),
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
// `sourceEventForOperation` when an outbox event cannot be built.
|
|
177
|
+
// The server validates every incoming data-source event before appending it
|
|
178
|
+
// to the log and rejects the whole batch with this code; `param` names the
|
|
179
|
+
// offending index and field path, such as `events[3].entityId`. It is also
|
|
180
|
+
// raised on the client when an outbound source event cannot be built.
|
|
183
181
|
source_event_invalid: wire('validation', 400, false, 'A data-source event was malformed — missing or invalid id, model, entityId, type, or field value. The whole event batch was rejected and nothing was ingested; fix the offending outbox row and re-send.'),
|
|
184
182
|
duration_invalid: client('validation', 'A duration value was not a number of seconds or a "500ms" | "30s" | "3m" | "24h" string.'),
|
|
185
183
|
schema_definition_invalid: client('validation', 'A schema definition value was invalid (bad column identifier, non-finite backfill, or unsupported schema-JSON version).'),
|
|
186
184
|
cli_invalid_arguments: client('validation', 'The CLI was invoked with an unknown flag or a malformed flag value.'),
|
|
187
185
|
turn_validation_failed: wire('validation', 422, false, 'The agent turn payload failed server-side validation and was not applied.'),
|
|
188
186
|
commit_operation_required: wire('validation', 400, false, 'A commit must carry `operation` or `operations`.'),
|
|
189
|
-
//
|
|
190
|
-
// `/v1/commits`
|
|
191
|
-
//
|
|
192
|
-
//
|
|
187
|
+
// Both commit transports — the WebSocket `commit` frame and the HTTP
|
|
188
|
+
// `/v1/commits` endpoint — validate every operation and reject the whole batch
|
|
189
|
+
// with this code. `param` names the offending index and field path, such as
|
|
190
|
+
// `operations[3].readAt`.
|
|
193
191
|
commit_operation_invalid: wire('validation', 400, false, 'A commit operation failed validation against the wire commit-operation schema — wrong field type (e.g. a string `readAt`), unknown `type`, or missing `model`. The whole batch was rejected; the error names the offending operation index and field path.'),
|
|
194
192
|
commit_operation_model_required: wire('validation', 400, false, 'A commit operation is missing its `model`.'),
|
|
195
193
|
commit_operations_ambiguous: wire('validation', 400, false, 'A commit supplied both `operation` and `operations`. Send one or the other, not both.'),
|
|
@@ -206,13 +204,12 @@ export const ERROR_CODES = {
|
|
|
206
204
|
model_not_found: wire('not_found', 404, false, 'No row of this model exists with the requested id. It may have been deleted, or the id may belong to a different environment.'),
|
|
207
205
|
mutate_update_entity_not_found: wire('not_found', 404, false, 'The row targeted by this update does not exist — it may have been deleted since you read it. Re-read before retrying.'),
|
|
208
206
|
task_id_missing: wire('server', 502, true, 'The task-create response arrived without a task id, so the result cannot be used. Retry the request.'),
|
|
209
|
-
// ── data integrity /
|
|
210
|
-
// Emitted when a
|
|
211
|
-
//
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
// the driver's message text onto the wire.
|
|
207
|
+
// ── data integrity / database constraints ──────────────────────────
|
|
208
|
+
// Emitted when a database integrity constraint rejects a write. None are
|
|
209
|
+
// retryable: the same payload re-sent unchanged fails identically, so the
|
|
210
|
+
// client must roll back rather than retry. The server maps the underlying SQL
|
|
211
|
+
// constraint to one of these codes and places the raw constraint, column, and
|
|
212
|
+
// table detail in `details` instead of exposing the driver's message text.
|
|
216
213
|
not_null_violation: wire('validation', 400, false, 'The database rejected the write because a required column was left empty — a not-null constraint. The error details name the column; supply a value and retry.'),
|
|
217
214
|
foreign_key_violation: wire('conflict', 409, false, 'The database rejected the write on a foreign-key constraint: a referenced row does not exist, or the row being deleted is still referenced by others. The error details name the constraint.'),
|
|
218
215
|
unique_violation: wire('conflict', 409, false, 'The write duplicates a value that must be unique — another row already holds it. Choose a different value, or update the existing row.'),
|
|
@@ -276,17 +273,18 @@ export const ERROR_CODES = {
|
|
|
276
273
|
// ── quota / rate limit (429) ──────────────────────────────────────
|
|
277
274
|
quota_exceeded: wire('rate_limit', 429, true, 'Your organization has used up its configured usage quota. Requests will succeed again once the quota resets or the limit is raised.'),
|
|
278
275
|
connection_limit_exceeded: wire('rate_limit', 429, true, 'Too many concurrent WebSocket connections for this principal or organization. Close idle connections, or retry once others drain.'),
|
|
279
|
-
//
|
|
280
|
-
//
|
|
281
|
-
// so one noisy key backs off without affecting
|
|
282
|
-
// `Retry-After` header carries the
|
|
276
|
+
// A per-key request-rate limit — the fast, requests-per-second axis, as
|
|
277
|
+
// opposed to `quota_exceeded`, which is the slower organization-wide usage
|
|
278
|
+
// limit. It is keyed per API key, so one noisy key backs off without affecting
|
|
279
|
+
// the rest of the organization. The `Retry-After` header carries the delay
|
|
280
|
+
// before the next request is allowed.
|
|
283
281
|
rate_limit_exceeded: wire('rate_limit', 429, true, 'This API key is sending requests faster than its rate limit allows. Slow down and retry after the delay in the `Retry-After` header.'),
|
|
284
282
|
// ── server (5xx) ───────────────────────────────────────────────────
|
|
285
283
|
internal_error: wire('server', 500, true, "Something went wrong on Ablo's side — an unexpected server error. It is safe to retry."),
|
|
286
284
|
quota_lookup_failed: wire('server', 503, true, "The server could not load this organization's quota state, so the request was rejected rather than admitted unchecked. Retry shortly."),
|
|
287
|
-
// The
|
|
288
|
-
//
|
|
289
|
-
//
|
|
285
|
+
// The rate-limiter backend was unreachable and this endpoint is configured to
|
|
286
|
+
// fail closed, so the request was rejected rather than admitted unchecked. It
|
|
287
|
+
// is retryable: the next attempt re-probes the backend.
|
|
290
288
|
rate_limiter_unavailable: wire('server', 503, true, 'The rate-limiter backend is unavailable and this endpoint is configured to fail closed; retry shortly.'),
|
|
291
289
|
turn_open_failed: wire('server', 500, true, 'The agent turn could not be opened on the server. It is safe to retry.'),
|
|
292
290
|
turn_close_failed: wire('server', 500, true, 'The agent turn could not be closed cleanly on the server. It is safe to retry the close.'),
|
|
@@ -329,7 +327,7 @@ export const ERROR_CODES = {
|
|
|
329
327
|
undo_entry_invalid: client('client', 'An undo entry failed inverse-op schema validation.'),
|
|
330
328
|
mock_mutation_failed: client('client', 'A mock mutation adapter was configured to fail.'),
|
|
331
329
|
mock_unsupported_operation: client('client', 'A mock adapter received an unsupported operation.'),
|
|
332
|
-
// ── HTTP route edge codes
|
|
330
|
+
// ── HTTP route edge codes ──────────────────────────────────────────
|
|
333
331
|
invalid_body: wire('validation', 400, false, 'The request body was missing, unparseable, or the wrong shape.'),
|
|
334
332
|
invalid_json: wire('validation', 400, false, 'The request body was not valid JSON.'),
|
|
335
333
|
capability_id_required: wire('validation', 400, false, 'A capability id is required for this request.'),
|
|
@@ -359,6 +357,7 @@ export const ERROR_CODES = {
|
|
|
359
357
|
protocol_version_unsupported: wire('transport', 426, false, 'The client sync-protocol version is outside the range this server supports — upgrade the SDK (or the server was rolled back mid-fleet).'),
|
|
360
358
|
database_unreachable: wire('validation', 400, false, "Ablo could not reach this database to check that it can stream replication. The connection string may be wrong, the host may not be reachable from Ablo's servers, or the credentials may not be accepted."),
|
|
361
359
|
database_not_replication_ready: wire('validation', 400, false, 'This database is not set up for logical replication yet. Every failing item — wal_level, the publication, the replication grant, a replica identity — is listed in the error details with its exact fix. `ablo connect` prints the one-time setup; `ablo connect --check` verifies it.'),
|
|
360
|
+
replication_publication_drift: wire('validation', 400, false, 'Your schema maps to tables that are not members of the replication publication, so their changes silently never stream and the source looks frozen. The missing tables and the exact `ALTER PUBLICATION … ADD TABLE …` to add them are in the error details — Ablo never alters your database for you.'),
|
|
362
361
|
query_unknown_relation: wire('validation', 400, false, 'The query references a relation the model does not define. Check the relation name against the schema.'),
|
|
363
362
|
query_relation_target_unknown: wire('schema', 500, false, 'A relation in the query targets a model the schema does not define.'),
|
|
364
363
|
query_invalid_identifier: wire('validation', 400, false, 'The query contained an invalid identifier.'),
|
|
@@ -390,32 +389,33 @@ export const ERROR_CODES = {
|
|
|
390
389
|
invalid_schema: wire('validation', 400, false, 'The submitted schema could not be parsed.'),
|
|
391
390
|
incompatible_change: wire('conflict', 409, false, 'The schema change is incompatible with the schema currently deployed and cannot be applied as-is.'),
|
|
392
391
|
};
|
|
393
|
-
/**
|
|
394
|
-
* `policy:*` family and for any
|
|
395
|
-
*
|
|
392
|
+
/** Looks up the {@link ErrorCodeSpec} for a code. Returns `undefined` for the
|
|
393
|
+
* dynamic `policy:*` family and for any newer code this client does not yet
|
|
394
|
+
* recognize. */
|
|
396
395
|
export function errorCodeSpec(code) {
|
|
397
396
|
return ERROR_CODES[code];
|
|
398
397
|
}
|
|
399
|
-
/**
|
|
400
|
-
* default to non-retryable
|
|
398
|
+
/** Reports whether a code is marked retryable. Unknown and dynamic codes
|
|
399
|
+
* default to non-retryable, so an unrecognized failure is never retried
|
|
400
|
+
* automatically. */
|
|
401
401
|
export function isRetryableCode(code) {
|
|
402
402
|
return errorCodeSpec(code)?.retryable ?? false;
|
|
403
403
|
}
|
|
404
404
|
/**
|
|
405
|
-
*
|
|
406
|
-
*
|
|
405
|
+
* Classifies a code into its {@link RecoveryClass} — the single value the
|
|
406
|
+
* connection layer and the network probe branch on to decide how to recover.
|
|
407
407
|
*
|
|
408
|
-
* The registry
|
|
409
|
-
*
|
|
410
|
-
*
|
|
411
|
-
* - retryable
|
|
412
|
-
* - 403
|
|
413
|
-
* -
|
|
414
|
-
* -
|
|
408
|
+
* The registry is the source of truth. An explicit `recovery` on the code's spec
|
|
409
|
+
* wins; it is set only on the few authentication codes whose remedy the HTTP
|
|
410
|
+
* status cannot reveal. Every other code is derived from its spec:
|
|
411
|
+
* - retryable → `transient`
|
|
412
|
+
* - HTTP 403 → `permission`
|
|
413
|
+
* - remaining `auth` category → `auth_blocked` (the credential-type 401s)
|
|
414
|
+
* - anything else, or unknown → `none`
|
|
415
415
|
*
|
|
416
|
-
*
|
|
417
|
-
*
|
|
418
|
-
*
|
|
416
|
+
* An unknown code, a dynamic `policy:*` code, or a code this client predates
|
|
417
|
+
* (no spec) defaults to `none`, the same safe default as {@link isRetryableCode}:
|
|
418
|
+
* an unrecognized code is never treated as a credential expiry or a sign-out.
|
|
419
419
|
*/
|
|
420
420
|
export function classifyRecovery(code) {
|
|
421
421
|
const spec = errorCodeSpec(code);
|
|
@@ -432,10 +432,9 @@ export function classifyRecovery(code) {
|
|
|
432
432
|
return 'none';
|
|
433
433
|
}
|
|
434
434
|
/**
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
* closed-union discipline `ERROR_CODES` itself uses via `satisfies`.)
|
|
435
|
+
* A compile-time exhaustiveness guard: it forces every {@link RecoveryClass} to
|
|
436
|
+
* be listed here, so adding a class to {@link RECOVERY_CLASSES} without deciding
|
|
437
|
+
* its meaning is a type error rather than a silent gap.
|
|
439
438
|
*/
|
|
440
439
|
const _RECOVERY_CLASS_EXHAUSTIVE = {
|
|
441
440
|
access_credential_expiry: true,
|