@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/BaseSyncedStore.js
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The base class that application-specific sync stores extend. It supplies the
|
|
3
|
+
* shared orchestration for reads, writes, delta processing, and bootstrap, and
|
|
4
|
+
* exports the core types those stores build on.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* This file only contains types and the abstract contract — the actual
|
|
11
|
-
* implementation stays in the app's SyncedStore.ts until we incrementally
|
|
12
|
-
* pull generic methods into this base class.
|
|
6
|
+
* A subclass adds its own domain behavior — lazy-loaded relations,
|
|
7
|
+
* collaboration events, and model enrichment — by overriding the protected
|
|
8
|
+
* extension points defined here. The heavy lifting is delegated to injected
|
|
9
|
+
* collaborators: {@link SyncClient} owns pool writes and the transaction
|
|
10
|
+
* queue, {@link Database} owns local persistence, {@link InstanceCache} holds the
|
|
11
|
+
* in-memory models, and {@link ModelRegistry} holds their metadata.
|
|
13
12
|
*/
|
|
14
13
|
import { makeObservable, observable, action, computed, runInAction } from 'mobx';
|
|
15
14
|
import { AbloConnectionError, AbloValidationError, toAbloError } from './errors.js';
|
|
16
15
|
import { ConnectionManager } from './sync/ConnectionManager.js';
|
|
17
|
-
import {
|
|
16
|
+
import { SubscriptionManager } from './sync/SubscriptionManager.js';
|
|
18
17
|
import { resolveParticipantSyncGroups, } from './sync/participants.js';
|
|
19
18
|
import { ModelRegistry } from './ModelRegistry.js';
|
|
20
19
|
import { PropertyType } from './types/index.js';
|
|
@@ -23,7 +22,7 @@ import { QueryProcessor } from './core/QueryProcessor.js';
|
|
|
23
22
|
import { Model, rowAsModel } from './Model.js';
|
|
24
23
|
import { getContext } from './context.js';
|
|
25
24
|
import { SyncSessionError, isAccessCredentialExpiryCloseReason } from './errors.js';
|
|
26
|
-
import { ModelScope } from './
|
|
25
|
+
import { ModelScope } from './InstanceCache.js';
|
|
27
26
|
import { LazyReferenceCollection } from './LazyReferenceCollection.js';
|
|
28
27
|
import { deriveSyncPlanFromSchema } from './sync/syncPlan.js';
|
|
29
28
|
import { CredentialLifecycle } from './sync/credentialLifecycle.js';
|
|
@@ -38,21 +37,20 @@ export const BOOTSTRAP_CONFIG = {
|
|
|
38
37
|
};
|
|
39
38
|
// Re-export for clean API
|
|
40
39
|
export { ModelScope };
|
|
41
|
-
// deriveSyncPlanFromSchema
|
|
42
|
-
//
|
|
40
|
+
// deriveSyncPlanFromSchema derives a sync plan from a schema and is
|
|
41
|
+
// re-exported here.
|
|
43
42
|
export { deriveSyncPlanFromSchema } from './sync/syncPlan.js';
|
|
44
43
|
// ── Base class ──────────────────────────────────────────────────────────────
|
|
45
44
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* base class incrementally as they are genericized.
|
|
45
|
+
* The abstract base class that application-specific sync stores extend. It
|
|
46
|
+
* carries the injected collaborators, the observable sync status, and the
|
|
47
|
+
* orchestration for initialization, delta processing, bootstrap, and the
|
|
48
|
+
* read and write API. A subclass supplies its own domain behavior by
|
|
49
|
+
* overriding the protected extension points defined here and by typing its
|
|
50
|
+
* collaboration events through the generic parameter.
|
|
53
51
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
52
|
+
* A subclass must call `super(dependencies, config)` and then set up its own
|
|
53
|
+
* MobX observables.
|
|
56
54
|
*
|
|
57
55
|
* Generic over `TCollaboration` — an app-defined event map for real-time
|
|
58
56
|
* collaboration events (cursors, selections, presence beyond the core set).
|
|
@@ -120,23 +118,23 @@ export class BaseSyncedStore {
|
|
|
120
118
|
// ── Area-of-interest (dynamic read subscription) ─────────────────
|
|
121
119
|
//
|
|
122
120
|
// `enterScope`/`leaveScope` move the connection's read interest as the
|
|
123
|
-
// user navigates (open
|
|
121
|
+
// user navigates (open or close a deck, sheet, or doc); `pinScope`/`unpinScope`
|
|
124
122
|
// express prominence (an active claim keeps a group subscribed). All four
|
|
125
|
-
// resolve the scope to sync-group strings through the
|
|
123
|
+
// resolve the scope to sync-group strings through the same resolver the
|
|
126
124
|
// claim path uses (`resolveParticipantSyncGroups`), so read interest and
|
|
127
|
-
// write claims always agree on the string for a given entity.
|
|
128
|
-
// before the socket exists
|
|
129
|
-
//
|
|
125
|
+
// write claims always agree on the string for a given entity. They are
|
|
126
|
+
// no-ops before the socket exists, and they never reject when the transport
|
|
127
|
+
// is offline (see {@link SubscriptionManager.reconcile}).
|
|
130
128
|
scopeToGroups(scope) {
|
|
131
129
|
return resolveParticipantSyncGroups(scope, this.schema);
|
|
132
130
|
}
|
|
133
131
|
/**
|
|
134
|
-
* Bring a scope into view
|
|
135
|
-
* `{ hydrate: true }`,
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* and the live
|
|
132
|
+
* Bring a scope into view and subscribe to its sync groups. With
|
|
133
|
+
* `{ hydrate: true }`, also backfill the groups' current state into the pool
|
|
134
|
+
* once the subscription is active. The order matters: subscribing first
|
|
135
|
+
* guarantees no live delta is missed in the gap before the snapshot lands.
|
|
136
|
+
* Hydration is best-effort — a failed backfill never rejects `enterScope`,
|
|
137
|
+
* and the live delta stream keeps flowing regardless.
|
|
140
138
|
*/
|
|
141
139
|
enterScope(scope, opts) {
|
|
142
140
|
const mgr = this.areaOfInterest;
|
|
@@ -149,11 +147,11 @@ export class BaseSyncedStore {
|
|
|
149
147
|
return subscribed.then(() => this.hydrateGroups(groups));
|
|
150
148
|
}
|
|
151
149
|
/**
|
|
152
|
-
* Backfill the current state of `syncGroups` into the pool
|
|
153
|
-
* snapshot fetch
|
|
154
|
-
* (skips groups already hydrated) and single-flight (concurrent
|
|
155
|
-
* same group share one fetch).
|
|
156
|
-
*
|
|
150
|
+
* Backfill the current state of `syncGroups` into the pool with a side-effect-free
|
|
151
|
+
* scoped snapshot fetch followed by the version-guarded scoped apply. The call
|
|
152
|
+
* is idempotent (it skips groups already hydrated) and single-flight (concurrent
|
|
153
|
+
* enters of the same group share one fetch). On error the groups are left
|
|
154
|
+
* unmarked, so a later re-enter retries.
|
|
157
155
|
*/
|
|
158
156
|
async hydrateGroups(syncGroups) {
|
|
159
157
|
const need = syncGroups.filter((g) => !this.hydratedGroups.has(g) && !this.hydratingGroups.has(g));
|
|
@@ -222,15 +220,9 @@ export class BaseSyncedStore {
|
|
|
222
220
|
initialized = false;
|
|
223
221
|
dataReady = false;
|
|
224
222
|
// ── User context ──
|
|
225
|
-
//
|
|
226
|
-
//
|
|
227
|
-
//
|
|
228
|
-
// tokens; the SDK should eventually expose this as an opaque
|
|
229
|
-
// `principal` blob so consumers with different identity models
|
|
230
|
-
// aren't forced into user/org. See the architectural note in the
|
|
231
|
-
// README — "currentUserId" is a domain concept, not an SDK
|
|
232
|
-
// primitive, and the host (apps/web/SyncEngineProvider) is the
|
|
233
|
-
// right place to surface it.
|
|
223
|
+
// The identity the consumer supplied to `initialize()`: user id,
|
|
224
|
+
// organization id, and optional team ids. Reads are scoped to this
|
|
225
|
+
// identity, and the sync-group subscription is derived from it.
|
|
234
226
|
userContext = null;
|
|
235
227
|
// ── Smart sync ──
|
|
236
228
|
/**
|
|
@@ -272,18 +264,16 @@ export class BaseSyncedStore {
|
|
|
272
264
|
this._syncServerUrl = dependencies.url;
|
|
273
265
|
// Set this store as the global Model store
|
|
274
266
|
Model.setStore(this);
|
|
275
|
-
// ── Schema-derived sync plan
|
|
267
|
+
// ── Schema-derived sync plan ───────────────────────────────────────
|
|
276
268
|
//
|
|
277
|
-
// When a schema is provided, derive
|
|
278
|
-
// plan from declarative annotations on
|
|
279
|
-
// relations. Explicit config fields layer on top, so
|
|
280
|
-
//
|
|
281
|
-
// needing a full schema.generated.ts.
|
|
269
|
+
// When a schema is provided, derive foreign-key indexes and the
|
|
270
|
+
// enrichment plan from the declarative annotations on its `belongsTo`
|
|
271
|
+
// relations. Explicit config fields layer on top, so a subclass can
|
|
272
|
+
// pass hardcoded arrays without supplying a full schema.
|
|
282
273
|
//
|
|
283
|
-
// Order matters: schema-derived
|
|
284
|
-
//
|
|
285
|
-
// explicit config entries are
|
|
286
|
-
// accidentally shadowed by schema derivation.
|
|
274
|
+
// Order matters: schema-derived entries are registered first and
|
|
275
|
+
// config entries second, so that when a caller supplies both, the
|
|
276
|
+
// explicit config entries win and are never shadowed by derivation.
|
|
287
277
|
const derived = dependencies.schema
|
|
288
278
|
? deriveSyncPlanFromSchema(dependencies.schema)
|
|
289
279
|
: { enrichmentPlan: [], foreignKeyIndexes: [] };
|
|
@@ -294,9 +284,8 @@ export class BaseSyncedStore {
|
|
|
294
284
|
for (const { modelName, fieldName } of mergedForeignKeyIndexes) {
|
|
295
285
|
this.objectPool.registerForeignKey(modelName, fieldName);
|
|
296
286
|
}
|
|
297
|
-
//
|
|
298
|
-
//
|
|
299
|
-
// Kept for backwards compat; subclasses migrate to config at leisure.
|
|
287
|
+
// Override hook — called after schema-driven registration so a subclass
|
|
288
|
+
// can add more foreign keys on top of the declarative set.
|
|
300
289
|
this.registerForeignKeys();
|
|
301
290
|
this.enrichmentPlan = [
|
|
302
291
|
...derived.enrichmentPlan,
|
|
@@ -328,15 +317,13 @@ export class BaseSyncedStore {
|
|
|
328
317
|
this.queryProcessor.invalidateCache(`.*${name}.*`);
|
|
329
318
|
}
|
|
330
319
|
});
|
|
331
|
-
// Make sync
|
|
320
|
+
// Make the sync-status fields observable so consumer code can do
|
|
332
321
|
// reaction(() => store.isReady, ...)
|
|
333
322
|
// observer(() => store.isOffline)
|
|
334
323
|
// and actually receive notifications. Without these annotations,
|
|
335
|
-
// `syncStatus`
|
|
336
|
-
// getters (isReady, isSyncing, isOffline,
|
|
337
|
-
// signals — a
|
|
338
|
-
// (one stuck forever on the loading skeleton because `reaction`
|
|
339
|
-
// to `store.isReady` never fired). Explicit > accidental.
|
|
324
|
+
// `syncStatus` and `dataReady` are plain properties, and the derived
|
|
325
|
+
// getters (isReady, isSyncing, isOffline, and the rest) never emit
|
|
326
|
+
// change signals — so a `reaction` on `store.isReady` would never fire.
|
|
340
327
|
makeObservable(this, {
|
|
341
328
|
syncStatus: observable,
|
|
342
329
|
dataReady: observable,
|
|
@@ -350,17 +337,17 @@ export class BaseSyncedStore {
|
|
|
350
337
|
}
|
|
351
338
|
// ── Protected extension points ────────────────────────────────────────────
|
|
352
339
|
/**
|
|
353
|
-
* Register foreign
|
|
340
|
+
* Register foreign-key indexes for constant-time lookups.
|
|
354
341
|
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
*
|
|
342
|
+
* This is an override hook. The preferred way to declare a foreign-key
|
|
343
|
+
* index is `config.foreignKeyIndexes` at construction time, or marking the
|
|
344
|
+
* `belongsTo` relation with `{ index: true }` in the schema. The hook fires
|
|
345
|
+
* after the schema-derived and config registrations, so a subclass can
|
|
346
|
+
* layer additional indexes on top.
|
|
360
347
|
*/
|
|
361
348
|
registerForeignKeys() { }
|
|
362
349
|
/**
|
|
363
|
-
* Enrich delta data with related models from the
|
|
350
|
+
* Enrich delta data with related models from the InstanceCache.
|
|
364
351
|
*
|
|
365
352
|
* Base implementation walks `this.enrichmentPlan` — entries populated
|
|
366
353
|
* from the schema's `{ enrich: true }` relations and from
|
|
@@ -656,11 +643,12 @@ export class BaseSyncedStore {
|
|
|
656
643
|
return this.credentialLifecycle.refresh();
|
|
657
644
|
}
|
|
658
645
|
/**
|
|
659
|
-
*
|
|
660
|
-
*
|
|
661
|
-
*
|
|
662
|
-
*
|
|
663
|
-
*
|
|
646
|
+
* The authentication-recovery path for HTTP transports, such as the lazy
|
|
647
|
+
* query lane. It runs a single-flight credential re-mint driven by the
|
|
648
|
+
* rejection's recovery class, routing outcomes through the same state
|
|
649
|
+
* machine the WebSocket probe uses. `'retry'` means a fresh credential is
|
|
650
|
+
* now in the credential source and the request should be replayed once.
|
|
651
|
+
* Full contract on {@link CredentialLifecycle.recoverFromAuthRejection}.
|
|
664
652
|
*/
|
|
665
653
|
async recoverFromAuthRejection(recovery) {
|
|
666
654
|
return this.credentialLifecycle.recoverFromAuthRejection(recovery);
|
|
@@ -676,11 +664,11 @@ export class BaseSyncedStore {
|
|
|
676
664
|
this.connectionManager?.send({ type: 'CREDENTIAL_REFRESHED' });
|
|
677
665
|
}
|
|
678
666
|
/**
|
|
679
|
-
* Install the access-credential lifecycle
|
|
680
|
-
*
|
|
681
|
-
*
|
|
682
|
-
*
|
|
683
|
-
*
|
|
667
|
+
* Install the client-owned access-credential lifecycle: register `getToken`
|
|
668
|
+
* as the reactive re-mint hook and arm the browser-only proactive refresh
|
|
669
|
+
* (a refresh timer plus an OS-wake re-mint). Idempotent — a second call
|
|
670
|
+
* replaces the first — and torn down on {@link disconnect}. Full rationale
|
|
671
|
+
* on {@link CredentialLifecycle.start}.
|
|
684
672
|
*/
|
|
685
673
|
startCredentialLifecycle(getToken, opts) {
|
|
686
674
|
this.credentialLifecycle.start(getToken, opts);
|
|
@@ -689,12 +677,12 @@ export class BaseSyncedStore {
|
|
|
689
677
|
stopCredentialLifecycle() {
|
|
690
678
|
this.credentialLifecycle.stop();
|
|
691
679
|
}
|
|
692
|
-
// ── Sync
|
|
680
|
+
// ── Sync group management ────────────────────────────────────────────────
|
|
693
681
|
//
|
|
694
|
-
//
|
|
695
|
-
//
|
|
696
|
-
// override points
|
|
697
|
-
// calls back through `groupChangeContext()`
|
|
682
|
+
// The implementation lives in the sync/groupChange module. The methods
|
|
683
|
+
// below are thin protected delegates that keep their signatures, so
|
|
684
|
+
// subclass override points still work; the module routes cross-handler
|
|
685
|
+
// calls back through `groupChangeContext()` to preserve dynamic dispatch.
|
|
698
686
|
/** Narrow context the group-change leaf talks back through. */
|
|
699
687
|
groupChangeContext() {
|
|
700
688
|
return {
|
|
@@ -726,8 +714,9 @@ export class BaseSyncedStore {
|
|
|
726
714
|
return groupChange.handleGroupAdded(this.groupChangeContext(), payload, syncId);
|
|
727
715
|
}
|
|
728
716
|
/**
|
|
729
|
-
* Handle an actionType 'S' (GroupRemoved) delta
|
|
730
|
-
* state
|
|
717
|
+
* Handle an actionType 'S' (GroupRemoved) delta: for safety, clear the
|
|
718
|
+
* revoked local state and trigger a full re-bootstrap. See
|
|
719
|
+
* {@link groupChange.handleGroupRemoved}.
|
|
731
720
|
*/
|
|
732
721
|
async handleGroupRemoved(delta) {
|
|
733
722
|
return groupChange.handleGroupRemoved(this.groupChangeContext(), delta);
|
|
@@ -758,10 +747,10 @@ export class BaseSyncedStore {
|
|
|
758
747
|
}
|
|
759
748
|
// ── Bootstrap apply ──────────────────────────────────────────────────────
|
|
760
749
|
//
|
|
761
|
-
//
|
|
762
|
-
// delegates below keep
|
|
763
|
-
//
|
|
764
|
-
//
|
|
750
|
+
// The implementation lives in the sync/bootstrapApply module. The protected
|
|
751
|
+
// delegates below keep their signatures and subclass overridability; the
|
|
752
|
+
// module talks back through `poolContext()`, with enrichment pre-bound to
|
|
753
|
+
// `this.enrichRelations` so that override point still applies.
|
|
765
754
|
/** Narrow context the bootstrap-apply leaf talks back through. */
|
|
766
755
|
poolContext() {
|
|
767
756
|
const store = this;
|
|
@@ -777,8 +766,7 @@ export class BaseSyncedStore {
|
|
|
777
766
|
applyDeltaFrame: (deltas) => { this.applyDeltaFrame(deltas); },
|
|
778
767
|
};
|
|
779
768
|
}
|
|
780
|
-
/** Apply bootstrap data to the
|
|
781
|
-
/** Apply bootstrap data to the ObjectPool. Delegates pool writes to SyncClient. */
|
|
769
|
+
/** Apply bootstrap data to the {@link InstanceCache}, removing entities that are no longer present (ghost removal). Pool writes are delegated to {@link SyncClient}. */
|
|
782
770
|
applyBootstrapToPool(bootstrapResult, protectedIds) {
|
|
783
771
|
return bootstrapApply.applyBootstrapToPool(this.poolContext(), bootstrapResult, protectedIds);
|
|
784
772
|
}
|
|
@@ -791,14 +779,11 @@ export class BaseSyncedStore {
|
|
|
791
779
|
if (this.initialized)
|
|
792
780
|
return { success: true };
|
|
793
781
|
this.userContext = context;
|
|
794
|
-
// Propagate identity to SyncClient. Without this, every mutation
|
|
795
|
-
// silently drops in
|
|
796
|
-
//
|
|
797
|
-
//
|
|
798
|
-
//
|
|
799
|
-
// the drop was invisible because both guard sites just early-return
|
|
800
|
-
// rather than throw. The right fix is to do it here where the store
|
|
801
|
-
// receives the context, so identity is one source of truth.
|
|
782
|
+
// Propagate identity to the SyncClient. Without this, every mutation
|
|
783
|
+
// silently drops in the mutation-staging path with a null userId and
|
|
784
|
+
// organizationId, because those guards early-return rather than throw.
|
|
785
|
+
// Setting it here, where the store receives the context, keeps identity
|
|
786
|
+
// in a single place.
|
|
802
787
|
yield this.syncClient.initialize(context.userId, context.organizationId);
|
|
803
788
|
try {
|
|
804
789
|
this.updateSyncStatus({ state: 'syncing', progress: 0 });
|
|
@@ -1146,7 +1131,7 @@ export class BaseSyncedStore {
|
|
|
1146
1131
|
// connection. baseGroups (the org/user scopes) are always subscribed;
|
|
1147
1132
|
// enterScope/leaveScope move per-entity interest. Recreated with the
|
|
1148
1133
|
// socket; torn down via the disposer pushed below.
|
|
1149
|
-
this.areaOfInterest = new
|
|
1134
|
+
this.areaOfInterest = new SubscriptionManager({
|
|
1150
1135
|
transport: this.syncWebSocket,
|
|
1151
1136
|
baseGroups: this.resolveSyncGroups(context),
|
|
1152
1137
|
});
|
|
@@ -1394,14 +1379,14 @@ export class BaseSyncedStore {
|
|
|
1394
1379
|
this.disposers.push(unsubCreated, unsubCompleted, unsubFailed);
|
|
1395
1380
|
this.syncWebSocket.connect();
|
|
1396
1381
|
}
|
|
1397
|
-
// ── Delta
|
|
1382
|
+
// ── Delta processing pipeline ─────────────────────────────────────────────
|
|
1398
1383
|
//
|
|
1399
|
-
//
|
|
1400
|
-
// bookkeeping, debounce, flush). The methods below
|
|
1401
|
-
// delegates with unchanged signatures, and the
|
|
1402
|
-
//
|
|
1403
|
-
//
|
|
1404
|
-
//
|
|
1384
|
+
// The implementation lives in the sync/deltaPipeline module (deduplication,
|
|
1385
|
+
// enqueue bookkeeping, debounce, flush). The methods below are thin protected
|
|
1386
|
+
// delegates with unchanged signatures, and the module routes every call to a
|
|
1387
|
+
// protected override point back through `deltaPipelineContext`, so subclass
|
|
1388
|
+
// dynamic dispatch is preserved. `applyDeltaFrame`, the authoritative-apply
|
|
1389
|
+
// correctness point, deliberately stays here.
|
|
1405
1390
|
/** Memoized pipeline context — `enqueueDelta` runs once per delta, so the
|
|
1406
1391
|
* accessor object is built once and reused (the get/set accessors always
|
|
1407
1392
|
* read the live host fields). */
|
|
@@ -1464,35 +1449,36 @@ export class BaseSyncedStore {
|
|
|
1464
1449
|
/**
|
|
1465
1450
|
* Apply a complete, server-delivered delta frame atomically.
|
|
1466
1451
|
*
|
|
1467
|
-
* A `delta_batch`
|
|
1468
|
-
* the
|
|
1469
|
-
* `processDeltaWithBatching` path re-
|
|
1470
|
-
* debounce timer
|
|
1471
|
-
*
|
|
1472
|
-
*
|
|
1473
|
-
*
|
|
1452
|
+
* A `delta_batch` WebSocket event (a reconnect or catch-up replay) already
|
|
1453
|
+
* carries the full set of missed deltas. Routing it through the per-delta
|
|
1454
|
+
* `processDeltaWithBatching` path would re-chunk it via the live-traffic
|
|
1455
|
+
* debounce timer and `maxBatchSize` force-flush, so a 300-delta catch-up
|
|
1456
|
+
* would fan out into several separate `flushPendingDeltas` cycles — each its
|
|
1457
|
+
* own local write, pool mutation, `models:changed` emit, and re-render, so
|
|
1458
|
+
* the UI visibly repaints once per chunk.
|
|
1474
1459
|
*
|
|
1475
|
-
*
|
|
1476
|
-
* watermark,
|
|
1477
|
-
* a flush, then
|
|
1478
|
-
*
|
|
1479
|
-
*
|
|
1460
|
+
* Instead, this runs the per-delta bookkeeping (deduplication, ack, version
|
|
1461
|
+
* vector, watermark, group-change routing, delete cascade) for every delta
|
|
1462
|
+
* without scheduling a flush, then flushes once — collapsing the whole frame
|
|
1463
|
+
* into a single local write, pool mutation, `models:changed` emit, and
|
|
1464
|
+
* re-render. The post-bootstrap replay of deltas queued during bootstrap
|
|
1465
|
+
* uses the same path.
|
|
1480
1466
|
*
|
|
1481
|
-
*
|
|
1482
|
-
* with
|
|
1483
|
-
* eventually drives through `flushPendingDeltas`.
|
|
1467
|
+
* It is named `applyDeltaFrame`, not `processDeltaBatch`, to avoid confusion
|
|
1468
|
+
* with {@link Database.processDeltaBatch} — the lower-level local write this
|
|
1469
|
+
* eventually drives through `flushPendingDeltas`.
|
|
1484
1470
|
*/
|
|
1485
1471
|
applyDeltaFrame(deltas) {
|
|
1486
1472
|
let enqueuedAny = false;
|
|
1487
1473
|
for (const delta of deltas) {
|
|
1488
|
-
// A delta_batch frame is the server's
|
|
1474
|
+
// A delta_batch frame is the server's authoritative, ordered answer to
|
|
1489
1475
|
// "everything in my stream after cursor C" (reconnect/catch-up replay or
|
|
1490
|
-
// post-bootstrap drain). Apply every delta it carries
|
|
1476
|
+
// post-bootstrap drain). Apply every delta it carries; do not subject it
|
|
1491
1477
|
// to the live-traffic watermark dedup (`id <= applied`).
|
|
1492
1478
|
//
|
|
1493
1479
|
// That watermark is only valid under in-order delivery, and reconnect
|
|
1494
|
-
// breaks the assumption: an in-flight
|
|
1495
|
-
// land out of order
|
|
1480
|
+
// breaks the assumption: an in-flight live broadcast for a gap delta can
|
|
1481
|
+
// land out of order before the catch-up fills the ids below it (e.g. the
|
|
1496
1482
|
// server acks a write, then the test/client reconnects, then that write's
|
|
1497
1483
|
// pending broadcast arrives on the fresh socket — id 4 live before the
|
|
1498
1484
|
// catch-up's [2,3,4]). Applying id 4 advances `applied` to 4, and the
|
|
@@ -1565,16 +1551,15 @@ export class BaseSyncedStore {
|
|
|
1565
1551
|
});
|
|
1566
1552
|
}
|
|
1567
1553
|
}
|
|
1568
|
-
/** Flush pending deltas with deduplication
|
|
1569
|
-
/** Flush pending deltas with deduplication. Delegates pool writes to SyncClient. */
|
|
1554
|
+
/** Flush pending deltas with deduplication. Pool writes are delegated to {@link SyncClient}. */
|
|
1570
1555
|
async flushPendingDeltas() {
|
|
1571
1556
|
return deltaPipeline.flushPendingDeltas(this.deltaPipelineContext);
|
|
1572
1557
|
}
|
|
1573
|
-
// ── Core
|
|
1558
|
+
// ── Core mutations (thin delegation to SyncClient) ────────────────────────
|
|
1574
1559
|
//
|
|
1575
|
-
//
|
|
1576
|
-
//
|
|
1577
|
-
//
|
|
1560
|
+
// This class orchestrates; it does not implement the writes. {@link SyncClient}
|
|
1561
|
+
// owns the object-pool operations, the transaction queue, and local writes.
|
|
1562
|
+
// This class owns validation, lifecycle hooks, and pending-delete tracking.
|
|
1578
1563
|
/** Check if a model type is local-only (no sync). Override for domain-specific models. */
|
|
1579
1564
|
isLocalOnlyModel(_modelName) {
|
|
1580
1565
|
return false;
|
|
@@ -1646,9 +1631,9 @@ export class BaseSyncedStore {
|
|
|
1646
1631
|
this.syncClient.update(model);
|
|
1647
1632
|
}
|
|
1648
1633
|
// ── Query API ────────────────────────────────────────────────────────────
|
|
1649
|
-
// `
|
|
1650
|
-
//
|
|
1651
|
-
// `tx.<model
|
|
1634
|
+
// `ablo.<model>.get` / `ablo.<model>.getAll` is the read surface for
|
|
1635
|
+
// application code. Custom mutators read transactionally through
|
|
1636
|
+
// `tx.<model>`, backed by `createReaderActions`.
|
|
1652
1637
|
/** Retrieve a single entity by id. Synchronous pool read. */
|
|
1653
1638
|
retrieve(_modelClass, id) {
|
|
1654
1639
|
return this.objectPool.get(id);
|
|
@@ -1692,10 +1677,10 @@ export class BaseSyncedStore {
|
|
|
1692
1677
|
return this.objectPool.create(wireTypename, data);
|
|
1693
1678
|
}
|
|
1694
1679
|
/**
|
|
1695
|
-
*
|
|
1696
|
-
*
|
|
1697
|
-
*
|
|
1698
|
-
*
|
|
1680
|
+
* Query entry point for callers that hold a {@link Model} constructor and an
|
|
1681
|
+
* options object. It filters, orders, and paginates the matching models from
|
|
1682
|
+
* the pool. Prefer the schema-typed read surface (`ablo.<model>.list`) where
|
|
1683
|
+
* you can, since it infers concrete row types without a class value or cast.
|
|
1699
1684
|
*/
|
|
1700
1685
|
queryByClass(modelClass, options) {
|
|
1701
1686
|
const modelName = this.objectPool.registry.getModelNameFromConstructor(modelClass);
|
|
@@ -1804,8 +1789,7 @@ export class BaseSyncedStore {
|
|
|
1804
1789
|
return this.lastAckedId;
|
|
1805
1790
|
}
|
|
1806
1791
|
// ── Status convenience getters ──────────────────────────────────────────
|
|
1807
|
-
// Thin wrappers over syncStatus for consumer ergonomics.
|
|
1808
|
-
// SyncedStore; moved here so createSyncEngine consumers get them too.
|
|
1792
|
+
// Thin wrappers over `syncStatus` for consumer ergonomics.
|
|
1809
1793
|
get isReady() {
|
|
1810
1794
|
// Ready if: fully synced (idle + 100%) OR local data loaded (dataReady + syncing in background)
|
|
1811
1795
|
return (this.syncStatus.state === 'idle' && this.syncStatus.progress >= 100)
|