@abloatai/ablo 0.25.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/AGENTS.md +5 -3
- package/CHANGELOG.md +34 -0
- package/README.md +104 -88
- package/dist/BaseSyncedStore.d.ts +140 -266
- package/dist/BaseSyncedStore.js +338 -739
- package/dist/Database.d.ts +62 -77
- package/dist/Database.js +106 -127
- package/dist/{ObjectPool.d.ts → InstanceCache.d.ts} +10 -13
- package/dist/{ObjectPool.js → InstanceCache.js} +91 -83
- package/dist/LazyReferenceCollection.d.ts +11 -15
- package/dist/LazyReferenceCollection.js +16 -15
- package/dist/Model.d.ts +37 -52
- package/dist/Model.js +52 -69
- package/dist/ModelRegistry.d.ts +46 -25
- package/dist/ModelRegistry.js +32 -30
- package/dist/NetworkMonitor.d.ts +5 -6
- package/dist/NetworkMonitor.js +6 -7
- package/dist/SyncClient.d.ts +119 -109
- package/dist/SyncClient.js +303 -224
- package/dist/SyncEngineContext.d.ts +1 -3
- package/dist/SyncEngineContext.js +1 -2
- 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 +39 -31
- package/dist/agent/Agent.js +35 -23
- 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} +30 -31
- package/dist/ai-sdk/index.d.ts +25 -22
- package/dist/ai-sdk/index.js +25 -22
- package/dist/ai-sdk/wrap.d.ts +7 -8
- package/dist/ai-sdk/wrap.js +2 -2
- package/dist/auth/credentialPolicy.d.ts +74 -71
- package/dist/auth/credentialPolicy.js +51 -56
- package/dist/auth/credentialSource.d.ts +7 -18
- package/dist/auth/credentialSource.js +10 -18
- package/dist/auth/index.d.ts +59 -58
- package/dist/auth/index.js +34 -40
- 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 +483 -369
- package/dist/client/Ablo.d.ts +107 -836
- package/dist/client/Ablo.js +174 -833
- package/dist/client/ApiClient.d.ts +44 -20
- package/dist/client/ApiClient.js +193 -44
- package/dist/client/auth.d.ts +51 -60
- package/dist/client/auth.js +137 -110
- package/dist/client/claimHeartbeatLoop.d.ts +50 -0
- package/dist/client/claimHeartbeatLoop.js +88 -0
- package/dist/client/consoleLogger.d.ts +35 -0
- package/dist/client/consoleLogger.js +44 -0
- package/dist/client/createInternalComponents.d.ts +14 -17
- package/dist/client/createInternalComponents.js +26 -31
- package/dist/client/createModelProxy.d.ts +130 -120
- package/dist/client/createModelProxy.js +158 -124
- package/dist/client/credentialEndpoint.d.ts +61 -0
- package/dist/client/credentialEndpoint.js +86 -0
- package/dist/client/functionalUpdate.d.ts +29 -27
- package/dist/client/functionalUpdate.js +21 -21
- package/dist/client/hostedEndpoints.d.ts +21 -0
- package/dist/client/hostedEndpoints.js +21 -0
- package/dist/client/httpClient.d.ts +58 -54
- package/dist/client/httpClient.js +29 -31
- package/dist/client/identity.d.ts +15 -20
- package/dist/client/identity.js +49 -59
- package/dist/client/modelRegistration.d.ts +10 -0
- package/dist/client/modelRegistration.js +301 -0
- package/dist/client/options.d.ts +373 -0
- package/dist/client/options.js +6 -0
- package/dist/client/registerDataSource.d.ts +9 -9
- package/dist/client/registerDataSource.js +15 -16
- package/dist/client/resourceTypes.d.ts +333 -0
- package/dist/client/resourceTypes.js +7 -0
- package/dist/client/schemaConfig.d.ts +44 -0
- package/dist/client/schemaConfig.js +176 -0
- package/dist/client/sessionMint.d.ts +17 -13
- package/dist/client/sessionMint.js +26 -31
- package/dist/client/validateAbloOptions.d.ts +12 -14
- package/dist/client/validateAbloOptions.js +9 -10
- package/dist/client/writeOptionsSchema.d.ts +18 -16
- package/dist/client/writeOptionsSchema.js +23 -20
- package/dist/client/wsMutationExecutor.d.ts +28 -0
- package/dist/client/wsMutationExecutor.js +71 -0
- package/dist/context.d.ts +6 -4
- package/dist/context.js +6 -7
- package/dist/coordination/index.d.ts +13 -4
- package/dist/coordination/index.js +29 -4
- package/dist/coordination/schema.d.ts +176 -128
- package/dist/coordination/schema.js +197 -133
- package/dist/coordination/trace.d.ts +9 -11
- package/dist/coordination/trace.js +13 -15
- package/dist/core/DatabaseManager.d.ts +5 -8
- package/dist/core/DatabaseManager.js +38 -40
- package/dist/core/QueryProcessor.d.ts +7 -9
- package/dist/core/QueryProcessor.js +27 -34
- package/dist/core/QueryView.d.ts +17 -5
- package/dist/core/QueryView.js +6 -7
- package/dist/core/StoreManager.d.ts +14 -16
- package/dist/core/StoreManager.js +26 -25
- package/dist/core/ViewRegistry.d.ts +5 -5
- package/dist/core/ViewRegistry.js +4 -4
- package/dist/core/index.d.ts +18 -13
- package/dist/core/index.js +32 -26
- package/dist/core/openIDBWithTimeout.d.ts +38 -36
- package/dist/core/openIDBWithTimeout.js +57 -54
- package/dist/core/queryUtils.d.ts +45 -0
- package/dist/core/queryUtils.js +69 -0
- package/dist/core/storeContract.d.ts +145 -0
- package/dist/core/storeContract.js +12 -0
- package/dist/environment.d.ts +28 -0
- package/dist/environment.js +21 -0
- package/dist/errorCodes.d.ts +118 -101
- package/dist/errorCodes.js +277 -260
- package/dist/errors.d.ts +170 -165
- package/dist/errors.js +161 -151
- package/dist/index.d.ts +30 -27
- package/dist/index.js +90 -82
- package/dist/interfaces/index.d.ts +108 -133
- package/dist/interfaces/index.js +5 -4
- package/dist/keys/index.d.ts +27 -29
- package/dist/keys/index.js +59 -49
- 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 +149 -155
- package/dist/mutators/defineMutators.d.ts +24 -37
- 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 +105 -101
- package/dist/policy/types.js +67 -66
- package/dist/query/client.d.ts +32 -16
- package/dist/query/client.js +103 -72
- package/dist/query/types.d.ts +37 -60
- package/dist/query/types.js +13 -33
- package/dist/react/AbloProvider.d.ts +7 -11
- package/dist/react/AbloProvider.js +24 -17
- package/dist/react/context.d.ts +27 -146
- 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 +17 -15
- 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 +11 -12
- package/dist/react/useMutationFailureListener.d.ts +8 -8
- package/dist/react/useMutationFailureListener.js +9 -9
- package/dist/react/useMutators.d.ts +11 -11
- package/dist/react/useMutators.js +10 -4
- package/dist/react/useReactive.js +2 -3
- package/dist/react/useSyncStatus.d.ts +4 -6
- package/dist/react/useUndoScope.d.ts +7 -9
- package/dist/react/useUndoScope.js +3 -3
- 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 +35 -0
- package/dist/schema/ddlLock.js +46 -0
- 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 +36 -49
- package/dist/schema/generate.d.ts +12 -12
- package/dist/schema/generate.js +12 -12
- package/dist/schema/index.d.ts +5 -4
- package/dist/schema/index.js +29 -21
- package/dist/schema/model.d.ts +121 -146
- package/dist/schema/model.js +24 -35
- package/dist/schema/openapi.d.ts +10 -9
- package/dist/schema/openapi.js +7 -1
- package/dist/schema/queries.d.ts +30 -32
- package/dist/schema/queries.js +24 -25
- package/dist/schema/relation.d.ts +89 -99
- package/dist/schema/relation.js +13 -13
- package/dist/schema/residency.d.ts +38 -0
- package/dist/schema/residency.js +30 -0
- package/dist/schema/roles.d.ts +45 -27
- package/dist/schema/roles.js +52 -21
- package/dist/schema/schema.d.ts +36 -45
- package/dist/schema/schema.js +42 -39
- package/dist/schema/select.d.ts +13 -13
- package/dist/schema/select.js +13 -13
- package/dist/schema/serialize.d.ts +36 -39
- 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} +27 -50
- 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 +31 -26
- package/dist/source/adapter.js +10 -10
- package/dist/source/adapters/drizzle.d.ts +28 -23
- package/dist/source/adapters/drizzle.js +34 -28
- package/dist/source/adapters/kysely.d.ts +27 -25
- package/dist/source/adapters/kysely.js +28 -26
- package/dist/source/adapters/memory.d.ts +8 -7
- package/dist/source/adapters/memory.js +10 -9
- package/dist/source/adapters/prisma.d.ts +13 -12
- package/dist/source/adapters/prisma.js +27 -29
- package/dist/source/conformance.d.ts +18 -11
- package/dist/source/conformance.js +27 -19
- package/dist/source/connector.d.ts +31 -32
- package/dist/source/connector.js +30 -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 +94 -0
- package/dist/source/factory.js +268 -0
- package/dist/source/index.d.ts +10 -462
- package/dist/source/index.js +17 -421
- package/dist/source/migrations.d.ts +9 -9
- package/dist/source/migrations.js +9 -9
- package/dist/source/next.d.ts +10 -11
- package/dist/source/next.js +7 -8
- package/dist/source/pushQueue.d.ts +70 -48
- package/dist/source/pushQueue.js +36 -29
- package/dist/source/signing.d.ts +88 -0
- package/dist/source/signing.js +159 -0
- package/dist/source/types.d.ts +351 -0
- package/dist/source/types.js +43 -0
- package/dist/stores/ObjectStore.d.ts +11 -12
- package/dist/stores/ObjectStore.js +34 -35
- package/dist/stores/ObjectStoreContract.d.ts +12 -15
- package/dist/stores/SyncActionStore.d.ts +8 -12
- package/dist/stores/SyncActionStore.js +77 -46
- package/dist/surface.d.ts +28 -21
- package/dist/surface.js +28 -20
- package/dist/sync/{BootstrapHelper.d.ts → BootstrapFetcher.d.ts} +37 -45
- package/dist/sync/{BootstrapHelper.js → BootstrapFetcher.js} +101 -80
- package/dist/sync/ConnectionManager.d.ts +47 -50
- package/dist/sync/ConnectionManager.js +74 -70
- package/dist/sync/NetworkProbe.d.ts +27 -31
- package/dist/sync/NetworkProbe.js +67 -72
- package/dist/sync/{HydrationCoordinator.d.ts → OnDemandLoader.d.ts} +49 -36
- package/dist/sync/{HydrationCoordinator.js → OnDemandLoader.js} +79 -54
- package/dist/sync/{AreaOfInterestManager.d.ts → SubscriptionManager.d.ts} +45 -59
- package/dist/sync/{AreaOfInterestManager.js → SubscriptionManager.js} +44 -52
- package/dist/sync/SyncWebSocket.d.ts +175 -250
- package/dist/sync/SyncWebSocket.js +431 -769
- package/dist/sync/awaitClaimGrant.d.ts +18 -18
- package/dist/sync/awaitClaimGrant.js +38 -30
- package/dist/sync/bootstrapApply.d.ts +70 -0
- package/dist/sync/bootstrapApply.js +73 -0
- package/dist/sync/commitFrames.d.ts +44 -0
- package/dist/sync/commitFrames.js +94 -0
- package/dist/sync/createClaimStream.d.ts +23 -22
- package/dist/sync/createClaimStream.js +108 -25
- package/dist/sync/createPresenceStream.d.ts +19 -18
- package/dist/sync/createPresenceStream.js +25 -26
- package/dist/sync/createSnapshot.d.ts +13 -17
- package/dist/sync/createSnapshot.js +20 -26
- package/dist/sync/credentialLifecycle.d.ts +175 -0
- package/dist/sync/credentialLifecycle.js +322 -0
- package/dist/sync/deltaPipeline.d.ts +113 -0
- package/dist/sync/deltaPipeline.js +261 -0
- package/dist/sync/groupChange.d.ts +113 -0
- package/dist/sync/groupChange.js +242 -0
- package/dist/sync/heartbeat.d.ts +63 -0
- package/dist/sync/heartbeat.js +91 -0
- package/dist/sync/participants.d.ts +27 -27
- package/dist/sync/schemas.d.ts +3 -2
- package/dist/sync/schemas.js +14 -10
- package/dist/sync/syncCursor.d.ts +40 -0
- package/dist/sync/syncCursor.js +55 -0
- package/dist/sync/syncPlan.d.ts +54 -0
- package/dist/sync/syncPlan.js +50 -0
- package/dist/sync/syncPosition.d.ts +54 -49
- package/dist/sync/syncPosition.js +57 -52
- package/dist/sync/wsFrameHandlers.d.ts +116 -0
- package/dist/sync/wsFrameHandlers.js +374 -0
- package/dist/testing/fixtures/bootstrap.d.ts +21 -17
- package/dist/testing/fixtures/bootstrap.js +12 -6
- package/dist/testing/fixtures/deltas.d.ts +31 -34
- package/dist/testing/fixtures/deltas.js +30 -33
- package/dist/testing/fixtures/models.d.ts +11 -10
- package/dist/testing/fixtures/models.js +12 -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 -18
- package/dist/testing/helpers/{sync-engine-harness.js → syncEngineHarness.js} +14 -11
- package/dist/testing/helpers/wait.d.ts +13 -8
- package/dist/testing/helpers/wait.js +13 -8
- package/dist/testing/index.d.ts +4 -4
- package/dist/testing/index.js +3 -3
- 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 +21 -34
- package/dist/testing/mocks/MockSyncContext.js +16 -45
- package/dist/testing/mocks/MockSyncStore.d.ts +14 -14
- package/dist/testing/mocks/MockSyncStore.js +11 -11
- package/dist/testing/mocks/MockWebSocket.d.ts +28 -24
- package/dist/testing/mocks/MockWebSocket.js +22 -21
- package/dist/transactions/TransactionQueue.d.ts +190 -221
- package/dist/transactions/TransactionQueue.js +424 -822
- package/dist/transactions/TransactionStore.d.ts +20 -0
- package/dist/transactions/TransactionStore.js +53 -0
- package/dist/transactions/UnconfirmedWrites.d.ts +82 -0
- package/dist/transactions/UnconfirmedWrites.js +104 -0
- package/dist/transactions/coalesceRules.d.ts +58 -0
- package/dist/transactions/coalesceRules.js +140 -0
- package/dist/transactions/commitPayload.d.ts +130 -0
- package/dist/transactions/commitPayload.js +143 -0
- package/dist/transactions/deltaConfirmation.d.ts +58 -0
- package/dist/transactions/deltaConfirmation.js +215 -0
- package/dist/transactions/optimisticApply.d.ts +49 -0
- package/dist/transactions/optimisticApply.js +65 -0
- package/dist/transactions/replayValidation.d.ts +99 -0
- package/dist/transactions/replayValidation.js +111 -0
- package/dist/types/global.d.ts +46 -41
- package/dist/types/global.js +20 -19
- package/dist/types/index.d.ts +74 -80
- package/dist/types/index.js +22 -27
- package/dist/types/modelData.d.ts +10 -0
- package/dist/types/modelData.js +9 -0
- package/dist/types/participant.d.ts +20 -0
- package/dist/types/participant.js +10 -0
- package/dist/types/streams.d.ts +216 -209
- 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} +44 -100
- 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 +35 -27
- package/dist/wire/errorEnvelope.js +38 -32
- package/dist/wire/frames.d.ts +150 -67
- package/dist/wire/frames.js +48 -1
- package/dist/wire/index.d.ts +18 -13
- package/dist/wire/index.js +36 -13
- package/dist/wire/listEnvelope.d.ts +16 -23
- package/dist/wire/listEnvelope.js +7 -6
- package/dist/wire/protocol.d.ts +38 -0
- package/dist/wire/protocol.js +38 -0
- package/dist/wire/protocolVersion.d.ts +60 -0
- package/dist/wire/protocolVersion.js +67 -0
- package/docs/api-keys.md +4 -3
- package/docs/coordination.md +59 -0
- package/docs/examples/existing-python-backend.md +3 -3
- package/docs/identity.md +4 -4
- package/docs/integration-guide.md +1 -1
- package/docs/react.md +1 -1
- package/docs/sessions.md +5 -7
- package/package.json +24 -21
- package/dist/ai-sdk/coordinated-tool.d.ts +0 -101
- package/dist/ai-sdk/coordination-context.d.ts +0 -52
- package/dist/client/index.d.ts +0 -36
- package/dist/client/index.js +0 -33
- package/dist/config/index.d.ts +0 -10
- package/dist/config/index.js +0 -12
- package/dist/core/query-utils.d.ts +0 -34
- package/dist/core/query-utils.js +0 -59
- package/dist/interfaces/headless.d.ts +0 -95
- package/dist/interfaces/headless.js +0 -41
- package/dist/query/index.d.ts +0 -6
- package/dist/query/index.js +0 -5
- package/dist/realtime/index.d.ts +0 -10
- package/dist/realtime/index.js +0 -9
- package/dist/schema/plane.d.ts +0 -23
- package/dist/schema/plane.js +0 -19
- package/dist/schema/sync-delta-row.js +0 -103
- package/dist/schema/sync-delta-wire.js +0 -102
- package/dist/server/next.d.ts +0 -51
- package/dist/server/next.js +0 -47
- 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 -1
- package/dist/server/storage-mode.js +0 -18
- package/dist/source/connector-protocol.d.ts +0 -159
- package/dist/source/connector-protocol.js +0 -161
- package/dist/sync/OfflineFlush.d.ts +0 -9
- package/dist/sync/OfflineFlush.js +0 -22
- package/dist/sync/OfflineTransactionStore.d.ts +0 -37
- package/dist/sync/OfflineTransactionStore.js +0 -263
- package/dist/transactions/OptimisticEchoTracker.d.ts +0 -82
- package/dist/transactions/OptimisticEchoTracker.js +0 -104
- package/dist/transactions/index.d.ts +0 -16
- package/dist/transactions/index.js +0 -7
- package/dist/transactions/mutation-error-handler.d.ts +0 -5
- package/dist/transactions/mutation-error-handler.js +0 -39
- package/dist/utils/mobx-setup.d.ts +0 -42
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The types that describe a data source — the shapes exchanged over the wire
|
|
3
|
+
* and the handler interfaces you implement.
|
|
4
|
+
*
|
|
5
|
+
* A data source lets Ablo read from and write to your own database. These
|
|
6
|
+
* types cover the four request kinds Ablo can send ({@link SourceRequest}),
|
|
7
|
+
* the operations and change events they carry ({@link SourceOperation} and
|
|
8
|
+
* {@link SourceEvent}), the list-query and pagination shapes, and the handler
|
|
9
|
+
* and context types your source implements. {@link sourceEventForOperation}
|
|
10
|
+
* builds a change-event marker from an operation.
|
|
11
|
+
*/
|
|
12
|
+
import type { Environment } from '../environment.js';
|
|
13
|
+
/** A scalar value that can appear in a source filter. */
|
|
14
|
+
export type SourcePrimitive = string | number | boolean | null;
|
|
15
|
+
/**
|
|
16
|
+
* A single filter condition on a `list` query: a field paired with a value,
|
|
17
|
+
* or a field, comparison operator, and value. The two-element form is
|
|
18
|
+
* shorthand for equality.
|
|
19
|
+
*/
|
|
20
|
+
export type SourceWhere = readonly [field: string, value: SourcePrimitive] | readonly [
|
|
21
|
+
field: string,
|
|
22
|
+
op: '=' | '!=' | '<' | '<=' | '>' | '>=' | 'IN' | 'NOT IN' | 'IS' | 'IS NOT' | 'LIKE' | 'NOT LIKE' | 'ILIKE' | 'NOT ILIKE',
|
|
23
|
+
value: SourcePrimitive | readonly SourcePrimitive[]
|
|
24
|
+
];
|
|
25
|
+
/** The query Ablo passes to your `list` handler: filters, ordering, a limit, and a pagination cursor. */
|
|
26
|
+
export interface SourceListQuery {
|
|
27
|
+
readonly where?: readonly SourceWhere[];
|
|
28
|
+
readonly limit?: number;
|
|
29
|
+
readonly orderBy?: string;
|
|
30
|
+
readonly order?: 'asc' | 'desc';
|
|
31
|
+
readonly related?: readonly string[];
|
|
32
|
+
/**
|
|
33
|
+
* An opaque cursor returned by a previous `list` call. Your `list` handler
|
|
34
|
+
* decides what it encodes — a page index, a last id, a keyset. Ablo treats
|
|
35
|
+
* it as a black box and hands it back to fetch the next page until your
|
|
36
|
+
* handler stops returning a `nextCursor`.
|
|
37
|
+
*/
|
|
38
|
+
readonly cursor?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The paginated return shape for a `list` handler. A handler may return a
|
|
42
|
+
* plain `Row[]` for a single, unpaginated page, or return this shape to expose
|
|
43
|
+
* a `nextCursor` that Ablo hands back on the following request.
|
|
44
|
+
*/
|
|
45
|
+
export interface SourceListPage<Row> {
|
|
46
|
+
readonly rows: readonly Row[];
|
|
47
|
+
readonly nextCursor?: string;
|
|
48
|
+
}
|
|
49
|
+
/** What a `list` handler returns: either a plain array of rows or a {@link SourceListPage}. */
|
|
50
|
+
export type SourceListResult<Row> = readonly Row[] | SourceListPage<Row>;
|
|
51
|
+
/**
|
|
52
|
+
* The scope of a source request: who is asking and what they are allowed to
|
|
53
|
+
* see. Ablo attaches this so your `authorize` and model handlers can reject
|
|
54
|
+
* calls that fall outside the participant's permitted sync groups.
|
|
55
|
+
*
|
|
56
|
+
* It is advisory. Because the canonical data lives in your database, your
|
|
57
|
+
* handlers are the only place that can actually enforce these limits.
|
|
58
|
+
*/
|
|
59
|
+
export interface SourceRequestContext {
|
|
60
|
+
readonly participantId?: string;
|
|
61
|
+
readonly participantKind?: 'user' | 'agent' | 'system';
|
|
62
|
+
readonly organizationId?: string;
|
|
63
|
+
readonly requiredSyncGroups?: readonly string[];
|
|
64
|
+
/**
|
|
65
|
+
* Whether this request runs in production or sandbox mode. Branch your
|
|
66
|
+
* handlers on it — for example, read and write a separate sandbox database
|
|
67
|
+
* when `mode === 'sandbox'` — so sandbox traffic exercises the same code
|
|
68
|
+
* against isolated data. Keeping the two apart is your handler's
|
|
69
|
+
* responsibility, since your database holds the canonical rows.
|
|
70
|
+
*
|
|
71
|
+
* Defaults to `'production'` when omitted.
|
|
72
|
+
*/
|
|
73
|
+
readonly mode?: Environment;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* A single change Ablo asks your source to apply — a create, update, delete,
|
|
77
|
+
* archive, or unarchive of one row of `model`. Operations arrive in your
|
|
78
|
+
* `commit` handler through {@link SourceCommitParams}. `onStale` says what to
|
|
79
|
+
* do when the row changed since it was read at `readAt`.
|
|
80
|
+
*/
|
|
81
|
+
export interface SourceOperation {
|
|
82
|
+
readonly type: 'CREATE' | 'UPDATE' | 'DELETE' | 'ARCHIVE' | 'UNARCHIVE';
|
|
83
|
+
readonly model: string;
|
|
84
|
+
readonly id?: string | null;
|
|
85
|
+
readonly input?: Record<string, unknown> | null;
|
|
86
|
+
readonly transactionId?: string | null;
|
|
87
|
+
readonly readAt?: number | null;
|
|
88
|
+
readonly onStale?: 'reject' | 'overwrite' | 'notify' | null;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* A computed change to one row, ready to append to the change log. Your
|
|
92
|
+
* `commit` handler may return these directly, or return rows and let Ablo
|
|
93
|
+
* derive the deltas from them.
|
|
94
|
+
*/
|
|
95
|
+
export interface SourceDelta {
|
|
96
|
+
readonly model: string;
|
|
97
|
+
readonly id: string;
|
|
98
|
+
readonly type: SourceOperation['type'];
|
|
99
|
+
readonly data?: Record<string, unknown> | null;
|
|
100
|
+
readonly transactionId?: string | null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* A change that already happened in your database. Your `events` handler
|
|
104
|
+
* returns these, and Ablo appends them to the `sync_deltas` change log and
|
|
105
|
+
* fans them out to connected clients, exactly as it would a change made
|
|
106
|
+
* through the SDK.
|
|
107
|
+
*
|
|
108
|
+
* Your handler can return the whole outbox unfiltered. Ablo deduplicates on
|
|
109
|
+
* the stable `id` and uses `clientTxId` to drop echoes of changes the SDK
|
|
110
|
+
* already committed. If that earlier commit never landed, the same outbox
|
|
111
|
+
* event repairs the gap on the next poll or push.
|
|
112
|
+
*/
|
|
113
|
+
export interface SourceEvent {
|
|
114
|
+
/**
|
|
115
|
+
* A globally unique event id from your outbox. Ablo uses it for replay
|
|
116
|
+
* protection, so re-delivering the same id is a no-op.
|
|
117
|
+
*/
|
|
118
|
+
readonly id: string;
|
|
119
|
+
readonly model: string;
|
|
120
|
+
readonly entityId: string;
|
|
121
|
+
readonly type: SourceOperation['type'];
|
|
122
|
+
readonly data?: Record<string, unknown> | null;
|
|
123
|
+
/**
|
|
124
|
+
* The tenant this event belongs to. Populate it from the row's organization
|
|
125
|
+
* column for multi-tenant data; a single-tenant source may omit it and let
|
|
126
|
+
* the poller fall back to its configured default. It drives fan-out: clients
|
|
127
|
+
* in `org:${organizationId}` receive the resulting change.
|
|
128
|
+
*/
|
|
129
|
+
readonly organizationId?: string;
|
|
130
|
+
/**
|
|
131
|
+
* The originating SDK commit id, when you know it. If your outbox records the
|
|
132
|
+
* `clientTxId` that Ablo passed into the matching `commit` handler, echo it
|
|
133
|
+
* back here and Ablo will skip events whose commit already produced a change.
|
|
134
|
+
* Leave it unset for changes made outside the SDK, such as cron jobs, batch
|
|
135
|
+
* imports, or manual edits.
|
|
136
|
+
*/
|
|
137
|
+
readonly clientTxId?: string;
|
|
138
|
+
/**
|
|
139
|
+
* When the change occurred in your database. Optional and used only as an
|
|
140
|
+
* ordering hint; Ablo trusts the order of your handler's response over this
|
|
141
|
+
* field.
|
|
142
|
+
*/
|
|
143
|
+
readonly occurredAt?: number;
|
|
144
|
+
}
|
|
145
|
+
/** Inputs to {@link sourceEventForOperation}. */
|
|
146
|
+
export interface SourceEventForOperationOptions {
|
|
147
|
+
/**
|
|
148
|
+
* The stable id from your outbox table. It is Ablo's replay-protection key,
|
|
149
|
+
* so retries must return the same id.
|
|
150
|
+
*/
|
|
151
|
+
readonly eventId: string;
|
|
152
|
+
readonly operation: SourceOperation;
|
|
153
|
+
/**
|
|
154
|
+
* The committed row id. Defaults to `operation.id`; pass it explicitly for
|
|
155
|
+
* creates where the database assigns the id inside the transaction.
|
|
156
|
+
*/
|
|
157
|
+
readonly entityId?: string;
|
|
158
|
+
/**
|
|
159
|
+
* The row's payload after the write. Pass `null` for a delete. When omitted,
|
|
160
|
+
* the event carries no payload, which is valid but leaves less for clients to
|
|
161
|
+
* hydrate from in realtime.
|
|
162
|
+
*/
|
|
163
|
+
readonly data?: Record<string, unknown> | null;
|
|
164
|
+
/**
|
|
165
|
+
* The commit request's idempotency key. Echoing it lets Ablo drop echoes of
|
|
166
|
+
* a change the SDK already committed, while still letting the outbox event
|
|
167
|
+
* repair that change if it never landed.
|
|
168
|
+
*/
|
|
169
|
+
readonly clientTxId?: string;
|
|
170
|
+
readonly organizationId?: string;
|
|
171
|
+
readonly occurredAt?: number | Date;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Build the {@link SourceEvent} marker you should record in your outbox table,
|
|
175
|
+
* within the same transaction as the row change it describes.
|
|
176
|
+
*
|
|
177
|
+
* This helper only shapes the marker; it does not persist anything. Writing the
|
|
178
|
+
* returned event through your ORM or raw SQL keeps every source emitting the
|
|
179
|
+
* fields Ablo expects when it reconciles the change.
|
|
180
|
+
*/
|
|
181
|
+
export declare function sourceEventForOperation(options: SourceEventForOperationOptions): SourceEvent;
|
|
182
|
+
/** What your `commit` handler returns after applying operations. */
|
|
183
|
+
export interface SourceCommitResult<Row = Record<string, unknown>> {
|
|
184
|
+
/**
|
|
185
|
+
* The rows as they stand after the write. Ablo uses them to update its
|
|
186
|
+
* realtime projections and append the resulting changes.
|
|
187
|
+
*/
|
|
188
|
+
readonly rows?: readonly Row[];
|
|
189
|
+
/**
|
|
190
|
+
* Explicit changes, for sources that already compute them. Most sources can
|
|
191
|
+
* return rows instead and let Ablo derive the change payload.
|
|
192
|
+
*/
|
|
193
|
+
readonly deltas?: readonly SourceDelta[];
|
|
194
|
+
}
|
|
195
|
+
/** The arguments passed to a top-level {@link SourceCommitHandler}. */
|
|
196
|
+
export interface SourceCommitParams<TAuth = unknown> {
|
|
197
|
+
readonly operations: readonly SourceOperation[];
|
|
198
|
+
readonly clientTxId?: string;
|
|
199
|
+
readonly context: SourceHandlerContext<TAuth>;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* The operation an API key is permitted to invoke, one per request kind:
|
|
203
|
+
* `load` and `list` read, `commit` writes, and `events` reads the change feed.
|
|
204
|
+
* A key carries the set of scopes it is allowed to use.
|
|
205
|
+
*/
|
|
206
|
+
export type SourceScope = 'load' | 'list' | 'commit' | 'events';
|
|
207
|
+
/** What your `events` handler returns: a batch of changes and an optional next cursor. */
|
|
208
|
+
export interface SourceEventsResult {
|
|
209
|
+
readonly events: readonly SourceEvent[];
|
|
210
|
+
/**
|
|
211
|
+
* The cursor for the next poll. When omitted, Ablo treats the feed as fully
|
|
212
|
+
* drained for this round and reuses the last event's cursor, or the initial
|
|
213
|
+
* cursor, on the following call.
|
|
214
|
+
*/
|
|
215
|
+
readonly nextCursor?: string;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Your handler for the `events` request. Return the changes since `cursor` so
|
|
219
|
+
* Ablo can append and fan them out. See {@link SourceEvent}.
|
|
220
|
+
*/
|
|
221
|
+
export type SourceEventsHandler<TAuth = unknown> = (params: {
|
|
222
|
+
/**
|
|
223
|
+
* The cursor from a previous `events` call, or undefined on the first poll of
|
|
224
|
+
* a newly connected source. You decide what it encodes — a last event id, a
|
|
225
|
+
* timestamp, a log sequence number.
|
|
226
|
+
*/
|
|
227
|
+
readonly cursor?: string;
|
|
228
|
+
/**
|
|
229
|
+
* A suggested upper bound on how many events to return. You may return fewer;
|
|
230
|
+
* returning many more risks tripping Ablo's per-poll cap.
|
|
231
|
+
*/
|
|
232
|
+
readonly limit?: number;
|
|
233
|
+
readonly context: SourceHandlerContext<TAuth>;
|
|
234
|
+
}) => Promise<SourceEventsResult> | SourceEventsResult;
|
|
235
|
+
/** The request being authorized, passed to a function-form {@link SourceApiKey} or an `authorize` hook. */
|
|
236
|
+
export interface SourceAuthorizeContext {
|
|
237
|
+
readonly request: Request;
|
|
238
|
+
readonly body: unknown;
|
|
239
|
+
readonly rawBody: string;
|
|
240
|
+
}
|
|
241
|
+
export interface SourceHandlerContext<TAuth = unknown> {
|
|
242
|
+
readonly auth: TAuth;
|
|
243
|
+
readonly request: Request;
|
|
244
|
+
/**
|
|
245
|
+
* The `webhook-id` from the signed request, globally unique per the
|
|
246
|
+
* Standard Webhooks specification. Dedupe by this id to defend against
|
|
247
|
+
* replay: Ablo does not deduplicate at the source-handler boundary.
|
|
248
|
+
* Commit idempotency keys on `clientTxId`, and event replay protection
|
|
249
|
+
* keys on the outbox event `id`.
|
|
250
|
+
*/
|
|
251
|
+
readonly messageId?: string;
|
|
252
|
+
readonly signedAt?: number;
|
|
253
|
+
/**
|
|
254
|
+
* The scope context Ablo attached to this request, naming the participant
|
|
255
|
+
* and the sync groups they are allowed to see. Present when the host
|
|
256
|
+
* opted into scope-aware requests. Use it in `authorize` to reject
|
|
257
|
+
* out-of-scope calls, and in `list` and `load` to filter rows down to
|
|
258
|
+
* what the participant may see.
|
|
259
|
+
*
|
|
260
|
+
* Absent for requests made without scope context, such as tests or
|
|
261
|
+
* single-tenant deployments that do not need scoped fan-out.
|
|
262
|
+
*/
|
|
263
|
+
readonly scope?: SourceRequestContext;
|
|
264
|
+
}
|
|
265
|
+
export interface SourceModelHandlers<Row, CreateInput, TAuth = unknown> {
|
|
266
|
+
load?(params: {
|
|
267
|
+
readonly id: string;
|
|
268
|
+
readonly context: SourceHandlerContext<TAuth>;
|
|
269
|
+
}): Promise<Row | null> | Row | null;
|
|
270
|
+
list?(params: {
|
|
271
|
+
readonly query: SourceListQuery;
|
|
272
|
+
readonly context: SourceHandlerContext<TAuth>;
|
|
273
|
+
}): Promise<SourceListResult<Row>> | SourceListResult<Row>;
|
|
274
|
+
/**
|
|
275
|
+
* Apply one or more operations for this model within your own database
|
|
276
|
+
* transaction. Your handler must be idempotent on the operation and its
|
|
277
|
+
* `clientTxId`, so that a retried commit does not apply the change twice.
|
|
278
|
+
*/
|
|
279
|
+
commit?(params: {
|
|
280
|
+
readonly operations: readonly SourceOperation[];
|
|
281
|
+
readonly clientTxId?: string;
|
|
282
|
+
readonly context: SourceHandlerContext<TAuth>;
|
|
283
|
+
}): Promise<SourceCommitResult<Row>> | SourceCommitResult<Row>;
|
|
284
|
+
}
|
|
285
|
+
export type SourceCommitHandler<TAuth = unknown> = (params: SourceCommitParams<TAuth>) => Promise<SourceCommitResult> | SourceCommitResult;
|
|
286
|
+
export type SourceApiKey = string | ((context: SourceAuthorizeContext) => Promise<string> | string);
|
|
287
|
+
export interface SourceLoadRequest {
|
|
288
|
+
readonly type: 'load';
|
|
289
|
+
readonly model: string;
|
|
290
|
+
readonly id: string;
|
|
291
|
+
readonly scope?: SourceRequestContext;
|
|
292
|
+
}
|
|
293
|
+
export interface SourceListRequest {
|
|
294
|
+
readonly type: 'list';
|
|
295
|
+
readonly model: string;
|
|
296
|
+
readonly query?: SourceListQuery;
|
|
297
|
+
readonly scope?: SourceRequestContext;
|
|
298
|
+
}
|
|
299
|
+
export interface SourceCommitRequest {
|
|
300
|
+
readonly type: 'commit';
|
|
301
|
+
/**
|
|
302
|
+
* Optional single-model hint. Omit for cross-model commits; top-level
|
|
303
|
+
* `commit` receives the whole operation array unchanged.
|
|
304
|
+
*/
|
|
305
|
+
readonly model?: string;
|
|
306
|
+
readonly operations: readonly SourceOperation[];
|
|
307
|
+
readonly clientTxId?: string;
|
|
308
|
+
readonly scope?: SourceRequestContext;
|
|
309
|
+
}
|
|
310
|
+
export interface SourceEventsRequest {
|
|
311
|
+
readonly type: 'events';
|
|
312
|
+
readonly cursor?: string;
|
|
313
|
+
readonly limit?: number;
|
|
314
|
+
readonly scope?: SourceRequestContext;
|
|
315
|
+
}
|
|
316
|
+
export type SourceRequest = SourceLoadRequest | SourceListRequest | SourceCommitRequest | SourceEventsRequest;
|
|
317
|
+
export type SourceResponse<Row = Record<string, unknown>> = {
|
|
318
|
+
readonly row: Row | null;
|
|
319
|
+
} | {
|
|
320
|
+
readonly rows: readonly Row[];
|
|
321
|
+
readonly nextCursor?: string;
|
|
322
|
+
} | {
|
|
323
|
+
readonly rows?: readonly Row[];
|
|
324
|
+
readonly deltas?: readonly SourceDelta[];
|
|
325
|
+
};
|
|
326
|
+
export type DataSourcePrimitive = SourcePrimitive;
|
|
327
|
+
export type DataSourceWhere = SourceWhere;
|
|
328
|
+
export type DataSourceListQuery = SourceListQuery;
|
|
329
|
+
export type DataSourceListPage<Row> = SourceListPage<Row>;
|
|
330
|
+
export type DataSourceListResult<Row> = SourceListResult<Row>;
|
|
331
|
+
export type DataSourceRequestContext = SourceRequestContext;
|
|
332
|
+
export type DataSourceOperation = SourceOperation;
|
|
333
|
+
export type DataSourceDelta = SourceDelta;
|
|
334
|
+
export type DataSourceEvent = SourceEvent;
|
|
335
|
+
export type DataSourceEventForOperationOptions = SourceEventForOperationOptions;
|
|
336
|
+
export type DataSourceCommitResult<Row = Record<string, unknown>> = SourceCommitResult<Row>;
|
|
337
|
+
export type DataSourceCommitParams<TAuth = unknown> = SourceCommitParams<TAuth>;
|
|
338
|
+
export type DataSourceScope = SourceScope;
|
|
339
|
+
export type DataSourceEventsResult = SourceEventsResult;
|
|
340
|
+
export type DataSourceEventsHandler<TAuth = unknown> = SourceEventsHandler<TAuth>;
|
|
341
|
+
export type DataSourceAuthorizeContext = SourceAuthorizeContext;
|
|
342
|
+
export type DataSourceHandlerContext<TAuth = unknown> = SourceHandlerContext<TAuth>;
|
|
343
|
+
export type DataSourceModelHandlers<Row, CreateInput, TAuth = unknown> = SourceModelHandlers<Row, CreateInput, TAuth>;
|
|
344
|
+
export type DataSourceCommitHandler<TAuth = unknown> = SourceCommitHandler<TAuth>;
|
|
345
|
+
export type DataSourceApiKey = SourceApiKey;
|
|
346
|
+
export type DataSourceLoadRequest = SourceLoadRequest;
|
|
347
|
+
export type DataSourceListRequest = SourceListRequest;
|
|
348
|
+
export type DataSourceCommitRequest = SourceCommitRequest;
|
|
349
|
+
export type DataSourceEventsRequest = SourceEventsRequest;
|
|
350
|
+
export type DataSourceRequest = SourceRequest;
|
|
351
|
+
export type DataSourceResponse<Row = Record<string, unknown>> = SourceResponse<Row>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The types that describe a data source — the shapes exchanged over the wire
|
|
3
|
+
* and the handler interfaces you implement.
|
|
4
|
+
*
|
|
5
|
+
* A data source lets Ablo read from and write to your own database. These
|
|
6
|
+
* types cover the four request kinds Ablo can send ({@link SourceRequest}),
|
|
7
|
+
* the operations and change events they carry ({@link SourceOperation} and
|
|
8
|
+
* {@link SourceEvent}), the list-query and pagination shapes, and the handler
|
|
9
|
+
* and context types your source implements. {@link sourceEventForOperation}
|
|
10
|
+
* builds a change-event marker from an operation.
|
|
11
|
+
*/
|
|
12
|
+
import { AbloValidationError } from '../errors.js';
|
|
13
|
+
/**
|
|
14
|
+
* Build the {@link SourceEvent} marker you should record in your outbox table,
|
|
15
|
+
* within the same transaction as the row change it describes.
|
|
16
|
+
*
|
|
17
|
+
* This helper only shapes the marker; it does not persist anything. Writing the
|
|
18
|
+
* returned event through your ORM or raw SQL keeps every source emitting the
|
|
19
|
+
* fields Ablo expects when it reconciles the change.
|
|
20
|
+
*/
|
|
21
|
+
export function sourceEventForOperation(options) {
|
|
22
|
+
const entityId = options.entityId ?? options.operation.id;
|
|
23
|
+
if (typeof entityId !== 'string' || entityId.length === 0) {
|
|
24
|
+
throw new AbloValidationError('sourceEventForOperation requires operation.id or an explicit entityId', { code: 'source_event_invalid' });
|
|
25
|
+
}
|
|
26
|
+
const occurredAt = normalizeEventOccurredAt(options.occurredAt);
|
|
27
|
+
return {
|
|
28
|
+
id: options.eventId,
|
|
29
|
+
model: options.operation.model,
|
|
30
|
+
entityId,
|
|
31
|
+
type: options.operation.type,
|
|
32
|
+
...(options.data !== undefined ? { data: options.data } : {}),
|
|
33
|
+
...(options.organizationId ? { organizationId: options.organizationId } : {}),
|
|
34
|
+
...(options.clientTxId ? { clientTxId: options.clientTxId } : {}),
|
|
35
|
+
...(occurredAt !== undefined ? { occurredAt } : {}),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function normalizeEventOccurredAt(value) {
|
|
39
|
+
if (value === undefined)
|
|
40
|
+
return undefined;
|
|
41
|
+
const timestamp = value instanceof Date ? value.getTime() : value;
|
|
42
|
+
return Number.isFinite(timestamp) ? timestamp : undefined;
|
|
43
|
+
}
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Abstract base class for all store implementations.
|
|
5
|
-
* Provides the interface for storing and retrieving models from IndexedDB.
|
|
6
|
-
* Uses native IndexedDB for maximum performance (no wrapper overhead).
|
|
2
|
+
* The IndexedDB-backed object store: durable, per-model record storage for
|
|
3
|
+
* the browser. See {@link ObjectStore}.
|
|
7
4
|
*/
|
|
8
|
-
import { ModelMetadata } from '../types/index.js';
|
|
5
|
+
import type { ModelMetadata } from '../types/index.js';
|
|
9
6
|
import type { ObjectStoreContract } from './ObjectStoreContract.js';
|
|
10
7
|
/**
|
|
11
|
-
*
|
|
8
|
+
* An IndexedDB-backed store holding the records of a single model.
|
|
12
9
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
10
|
+
* It implements {@link ObjectStoreContract}, the shared surface that
|
|
11
|
+
* {@link InMemoryObjectStore} also satisfies, so callers can hold either
|
|
12
|
+
* implementation behind one type. Because both are checked against the same
|
|
13
|
+
* interface, any drift between them surfaces as a typecheck error rather
|
|
14
|
+
* than a runtime surprise.
|
|
17
15
|
*
|
|
18
|
-
*
|
|
16
|
+
* The store talks to the native IndexedDB API directly and uses relaxed
|
|
17
|
+
* transaction durability on writes.
|
|
19
18
|
*/
|
|
20
19
|
export declare class ObjectStore implements ObjectStoreContract {
|
|
21
20
|
protected db: IDBDatabase;
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Abstract base class for all store implementations.
|
|
5
|
-
* Provides the interface for storing and retrieving models from IndexedDB.
|
|
6
|
-
* Uses native IndexedDB for maximum performance (no wrapper overhead).
|
|
2
|
+
* The IndexedDB-backed object store: durable, per-model record storage for
|
|
3
|
+
* the browser. See {@link ObjectStore}.
|
|
7
4
|
*/
|
|
8
5
|
/**
|
|
9
|
-
*
|
|
6
|
+
* An IndexedDB-backed store holding the records of a single model.
|
|
10
7
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
8
|
+
* It implements {@link ObjectStoreContract}, the shared surface that
|
|
9
|
+
* {@link InMemoryObjectStore} also satisfies, so callers can hold either
|
|
10
|
+
* implementation behind one type. Because both are checked against the same
|
|
11
|
+
* interface, any drift between them surfaces as a typecheck error rather
|
|
12
|
+
* than a runtime surprise.
|
|
15
13
|
*
|
|
16
|
-
*
|
|
14
|
+
* The store talks to the native IndexedDB API directly and uses relaxed
|
|
15
|
+
* transaction durability on writes.
|
|
17
16
|
*/
|
|
18
17
|
export class ObjectStore {
|
|
19
18
|
db;
|
|
@@ -45,7 +44,7 @@ export class ObjectStore {
|
|
|
45
44
|
// but we can check if the database object is still valid
|
|
46
45
|
try {
|
|
47
46
|
// Accessing objectStoreNames will throw if the database is closed
|
|
48
|
-
|
|
47
|
+
void this.db.objectStoreNames;
|
|
49
48
|
return true;
|
|
50
49
|
}
|
|
51
50
|
catch (error) {
|
|
@@ -70,9 +69,9 @@ export class ObjectStore {
|
|
|
70
69
|
});
|
|
71
70
|
const store = tx.objectStore(this.storeName);
|
|
72
71
|
const request = store.put(data);
|
|
73
|
-
tx.oncomplete = () => resolve();
|
|
74
|
-
tx.onerror = () => reject(tx.error || new Error('IndexedDB transaction error'));
|
|
75
|
-
request.onerror = () => reject(request.error || new Error('IndexedDB request error'));
|
|
72
|
+
tx.oncomplete = () => { resolve(); };
|
|
73
|
+
tx.onerror = () => { reject(tx.error || new Error('IndexedDB transaction error')); };
|
|
74
|
+
request.onerror = () => { reject(request.error || new Error('IndexedDB request error')); };
|
|
76
75
|
}
|
|
77
76
|
catch (error) {
|
|
78
77
|
// Propagate failure so callers do not continue with inconsistent state
|
|
@@ -92,8 +91,8 @@ export class ObjectStore {
|
|
|
92
91
|
const tx = this.db.transaction([this.storeName], 'readonly');
|
|
93
92
|
const store = tx.objectStore(this.storeName);
|
|
94
93
|
const request = store.get(id);
|
|
95
|
-
request.onsuccess = () => resolve(request.result);
|
|
96
|
-
request.onerror = () => reject(request.error);
|
|
94
|
+
request.onsuccess = () => { resolve(request.result); };
|
|
95
|
+
request.onerror = () => { reject(request.error); };
|
|
97
96
|
}
|
|
98
97
|
catch (error) {
|
|
99
98
|
resolve(undefined);
|
|
@@ -132,7 +131,7 @@ export class ObjectStore {
|
|
|
132
131
|
}
|
|
133
132
|
};
|
|
134
133
|
}
|
|
135
|
-
tx.onerror = () => reject(tx.error);
|
|
134
|
+
tx.onerror = () => { reject(tx.error); };
|
|
136
135
|
}
|
|
137
136
|
catch (error) {
|
|
138
137
|
resolve(results);
|
|
@@ -151,8 +150,8 @@ export class ObjectStore {
|
|
|
151
150
|
const tx = this.db.transaction([this.storeName], 'readonly');
|
|
152
151
|
const store = tx.objectStore(this.storeName);
|
|
153
152
|
const request = store.getAll();
|
|
154
|
-
request.onsuccess = () => resolve(request.result || []);
|
|
155
|
-
request.onerror = () => reject(request.error);
|
|
153
|
+
request.onsuccess = () => { resolve(request.result || []); };
|
|
154
|
+
request.onerror = () => { reject(request.error); };
|
|
156
155
|
}
|
|
157
156
|
catch (error) {
|
|
158
157
|
resolve([]);
|
|
@@ -174,9 +173,9 @@ export class ObjectStore {
|
|
|
174
173
|
});
|
|
175
174
|
const store = tx.objectStore(this.storeName);
|
|
176
175
|
const request = store.delete(id);
|
|
177
|
-
tx.oncomplete = () => resolve();
|
|
178
|
-
tx.onerror = () => reject(tx.error || new Error('IndexedDB transaction error'));
|
|
179
|
-
request.onerror = () => reject(request.error || new Error('IndexedDB request error'));
|
|
176
|
+
tx.oncomplete = () => { resolve(); };
|
|
177
|
+
tx.onerror = () => { reject(tx.error || new Error('IndexedDB transaction error')); };
|
|
178
|
+
request.onerror = () => { reject(request.error || new Error('IndexedDB request error')); };
|
|
180
179
|
}
|
|
181
180
|
catch (error) {
|
|
182
181
|
reject(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -210,9 +209,9 @@ export class ObjectStore {
|
|
|
210
209
|
});
|
|
211
210
|
const store = tx.objectStore(this.storeName);
|
|
212
211
|
const request = store.clear();
|
|
213
|
-
tx.oncomplete = () => resolve();
|
|
214
|
-
tx.onerror = () => reject(tx.error || new Error('IndexedDB transaction error'));
|
|
215
|
-
request.onerror = () => reject(request.error || new Error('IndexedDB request error'));
|
|
212
|
+
tx.oncomplete = () => { resolve(); };
|
|
213
|
+
tx.onerror = () => { reject(tx.error || new Error('IndexedDB transaction error')); };
|
|
214
|
+
request.onerror = () => { reject(request.error || new Error('IndexedDB request error')); };
|
|
216
215
|
}
|
|
217
216
|
catch (error) {
|
|
218
217
|
reject(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -231,8 +230,8 @@ export class ObjectStore {
|
|
|
231
230
|
const tx = this.db.transaction([this.storeName], 'readonly');
|
|
232
231
|
const store = tx.objectStore(this.storeName);
|
|
233
232
|
const request = store.count();
|
|
234
|
-
request.onsuccess = () => resolve(request.result);
|
|
235
|
-
request.onerror = () => reject(request.error);
|
|
233
|
+
request.onsuccess = () => { resolve(request.result); };
|
|
234
|
+
request.onerror = () => { reject(request.error); };
|
|
236
235
|
}
|
|
237
236
|
catch (error) {
|
|
238
237
|
resolve(0);
|
|
@@ -252,8 +251,8 @@ export class ObjectStore {
|
|
|
252
251
|
const store = tx.objectStore(this.storeName);
|
|
253
252
|
const index = store.index(indexName);
|
|
254
253
|
const request = index.getAll(value);
|
|
255
|
-
request.onsuccess = () => resolve(request.result);
|
|
256
|
-
request.onerror = () => reject(request.error);
|
|
254
|
+
request.onsuccess = () => { resolve(request.result); };
|
|
255
|
+
request.onerror = () => { reject(request.error); };
|
|
257
256
|
}
|
|
258
257
|
catch (error) {
|
|
259
258
|
resolve([]);
|
|
@@ -292,7 +291,7 @@ export class ObjectStore {
|
|
|
292
291
|
});
|
|
293
292
|
resolve(filtered);
|
|
294
293
|
};
|
|
295
|
-
request.onerror = () => reject(request.error);
|
|
294
|
+
request.onerror = () => { reject(request.error); };
|
|
296
295
|
}
|
|
297
296
|
catch (error) {
|
|
298
297
|
resolve([]);
|
|
@@ -312,8 +311,8 @@ export class ObjectStore {
|
|
|
312
311
|
const store = tx.objectStore(this.storeName);
|
|
313
312
|
const index = store.index(indexName);
|
|
314
313
|
const request = index.get(value);
|
|
315
|
-
request.onsuccess = () => resolve(request.result);
|
|
316
|
-
request.onerror = () => reject(request.error);
|
|
314
|
+
request.onsuccess = () => { resolve(request.result); };
|
|
315
|
+
request.onerror = () => { reject(request.error); };
|
|
317
316
|
}
|
|
318
317
|
catch (error) {
|
|
319
318
|
resolve(undefined);
|
|
@@ -333,8 +332,8 @@ export class ObjectStore {
|
|
|
333
332
|
const store = tx.objectStore(this.storeName);
|
|
334
333
|
const index = store.index(indexName);
|
|
335
334
|
const request = index.count(value);
|
|
336
|
-
request.onsuccess = () => resolve(request.result);
|
|
337
|
-
request.onerror = () => reject(request.error);
|
|
335
|
+
request.onsuccess = () => { resolve(request.result); };
|
|
336
|
+
request.onerror = () => { reject(request.error); };
|
|
338
337
|
}
|
|
339
338
|
catch (error) {
|
|
340
339
|
resolve(0);
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The shared interface for record-shaped object stores. Two implementations
|
|
3
|
+
* satisfy it:
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - {@link InMemoryObjectStore} —
|
|
5
|
+
* - {@link ObjectStore} — backed by IndexedDB, for durable persistence in
|
|
6
|
+
* the browser.
|
|
7
|
+
* - {@link InMemoryObjectStore} — backed by a Map, for tests and
|
|
8
|
+
* environments without IndexedDB.
|
|
7
9
|
*
|
|
8
|
-
* Both expose the same
|
|
9
|
-
* `delete
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* Centralizing the types here means a future drift between the two
|
|
16
|
-
* stores trips a typecheck error at the implementor, not silently in
|
|
17
|
-
* a caller. This replaced ad-hoc `as unknown as ReturnType<...>`
|
|
18
|
-
* casts in `Database.ts` that bridged the two classes.
|
|
10
|
+
* Both expose the same asynchronous surface — `put`, `get`, `getAll`,
|
|
11
|
+
* `delete`, `getAllFromIndex`, `clear`, and `markAsClosing` — so callers
|
|
12
|
+
* work against this interface and never branch on which concrete store they
|
|
13
|
+
* hold. Because both implementations are checked against one interface, any
|
|
14
|
+
* drift between them surfaces as a typecheck error at the store rather than
|
|
15
|
+
* a silent failure in a caller.
|
|
19
16
|
*/
|
|
20
17
|
export interface ObjectStoreContract {
|
|
21
18
|
/** Insert or update a record. The record must carry an `id` field. */
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Stores and manages sync actions received from the server.
|
|
5
|
-
* Critical for delta sync and maintaining sync state consistency.
|
|
2
|
+
* A local, IndexedDB-backed store for the sync actions (change deltas) the
|
|
3
|
+
* server sends. See {@link SyncActionStore}.
|
|
6
4
|
*/
|
|
7
|
-
import { SyncAction } from '../types/index.js';
|
|
5
|
+
import type { SyncAction } from '../types/index.js';
|
|
8
6
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* - Enables rewind/replay for conflict resolution
|
|
15
|
-
* - Maintains sync watermark
|
|
7
|
+
* Stores the sync actions (the change deltas) the server sends, keyed by
|
|
8
|
+
* their sync id, and tracks which ones have been applied. It keeps a
|
|
9
|
+
* watermark of the last applied id, holds not-yet-applied actions as
|
|
10
|
+
* pending, and can rewind a range of actions back to pending so they can be
|
|
11
|
+
* replayed while resolving a conflict.
|
|
16
12
|
*/
|
|
17
13
|
export declare class SyncActionStore {
|
|
18
14
|
private db;
|