@tearleads/client-sdk 0.1.51 → 0.1.53
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/client/deviceFirst.js +5 -3
- package/client/deviceFirst.js.map +1 -1
- package/{sync/reconciliation/documentContentPull.js → client/reconciledDocumentContentPull.js} +2 -2
- package/client/reconciledDocumentContentPull.js.map +1 -0
- package/data/sync/originatedDocuments.d.ts +11 -0
- package/data/sync/originatedDocuments.js.map +1 -0
- package/package.json +3 -3
- package/{sqlite.d.ts → sqlite/index.d.ts} +9 -9
- package/sqlite/index.js +11 -0
- package/sqlite/index.js.map +1 -0
- package/stores/documents/documentStore/syncAttachments.js +1 -1
- package/stores/documents/documentStore/syncAttachments.js.map +1 -1
- package/sync/reconciliation/clock.d.ts +11 -0
- package/sync/reconciliation/clock.js +8 -0
- package/sync/reconciliation/clock.js.map +1 -0
- package/sync/reconciliation/index.d.ts +1 -0
- package/sync/reconciliation/index.js +1 -0
- package/sync/reconciliation/index.js.map +1 -1
- package/sync/reconciliation/initialDocumentProbe.d.ts +2 -0
- package/sync/reconciliation/initialDocumentProbe.js +7 -7
- package/sync/reconciliation/initialDocumentProbe.js.map +1 -1
- package/sync/reconciliation/knownContainerSweep.js +8 -9
- package/sync/reconciliation/knownContainerSweep.js.map +1 -1
- package/sync/reconciliation/service.js +3 -5
- package/sync/reconciliation/service.js.map +1 -1
- package/sync/reconciliation/serviceTypes.d.ts +10 -1
- package/sync/reconciliation/serviceTypes.js.map +1 -1
- package/sync/reconciliation/triggers.js +1 -1
- package/sync/reconciliation/triggers.js.map +1 -1
- package/workflows/sync/index.d.ts +1 -0
- package/workflows/sync/index.js +1 -0
- package/workflows/sync/index.js.map +1 -1
- package/sqlite.js +0 -11
- package/sqlite.js.map +0 -1
- package/sync/reconciliation/documentContentPull.js.map +0 -1
- package/sync/reconciliation/originatedDocuments.js.map +0 -1
- /package/{sync/reconciliation → data/sync}/originatedDocuments.js +0 -0
package/client/deviceFirst.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { disposeDomainSyncCoordinator, isDatabaseUnavailableError, } from "../data/sync/syncCoordinator.js";
|
|
1
|
+
import { disposeDomainSyncCoordinator, getOrCreateDomainSyncCoordinator, isDatabaseUnavailableError, } from "../data/sync/syncCoordinator.js";
|
|
2
2
|
import { isAutomaticRootCatchupContainerNode, isReconcilableContainerNode, isRemoteBackedContainerNode, } from "../stores/container-contents/index.js";
|
|
3
3
|
import { openDocumentStore } from "../stores/documents/index.js";
|
|
4
4
|
import { requestRegisteredDocumentRemoteSync, subscribeToPersistedDocumentDeletions, subscribeToPersistedDocuments, } from "../stores/documents/registry.js";
|
|
5
5
|
import { getOrCreateLocalProjectionStore, } from "../stores/local-projection/index.js";
|
|
6
|
-
import { connectReconciliationTriggers, createReconciliationService, } from "../sync/reconciliation/index.js";
|
|
7
|
-
import { createReconciledDocumentContentPuller } from "../sync/reconciliation/documentContentPull.js";
|
|
6
|
+
import { connectReconciliationTriggers, createReconciliationService, systemReconciliationClock, } from "../sync/reconciliation/index.js";
|
|
8
7
|
import { listAllContainerDocumentIdsFromApi } from "../workflows/container-contents/containerDocumentListing.js";
|
|
9
8
|
import { probeUndiscoveredRemoteDocumentBatch } from "../workflows/container-contents/documentHydrationProbe.js";
|
|
10
9
|
import { createContainerContentsDocumentsRuntime, createContainerContentsStoreWorkflowRuntime, } from "../workflows/container-contents/runtime.js";
|
|
11
10
|
import { discoverContainerDocumentLanesForRuntime } from "./containerDocumentDiscovery.js";
|
|
11
|
+
import { createReconciledDocumentContentPuller } from "./reconciledDocumentContentPull.js";
|
|
12
12
|
export function createDeviceFirst(runtimeService, containerContents) {
|
|
13
13
|
return new DeviceFirstService(runtimeService, containerContents);
|
|
14
14
|
}
|
|
@@ -154,7 +154,9 @@ class DeviceFirstService {
|
|
|
154
154
|
: []);
|
|
155
155
|
};
|
|
156
156
|
return {
|
|
157
|
+
clock: systemReconciliationClock,
|
|
157
158
|
domainScope,
|
|
159
|
+
registerLane: (key, config) => getOrCreateDomainSyncCoordinator(domainScope).registerLane(key, config),
|
|
158
160
|
canDiscoverContainerDocuments,
|
|
159
161
|
getRuntimeStatus: () => {
|
|
160
162
|
const input = runtimeService.workflowInput();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deviceFirst.js","sourceRoot":"","sources":["deviceFirst.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DomainScope } from \"../data/domainScope\";\nimport {\n disposeDomainSyncCoordinator,\n isDatabaseUnavailableError,\n} from \"../data/sync/syncCoordinator\";\nimport {\n type ContainerContentsStore,\n type ContainerContentsStoreOptions,\n isAutomaticRootCatchupContainerNode,\n isReconcilableContainerNode,\n isRemoteBackedContainerNode,\n} from \"../stores/container-contents\";\nimport { openDocumentStore } from \"../stores/documents\";\nimport {\n requestRegisteredDocumentRemoteSync,\n subscribeToPersistedDocumentDeletions,\n subscribeToPersistedDocuments,\n} from \"../stores/documents/registry\";\nimport {\n getOrCreateLocalProjectionStore,\n type LocalProjectionStore,\n type LocalProjectionView,\n} from \"../stores/local-projection\";\nimport {\n connectReconciliationTriggers,\n createReconciliationService,\n type ReconciliationHost,\n type ReconciliationService,\n} from \"../sync/reconciliation\";\nimport { createReconciledDocumentContentPuller } from \"../sync/reconciliation/documentContentPull\";\nimport { listAllContainerDocumentIdsFromApi } from \"../workflows/container-contents/containerDocumentListing\";\nimport { probeUndiscoveredRemoteDocumentBatch } from \"../workflows/container-contents/documentHydrationProbe\";\nimport {\n type ContainerContentsStoreWorkflowRuntime,\n createContainerContentsDocumentsRuntime,\n createContainerContentsStoreWorkflowRuntime,\n} from \"../workflows/container-contents/runtime\";\nimport type { ContainerContents } from \"./containerContents\";\nimport { discoverContainerDocumentLanesForRuntime } from \"./containerDocumentDiscovery\";\nimport type { InternalRuntime } from \"./workflowRuntime\";\n\nexport type { LocalProjectionView } from \"../stores/local-projection\";\n\n/** One domain scope's local read, local-write, and reconciliation handles. */\nexport interface DeviceFirstContainerContents {\n /**\n * Locally durable container tree and mutation store. Ordinary tree writes\n * update this store after local persistence and queue remote convergence in\n * the background; security-sensitive remote operations keep their explicit\n * online requirements.\n */\n readonly containerStore: ContainerContentsStore;\n /** Background remote reconciliation; never awaited for first paint. */\n readonly reconciler: ReconciliationService;\n /** Synchronously readable container/document projection. */\n readonly view: LocalProjectionView;\n}\n\n/**\n * Device-first facade. Opens a shared scope that pairs immediate local reads\n * and locally durable writes with background remote convergence.\n */\nexport interface DeviceFirst {\n /** Open all device-first handles for the current domain scope. */\n open(\n options?: ContainerContentsStoreOptions | undefined,\n ): DeviceFirstContainerContents;\n /** Stop the current scope's reconciler (if one was created) on teardown. */\n dispose(): void;\n}\n\nexport function createDeviceFirst(\n runtimeService: InternalRuntime,\n containerContents: ContainerContents,\n): DeviceFirst {\n return new DeviceFirstService(runtimeService, containerContents);\n}\n\ninterface DeviceFirstScopeEntry {\n contents: DeviceFirstContainerContents;\n disconnectReconciliationTriggers: () => void;\n unsubscribePersistedDocumentDeletions: () => void;\n unsubscribePersistedDocuments: () => void;\n}\n\nfunction createInitialDocumentProbeHost(\n runtimeService: InternalRuntime,\n domainScope: ReconciliationHost[\"domainScope\"],\n): Pick<\n ReconciliationHost,\n | \"listContainerDocumentIds\"\n | \"probeUndiscoveredDocumentsBatch\"\n | \"reportInitialDocumentProbeComplete\"\n> {\n return {\n listContainerDocumentIds: (containerId) => {\n const runtime = createDeviceFirstWorkflowRuntime(runtimeService);\n return listAllContainerDocumentIdsFromApi({\n apiClient: runtime.apiClient,\n containerId,\n reportErrors: false,\n });\n },\n probeUndiscoveredDocumentsBatch: async (input) => {\n const runtime = createDeviceFirstWorkflowRuntime(runtimeService);\n return probeUndiscoveredRemoteDocumentBatch({\n ...input,\n host: {\n documentWorkflowRuntime: (containerId) =>\n createContainerContentsDocumentsRuntime(runtime, containerId),\n openDocumentStore: (target) =>\n openDocumentStore(\n domainScope,\n target.localId,\n target.runtime,\n target.documentId,\n ),\n },\n runtime,\n });\n },\n reportInitialDocumentProbeComplete: (requestedCount) => {\n runtimeService\n .workflowInput()\n .util.log(\n `Initial document hydration probe requested ${requestedCount} syncs`,\n );\n },\n };\n}\n\nclass DeviceFirstService implements DeviceFirst {\n // A real Map (not WeakMap) so dispose() can iterate every scope seen this\n // session. A running reconciler holds event subscriptions that keep an idle,\n // stale-scope service (and its scope) reachable anyway, so WeakMap bought no\n // GC here; the entries are released explicitly on dispose().\n private readonly entriesByScope = new Map<\n DomainScope,\n DeviceFirstScopeEntry\n >();\n\n constructor(\n private readonly runtimeService: InternalRuntime,\n private readonly containerContents: ContainerContents,\n ) {}\n\n open(\n options?: ContainerContentsStoreOptions | undefined,\n ): DeviceFirstContainerContents {\n return this.getOrCreateEntry(options).contents;\n }\n\n dispose(): void {\n // Stop every reconciler created this session — not just the current\n // scope's. A scope change (e.g. anonymous -> authenticated) leaves the old\n // scope's reconciler subscribed and its coordinator pump live; tear them\n // all down. Force-stop each scope's coordinator (which runs the reconciler\n // lane) alongside stopping the service.\n for (const [domainScope, entry] of this.entriesByScope) {\n entry.contents.reconciler.stop();\n entry.disconnectReconciliationTriggers();\n entry.unsubscribePersistedDocumentDeletions();\n entry.unsubscribePersistedDocuments();\n disposeDomainSyncCoordinator(domainScope);\n }\n this.entriesByScope.clear();\n }\n\n private workflowRuntime(): ContainerContentsStoreWorkflowRuntime {\n return createDeviceFirstWorkflowRuntime(this.runtimeService);\n }\n\n private getOrCreateEntry(\n options?: ContainerContentsStoreOptions | undefined,\n ): DeviceFirstScopeEntry {\n const runtime = this.workflowRuntime();\n const domainScope = runtime.state.domainScope;\n const existing = this.entriesByScope.get(domainScope);\n if (existing) {\n // Do not call updateRuntime here: facade selectors run during React\n // render, and updateRuntime emits synchronously. Hosts drive runtime\n // updates through view.updateRuntime() from an effect instead.\n return existing;\n }\n\n const store = getOrCreateLocalProjectionStore({\n domainScope,\n runtime,\n options,\n });\n const service = createReconciliationService(\n this.createReconciliationHost(store, domainScope),\n );\n const disconnectReconciliationTriggers = connectReconciliationTriggers({\n service,\n store,\n });\n const unsubscribePersistedDocumentDeletions =\n subscribeToPersistedDocumentDeletions(domainScope, (localId) => {\n store.removePersistedDocument(localId);\n });\n const unsubscribePersistedDocuments = subscribeToPersistedDocuments(\n domainScope,\n (document, change) =>\n store.refreshPersistedDocument(document, change.placementChanged),\n );\n service.start();\n\n const view: LocalProjectionView = {\n getSnapshot: () => store.getSnapshot(),\n subscribe: (listener) => store.subscribe(listener),\n setActiveContainer: (containerId) =>\n store.setActiveContainer(containerId),\n updateRuntime: (nextRuntime) => store.updateRuntime(nextRuntime),\n };\n\n const entry: DeviceFirstScopeEntry = {\n contents: {\n containerStore: store.getContainerStore(),\n reconciler: service,\n view,\n },\n disconnectReconciliationTriggers,\n unsubscribePersistedDocumentDeletions,\n unsubscribePersistedDocuments,\n };\n this.entriesByScope.set(domainScope, entry);\n return entry;\n }\n\n private createReconciliationHost(\n store: LocalProjectionStore,\n domainScope: ReconciliationHost[\"domainScope\"],\n ): ReconciliationHost {\n const runtimeService = this.runtimeService;\n const containerContents = this.containerContents;\n const requestDocumentContentPull = createReconciledDocumentContentPuller({\n getContainer: (containerId) =>\n store\n .getSnapshot()\n .containers.find((container) => container.id === containerId) ?? null,\n pullDocumentContent: (input) =>\n containerContents.pullDocumentContent(input),\n requestRegisteredDocumentRemoteSync: (localId, documentId) =>\n requestRegisteredDocumentRemoteSync(domainScope, localId, documentId),\n });\n const canDiscoverContainerDocuments = (containerId: string) => {\n const container = store\n .getSnapshot()\n .containers.find((node) => node.id === containerId);\n return container ? isRemoteBackedContainerNode(container) : false;\n };\n const listKnownContainerIds = () => {\n // A local-first id is not listable yet and may still be racing its create\n // request. Once remote-backed, it participates normally; system contents\n // are especially important during rematerialization because this device\n // may not have authored them.\n const homeOrganizationId =\n runtimeService.workflowInput().auth.organizationId;\n return store\n .getSnapshot()\n .containers.flatMap((node) =>\n isReconcilableContainerNode(node, homeOrganizationId)\n ? [node.id]\n : [],\n );\n };\n const listAutomaticRootCatchupContainerIds = () => {\n const homeOrganizationId =\n runtimeService.workflowInput().auth.organizationId;\n return store\n .getSnapshot()\n .containers.flatMap((node) =>\n isAutomaticRootCatchupContainerNode(node, homeOrganizationId)\n ? [node.id]\n : [],\n );\n };\n return {\n domainScope,\n canDiscoverContainerDocuments,\n getRuntimeStatus: () => {\n const input = runtimeService.workflowInput();\n return {\n dbStatus: input.infra.dbStatus,\n isAuthenticated: input.auth.isAuthenticated,\n online: input.state.online,\n };\n },\n listKnownContainerIds,\n // Root discovery can surface directly granted non-root containers, so\n // retain every regular container. Exclude only this identity's own system\n // children; cold/auth backfill and explicit full Refresh cover those.\n listAutomaticRootCatchupContainerIds,\n discoverContainerDocuments: (requests, onOutcome) =>\n discoverContainerDocumentLanesForRuntime({\n onOutcome,\n requests,\n runtimeService,\n }),\n loadContainerDelta: (containerId) =>\n store.loadContainerDelta(containerId),\n applyReconciled: (delta) => store.applyReconciled(delta),\n requestDocumentContentPull,\n ...createInitialDocumentProbeHost(runtimeService, domainScope),\n refreshTree: async () => {\n await store.getContainerStore().refresh();\n },\n refreshRootTree: async () => {\n await store.getContainerStore().refreshRootLane();\n },\n isIgnorableError: isDatabaseUnavailableError,\n // Resolve the runtime per call: it is rebuilt as the active identity\n // changes, and a reference captured here would report into a dead host.\n logError: (message, error) =>\n logReconciliationFailure(this.workflowRuntime(), message, error),\n };\n }\n}\n\n// Returns the host's result: both callbacks are declared `=> void` but may be\n// async, and the lane reporter only catches a rejection it is handed.\nfunction logReconciliationFailure(\n runtime: ContainerContentsStoreWorkflowRuntime,\n message: string,\n error: unknown,\n): unknown {\n const { log, logError } = runtime.util;\n return logError ? logError(message, error) : log(message);\n}\n\nexport function createDeviceFirstWorkflowRuntime(\n runtimeService: InternalRuntime,\n): ContainerContentsStoreWorkflowRuntime {\n return createContainerContentsStoreWorkflowRuntime(\n runtimeService.workflowInput(),\n runtimeService.adoptRootContainer,\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"deviceFirst.js","sourceRoot":"","sources":["deviceFirst.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DomainScope } from \"../data/domainScope\";\nimport {\n disposeDomainSyncCoordinator,\n getOrCreateDomainSyncCoordinator,\n isDatabaseUnavailableError,\n} from \"../data/sync/syncCoordinator\";\nimport {\n type ContainerContentsStore,\n type ContainerContentsStoreOptions,\n isAutomaticRootCatchupContainerNode,\n isReconcilableContainerNode,\n isRemoteBackedContainerNode,\n} from \"../stores/container-contents\";\nimport { openDocumentStore } from \"../stores/documents\";\nimport {\n requestRegisteredDocumentRemoteSync,\n subscribeToPersistedDocumentDeletions,\n subscribeToPersistedDocuments,\n} from \"../stores/documents/registry\";\nimport {\n getOrCreateLocalProjectionStore,\n type LocalProjectionStore,\n type LocalProjectionView,\n} from \"../stores/local-projection\";\nimport {\n connectReconciliationTriggers,\n createReconciliationService,\n type ReconciliationHost,\n type ReconciliationService,\n systemReconciliationClock,\n} from \"../sync/reconciliation\";\nimport { listAllContainerDocumentIdsFromApi } from \"../workflows/container-contents/containerDocumentListing\";\nimport { probeUndiscoveredRemoteDocumentBatch } from \"../workflows/container-contents/documentHydrationProbe\";\nimport {\n type ContainerContentsStoreWorkflowRuntime,\n createContainerContentsDocumentsRuntime,\n createContainerContentsStoreWorkflowRuntime,\n} from \"../workflows/container-contents/runtime\";\nimport type { ContainerContents } from \"./containerContents\";\nimport { discoverContainerDocumentLanesForRuntime } from \"./containerDocumentDiscovery\";\nimport { createReconciledDocumentContentPuller } from \"./reconciledDocumentContentPull\";\nimport type { InternalRuntime } from \"./workflowRuntime\";\n\nexport type { LocalProjectionView } from \"../stores/local-projection\";\n\n/** One domain scope's local read, local-write, and reconciliation handles. */\nexport interface DeviceFirstContainerContents {\n /**\n * Locally durable container tree and mutation store. Ordinary tree writes\n * update this store after local persistence and queue remote convergence in\n * the background; security-sensitive remote operations keep their explicit\n * online requirements.\n */\n readonly containerStore: ContainerContentsStore;\n /** Background remote reconciliation; never awaited for first paint. */\n readonly reconciler: ReconciliationService;\n /** Synchronously readable container/document projection. */\n readonly view: LocalProjectionView;\n}\n\n/**\n * Device-first facade. Opens a shared scope that pairs immediate local reads\n * and locally durable writes with background remote convergence.\n */\nexport interface DeviceFirst {\n /** Open all device-first handles for the current domain scope. */\n open(\n options?: ContainerContentsStoreOptions | undefined,\n ): DeviceFirstContainerContents;\n /** Stop the current scope's reconciler (if one was created) on teardown. */\n dispose(): void;\n}\n\nexport function createDeviceFirst(\n runtimeService: InternalRuntime,\n containerContents: ContainerContents,\n): DeviceFirst {\n return new DeviceFirstService(runtimeService, containerContents);\n}\n\ninterface DeviceFirstScopeEntry {\n contents: DeviceFirstContainerContents;\n disconnectReconciliationTriggers: () => void;\n unsubscribePersistedDocumentDeletions: () => void;\n unsubscribePersistedDocuments: () => void;\n}\n\nfunction createInitialDocumentProbeHost(\n runtimeService: InternalRuntime,\n domainScope: ReconciliationHost[\"domainScope\"],\n): Pick<\n ReconciliationHost,\n | \"listContainerDocumentIds\"\n | \"probeUndiscoveredDocumentsBatch\"\n | \"reportInitialDocumentProbeComplete\"\n> {\n return {\n listContainerDocumentIds: (containerId) => {\n const runtime = createDeviceFirstWorkflowRuntime(runtimeService);\n return listAllContainerDocumentIdsFromApi({\n apiClient: runtime.apiClient,\n containerId,\n reportErrors: false,\n });\n },\n probeUndiscoveredDocumentsBatch: async (input) => {\n const runtime = createDeviceFirstWorkflowRuntime(runtimeService);\n return probeUndiscoveredRemoteDocumentBatch({\n ...input,\n host: {\n documentWorkflowRuntime: (containerId) =>\n createContainerContentsDocumentsRuntime(runtime, containerId),\n openDocumentStore: (target) =>\n openDocumentStore(\n domainScope,\n target.localId,\n target.runtime,\n target.documentId,\n ),\n },\n runtime,\n });\n },\n reportInitialDocumentProbeComplete: (requestedCount) => {\n runtimeService\n .workflowInput()\n .util.log(\n `Initial document hydration probe requested ${requestedCount} syncs`,\n );\n },\n };\n}\n\nclass DeviceFirstService implements DeviceFirst {\n // A real Map (not WeakMap) so dispose() can iterate every scope seen this\n // session. A running reconciler holds event subscriptions that keep an idle,\n // stale-scope service (and its scope) reachable anyway, so WeakMap bought no\n // GC here; the entries are released explicitly on dispose().\n private readonly entriesByScope = new Map<\n DomainScope,\n DeviceFirstScopeEntry\n >();\n\n constructor(\n private readonly runtimeService: InternalRuntime,\n private readonly containerContents: ContainerContents,\n ) {}\n\n open(\n options?: ContainerContentsStoreOptions | undefined,\n ): DeviceFirstContainerContents {\n return this.getOrCreateEntry(options).contents;\n }\n\n dispose(): void {\n // Stop every reconciler created this session — not just the current\n // scope's. A scope change (e.g. anonymous -> authenticated) leaves the old\n // scope's reconciler subscribed and its coordinator pump live; tear them\n // all down. Force-stop each scope's coordinator (which runs the reconciler\n // lane) alongside stopping the service.\n for (const [domainScope, entry] of this.entriesByScope) {\n entry.contents.reconciler.stop();\n entry.disconnectReconciliationTriggers();\n entry.unsubscribePersistedDocumentDeletions();\n entry.unsubscribePersistedDocuments();\n disposeDomainSyncCoordinator(domainScope);\n }\n this.entriesByScope.clear();\n }\n\n private workflowRuntime(): ContainerContentsStoreWorkflowRuntime {\n return createDeviceFirstWorkflowRuntime(this.runtimeService);\n }\n\n private getOrCreateEntry(\n options?: ContainerContentsStoreOptions | undefined,\n ): DeviceFirstScopeEntry {\n const runtime = this.workflowRuntime();\n const domainScope = runtime.state.domainScope;\n const existing = this.entriesByScope.get(domainScope);\n if (existing) {\n // Do not call updateRuntime here: facade selectors run during React\n // render, and updateRuntime emits synchronously. Hosts drive runtime\n // updates through view.updateRuntime() from an effect instead.\n return existing;\n }\n\n const store = getOrCreateLocalProjectionStore({\n domainScope,\n runtime,\n options,\n });\n const service = createReconciliationService(\n this.createReconciliationHost(store, domainScope),\n );\n const disconnectReconciliationTriggers = connectReconciliationTriggers({\n service,\n store,\n });\n const unsubscribePersistedDocumentDeletions =\n subscribeToPersistedDocumentDeletions(domainScope, (localId) => {\n store.removePersistedDocument(localId);\n });\n const unsubscribePersistedDocuments = subscribeToPersistedDocuments(\n domainScope,\n (document, change) =>\n store.refreshPersistedDocument(document, change.placementChanged),\n );\n service.start();\n\n const view: LocalProjectionView = {\n getSnapshot: () => store.getSnapshot(),\n subscribe: (listener) => store.subscribe(listener),\n setActiveContainer: (containerId) =>\n store.setActiveContainer(containerId),\n updateRuntime: (nextRuntime) => store.updateRuntime(nextRuntime),\n };\n\n const entry: DeviceFirstScopeEntry = {\n contents: {\n containerStore: store.getContainerStore(),\n reconciler: service,\n view,\n },\n disconnectReconciliationTriggers,\n unsubscribePersistedDocumentDeletions,\n unsubscribePersistedDocuments,\n };\n this.entriesByScope.set(domainScope, entry);\n return entry;\n }\n\n private createReconciliationHost(\n store: LocalProjectionStore,\n domainScope: ReconciliationHost[\"domainScope\"],\n ): ReconciliationHost {\n const runtimeService = this.runtimeService;\n const containerContents = this.containerContents;\n const requestDocumentContentPull = createReconciledDocumentContentPuller({\n getContainer: (containerId) =>\n store\n .getSnapshot()\n .containers.find((container) => container.id === containerId) ?? null,\n pullDocumentContent: (input) =>\n containerContents.pullDocumentContent(input),\n requestRegisteredDocumentRemoteSync: (localId, documentId) =>\n requestRegisteredDocumentRemoteSync(domainScope, localId, documentId),\n });\n const canDiscoverContainerDocuments = (containerId: string) => {\n const container = store\n .getSnapshot()\n .containers.find((node) => node.id === containerId);\n return container ? isRemoteBackedContainerNode(container) : false;\n };\n const listKnownContainerIds = () => {\n // A local-first id is not listable yet and may still be racing its create\n // request. Once remote-backed, it participates normally; system contents\n // are especially important during rematerialization because this device\n // may not have authored them.\n const homeOrganizationId =\n runtimeService.workflowInput().auth.organizationId;\n return store\n .getSnapshot()\n .containers.flatMap((node) =>\n isReconcilableContainerNode(node, homeOrganizationId)\n ? [node.id]\n : [],\n );\n };\n const listAutomaticRootCatchupContainerIds = () => {\n const homeOrganizationId =\n runtimeService.workflowInput().auth.organizationId;\n return store\n .getSnapshot()\n .containers.flatMap((node) =>\n isAutomaticRootCatchupContainerNode(node, homeOrganizationId)\n ? [node.id]\n : [],\n );\n };\n return {\n clock: systemReconciliationClock,\n domainScope,\n registerLane: (key, config) =>\n getOrCreateDomainSyncCoordinator(domainScope).registerLane(key, config),\n canDiscoverContainerDocuments,\n getRuntimeStatus: () => {\n const input = runtimeService.workflowInput();\n return {\n dbStatus: input.infra.dbStatus,\n isAuthenticated: input.auth.isAuthenticated,\n online: input.state.online,\n };\n },\n listKnownContainerIds,\n // Root discovery can surface directly granted non-root containers, so\n // retain every regular container. Exclude only this identity's own system\n // children; cold/auth backfill and explicit full Refresh cover those.\n listAutomaticRootCatchupContainerIds,\n discoverContainerDocuments: (requests, onOutcome) =>\n discoverContainerDocumentLanesForRuntime({\n onOutcome,\n requests,\n runtimeService,\n }),\n loadContainerDelta: (containerId) =>\n store.loadContainerDelta(containerId),\n applyReconciled: (delta) => store.applyReconciled(delta),\n requestDocumentContentPull,\n ...createInitialDocumentProbeHost(runtimeService, domainScope),\n refreshTree: async () => {\n await store.getContainerStore().refresh();\n },\n refreshRootTree: async () => {\n await store.getContainerStore().refreshRootLane();\n },\n isIgnorableError: isDatabaseUnavailableError,\n // Resolve the runtime per call: it is rebuilt as the active identity\n // changes, and a reference captured here would report into a dead host.\n logError: (message, error) =>\n logReconciliationFailure(this.workflowRuntime(), message, error),\n };\n }\n}\n\n// Returns the host's result: both callbacks are declared `=> void` but may be\n// async, and the lane reporter only catches a rejection it is handed.\nfunction logReconciliationFailure(\n runtime: ContainerContentsStoreWorkflowRuntime,\n message: string,\n error: unknown,\n): unknown {\n const { log, logError } = runtime.util;\n return logError ? logError(message, error) : log(message);\n}\n\nexport function createDeviceFirstWorkflowRuntime(\n runtimeService: InternalRuntime,\n): ContainerContentsStoreWorkflowRuntime {\n return createContainerContentsStoreWorkflowRuntime(\n runtimeService.workflowInput(),\n runtimeService.adoptRootContainer,\n );\n}\n"]}
|
package/{sync/reconciliation/documentContentPull.js → client/reconciledDocumentContentPull.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isSystemContainerNode } from "
|
|
1
|
+
import { isSystemContainerNode } from "../stores/container-contents/index.js";
|
|
2
2
|
function shouldForceDocument(force, documentIds, documentId) {
|
|
3
3
|
return force && (!documentIds || documentIds.has(documentId));
|
|
4
4
|
}
|
|
@@ -50,4 +50,4 @@ export function createReconciledDocumentContentPuller(host) {
|
|
|
50
50
|
}
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
|
-
//# sourceMappingURL=
|
|
53
|
+
//# sourceMappingURL=reconciledDocumentContentPull.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconciledDocumentContentPull.js","sourceRoot":"","sources":["reconciledDocumentContentPull.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAgBrE,SAAS,mBAAmB,CAC1B,KAAc,EACd,WAA4C,EAC5C,UAAkB;IAElB,OAAO,KAAK,IAAI,CAAC,CAAC,WAAW,IAAI,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;AAChE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qCAAqC,CACnD,IAAuC;IAEvC,4EAA4E;IAC5E,4EAA4E;IAC5E,kDAAkD;IAClD,MAAM,4BAA4B,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE/D,OAAO,CAAC,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE;QACpD,MAAM,eAAe,GAAG,qBAAqB,CAC3C,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CACvD,CAAC;QAEF,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;gBACzB,SAAS;YACX,CAAC;YAED,MAAM,aAAa,GAAG,mBAAmB,CACvC,KAAK,EACL,WAAW,EACX,QAAQ,CAAC,UAAU,CACpB,CAAC;YAEF,IAAI,eAAe,EAAE,CAAC;gBACpB,uEAAuE;gBACvE,uEAAuE;gBACvE,oCAAoC;gBACpC,MAAM,WAAW,GAAG,GAAG,WAAW,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;gBAC5D,IACE,4BAA4B,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,QAAQ,CAAC,SAAS,EACpE,CAAC;oBACD,IAAI,CAAC,mBAAmB,CAAC;wBACvB,WAAW;wBACX,UAAU,EAAE,QAAQ,CAAC,UAAU;wBAC/B,OAAO,EAAE,QAAQ,CAAC,EAAE;qBACrB,CAAC,CAAC;oBACH,4BAA4B,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;gBACpE,CAAC;qBAAM,IAAI,aAAa,EAAE,CAAC;oBACzB,oEAAoE;oBACpE,qEAAqE;oBACrE,oEAAoE;oBACpE,qBAAqB;oBACrB,IAAI,CAAC,mCAAmC,CACtC,QAAQ,CAAC,EAAE,EACX,QAAQ,CAAC,UAAU,CACpB,CAAC;gBACJ,CAAC;gBACD,SAAS;YACX,CAAC;YAED,IAAI,aAAa,EAAE,CAAC;gBAClB,IAAI,CAAC,mCAAmC,CACtC,QAAQ,CAAC,EAAE,EACX,QAAQ,CAAC,UAAU,CACpB,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import type { ContainerNode } from \"../stores/container-contents\";\nimport { isSystemContainerNode } from \"../stores/container-contents\";\nimport type { ReconciliationHost } from \"../sync/reconciliation\";\n\ninterface ReconciledDocumentContentPullHost {\n readonly getContainer: (containerId: string) => ContainerNode | null;\n readonly pullDocumentContent: (input: {\n readonly containerId: string;\n readonly documentId: string;\n readonly localId: string;\n }) => void;\n readonly requestRegisteredDocumentRemoteSync: (\n localId: string,\n documentId: string,\n ) => void;\n}\n\nfunction shouldForceDocument(\n force: boolean,\n documentIds: ReadonlySet<string> | undefined,\n documentId: string,\n): boolean {\n return force && (!documentIds || documentIds.has(documentId));\n}\n\n/**\n * Routes reconciled summaries into the appropriate content-sync policy.\n *\n * System-container contents cannot rely on a mini-app opening them on this\n * device, especially during identity recovery. Pull each remote version once.\n * Ordinary documents stay lazy unless an explicit refresh asks an already-open\n * store to revalidate.\n */\nexport function createReconciledDocumentContentPuller(\n host: ReconciledDocumentContentPullHost,\n): ReconciliationHost[\"requestDocumentContentPull\"] {\n // Retain only the last observed version for each system-container document.\n // Keeping the version in the key would grow this cache for every historical\n // updatedAt value over the reconciler's lifetime.\n const pulledSystemDocumentVersions = new Map<string, string>();\n\n return (containerId, documents, force, documentIds) => {\n const systemContainer = isSystemContainerNode(\n host.getContainer(containerId) ?? { systemSlot: null },\n );\n\n for (const document of documents) {\n if (!document.documentId) {\n continue;\n }\n\n const forceDocument = shouldForceDocument(\n force,\n documentIds,\n document.documentId,\n );\n\n if (systemContainer) {\n // Container scope is significant: the same document can be linked into\n // multiple system containers whose keys establish different decryption\n // contexts for the transient store.\n const documentKey = `${containerId}:${document.documentId}`;\n if (\n pulledSystemDocumentVersions.get(documentKey) !== document.updatedAt\n ) {\n host.pullDocumentContent({\n containerId,\n documentId: document.documentId,\n localId: document.id,\n });\n pulledSystemDocumentVersions.set(documentKey, document.updatedAt);\n } else if (forceDocument) {\n // The first eager pull registers a document store before scheduling\n // its async sync. If that sync failed, a later explicit Refresh must\n // be able to retry the same server version without defeating normal\n // background dedupe.\n host.requestRegisteredDocumentRemoteSync(\n document.id,\n document.documentId,\n );\n }\n continue;\n }\n\n if (forceDocument) {\n host.requestRegisteredDocumentRemoteSync(\n document.id,\n document.documentId,\n );\n }\n }\n };\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DomainScope } from "../domainScope.js";
|
|
2
|
+
/** Record remote document ids this client just created/uploaded in `scope`. */
|
|
3
|
+
export declare function markOriginatedDocuments(scope: DomainScope, documentIds: Iterable<string>): void;
|
|
4
|
+
/**
|
|
5
|
+
* Consume an originated id: returns true (and removes it) when `documentId` was
|
|
6
|
+
* an echo of this client's own write, false otherwise. Single-use so only the
|
|
7
|
+
* first echo is suppressed.
|
|
8
|
+
*/
|
|
9
|
+
export declare function consumeOriginatedDocument(scope: DomainScope, documentId: string): boolean;
|
|
10
|
+
/** Drop all originated ids for a scope (reconciler stop / identity teardown). */
|
|
11
|
+
export declare function clearOriginatedDocuments(scope: DomainScope): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"originatedDocuments.js","sourceRoot":"","sources":["originatedDocuments.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AACH,MAAM,4BAA4B,GAAG,IAAI,OAAO,EAA4B,CAAC;AAE7E,SAAS,wBAAwB,CAAC,KAAkB;IAClD,MAAM,QAAQ,GAAG,4BAA4B,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACzD,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,4BAA4B,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACjD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,uBAAuB,CACrC,KAAkB,EAClB,WAA6B;IAE7B,MAAM,GAAG,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;IAC5C,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,IAAI,UAAU,EAAE,CAAC;YACf,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CACvC,KAAkB,EAClB,UAAkB;IAElB,OAAO,4BAA4B,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC;AAC9E,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,wBAAwB,CAAC,KAAkB;IACzD,6EAA6E;IAC7E,uDAAuD;IACvD,4BAA4B,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC7C,CAAC","sourcesContent":["import type { DomainScope } from \"../domainScope\";\n\n/**\n * Per-scope set of remote document ids this client just originated (created or\n * uploaded). The server echoes a `document_update_created` for the client's own\n * writes; reconciling on that echo is pure waste because the client already has\n * the document locally. The local-projection link index cannot be relied on to\n * suppress it — that cache is refreshed by navigation/hydration/reconcile, not\n * by an upload completing, so a fast echo races ahead of it (see the residual\n * documented on `takePendingReconciliationEvents`). Recording the id at the\n * moment it is bound closes that race independently of cache timing.\n *\n * Entries are single-use: the first matching echo consumes them, so a genuine\n * later remote change to the same document still reconciles. The set is scoped\n * per `DomainScope` and cleared when the reconciler for that scope stops (scope\n * or identity teardown), so ids never leak across sessions.\n */\nconst originatedDocumentIdsByScope = new WeakMap<DomainScope, Set<string>>();\n\nfunction getOrCreateOriginatedSet(scope: DomainScope): Set<string> {\n const existing = originatedDocumentIdsByScope.get(scope);\n if (existing) {\n return existing;\n }\n const created = new Set<string>();\n originatedDocumentIdsByScope.set(scope, created);\n return created;\n}\n\n/** Record remote document ids this client just created/uploaded in `scope`. */\nexport function markOriginatedDocuments(\n scope: DomainScope,\n documentIds: Iterable<string>,\n): void {\n const set = getOrCreateOriginatedSet(scope);\n for (const documentId of documentIds) {\n if (documentId) {\n set.add(documentId);\n }\n }\n}\n\n/**\n * Consume an originated id: returns true (and removes it) when `documentId` was\n * an echo of this client's own write, false otherwise. Single-use so only the\n * first echo is suppressed.\n */\nexport function consumeOriginatedDocument(\n scope: DomainScope,\n documentId: string,\n): boolean {\n return originatedDocumentIdsByScope.get(scope)?.delete(documentId) ?? false;\n}\n\n/** Drop all originated ids for a scope (reconciler stop / identity teardown). */\nexport function clearOriginatedDocuments(scope: DomainScope): void {\n // Delete the whole entry so the Set can be collected immediately rather than\n // lingering empty for the lifetime of the DomainScope.\n originatedDocumentIdsByScope.delete(scope);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tearleads/client-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.53",
|
|
4
4
|
"description": "React-free Tearleads client runtime: local SQLite persistence, keys, sync, encrypted blobs, and domain workflows.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"default": "./index.js"
|
|
18
18
|
},
|
|
19
19
|
"./sqlite": {
|
|
20
|
-
"types": "./sqlite.d.ts",
|
|
21
|
-
"default": "./sqlite.js"
|
|
20
|
+
"types": "./sqlite/index.d.ts",
|
|
21
|
+
"default": "./sqlite/index.js"
|
|
22
22
|
},
|
|
23
23
|
"./sqlite/worker.js": "./sqlite/worker.js",
|
|
24
24
|
"./sqlite/sqlite3.wasm": "./sqlite/sqlite3.wasm",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { DatabaseWorkerClient, WorkerLike } from "
|
|
2
|
-
import { type CreateModuleDatabaseRuntimeOptions, type DatabaseRuntime, type ModuleWorkerConstructor, type ModuleWorkerLike } from "
|
|
1
|
+
import type { DatabaseWorkerClient, WorkerLike } from "../internal/sqlite-worker/client.js";
|
|
2
|
+
import { type CreateModuleDatabaseRuntimeOptions, type DatabaseRuntime, type ModuleWorkerConstructor, type ModuleWorkerLike } from "../internal/sqlite-worker/runtime.js";
|
|
3
3
|
export type SQLiteWorkerClient = DatabaseWorkerClient;
|
|
4
4
|
export type CreateSQLiteRuntimeOptions = CreateModuleDatabaseRuntimeOptions;
|
|
5
5
|
export type SQLiteRuntime = DatabaseRuntime;
|
|
@@ -8,11 +8,11 @@ export type SQLiteModuleWorkerLike = ModuleWorkerLike;
|
|
|
8
8
|
export interface SQLiteRuntimeWorker extends WorkerLike {
|
|
9
9
|
terminate(): void;
|
|
10
10
|
}
|
|
11
|
-
export { createModuleDatabaseRuntime as createSQLiteRuntime } from "
|
|
11
|
+
export { createModuleDatabaseRuntime as createSQLiteRuntime } from "../internal/sqlite-worker/runtime.js";
|
|
12
12
|
export declare function createSQLiteRuntimeFromWorker(worker: SQLiteRuntimeWorker): SQLiteRuntime;
|
|
13
|
-
export { purgeOpfsSqliteDatabase } from "
|
|
14
|
-
export type { DatabasePersistenceMode } from "
|
|
15
|
-
export { clientSQLiteSchema } from "
|
|
16
|
-
export { type ClientSQLiteDatabase as SQLiteDatabase, type ClientSQLitePersistenceRuntime as SQLitePersistenceRuntime, type ClientSQLiteTransactionScope as SQLiteTransaction, getClientSQLitePersistenceRuntime as getSQLitePersistenceRuntime, } from "
|
|
17
|
-
export { createExecSql, defineSqlTableSchema, type ExecSql, type ExecSqlClientLike, ensureSqlTables, resetConnectionSchemaMemo, runSerializedSqlMutation, type SqlArrayRow, type SqlBind, type SqlRow, type SqlRowMode, type SqlRowValue, type SqlTableSchema, unavailableExecSql, } from "
|
|
18
|
-
export { EPHEMERAL_STORAGE_POLICY, isPersistentStorageSupported, PERSISTENT_STORAGE_POLICY, type RequestPersistentStorageResult, requestPersistentStorage, resolveStoragePersistencePolicy, type StoragePersistencePolicy, } from "
|
|
13
|
+
export { purgeOpfsSqliteDatabase } from "../internal/sqlite-worker/purgeOpfsDatabase.js";
|
|
14
|
+
export type { DatabasePersistenceMode } from "../internal/sqlite-worker/types.js";
|
|
15
|
+
export { clientSQLiteSchema } from "../data/sqlite/schema.js";
|
|
16
|
+
export { type ClientSQLiteDatabase as SQLiteDatabase, type ClientSQLitePersistenceRuntime as SQLitePersistenceRuntime, type ClientSQLiteTransactionScope as SQLiteTransaction, getClientSQLitePersistenceRuntime as getSQLitePersistenceRuntime, } from "../data/sqlite/sqlitePersistenceRuntime.js";
|
|
17
|
+
export { createExecSql, defineSqlTableSchema, type ExecSql, type ExecSqlClientLike, ensureSqlTables, resetConnectionSchemaMemo, runSerializedSqlMutation, type SqlArrayRow, type SqlBind, type SqlRow, type SqlRowMode, type SqlRowValue, type SqlTableSchema, unavailableExecSql, } from "../data/sqlite/sqlSchema.js";
|
|
18
|
+
export { EPHEMERAL_STORAGE_POLICY, isPersistentStorageSupported, PERSISTENT_STORAGE_POLICY, type RequestPersistentStorageResult, requestPersistentStorage, resolveStoragePersistencePolicy, type StoragePersistencePolicy, } from "../data/sqlite/storagePersistence.js";
|
package/sqlite/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createDatabaseRuntime, } from "../internal/sqlite-worker/runtime.js";
|
|
2
|
+
export { createModuleDatabaseRuntime as createSQLiteRuntime } from "../internal/sqlite-worker/runtime.js";
|
|
3
|
+
export function createSQLiteRuntimeFromWorker(worker) {
|
|
4
|
+
return createDatabaseRuntime(worker);
|
|
5
|
+
}
|
|
6
|
+
export { purgeOpfsSqliteDatabase } from "../internal/sqlite-worker/purgeOpfsDatabase.js";
|
|
7
|
+
export { clientSQLiteSchema } from "../data/sqlite/schema.js";
|
|
8
|
+
export { getClientSQLitePersistenceRuntime as getSQLitePersistenceRuntime, } from "../data/sqlite/sqlitePersistenceRuntime.js";
|
|
9
|
+
export { createExecSql, defineSqlTableSchema, ensureSqlTables, resetConnectionSchemaMemo, runSerializedSqlMutation, unavailableExecSql, } from "../data/sqlite/sqlSchema.js";
|
|
10
|
+
export { EPHEMERAL_STORAGE_POLICY, isPersistentStorageSupported, PERSISTENT_STORAGE_POLICY, requestPersistentStorage, resolveStoragePersistencePolicy, } from "../data/sqlite/storagePersistence.js";
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,qBAAqB,GAItB,MAAM,kCAAkC,CAAC;AAW1C,OAAO,EAAE,2BAA2B,IAAI,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAEtG,MAAM,UAAU,6BAA6B,CAC3C,MAA2B;IAE3B,OAAO,qBAAqB,CAAC,MAAM,CAAC,CAAC;AACvC,CAAC;AAED,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AAEvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAIL,iCAAiC,IAAI,2BAA2B,GACjE,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,aAAa,EACb,oBAAoB,EAGpB,eAAe,EACf,yBAAyB,EACzB,wBAAwB,EAOxB,kBAAkB,GACnB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,wBAAwB,EACxB,4BAA4B,EAC5B,yBAAyB,EAEzB,wBAAwB,EACxB,+BAA+B,GAEhC,MAAM,mCAAmC,CAAC","sourcesContent":["import type {\n DatabaseWorkerClient,\n WorkerLike,\n} from \"@tearleads/sqlite-worker/client\";\nimport {\n type CreateModuleDatabaseRuntimeOptions,\n createDatabaseRuntime,\n type DatabaseRuntime,\n type ModuleWorkerConstructor,\n type ModuleWorkerLike,\n} from \"@tearleads/sqlite-worker/runtime\";\n\nexport type SQLiteWorkerClient = DatabaseWorkerClient;\nexport type CreateSQLiteRuntimeOptions = CreateModuleDatabaseRuntimeOptions;\nexport type SQLiteRuntime = DatabaseRuntime;\nexport type SQLiteModuleWorkerConstructor = ModuleWorkerConstructor;\nexport type SQLiteModuleWorkerLike = ModuleWorkerLike;\nexport interface SQLiteRuntimeWorker extends WorkerLike {\n terminate(): void;\n}\n\nexport { createModuleDatabaseRuntime as createSQLiteRuntime } from \"@tearleads/sqlite-worker/runtime\";\n\nexport function createSQLiteRuntimeFromWorker(\n worker: SQLiteRuntimeWorker,\n): SQLiteRuntime {\n return createDatabaseRuntime(worker);\n}\n\nexport { purgeOpfsSqliteDatabase } from \"@tearleads/sqlite-worker/purge-opfs-database\";\nexport type { DatabasePersistenceMode } from \"@tearleads/sqlite-worker/types\";\nexport { clientSQLiteSchema } from \"../data/sqlite/schema\";\nexport {\n type ClientSQLiteDatabase as SQLiteDatabase,\n type ClientSQLitePersistenceRuntime as SQLitePersistenceRuntime,\n type ClientSQLiteTransactionScope as SQLiteTransaction,\n getClientSQLitePersistenceRuntime as getSQLitePersistenceRuntime,\n} from \"../data/sqlite/sqlitePersistenceRuntime\";\nexport {\n createExecSql,\n defineSqlTableSchema,\n type ExecSql,\n type ExecSqlClientLike,\n ensureSqlTables,\n resetConnectionSchemaMemo,\n runSerializedSqlMutation,\n type SqlArrayRow,\n type SqlBind,\n type SqlRow,\n type SqlRowMode,\n type SqlRowValue,\n type SqlTableSchema,\n unavailableExecSql,\n} from \"../data/sqlite/sqlSchema\";\nexport {\n EPHEMERAL_STORAGE_POLICY,\n isPersistentStorageSupported,\n PERSISTENT_STORAGE_POLICY,\n type RequestPersistentStorageResult,\n requestPersistentStorage,\n resolveStoragePersistencePolicy,\n type StoragePersistencePolicy,\n} from \"../data/sqlite/storagePersistence\";\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { markOriginatedDocuments } from "../../../sync/reconciliation/originatedDocuments.js";
|
|
2
1
|
import { clearDocumentSyncFailure, DOCUMENTS_APP_KIND, recordDocumentSyncFailure, resolveDocumentCreateAuthor, runSerializedSqlMutation, } from "../../../workflows/documents/index.js";
|
|
3
2
|
import { createRuntimePrincipalPolicyWarmer } from "../../../workflows/principals/runtimePolicyWarmer.js";
|
|
3
|
+
import { markOriginatedDocuments } from "../../../workflows/sync/index.js";
|
|
4
4
|
import { requestDocumentStoreSync } from "../registry.js";
|
|
5
5
|
import { dropUnavailablePendingAttachment, pendingAttachmentMatchesIntent, resolveAttachmentSourceUpload, } from "./attachmentSourceUpload.js";
|
|
6
6
|
import { uploadAttachmentWithWriterProjectionRetry } from "./attachmentUploadAttempt.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncAttachments.js","sourceRoot":"","sources":["syncAttachments.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BlobByteSource } from \"../../../data/blobContracts\";\nimport type { BlobSourceSnapshot } from \"../../../data/documents/blob/shared/blobSourceSnapshot\";\nimport { markOriginatedDocuments } from \"../../../sync/reconciliation/originatedDocuments\";\nimport type { uploadPreparedDocumentAttachment as uploadDocumentAttachment } from \"../../../workflows/blobs/upload\";\nimport {\n clearDocumentSyncFailure,\n DOCUMENTS_APP_KIND,\n type DocumentRecord,\n type PendingAttachmentRecord,\n recordDocumentSyncFailure,\n resolveDocumentCreateAuthor,\n runSerializedSqlMutation,\n} from \"../../../workflows/documents\";\nimport { createRuntimePrincipalPolicyWarmer } from \"../../../workflows/principals/runtimePolicyWarmer\";\nimport { requestDocumentStoreSync } from \"../registry\";\nimport {\n dropUnavailablePendingAttachment,\n pendingAttachmentMatchesIntent,\n resolveAttachmentSourceUpload,\n} from \"./attachmentSourceUpload\";\nimport { uploadAttachmentWithWriterProjectionRetry } from \"./attachmentUploadAttempt\";\nimport {\n type AttachmentUploadLaneReporter,\n createAttachmentUploadFailedError,\n createAttachmentUploadLaneReporter,\n} from \"./attachmentUploadLane\";\nimport type { AttachmentUploadResume } from \"./attachmentUploadResume\";\nimport type {\n DocumentAttachmentBinding,\n DocumentState,\n DocumentStoreState,\n EncapsulationKeyPair,\n PendingMutationSyncResult,\n} from \"./state\";\nimport {\n isNewPendingAttachmentSlot,\n removeSettledPendingAttachment,\n settleRecoveredAttachment,\n settleUploadedAttachment,\n} from \"./syncAttachmentSettlement\";\nimport {\n type DocumentStoreSyncGeneration,\n isDocumentStoreSyncGenerationCurrent,\n} from \"./syncGeneration\";\nimport { ensureRemoteDocument } from \"./syncShared\";\n\n// Preserve transient failures for retry. Drop irrecoverable missing bytes so a\n// pending attachment cannot wedge the document sync lane across restarts.\ntype PendingAttachmentUploadOutcome =\n | \"uploaded\"\n | \"recovered\"\n | \"retry\"\n | \"dropped\";\n\ninterface AttachmentSyncProgress {\n activeBindingBySlotId: Map<string, DocumentAttachmentBinding>;\n fetchedRemoteBindings: boolean;\n settledAttachment: boolean;\n uploadedAttachment: boolean;\n}\n\nfunction rearmStaleAttachmentSync(\n state: DocumentStoreState,\n generation: DocumentStoreSyncGeneration,\n): void {\n if (!isDocumentStoreSyncGenerationCurrent(state, generation)) {\n requestDocumentStoreSync(state);\n }\n}\n\nexport async function syncPendingAttachments(\n state: DocumentStoreState,\n nextRecord: DocumentRecord,\n encapsulationKeyPair: EncapsulationKeyPair,\n attachmentGeneration: DocumentStoreSyncGeneration,\n): Promise<PendingMutationSyncResult> {\n if (state.pendingAttachments.length === 0) {\n return { completed: false, nextRecord };\n }\n\n const currentDoc = state.doc;\n if (!currentDoc) {\n return { completed: false, nextRecord };\n }\n\n const currentRecord = await ensureRemoteDocumentForAttachmentSync(\n state,\n currentDoc,\n nextRecord,\n encapsulationKeyPair,\n attachmentGeneration,\n );\n if (!currentRecord?.documentId) {\n return { completed: false, nextRecord };\n }\n const remoteDocumentId = currentRecord.documentId;\n\n const progress: AttachmentSyncProgress = {\n activeBindingBySlotId: new Map(),\n fetchedRemoteBindings: false,\n settledAttachment: false,\n uploadedAttachment: false,\n };\n\n for (const pendingAttachment of [...state.pendingAttachments]) {\n if (!isDocumentStoreSyncGenerationCurrent(state, attachmentGeneration)) {\n requestDocumentStoreSync(state);\n return {\n completed: progress.settledAttachment,\n nextRecord: currentRecord,\n };\n }\n const bindingsReady = await loadRemoteAttachmentBindings({\n pendingAttachment,\n progress,\n remoteDocumentId,\n state,\n });\n if (!bindingsReady) {\n return {\n completed: progress.settledAttachment,\n nextRecord: currentRecord,\n };\n }\n\n const outcome = await syncPendingAttachmentUpload({\n activeBindingBySlotId: progress.activeBindingBySlotId,\n attachmentGeneration,\n encapsulationKeyPair,\n pendingAttachment,\n remoteDocumentId,\n state,\n });\n if (outcome === \"retry\") {\n rearmStaleAttachmentSync(state, attachmentGeneration);\n return {\n completed: progress.settledAttachment,\n nextRecord: currentRecord,\n };\n }\n\n // Re-check after the upload's awaits: settling writes local rows, which a\n // teardown that happened mid-upload has just deleted on purpose.\n if (!isDocumentStoreSyncGenerationCurrent(state, attachmentGeneration)) {\n requestDocumentStoreSync(state);\n return {\n completed: progress.settledAttachment,\n nextRecord: currentRecord,\n };\n }\n removeSettledPendingAttachment(state, currentDoc, pendingAttachment);\n if (outcome === \"uploaded\" || outcome === \"recovered\") {\n progress.settledAttachment = true;\n }\n if (outcome === \"uploaded\") {\n progress.uploadedAttachment = true;\n }\n }\n\n if (!progress.settledAttachment) {\n return { completed: false, nextRecord: currentRecord };\n }\n\n // Only record the origination once a write landed: the server echoes a\n // document_update_created for it, and this lets the reconciler skip\n // rediscovering a delta we already have locally rather than cycling its lane\n // per uploaded file. Marking here (not before the loop) avoids a dangling id\n // that would suppress the next genuine remote update if every upload failed.\n if (progress.uploadedAttachment) {\n markOriginatedDocuments(state.runtime.state.domainScope, [\n remoteDocumentId,\n ]);\n }\n\n // A settled attachment pass invalidates any recorded failure itself: a\n // retried upload with no pending CRDT update never reaches the document\n // submission that would otherwise clear it, and the stale row would flag the\n // next unrelated edit as failed before it was attempted.\n await clearDocumentSyncFailure(state.runtime.infra.execSql, {\n appKind: DOCUMENTS_APP_KIND,\n localId: state.localId,\n });\n\n // The snapshot was already republished progressively inside the loop as each\n // attachment settled, so there is nothing left to publish here.\n return { completed: true, nextRecord: currentRecord };\n}\n\nasync function loadRemoteAttachmentBindings(input: {\n pendingAttachment: PendingAttachmentRecord;\n progress: AttachmentSyncProgress;\n remoteDocumentId: string;\n state: DocumentStoreState;\n}): Promise<boolean> {\n const { pendingAttachment, progress, state } = input;\n const freshUpload =\n pendingAttachment.upload == null &&\n isNewPendingAttachmentSlot(state, pendingAttachment);\n if (progress.fetchedRemoteBindings || freshUpload) {\n return true;\n }\n\n const remoteBindings = await state.runtime.apiClient.listDocumentAttachments(\n input.remoteDocumentId,\n );\n if (!remoteBindings) {\n return false;\n }\n\n progress.fetchedRemoteBindings = true;\n for (const binding of remoteBindings) {\n progress.activeBindingBySlotId.set(binding.slotId, binding);\n }\n return true;\n}\n\nasync function ensureRemoteDocumentForAttachmentSync(\n state: DocumentStoreState,\n currentDoc: DocumentState,\n nextRecord: DocumentRecord,\n encapsulationKeyPair: EncapsulationKeyPair,\n generation?: DocumentStoreSyncGeneration,\n): Promise<DocumentRecord | null> {\n if (nextRecord.documentId) {\n return nextRecord;\n }\n\n return ensureRemoteDocument(\n state,\n currentDoc,\n nextRecord,\n encapsulationKeyPair,\n generation,\n );\n}\n\ninterface PendingAttachmentUploadInput {\n activeBindingBySlotId: Map<string, DocumentAttachmentBinding>;\n attachmentGeneration: DocumentStoreSyncGeneration;\n encapsulationKeyPair: EncapsulationKeyPair;\n pendingAttachment: PendingAttachmentRecord;\n remoteDocumentId: string;\n state: DocumentStoreState;\n}\n\nasync function recoverCommittedAttachment(\n input: PendingAttachmentUploadInput,\n): Promise<boolean> {\n const { pendingAttachment, state } = input;\n const persistedUpload = pendingAttachment.upload;\n const activeBinding = input.activeBindingBySlotId.get(\n pendingAttachment.slotId,\n );\n if (!persistedUpload || activeBinding?.blobId !== persistedUpload.blobId) {\n return false;\n }\n\n const uploadLane = createAttachmentUploadLaneReporter({\n blobId: persistedUpload.blobId,\n domainScope: state.runtime.state.domainScope,\n pendingAttachment,\n });\n try {\n await settleRecoveredAttachment({\n attachmentGeneration: input.attachmentGeneration,\n binding: activeBinding,\n pendingAttachment,\n remoteDocumentId: input.remoteDocumentId,\n state,\n uploadLane,\n });\n return true;\n } catch (error) {\n reportAttachmentUploadFailure({\n attachmentGeneration: input.attachmentGeneration,\n error,\n pendingAttachment,\n state,\n uploadLane,\n });\n throw error;\n }\n}\n\nfunction createAttachmentBaseUploadInput(input: {\n author: Parameters<typeof uploadDocumentAttachment>[0][\"author\"];\n pendingUpload: PendingAttachmentUploadInput;\n resume: AttachmentUploadResume;\n source: BlobByteSource;\n sourceSnapshot: BlobSourceSnapshot;\n uploadLane: AttachmentUploadLaneReporter;\n}): Parameters<typeof uploadDocumentAttachment>[0] {\n const { pendingAttachment, state } = input.pendingUpload;\n return {\n apiClient: state.runtime.apiClient,\n author: input.author,\n blobId: input.resume.blobId,\n bytes: input.source,\n contentKey: input.resume.contentKey,\n contentKeyEpoch: input.resume.contentKeyEpoch,\n documentId: input.pendingUpload.remoteDocumentId,\n execSql: state.runtime.infra.execSql,\n expectedBindingId:\n input.pendingUpload.activeBindingBySlotId.get(pendingAttachment.slotId)\n ?.bindingId ?? null,\n nonceSeed: input.resume.nonceSeed,\n multipart: input.resume.multipart,\n onMultipartProgress: input.uploadLane.onMultipartProgress,\n onStageResolved: input.resume.onStageResolved,\n onStageUnavailable: async (stageId) => {\n await input.resume.onStageUnavailable(\n stageId,\n input.pendingUpload.remoteDocumentId,\n );\n requestDocumentStoreSync(state);\n },\n resolveProjectionUserKey: state.resolveProjectionUserKey,\n slotId: pendingAttachment.slotId,\n sourceSnapshot: input.sourceSnapshot,\n targetSecretKey: input.pendingUpload.encapsulationKeyPair.secretKey,\n warmReferencedPrincipalPolicies: createRuntimePrincipalPolicyWarmer(\n state.runtime,\n ),\n };\n}\n\nasync function uploadPendingAttachmentBytes(input: {\n baseUploadInput: Parameters<typeof uploadDocumentAttachment>[0];\n pendingUpload: PendingAttachmentUploadInput;\n uploadLane: AttachmentUploadLaneReporter;\n}): Promise<PendingAttachmentUploadOutcome> {\n const { pendingAttachment, state } = input.pendingUpload;\n const writerProjection =\n state.writerProjection?.documentId === input.pendingUpload.remoteDocumentId\n ? state.writerProjection\n : null;\n const writerProjectionGeneration = state.writerProjectionGeneration;\n\n try {\n const uploadAttempt = await uploadAttachmentWithWriterProjectionRetry({\n baseUploadInput: input.baseUploadInput,\n state,\n writerProjection,\n });\n const { error: uploadError, remoteSyncBlocked, uploaded } = uploadAttempt;\n if (!uploaded) {\n if (remoteSyncBlocked) {\n input.uploadLane.failIfStarted(\n new Error(\"Attachment upload paused because remote sync is blocked.\"),\n );\n return \"retry\";\n }\n reportAttachmentUploadFailure({\n attachmentGeneration: input.pendingUpload.attachmentGeneration,\n error: uploadError,\n pendingAttachment,\n state,\n uploadLane: input.uploadLane,\n });\n return \"retry\";\n }\n\n await settleUploadedAttachment({\n activeBindingBySlotId: input.pendingUpload.activeBindingBySlotId,\n attachmentGeneration: input.pendingUpload.attachmentGeneration,\n pendingAttachment,\n remoteDocumentId: input.pendingUpload.remoteDocumentId,\n state,\n uploadLane: input.uploadLane,\n uploaded,\n writerProjectionGeneration,\n });\n return \"uploaded\";\n } catch (error) {\n reportAttachmentUploadFailure({\n attachmentGeneration: input.pendingUpload.attachmentGeneration,\n error,\n pendingAttachment,\n state,\n uploadLane: input.uploadLane,\n });\n throw error;\n }\n}\n\nasync function syncPendingAttachmentUpload(\n input: PendingAttachmentUploadInput,\n): Promise<PendingAttachmentUploadOutcome> {\n const { pendingAttachment, state } = input;\n // The binding fetch before this call awaits; a teardown during it must not\n // let a brand-new upload start against rows that no longer exist.\n if (\n !isDocumentStoreSyncGenerationCurrent(state, input.attachmentGeneration)\n ) {\n return \"retry\";\n }\n if (!pendingAttachmentMatchesIntent(input)) {\n return dropUnavailablePendingAttachment(input);\n }\n if (await recoverCommittedAttachment(input)) {\n return \"recovered\";\n }\n\n const source = await state.runtime.infra.blobStore.openByteSource(\n pendingAttachment.storageKey,\n );\n if (!source) {\n return dropUnavailablePendingAttachment(input);\n }\n\n const author = resolveDocumentCreateAuthor(state.runtime);\n if (!author) {\n state.runtime.util.log(\n \"Documents: skipped attachment upload because the writer context is unavailable.\",\n );\n return \"retry\";\n }\n\n const prepared = await resolveAttachmentSourceUpload({\n attachmentGeneration: input.attachmentGeneration,\n pendingAttachment,\n source,\n state,\n });\n // Preparation awaited (byte-source open plus resume resolution); a teardown\n // during those awaits must not let the upload itself start — its remote\n // commit could not be recalled once sent.\n if (\n !isDocumentStoreSyncGenerationCurrent(state, input.attachmentGeneration)\n ) {\n return \"retry\";\n }\n if (!prepared) return dropUnavailablePendingAttachment(input);\n const { resume, snapshot } = prepared;\n const uploadLane = createAttachmentUploadLaneReporter({\n blobId: resume.blobId,\n domainScope: state.runtime.state.domainScope,\n pendingAttachment,\n });\n const baseUploadInput = createAttachmentBaseUploadInput({\n author,\n pendingUpload: input,\n resume,\n source,\n sourceSnapshot: snapshot,\n uploadLane,\n });\n return uploadPendingAttachmentBytes({\n baseUploadInput,\n pendingUpload: input,\n uploadLane,\n });\n}\n\nfunction reportAttachmentUploadFailure(input: {\n attachmentGeneration: DocumentStoreSyncGeneration;\n error: unknown;\n pendingAttachment: PendingAttachmentRecord;\n state: DocumentStoreState;\n uploadLane: AttachmentUploadLaneReporter;\n}): void {\n const error = createAttachmentUploadFailedError(\n input.pendingAttachment,\n input.error,\n );\n input.uploadLane.fail(error);\n input.state.runtime.util.log(`Documents: ${error.message}`);\n // Surface the stuck upload in the write queue; a later successful sync pass\n // for the document clears the record. Validated inside the serialized\n // mutation: a teardown (reset/discard) racing this pass just cleared the\n // failure row, and re-recording it would resurrect a phantom queue entry\n // for work that no longer exists.\n void runSerializedSqlMutation(\n input.state.runtime.infra.execSql,\n async (lockedExecSql) => {\n if (\n !isDocumentStoreSyncGenerationCurrent(\n input.state,\n input.attachmentGeneration,\n )\n ) {\n return;\n }\n await recordDocumentSyncFailure(\n lockedExecSql,\n { appKind: DOCUMENTS_APP_KIND, localId: input.state.localId },\n {\n attemptedAt: new Date().toISOString(),\n message: error.message,\n status: null,\n },\n );\n },\n ).catch(() => undefined);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"syncAttachments.js","sourceRoot":"","sources":["syncAttachments.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BlobByteSource } from \"../../../data/blobContracts\";\nimport type { BlobSourceSnapshot } from \"../../../data/documents/blob/shared/blobSourceSnapshot\";\nimport type { uploadPreparedDocumentAttachment as uploadDocumentAttachment } from \"../../../workflows/blobs/upload\";\nimport {\n clearDocumentSyncFailure,\n DOCUMENTS_APP_KIND,\n type DocumentRecord,\n type PendingAttachmentRecord,\n recordDocumentSyncFailure,\n resolveDocumentCreateAuthor,\n runSerializedSqlMutation,\n} from \"../../../workflows/documents\";\nimport { createRuntimePrincipalPolicyWarmer } from \"../../../workflows/principals/runtimePolicyWarmer\";\nimport { markOriginatedDocuments } from \"../../../workflows/sync\";\nimport { requestDocumentStoreSync } from \"../registry\";\nimport {\n dropUnavailablePendingAttachment,\n pendingAttachmentMatchesIntent,\n resolveAttachmentSourceUpload,\n} from \"./attachmentSourceUpload\";\nimport { uploadAttachmentWithWriterProjectionRetry } from \"./attachmentUploadAttempt\";\nimport {\n type AttachmentUploadLaneReporter,\n createAttachmentUploadFailedError,\n createAttachmentUploadLaneReporter,\n} from \"./attachmentUploadLane\";\nimport type { AttachmentUploadResume } from \"./attachmentUploadResume\";\nimport type {\n DocumentAttachmentBinding,\n DocumentState,\n DocumentStoreState,\n EncapsulationKeyPair,\n PendingMutationSyncResult,\n} from \"./state\";\nimport {\n isNewPendingAttachmentSlot,\n removeSettledPendingAttachment,\n settleRecoveredAttachment,\n settleUploadedAttachment,\n} from \"./syncAttachmentSettlement\";\nimport {\n type DocumentStoreSyncGeneration,\n isDocumentStoreSyncGenerationCurrent,\n} from \"./syncGeneration\";\nimport { ensureRemoteDocument } from \"./syncShared\";\n\n// Preserve transient failures for retry. Drop irrecoverable missing bytes so a\n// pending attachment cannot wedge the document sync lane across restarts.\ntype PendingAttachmentUploadOutcome =\n | \"uploaded\"\n | \"recovered\"\n | \"retry\"\n | \"dropped\";\n\ninterface AttachmentSyncProgress {\n activeBindingBySlotId: Map<string, DocumentAttachmentBinding>;\n fetchedRemoteBindings: boolean;\n settledAttachment: boolean;\n uploadedAttachment: boolean;\n}\n\nfunction rearmStaleAttachmentSync(\n state: DocumentStoreState,\n generation: DocumentStoreSyncGeneration,\n): void {\n if (!isDocumentStoreSyncGenerationCurrent(state, generation)) {\n requestDocumentStoreSync(state);\n }\n}\n\nexport async function syncPendingAttachments(\n state: DocumentStoreState,\n nextRecord: DocumentRecord,\n encapsulationKeyPair: EncapsulationKeyPair,\n attachmentGeneration: DocumentStoreSyncGeneration,\n): Promise<PendingMutationSyncResult> {\n if (state.pendingAttachments.length === 0) {\n return { completed: false, nextRecord };\n }\n\n const currentDoc = state.doc;\n if (!currentDoc) {\n return { completed: false, nextRecord };\n }\n\n const currentRecord = await ensureRemoteDocumentForAttachmentSync(\n state,\n currentDoc,\n nextRecord,\n encapsulationKeyPair,\n attachmentGeneration,\n );\n if (!currentRecord?.documentId) {\n return { completed: false, nextRecord };\n }\n const remoteDocumentId = currentRecord.documentId;\n\n const progress: AttachmentSyncProgress = {\n activeBindingBySlotId: new Map(),\n fetchedRemoteBindings: false,\n settledAttachment: false,\n uploadedAttachment: false,\n };\n\n for (const pendingAttachment of [...state.pendingAttachments]) {\n if (!isDocumentStoreSyncGenerationCurrent(state, attachmentGeneration)) {\n requestDocumentStoreSync(state);\n return {\n completed: progress.settledAttachment,\n nextRecord: currentRecord,\n };\n }\n const bindingsReady = await loadRemoteAttachmentBindings({\n pendingAttachment,\n progress,\n remoteDocumentId,\n state,\n });\n if (!bindingsReady) {\n return {\n completed: progress.settledAttachment,\n nextRecord: currentRecord,\n };\n }\n\n const outcome = await syncPendingAttachmentUpload({\n activeBindingBySlotId: progress.activeBindingBySlotId,\n attachmentGeneration,\n encapsulationKeyPair,\n pendingAttachment,\n remoteDocumentId,\n state,\n });\n if (outcome === \"retry\") {\n rearmStaleAttachmentSync(state, attachmentGeneration);\n return {\n completed: progress.settledAttachment,\n nextRecord: currentRecord,\n };\n }\n\n // Re-check after the upload's awaits: settling writes local rows, which a\n // teardown that happened mid-upload has just deleted on purpose.\n if (!isDocumentStoreSyncGenerationCurrent(state, attachmentGeneration)) {\n requestDocumentStoreSync(state);\n return {\n completed: progress.settledAttachment,\n nextRecord: currentRecord,\n };\n }\n removeSettledPendingAttachment(state, currentDoc, pendingAttachment);\n if (outcome === \"uploaded\" || outcome === \"recovered\") {\n progress.settledAttachment = true;\n }\n if (outcome === \"uploaded\") {\n progress.uploadedAttachment = true;\n }\n }\n\n if (!progress.settledAttachment) {\n return { completed: false, nextRecord: currentRecord };\n }\n\n // Only record the origination once a write landed: the server echoes a\n // document_update_created for it, and this lets the reconciler skip\n // rediscovering a delta we already have locally rather than cycling its lane\n // per uploaded file. Marking here (not before the loop) avoids a dangling id\n // that would suppress the next genuine remote update if every upload failed.\n if (progress.uploadedAttachment) {\n markOriginatedDocuments(state.runtime.state.domainScope, [\n remoteDocumentId,\n ]);\n }\n\n // A settled attachment pass invalidates any recorded failure itself: a\n // retried upload with no pending CRDT update never reaches the document\n // submission that would otherwise clear it, and the stale row would flag the\n // next unrelated edit as failed before it was attempted.\n await clearDocumentSyncFailure(state.runtime.infra.execSql, {\n appKind: DOCUMENTS_APP_KIND,\n localId: state.localId,\n });\n\n // The snapshot was already republished progressively inside the loop as each\n // attachment settled, so there is nothing left to publish here.\n return { completed: true, nextRecord: currentRecord };\n}\n\nasync function loadRemoteAttachmentBindings(input: {\n pendingAttachment: PendingAttachmentRecord;\n progress: AttachmentSyncProgress;\n remoteDocumentId: string;\n state: DocumentStoreState;\n}): Promise<boolean> {\n const { pendingAttachment, progress, state } = input;\n const freshUpload =\n pendingAttachment.upload == null &&\n isNewPendingAttachmentSlot(state, pendingAttachment);\n if (progress.fetchedRemoteBindings || freshUpload) {\n return true;\n }\n\n const remoteBindings = await state.runtime.apiClient.listDocumentAttachments(\n input.remoteDocumentId,\n );\n if (!remoteBindings) {\n return false;\n }\n\n progress.fetchedRemoteBindings = true;\n for (const binding of remoteBindings) {\n progress.activeBindingBySlotId.set(binding.slotId, binding);\n }\n return true;\n}\n\nasync function ensureRemoteDocumentForAttachmentSync(\n state: DocumentStoreState,\n currentDoc: DocumentState,\n nextRecord: DocumentRecord,\n encapsulationKeyPair: EncapsulationKeyPair,\n generation?: DocumentStoreSyncGeneration,\n): Promise<DocumentRecord | null> {\n if (nextRecord.documentId) {\n return nextRecord;\n }\n\n return ensureRemoteDocument(\n state,\n currentDoc,\n nextRecord,\n encapsulationKeyPair,\n generation,\n );\n}\n\ninterface PendingAttachmentUploadInput {\n activeBindingBySlotId: Map<string, DocumentAttachmentBinding>;\n attachmentGeneration: DocumentStoreSyncGeneration;\n encapsulationKeyPair: EncapsulationKeyPair;\n pendingAttachment: PendingAttachmentRecord;\n remoteDocumentId: string;\n state: DocumentStoreState;\n}\n\nasync function recoverCommittedAttachment(\n input: PendingAttachmentUploadInput,\n): Promise<boolean> {\n const { pendingAttachment, state } = input;\n const persistedUpload = pendingAttachment.upload;\n const activeBinding = input.activeBindingBySlotId.get(\n pendingAttachment.slotId,\n );\n if (!persistedUpload || activeBinding?.blobId !== persistedUpload.blobId) {\n return false;\n }\n\n const uploadLane = createAttachmentUploadLaneReporter({\n blobId: persistedUpload.blobId,\n domainScope: state.runtime.state.domainScope,\n pendingAttachment,\n });\n try {\n await settleRecoveredAttachment({\n attachmentGeneration: input.attachmentGeneration,\n binding: activeBinding,\n pendingAttachment,\n remoteDocumentId: input.remoteDocumentId,\n state,\n uploadLane,\n });\n return true;\n } catch (error) {\n reportAttachmentUploadFailure({\n attachmentGeneration: input.attachmentGeneration,\n error,\n pendingAttachment,\n state,\n uploadLane,\n });\n throw error;\n }\n}\n\nfunction createAttachmentBaseUploadInput(input: {\n author: Parameters<typeof uploadDocumentAttachment>[0][\"author\"];\n pendingUpload: PendingAttachmentUploadInput;\n resume: AttachmentUploadResume;\n source: BlobByteSource;\n sourceSnapshot: BlobSourceSnapshot;\n uploadLane: AttachmentUploadLaneReporter;\n}): Parameters<typeof uploadDocumentAttachment>[0] {\n const { pendingAttachment, state } = input.pendingUpload;\n return {\n apiClient: state.runtime.apiClient,\n author: input.author,\n blobId: input.resume.blobId,\n bytes: input.source,\n contentKey: input.resume.contentKey,\n contentKeyEpoch: input.resume.contentKeyEpoch,\n documentId: input.pendingUpload.remoteDocumentId,\n execSql: state.runtime.infra.execSql,\n expectedBindingId:\n input.pendingUpload.activeBindingBySlotId.get(pendingAttachment.slotId)\n ?.bindingId ?? null,\n nonceSeed: input.resume.nonceSeed,\n multipart: input.resume.multipart,\n onMultipartProgress: input.uploadLane.onMultipartProgress,\n onStageResolved: input.resume.onStageResolved,\n onStageUnavailable: async (stageId) => {\n await input.resume.onStageUnavailable(\n stageId,\n input.pendingUpload.remoteDocumentId,\n );\n requestDocumentStoreSync(state);\n },\n resolveProjectionUserKey: state.resolveProjectionUserKey,\n slotId: pendingAttachment.slotId,\n sourceSnapshot: input.sourceSnapshot,\n targetSecretKey: input.pendingUpload.encapsulationKeyPair.secretKey,\n warmReferencedPrincipalPolicies: createRuntimePrincipalPolicyWarmer(\n state.runtime,\n ),\n };\n}\n\nasync function uploadPendingAttachmentBytes(input: {\n baseUploadInput: Parameters<typeof uploadDocumentAttachment>[0];\n pendingUpload: PendingAttachmentUploadInput;\n uploadLane: AttachmentUploadLaneReporter;\n}): Promise<PendingAttachmentUploadOutcome> {\n const { pendingAttachment, state } = input.pendingUpload;\n const writerProjection =\n state.writerProjection?.documentId === input.pendingUpload.remoteDocumentId\n ? state.writerProjection\n : null;\n const writerProjectionGeneration = state.writerProjectionGeneration;\n\n try {\n const uploadAttempt = await uploadAttachmentWithWriterProjectionRetry({\n baseUploadInput: input.baseUploadInput,\n state,\n writerProjection,\n });\n const { error: uploadError, remoteSyncBlocked, uploaded } = uploadAttempt;\n if (!uploaded) {\n if (remoteSyncBlocked) {\n input.uploadLane.failIfStarted(\n new Error(\"Attachment upload paused because remote sync is blocked.\"),\n );\n return \"retry\";\n }\n reportAttachmentUploadFailure({\n attachmentGeneration: input.pendingUpload.attachmentGeneration,\n error: uploadError,\n pendingAttachment,\n state,\n uploadLane: input.uploadLane,\n });\n return \"retry\";\n }\n\n await settleUploadedAttachment({\n activeBindingBySlotId: input.pendingUpload.activeBindingBySlotId,\n attachmentGeneration: input.pendingUpload.attachmentGeneration,\n pendingAttachment,\n remoteDocumentId: input.pendingUpload.remoteDocumentId,\n state,\n uploadLane: input.uploadLane,\n uploaded,\n writerProjectionGeneration,\n });\n return \"uploaded\";\n } catch (error) {\n reportAttachmentUploadFailure({\n attachmentGeneration: input.pendingUpload.attachmentGeneration,\n error,\n pendingAttachment,\n state,\n uploadLane: input.uploadLane,\n });\n throw error;\n }\n}\n\nasync function syncPendingAttachmentUpload(\n input: PendingAttachmentUploadInput,\n): Promise<PendingAttachmentUploadOutcome> {\n const { pendingAttachment, state } = input;\n // The binding fetch before this call awaits; a teardown during it must not\n // let a brand-new upload start against rows that no longer exist.\n if (\n !isDocumentStoreSyncGenerationCurrent(state, input.attachmentGeneration)\n ) {\n return \"retry\";\n }\n if (!pendingAttachmentMatchesIntent(input)) {\n return dropUnavailablePendingAttachment(input);\n }\n if (await recoverCommittedAttachment(input)) {\n return \"recovered\";\n }\n\n const source = await state.runtime.infra.blobStore.openByteSource(\n pendingAttachment.storageKey,\n );\n if (!source) {\n return dropUnavailablePendingAttachment(input);\n }\n\n const author = resolveDocumentCreateAuthor(state.runtime);\n if (!author) {\n state.runtime.util.log(\n \"Documents: skipped attachment upload because the writer context is unavailable.\",\n );\n return \"retry\";\n }\n\n const prepared = await resolveAttachmentSourceUpload({\n attachmentGeneration: input.attachmentGeneration,\n pendingAttachment,\n source,\n state,\n });\n // Preparation awaited (byte-source open plus resume resolution); a teardown\n // during those awaits must not let the upload itself start — its remote\n // commit could not be recalled once sent.\n if (\n !isDocumentStoreSyncGenerationCurrent(state, input.attachmentGeneration)\n ) {\n return \"retry\";\n }\n if (!prepared) return dropUnavailablePendingAttachment(input);\n const { resume, snapshot } = prepared;\n const uploadLane = createAttachmentUploadLaneReporter({\n blobId: resume.blobId,\n domainScope: state.runtime.state.domainScope,\n pendingAttachment,\n });\n const baseUploadInput = createAttachmentBaseUploadInput({\n author,\n pendingUpload: input,\n resume,\n source,\n sourceSnapshot: snapshot,\n uploadLane,\n });\n return uploadPendingAttachmentBytes({\n baseUploadInput,\n pendingUpload: input,\n uploadLane,\n });\n}\n\nfunction reportAttachmentUploadFailure(input: {\n attachmentGeneration: DocumentStoreSyncGeneration;\n error: unknown;\n pendingAttachment: PendingAttachmentRecord;\n state: DocumentStoreState;\n uploadLane: AttachmentUploadLaneReporter;\n}): void {\n const error = createAttachmentUploadFailedError(\n input.pendingAttachment,\n input.error,\n );\n input.uploadLane.fail(error);\n input.state.runtime.util.log(`Documents: ${error.message}`);\n // Surface the stuck upload in the write queue; a later successful sync pass\n // for the document clears the record. Validated inside the serialized\n // mutation: a teardown (reset/discard) racing this pass just cleared the\n // failure row, and re-recording it would resurrect a phantom queue entry\n // for work that no longer exists.\n void runSerializedSqlMutation(\n input.state.runtime.infra.execSql,\n async (lockedExecSql) => {\n if (\n !isDocumentStoreSyncGenerationCurrent(\n input.state,\n input.attachmentGeneration,\n )\n ) {\n return;\n }\n await recordDocumentSyncFailure(\n lockedExecSql,\n { appKind: DOCUMENTS_APP_KIND, localId: input.state.localId },\n {\n attemptedAt: new Date().toISOString(),\n message: error.message,\n status: null,\n },\n );\n },\n ).catch(() => undefined);\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time source for the reconciler's own retry and continuation timers. Sync
|
|
3
|
+
* lane timing (failure backoff, delayed requests) belongs to the coordinator
|
|
4
|
+
* the host registers the lane on, not to this clock.
|
|
5
|
+
*/
|
|
6
|
+
export interface ReconciliationClock {
|
|
7
|
+
readonly now: () => number;
|
|
8
|
+
/** Run `callback` after `delayMs`; the returned function cancels it. */
|
|
9
|
+
readonly schedule: (callback: () => void, delayMs: number) => () => void;
|
|
10
|
+
}
|
|
11
|
+
export declare const systemReconciliationClock: ReconciliationClock;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clock.js","sourceRoot":"","sources":["clock.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,yBAAyB,GAAwB;IAC5D,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;IACrB,QAAQ,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE;QAC9B,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC5C,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;CACF,CAAC","sourcesContent":["/**\n * Time source for the reconciler's own retry and continuation timers. Sync\n * lane timing (failure backoff, delayed requests) belongs to the coordinator\n * the host registers the lane on, not to this clock.\n */\nexport interface ReconciliationClock {\n readonly now: () => number;\n /** Run `callback` after `delayMs`; the returned function cancels it. */\n readonly schedule: (callback: () => void, delayMs: number) => () => void;\n}\n\nexport const systemReconciliationClock: ReconciliationClock = {\n now: () => Date.now(),\n schedule: (callback, delayMs) => {\n const timer = setTimeout(callback, delayMs);\n return () => clearTimeout(timer);\n },\n};\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { systemReconciliationClock } from "./clock.js";
|
|
1
2
|
export { createReconciliationService } from "./service.js";
|
|
2
3
|
export type { ReconciliationHost, ReconciliationService } from "./serviceTypes.js";
|
|
3
4
|
export { connectReconciliationTriggers, enqueueReconciliationForEvents, } from "./triggers.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,WAAW,CAAC;AAExD,OAAO,EACL,6BAA6B,EAC7B,8BAA8B,GAC/B,MAAM,YAAY,CAAC","sourcesContent":["export { createReconciliationService } from \"./service\";\nexport type { ReconciliationHost, ReconciliationService } from \"./serviceTypes\";\nexport {\n connectReconciliationTriggers,\n enqueueReconciliationForEvents,\n} from \"./triggers\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,2BAA2B,EAAE,MAAM,WAAW,CAAC;AAExD,OAAO,EACL,6BAA6B,EAC7B,8BAA8B,GAC/B,MAAM,YAAY,CAAC","sourcesContent":["export { systemReconciliationClock } from \"./clock\";\nexport { createReconciliationService } from \"./service\";\nexport type { ReconciliationHost, ReconciliationService } from \"./serviceTypes\";\nexport {\n connectReconciliationTriggers,\n enqueueReconciliationForEvents,\n} from \"./triggers\";\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ReconciliationClock } from "./clock.js";
|
|
1
2
|
export interface InitialDocumentProbeBatchInput {
|
|
2
3
|
readonly afterLocalId: string | null;
|
|
3
4
|
readonly listedContainerIds: ReadonlySet<string>;
|
|
@@ -9,6 +10,7 @@ export interface InitialDocumentProbeBatchResult {
|
|
|
9
10
|
readonly requestedCount: number;
|
|
10
11
|
}
|
|
11
12
|
export interface InitialDocumentProbeHost {
|
|
13
|
+
readonly clock: ReconciliationClock;
|
|
12
14
|
readonly listContainerDocumentIds: (containerId: string) => Promise<ReadonlyArray<string> | null>;
|
|
13
15
|
readonly probeUndiscoveredDocumentsBatch: (input: InitialDocumentProbeBatchInput) => Promise<InitialDocumentProbeBatchResult>;
|
|
14
16
|
readonly reportInitialDocumentProbeComplete: (requestedCount: number) => void;
|
|
@@ -40,14 +40,14 @@ function unlistedContainerRetries(state) {
|
|
|
40
40
|
notBeforeMs: state.listingRetryNotBeforeByContainer.get(id) ?? 0,
|
|
41
41
|
}));
|
|
42
42
|
}
|
|
43
|
-
function canRun(state, nowMs
|
|
43
|
+
function canRun(state, nowMs) {
|
|
44
44
|
if (!state.armed || state.running || !hasPendingWork(state)) {
|
|
45
45
|
return false;
|
|
46
46
|
}
|
|
47
47
|
const retries = unlistedContainerRetries(state);
|
|
48
48
|
return (retries.length === 0 || retries.some((retry) => retry.notBeforeMs <= nowMs));
|
|
49
49
|
}
|
|
50
|
-
function continuationDelayMs(state, nowMs
|
|
50
|
+
function continuationDelayMs(state, nowMs) {
|
|
51
51
|
const retries = unlistedContainerRetries(state);
|
|
52
52
|
if (retries.length === 0 ||
|
|
53
53
|
retries.some((retry) => retry.notBeforeMs <= nowMs)) {
|
|
@@ -135,7 +135,7 @@ function readListedDocumentIds(state, eligibleIds) {
|
|
|
135
135
|
}
|
|
136
136
|
async function listNextEligibleContainer(input) {
|
|
137
137
|
const { eligibleIds, host, runGeneration, state } = input;
|
|
138
|
-
const nowMs =
|
|
138
|
+
const nowMs = host.clock.now();
|
|
139
139
|
const containerId = eligibleIds.find((id) => !state.listedDocumentIdsByContainer.has(id) &&
|
|
140
140
|
(state.listingRetryNotBeforeByContainer.get(id) ?? 0) <= nowMs);
|
|
141
141
|
if (!containerId) {
|
|
@@ -165,7 +165,7 @@ async function listNextEligibleContainer(input) {
|
|
|
165
165
|
}
|
|
166
166
|
else {
|
|
167
167
|
const retryDelayMs = INITIAL_DOCUMENT_LISTING_RETRY_BASE_DELAY_MS * 2 ** (attempts - 1);
|
|
168
|
-
state.listingRetryNotBeforeByContainer.set(containerId,
|
|
168
|
+
state.listingRetryNotBeforeByContainer.set(containerId, host.clock.now() + retryDelayMs);
|
|
169
169
|
}
|
|
170
170
|
return true;
|
|
171
171
|
}
|
|
@@ -175,7 +175,7 @@ async function listNextEligibleContainer(input) {
|
|
|
175
175
|
return true;
|
|
176
176
|
}
|
|
177
177
|
async function runProbe(host, state) {
|
|
178
|
-
if (!canRun(state)) {
|
|
178
|
+
if (!canRun(state, host.clock.now())) {
|
|
179
179
|
return;
|
|
180
180
|
}
|
|
181
181
|
state.running = true;
|
|
@@ -258,8 +258,8 @@ export function createInitialDocumentProbe(host) {
|
|
|
258
258
|
state.listingRetryNotBeforeByContainer.delete(containerId);
|
|
259
259
|
};
|
|
260
260
|
},
|
|
261
|
-
canRun: () => canRun(state),
|
|
262
|
-
continuationDelayMs: () => continuationDelayMs(state),
|
|
261
|
+
canRun: () => canRun(state, host.clock.now()),
|
|
262
|
+
continuationDelayMs: () => continuationDelayMs(state, host.clock.now()),
|
|
263
263
|
hasPendingWork: () => state.armed && hasPendingWork(state),
|
|
264
264
|
resetPending: () => resetPending(state),
|
|
265
265
|
resetSkippedListings: () => resetSkippedListings(state),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initialDocumentProbe.js","sourceRoot":"","sources":["initialDocumentProbe.ts"],"names":[],"mappings":"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","sourcesContent":["export interface InitialDocumentProbeBatchInput {\n readonly afterLocalId: string | null;\n readonly listedContainerIds: ReadonlySet<string>;\n readonly listedDocumentIds: ReadonlySet<string>;\n}\n\nexport interface InitialDocumentProbeBatchResult {\n readonly done: boolean;\n readonly nextCursor: string | null;\n readonly requestedCount: number;\n}\n\nexport interface InitialDocumentProbeHost {\n readonly listContainerDocumentIds: (\n containerId: string,\n ) => Promise<ReadonlyArray<string> | null>;\n readonly probeUndiscoveredDocumentsBatch: (\n input: InitialDocumentProbeBatchInput,\n ) => Promise<InitialDocumentProbeBatchResult>;\n readonly reportInitialDocumentProbeComplete: (requestedCount: number) => void;\n}\n\nexport interface InitialDocumentProbe {\n readonly arm: (eligibleContainerIds: ReadonlyArray<string>) => void;\n /** Accept a completed full discovery listing only in the current probe session. */\n readonly captureListing: (\n containerId: string,\n ) => (documentIds: ReadonlyArray<string>) => void;\n readonly canRun: () => boolean;\n readonly continuationDelayMs: () => number;\n readonly hasPendingWork: () => boolean;\n readonly resetPending: () => void;\n readonly resetSkippedListings: () => void;\n readonly run: () => Promise<void>;\n}\n\nconst MAX_INITIAL_DOCUMENT_LISTING_ATTEMPTS = 3;\nconst INITIAL_DOCUMENT_LISTING_RETRY_BASE_DELAY_MS = 1_000;\n\ninterface InitialDocumentProbeState {\n armed: boolean;\n cursor: string | null;\n eligibleContainerIds: Set<string>;\n generation: number;\n listedDocumentIds: ReadonlySet<string> | null;\n listedDocumentIdsByContainer: Map<string, ReadonlySet<string>>;\n listingAttemptsByContainer: Map<string, number>;\n listingRetryNotBeforeByContainer: Map<string, number>;\n probedContainerIds: Set<string>;\n reported: boolean;\n requestedCount: number;\n running: boolean;\n skippedContainerIds: Set<string>;\n}\n\nfunction createInitialDocumentProbeState(): InitialDocumentProbeState {\n return {\n armed: false,\n cursor: null,\n eligibleContainerIds: new Set(),\n generation: 0,\n listedDocumentIds: null,\n listedDocumentIdsByContainer: new Map(),\n listingAttemptsByContainer: new Map(),\n listingRetryNotBeforeByContainer: new Map(),\n probedContainerIds: new Set(),\n reported: false,\n requestedCount: 0,\n running: false,\n skippedContainerIds: new Set(),\n };\n}\n\nfunction sameIds(\n left: ReadonlySet<string>,\n right: ReadonlySet<string>,\n): boolean {\n return left.size === right.size && [...left].every((id) => right.has(id));\n}\n\nfunction pendingProbeContainerIds(\n state: InitialDocumentProbeState,\n): ReadonlyArray<string> {\n return [...state.eligibleContainerIds].filter(\n (id) => !state.probedContainerIds.has(id),\n );\n}\n\nfunction hasPendingWork(state: InitialDocumentProbeState): boolean {\n return (\n state.eligibleContainerIds.size > 0 &&\n ([...state.eligibleContainerIds].some(\n (id) => !state.listedDocumentIdsByContainer.has(id),\n ) ||\n pendingProbeContainerIds(state).length > 0)\n );\n}\n\n/**\n * Eligible containers still awaiting an authoritative listing, each with the\n * earliest time its bounded retry backoff allows another attempt (0 = now).\n */\nfunction unlistedContainerRetries(\n state: InitialDocumentProbeState,\n): Array<{ containerId: string; notBeforeMs: number }> {\n return [...state.eligibleContainerIds]\n .filter((id) => !state.listedDocumentIdsByContainer.has(id))\n .map((id) => ({\n containerId: id,\n notBeforeMs: state.listingRetryNotBeforeByContainer.get(id) ?? 0,\n }));\n}\n\nfunction canRun(state: InitialDocumentProbeState, nowMs = Date.now()): boolean {\n if (!state.armed || state.running || !hasPendingWork(state)) {\n return false;\n }\n const retries = unlistedContainerRetries(state);\n return (\n retries.length === 0 || retries.some((retry) => retry.notBeforeMs <= nowMs)\n );\n}\n\nfunction continuationDelayMs(\n state: InitialDocumentProbeState,\n nowMs = Date.now(),\n): number {\n const retries = unlistedContainerRetries(state);\n if (\n retries.length === 0 ||\n retries.some((retry) => retry.notBeforeMs <= nowMs)\n ) {\n return 0;\n }\n return Math.max(\n 0,\n Math.min(...retries.map((retry) => retry.notBeforeMs)) - nowMs,\n );\n}\n\n/**\n * Invalidate the listing session: bump the generation, drop the candidate\n * cursor and merged listing set, and clear listing retry bookkeeping.\n * Per-container listing/probe results survive unless the caller clears them.\n */\nfunction resetListingSession(state: InitialDocumentProbeState): void {\n state.generation += 1;\n state.armed = false;\n state.cursor = null;\n state.listedDocumentIds = null;\n state.listingAttemptsByContainer.clear();\n state.listingRetryNotBeforeByContainer.clear();\n state.reported = false;\n state.requestedCount = 0;\n state.skippedContainerIds.clear();\n}\n\nfunction resetPending(state: InitialDocumentProbeState): void {\n resetListingSession(state);\n state.eligibleContainerIds.clear();\n state.listedDocumentIdsByContainer.clear();\n state.probedContainerIds.clear();\n}\n\nfunction resetSkippedListings(state: InitialDocumentProbeState): void {\n if (state.skippedContainerIds.size === 0) {\n return;\n }\n // A reconnect is a new opportunity for lanes that exhausted their bounded\n // listing retries. Preserve completed healthy lanes, but invalidate any\n // candidate cursor because the authoritative listing set can now grow.\n resetListingSession(state);\n}\n\nfunction retainEligibleProbeState(\n state: InitialDocumentProbeState,\n nextIds: ReadonlySet<string>,\n): void {\n for (const containerId of state.listedDocumentIdsByContainer.keys()) {\n if (!nextIds.has(containerId)) {\n state.listedDocumentIdsByContainer.delete(containerId);\n }\n }\n for (const containerId of state.probedContainerIds) {\n if (!nextIds.has(containerId)) {\n state.probedContainerIds.delete(containerId);\n }\n }\n for (const containerId of state.listingAttemptsByContainer.keys()) {\n if (!nextIds.has(containerId)) {\n state.listingAttemptsByContainer.delete(containerId);\n state.listingRetryNotBeforeByContainer.delete(containerId);\n }\n }\n}\n\nfunction replaceEligibleContainerIds(\n state: InitialDocumentProbeState,\n nextIds: Set<string>,\n): void {\n state.generation += 1;\n state.cursor = null;\n state.eligibleContainerIds = nextIds;\n state.listedDocumentIds = null;\n retainEligibleProbeState(state, nextIds);\n}\n\nfunction armProbe(\n state: InitialDocumentProbeState,\n nextEligibleContainerIds: ReadonlyArray<string>,\n): void {\n if (state.reported) {\n return;\n }\n const nextIds = new Set(\n nextEligibleContainerIds.filter(\n (id) => Boolean(id) && !state.skippedContainerIds.has(id),\n ),\n );\n if (!sameIds(state.eligibleContainerIds, nextIds)) {\n replaceEligibleContainerIds(state, nextIds);\n }\n if (!hasPendingWork(state)) {\n return;\n }\n state.armed = true;\n}\n\nfunction readListedDocumentIds(\n state: InitialDocumentProbeState,\n eligibleIds: ReadonlyArray<string>,\n): ReadonlySet<string> {\n if (state.listedDocumentIds !== null) {\n return state.listedDocumentIds;\n }\n state.listedDocumentIds = new Set(\n eligibleIds.flatMap((containerId) => [\n ...(state.listedDocumentIdsByContainer.get(containerId) ?? []),\n ]),\n );\n return state.listedDocumentIds;\n}\n\nasync function listNextEligibleContainer(input: {\n readonly eligibleIds: ReadonlyArray<string>;\n readonly host: InitialDocumentProbeHost;\n readonly runGeneration: number;\n readonly state: InitialDocumentProbeState;\n}): Promise<boolean> {\n const { eligibleIds, host, runGeneration, state } = input;\n const nowMs = Date.now();\n const containerId = eligibleIds.find(\n (id) =>\n !state.listedDocumentIdsByContainer.has(id) &&\n (state.listingRetryNotBeforeByContainer.get(id) ?? 0) <= nowMs,\n );\n if (!containerId) {\n return false;\n }\n\n let documentIds: ReadonlyArray<string> | null;\n try {\n documentIds = await host.listContainerDocumentIds(containerId);\n } catch {\n documentIds = null;\n }\n if (state.generation !== runGeneration) {\n return true;\n }\n if (documentIds === null) {\n const attempts =\n (state.listingAttemptsByContainer.get(containerId) ?? 0) + 1;\n state.listingAttemptsByContainer.set(containerId, attempts);\n // Retry transient listing failures with bounded, seconds-scale backoff.\n // Exhausted lanes are skipped until prerequisites are regained so healthy\n // lanes can converge without suppressing the failed lane forever.\n if (attempts >= MAX_INITIAL_DOCUMENT_LISTING_ATTEMPTS) {\n state.skippedContainerIds.add(containerId);\n state.eligibleContainerIds.delete(containerId);\n state.listingAttemptsByContainer.delete(containerId);\n state.listingRetryNotBeforeByContainer.delete(containerId);\n } else {\n const retryDelayMs =\n INITIAL_DOCUMENT_LISTING_RETRY_BASE_DELAY_MS * 2 ** (attempts - 1);\n state.listingRetryNotBeforeByContainer.set(\n containerId,\n Date.now() + retryDelayMs,\n );\n }\n return true;\n }\n state.listingAttemptsByContainer.delete(containerId);\n state.listingRetryNotBeforeByContainer.delete(containerId);\n state.listedDocumentIdsByContainer.set(containerId, new Set(documentIds));\n return true;\n}\n\nasync function runProbe(\n host: InitialDocumentProbeHost,\n state: InitialDocumentProbeState,\n): Promise<void> {\n if (!canRun(state)) {\n return;\n }\n\n state.running = true;\n const runGeneration = state.generation;\n try {\n const eligibleIds = [...state.eligibleContainerIds];\n if (\n await listNextEligibleContainer({\n eligibleIds,\n host,\n runGeneration,\n state,\n })\n ) {\n return;\n }\n if (eligibleIds.some((id) => !state.listedDocumentIdsByContainer.has(id))) {\n return;\n }\n\n const candidateContainerIds = pendingProbeContainerIds(state);\n if (candidateContainerIds.length === 0) {\n state.armed = false;\n return;\n }\n const batch = await host.probeUndiscoveredDocumentsBatch({\n afterLocalId: state.cursor,\n listedContainerIds: new Set(candidateContainerIds),\n listedDocumentIds: readListedDocumentIds(state, eligibleIds),\n });\n if (state.generation !== runGeneration) {\n return;\n }\n\n state.requestedCount += batch.requestedCount;\n if (!batch.done) {\n if (batch.nextCursor !== null) {\n state.cursor = batch.nextCursor;\n return;\n }\n }\n for (const containerId of candidateContainerIds) {\n state.probedContainerIds.add(containerId);\n }\n state.cursor = null;\n state.armed = hasPendingWork(state);\n if (!state.reported) {\n state.reported = true;\n host.reportInitialDocumentProbeComplete(state.requestedCount);\n }\n } finally {\n state.running = false;\n }\n}\n\n/**\n * Runs one bounded unit per call: first one authoritative container listing,\n * then one local candidate batch. Incremental discovery watermarks are never\n * used as evidence that a document is absent from the server.\n *\n * The eligible set is replaceable because local hydration can become ready\n * before the remote tree crawl has surfaced every container. Growth before\n * completion restarts the candidate cursor; an empty early set never completes\n * the probe or prevents a later remote-containers-added signal from arming it.\n */\nexport function createInitialDocumentProbe(\n host: InitialDocumentProbeHost,\n): InitialDocumentProbe {\n const state = createInitialDocumentProbeState();\n return {\n arm: (eligibleContainerIds) => armProbe(state, eligibleContainerIds),\n captureListing: (containerId) => {\n // Early active-container discovery may precede arming; a later probe\n // deliberately re-reads when it cannot reuse this session's listing.\n const generation = state.generation;\n return (documentIds) => {\n if (\n state.generation !== generation ||\n !state.armed ||\n !state.eligibleContainerIds.has(containerId) ||\n state.listedDocumentIdsByContainer.has(containerId)\n ) {\n return;\n }\n state.listedDocumentIdsByContainer.set(\n containerId,\n new Set(documentIds),\n );\n state.listedDocumentIds = null;\n state.listingAttemptsByContainer.delete(containerId);\n state.listingRetryNotBeforeByContainer.delete(containerId);\n };\n },\n canRun: () => canRun(state),\n continuationDelayMs: () => continuationDelayMs(state),\n hasPendingWork: () => state.armed && hasPendingWork(state),\n resetPending: () => resetPending(state),\n resetSkippedListings: () => resetSkippedListings(state),\n run: () => runProbe(host, state),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"initialDocumentProbe.js","sourceRoot":"","sources":["initialDocumentProbe.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReconciliationClock } from \"./clock\";\n\nexport interface InitialDocumentProbeBatchInput {\n readonly afterLocalId: string | null;\n readonly listedContainerIds: ReadonlySet<string>;\n readonly listedDocumentIds: ReadonlySet<string>;\n}\n\nexport interface InitialDocumentProbeBatchResult {\n readonly done: boolean;\n readonly nextCursor: string | null;\n readonly requestedCount: number;\n}\n\nexport interface InitialDocumentProbeHost {\n readonly clock: ReconciliationClock;\n readonly listContainerDocumentIds: (\n containerId: string,\n ) => Promise<ReadonlyArray<string> | null>;\n readonly probeUndiscoveredDocumentsBatch: (\n input: InitialDocumentProbeBatchInput,\n ) => Promise<InitialDocumentProbeBatchResult>;\n readonly reportInitialDocumentProbeComplete: (requestedCount: number) => void;\n}\n\nexport interface InitialDocumentProbe {\n readonly arm: (eligibleContainerIds: ReadonlyArray<string>) => void;\n /** Accept a completed full discovery listing only in the current probe session. */\n readonly captureListing: (\n containerId: string,\n ) => (documentIds: ReadonlyArray<string>) => void;\n readonly canRun: () => boolean;\n readonly continuationDelayMs: () => number;\n readonly hasPendingWork: () => boolean;\n readonly resetPending: () => void;\n readonly resetSkippedListings: () => void;\n readonly run: () => Promise<void>;\n}\n\nconst MAX_INITIAL_DOCUMENT_LISTING_ATTEMPTS = 3;\nconst INITIAL_DOCUMENT_LISTING_RETRY_BASE_DELAY_MS = 1_000;\n\ninterface InitialDocumentProbeState {\n armed: boolean;\n cursor: string | null;\n eligibleContainerIds: Set<string>;\n generation: number;\n listedDocumentIds: ReadonlySet<string> | null;\n listedDocumentIdsByContainer: Map<string, ReadonlySet<string>>;\n listingAttemptsByContainer: Map<string, number>;\n listingRetryNotBeforeByContainer: Map<string, number>;\n probedContainerIds: Set<string>;\n reported: boolean;\n requestedCount: number;\n running: boolean;\n skippedContainerIds: Set<string>;\n}\n\nfunction createInitialDocumentProbeState(): InitialDocumentProbeState {\n return {\n armed: false,\n cursor: null,\n eligibleContainerIds: new Set(),\n generation: 0,\n listedDocumentIds: null,\n listedDocumentIdsByContainer: new Map(),\n listingAttemptsByContainer: new Map(),\n listingRetryNotBeforeByContainer: new Map(),\n probedContainerIds: new Set(),\n reported: false,\n requestedCount: 0,\n running: false,\n skippedContainerIds: new Set(),\n };\n}\n\nfunction sameIds(\n left: ReadonlySet<string>,\n right: ReadonlySet<string>,\n): boolean {\n return left.size === right.size && [...left].every((id) => right.has(id));\n}\n\nfunction pendingProbeContainerIds(\n state: InitialDocumentProbeState,\n): ReadonlyArray<string> {\n return [...state.eligibleContainerIds].filter(\n (id) => !state.probedContainerIds.has(id),\n );\n}\n\nfunction hasPendingWork(state: InitialDocumentProbeState): boolean {\n return (\n state.eligibleContainerIds.size > 0 &&\n ([...state.eligibleContainerIds].some(\n (id) => !state.listedDocumentIdsByContainer.has(id),\n ) ||\n pendingProbeContainerIds(state).length > 0)\n );\n}\n\n/**\n * Eligible containers still awaiting an authoritative listing, each with the\n * earliest time its bounded retry backoff allows another attempt (0 = now).\n */\nfunction unlistedContainerRetries(\n state: InitialDocumentProbeState,\n): Array<{ containerId: string; notBeforeMs: number }> {\n return [...state.eligibleContainerIds]\n .filter((id) => !state.listedDocumentIdsByContainer.has(id))\n .map((id) => ({\n containerId: id,\n notBeforeMs: state.listingRetryNotBeforeByContainer.get(id) ?? 0,\n }));\n}\n\nfunction canRun(state: InitialDocumentProbeState, nowMs: number): boolean {\n if (!state.armed || state.running || !hasPendingWork(state)) {\n return false;\n }\n const retries = unlistedContainerRetries(state);\n return (\n retries.length === 0 || retries.some((retry) => retry.notBeforeMs <= nowMs)\n );\n}\n\nfunction continuationDelayMs(\n state: InitialDocumentProbeState,\n nowMs: number,\n): number {\n const retries = unlistedContainerRetries(state);\n if (\n retries.length === 0 ||\n retries.some((retry) => retry.notBeforeMs <= nowMs)\n ) {\n return 0;\n }\n return Math.max(\n 0,\n Math.min(...retries.map((retry) => retry.notBeforeMs)) - nowMs,\n );\n}\n\n/**\n * Invalidate the listing session: bump the generation, drop the candidate\n * cursor and merged listing set, and clear listing retry bookkeeping.\n * Per-container listing/probe results survive unless the caller clears them.\n */\nfunction resetListingSession(state: InitialDocumentProbeState): void {\n state.generation += 1;\n state.armed = false;\n state.cursor = null;\n state.listedDocumentIds = null;\n state.listingAttemptsByContainer.clear();\n state.listingRetryNotBeforeByContainer.clear();\n state.reported = false;\n state.requestedCount = 0;\n state.skippedContainerIds.clear();\n}\n\nfunction resetPending(state: InitialDocumentProbeState): void {\n resetListingSession(state);\n state.eligibleContainerIds.clear();\n state.listedDocumentIdsByContainer.clear();\n state.probedContainerIds.clear();\n}\n\nfunction resetSkippedListings(state: InitialDocumentProbeState): void {\n if (state.skippedContainerIds.size === 0) {\n return;\n }\n // A reconnect is a new opportunity for lanes that exhausted their bounded\n // listing retries. Preserve completed healthy lanes, but invalidate any\n // candidate cursor because the authoritative listing set can now grow.\n resetListingSession(state);\n}\n\nfunction retainEligibleProbeState(\n state: InitialDocumentProbeState,\n nextIds: ReadonlySet<string>,\n): void {\n for (const containerId of state.listedDocumentIdsByContainer.keys()) {\n if (!nextIds.has(containerId)) {\n state.listedDocumentIdsByContainer.delete(containerId);\n }\n }\n for (const containerId of state.probedContainerIds) {\n if (!nextIds.has(containerId)) {\n state.probedContainerIds.delete(containerId);\n }\n }\n for (const containerId of state.listingAttemptsByContainer.keys()) {\n if (!nextIds.has(containerId)) {\n state.listingAttemptsByContainer.delete(containerId);\n state.listingRetryNotBeforeByContainer.delete(containerId);\n }\n }\n}\n\nfunction replaceEligibleContainerIds(\n state: InitialDocumentProbeState,\n nextIds: Set<string>,\n): void {\n state.generation += 1;\n state.cursor = null;\n state.eligibleContainerIds = nextIds;\n state.listedDocumentIds = null;\n retainEligibleProbeState(state, nextIds);\n}\n\nfunction armProbe(\n state: InitialDocumentProbeState,\n nextEligibleContainerIds: ReadonlyArray<string>,\n): void {\n if (state.reported) {\n return;\n }\n const nextIds = new Set(\n nextEligibleContainerIds.filter(\n (id) => Boolean(id) && !state.skippedContainerIds.has(id),\n ),\n );\n if (!sameIds(state.eligibleContainerIds, nextIds)) {\n replaceEligibleContainerIds(state, nextIds);\n }\n if (!hasPendingWork(state)) {\n return;\n }\n state.armed = true;\n}\n\nfunction readListedDocumentIds(\n state: InitialDocumentProbeState,\n eligibleIds: ReadonlyArray<string>,\n): ReadonlySet<string> {\n if (state.listedDocumentIds !== null) {\n return state.listedDocumentIds;\n }\n state.listedDocumentIds = new Set(\n eligibleIds.flatMap((containerId) => [\n ...(state.listedDocumentIdsByContainer.get(containerId) ?? []),\n ]),\n );\n return state.listedDocumentIds;\n}\n\nasync function listNextEligibleContainer(input: {\n readonly eligibleIds: ReadonlyArray<string>;\n readonly host: InitialDocumentProbeHost;\n readonly runGeneration: number;\n readonly state: InitialDocumentProbeState;\n}): Promise<boolean> {\n const { eligibleIds, host, runGeneration, state } = input;\n const nowMs = host.clock.now();\n const containerId = eligibleIds.find(\n (id) =>\n !state.listedDocumentIdsByContainer.has(id) &&\n (state.listingRetryNotBeforeByContainer.get(id) ?? 0) <= nowMs,\n );\n if (!containerId) {\n return false;\n }\n\n let documentIds: ReadonlyArray<string> | null;\n try {\n documentIds = await host.listContainerDocumentIds(containerId);\n } catch {\n documentIds = null;\n }\n if (state.generation !== runGeneration) {\n return true;\n }\n if (documentIds === null) {\n const attempts =\n (state.listingAttemptsByContainer.get(containerId) ?? 0) + 1;\n state.listingAttemptsByContainer.set(containerId, attempts);\n // Retry transient listing failures with bounded, seconds-scale backoff.\n // Exhausted lanes are skipped until prerequisites are regained so healthy\n // lanes can converge without suppressing the failed lane forever.\n if (attempts >= MAX_INITIAL_DOCUMENT_LISTING_ATTEMPTS) {\n state.skippedContainerIds.add(containerId);\n state.eligibleContainerIds.delete(containerId);\n state.listingAttemptsByContainer.delete(containerId);\n state.listingRetryNotBeforeByContainer.delete(containerId);\n } else {\n const retryDelayMs =\n INITIAL_DOCUMENT_LISTING_RETRY_BASE_DELAY_MS * 2 ** (attempts - 1);\n state.listingRetryNotBeforeByContainer.set(\n containerId,\n host.clock.now() + retryDelayMs,\n );\n }\n return true;\n }\n state.listingAttemptsByContainer.delete(containerId);\n state.listingRetryNotBeforeByContainer.delete(containerId);\n state.listedDocumentIdsByContainer.set(containerId, new Set(documentIds));\n return true;\n}\n\nasync function runProbe(\n host: InitialDocumentProbeHost,\n state: InitialDocumentProbeState,\n): Promise<void> {\n if (!canRun(state, host.clock.now())) {\n return;\n }\n\n state.running = true;\n const runGeneration = state.generation;\n try {\n const eligibleIds = [...state.eligibleContainerIds];\n if (\n await listNextEligibleContainer({\n eligibleIds,\n host,\n runGeneration,\n state,\n })\n ) {\n return;\n }\n if (eligibleIds.some((id) => !state.listedDocumentIdsByContainer.has(id))) {\n return;\n }\n\n const candidateContainerIds = pendingProbeContainerIds(state);\n if (candidateContainerIds.length === 0) {\n state.armed = false;\n return;\n }\n const batch = await host.probeUndiscoveredDocumentsBatch({\n afterLocalId: state.cursor,\n listedContainerIds: new Set(candidateContainerIds),\n listedDocumentIds: readListedDocumentIds(state, eligibleIds),\n });\n if (state.generation !== runGeneration) {\n return;\n }\n\n state.requestedCount += batch.requestedCount;\n if (!batch.done) {\n if (batch.nextCursor !== null) {\n state.cursor = batch.nextCursor;\n return;\n }\n }\n for (const containerId of candidateContainerIds) {\n state.probedContainerIds.add(containerId);\n }\n state.cursor = null;\n state.armed = hasPendingWork(state);\n if (!state.reported) {\n state.reported = true;\n host.reportInitialDocumentProbeComplete(state.requestedCount);\n }\n } finally {\n state.running = false;\n }\n}\n\n/**\n * Runs one bounded unit per call: first one authoritative container listing,\n * then one local candidate batch. Incremental discovery watermarks are never\n * used as evidence that a document is absent from the server.\n *\n * The eligible set is replaceable because local hydration can become ready\n * before the remote tree crawl has surfaced every container. Growth before\n * completion restarts the candidate cursor; an empty early set never completes\n * the probe or prevents a later remote-containers-added signal from arming it.\n */\nexport function createInitialDocumentProbe(\n host: InitialDocumentProbeHost,\n): InitialDocumentProbe {\n const state = createInitialDocumentProbeState();\n return {\n arm: (eligibleContainerIds) => armProbe(state, eligibleContainerIds),\n captureListing: (containerId) => {\n // Early active-container discovery may precede arming; a later probe\n // deliberately re-reads when it cannot reuse this session's listing.\n const generation = state.generation;\n return (documentIds) => {\n if (\n state.generation !== generation ||\n !state.armed ||\n !state.eligibleContainerIds.has(containerId) ||\n state.listedDocumentIdsByContainer.has(containerId)\n ) {\n return;\n }\n state.listedDocumentIdsByContainer.set(\n containerId,\n new Set(documentIds),\n );\n state.listedDocumentIds = null;\n state.listingAttemptsByContainer.delete(containerId);\n state.listingRetryNotBeforeByContainer.delete(containerId);\n };\n },\n canRun: () => canRun(state, host.clock.now()),\n continuationDelayMs: () => continuationDelayMs(state, host.clock.now()),\n hasPendingWork: () => state.armed && hasPendingWork(state),\n resetPending: () => resetPending(state),\n resetSkippedListings: () => resetSkippedListings(state),\n run: () => runProbe(host, state),\n };\n}\n"]}
|
|
@@ -4,23 +4,22 @@ import { rearmFailedSweepContainers, } from "./laneFailure.js";
|
|
|
4
4
|
import { reconcileContainers, } from "./reconcileContainer.js";
|
|
5
5
|
const pendingRetries = new WeakMap();
|
|
6
6
|
export function clearPendingDiscoveryRetries(state) {
|
|
7
|
-
for (const {
|
|
8
|
-
|
|
7
|
+
for (const { cancel } of pendingRetries.get(state)?.values() ?? [])
|
|
8
|
+
cancel();
|
|
9
9
|
pendingRetries.delete(state);
|
|
10
10
|
}
|
|
11
|
-
function schedulePendingDiscovery(state, containerId, delayMs) {
|
|
11
|
+
function schedulePendingDiscovery(clock, state, containerId, delayMs) {
|
|
12
12
|
if (!state.active)
|
|
13
13
|
return;
|
|
14
14
|
const retries = pendingRetries.get(state) ?? new Map();
|
|
15
15
|
pendingRetries.set(state, retries);
|
|
16
|
-
const due =
|
|
16
|
+
const due = clock.now() + delayMs;
|
|
17
17
|
const existing = retries.get(containerId);
|
|
18
18
|
if (existing && existing.due <= due)
|
|
19
19
|
return;
|
|
20
|
-
|
|
21
|
-
clearTimeout(existing.timer);
|
|
20
|
+
existing?.cancel();
|
|
22
21
|
const generation = state.lifecycleGeneration;
|
|
23
|
-
const
|
|
22
|
+
const cancel = clock.schedule(() => {
|
|
24
23
|
retries.delete(containerId);
|
|
25
24
|
if (!state.active || state.lifecycleGeneration !== generation)
|
|
26
25
|
return;
|
|
@@ -28,7 +27,7 @@ function schedulePendingDiscovery(state, containerId, delayMs) {
|
|
|
28
27
|
state.queue.enqueue(containerId, state.activeContainerId === containerId ? "active" : "idle");
|
|
29
28
|
state.lane?.requestSync();
|
|
30
29
|
}, Math.max(1, delayMs));
|
|
31
|
-
retries.set(containerId, {
|
|
30
|
+
retries.set(containerId, { cancel, due });
|
|
32
31
|
}
|
|
33
32
|
export function isCurrentReconciliationLifecycle(state, generation) {
|
|
34
33
|
return (state.lifecycleGeneration === generation &&
|
|
@@ -45,7 +44,7 @@ export async function reconcileMarkedContainers(host, state, containers, fullRev
|
|
|
45
44
|
: state.forcedDocumentIds.get(containerId),
|
|
46
45
|
forceDocumentContentPull,
|
|
47
46
|
onFullListing: state.initialDocumentProbe.captureListing(containerId),
|
|
48
|
-
onPendingDiscovery: (delayMs) => schedulePendingDiscovery(state, containerId, delayMs),
|
|
47
|
+
onPendingDiscovery: (delayMs) => schedulePendingDiscovery(host.clock, state, containerId, delayMs),
|
|
49
48
|
})));
|
|
50
49
|
for (const result of results) {
|
|
51
50
|
if (result.status !== "reconciled") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"knownContainerSweep.js","sourceRoot":"","sources":["knownContainerSweep.ts"],"names":[],"mappings":"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","sourcesContent":["import { MAX_CONTAINER_DOCUMENT_LANES } from \"@tearleads/validators/request\";\nimport {\n acknowledgeContainerForce,\n type IdleBackfillState,\n} from \"./idleBackfill\";\nimport {\n type FailedForcedContainer,\n rearmFailedSweepContainers,\n} from \"./laneFailure\";\nimport {\n type ContainerReconcileResult,\n reconcileContainers,\n} from \"./reconcileContainer\";\nimport type { ReconciliationHost } from \"./serviceTypes\";\n\ninterface KnownContainerSweepState extends IdleBackfillState {\n active: boolean;\n lane: {\n requestSync: () => void;\n requestSyncAfter: (delayMs: number) => void;\n } | null;\n lifecycleGeneration: number;\n}\n\nconst pendingRetries = new WeakMap<\n KnownContainerSweepState,\n Map<string, { due: number; timer: ReturnType<typeof setTimeout> }>\n>();\n\nexport function clearPendingDiscoveryRetries(\n state: KnownContainerSweepState,\n): void {\n for (const { timer } of pendingRetries.get(state)?.values() ?? [])\n clearTimeout(timer);\n pendingRetries.delete(state);\n}\n\nfunction schedulePendingDiscovery(\n state: KnownContainerSweepState,\n containerId: string,\n delayMs: number,\n): void {\n if (!state.active) return;\n const retries = pendingRetries.get(state) ?? new Map();\n pendingRetries.set(state, retries);\n const due = Date.now() + delayMs;\n const existing = retries.get(containerId);\n if (existing && existing.due <= due) return;\n if (existing) clearTimeout(existing.timer);\n const generation = state.lifecycleGeneration;\n const timer = setTimeout(\n () => {\n retries.delete(containerId);\n if (!state.active || state.lifecycleGeneration !== generation) return;\n state.discoveredContainerIds.delete(containerId);\n state.queue.enqueue(\n containerId,\n state.activeContainerId === containerId ? \"active\" : \"idle\",\n );\n state.lane?.requestSync();\n },\n Math.max(1, delayMs),\n );\n retries.set(containerId, { due, timer });\n}\n\nexport function isCurrentReconciliationLifecycle(\n state: KnownContainerSweepState,\n generation: number,\n): boolean {\n return (\n state.lifecycleGeneration === generation &&\n (state.active || generation === 0)\n );\n}\n\n// Reconcile containers whose discovered marks are already set, rolling a mark\n// back when its container is skipped or fails so a transient error (or a\n// container that later becomes eligible) can be retried.\nexport async function reconcileMarkedContainers(\n host: ReconciliationHost,\n state: KnownContainerSweepState,\n containers: ReadonlyArray<{\n readonly containerId: string;\n readonly forceDocumentContentPull: boolean;\n }>,\n fullRevalidation = false,\n): Promise<ReadonlyArray<ContainerReconcileResult>> {\n const results = await reconcileContainers(\n host,\n containers.map(({ containerId, forceDocumentContentPull }) => ({\n containerId,\n documentIds: fullRevalidation\n ? undefined\n : state.forcedDocumentIds.get(containerId),\n forceDocumentContentPull,\n onFullListing: state.initialDocumentProbe.captureListing(containerId),\n onPendingDiscovery: (delayMs: number) =>\n schedulePendingDiscovery(state, containerId, delayMs),\n })),\n );\n for (const result of results) {\n if (result.status !== \"reconciled\") {\n state.discoveredContainerIds.delete(result.containerId);\n }\n }\n return results;\n}\n\n/**\n * Reconciles one batch of a sweep. Force generations are read before the\n * batched listing starts, so a hint arriving while the batch is in flight\n * keeps its newer generation and runs again.\n */\nasync function sweepContainerBatch(input: {\n containerIds: ReadonlyArray<string>;\n forceAllDocumentContentPulls: boolean;\n host: ReconciliationHost;\n lifecycleGeneration: number;\n state: KnownContainerSweepState;\n}): Promise<{\n readonly failedForces: ReadonlyArray<FailedForcedContainer>;\n readonly firstError: unknown;\n} | null> {\n const { containerIds, forceAllDocumentContentPulls, host, state } = input;\n const forceGenerations = new Map(\n containerIds.map((containerId) => [\n containerId,\n state.forcedContainerGenerations.get(containerId),\n ]),\n );\n const results = await reconcileMarkedContainers(\n host,\n state,\n containerIds.map((containerId) => ({\n containerId,\n forceDocumentContentPull:\n forceAllDocumentContentPulls ||\n forceGenerations.get(containerId) !== undefined,\n })),\n forceAllDocumentContentPulls,\n );\n if (!isCurrentReconciliationLifecycle(state, input.lifecycleGeneration)) {\n return null;\n }\n const failedForces: FailedForcedContainer[] = [];\n let firstError: unknown;\n for (const result of results) {\n const forceGeneration = forceGenerations.get(result.containerId);\n if (result.status === \"reconciled\") {\n acknowledgeContainerForce(state, result.containerId, forceGeneration);\n } else if (result.status === \"failed\") {\n if (forceGeneration !== undefined) {\n failedForces.push({ containerId: result.containerId, forceGeneration });\n }\n if (!host.isIgnorableError(result.error) && firstError === undefined) {\n firstError = result.error;\n }\n }\n }\n return { failedForces, firstError };\n}\n\nexport async function sweepKnownContainers(input: {\n forceAllDocumentContentPulls: boolean;\n host: ReconciliationHost;\n knownIds: ReadonlyArray<string>;\n lifecycleGeneration: number;\n state: KnownContainerSweepState;\n}): Promise<void> {\n const {\n forceAllDocumentContentPulls,\n host,\n knownIds,\n lifecycleGeneration,\n state,\n } = input;\n if (!isCurrentReconciliationLifecycle(state, lifecycleGeneration)) {\n return;\n }\n const containerIds = forceAllDocumentContentPulls\n ? knownIds\n : knownIds.filter(\n (containerId) =>\n state.forcedContainerGenerations.has(containerId) ||\n !state.discoveredContainerIds.has(containerId),\n );\n // Mark only the containers this sweep will fetch. Automatic root hints are\n // discovery signals, so already-reconciled containers stay settled unless a\n // targeted event forced them; explicit full refreshes still fetch every id.\n for (const containerId of containerIds) {\n state.discoveredContainerIds.add(containerId);\n }\n // Reconcile every container independently: one failing container must not\n // block refreshing the rest. Surface the first real error after the sweep.\n let firstError: unknown;\n const failedForces: FailedForcedContainer[] = [];\n for (\n let offset = 0;\n offset < containerIds.length;\n offset += MAX_CONTAINER_DOCUMENT_LANES\n ) {\n const batch = await sweepContainerBatch({\n containerIds: containerIds.slice(\n offset,\n offset + MAX_CONTAINER_DOCUMENT_LANES,\n ),\n forceAllDocumentContentPulls,\n host,\n lifecycleGeneration,\n state,\n });\n if (!batch) return;\n failedForces.push(...batch.failedForces);\n if (firstError === undefined) firstError = batch.firstError;\n }\n rearmFailedSweepContainers(state, failedForces, lifecycleGeneration);\n if (firstError !== undefined) {\n throw firstError;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"knownContainerSweep.js","sourceRoot":"","sources":["knownContainerSweep.ts"],"names":[],"mappings":"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","sourcesContent":["import { MAX_CONTAINER_DOCUMENT_LANES } from \"@tearleads/validators/request\";\nimport type { ReconciliationClock } from \"./clock\";\nimport {\n acknowledgeContainerForce,\n type IdleBackfillState,\n} from \"./idleBackfill\";\nimport {\n type FailedForcedContainer,\n rearmFailedSweepContainers,\n} from \"./laneFailure\";\nimport {\n type ContainerReconcileResult,\n reconcileContainers,\n} from \"./reconcileContainer\";\nimport type { ReconciliationHost } from \"./serviceTypes\";\n\ninterface KnownContainerSweepState extends IdleBackfillState {\n active: boolean;\n lane: {\n requestSync: () => void;\n requestSyncAfter: (delayMs: number) => void;\n } | null;\n lifecycleGeneration: number;\n}\n\nconst pendingRetries = new WeakMap<\n KnownContainerSweepState,\n Map<string, { cancel: () => void; due: number }>\n>();\n\nexport function clearPendingDiscoveryRetries(\n state: KnownContainerSweepState,\n): void {\n for (const { cancel } of pendingRetries.get(state)?.values() ?? []) cancel();\n pendingRetries.delete(state);\n}\n\nfunction schedulePendingDiscovery(\n clock: ReconciliationClock,\n state: KnownContainerSweepState,\n containerId: string,\n delayMs: number,\n): void {\n if (!state.active) return;\n const retries = pendingRetries.get(state) ?? new Map();\n pendingRetries.set(state, retries);\n const due = clock.now() + delayMs;\n const existing = retries.get(containerId);\n if (existing && existing.due <= due) return;\n existing?.cancel();\n const generation = state.lifecycleGeneration;\n const cancel = clock.schedule(\n () => {\n retries.delete(containerId);\n if (!state.active || state.lifecycleGeneration !== generation) return;\n state.discoveredContainerIds.delete(containerId);\n state.queue.enqueue(\n containerId,\n state.activeContainerId === containerId ? \"active\" : \"idle\",\n );\n state.lane?.requestSync();\n },\n Math.max(1, delayMs),\n );\n retries.set(containerId, { cancel, due });\n}\n\nexport function isCurrentReconciliationLifecycle(\n state: KnownContainerSweepState,\n generation: number,\n): boolean {\n return (\n state.lifecycleGeneration === generation &&\n (state.active || generation === 0)\n );\n}\n\n// Reconcile containers whose discovered marks are already set, rolling a mark\n// back when its container is skipped or fails so a transient error (or a\n// container that later becomes eligible) can be retried.\nexport async function reconcileMarkedContainers(\n host: ReconciliationHost,\n state: KnownContainerSweepState,\n containers: ReadonlyArray<{\n readonly containerId: string;\n readonly forceDocumentContentPull: boolean;\n }>,\n fullRevalidation = false,\n): Promise<ReadonlyArray<ContainerReconcileResult>> {\n const results = await reconcileContainers(\n host,\n containers.map(({ containerId, forceDocumentContentPull }) => ({\n containerId,\n documentIds: fullRevalidation\n ? undefined\n : state.forcedDocumentIds.get(containerId),\n forceDocumentContentPull,\n onFullListing: state.initialDocumentProbe.captureListing(containerId),\n onPendingDiscovery: (delayMs: number) =>\n schedulePendingDiscovery(host.clock, state, containerId, delayMs),\n })),\n );\n for (const result of results) {\n if (result.status !== \"reconciled\") {\n state.discoveredContainerIds.delete(result.containerId);\n }\n }\n return results;\n}\n\n/**\n * Reconciles one batch of a sweep. Force generations are read before the\n * batched listing starts, so a hint arriving while the batch is in flight\n * keeps its newer generation and runs again.\n */\nasync function sweepContainerBatch(input: {\n containerIds: ReadonlyArray<string>;\n forceAllDocumentContentPulls: boolean;\n host: ReconciliationHost;\n lifecycleGeneration: number;\n state: KnownContainerSweepState;\n}): Promise<{\n readonly failedForces: ReadonlyArray<FailedForcedContainer>;\n readonly firstError: unknown;\n} | null> {\n const { containerIds, forceAllDocumentContentPulls, host, state } = input;\n const forceGenerations = new Map(\n containerIds.map((containerId) => [\n containerId,\n state.forcedContainerGenerations.get(containerId),\n ]),\n );\n const results = await reconcileMarkedContainers(\n host,\n state,\n containerIds.map((containerId) => ({\n containerId,\n forceDocumentContentPull:\n forceAllDocumentContentPulls ||\n forceGenerations.get(containerId) !== undefined,\n })),\n forceAllDocumentContentPulls,\n );\n if (!isCurrentReconciliationLifecycle(state, input.lifecycleGeneration)) {\n return null;\n }\n const failedForces: FailedForcedContainer[] = [];\n let firstError: unknown;\n for (const result of results) {\n const forceGeneration = forceGenerations.get(result.containerId);\n if (result.status === \"reconciled\") {\n acknowledgeContainerForce(state, result.containerId, forceGeneration);\n } else if (result.status === \"failed\") {\n if (forceGeneration !== undefined) {\n failedForces.push({ containerId: result.containerId, forceGeneration });\n }\n if (!host.isIgnorableError(result.error) && firstError === undefined) {\n firstError = result.error;\n }\n }\n }\n return { failedForces, firstError };\n}\n\nexport async function sweepKnownContainers(input: {\n forceAllDocumentContentPulls: boolean;\n host: ReconciliationHost;\n knownIds: ReadonlyArray<string>;\n lifecycleGeneration: number;\n state: KnownContainerSweepState;\n}): Promise<void> {\n const {\n forceAllDocumentContentPulls,\n host,\n knownIds,\n lifecycleGeneration,\n state,\n } = input;\n if (!isCurrentReconciliationLifecycle(state, lifecycleGeneration)) {\n return;\n }\n const containerIds = forceAllDocumentContentPulls\n ? knownIds\n : knownIds.filter(\n (containerId) =>\n state.forcedContainerGenerations.has(containerId) ||\n !state.discoveredContainerIds.has(containerId),\n );\n // Mark only the containers this sweep will fetch. Automatic root hints are\n // discovery signals, so already-reconciled containers stay settled unless a\n // targeted event forced them; explicit full refreshes still fetch every id.\n for (const containerId of containerIds) {\n state.discoveredContainerIds.add(containerId);\n }\n // Reconcile every container independently: one failing container must not\n // block refreshing the rest. Surface the first real error after the sweep.\n let firstError: unknown;\n const failedForces: FailedForcedContainer[] = [];\n for (\n let offset = 0;\n offset < containerIds.length;\n offset += MAX_CONTAINER_DOCUMENT_LANES\n ) {\n const batch = await sweepContainerBatch({\n containerIds: containerIds.slice(\n offset,\n offset + MAX_CONTAINER_DOCUMENT_LANES,\n ),\n forceAllDocumentContentPulls,\n host,\n lifecycleGeneration,\n state,\n });\n if (!batch) return;\n failedForces.push(...batch.failedForces);\n if (firstError === undefined) firstError = batch.firstError;\n }\n rearmFailedSweepContainers(state, failedForces, lifecycleGeneration);\n if (firstError !== undefined) {\n throw firstError;\n }\n}\n"]}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { clearOriginatedDocuments } from "../../data/sync/originatedDocuments.js";
|
|
2
2
|
import { acknowledgeContainerForce, activateContainer, enqueueKnownContainersForIdleBackfill, markContainerForced, } from "./idleBackfill.js";
|
|
3
3
|
import { createInitialDocumentProbe } from "./initialDocumentProbe.js";
|
|
4
4
|
import { clearPendingDiscoveryRetries, isCurrentReconciliationLifecycle, reconcileMarkedContainers, sweepKnownContainers, } from "./knownContainerSweep.js";
|
|
5
5
|
import { rearmFailedContainer } from "./laneFailure.js";
|
|
6
|
-
import { clearOriginatedDocuments } from "./originatedDocuments.js";
|
|
7
6
|
import { createReconcileQueue } from "./queue.js";
|
|
8
7
|
function canReconcile(status) {
|
|
9
8
|
return status.dbStatus === "ready" && status.isAuthenticated && status.online;
|
|
@@ -84,13 +83,12 @@ function scheduleProbeContinuation(host, state) {
|
|
|
84
83
|
state.probeContinuationCancel = null;
|
|
85
84
|
return;
|
|
86
85
|
}
|
|
87
|
-
|
|
86
|
+
state.probeContinuationCancel = host.clock.schedule(() => {
|
|
88
87
|
if (canContinue()) {
|
|
89
88
|
state.probeContinuationCancel = null;
|
|
90
89
|
state.lane?.requestSync();
|
|
91
90
|
}
|
|
92
91
|
}, state.initialDocumentProbe.continuationDelayMs());
|
|
93
|
-
state.probeContinuationCancel = () => clearTimeout(timeout);
|
|
94
92
|
}
|
|
95
93
|
function forgetIneligibleDiscoveredContainers(host, state) {
|
|
96
94
|
for (const containerId of state.discoveredContainerIds) {
|
|
@@ -197,7 +195,7 @@ function startReconciliationLane(host, state) {
|
|
|
197
195
|
}
|
|
198
196
|
state.lifecycleGeneration += 1;
|
|
199
197
|
state.active = true;
|
|
200
|
-
state.lane =
|
|
198
|
+
state.lane = host.registerLane("reconciliation:documents", {
|
|
201
199
|
label: "Device-first document reconciliation",
|
|
202
200
|
// Document discovery runs in the document phase, after structural
|
|
203
201
|
// container hydration/metadata sync settles for the scope.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["service.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gCAAgC,GAEjC,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,qCAAqC,EAErC,mBAAmB,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EACL,4BAA4B,EAC5B,gCAAgC,EAChC,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAA0B,MAAM,SAAS,CAAC;AAOvE,SAAS,YAAY,CAAC,MAAmC;IACvD,OAAO,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,CAAC,eAAe,IAAI,MAAM,CAAC,MAAM,CAAC;AAChF,CAAC;AAgBD;;;;GAIG;AACH,MAAM,yBAAyB,GAAG,CAAC,CAAC;AAEpC,SAAS,qBAAqB,CAAC,KAA0B;IACvD,MAAM,YAAY,GAAa,EAAE,CAAC;IAClC,OAAO,YAAY,CAAC,MAAM,GAAG,yBAAyB,EAAE,CAAC;QACvD,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QAC1C,IAAI,CAAC,WAAW;YAAE,MAAM;QACxB,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC7B,IAAwB,EACxB,KAA0B;IAE1B,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,EAAE,CAAC;QAC5D,OAAO;IACT,CAAC;IACD,MAAM,mBAAmB,GAAG,KAAK,CAAC,mBAAmB,CAAC;IAEtD,MAAM,YAAY,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAClD,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,MAAM,KAAK,CAAC,oBAAoB,CAAC,GAAG,EAAE,CAAC;QACvC,yBAAyB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvC,OAAO;IACT,CAAC;IACD,wEAAwE;IACxE,yEAAyE;IACzE,6EAA6E;IAC7E,2EAA2E;IAC3E,0DAA0D;IAC1D,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,EAAE;QACjD,MAAM,eAAe,GAAG,KAAK,CAAC,0BAA0B,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC1E,IACE,eAAe,KAAK,SAAS;YAC7B,KAAK,CAAC,sBAAsB,CAAC,GAAG,CAAC,WAAW,CAAC,EAC7C,CAAC;YACD,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,KAAK,CAAC,sBAAsB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC9C,OAAO,CAAC,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,yBAAyB,CAC7C,IAAI,EACJ,KAAK,EACL,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,EAAE,eAAe,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/C,WAAW;QACX,wBAAwB,EAAE,eAAe,KAAK,SAAS;KACxD,CAAC,CAAC,CACJ,CAAC;IACF,IAAI,UAAmB,CAAC;IACxB,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;QAChC,MAAM,eAAe,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,eAAe,CAAC;QACtD,IAAI,MAAM,CAAC,MAAM,KAAK,YAAY,EAAE,CAAC;YACnC,yBAAyB,CAAC,KAAK,EAAE,MAAM,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;QACxE,CAAC;aAAM,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YACtC,oBAAoB,CAClB,KAAK,EACL,MAAM,CAAC,WAAW,EAClB,eAAe,EACf,mBAAmB,CACpB,CAAC;YACF,UAAU,KAAK,MAAM,CAAC,KAAK,CAAC;QAC9B,CAAC;IACH,CAAC,CAAC,CAAC;IACH,IAAI,UAAU,KAAK,SAAS;QAAE,MAAM,UAAU,CAAC;IAE/C,mEAAmE;IACnE,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC;IAC5B,CAAC;SAAM,CAAC;QACN,yBAAyB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED,SAAS,yBAAyB,CAChC,IAAwB,EACxB,KAA0B;IAE1B,KAAK,CAAC,uBAAuB,EAAE,EAAE,CAAC;IAClC,MAAM,WAAW,GAAG,GAAG,EAAE,CACvB,KAAK,CAAC,MAAM;QACZ,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC;QACtB,YAAY,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACrC,KAAK,CAAC,oBAAoB,CAAC,cAAc,EAAE,CAAC;IAC9C,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACnB,KAAK,CAAC,uBAAuB,GAAG,IAAI,CAAC;QACrC,OAAO;IACT,CAAC;IACD,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;QAC9B,IAAI,WAAW,EAAE,EAAE,CAAC;YAClB,KAAK,CAAC,uBAAuB,GAAG,IAAI,CAAC;YACrC,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC;QAC5B,CAAC;IACH,CAAC,EAAE,KAAK,CAAC,oBAAoB,CAAC,mBAAmB,EAAE,CAAC,CAAC;IACrD,KAAK,CAAC,uBAAuB,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,oCAAoC,CAC3C,IAAwB,EACxB,KAA0B;IAE1B,KAAK,MAAM,WAAW,IAAI,KAAK,CAAC,sBAAsB,EAAE,CAAC;QACvD,IAAI,CAAC,IAAI,CAAC,6BAA6B,CAAC,WAAW,CAAC,EAAE,CAAC;YACrD,KAAK,CAAC,sBAAsB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;AACH,CAAC;AAED,KAAK,UAAU,6BAA6B,CAAC,KAM5C;IACC,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAC7D,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,EAAE,CAAC;QAC3C,OAAO;IACT,CAAC;IACD,MAAM,mBAAmB,GAAG,KAAK,CAAC,mBAAmB,CAAC;IAEtD,IAAI,KAAK,CAAC,4BAA4B,EAAE,CAAC;QACvC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IACD,IAAI,CAAC;QACH,2EAA2E;QAC3E,2EAA2E;QAC3E,yEAAyE;QACzE,kCAAkC;QAClC,oCAAoC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAClD,MAAM,WAAW,EAAE,CAAC;QACpB,IAAI,CAAC,gCAAgC,CAAC,KAAK,EAAE,mBAAmB,CAAC,EAAE,CAAC;YAClE,OAAO;QACT,CAAC;QACD,0EAA0E;QAC1E,yEAAyE;QACzE,2EAA2E;QAC3E,2EAA2E;QAC3E,eAAe;QACf,oCAAoC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAClD,MAAM,oBAAoB,CAAC;YACzB,4BAA4B,EAAE,KAAK,CAAC,4BAA4B;YAChE,IAAI;YACJ,QAAQ,EAAE,gBAAgB,EAAE;YAC5B,mBAAmB;YACnB,KAAK;SACN,CAAC,CAAC;QACH,IAAI,CAAC,gCAAgC,CAAC,KAAK,EAAE,mBAAmB,CAAC,EAAE,CAAC;YAClE,OAAO;QACT,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,gCAAgC,CAAC,KAAK,EAAE,mBAAmB,CAAC,EAAE,CAAC;YAClE,OAAO;QACT,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;YAClC,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;AACH,CAAC;AAED,wEAAwE;AACxE,6EAA6E;AAC7E,4EAA4E;AAC5E,+EAA+E;AAC/E,8EAA8E;AAC9E,SAAS,uBAAuB,CAAC,KAOhC;IACC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAC/B,MAAM,OAAO,GAAG,KAAK,CAAC,cAAc,CAAC;IACrC,IACE,OAAO;QACP,CAAC,KAAK,KAAK,MAAM,IAAI,OAAO,CAAC,KAAK,KAAK,MAAM,CAAC;QAC9C,CAAC,CAAC,KAAK,CAAC,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAC9C,CAAC;QACD,OAAO,OAAO,CAAC,OAAO,CAAC;IACzB,CAAC;IAED,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IACjC,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,OAAO,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACrD,8DAA8D;SAC7D,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;SACtB,IAAI,CAAC,GAAG,EAAE;QACT,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;QACrB,OAAO,6BAA6B,CAAC;YACnC,4BAA4B,EAAE,KAAK,KAAK,MAAM;YAC9C,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;YACxC,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,KAAK;SACN,CAAC,CAAC;IACL,CAAC,CAAC;SACD,OAAO,CAAC,GAAG,EAAE;QACZ,IAAI,KAAK,CAAC,cAAc,EAAE,OAAO,KAAK,OAAO,EAAE,CAAC;YAC9C,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC;QAC9B,CAAC;IACH,CAAC,CAAC,CAAC;IACL,KAAK,CAAC,cAAc,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IACnD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,2BAA2B,CAClC,IAAwB,EACxB,KAA0B;IAE1B,MAAM,QAAQ,GAAG,IAAI,CAAC,qBAAqB,EAAE,CAAC;IAC9C,MAAM,iBAAiB,GAAG,KAAK,CAAC,iBAAiB,CAAC;IAClD,IACE,CAAC,iBAAiB;QAClB,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,CAAC;QACpC,CAAC,IAAI,CAAC,6BAA6B,CAAC,iBAAiB,CAAC,EACtD,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,0EAA0E;IAC1E,sCAAsC;IACtC,OAAO,CAAC,GAAG,QAAQ,EAAE,iBAAiB,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,uBAAuB,CAC9B,IAAwB,EACxB,KAA0B;IAE1B,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjB,OAAO;IACT,CAAC;IACD,KAAK,CAAC,mBAAmB,IAAI,CAAC,CAAC;IAC/B,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,CAAC,IAAI,GAAG,gCAAgC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,YAAY,CAC1E,0BAA0B,EAC1B;QACE,KAAK,EAAE,sCAAsC;QAC7C,kEAAkE;QAClE,2DAA2D;QAC3D,KAAK,EAAE,UAAU;QACjB,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAClD,iBAAiB,EAAE,CAAC,KAAK,EAAE,EAAE;YAC3B,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAC;QAC9D,CAAC;QACD,qEAAqE;QACrE,sEAAsE;QACtE,yEAAyE;QACzE,mDAAmD;QACnD,qBAAqB,EAAE,CAAC,KAAK,EAAE,EAAE,CAC/B,IAAI,CAAC,QAAQ,EAAE,CAAC,kCAAkC,EAAE,KAAK,CAAC;QAC5D,GAAG,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,KAAK,CAAC;QACxC,iBAAiB,EAAE,IAAI,CAAC,gBAAgB;KACzC,CACF,CAAC;AACJ,CAAC;AAED,SAAS,yBAAyB,CAChC,IAAwB;IAExB,OAAO;QACL,MAAM,EAAE,KAAK;QACb,iBAAiB,EAAE,IAAI;QACvB,yBAAyB,EAAE,IAAI,GAAG,EAAE;QACpC,sBAAsB,EAAE,IAAI,GAAG,EAAE;QACjC,0BAA0B,EAAE,IAAI,GAAG,EAAE;QACrC,iBAAiB,EAAE,IAAI,GAAG,EAAE;QAC5B,oBAAoB,EAAE,0BAA0B,CAAC,IAAI,CAAC;QACtD,IAAI,EAAE,IAAI;QACV,mBAAmB,EAAE,CAAC;QACtB,mBAAmB,EAAE,CAAC;QACtB,uBAAuB,EAAE,IAAI;QAC7B,KAAK,EAAE,oBAAoB,EAAE;QAC7B,cAAc,EAAE,IAAI;KACrB,CAAC;AACJ,CAAC;AAED,SAAS,yBAAyB,CAChC,IAAwB,EACxB,KAA0B;IAE1B,4BAA4B,CAAC,KAAK,CAAC,CAAC;IACpC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;IACrB,KAAK,CAAC,uBAAuB,EAAE,EAAE,CAAC;IAClC,KAAK,CAAC,uBAAuB,GAAG,IAAI,CAAC;IACrC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACpB,KAAK,CAAC,yBAAyB,CAAC,KAAK,EAAE,CAAC;IACxC,KAAK,CAAC,0BAA0B,CAAC,KAAK,EAAE,CAAC;IACzC,KAAK,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC;IAChC,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,oBAAoB,CAAC,YAAY,EAAE,CAAC;IAC1C,mEAAmE;IACnE,yEAAyE;IACzE,yEAAyE;IACzE,KAAK,CAAC,sBAAsB,CAAC,KAAK,EAAE,CAAC;IACrC,0EAA0E;IAC1E,6BAA6B;IAC7B,wBAAwB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,2BAA2B,CACzC,IAAwB;IAExB,MAAM,KAAK,GAAG,yBAAyB,CAAC,IAAI,CAAC,CAAC;IAE9C,MAAM,aAAa,GAAG,GAAG,EAAE;QACzB,IACE,CAAC,KAAK,CAAC,MAAM;YACb,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,MAAM,EAAE,CAAC,EAChE,CAAC;YACD,OAAO;QACT,CAAC;QACD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,EAAE,CAAC;YAC3C,OAAO;QACT,CAAC;QACD,KAAK,CAAC,uBAAuB,EAAE,EAAE,CAAC;QAClC,KAAK,CAAC,uBAAuB,GAAG,IAAI,CAAC;QACrC,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC;IAC5B,CAAC,CAAC;IAEF,MAAM,gBAAgB,GAAG,CACvB,WAAmB,EACnB,QAA2B,EAC3B,KAAK,GAAG,KAAK,EACb,UAAmB,EACnB,EAAE;QACF,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QACD,wEAAwE;QACxE,sEAAsE;QACtE,yEAAyE;QACzE,IACE,CAAC,KAAK;YACN,CAAC,KAAK,CAAC,0BAA0B,CAAC,GAAG,CAAC,WAAW,CAAC;YAClD,KAAK,CAAC,sBAAsB,CAAC,GAAG,CAAC,WAAW,CAAC,EAC7C,CAAC;YACD,OAAO;QACT,CAAC;QACD,IAAI,KAAK,EAAE,CAAC;YACV,mBAAmB,CAAC,KAAK,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;QACtD,CAAC;QACD,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;QAC3C,aAAa,EAAE,CAAC;IAClB,CAAC,CAAC;IAEF,MAAM,mBAAmB,GAAG,GAAG,EAAE;QAC/B,qCAAqC,CAAC;YACpC,IAAI;YACJ,aAAa;YACb,KAAK;SACN,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO;QACL,KAAK,EAAE,GAAG,EAAE;YACV,uBAAuB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACrC,aAAa,EAAE,CAAC;QAClB,CAAC;QACD,4EAA4E;QAC5E,kBAAkB,EAAE,CAAC,WAAW,EAAE,EAAE,CAClC,iBAAiB,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC,QAAQ,EAAE,EAAE,CACjD,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CACrC;QACH,gBAAgB;QAChB,mBAAmB;QACnB,eAAe,EAAE,GAAG,EAAE;YACpB,KAAK,CAAC,sBAAsB,CAAC,KAAK,EAAE,CAAC;YACrC,KAAK,CAAC,oBAAoB,CAAC,oBAAoB,EAAE,CAAC;QACpD,CAAC;QACD,0BAA0B,EAAE,CAAC,OAAO,EAAE,EAAE,CACtC,uBAAuB,CAAC;YACtB,YAAY,EAAE,OAAO,EAAE,YAAY,KAAK,IAAI;YAC5C,IAAI;YACJ,KAAK;YACL,WAAW,EAAE,IAAI,CAAC,eAAe;YACjC,gBAAgB,EAAE,IAAI,CAAC,oCAAoC;YAC3D,KAAK,EAAE,MAAM;SACd,CAAC;QACJ,YAAY,EAAE,GAAG,EAAE,CACjB,uBAAuB,CAAC;YACtB,YAAY,EAAE,KAAK;YACnB,IAAI;YACJ,KAAK;YACL,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,gBAAgB,EAAE,GAAG,EAAE,CAAC,2BAA2B,CAAC,IAAI,EAAE,KAAK,CAAC;YAChE,KAAK,EAAE,MAAM;SACd,CAAC;QACJ,IAAI,EAAE,GAAG,EAAE,CAAC,yBAAyB,CAAC,IAAI,EAAE,KAAK,CAAC;KACnD,CAAC;AACJ,CAAC","sourcesContent":["import {\n getOrCreateDomainSyncCoordinator,\n type SyncLane,\n} from \"../../data/sync/syncCoordinator\";\nimport {\n acknowledgeContainerForce,\n activateContainer,\n enqueueKnownContainersForIdleBackfill,\n type IdleBackfillState,\n markContainerForced,\n} from \"./idleBackfill\";\nimport { createInitialDocumentProbe } from \"./initialDocumentProbe\";\nimport {\n clearPendingDiscoveryRetries,\n isCurrentReconciliationLifecycle,\n reconcileMarkedContainers,\n sweepKnownContainers,\n} from \"./knownContainerSweep\";\nimport { rearmFailedContainer } from \"./laneFailure\";\nimport { clearOriginatedDocuments } from \"./originatedDocuments\";\nimport { createReconcileQueue, type ReconcilePriority } from \"./queue\";\nimport type {\n ReconciliationHost,\n ReconciliationRuntimeStatus,\n ReconciliationService,\n} from \"./serviceTypes\";\n\nfunction canReconcile(status: ReconciliationRuntimeStatus): boolean {\n return status.dbStatus === \"ready\" && status.isAuthenticated && status.online;\n}\n\ntype RefreshScope = \"root\" | \"full\";\n\ninterface ReconciliationState extends IdleBackfillState {\n active: boolean;\n lane: SyncLane | null;\n lifecycleGeneration: number;\n probeContinuationCancel: (() => void) | null;\n pendingRefresh: {\n readonly promise: Promise<void>;\n readonly scope: RefreshScope;\n readonly started: { value: boolean };\n } | null;\n}\n\n/**\n * Queued containers one lane run lists together. Each still verifies and\n * applies on its own; the bound keeps one run's head verification well\n * inside the lane watchdog.\n */\nconst RECONCILE_LANE_BATCH_SIZE = 4;\n\nfunction dequeueContainerBatch(state: ReconciliationState): string[] {\n const containerIds: string[] = [];\n while (containerIds.length < RECONCILE_LANE_BATCH_SIZE) {\n const containerId = state.queue.dequeue();\n if (!containerId) break;\n containerIds.push(containerId);\n }\n return containerIds;\n}\n\nasync function runReconcileLane(\n host: ReconciliationHost,\n state: ReconciliationState,\n): Promise<void> {\n if (!state.active || !canReconcile(host.getRuntimeStatus())) {\n return;\n }\n const lifecycleGeneration = state.lifecycleGeneration;\n\n const containerIds = dequeueContainerBatch(state);\n if (containerIds.length === 0) {\n await state.initialDocumentProbe.run();\n scheduleProbeContinuation(host, state);\n return;\n }\n // Double-check at run time: a container may have been discovered (by an\n // explicit refresh or an earlier lane pass) after it was queued. Mark it\n // discovered up front to collapse concurrent re-enqueues into one fetch, but\n // roll the mark back on failure so a transient error can be retried. Force\n // generations are read before the batched listing starts.\n const batch = containerIds.flatMap((containerId) => {\n const forceGeneration = state.forcedContainerGenerations.get(containerId);\n if (\n forceGeneration === undefined &&\n state.discoveredContainerIds.has(containerId)\n ) {\n return [];\n }\n state.discoveredContainerIds.add(containerId);\n return [{ containerId, forceGeneration }];\n });\n const results = await reconcileMarkedContainers(\n host,\n state,\n batch.map(({ containerId, forceGeneration }) => ({\n containerId,\n forceDocumentContentPull: forceGeneration !== undefined,\n })),\n );\n let firstError: unknown;\n results.forEach((result, index) => {\n const forceGeneration = batch[index]?.forceGeneration;\n if (result.status === \"reconciled\") {\n acknowledgeContainerForce(state, result.containerId, forceGeneration);\n } else if (result.status === \"failed\") {\n rearmFailedContainer(\n state,\n result.containerId,\n forceGeneration,\n lifecycleGeneration,\n );\n firstError ??= result.error;\n }\n });\n if (firstError !== undefined) throw firstError;\n\n // Keep draining: schedule another pass while the queue holds work.\n if (state.queue.size > 0) {\n state.lane?.requestSync();\n } else {\n scheduleProbeContinuation(host, state);\n }\n}\n\nfunction scheduleProbeContinuation(\n host: ReconciliationHost,\n state: ReconciliationState,\n): void {\n state.probeContinuationCancel?.();\n const canContinue = () =>\n state.active &&\n state.queue.size === 0 &&\n canReconcile(host.getRuntimeStatus()) &&\n state.initialDocumentProbe.hasPendingWork();\n if (!canContinue()) {\n state.probeContinuationCancel = null;\n return;\n }\n const timeout = setTimeout(() => {\n if (canContinue()) {\n state.probeContinuationCancel = null;\n state.lane?.requestSync();\n }\n }, state.initialDocumentProbe.continuationDelayMs());\n state.probeContinuationCancel = () => clearTimeout(timeout);\n}\n\nfunction forgetIneligibleDiscoveredContainers(\n host: ReconciliationHost,\n state: ReconciliationState,\n): void {\n for (const containerId of state.discoveredContainerIds) {\n if (!host.canDiscoverContainerDocuments(containerId)) {\n state.discoveredContainerIds.delete(containerId);\n }\n }\n}\n\nasync function refreshAndReconcileContainers(input: {\n forceAllDocumentContentPulls: boolean;\n host: ReconciliationHost;\n listContainerIds: () => ReadonlyArray<string>;\n refreshTree: () => Promise<void>;\n state: ReconciliationState;\n}): Promise<void> {\n const { host, listContainerIds, refreshTree, state } = input;\n if (!canReconcile(host.getRuntimeStatus())) {\n return;\n }\n const lifecycleGeneration = state.lifecycleGeneration;\n\n if (input.forceAllDocumentContentPulls) {\n state.queue.clear();\n }\n try {\n // A resync_required structural refresh runs independently of this service.\n // If it already removed a container after the forced document lane won its\n // race against the old tree, forget the stale discovered bit before this\n // refresh can re-add the same id.\n forgetIneligibleDiscoveredContainers(host, state);\n await refreshTree();\n if (!isCurrentReconciliationLifecycle(state, lifecycleGeneration)) {\n return;\n }\n // Structural hydration can revoke and remove a container after a targeted\n // force already reconciled it against the old tree. Forget every id that\n // is no longer remotely listable so a later share of the same container id\n // is treated as newly surfaced instead of being suppressed for the rest of\n // the session.\n forgetIneligibleDiscoveredContainers(host, state);\n await sweepKnownContainers({\n forceAllDocumentContentPulls: input.forceAllDocumentContentPulls,\n host,\n knownIds: listContainerIds(),\n lifecycleGeneration,\n state,\n });\n if (!isCurrentReconciliationLifecycle(state, lifecycleGeneration)) {\n return;\n }\n } catch (error) {\n if (!isCurrentReconciliationLifecycle(state, lifecycleGeneration)) {\n return;\n }\n if (!host.isIgnorableError(error)) {\n throw error;\n }\n }\n}\n\n// Sweeps share the queue and discovered set, so they must run serially.\n// Root requests join either kind of sweep; a full request after a root sweep\n// must wait for it and then refresh the whole tree. A fresh-listing request\n// joins only a sweep that has not started: one already running may have listed\n// before the event behind the request, so it queues a trailing sweep instead.\nfunction requestContainerRefresh(input: {\n freshListing: boolean;\n host: ReconciliationHost;\n state: ReconciliationState;\n refreshTree: () => Promise<void>;\n listContainerIds: () => ReadonlyArray<string>;\n scope: RefreshScope;\n}): Promise<void> {\n const { state, scope } = input;\n const pending = state.pendingRefresh;\n if (\n pending &&\n (scope === \"root\" || pending.scope === \"full\") &&\n !(input.freshListing && pending.started.value)\n ) {\n return pending.promise;\n }\n\n const started = { value: false };\n const promise = (pending?.promise ?? Promise.resolve())\n // A failed earlier sweep must not prevent the queued refresh.\n .catch(() => undefined)\n .then(() => {\n started.value = true;\n return refreshAndReconcileContainers({\n forceAllDocumentContentPulls: scope === \"full\",\n host: input.host,\n listContainerIds: input.listContainerIds,\n refreshTree: input.refreshTree,\n state,\n });\n })\n .finally(() => {\n if (state.pendingRefresh?.promise === promise) {\n state.pendingRefresh = null;\n }\n });\n state.pendingRefresh = { promise, scope, started };\n return promise;\n}\n\nfunction listFullRefreshContainerIds(\n host: ReconciliationHost,\n state: ReconciliationState,\n): ReadonlyArray<string> {\n const knownIds = host.listKnownContainerIds();\n const activeContainerId = state.activeContainerId;\n if (\n !activeContainerId ||\n knownIds.includes(activeContainerId) ||\n !host.canDiscoverContainerDocuments(activeContainerId)\n ) {\n return knownIds;\n }\n // Explicit refresh also retries an open foreign system container that the\n // background discovery list excludes.\n return [...knownIds, activeContainerId];\n}\n\nfunction startReconciliationLane(\n host: ReconciliationHost,\n state: ReconciliationState,\n): void {\n if (state.active) {\n return;\n }\n state.lifecycleGeneration += 1;\n state.active = true;\n state.lane = getOrCreateDomainSyncCoordinator(host.domainScope).registerLane(\n \"reconciliation:documents\",\n {\n label: \"Device-first document reconciliation\",\n // Document discovery runs in the document phase, after structural\n // container hydration/metadata sync settles for the scope.\n phase: \"document\",\n priority: () => (state.activeContainerId ? -1 : 0),\n onUnexpectedError: (error) => {\n console.error(\"Device-first reconciliation failed:\", error);\n },\n // Fixed literal: no container or document id may reach a report. The\n // mapped stack identifies which lane body threw. Returning the host's\n // result hands a rejection to the lane reporter's wrapper; discarding it\n // would surface as an unhandled rejection instead.\n reportUnexpectedError: (error) =>\n host.logError?.(\"Reconciliation: sync lane failed\", error),\n run: () => runReconcileLane(host, state),\n shouldIgnoreError: host.isIgnorableError,\n },\n );\n}\n\nfunction createReconciliationState(\n host: ReconciliationHost,\n): ReconciliationState {\n return {\n active: false,\n activeContainerId: null,\n automaticRetryGenerations: new Map(),\n discoveredContainerIds: new Set(),\n forcedContainerGenerations: new Map(),\n forcedDocumentIds: new Map(),\n initialDocumentProbe: createInitialDocumentProbe(host),\n lane: null,\n lifecycleGeneration: 0,\n nextForceGeneration: 0,\n probeContinuationCancel: null,\n queue: createReconcileQueue(),\n pendingRefresh: null,\n };\n}\n\nfunction stopReconciliationService(\n host: ReconciliationHost,\n state: ReconciliationState,\n): void {\n clearPendingDiscoveryRetries(state);\n state.active = false;\n state.probeContinuationCancel?.();\n state.probeContinuationCancel = null;\n state.queue.clear();\n state.automaticRetryGenerations.clear();\n state.forcedContainerGenerations.clear();\n state.forcedDocumentIds.clear();\n state.pendingRefresh = null;\n state.initialDocumentProbe.resetPending();\n // Drop the per-session discovered suppression cache too: a stopped\n // reconciler is being torn down (scope/identity change) or paused across\n // a prerequisite loss, after which every container must be re-validated.\n state.discoveredContainerIds.clear();\n // Drop pending self-echo originations too — they are session-scoped and a\n // teardown invalidates them.\n clearOriginatedDocuments(host.domainScope);\n}\n\nexport function createReconciliationService(\n host: ReconciliationHost,\n): ReconciliationService {\n const state = createReconciliationState(host);\n\n const scheduleDrain = () => {\n if (\n !state.active ||\n (state.queue.size === 0 && !state.initialDocumentProbe.canRun())\n ) {\n return;\n }\n if (!canReconcile(host.getRuntimeStatus())) {\n return;\n }\n state.probeContinuationCancel?.();\n state.probeContinuationCancel = null;\n state.lane?.requestSync();\n };\n\n const enqueueContainer = (\n containerId: string,\n priority: ReconcilePriority,\n force = false,\n documentId?: string,\n ) => {\n if (!containerId) {\n return;\n }\n // Skip containers already reconciled this session unless forced. Events\n // force re-discovery; passive active/backfill scheduling does not, so\n // opening Explorer does not re-fetch every container on each navigation.\n if (\n !force &&\n !state.forcedContainerGenerations.has(containerId) &&\n state.discoveredContainerIds.has(containerId)\n ) {\n return;\n }\n if (force) {\n markContainerForced(state, containerId, documentId);\n }\n state.queue.enqueue(containerId, priority);\n scheduleDrain();\n };\n\n const enqueueIdleBackfill = () => {\n enqueueKnownContainersForIdleBackfill({\n host,\n scheduleDrain,\n state,\n });\n };\n\n return {\n start: () => {\n startReconciliationLane(host, state);\n scheduleDrain();\n },\n // Reconcile the active container first; siblings remain lazy until visited.\n setActiveContainer: (containerId) =>\n activateContainer(state, containerId, (activeId) =>\n enqueueContainer(activeId, \"active\"),\n ),\n enqueueContainer,\n enqueueIdleBackfill,\n resetDiscovered: () => {\n state.discoveredContainerIds.clear();\n state.initialDocumentProbe.resetSkippedListings();\n },\n reconcileRootContainersNow: (options) =>\n requestContainerRefresh({\n freshListing: options?.freshListing === true,\n host,\n state,\n refreshTree: host.refreshRootTree,\n listContainerIds: host.listAutomaticRootCatchupContainerIds,\n scope: \"root\",\n }),\n reconcileNow: () =>\n requestContainerRefresh({\n freshListing: false,\n host,\n state,\n refreshTree: host.refreshTree,\n listContainerIds: () => listFullRefreshContainerIds(host, state),\n scope: \"full\",\n }),\n stop: () => stopReconciliationService(host, state),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["service.ts"],"names":[],"mappings":"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","sourcesContent":["import { clearOriginatedDocuments } from \"../../data/sync/originatedDocuments\";\nimport type { SyncLane } from \"../../data/sync/syncLaneConfig\";\nimport {\n acknowledgeContainerForce,\n activateContainer,\n enqueueKnownContainersForIdleBackfill,\n type IdleBackfillState,\n markContainerForced,\n} from \"./idleBackfill\";\nimport { createInitialDocumentProbe } from \"./initialDocumentProbe\";\nimport {\n clearPendingDiscoveryRetries,\n isCurrentReconciliationLifecycle,\n reconcileMarkedContainers,\n sweepKnownContainers,\n} from \"./knownContainerSweep\";\nimport { rearmFailedContainer } from \"./laneFailure\";\nimport { createReconcileQueue, type ReconcilePriority } from \"./queue\";\nimport type {\n ReconciliationHost,\n ReconciliationRuntimeStatus,\n ReconciliationService,\n} from \"./serviceTypes\";\n\nfunction canReconcile(status: ReconciliationRuntimeStatus): boolean {\n return status.dbStatus === \"ready\" && status.isAuthenticated && status.online;\n}\n\ntype RefreshScope = \"root\" | \"full\";\n\ninterface ReconciliationState extends IdleBackfillState {\n active: boolean;\n lane: SyncLane | null;\n lifecycleGeneration: number;\n probeContinuationCancel: (() => void) | null;\n pendingRefresh: {\n readonly promise: Promise<void>;\n readonly scope: RefreshScope;\n readonly started: { value: boolean };\n } | null;\n}\n\n/**\n * Queued containers one lane run lists together. Each still verifies and\n * applies on its own; the bound keeps one run's head verification well\n * inside the lane watchdog.\n */\nconst RECONCILE_LANE_BATCH_SIZE = 4;\n\nfunction dequeueContainerBatch(state: ReconciliationState): string[] {\n const containerIds: string[] = [];\n while (containerIds.length < RECONCILE_LANE_BATCH_SIZE) {\n const containerId = state.queue.dequeue();\n if (!containerId) break;\n containerIds.push(containerId);\n }\n return containerIds;\n}\n\nasync function runReconcileLane(\n host: ReconciliationHost,\n state: ReconciliationState,\n): Promise<void> {\n if (!state.active || !canReconcile(host.getRuntimeStatus())) {\n return;\n }\n const lifecycleGeneration = state.lifecycleGeneration;\n\n const containerIds = dequeueContainerBatch(state);\n if (containerIds.length === 0) {\n await state.initialDocumentProbe.run();\n scheduleProbeContinuation(host, state);\n return;\n }\n // Double-check at run time: a container may have been discovered (by an\n // explicit refresh or an earlier lane pass) after it was queued. Mark it\n // discovered up front to collapse concurrent re-enqueues into one fetch, but\n // roll the mark back on failure so a transient error can be retried. Force\n // generations are read before the batched listing starts.\n const batch = containerIds.flatMap((containerId) => {\n const forceGeneration = state.forcedContainerGenerations.get(containerId);\n if (\n forceGeneration === undefined &&\n state.discoveredContainerIds.has(containerId)\n ) {\n return [];\n }\n state.discoveredContainerIds.add(containerId);\n return [{ containerId, forceGeneration }];\n });\n const results = await reconcileMarkedContainers(\n host,\n state,\n batch.map(({ containerId, forceGeneration }) => ({\n containerId,\n forceDocumentContentPull: forceGeneration !== undefined,\n })),\n );\n let firstError: unknown;\n results.forEach((result, index) => {\n const forceGeneration = batch[index]?.forceGeneration;\n if (result.status === \"reconciled\") {\n acknowledgeContainerForce(state, result.containerId, forceGeneration);\n } else if (result.status === \"failed\") {\n rearmFailedContainer(\n state,\n result.containerId,\n forceGeneration,\n lifecycleGeneration,\n );\n firstError ??= result.error;\n }\n });\n if (firstError !== undefined) throw firstError;\n\n // Keep draining: schedule another pass while the queue holds work.\n if (state.queue.size > 0) {\n state.lane?.requestSync();\n } else {\n scheduleProbeContinuation(host, state);\n }\n}\n\nfunction scheduleProbeContinuation(\n host: ReconciliationHost,\n state: ReconciliationState,\n): void {\n state.probeContinuationCancel?.();\n const canContinue = () =>\n state.active &&\n state.queue.size === 0 &&\n canReconcile(host.getRuntimeStatus()) &&\n state.initialDocumentProbe.hasPendingWork();\n if (!canContinue()) {\n state.probeContinuationCancel = null;\n return;\n }\n state.probeContinuationCancel = host.clock.schedule(() => {\n if (canContinue()) {\n state.probeContinuationCancel = null;\n state.lane?.requestSync();\n }\n }, state.initialDocumentProbe.continuationDelayMs());\n}\n\nfunction forgetIneligibleDiscoveredContainers(\n host: ReconciliationHost,\n state: ReconciliationState,\n): void {\n for (const containerId of state.discoveredContainerIds) {\n if (!host.canDiscoverContainerDocuments(containerId)) {\n state.discoveredContainerIds.delete(containerId);\n }\n }\n}\n\nasync function refreshAndReconcileContainers(input: {\n forceAllDocumentContentPulls: boolean;\n host: ReconciliationHost;\n listContainerIds: () => ReadonlyArray<string>;\n refreshTree: () => Promise<void>;\n state: ReconciliationState;\n}): Promise<void> {\n const { host, listContainerIds, refreshTree, state } = input;\n if (!canReconcile(host.getRuntimeStatus())) {\n return;\n }\n const lifecycleGeneration = state.lifecycleGeneration;\n\n if (input.forceAllDocumentContentPulls) {\n state.queue.clear();\n }\n try {\n // A resync_required structural refresh runs independently of this service.\n // If it already removed a container after the forced document lane won its\n // race against the old tree, forget the stale discovered bit before this\n // refresh can re-add the same id.\n forgetIneligibleDiscoveredContainers(host, state);\n await refreshTree();\n if (!isCurrentReconciliationLifecycle(state, lifecycleGeneration)) {\n return;\n }\n // Structural hydration can revoke and remove a container after a targeted\n // force already reconciled it against the old tree. Forget every id that\n // is no longer remotely listable so a later share of the same container id\n // is treated as newly surfaced instead of being suppressed for the rest of\n // the session.\n forgetIneligibleDiscoveredContainers(host, state);\n await sweepKnownContainers({\n forceAllDocumentContentPulls: input.forceAllDocumentContentPulls,\n host,\n knownIds: listContainerIds(),\n lifecycleGeneration,\n state,\n });\n if (!isCurrentReconciliationLifecycle(state, lifecycleGeneration)) {\n return;\n }\n } catch (error) {\n if (!isCurrentReconciliationLifecycle(state, lifecycleGeneration)) {\n return;\n }\n if (!host.isIgnorableError(error)) {\n throw error;\n }\n }\n}\n\n// Sweeps share the queue and discovered set, so they must run serially.\n// Root requests join either kind of sweep; a full request after a root sweep\n// must wait for it and then refresh the whole tree. A fresh-listing request\n// joins only a sweep that has not started: one already running may have listed\n// before the event behind the request, so it queues a trailing sweep instead.\nfunction requestContainerRefresh(input: {\n freshListing: boolean;\n host: ReconciliationHost;\n state: ReconciliationState;\n refreshTree: () => Promise<void>;\n listContainerIds: () => ReadonlyArray<string>;\n scope: RefreshScope;\n}): Promise<void> {\n const { state, scope } = input;\n const pending = state.pendingRefresh;\n if (\n pending &&\n (scope === \"root\" || pending.scope === \"full\") &&\n !(input.freshListing && pending.started.value)\n ) {\n return pending.promise;\n }\n\n const started = { value: false };\n const promise = (pending?.promise ?? Promise.resolve())\n // A failed earlier sweep must not prevent the queued refresh.\n .catch(() => undefined)\n .then(() => {\n started.value = true;\n return refreshAndReconcileContainers({\n forceAllDocumentContentPulls: scope === \"full\",\n host: input.host,\n listContainerIds: input.listContainerIds,\n refreshTree: input.refreshTree,\n state,\n });\n })\n .finally(() => {\n if (state.pendingRefresh?.promise === promise) {\n state.pendingRefresh = null;\n }\n });\n state.pendingRefresh = { promise, scope, started };\n return promise;\n}\n\nfunction listFullRefreshContainerIds(\n host: ReconciliationHost,\n state: ReconciliationState,\n): ReadonlyArray<string> {\n const knownIds = host.listKnownContainerIds();\n const activeContainerId = state.activeContainerId;\n if (\n !activeContainerId ||\n knownIds.includes(activeContainerId) ||\n !host.canDiscoverContainerDocuments(activeContainerId)\n ) {\n return knownIds;\n }\n // Explicit refresh also retries an open foreign system container that the\n // background discovery list excludes.\n return [...knownIds, activeContainerId];\n}\n\nfunction startReconciliationLane(\n host: ReconciliationHost,\n state: ReconciliationState,\n): void {\n if (state.active) {\n return;\n }\n state.lifecycleGeneration += 1;\n state.active = true;\n state.lane = host.registerLane(\"reconciliation:documents\", {\n label: \"Device-first document reconciliation\",\n // Document discovery runs in the document phase, after structural\n // container hydration/metadata sync settles for the scope.\n phase: \"document\",\n priority: () => (state.activeContainerId ? -1 : 0),\n onUnexpectedError: (error) => {\n console.error(\"Device-first reconciliation failed:\", error);\n },\n // Fixed literal: no container or document id may reach a report. The\n // mapped stack identifies which lane body threw. Returning the host's\n // result hands a rejection to the lane reporter's wrapper; discarding it\n // would surface as an unhandled rejection instead.\n reportUnexpectedError: (error) =>\n host.logError?.(\"Reconciliation: sync lane failed\", error),\n run: () => runReconcileLane(host, state),\n shouldIgnoreError: host.isIgnorableError,\n });\n}\n\nfunction createReconciliationState(\n host: ReconciliationHost,\n): ReconciliationState {\n return {\n active: false,\n activeContainerId: null,\n automaticRetryGenerations: new Map(),\n discoveredContainerIds: new Set(),\n forcedContainerGenerations: new Map(),\n forcedDocumentIds: new Map(),\n initialDocumentProbe: createInitialDocumentProbe(host),\n lane: null,\n lifecycleGeneration: 0,\n nextForceGeneration: 0,\n probeContinuationCancel: null,\n queue: createReconcileQueue(),\n pendingRefresh: null,\n };\n}\n\nfunction stopReconciliationService(\n host: ReconciliationHost,\n state: ReconciliationState,\n): void {\n clearPendingDiscoveryRetries(state);\n state.active = false;\n state.probeContinuationCancel?.();\n state.probeContinuationCancel = null;\n state.queue.clear();\n state.automaticRetryGenerations.clear();\n state.forcedContainerGenerations.clear();\n state.forcedDocumentIds.clear();\n state.pendingRefresh = null;\n state.initialDocumentProbe.resetPending();\n // Drop the per-session discovered suppression cache too: a stopped\n // reconciler is being torn down (scope/identity change) or paused across\n // a prerequisite loss, after which every container must be re-validated.\n state.discoveredContainerIds.clear();\n // Drop pending self-echo originations too — they are session-scoped and a\n // teardown invalidates them.\n clearOriginatedDocuments(host.domainScope);\n}\n\nexport function createReconciliationService(\n host: ReconciliationHost,\n): ReconciliationService {\n const state = createReconciliationState(host);\n\n const scheduleDrain = () => {\n if (\n !state.active ||\n (state.queue.size === 0 && !state.initialDocumentProbe.canRun())\n ) {\n return;\n }\n if (!canReconcile(host.getRuntimeStatus())) {\n return;\n }\n state.probeContinuationCancel?.();\n state.probeContinuationCancel = null;\n state.lane?.requestSync();\n };\n\n const enqueueContainer = (\n containerId: string,\n priority: ReconcilePriority,\n force = false,\n documentId?: string,\n ) => {\n if (!containerId) {\n return;\n }\n // Skip containers already reconciled this session unless forced. Events\n // force re-discovery; passive active/backfill scheduling does not, so\n // opening Explorer does not re-fetch every container on each navigation.\n if (\n !force &&\n !state.forcedContainerGenerations.has(containerId) &&\n state.discoveredContainerIds.has(containerId)\n ) {\n return;\n }\n if (force) {\n markContainerForced(state, containerId, documentId);\n }\n state.queue.enqueue(containerId, priority);\n scheduleDrain();\n };\n\n const enqueueIdleBackfill = () => {\n enqueueKnownContainersForIdleBackfill({\n host,\n scheduleDrain,\n state,\n });\n };\n\n return {\n start: () => {\n startReconciliationLane(host, state);\n scheduleDrain();\n },\n // Reconcile the active container first; siblings remain lazy until visited.\n setActiveContainer: (containerId) =>\n activateContainer(state, containerId, (activeId) =>\n enqueueContainer(activeId, \"active\"),\n ),\n enqueueContainer,\n enqueueIdleBackfill,\n resetDiscovered: () => {\n state.discoveredContainerIds.clear();\n state.initialDocumentProbe.resetSkippedListings();\n },\n reconcileRootContainersNow: (options) =>\n requestContainerRefresh({\n freshListing: options?.freshListing === true,\n host,\n state,\n refreshTree: host.refreshRootTree,\n listContainerIds: host.listAutomaticRootCatchupContainerIds,\n scope: \"root\",\n }),\n reconcileNow: () =>\n requestContainerRefresh({\n freshListing: false,\n host,\n state,\n refreshTree: host.refreshTree,\n listContainerIds: () => listFullRefreshContainerIds(host, state),\n scope: \"full\",\n }),\n stop: () => stopReconciliationService(host, state),\n };\n}\n"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DocumentSummary } from "../../data/documents/documentSummary.js";
|
|
2
2
|
import type { DomainScope } from "../../data/domainScope.js";
|
|
3
|
+
import type { SyncLane, SyncLaneConfig } from "../../data/sync/syncLaneConfig.js";
|
|
3
4
|
import type { LocalProjectionReconciledDelta } from "../../stores/local-projection/index.js";
|
|
4
5
|
import type { InitialDocumentProbeHost } from "./initialDocumentProbe.js";
|
|
5
6
|
import type { ReconcilePriority } from "./queue.js";
|
|
@@ -27,10 +28,18 @@ export interface ReconciliationRuntimeStatus {
|
|
|
27
28
|
/**
|
|
28
29
|
* Host contract injected by the device-first facade. Keeps the reconciler
|
|
29
30
|
* React-free and decoupled from runtime/persistence construction — it only
|
|
30
|
-
* orchestrates *when* these run and routes their results into Layer A.
|
|
31
|
+
* orchestrates *when* these run and routes their results into Layer A. The
|
|
32
|
+
* host also supplies the coordinator lane and the clock, so the reconciler
|
|
33
|
+
* imports no runtime code from the client, store, workflow, or coordinator
|
|
34
|
+
* layers; `lint:architecture` enforces this.
|
|
31
35
|
*/
|
|
32
36
|
export interface ReconciliationHost extends InitialDocumentProbeHost {
|
|
33
37
|
domainScope: DomainScope;
|
|
38
|
+
/**
|
|
39
|
+
* Register the reconciler's lane on the scope's sync coordinator. The host
|
|
40
|
+
* owns the coordinator; the reconciler only schedules work on its lane.
|
|
41
|
+
*/
|
|
42
|
+
registerLane: (key: string, config: SyncLaneConfig) => SyncLane;
|
|
34
43
|
getRuntimeStatus: () => ReconciliationRuntimeStatus;
|
|
35
44
|
/** True when this container has enough remote state to list documents. */
|
|
36
45
|
canDiscoverContainerDocuments: (containerId: string) => boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serviceTypes.js","sourceRoot":"","sources":["serviceTypes.ts"],"names":[],"mappings":"","sourcesContent":["import type { DocumentSummary } from \"../../data/documents/documentSummary\";\nimport type { DomainScope } from \"../../data/domainScope\";\nimport type { LocalProjectionReconciledDelta } from \"../../stores/local-projection\";\nimport type { InitialDocumentProbeHost } from \"./initialDocumentProbe\";\nimport type { ReconcilePriority } from \"./queue\";\n\n/** One container a reconciliation pass lists, with its per-lane callbacks. */\nexport interface ContainerDiscoveryRequest {\n readonly containerId: string;\n readonly onFullListing?:\n | ((documentIds: ReadonlyArray<string>) => void)\n | undefined;\n readonly onPendingDiscovery?: ((delayMs: number) => void) | undefined;\n}\n\n/** A discovered lane: `null` means it was not fully listed or applied nothing. */\nexport type ContainerDiscoveryOutcome =\n | {\n readonly containerId: string;\n readonly status: \"fulfilled\";\n readonly value: ReadonlyArray<DocumentSummary> | null;\n }\n | {\n readonly containerId: string;\n readonly reason: unknown;\n readonly status: \"rejected\";\n };\n\nexport interface ReconciliationRuntimeStatus {\n dbStatus: string;\n isAuthenticated: boolean;\n online: boolean;\n}\n\n/**\n * Host contract injected by the device-first facade. Keeps the reconciler\n * React-free and decoupled from runtime/persistence construction — it only\n * orchestrates *when* these run and routes their results into Layer A.\n */\nexport interface ReconciliationHost extends InitialDocumentProbeHost {\n domainScope: DomainScope;\n getRuntimeStatus: () => ReconciliationRuntimeStatus;\n /** True when this container has enough remote state to list documents. */\n canDiscoverContainerDocuments: (containerId: string) => boolean;\n /** Known container ids eligible for idle document reconciliation. */\n listKnownContainerIds: () => ReadonlyArray<string>;\n /** Known ids eligible after an automatic root-lane discovery hint. */\n listAutomaticRootCatchupContainerIds: () => ReadonlyArray<string>;\n /**\n * Discover + persist documents for every requested container through one\n * batched listing. Each lane's outcome is handed to `onOutcome` (awaited)\n * as soon as it applies, before later lanes apply; every outcome is also\n * returned.\n */\n discoverContainerDocuments: (\n requests: ReadonlyArray<ContainerDiscoveryRequest>,\n onOutcome: (outcome: ContainerDiscoveryOutcome) => Promise<void>,\n ) => Promise<ReadonlyArray<ContainerDiscoveryOutcome>>;\n /** Read a container's freshly-persisted summaries+links from SQLite. */\n loadContainerDelta: (\n containerId: string,\n ) => Promise<LocalProjectionReconciledDelta>;\n /** Push a reconciled delta into the local projection store. */\n applyReconciled: (delta: LocalProjectionReconciledDelta) => void;\n /**\n * Sync document bodies for a reconciled container. `force` (an explicit\n * refresh) revalidates registered ordinary documents and retries system\n * documents; otherwise only unopened system documents are synced, so a\n * background pass can materialize projection-owned content without eagerly\n * opening every ordinary document. A documentIds set limits forced pulls to\n * hint-named documents while still discovering new system content.\n */\n requestDocumentContentPull: (\n containerId: string,\n documents: ReadonlyArray<DocumentSummary>,\n force: boolean,\n documentIds?: ReadonlySet<string>,\n ) => void;\n /** Refresh the container tree from the server (explicit refresh). */\n refreshTree: () => Promise<void>;\n /** Refresh only the top-level root lane from the server. */\n refreshRootTree: () => Promise<void>;\n /** True if the destroyed-db error should be swallowed rather than surfaced. */\n isIgnorableError: (error: unknown) => boolean;\n /** Host diagnostics sink; bare runtimes do not structure errors. */\n logError?: ((message: string, error: unknown) => void) | undefined;\n}\n\nexport interface ReconciliationService {\n start: () => void;\n setActiveContainer: (containerId: string | null) => void;\n /** Force a listing; documentId limits content revalidation to that hint. */\n enqueueContainer: (\n containerId: string,\n priority: ReconcilePriority,\n force?: boolean,\n documentId?: string,\n ) => void;\n /** Queue known containers at idle priority. */\n enqueueIdleBackfill: () => void;\n /**\n * Forget which containers were reconciled this session so the next enqueue of\n * each re-validates against the server exactly once. Call on the\n * cannot-reconcile → can-reconcile edge (relogin/reconnect): the discovered\n * set is a per-session suppression cache, a previously-visited container may\n * have changed remotely, and initial-probe listings that exhausted their\n * retries need a fresh opportunity once the server is reachable again.\n */\n resetDiscovered: () => void;\n /**\n * Re-list root containers and reconcile the root catch-up set. By default a\n * request joins a sweep already in flight. With `freshListing`, the returned\n * sweep starts after this call: a discovery event (a root shared with or\n * created for this user) must not be satisfied by a sweep that may have\n * listed roots before the event committed.\n */\n reconcileRootContainersNow: (options?: {\n readonly freshListing?: boolean;\n }) => Promise<void>;\n reconcileNow: () => Promise<void>;\n stop: () => void;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"serviceTypes.js","sourceRoot":"","sources":["serviceTypes.ts"],"names":[],"mappings":"","sourcesContent":["import type { DocumentSummary } from \"../../data/documents/documentSummary\";\nimport type { DomainScope } from \"../../data/domainScope\";\nimport type { SyncLane, SyncLaneConfig } from \"../../data/sync/syncLaneConfig\";\nimport type { LocalProjectionReconciledDelta } from \"../../stores/local-projection\";\nimport type { InitialDocumentProbeHost } from \"./initialDocumentProbe\";\nimport type { ReconcilePriority } from \"./queue\";\n\n/** One container a reconciliation pass lists, with its per-lane callbacks. */\nexport interface ContainerDiscoveryRequest {\n readonly containerId: string;\n readonly onFullListing?:\n | ((documentIds: ReadonlyArray<string>) => void)\n | undefined;\n readonly onPendingDiscovery?: ((delayMs: number) => void) | undefined;\n}\n\n/** A discovered lane: `null` means it was not fully listed or applied nothing. */\nexport type ContainerDiscoveryOutcome =\n | {\n readonly containerId: string;\n readonly status: \"fulfilled\";\n readonly value: ReadonlyArray<DocumentSummary> | null;\n }\n | {\n readonly containerId: string;\n readonly reason: unknown;\n readonly status: \"rejected\";\n };\n\nexport interface ReconciliationRuntimeStatus {\n dbStatus: string;\n isAuthenticated: boolean;\n online: boolean;\n}\n\n/**\n * Host contract injected by the device-first facade. Keeps the reconciler\n * React-free and decoupled from runtime/persistence construction — it only\n * orchestrates *when* these run and routes their results into Layer A. The\n * host also supplies the coordinator lane and the clock, so the reconciler\n * imports no runtime code from the client, store, workflow, or coordinator\n * layers; `lint:architecture` enforces this.\n */\nexport interface ReconciliationHost extends InitialDocumentProbeHost {\n domainScope: DomainScope;\n /**\n * Register the reconciler's lane on the scope's sync coordinator. The host\n * owns the coordinator; the reconciler only schedules work on its lane.\n */\n registerLane: (key: string, config: SyncLaneConfig) => SyncLane;\n getRuntimeStatus: () => ReconciliationRuntimeStatus;\n /** True when this container has enough remote state to list documents. */\n canDiscoverContainerDocuments: (containerId: string) => boolean;\n /** Known container ids eligible for idle document reconciliation. */\n listKnownContainerIds: () => ReadonlyArray<string>;\n /** Known ids eligible after an automatic root-lane discovery hint. */\n listAutomaticRootCatchupContainerIds: () => ReadonlyArray<string>;\n /**\n * Discover + persist documents for every requested container through one\n * batched listing. Each lane's outcome is handed to `onOutcome` (awaited)\n * as soon as it applies, before later lanes apply; every outcome is also\n * returned.\n */\n discoverContainerDocuments: (\n requests: ReadonlyArray<ContainerDiscoveryRequest>,\n onOutcome: (outcome: ContainerDiscoveryOutcome) => Promise<void>,\n ) => Promise<ReadonlyArray<ContainerDiscoveryOutcome>>;\n /** Read a container's freshly-persisted summaries+links from SQLite. */\n loadContainerDelta: (\n containerId: string,\n ) => Promise<LocalProjectionReconciledDelta>;\n /** Push a reconciled delta into the local projection store. */\n applyReconciled: (delta: LocalProjectionReconciledDelta) => void;\n /**\n * Sync document bodies for a reconciled container. `force` (an explicit\n * refresh) revalidates registered ordinary documents and retries system\n * documents; otherwise only unopened system documents are synced, so a\n * background pass can materialize projection-owned content without eagerly\n * opening every ordinary document. A documentIds set limits forced pulls to\n * hint-named documents while still discovering new system content.\n */\n requestDocumentContentPull: (\n containerId: string,\n documents: ReadonlyArray<DocumentSummary>,\n force: boolean,\n documentIds?: ReadonlySet<string>,\n ) => void;\n /** Refresh the container tree from the server (explicit refresh). */\n refreshTree: () => Promise<void>;\n /** Refresh only the top-level root lane from the server. */\n refreshRootTree: () => Promise<void>;\n /** True if the destroyed-db error should be swallowed rather than surfaced. */\n isIgnorableError: (error: unknown) => boolean;\n /** Host diagnostics sink; bare runtimes do not structure errors. */\n logError?: ((message: string, error: unknown) => void) | undefined;\n}\n\nexport interface ReconciliationService {\n start: () => void;\n setActiveContainer: (containerId: string | null) => void;\n /** Force a listing; documentId limits content revalidation to that hint. */\n enqueueContainer: (\n containerId: string,\n priority: ReconcilePriority,\n force?: boolean,\n documentId?: string,\n ) => void;\n /** Queue known containers at idle priority. */\n enqueueIdleBackfill: () => void;\n /**\n * Forget which containers were reconciled this session so the next enqueue of\n * each re-validates against the server exactly once. Call on the\n * cannot-reconcile → can-reconcile edge (relogin/reconnect): the discovered\n * set is a per-session suppression cache, a previously-visited container may\n * have changed remotely, and initial-probe listings that exhausted their\n * retries need a fresh opportunity once the server is reachable again.\n */\n resetDiscovered: () => void;\n /**\n * Re-list root containers and reconcile the root catch-up set. By default a\n * request joins a sweep already in flight. With `freshListing`, the returned\n * sweep starts after this call: a discovery event (a root shared with or\n * created for this user) must not be satisfied by a sweep that may have\n * listed roots before the event committed.\n */\n reconcileRootContainersNow: (options?: {\n readonly freshListing?: boolean;\n }) => Promise<void>;\n reconcileNow: () => Promise<void>;\n stop: () => void;\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isDocumentMutationCreatedEvent, isDocumentUpdateCreatedEvent, } from "../../data/documents/documentSync.js";
|
|
2
|
-
import { consumeOriginatedDocument } from "
|
|
2
|
+
import { consumeOriginatedDocument } from "../../data/sync/originatedDocuments.js";
|
|
3
3
|
function enqueueKnownEventContainers(input) {
|
|
4
4
|
for (const containerId of input.containerIds) {
|
|
5
5
|
if (input.knownContainerIds.has(containerId)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"triggers.js","sourceRoot":"","sources":["triggers.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,8BAA8B,EAC9B,4BAA4B,GAC7B,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"triggers.js","sourceRoot":"","sources":["triggers.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n isDocumentMutationCreatedEvent,\n isDocumentUpdateCreatedEvent,\n} from \"../../data/documents/documentSync\";\nimport type { DomainScope } from \"../../data/domainScope\";\nimport { consumeOriginatedDocument } from \"../../data/sync/originatedDocuments\";\nimport type { LocalProjectionStore } from \"../../stores/local-projection\";\nimport type { ReconciliationService } from \"./serviceTypes\";\n\nfunction enqueueKnownEventContainers(input: {\n readonly containerIds: ReadonlyArray<string>;\n readonly documentId: string;\n readonly knownContainerIds: ReadonlySet<string>;\n readonly service: ReconciliationService;\n}): void {\n for (const containerId of input.containerIds) {\n if (input.knownContainerIds.has(containerId)) {\n input.service.enqueueContainer(\n containerId,\n \"active\",\n true,\n input.documentId,\n );\n }\n }\n}\n\n/**\n * Wire the local projection store's reconcile signals into the background\n * reconciler. Returns an unsubscribe handle. This is the only place Layer A and\n * Layer B are connected: signals in, scheduling out — no React involved.\n */\nexport function connectReconciliationTriggers(input: {\n store: LocalProjectionStore;\n service: ReconciliationService;\n}): () => void {\n const { service, store } = input;\n\n return store.onReconcileSignal((signal) => {\n switch (signal.reason) {\n case \"active-changed\":\n service.setActiveContainer(signal.activeContainerId);\n break;\n case \"hydrated\":\n if (signal.activeContainerId) {\n service.enqueueContainer(signal.activeContainerId, \"active\");\n }\n break;\n case \"remote-containers-added\":\n // A cold-cache tree crawl can finish after the auth-edge backfill was\n // queued. Pick up the newly materialized ids without resetting the\n // discovered suppression set for containers already reconciled.\n service.enqueueIdleBackfill();\n break;\n case \"prerequisites-regained\":\n // Auth/connectivity was just regained (relogin or reconnect). Forget the\n // per-session discovered set first: containers visited before the gap\n // may have changed remotely while this client was offline, and the\n // enqueue calls below are otherwise suppressed for already-discovered\n // containers. Resetting makes the active container and every backfill\n // target re-validate against the server exactly once.\n service.resetDiscovered();\n if (signal.activeContainerId) {\n service.enqueueContainer(signal.activeContainerId, \"active\");\n }\n service.enqueueIdleBackfill();\n break;\n }\n });\n}\n\n/**\n * Translate a batch of server events into reconciler work. Containers named by\n * document content or structural mutation events are reconciled at active\n * priority; content pulls target only the event's document. Document updates\n * must carry the current container-scoped hint shape.\n *\n * `domainScope`, when provided, suppresses self-echoes: a `document_update_created`\n * whose documentId this client just originated (created/uploaded) is dropped\n * once, since reconciling it would only re-fetch a delta the client already has.\n * See {@link consumeOriginatedDocument}.\n */\nexport function enqueueReconciliationForEvents(input: {\n domainScope?: DomainScope;\n events: ReadonlyArray<unknown>;\n knownContainerIds: ReadonlyArray<string>;\n service: ReconciliationService;\n}): void {\n const { domainScope, events, knownContainerIds, service } = input;\n const knownContainerIdSet = new Set(knownContainerIds);\n\n for (const event of events) {\n if (isDocumentMutationCreatedEvent(event)) {\n // A link-set mutation changes membership even when the document is\n // already present locally. Never apply content-origination self-echo\n // suppression here: the server excludes only the exact authoring session,\n // while another session for the same identity must reconcile both sides.\n enqueueKnownEventContainers({\n containerIds: event.containerIds,\n documentId: event.documentId,\n knownContainerIds: knownContainerIdSet,\n service,\n });\n continue;\n }\n if (!isDocumentUpdateCreatedEvent(event)) {\n continue;\n }\n // Self-echo of this client's own create/upload: consume the origination and\n // skip. Single-use, so a genuine later remote change still reconciles.\n if (\n domainScope !== undefined &&\n consumeOriginatedDocument(domainScope, event.documentId)\n ) {\n continue;\n }\n // Events signal fresh remote data, so force re-discovery even if this\n // container was already reconciled this session.\n enqueueKnownEventContainers({\n containerIds: event.containerIds,\n documentId: event.documentId,\n knownContainerIds: knownContainerIdSet,\n service,\n });\n }\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { createDomainScope, type DomainScope, } from "../../data/domainScope.js";
|
|
2
|
+
export { markOriginatedDocuments } from "../../data/sync/originatedDocuments.js";
|
|
2
3
|
export type { DomainSyncCoordinator, DomainSyncSnapshot, SyncIdleOptions, SyncLane, SyncLaneConfig, SyncLaneLastAction, SyncLanePhase, SyncLaneProgress, SyncLaneSnapshot, SyncLaneStatus, UploadSyncLane, UploadSyncLaneOptions, } from "../../data/sync/syncCoordinator.js";
|
|
3
4
|
export { beginDomainSyncUploadLane, getDomainSyncCoordinatorSnapshot, getOrCreateDomainSyncCoordinator, hasDomainSyncCoordinatorPendingWork, requestAllDomainSyncLanes, subscribeToDomainSyncCoordinator, waitForDomainSyncCoordinatorToSettle, } from "../../data/sync/syncCoordinator.js";
|
|
4
5
|
export { type ClearRemoteSyncStateResult, clearRemoteSyncState, type RemoteResetInput, type RemoteResetReplacement, } from "./remoteReset.js";
|
package/workflows/sync/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { createDomainScope, } from "../../data/domainScope.js";
|
|
2
|
+
export { markOriginatedDocuments } from "../../data/sync/originatedDocuments.js";
|
|
2
3
|
export { beginDomainSyncUploadLane, getDomainSyncCoordinatorSnapshot, getOrCreateDomainSyncCoordinator, hasDomainSyncCoordinatorPendingWork, requestAllDomainSyncLanes, subscribeToDomainSyncCoordinator, waitForDomainSyncCoordinatorToSettle, } from "../../data/sync/syncCoordinator.js";
|
|
3
4
|
export { clearRemoteSyncState, } from "./remoteReset.js";
|
|
4
5
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,GAElB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,GAElB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,uBAAuB,EAAE,MAAM,qCAAqC,CAAC;AAe9E,OAAO,EACL,yBAAyB,EACzB,gCAAgC,EAChC,gCAAgC,EAChC,mCAAmC,EACnC,yBAAyB,EACzB,gCAAgC,EAChC,oCAAoC,GACrC,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAEL,oBAAoB,GAGrB,MAAM,eAAe,CAAC","sourcesContent":["export {\n createDomainScope,\n type DomainScope,\n} from \"../../data/domainScope\";\nexport { markOriginatedDocuments } from \"../../data/sync/originatedDocuments\";\nexport type {\n DomainSyncCoordinator,\n DomainSyncSnapshot,\n SyncIdleOptions,\n SyncLane,\n SyncLaneConfig,\n SyncLaneLastAction,\n SyncLanePhase,\n SyncLaneProgress,\n SyncLaneSnapshot,\n SyncLaneStatus,\n UploadSyncLane,\n UploadSyncLaneOptions,\n} from \"../../data/sync/syncCoordinator\";\nexport {\n beginDomainSyncUploadLane,\n getDomainSyncCoordinatorSnapshot,\n getOrCreateDomainSyncCoordinator,\n hasDomainSyncCoordinatorPendingWork,\n requestAllDomainSyncLanes,\n subscribeToDomainSyncCoordinator,\n waitForDomainSyncCoordinatorToSettle,\n} from \"../../data/sync/syncCoordinator\";\nexport {\n type ClearRemoteSyncStateResult,\n clearRemoteSyncState,\n type RemoteResetInput,\n type RemoteResetReplacement,\n} from \"./remoteReset\";\n"]}
|
package/sqlite.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { createDatabaseRuntime, } from "./internal/sqlite-worker/runtime.js";
|
|
2
|
-
export { createModuleDatabaseRuntime as createSQLiteRuntime } from "./internal/sqlite-worker/runtime.js";
|
|
3
|
-
export function createSQLiteRuntimeFromWorker(worker) {
|
|
4
|
-
return createDatabaseRuntime(worker);
|
|
5
|
-
}
|
|
6
|
-
export { purgeOpfsSqliteDatabase } from "./internal/sqlite-worker/purgeOpfsDatabase.js";
|
|
7
|
-
export { clientSQLiteSchema } from "./data/sqlite/schema.js";
|
|
8
|
-
export { getClientSQLitePersistenceRuntime as getSQLitePersistenceRuntime, } from "./data/sqlite/sqlitePersistenceRuntime.js";
|
|
9
|
-
export { createExecSql, defineSqlTableSchema, ensureSqlTables, resetConnectionSchemaMemo, runSerializedSqlMutation, unavailableExecSql, } from "./data/sqlite/sqlSchema.js";
|
|
10
|
-
export { EPHEMERAL_STORAGE_POLICY, isPersistentStorageSupported, PERSISTENT_STORAGE_POLICY, requestPersistentStorage, resolveStoragePersistencePolicy, } from "./data/sqlite/storagePersistence.js";
|
|
11
|
-
//# sourceMappingURL=sqlite.js.map
|
package/sqlite.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["sqlite.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,qBAAqB,GAItB,MAAM,kCAAkC,CAAC;AAW1C,OAAO,EAAE,2BAA2B,IAAI,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAEtG,MAAM,UAAU,6BAA6B,CAC3C,MAA2B;IAE3B,OAAO,qBAAqB,CAAC,MAAM,CAAC,CAAC;AACvC,CAAC;AAED,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AAEvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAIL,iCAAiC,IAAI,2BAA2B,GACjE,MAAM,wCAAwC,CAAC;AAChD,OAAO,EACL,aAAa,EACb,oBAAoB,EAGpB,eAAe,EACf,yBAAyB,EACzB,wBAAwB,EAOxB,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,wBAAwB,EACxB,4BAA4B,EAC5B,yBAAyB,EAEzB,wBAAwB,EACxB,+BAA+B,GAEhC,MAAM,kCAAkC,CAAC","sourcesContent":["import type {\n DatabaseWorkerClient,\n WorkerLike,\n} from \"@tearleads/sqlite-worker/client\";\nimport {\n type CreateModuleDatabaseRuntimeOptions,\n createDatabaseRuntime,\n type DatabaseRuntime,\n type ModuleWorkerConstructor,\n type ModuleWorkerLike,\n} from \"@tearleads/sqlite-worker/runtime\";\n\nexport type SQLiteWorkerClient = DatabaseWorkerClient;\nexport type CreateSQLiteRuntimeOptions = CreateModuleDatabaseRuntimeOptions;\nexport type SQLiteRuntime = DatabaseRuntime;\nexport type SQLiteModuleWorkerConstructor = ModuleWorkerConstructor;\nexport type SQLiteModuleWorkerLike = ModuleWorkerLike;\nexport interface SQLiteRuntimeWorker extends WorkerLike {\n terminate(): void;\n}\n\nexport { createModuleDatabaseRuntime as createSQLiteRuntime } from \"@tearleads/sqlite-worker/runtime\";\n\nexport function createSQLiteRuntimeFromWorker(\n worker: SQLiteRuntimeWorker,\n): SQLiteRuntime {\n return createDatabaseRuntime(worker);\n}\n\nexport { purgeOpfsSqliteDatabase } from \"@tearleads/sqlite-worker/purge-opfs-database\";\nexport type { DatabasePersistenceMode } from \"@tearleads/sqlite-worker/types\";\nexport { clientSQLiteSchema } from \"./data/sqlite/schema\";\nexport {\n type ClientSQLiteDatabase as SQLiteDatabase,\n type ClientSQLitePersistenceRuntime as SQLitePersistenceRuntime,\n type ClientSQLiteTransactionScope as SQLiteTransaction,\n getClientSQLitePersistenceRuntime as getSQLitePersistenceRuntime,\n} from \"./data/sqlite/sqlitePersistenceRuntime\";\nexport {\n createExecSql,\n defineSqlTableSchema,\n type ExecSql,\n type ExecSqlClientLike,\n ensureSqlTables,\n resetConnectionSchemaMemo,\n runSerializedSqlMutation,\n type SqlArrayRow,\n type SqlBind,\n type SqlRow,\n type SqlRowMode,\n type SqlRowValue,\n type SqlTableSchema,\n unavailableExecSql,\n} from \"./data/sqlite/sqlSchema\";\nexport {\n EPHEMERAL_STORAGE_POLICY,\n isPersistentStorageSupported,\n PERSISTENT_STORAGE_POLICY,\n type RequestPersistentStorageResult,\n requestPersistentStorage,\n resolveStoragePersistencePolicy,\n type StoragePersistencePolicy,\n} from \"./data/sqlite/storagePersistence\";\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"documentContentPull.js","sourceRoot":"","sources":["documentContentPull.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAgBxE,SAAS,mBAAmB,CAC1B,KAAc,EACd,WAA4C,EAC5C,UAAkB;IAElB,OAAO,KAAK,IAAI,CAAC,CAAC,WAAW,IAAI,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;AAChE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qCAAqC,CACnD,IAAuC;IAEvC,4EAA4E;IAC5E,4EAA4E;IAC5E,kDAAkD;IAClD,MAAM,4BAA4B,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE/D,OAAO,CAAC,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE;QACpD,MAAM,eAAe,GAAG,qBAAqB,CAC3C,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CACvD,CAAC;QAEF,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;gBACzB,SAAS;YACX,CAAC;YAED,MAAM,aAAa,GAAG,mBAAmB,CACvC,KAAK,EACL,WAAW,EACX,QAAQ,CAAC,UAAU,CACpB,CAAC;YAEF,IAAI,eAAe,EAAE,CAAC;gBACpB,uEAAuE;gBACvE,uEAAuE;gBACvE,oCAAoC;gBACpC,MAAM,WAAW,GAAG,GAAG,WAAW,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;gBAC5D,IACE,4BAA4B,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,QAAQ,CAAC,SAAS,EACpE,CAAC;oBACD,IAAI,CAAC,mBAAmB,CAAC;wBACvB,WAAW;wBACX,UAAU,EAAE,QAAQ,CAAC,UAAU;wBAC/B,OAAO,EAAE,QAAQ,CAAC,EAAE;qBACrB,CAAC,CAAC;oBACH,4BAA4B,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;gBACpE,CAAC;qBAAM,IAAI,aAAa,EAAE,CAAC;oBACzB,oEAAoE;oBACpE,qEAAqE;oBACrE,oEAAoE;oBACpE,qBAAqB;oBACrB,IAAI,CAAC,mCAAmC,CACtC,QAAQ,CAAC,EAAE,EACX,QAAQ,CAAC,UAAU,CACpB,CAAC;gBACJ,CAAC;gBACD,SAAS;YACX,CAAC;YAED,IAAI,aAAa,EAAE,CAAC;gBAClB,IAAI,CAAC,mCAAmC,CACtC,QAAQ,CAAC,EAAE,EACX,QAAQ,CAAC,UAAU,CACpB,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import type { ContainerNode } from \"../../stores/container-contents\";\nimport { isSystemContainerNode } from \"../../stores/container-contents\";\nimport type { ReconciliationHost } from \"./serviceTypes\";\n\ninterface ReconciledDocumentContentPullHost {\n readonly getContainer: (containerId: string) => ContainerNode | null;\n readonly pullDocumentContent: (input: {\n readonly containerId: string;\n readonly documentId: string;\n readonly localId: string;\n }) => void;\n readonly requestRegisteredDocumentRemoteSync: (\n localId: string,\n documentId: string,\n ) => void;\n}\n\nfunction shouldForceDocument(\n force: boolean,\n documentIds: ReadonlySet<string> | undefined,\n documentId: string,\n): boolean {\n return force && (!documentIds || documentIds.has(documentId));\n}\n\n/**\n * Routes reconciled summaries into the appropriate content-sync policy.\n *\n * System-container contents cannot rely on a mini-app opening them on this\n * device, especially during identity recovery. Pull each remote version once.\n * Ordinary documents stay lazy unless an explicit refresh asks an already-open\n * store to revalidate.\n */\nexport function createReconciledDocumentContentPuller(\n host: ReconciledDocumentContentPullHost,\n): ReconciliationHost[\"requestDocumentContentPull\"] {\n // Retain only the last observed version for each system-container document.\n // Keeping the version in the key would grow this cache for every historical\n // updatedAt value over the reconciler's lifetime.\n const pulledSystemDocumentVersions = new Map<string, string>();\n\n return (containerId, documents, force, documentIds) => {\n const systemContainer = isSystemContainerNode(\n host.getContainer(containerId) ?? { systemSlot: null },\n );\n\n for (const document of documents) {\n if (!document.documentId) {\n continue;\n }\n\n const forceDocument = shouldForceDocument(\n force,\n documentIds,\n document.documentId,\n );\n\n if (systemContainer) {\n // Container scope is significant: the same document can be linked into\n // multiple system containers whose keys establish different decryption\n // contexts for the transient store.\n const documentKey = `${containerId}:${document.documentId}`;\n if (\n pulledSystemDocumentVersions.get(documentKey) !== document.updatedAt\n ) {\n host.pullDocumentContent({\n containerId,\n documentId: document.documentId,\n localId: document.id,\n });\n pulledSystemDocumentVersions.set(documentKey, document.updatedAt);\n } else if (forceDocument) {\n // The first eager pull registers a document store before scheduling\n // its async sync. If that sync failed, a later explicit Refresh must\n // be able to retry the same server version without defeating normal\n // background dedupe.\n host.requestRegisteredDocumentRemoteSync(\n document.id,\n document.documentId,\n );\n }\n continue;\n }\n\n if (forceDocument) {\n host.requestRegisteredDocumentRemoteSync(\n document.id,\n document.documentId,\n );\n }\n }\n };\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"originatedDocuments.js","sourceRoot":"","sources":["originatedDocuments.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AACH,MAAM,4BAA4B,GAAG,IAAI,OAAO,EAA4B,CAAC;AAE7E,SAAS,wBAAwB,CAAC,KAAkB;IAClD,MAAM,QAAQ,GAAG,4BAA4B,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACzD,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,4BAA4B,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACjD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,uBAAuB,CACrC,KAAkB,EAClB,WAA6B;IAE7B,MAAM,GAAG,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;IAC5C,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,IAAI,UAAU,EAAE,CAAC;YACf,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CACvC,KAAkB,EAClB,UAAkB;IAElB,OAAO,4BAA4B,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC;AAC9E,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,wBAAwB,CAAC,KAAkB;IACzD,6EAA6E;IAC7E,uDAAuD;IACvD,4BAA4B,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC7C,CAAC","sourcesContent":["import type { DomainScope } from \"../../data/domainScope\";\n\n/**\n * Per-scope set of remote document ids this client just originated (created or\n * uploaded). The server echoes a `document_update_created` for the client's own\n * writes; reconciling on that echo is pure waste because the client already has\n * the document locally. The local-projection link index cannot be relied on to\n * suppress it — that cache is refreshed by navigation/hydration/reconcile, not\n * by an upload completing, so a fast echo races ahead of it (see the residual\n * documented on `takePendingReconciliationEvents`). Recording the id at the\n * moment it is bound closes that race independently of cache timing.\n *\n * Entries are single-use: the first matching echo consumes them, so a genuine\n * later remote change to the same document still reconciles. The set is scoped\n * per `DomainScope` and cleared when the reconciler for that scope stops (scope\n * or identity teardown), so ids never leak across sessions.\n */\nconst originatedDocumentIdsByScope = new WeakMap<DomainScope, Set<string>>();\n\nfunction getOrCreateOriginatedSet(scope: DomainScope): Set<string> {\n const existing = originatedDocumentIdsByScope.get(scope);\n if (existing) {\n return existing;\n }\n const created = new Set<string>();\n originatedDocumentIdsByScope.set(scope, created);\n return created;\n}\n\n/** Record remote document ids this client just created/uploaded in `scope`. */\nexport function markOriginatedDocuments(\n scope: DomainScope,\n documentIds: Iterable<string>,\n): void {\n const set = getOrCreateOriginatedSet(scope);\n for (const documentId of documentIds) {\n if (documentId) {\n set.add(documentId);\n }\n }\n}\n\n/**\n * Consume an originated id: returns true (and removes it) when `documentId` was\n * an echo of this client's own write, false otherwise. Single-use so only the\n * first echo is suppressed.\n */\nexport function consumeOriginatedDocument(\n scope: DomainScope,\n documentId: string,\n): boolean {\n return originatedDocumentIdsByScope.get(scope)?.delete(documentId) ?? false;\n}\n\n/** Drop all originated ids for a scope (reconciler stop / identity teardown). */\nexport function clearOriginatedDocuments(scope: DomainScope): void {\n // Delete the whole entry so the Set can be collected immediately rather than\n // lingering empty for the lifetime of the DomainScope.\n originatedDocumentIdsByScope.delete(scope);\n}\n"]}
|
|
File without changes
|