@abloatai/ablo 0.26.0 → 0.28.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 +42 -2
- package/README.md +102 -86
- package/dist/BaseSyncedStore.d.ts +85 -88
- package/dist/BaseSyncedStore.js +134 -151
- package/dist/Database.d.ts +68 -69
- package/dist/Database.js +316 -135
- 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 +54 -52
- package/dist/Model.js +78 -62
- 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 +122 -118
- package/dist/SyncClient.js +541 -245
- package/dist/adapters/alwaysOnline.d.ts +6 -8
- package/dist/adapters/alwaysOnline.js +6 -8
- package/dist/adapters/inMemoryStorage.d.ts +10 -9
- package/dist/adapters/inMemoryStorage.js +21 -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 +173 -121
- package/dist/client/Ablo.d.ts +97 -74
- package/dist/client/Ablo.js +129 -163
- package/dist/client/ApiClient.d.ts +30 -19
- package/dist/client/ApiClient.js +442 -81
- 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 +16 -17
- package/dist/client/createInternalComponents.js +26 -31
- 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 +59 -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 +78 -87
- package/dist/client/options.d.ts +157 -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 +16 -20
- package/dist/client/wsMutationExecutor.js +18 -23
- package/dist/commit/contract.d.ts +493 -0
- package/dist/commit/contract.js +187 -0
- package/dist/commit/index.d.ts +6 -0
- package/dist/commit/index.js +5 -0
- 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 +14 -14
- package/dist/core/StoreManager.js +33 -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 +137 -134
- package/dist/errors.d.ts +160 -166
- package/dist/errors.js +155 -158
- package/dist/index.d.ts +36 -27
- package/dist/index.js +91 -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 +124 -131
- package/dist/mutators/UndoManager.js +177 -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 +28 -25
- package/dist/react/useAblo.js +41 -17
- 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 +3 -3
- 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 +64 -43
- 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 +24 -12
- package/dist/stores/ObjectStore.js +38 -16
- package/dist/stores/ObjectStoreContract.d.ts +14 -15
- package/dist/stores/SyncActionStore.d.ts +7 -11
- package/dist/stores/SyncActionStore.js +13 -17
- package/dist/surface.d.ts +28 -21
- package/dist/surface.js +29 -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 +141 -166
- 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/persistedPrefix.d.ts +12 -0
- package/dist/sync/persistedPrefix.js +22 -0
- 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 +5 -3
- package/dist/testing/index.js +3 -2
- package/dist/testing/mocks/FakeDatabase.d.ts +18 -0
- package/dist/testing/mocks/FakeDatabase.js +10 -0
- 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 +28 -23
- package/dist/testing/mocks/MockWebSocket.js +22 -21
- package/dist/transactions/TransactionQueue.d.ts +244 -181
- package/dist/transactions/TransactionQueue.js +929 -423
- 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/commitEnvelope.d.ts +132 -0
- package/dist/transactions/commitEnvelope.js +139 -0
- package/dist/transactions/commitOutboxStore.d.ts +32 -0
- package/dist/transactions/commitOutboxStore.js +26 -0
- package/dist/transactions/commitPayload.d.ts +63 -52
- package/dist/transactions/commitPayload.js +54 -57
- package/dist/transactions/deltaConfirmation.d.ts +20 -22
- package/dist/transactions/deltaConfirmation.js +37 -45
- package/dist/transactions/httpCommitEnvelope.d.ts +43 -0
- package/dist/transactions/httpCommitEnvelope.js +179 -0
- package/dist/transactions/optimisticApply.d.ts +49 -0
- package/dist/transactions/optimisticApply.js +65 -0
- package/dist/transactions/replayValidation.d.ts +182 -0
- package/dist/transactions/replayValidation.js +156 -0
- 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/wire/bootstrapReason.d.ts +9 -0
- package/dist/wire/bootstrapReason.js +8 -0
- 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 +315 -86
- package/dist/wire/frames.js +47 -33
- package/dist/wire/index.d.ts +18 -14
- package/dist/wire/index.js +32 -27
- 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/api.md +10 -10
- package/docs/coordination.md +59 -0
- package/docs/mcp.md +1 -1
- package/package.json +17 -11
- 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/transactions/persistedReplay.d.ts +0 -93
- package/dist/transactions/persistedReplay.js +0 -105
- package/dist/utils/mobx-setup.d.ts +0 -42
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Loads model rows on demand — the lazy-load path of the sync engine. When
|
|
3
|
+
* something needs an entity that the initial bootstrap did not fetch,
|
|
4
|
+
* {@link OnDemandLoader.fetch | fetch} finds it and populates the
|
|
5
|
+
* in-memory {@link InstanceCache} so the rest of the engine can read it normally.
|
|
3
6
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
7
|
+
* A fetch resolves against three tiers in order, stopping at the first that can
|
|
8
|
+
* answer:
|
|
9
|
+
* 1. The object pool — if rows already in memory match the query, return them.
|
|
10
|
+
* 2. Local storage — if matching rows exist there, hydrate the pool and return.
|
|
11
|
+
* 3. The network — post the query to `/sync/query`, then hydrate both the pool
|
|
12
|
+
* and local storage.
|
|
8
13
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* 3. Network — `postQuery` against `/sync/query`, hydrate pool + IDB.
|
|
14
|
+
* Concurrent calls with the same query key share one in-flight promise, so a
|
|
15
|
+
* burst of components mounting and asking for the same data on first paint
|
|
16
|
+
* triggers a single fetch rather than one each.
|
|
13
17
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* The coordinator does NOT replace bootstrap (full sync of `instant`
|
|
19
|
-
* models) or live deltas (WS push). It only fills the gap for `lazy`
|
|
20
|
-
* models accessed by id/where after the engine is ready.
|
|
18
|
+
* The coordinator does not replace the bootstrap (which fully syncs instantly
|
|
19
|
+
* loaded models) or the live delta stream (pushed over the WebSocket). It only
|
|
20
|
+
* fills the gap for lazily loaded models read by id or filter after the engine
|
|
21
|
+
* is ready.
|
|
21
22
|
*/
|
|
22
|
-
import { ModelScope } from '../
|
|
23
|
+
import { ModelScope } from '../InstanceCache.js';
|
|
23
24
|
import { AbloValidationError } from '../errors.js';
|
|
24
25
|
import { postQuery } from '../query/client.js';
|
|
25
|
-
export class
|
|
26
|
+
export class OnDemandLoader {
|
|
26
27
|
opts;
|
|
27
28
|
inFlight = new Map();
|
|
28
29
|
/**
|
|
@@ -36,9 +37,9 @@ export class HydrationCoordinator {
|
|
|
36
37
|
/**
|
|
37
38
|
* Query keys that have been satisfied from the server at least once this
|
|
38
39
|
* session. Once a key is here, repeat reads serve purely from the pool with
|
|
39
|
-
*
|
|
40
|
-
* re-running the HTTP query would be redundant polling. This
|
|
41
|
-
*
|
|
40
|
+
* no network round-trip: the WebSocket delta stream keeps those pool rows
|
|
41
|
+
* fresh, so re-running the HTTP query would be redundant polling. This ledger
|
|
42
|
+
* is what stops an already-open view from re-querying on every navigation.
|
|
42
43
|
*
|
|
43
44
|
* Cleared on reconnect (see {@link invalidate}) so that, after a connection
|
|
44
45
|
* drop where deltas may have been missed, the next read re-confirms once.
|
|
@@ -46,16 +47,20 @@ export class HydrationCoordinator {
|
|
|
46
47
|
hydratedKeys = new Set();
|
|
47
48
|
authTokenProvider = null;
|
|
48
49
|
/**
|
|
49
|
-
* The
|
|
50
|
-
* late-bound like {@link setAuthTokenProvider} because the store
|
|
50
|
+
* The credential-recovery hook (the store's `recoverFromAuthRejection`),
|
|
51
|
+
* late-bound like {@link setAuthTokenProvider} because the store does not
|
|
51
52
|
* exist yet when the coordinator is constructed. Handed to `postQuery` so a
|
|
52
|
-
* 401 on the lazy lane re-mints through the
|
|
53
|
-
* probe
|
|
54
|
-
* returning empty rows against an expired key
|
|
53
|
+
* 401 on the lazy lane re-mints through the same single-flight path the
|
|
54
|
+
* WebSocket probe uses, then replays the query once — instead of silently
|
|
55
|
+
* returning empty rows against an expired key.
|
|
55
56
|
*/
|
|
56
57
|
credentialRecovery = null;
|
|
57
58
|
constructor(opts) {
|
|
58
59
|
this.opts = opts;
|
|
60
|
+
// Reading the deprecated `getCapabilityToken` is deliberate: it's the
|
|
61
|
+
// back-compat shim that keeps older callers who still pass it working
|
|
62
|
+
// until they migrate to `getAuthToken`.
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
59
64
|
this.authTokenProvider = opts.getAuthToken ?? opts.getCapabilityToken ?? null;
|
|
60
65
|
}
|
|
61
66
|
/**
|
|
@@ -84,7 +89,7 @@ export class HydrationCoordinator {
|
|
|
84
89
|
const ModelClass = this.opts.registry.getModelByName(typename)
|
|
85
90
|
?? this.opts.registry.getModelByName(modelName);
|
|
86
91
|
if (!ModelClass) {
|
|
87
|
-
throw new AbloValidationError(`
|
|
92
|
+
throw new AbloValidationError(`OnDemandLoader.fetch: unknown model "${modelName}" — ` +
|
|
88
93
|
`not registered in the schema.`, { code: 'model_not_registered' });
|
|
89
94
|
}
|
|
90
95
|
const clauses = normalizeWhere(options?.where);
|
|
@@ -114,24 +119,24 @@ export class HydrationCoordinator {
|
|
|
114
119
|
const hasExpand = !!(expand && expand.length > 0);
|
|
115
120
|
// Fast path — this exact query was already satisfied from the server this
|
|
116
121
|
// session. The WebSocket delta stream has kept the pool fresh since, so a
|
|
117
|
-
// repeat read needs
|
|
118
|
-
// stops an already-open
|
|
122
|
+
// repeat read needs no network: serve straight from local. This is what
|
|
123
|
+
// stops an already-open view from re-querying on every navigation when no
|
|
119
124
|
// new deltas have arrived.
|
|
120
125
|
if (!explicitComplete && this.hydratedKeys.has(queryKey)) {
|
|
121
126
|
return applyLimit(await this.readLocal(modelName, typename, ModelClass, clauses, hasExpand, expand), options?.limit);
|
|
122
127
|
}
|
|
123
128
|
// Not yet hydrated (or an explicit complete read). For a non-complete read
|
|
124
|
-
//
|
|
125
|
-
// after a reload), hand it back immediately and confirm with the
|
|
126
|
-
//
|
|
127
|
-
// are
|
|
129
|
+
// without expand, if there is anything local to show (a warm pool, or local
|
|
130
|
+
// storage after a reload), hand it back immediately and confirm with the
|
|
131
|
+
// server once in the background — then mark the key hydrated so subsequent
|
|
132
|
+
// reads are purely local. First paint never blocks on the network.
|
|
128
133
|
//
|
|
129
|
-
// Expand queries are deliberately excluded here: a
|
|
130
|
-
// nothing about whether its relations are loaded. Returning the
|
|
131
|
-
// would surface it with empty children
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
//
|
|
134
|
+
// Expand queries are deliberately excluded here: the presence of a primary
|
|
135
|
+
// row says nothing about whether its relations are loaded. Returning the
|
|
136
|
+
// parent now would surface it with empty children, letting a readiness flag
|
|
137
|
+
// flip before the children exist. So an un-hydrated expand query falls
|
|
138
|
+
// through to the blocking fetch that brings parent and children together;
|
|
139
|
+
// the second open is served by the fast path.
|
|
135
140
|
if (!explicitComplete && !hasExpand) {
|
|
136
141
|
const local = await this.readLocal(modelName, typename, ModelClass, clauses, hasExpand, expand);
|
|
137
142
|
if (local.length > 0) {
|
|
@@ -190,10 +195,10 @@ export class HydrationCoordinator {
|
|
|
190
195
|
async fetchFromNetwork(modelName, typename, clauses, options) {
|
|
191
196
|
const networkRows = await this.queryNetwork(modelName, clauses, options);
|
|
192
197
|
const networkModels = networkRows
|
|
193
|
-
// Strict: a row the
|
|
194
|
-
// registered is a genuine schema collision (the
|
|
195
|
-
//
|
|
196
|
-
//
|
|
198
|
+
// Strict: a row the server returned whose type name this client never
|
|
199
|
+
// registered is a genuine schema collision (the pushed schema differs
|
|
200
|
+
// from the local one). Throw here, naming the cause, rather than silently
|
|
201
|
+
// dropping the row and failing downstream as `entity_not_found`.
|
|
197
202
|
.map((raw) => this.hydrateOne(raw, typename, { strict: true }))
|
|
198
203
|
.filter((m) => m !== null);
|
|
199
204
|
if (networkModels.length > 0) {
|
|
@@ -205,13 +210,13 @@ export class HydrationCoordinator {
|
|
|
205
210
|
return networkModels;
|
|
206
211
|
}
|
|
207
212
|
/**
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
213
|
+
* Fires the single background confirm for a query that was just served from
|
|
214
|
+
* local cache but is not hydrated yet. On success the key is marked hydrated,
|
|
215
|
+
* so every later read serves purely from local with no network until a
|
|
211
216
|
* reconnect invalidates the ledger. Deduped per query key so a render burst
|
|
212
|
-
*
|
|
213
|
-
* local snapshot, and a failed confirm leaves the key un-hydrated so the
|
|
214
|
-
*
|
|
217
|
+
* does not stampede. Errors are swallowed — the caller already has a usable
|
|
218
|
+
* local snapshot, and a failed confirm leaves the key un-hydrated so the next
|
|
219
|
+
* read simply tries again.
|
|
215
220
|
*/
|
|
216
221
|
scheduleHydratingFetch(queryKey, modelName, typename, clauses, options) {
|
|
217
222
|
if (this.revalidating.has(queryKey))
|
|
@@ -325,7 +330,7 @@ export class HydrationCoordinator {
|
|
|
325
330
|
// after a missed delta (WS dropped, tab slept, redeploy) silently
|
|
326
331
|
// discards the fresh state and the consumer keeps seeing the
|
|
327
332
|
// birth-time snapshot forever. `updateFromData` is the same
|
|
328
|
-
// primitive `
|
|
333
|
+
// primitive `InstanceCache.upsert()` uses for delta application,
|
|
329
334
|
// so the behaviour matches "delta-applied" semantics exactly.
|
|
330
335
|
const existing = this.opts.objectPool.get(obj.id);
|
|
331
336
|
if (existing) {
|
|
@@ -337,9 +342,9 @@ export class HydrationCoordinator {
|
|
|
337
342
|
}
|
|
338
343
|
// Stamp the known relation typename onto the row when the source
|
|
339
344
|
// (IndexedDB rows, sometimes network rows) didn't carry one. Without
|
|
340
|
-
// this,
|
|
345
|
+
// this, InstanceCache.createFromData falls through to the 'Unknown'
|
|
341
346
|
// model-name branch and emits the
|
|
342
|
-
// "
|
|
347
|
+
// "InstanceCache.createFromData: No model identifier found" warning,
|
|
343
348
|
// failing to hydrate the entity from cache (network path then has to
|
|
344
349
|
// re-populate it). The typename comes from the schema relation
|
|
345
350
|
// (`'SlideLayer'`, `'SlideLayoutLayer'`, etc.) so no guessing involved.
|
|
@@ -352,7 +357,7 @@ export class HydrationCoordinator {
|
|
|
352
357
|
* `postgres.camel` driver leaves behind when the server's SQL
|
|
353
358
|
* bakes `__typename` into a JSONB literal — the driver's
|
|
354
359
|
* snake↔camel transform misreads `__typename` as `_typename` with
|
|
355
|
-
* a leading underscore and produces `_Typename`.
|
|
360
|
+
* a leading underscore and produces `_Typename`. InstanceCache only
|
|
356
361
|
* recognises `__typename`, so without this step nested rows fall
|
|
357
362
|
* through to the 'Unknown' branch and never instantiate.
|
|
358
363
|
*/
|
|
@@ -471,7 +476,7 @@ export class HydrationCoordinator {
|
|
|
471
476
|
resolveTypename(modelName) {
|
|
472
477
|
// Schema is the source of truth for wire typenames. The model proxy
|
|
473
478
|
// is keyed by camelCase plural (`slideLayers`) but the wire query +
|
|
474
|
-
//
|
|
479
|
+
// InstanceCache typeIndex use the typename (`SlideLayer`).
|
|
475
480
|
const def = this.opts.schema
|
|
476
481
|
.models?.[modelName];
|
|
477
482
|
return def?.typename ?? modelName;
|
|
@@ -1,51 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* the
|
|
2
|
+
* Decides which sync groups a connection subscribes to as the user navigates,
|
|
3
|
+
* and pushes each change through the {@link SubscriptionTransport}'s
|
|
4
|
+
* `update_subscription` call. It smooths two kinds of churn so that opening and
|
|
5
|
+
* closing entities does not turn into a storm of subscription changes.
|
|
4
6
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* The first is hysteresis. Calling {@link SubscriptionManager.leave | leave}
|
|
8
|
+
* on a group does not unsubscribe it right away; the group stays subscribed for
|
|
9
|
+
* a grace period — its warm window — and drops only once that window lapses.
|
|
10
|
+
* Re-entering within the window costs nothing, since the group was never
|
|
11
|
+
* dropped, so rapid back-and-forth navigation becomes a cache hit rather than a
|
|
12
|
+
* repeated bootstrap.
|
|
9
13
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* (already subscribed → no bootstrap), and only when the warm TTL
|
|
15
|
-
* lapses does the group actually drop. This is the boundary hysteresis
|
|
16
|
-
* that turns deck-tab flipping from a re-bootstrap storm into a
|
|
17
|
-
* cache hit.
|
|
14
|
+
* The second is prominence. A group that holds an active write claim is pinned
|
|
15
|
+
* (see {@link SubscriptionManager.pin | pin}) and stays subscribed regardless
|
|
16
|
+
* of navigation, so a row someone is actively editing never loses its live
|
|
17
|
+
* updates. The `baseGroups` are permanent scopes that are always subscribed.
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* `user:<id>`) that are always in the effective set.
|
|
27
|
-
*
|
|
28
|
-
* The effective set is recomputed and diffed against what was last sent;
|
|
29
|
-
* the transport's `update_subscription` is only called when it actually
|
|
30
|
-
* changes, so hysteresis genuinely suppresses network churn rather than
|
|
31
|
-
* just deferring it.
|
|
32
|
-
*
|
|
33
|
-
* Transport-agnostic: it depends only on {@link SubscriptionTransport},
|
|
34
|
-
* which `SyncWebSocket` satisfies structurally. `now` and the sweep timer
|
|
35
|
-
* are injectable so the policy is deterministic under test.
|
|
19
|
+
* The manager recomputes the full desired set on every change, diffs it against
|
|
20
|
+
* the set the transport last confirmed, and calls `update_subscription` only
|
|
21
|
+
* when the set actually changes — so the smoothing suppresses network traffic
|
|
22
|
+
* rather than merely deferring it. It depends only on
|
|
23
|
+
* {@link SubscriptionTransport}, which {@link SyncWebSocket} satisfies. The
|
|
24
|
+
* clock and the sweep timer are injectable so the policy is deterministic under
|
|
25
|
+
* test.
|
|
36
26
|
*/
|
|
37
27
|
/** The single capability this manager needs from the connection. */
|
|
38
28
|
export interface SubscriptionTransport {
|
|
39
29
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
30
|
+
* Replaces the connection's read interest with the complete group set. This
|
|
31
|
+
* is a full replace, not an incremental add or remove. Resolves with the
|
|
32
|
+
* effective set the server applied, which the manager treats as authoritative
|
|
33
|
+
* for its next diff.
|
|
43
34
|
*/
|
|
44
35
|
updateSubscription(syncGroups: readonly string[]): Promise<{
|
|
45
36
|
syncGroups: string[];
|
|
46
37
|
}>;
|
|
47
38
|
}
|
|
48
|
-
export interface
|
|
39
|
+
export interface SubscriptionManagerOptions {
|
|
49
40
|
/** Connection to drive. `SyncWebSocket` satisfies this structurally. */
|
|
50
41
|
transport: SubscriptionTransport;
|
|
51
42
|
/**
|
|
@@ -59,17 +50,16 @@ export interface AreaOfInterestOptions {
|
|
|
59
50
|
*/
|
|
60
51
|
warmTtlMs?: number;
|
|
61
52
|
/**
|
|
62
|
-
*
|
|
63
|
-
* navigation
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* This is the bounded relevant-set discipline from game netcode. Default 16.
|
|
53
|
+
* The maximum number of warm (left but still subscribed) groups. Under heavy
|
|
54
|
+
* navigation, warm groups would otherwise pile up until each one's window
|
|
55
|
+
* lapses, inflating the connection's subscription set. When the cap is
|
|
56
|
+
* exceeded, the least-recently-warmed group is dropped immediately instead of
|
|
57
|
+
* waiting for its window. Default 16.
|
|
68
58
|
*/
|
|
69
59
|
maxWarm?: number;
|
|
70
60
|
/**
|
|
71
61
|
* Auto-run the warm-expiry sweep on this cadence. Set `0` to disable and
|
|
72
|
-
* drive {@link
|
|
62
|
+
* drive {@link SubscriptionManager.sweep} yourself (tests do this).
|
|
73
63
|
* Default = `warmTtlMs` (checks about once per margin).
|
|
74
64
|
*/
|
|
75
65
|
sweepIntervalMs?: number;
|
|
@@ -81,7 +71,7 @@ export interface AreaOfInterestOptions {
|
|
|
81
71
|
*/
|
|
82
72
|
scheduler?: (fn: () => void, intervalMs: number) => () => void;
|
|
83
73
|
}
|
|
84
|
-
export declare class
|
|
74
|
+
export declare class SubscriptionManager {
|
|
85
75
|
private readonly transport;
|
|
86
76
|
private readonly baseGroups;
|
|
87
77
|
private readonly warmTtlMs;
|
|
@@ -99,7 +89,7 @@ export declare class AreaOfInterestManager {
|
|
|
99
89
|
private inFlight;
|
|
100
90
|
private dirty;
|
|
101
91
|
private readonly cancelSweep;
|
|
102
|
-
constructor(options:
|
|
92
|
+
constructor(options: SubscriptionManagerOptions);
|
|
103
93
|
/**
|
|
104
94
|
* Move a group into the warm set with a fresh TTL, maintaining LRU order
|
|
105
95
|
* and the `maxWarm` cap. JS `Map` preserves insertion order, so deleting
|
|
@@ -134,19 +124,15 @@ export declare class AreaOfInterestManager {
|
|
|
134
124
|
/** The set the manager believes is subscribed (post-confirmation). */
|
|
135
125
|
effectiveGroups(): string[];
|
|
136
126
|
/**
|
|
137
|
-
* Re-
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
* the manager's
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
* socket's server-side index matches local interest, even if warm/pinned
|
|
147
|
-
* groups drifted across the disconnect window. The connect-time URL
|
|
148
|
-
* already carries the last-acked set, so this is a correction frame, not
|
|
149
|
-
* the primary mechanism.
|
|
127
|
+
* Re-asserts the full desired set against the transport, forgetting what was
|
|
128
|
+
* previously confirmed. Call this after a reconnect: a fresh
|
|
129
|
+
* {@link SyncWebSocket} starts from the sync groups named in the connect-time
|
|
130
|
+
* URL, so the manager's diff baseline no longer reflects the new socket.
|
|
131
|
+
* Clearing that baseline makes the next reconcile push one
|
|
132
|
+
* `update_subscription` frame that re-establishes the current interest —
|
|
133
|
+
* including any warm or pinned groups that drifted while the connection was
|
|
134
|
+
* down. The connect-time URL already carries the last-acknowledged set, so
|
|
135
|
+
* this is a correction, not the primary mechanism.
|
|
150
136
|
*/
|
|
151
137
|
resync(): Promise<void>;
|
|
152
138
|
/** Stop the sweep timer. The connection is unaffected. */
|
|
@@ -1,38 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* the
|
|
2
|
+
* Decides which sync groups a connection subscribes to as the user navigates,
|
|
3
|
+
* and pushes each change through the {@link SubscriptionTransport}'s
|
|
4
|
+
* `update_subscription` call. It smooths two kinds of churn so that opening and
|
|
5
|
+
* closing entities does not turn into a storm of subscription changes.
|
|
4
6
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* The first is hysteresis. Calling {@link SubscriptionManager.leave | leave}
|
|
8
|
+
* on a group does not unsubscribe it right away; the group stays subscribed for
|
|
9
|
+
* a grace period — its warm window — and drops only once that window lapses.
|
|
10
|
+
* Re-entering within the window costs nothing, since the group was never
|
|
11
|
+
* dropped, so rapid back-and-forth navigation becomes a cache hit rather than a
|
|
12
|
+
* repeated bootstrap.
|
|
9
13
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* (already subscribed → no bootstrap), and only when the warm TTL
|
|
15
|
-
* lapses does the group actually drop. This is the boundary hysteresis
|
|
16
|
-
* that turns deck-tab flipping from a re-bootstrap storm into a
|
|
17
|
-
* cache hit.
|
|
14
|
+
* The second is prominence. A group that holds an active write claim is pinned
|
|
15
|
+
* (see {@link SubscriptionManager.pin | pin}) and stays subscribed regardless
|
|
16
|
+
* of navigation, so a row someone is actively editing never loses its live
|
|
17
|
+
* updates. The `baseGroups` are permanent scopes that are always subscribed.
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* `user:<id>`) that are always in the effective set.
|
|
27
|
-
*
|
|
28
|
-
* The effective set is recomputed and diffed against what was last sent;
|
|
29
|
-
* the transport's `update_subscription` is only called when it actually
|
|
30
|
-
* changes, so hysteresis genuinely suppresses network churn rather than
|
|
31
|
-
* just deferring it.
|
|
32
|
-
*
|
|
33
|
-
* Transport-agnostic: it depends only on {@link SubscriptionTransport},
|
|
34
|
-
* which `SyncWebSocket` satisfies structurally. `now` and the sweep timer
|
|
35
|
-
* are injectable so the policy is deterministic under test.
|
|
19
|
+
* The manager recomputes the full desired set on every change, diffs it against
|
|
20
|
+
* the set the transport last confirmed, and calls `update_subscription` only
|
|
21
|
+
* when the set actually changes — so the smoothing suppresses network traffic
|
|
22
|
+
* rather than merely deferring it. It depends only on
|
|
23
|
+
* {@link SubscriptionTransport}, which {@link SyncWebSocket} satisfies. The
|
|
24
|
+
* clock and the sweep timer are injectable so the policy is deterministic under
|
|
25
|
+
* test.
|
|
36
26
|
*/
|
|
37
27
|
function setsEqual(a, b) {
|
|
38
28
|
if (a.size !== b.size)
|
|
@@ -42,7 +32,7 @@ function setsEqual(a, b) {
|
|
|
42
32
|
return false;
|
|
43
33
|
return true;
|
|
44
34
|
}
|
|
45
|
-
export class
|
|
35
|
+
export class SubscriptionManager {
|
|
46
36
|
transport;
|
|
47
37
|
baseGroups;
|
|
48
38
|
warmTtlMs;
|
|
@@ -165,19 +155,15 @@ export class AreaOfInterestManager {
|
|
|
165
155
|
return [...this.lastSent];
|
|
166
156
|
}
|
|
167
157
|
/**
|
|
168
|
-
* Re-
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
* the manager's
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
* socket's server-side index matches local interest, even if warm/pinned
|
|
178
|
-
* groups drifted across the disconnect window. The connect-time URL
|
|
179
|
-
* already carries the last-acked set, so this is a correction frame, not
|
|
180
|
-
* the primary mechanism.
|
|
158
|
+
* Re-asserts the full desired set against the transport, forgetting what was
|
|
159
|
+
* previously confirmed. Call this after a reconnect: a fresh
|
|
160
|
+
* {@link SyncWebSocket} starts from the sync groups named in the connect-time
|
|
161
|
+
* URL, so the manager's diff baseline no longer reflects the new socket.
|
|
162
|
+
* Clearing that baseline makes the next reconcile push one
|
|
163
|
+
* `update_subscription` frame that re-establishes the current interest —
|
|
164
|
+
* including any warm or pinned groups that drifted while the connection was
|
|
165
|
+
* down. The connect-time URL already carries the last-acknowledged set, so
|
|
166
|
+
* this is a correction, not the primary mechanism.
|
|
181
167
|
*/
|
|
182
168
|
resync() {
|
|
183
169
|
this.lastSent = new Set();
|
|
@@ -214,14 +200,20 @@ export class AreaOfInterestManager {
|
|
|
214
200
|
this.lastSent = new Set(result.syncGroups);
|
|
215
201
|
}
|
|
216
202
|
catch {
|
|
217
|
-
// Transport unavailable (offline
|
|
218
|
-
// server rejected the set.
|
|
219
|
-
//
|
|
220
|
-
// `lastSent` unchanged
|
|
221
|
-
// next
|
|
222
|
-
// which
|
|
203
|
+
// Transport unavailable (offline, or socket not open) or the
|
|
204
|
+
// server rejected the set. Read interest is soft state, so enter,
|
|
205
|
+
// leave, and sweep never throw for an expected transient failure.
|
|
206
|
+
// Leaving `lastSent` unchanged keeps the pending diff; `resync()`
|
|
207
|
+
// on the next successful connect re-pushes the then-current desired
|
|
208
|
+
// set, which recovers any interest that changed while offline.
|
|
223
209
|
break;
|
|
224
210
|
}
|
|
211
|
+
// A concurrent reconcile() arriving during the await above sets
|
|
212
|
+
// `this.dirty` back to true (the coalescing path near line 245).
|
|
213
|
+
// TypeScript's intra-closure flow analysis can't see that cross-
|
|
214
|
+
// invocation mutation and reads this as always-false, but the loop
|
|
215
|
+
// is genuinely reentrant.
|
|
216
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
225
217
|
} while (this.dirty);
|
|
226
218
|
}
|
|
227
219
|
finally {
|