@tearleads/client-sdk 0.1.43 → 0.1.44

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.
Files changed (82) hide show
  1. package/client/containerDocumentDiscovery.js +71 -29
  2. package/client/containerDocumentDiscovery.js.map +1 -1
  3. package/client/deviceFirst.js +4 -5
  4. package/client/deviceFirst.js.map +1 -1
  5. package/data/persistence/containers/containerSyncWatermarkPersistence.d.ts +11 -3
  6. package/data/persistence/containers/containerSyncWatermarkPersistence.js +46 -58
  7. package/data/persistence/containers/containerSyncWatermarkPersistence.js.map +1 -1
  8. package/data/sqlite/schema.d.ts +34 -134
  9. package/data/sqlite/schema.js +22 -35
  10. package/data/sqlite/schema.js.map +1 -1
  11. package/internal/api-client/ApiClient.d.ts +6 -34
  12. package/internal/api-client/ApiClient.js +8 -10
  13. package/internal/api-client/ApiClient.js.map +1 -1
  14. package/internal/api-client/apiRequestRuntime.d.ts +2 -0
  15. package/internal/api-client/apiRequestRuntime.js +4 -0
  16. package/internal/api-client/apiRequestRuntime.js.map +1 -1
  17. package/internal/api-client/requestInternals.js +7 -7
  18. package/internal/api-client/requestInternals.js.map +1 -1
  19. package/internal/api-client/routes/containers/reads.d.ts +13 -7
  20. package/internal/api-client/routes/containers/reads.js +36 -15
  21. package/internal/api-client/routes/containers/reads.js.map +1 -1
  22. package/internal/api-client/types.d.ts +1 -5
  23. package/internal/api-client/types.js.map +1 -1
  24. package/internal/validators/containerReadRefinements.js +15 -5
  25. package/internal/validators/containerReadRefinements.js.map +1 -1
  26. package/internal/validators/containerReadSchemas.d.ts +36 -0
  27. package/internal/validators/containerReadSchemas.js +64 -1
  28. package/internal/validators/containerReadSchemas.js.map +1 -1
  29. package/internal/validators/operation/containerReads.d.ts +118 -182
  30. package/internal/validators/operation/containerReads.js +20 -54
  31. package/internal/validators/operation/containerReads.js.map +1 -1
  32. package/internal/validators/operation/index.d.ts +1 -1
  33. package/internal/validators/operation/index.js +1 -1
  34. package/internal/validators/operation/index.js.map +1 -1
  35. package/internal/validators/operation/registry.d.ts +2 -2
  36. package/internal/validators/operation/registry.js +2 -2
  37. package/internal/validators/operation/registry.js.map +1 -1
  38. package/internal/validators/request/containerDocumentLanes.d.ts +23 -0
  39. package/internal/validators/request/containerDocumentLanes.js +23 -0
  40. package/internal/validators/request/containerDocumentLanes.js.map +1 -0
  41. package/internal/validators/request/containerParentLanes.js +5 -35
  42. package/internal/validators/request/containerParentLanes.js.map +1 -1
  43. package/internal/validators/request/index.d.ts +1 -0
  44. package/internal/validators/request/index.js +1 -0
  45. package/internal/validators/request/index.js.map +1 -1
  46. package/internal/validators/response/containerDocumentLanes.d.ts +82 -0
  47. package/internal/validators/response/containerDocumentLanes.js +35 -0
  48. package/internal/validators/response/containerDocumentLanes.js.map +1 -0
  49. package/internal/validators/response/containerParentLanes.js +3 -15
  50. package/internal/validators/response/containerParentLanes.js.map +1 -1
  51. package/internal/validators/response/index.d.ts +2 -1
  52. package/internal/validators/response/index.js +1 -1
  53. package/internal/validators/response/index.js.map +1 -1
  54. package/package.json +1 -1
  55. package/sync/reconciliation/knownContainerSweep.js +57 -54
  56. package/sync/reconciliation/knownContainerSweep.js.map +1 -1
  57. package/sync/reconciliation/reconcileContainer.js +49 -17
  58. package/sync/reconciliation/reconcileContainer.js.map +1 -1
  59. package/sync/reconciliation/service.js +44 -16
  60. package/sync/reconciliation/service.js.map +1 -1
  61. package/sync/reconciliation/serviceTypes.d.ts +23 -2
  62. package/sync/reconciliation/serviceTypes.js.map +1 -1
  63. package/workflows/container-contents/containerDocumentListing.js +92 -38
  64. package/workflows/container-contents/containerDocumentListing.js.map +1 -1
  65. package/workflows/container-contents/containerPersistence.d.ts +8 -2
  66. package/workflows/container-contents/containerPersistence.js +6 -7
  67. package/workflows/container-contents/containerPersistence.js.map +1 -1
  68. package/workflows/container-contents/documentDiscovery.d.ts +3 -3
  69. package/workflows/container-contents/documentDiscovery.js +24 -96
  70. package/workflows/container-contents/documentDiscovery.js.map +1 -1
  71. package/workflows/container-contents/documentDiscoveryApply.js +65 -0
  72. package/workflows/container-contents/documentDiscoveryApply.js.map +1 -0
  73. package/workflows/container-contents/documentDiscoveryLanes.js +53 -0
  74. package/workflows/container-contents/documentDiscoveryLanes.js.map +1 -0
  75. package/workflows/container-contents/documentDiscoveryTypes.d.ts +25 -19
  76. package/workflows/container-contents/documentDiscoveryTypes.js.map +1 -1
  77. package/workflows/container-contents/remoteHydration/laneFetchMarkers.js +11 -17
  78. package/workflows/container-contents/remoteHydration/laneFetchMarkers.js.map +1 -1
  79. package/workflows/sync/remoteResetCursorScope.js +10 -18
  80. package/workflows/sync/remoteResetCursorScope.js.map +1 -1
  81. package/workflows/sync/remoteResetScope.js +4 -11
  82. package/workflows/sync/remoteResetScope.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","sourceRoot":"","sources":["service.ts"],"names":[],"mappings":"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","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 reconcileMarkedContainer,\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\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 containerId = state.queue.dequeue();\n if (!containerId) {\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 later.\n const forceGeneration = state.forcedContainerGenerations.get(containerId);\n const shouldForce = forceGeneration !== undefined;\n if (shouldForce || !state.discoveredContainerIds.has(containerId)) {\n state.discoveredContainerIds.add(containerId);\n try {\n const reconciled = await reconcileMarkedContainer(\n host,\n state,\n containerId,\n shouldForce,\n );\n if (reconciled) {\n acknowledgeContainerForce(state, containerId, forceGeneration);\n }\n } catch (error) {\n rearmFailedContainer(\n state,\n containerId,\n forceGeneration,\n lifecycleGeneration,\n );\n throw error;\n }\n }\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 {\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"]}
@@ -3,6 +3,22 @@ import type { DomainScope } from "../../data/domainScope.js";
3
3
  import type { LocalProjectionReconciledDelta } from "../../stores/local-projection/index.js";
4
4
  import type { InitialDocumentProbeHost } from "./initialDocumentProbe.js";
5
5
  import type { ReconcilePriority } from "./queue.js";
6
+ /** One container a reconciliation pass lists, with its per-lane callbacks. */
7
+ export interface ContainerDiscoveryRequest {
8
+ readonly containerId: string;
9
+ readonly onFullListing?: ((documentIds: ReadonlyArray<string>) => void) | undefined;
10
+ readonly onPendingDiscovery?: ((delayMs: number) => void) | undefined;
11
+ }
12
+ /** A discovered lane: `null` means it was not fully listed or applied nothing. */
13
+ export type ContainerDiscoveryOutcome = {
14
+ readonly containerId: string;
15
+ readonly status: "fulfilled";
16
+ readonly value: ReadonlyArray<DocumentSummary> | null;
17
+ } | {
18
+ readonly containerId: string;
19
+ readonly reason: unknown;
20
+ readonly status: "rejected";
21
+ };
6
22
  export interface ReconciliationRuntimeStatus {
7
23
  dbStatus: string;
8
24
  isAuthenticated: boolean;
@@ -22,8 +38,13 @@ export interface ReconciliationHost extends InitialDocumentProbeHost {
22
38
  listKnownContainerIds: () => ReadonlyArray<string>;
23
39
  /** Known ids eligible after an automatic root-lane discovery hint. */
24
40
  listAutomaticRootCatchupContainerIds: () => ReadonlyArray<string>;
25
- /** Discover + persist documents; `null` means the lane was not fully listed. */
26
- discoverContainerDocuments: (containerId: string, onFullListing?: ((documentIds: ReadonlyArray<string>) => void) | undefined, onPendingDiscovery?: ((delayMs: number) => void) | undefined) => Promise<ReadonlyArray<DocumentSummary> | null>;
41
+ /**
42
+ * Discover + persist documents for every requested container through one
43
+ * batched listing. Each lane's outcome is handed to `onOutcome` (awaited)
44
+ * as soon as it applies, before later lanes apply; every outcome is also
45
+ * returned.
46
+ */
47
+ discoverContainerDocuments: (requests: ReadonlyArray<ContainerDiscoveryRequest>, onOutcome: (outcome: ContainerDiscoveryOutcome) => Promise<void>) => Promise<ReadonlyArray<ContainerDiscoveryOutcome>>;
27
48
  /** Read a container's freshly-persisted summaries+links from SQLite. */
28
49
  loadContainerDelta: (containerId: string) => Promise<LocalProjectionReconciledDelta>;
29
50
  /** Push a reconciled delta into the local projection store. */
@@ -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\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 /** Discover + persist documents; `null` means the lane was not fully listed. */\n discoverContainerDocuments: (\n containerId: string,\n onFullListing?: ((documentIds: ReadonlyArray<string>) => void) | undefined,\n onPendingDiscovery?: ((delayMs: number) => void) | undefined,\n ) => Promise<ReadonlyArray<DocumentSummary> | null>;\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 { 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,52 +1,106 @@
1
- import { isContainerNotFoundFailure } from "../../data/containers/shared/mutationFailures.js";
2
- function isUnavailableContainerDocumentLane(failure) {
3
- return !failure.ok && isContainerNotFoundFailure(failure);
1
+ import { DEFAULT_CONTAINER_DOCUMENT_LANE_PAGE_LIMIT, MAX_CONTAINER_DOCUMENT_LANE_PAGE_TOTAL, MAX_CONTAINER_DOCUMENT_LANES, } from "../../internal/validators/request/index.js";
2
+ /**
3
+ * Rows each lane may return so a full batch stays within the single-page
4
+ * maximum; a lone lane keeps the ordinary page size.
5
+ */
6
+ function laneLimit(laneCount) {
7
+ return Math.max(1, Math.min(DEFAULT_CONTAINER_DOCUMENT_LANE_PAGE_LIMIT, Math.floor(MAX_CONTAINER_DOCUMENT_LANE_PAGE_TOTAL / laneCount)));
4
8
  }
5
- export async function listContainerDocumentsFromApi(apiClient, containerId, options, reportErrors = true) {
6
- if (apiClient.listContainerDocumentsResult) {
7
- const result = await apiClient.listContainerDocumentsResult(containerId, options, { reportErrors: false });
8
- if (result.ok) {
9
- return result.data;
10
- }
11
- if (reportErrors && !isUnavailableContainerDocumentLane(result)) {
12
- result.report();
13
- }
9
+ /**
10
+ * Lists one page per requested lane in a single batched request. A lane the
11
+ * server rejected yields `null` for that lane only; a failed request yields
12
+ * `null` for the batch.
13
+ */
14
+ export async function listContainerDocumentLanesFromApi(apiClient, lanes, reportErrors = true) {
15
+ const limit = laneLimit(lanes.length);
16
+ const requested = lanes.map((lane, index) => ({
17
+ containerId: lane.containerId,
18
+ laneId: `lane-${index}`,
19
+ limit,
20
+ watermark: lane.watermark,
21
+ }));
22
+ const response = await apiClient.listContainerDocumentLanes({ lanes: requested }, { reportErrors });
23
+ if (!response)
14
24
  return null;
15
- }
16
- return apiClient.listContainerDocuments(containerId, options);
25
+ const resultsByLaneId = new Map(response.results.map((result) => [result.laneId, result]));
26
+ return requested.map(({ laneId }) => {
27
+ const result = resultsByLaneId.get(laneId);
28
+ return result && "page" in result ? result.page : null;
29
+ });
17
30
  }
18
- export async function listAllContainerDocuments(input) {
19
- const items = [];
20
- const tombstones = [];
21
- let watermark = await input.loadContainerDocumentWatermark(input.containerId);
22
- const isFullListing = watermark === null;
23
- while (true) {
24
- const response = await input.listContainerDocuments(input.containerId, {
31
+ /**
32
+ * Pages every lane to exhaustion, batching first pages and continuations
33
+ * alike. Each container keeps its own watermark, so one lane's failure or
34
+ * continuation never changes another lane's result.
35
+ */
36
+ export async function listAllContainerDocumentLanes(input) {
37
+ const listed = new Map();
38
+ const pending = [];
39
+ const queued = new Set();
40
+ for (const { containerId, watermark } of input.lanes) {
41
+ if (queued.has(containerId))
42
+ continue;
43
+ queued.add(containerId);
44
+ pending.push({
45
+ containerId,
46
+ isFullListing: watermark === null,
47
+ items: [],
48
+ tombstones: [],
25
49
  watermark,
26
50
  });
27
- if (!response) {
28
- return null;
29
- }
30
- items.push(...response.items);
31
- tombstones.push(...response.tombstones);
32
- watermark = response.nextWatermark;
33
- if (!response.hasMore) {
34
- return { isFullListing, items, nextWatermark: watermark, tombstones };
35
- }
36
- if (!watermark) {
37
- return null;
38
- }
39
51
  }
52
+ while (pending.length > 0) {
53
+ const batch = pending.splice(0, MAX_CONTAINER_DOCUMENT_LANES);
54
+ const pages = await input.listContainerDocumentLanes(batch.map(({ containerId, watermark }) => ({ containerId, watermark })));
55
+ batch.forEach((lane, index) => {
56
+ const page = pages?.[index] ?? null;
57
+ if (!page) {
58
+ listed.set(lane.containerId, null);
59
+ return;
60
+ }
61
+ lane.items.push(...page.items);
62
+ lane.tombstones.push(...page.tombstones);
63
+ lane.watermark = page.nextWatermark;
64
+ if (!page.hasMore) {
65
+ listed.set(lane.containerId, {
66
+ isFullListing: lane.isFullListing,
67
+ items: lane.items,
68
+ nextWatermark: lane.watermark,
69
+ tombstones: lane.tombstones,
70
+ });
71
+ }
72
+ else if (lane.watermark) {
73
+ pending.push(lane);
74
+ }
75
+ else {
76
+ listed.set(lane.containerId, null);
77
+ }
78
+ });
79
+ }
80
+ return listed;
40
81
  }
41
82
  /** Lists the authoritative current ids by deliberately starting at no watermark. */
42
83
  export async function listAllContainerDocumentIdsFromApi(input) {
43
- const listed = await listAllContainerDocuments({
44
- containerId: input.containerId,
45
- loadContainerDocumentWatermark: async () => null,
46
- listContainerDocuments: (containerId, options) => listContainerDocumentsFromApi(input.apiClient, containerId, options, input.reportErrors),
47
- });
84
+ const listed = (await listAllContainerDocumentLanes({
85
+ lanes: [{ containerId: input.containerId, watermark: null }],
86
+ listContainerDocumentLanes: (lanes) => listContainerDocumentLanesFromApi(input.apiClient, lanes, input.reportErrors),
87
+ })).get(input.containerId);
48
88
  return listed
49
89
  ? Array.from(new Set(listed.items.map((document) => document.id)))
50
90
  : null;
51
91
  }
92
+ /** Loads each lane's persisted watermark, then lists every lane in batches. */
93
+ export async function listContainerDocumentsFromWatermarks(input) {
94
+ const lanes = [];
95
+ for (const containerId of new Set(input.containerIds)) {
96
+ lanes.push({
97
+ containerId,
98
+ watermark: await input.loadContainerDocumentWatermark(containerId),
99
+ });
100
+ }
101
+ return listAllContainerDocumentLanes({
102
+ lanes,
103
+ listContainerDocumentLanes: input.listContainerDocumentLanes,
104
+ });
105
+ }
52
106
  //# sourceMappingURL=containerDocumentListing.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"containerDocumentListing.js","sourceRoot":"","sources":["containerDocumentListing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,+CAA+C,CAAC;AAc3F,SAAS,kCAAkC,CACzC,OAIC;IAED,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,0BAA0B,CAAC,OAAO,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,SAAmC,EACnC,WAAmB,EACnB,OAEI,EACJ,YAAY,GAAG,IAAI;IAEnB,IAAI,SAAS,CAAC,4BAA4B,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,4BAA4B,CACzD,WAAW,EACX,OAAO,EACP,EAAE,YAAY,EAAE,KAAK,EAAE,CACxB,CAAC;QACF,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,OAAO,MAAM,CAAC,IAAI,CAAC;QACrB,CAAC;QACD,IAAI,YAAY,IAAI,CAAC,kCAAkC,CAAC,MAAM,CAAC,EAAE,CAAC;YAChE,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,SAAS,CAAC,sBAAsB,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,KAI/C;IACC,MAAM,KAAK,GAA8B,EAAE,CAAC;IAC5C,MAAM,UAAU,GAAiC,EAAE,CAAC;IACpD,IAAI,SAAS,GAAG,MAAM,KAAK,CAAC,8BAA8B,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAE9E,MAAM,aAAa,GAAG,SAAS,KAAK,IAAI,CAAC;IAEzC,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,sBAAsB,CAAC,KAAK,CAAC,WAAW,EAAE;YACrE,SAAS;SACV,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,IAAI,CAAC;QACd,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC9B,UAAU,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;QACxC,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAC;QAEnC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;YACtB,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;QACxE,CAAC;QACD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;AACH,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,kCAAkC,CAAC,KAIxD;IACC,MAAM,MAAM,GAAG,MAAM,yBAAyB,CAAC;QAC7C,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,8BAA8B,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI;QAChD,sBAAsB,EAAE,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE,CAC/C,6BAA6B,CAC3B,KAAK,CAAC,SAAS,EACf,WAAW,EACX,OAAO,EACP,KAAK,CAAC,YAAY,CACnB;KACJ,CAAC,CAAC;IACH,OAAO,MAAM;QACX,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;QAClE,CAAC,CAAC,IAAI,CAAC;AACX,CAAC","sourcesContent":["import { isContainerNotFoundFailure } from \"../../data/containers/shared/mutationFailures\";\nimport type {\n ContainerDocumentDiscoveryApi,\n ContainerDocumentTombstone,\n DiscoverContainerDocumentsOptions,\n ListedContainerDocument,\n ListedContainerDocuments,\n} from \"./documentDiscoveryTypes\";\n\ntype ContainerDocumentListApi = Pick<\n ContainerDocumentDiscoveryApi,\n \"listContainerDocuments\" | \"listContainerDocumentsResult\"\n>;\n\nfunction isUnavailableContainerDocumentLane(\n failure: Awaited<\n ReturnType<\n NonNullable<ContainerDocumentDiscoveryApi[\"listContainerDocumentsResult\"]>\n >\n >,\n): boolean {\n return !failure.ok && isContainerNotFoundFailure(failure);\n}\n\nexport async function listContainerDocumentsFromApi(\n apiClient: ContainerDocumentListApi,\n containerId: string,\n options?: Parameters<\n ContainerDocumentDiscoveryApi[\"listContainerDocuments\"]\n >[1],\n reportErrors = true,\n): ReturnType<ContainerDocumentDiscoveryApi[\"listContainerDocuments\"]> {\n if (apiClient.listContainerDocumentsResult) {\n const result = await apiClient.listContainerDocumentsResult(\n containerId,\n options,\n { reportErrors: false },\n );\n if (result.ok) {\n return result.data;\n }\n if (reportErrors && !isUnavailableContainerDocumentLane(result)) {\n result.report();\n }\n return null;\n }\n\n return apiClient.listContainerDocuments(containerId, options);\n}\n\nexport async function listAllContainerDocuments(input: {\n containerId: string;\n loadContainerDocumentWatermark: DiscoverContainerDocumentsOptions[\"loadContainerDocumentWatermark\"];\n listContainerDocuments: DiscoverContainerDocumentsOptions[\"listContainerDocuments\"];\n}): Promise<ListedContainerDocuments | null> {\n const items: ListedContainerDocument[] = [];\n const tombstones: ContainerDocumentTombstone[] = [];\n let watermark = await input.loadContainerDocumentWatermark(input.containerId);\n\n const isFullListing = watermark === null;\n\n while (true) {\n const response = await input.listContainerDocuments(input.containerId, {\n watermark,\n });\n if (!response) {\n return null;\n }\n items.push(...response.items);\n tombstones.push(...response.tombstones);\n watermark = response.nextWatermark;\n\n if (!response.hasMore) {\n return { isFullListing, items, nextWatermark: watermark, tombstones };\n }\n if (!watermark) {\n return null;\n }\n }\n}\n\n/** Lists the authoritative current ids by deliberately starting at no watermark. */\nexport async function listAllContainerDocumentIdsFromApi(input: {\n readonly apiClient: ContainerDocumentListApi;\n readonly containerId: string;\n readonly reportErrors?: boolean;\n}): Promise<ReadonlyArray<string> | null> {\n const listed = await listAllContainerDocuments({\n containerId: input.containerId,\n loadContainerDocumentWatermark: async () => null,\n listContainerDocuments: (containerId, options) =>\n listContainerDocumentsFromApi(\n input.apiClient,\n containerId,\n options,\n input.reportErrors,\n ),\n });\n return listed\n ? Array.from(new Set(listed.items.map((document) => document.id)))\n : null;\n}\n"]}
1
+ {"version":3,"file":"containerDocumentListing.js","sourceRoot":"","sources":["containerDocumentListing.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n DEFAULT_CONTAINER_DOCUMENT_LANE_PAGE_LIMIT,\n MAX_CONTAINER_DOCUMENT_LANE_PAGE_TOTAL,\n MAX_CONTAINER_DOCUMENT_LANES,\n} from \"@tearleads/validators/request\";\nimport type { SyncWatermark } from \"@tearleads/validators/response\";\nimport type {\n ContainerDocumentDiscoveryApi,\n ContainerDocumentLaneLister,\n ContainerDocumentTombstone,\n ListedContainerDocument,\n ListedContainerDocuments,\n} from \"./documentDiscoveryTypes\";\n\ntype ContainerDocumentLaneApi = Pick<\n ContainerDocumentDiscoveryApi,\n \"listContainerDocumentLanes\"\n>;\n\n/**\n * Rows each lane may return so a full batch stays within the single-page\n * maximum; a lone lane keeps the ordinary page size.\n */\nfunction laneLimit(laneCount: number): number {\n return Math.max(\n 1,\n Math.min(\n DEFAULT_CONTAINER_DOCUMENT_LANE_PAGE_LIMIT,\n Math.floor(MAX_CONTAINER_DOCUMENT_LANE_PAGE_TOTAL / laneCount),\n ),\n );\n}\n\n/**\n * Lists one page per requested lane in a single batched request. A lane the\n * server rejected yields `null` for that lane only; a failed request yields\n * `null` for the batch.\n */\nexport async function listContainerDocumentLanesFromApi(\n apiClient: ContainerDocumentLaneApi,\n lanes: Parameters<ContainerDocumentLaneLister>[0],\n reportErrors = true,\n): ReturnType<ContainerDocumentLaneLister> {\n const limit = laneLimit(lanes.length);\n const requested = lanes.map((lane, index) => ({\n containerId: lane.containerId,\n laneId: `lane-${index}`,\n limit,\n watermark: lane.watermark,\n }));\n const response = await apiClient.listContainerDocumentLanes(\n { lanes: requested },\n { reportErrors },\n );\n if (!response) return null;\n const resultsByLaneId = new Map(\n response.results.map((result) => [result.laneId, result]),\n );\n return requested.map(({ laneId }) => {\n const result = resultsByLaneId.get(laneId);\n return result && \"page\" in result ? result.page : null;\n });\n}\n\ninterface PendingContainerDocumentLane {\n readonly containerId: string;\n readonly isFullListing: boolean;\n readonly items: ListedContainerDocument[];\n readonly tombstones: ContainerDocumentTombstone[];\n watermark: SyncWatermark | null;\n}\n\n/**\n * Pages every lane to exhaustion, batching first pages and continuations\n * alike. Each container keeps its own watermark, so one lane's failure or\n * continuation never changes another lane's result.\n */\nexport async function listAllContainerDocumentLanes(input: {\n readonly lanes: ReadonlyArray<{\n readonly containerId: string;\n readonly watermark: SyncWatermark | null;\n }>;\n readonly listContainerDocumentLanes: ContainerDocumentLaneLister;\n}): Promise<ReadonlyMap<string, ListedContainerDocuments | null>> {\n const listed = new Map<string, ListedContainerDocuments | null>();\n const pending: PendingContainerDocumentLane[] = [];\n const queued = new Set<string>();\n for (const { containerId, watermark } of input.lanes) {\n if (queued.has(containerId)) continue;\n queued.add(containerId);\n pending.push({\n containerId,\n isFullListing: watermark === null,\n items: [],\n tombstones: [],\n watermark,\n });\n }\n\n while (pending.length > 0) {\n const batch = pending.splice(0, MAX_CONTAINER_DOCUMENT_LANES);\n const pages = await input.listContainerDocumentLanes(\n batch.map(({ containerId, watermark }) => ({ containerId, watermark })),\n );\n batch.forEach((lane, index) => {\n const page = pages?.[index] ?? null;\n if (!page) {\n listed.set(lane.containerId, null);\n return;\n }\n lane.items.push(...page.items);\n lane.tombstones.push(...page.tombstones);\n lane.watermark = page.nextWatermark;\n if (!page.hasMore) {\n listed.set(lane.containerId, {\n isFullListing: lane.isFullListing,\n items: lane.items,\n nextWatermark: lane.watermark,\n tombstones: lane.tombstones,\n });\n } else if (lane.watermark) {\n pending.push(lane);\n } else {\n listed.set(lane.containerId, null);\n }\n });\n }\n return listed;\n}\n\n/** Lists the authoritative current ids by deliberately starting at no watermark. */\nexport async function listAllContainerDocumentIdsFromApi(input: {\n readonly apiClient: ContainerDocumentLaneApi;\n readonly containerId: string;\n readonly reportErrors?: boolean;\n}): Promise<ReadonlyArray<string> | null> {\n const listed = (\n await listAllContainerDocumentLanes({\n lanes: [{ containerId: input.containerId, watermark: null }],\n listContainerDocumentLanes: (lanes) =>\n listContainerDocumentLanesFromApi(\n input.apiClient,\n lanes,\n input.reportErrors,\n ),\n })\n ).get(input.containerId);\n return listed\n ? Array.from(new Set(listed.items.map((document) => document.id)))\n : null;\n}\n\n/** Loads each lane's persisted watermark, then lists every lane in batches. */\nexport async function listContainerDocumentsFromWatermarks(input: {\n readonly containerIds: ReadonlyArray<string>;\n readonly listContainerDocumentLanes: ContainerDocumentLaneLister;\n readonly loadContainerDocumentWatermark: (\n containerId: string,\n ) => Promise<SyncWatermark | null>;\n}): Promise<ReadonlyMap<string, ListedContainerDocuments | null>> {\n const lanes: Array<{ containerId: string; watermark: SyncWatermark | null }> =\n [];\n for (const containerId of new Set(input.containerIds)) {\n lanes.push({\n containerId,\n watermark: await input.loadContainerDocumentWatermark(containerId),\n });\n }\n return listAllContainerDocumentLanes({\n lanes,\n listContainerDocumentLanes: input.listContainerDocumentLanes,\n });\n}\n"]}
@@ -15,7 +15,13 @@ export declare function createContainerParentSyncLane(parentId: string | null):
15
15
  export declare function createContainerContentsSyncLane(containerId: string): ContainerSyncWatermarkLane;
16
16
  export declare function loadContainerSyncWatermark(execSql: ExecSql, syncLane: ContainerSyncWatermarkLane): Promise<SyncWatermark | null>;
17
17
  export declare function saveContainerSyncWatermark(execSql: ExecSql, syncLane: ContainerSyncWatermarkLane, watermark: SyncWatermark): Promise<void>;
18
- export declare function saveContainerSyncWatermarkIfCurrent(execSql: ExecSql, syncLane: ContainerSyncWatermarkLane, watermark: SyncWatermark, stillCurrent?: (() => boolean) | undefined): Promise<boolean>;
19
18
  export declare function loadContainerSyncLaneCheckRecords(execSql: ExecSql, syncLanes: ReadonlyArray<ContainerSyncWatermarkLane>): Promise<Array<ContainerSyncLaneCheckRecord | null>>;
20
19
  export declare function markContainerSyncLaneChecked(execSql: ExecSql, syncLane: ContainerSyncWatermarkLane): Promise<void>;
21
- export declare function markContainerSyncLaneCheckedIfCurrent(execSql: ExecSql, syncLane: ContainerSyncWatermarkLane, stillCurrent?: (() => boolean) | undefined): Promise<boolean>;
20
+ /**
21
+ * Records one lane fetch in a single guarded upsert: the cursor when the page
22
+ * carried one and the freshness marker when the lane is fully drained.
23
+ */
24
+ export declare function recordContainerSyncLaneFetchIfCurrent(execSql: ExecSql, syncLane: ContainerSyncWatermarkLane, fetch: {
25
+ readonly checked: boolean;
26
+ readonly watermark: SyncWatermark | null;
27
+ }, stillCurrent?: (() => boolean) | undefined): Promise<boolean>;
@@ -23,18 +23,17 @@ export async function loadContainerSyncWatermark(execSql, syncLane) {
23
23
  export async function saveContainerSyncWatermark(execSql, syncLane, watermark) {
24
24
  await sqlContainerSyncWatermarkPersistence.saveWatermark(execSql, syncLane, watermark);
25
25
  }
26
- export async function saveContainerSyncWatermarkIfCurrent(execSql, syncLane, watermark, stillCurrent) {
27
- return sqlContainerSyncWatermarkPersistence.saveWatermark(execSql, syncLane, watermark, { stillCurrent });
28
- }
29
26
  export async function loadContainerSyncLaneCheckRecords(execSql, syncLanes) {
30
27
  return sqlContainerSyncWatermarkPersistence.loadCheckRecords(execSql, syncLanes);
31
28
  }
32
29
  export async function markContainerSyncLaneChecked(execSql, syncLane) {
33
30
  await sqlContainerSyncWatermarkPersistence.markChecked(execSql, syncLane);
34
31
  }
35
- export async function markContainerSyncLaneCheckedIfCurrent(execSql, syncLane, stillCurrent) {
36
- return sqlContainerSyncWatermarkPersistence.markChecked(execSql, syncLane, {
37
- stillCurrent,
38
- });
32
+ /**
33
+ * Records one lane fetch in a single guarded upsert: the cursor when the page
34
+ * carried one and the freshness marker when the lane is fully drained.
35
+ */
36
+ export async function recordContainerSyncLaneFetchIfCurrent(execSql, syncLane, fetch, stillCurrent) {
37
+ return sqlContainerSyncWatermarkPersistence.recordLaneFetch(execSql, syncLane, fetch, { stillCurrent });
39
38
  }
40
39
  //# sourceMappingURL=containerPersistence.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"containerPersistence.js","sourceRoot":"","sources":["containerPersistence.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAE9E,OAAO,EAGL,yBAAyB,EACzB,uBAAuB,EACvB,oCAAoC,GACrC,MAAM,qEAAqE,CAAC;AAa7E,OAAO,EAAE,+BAA+B,IAAI,mCAAmC,EAAE,MAAM,wEAAwE,CAAC;AAIhK,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,OAAgB,EAChB,WAAyC,EACzC,MAIC;IAED,MAAM,mBAAmB,GAAG,yBAAyB,CACnD,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,mBAAmB,CAC3B,CAAC;IACF,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,WAAW,CAAC,oBAAoB,CAAC,OAAO,EAAE;QAC/C,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,GAAG,mBAAmB;KACvB,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,6BAA6B,CAC3C,QAAuB;IAEvB,OAAO,uBAAuB,CAAC,QAAQ,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,+BAA+B,CAC7C,WAAmB;IAEnB,OAAO,yBAAyB,CAAC,WAAW,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,OAAgB,EAChB,QAAoC;IAEpC,OAAO,oCAAoC,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,OAAgB,EAChB,QAAoC,EACpC,SAAwB;IAExB,MAAM,oCAAoC,CAAC,aAAa,CACtD,OAAO,EACP,QAAQ,EACR,SAAS,CACV,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mCAAmC,CACvD,OAAgB,EAChB,QAAoC,EACpC,SAAwB,EACxB,YAA0C;IAE1C,OAAO,oCAAoC,CAAC,aAAa,CACvD,OAAO,EACP,QAAQ,EACR,SAAS,EACT,EAAE,YAAY,EAAE,CACjB,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iCAAiC,CACrD,OAAgB,EAChB,SAAoD;IAEpD,OAAO,oCAAoC,CAAC,gBAAgB,CAC1D,OAAO,EACP,SAAS,CACV,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,OAAgB,EAChB,QAAoC;IAEpC,MAAM,oCAAoC,CAAC,WAAW,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qCAAqC,CACzD,OAAgB,EAChB,QAAoC,EACpC,YAA0C;IAE1C,OAAO,oCAAoC,CAAC,WAAW,CAAC,OAAO,EAAE,QAAQ,EAAE;QACzE,YAAY;KACb,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { SyncWatermark } from \"@tearleads/validators/response\";\nimport { createPendingUpdateFields } from \"../../data/documents/documentSync\";\nimport type { ContainerContentsPersistence } from \"../../data/persistence/container-contents/containerContentsPersistence\";\nimport {\n type ContainerSyncLaneCheckRecord,\n type ContainerSyncWatermarkLane,\n containerContentsSyncLane,\n containerParentSyncLane,\n sqlContainerSyncWatermarkPersistence,\n} from \"../../data/persistence/containers/containerSyncWatermarkPersistence\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\n\nexport type {\n ContainerContentsPersistence,\n ContainerCreateIntentRecord,\n ContainerHydrationTombstone,\n ContainerMetadataRecord,\n ContainerMetadataRecord as ContainerDocumentRecord,\n ContainerMoveIntentRecord,\n LocalRootDescendantReparentInput,\n StoredContainerState,\n} from \"../../data/persistence/container-contents/containerContentsPersistence\";\nexport { sqlContainerContentsPersistence as defaultContainerContentsPersistence } from \"../../data/persistence/container-contents/containerContentsPersistence\";\nexport type { ContainerRecord } from \"../../data/persistence/containers/containerPersistence\";\nexport type { ContainerSyncWatermarkLane } from \"../../data/persistence/containers/containerSyncWatermarkPersistence\";\n\nexport async function enqueuePendingContainerUpdate(\n execSql: ExecSql,\n persistence: ContainerContentsPersistence,\n params: {\n containerId: string;\n sourceVersionVector?: string | null | undefined;\n update: Uint8Array;\n },\n): Promise<string | null> {\n const pendingUpdateFields = createPendingUpdateFields(\n params.update,\n params.sourceVersionVector,\n );\n if (!pendingUpdateFields) {\n return null;\n }\n\n return persistence.enqueuePendingUpdate(execSql, {\n containerId: params.containerId,\n ...pendingUpdateFields,\n });\n}\n\nexport function createContainerParentSyncLane(\n parentId: string | null,\n): ContainerSyncWatermarkLane {\n return containerParentSyncLane(parentId);\n}\n\nexport function createContainerContentsSyncLane(\n containerId: string,\n): ContainerSyncWatermarkLane {\n return containerContentsSyncLane(containerId);\n}\n\nexport async function loadContainerSyncWatermark(\n execSql: ExecSql,\n syncLane: ContainerSyncWatermarkLane,\n): Promise<SyncWatermark | null> {\n return sqlContainerSyncWatermarkPersistence.loadWatermark(execSql, syncLane);\n}\n\nexport async function saveContainerSyncWatermark(\n execSql: ExecSql,\n syncLane: ContainerSyncWatermarkLane,\n watermark: SyncWatermark,\n): Promise<void> {\n await sqlContainerSyncWatermarkPersistence.saveWatermark(\n execSql,\n syncLane,\n watermark,\n );\n}\n\nexport async function saveContainerSyncWatermarkIfCurrent(\n execSql: ExecSql,\n syncLane: ContainerSyncWatermarkLane,\n watermark: SyncWatermark,\n stillCurrent?: (() => boolean) | undefined,\n): Promise<boolean> {\n return sqlContainerSyncWatermarkPersistence.saveWatermark(\n execSql,\n syncLane,\n watermark,\n { stillCurrent },\n );\n}\n\nexport async function loadContainerSyncLaneCheckRecords(\n execSql: ExecSql,\n syncLanes: ReadonlyArray<ContainerSyncWatermarkLane>,\n): Promise<Array<ContainerSyncLaneCheckRecord | null>> {\n return sqlContainerSyncWatermarkPersistence.loadCheckRecords(\n execSql,\n syncLanes,\n );\n}\n\nexport async function markContainerSyncLaneChecked(\n execSql: ExecSql,\n syncLane: ContainerSyncWatermarkLane,\n): Promise<void> {\n await sqlContainerSyncWatermarkPersistence.markChecked(execSql, syncLane);\n}\n\nexport async function markContainerSyncLaneCheckedIfCurrent(\n execSql: ExecSql,\n syncLane: ContainerSyncWatermarkLane,\n stillCurrent?: (() => boolean) | undefined,\n): Promise<boolean> {\n return sqlContainerSyncWatermarkPersistence.markChecked(execSql, syncLane, {\n stillCurrent,\n });\n}\n"]}
1
+ {"version":3,"file":"containerPersistence.js","sourceRoot":"","sources":["containerPersistence.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAE9E,OAAO,EAGL,yBAAyB,EACzB,uBAAuB,EACvB,oCAAoC,GACrC,MAAM,qEAAqE,CAAC;AAa7E,OAAO,EAAE,+BAA+B,IAAI,mCAAmC,EAAE,MAAM,wEAAwE,CAAC;AAIhK,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,OAAgB,EAChB,WAAyC,EACzC,MAIC;IAED,MAAM,mBAAmB,GAAG,yBAAyB,CACnD,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,mBAAmB,CAC3B,CAAC;IACF,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,WAAW,CAAC,oBAAoB,CAAC,OAAO,EAAE;QAC/C,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,GAAG,mBAAmB;KACvB,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,6BAA6B,CAC3C,QAAuB;IAEvB,OAAO,uBAAuB,CAAC,QAAQ,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,+BAA+B,CAC7C,WAAmB;IAEnB,OAAO,yBAAyB,CAAC,WAAW,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,OAAgB,EAChB,QAAoC;IAEpC,OAAO,oCAAoC,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,OAAgB,EAChB,QAAoC,EACpC,SAAwB;IAExB,MAAM,oCAAoC,CAAC,aAAa,CACtD,OAAO,EACP,QAAQ,EACR,SAAS,CACV,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iCAAiC,CACrD,OAAgB,EAChB,SAAoD;IAEpD,OAAO,oCAAoC,CAAC,gBAAgB,CAC1D,OAAO,EACP,SAAS,CACV,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,OAAgB,EAChB,QAAoC;IAEpC,MAAM,oCAAoC,CAAC,WAAW,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC5E,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qCAAqC,CACzD,OAAgB,EAChB,QAAoC,EACpC,KAGC,EACD,YAA0C;IAE1C,OAAO,oCAAoC,CAAC,eAAe,CACzD,OAAO,EACP,QAAQ,EACR,KAAK,EACL,EAAE,YAAY,EAAE,CACjB,CAAC;AACJ,CAAC","sourcesContent":["import type { SyncWatermark } from \"@tearleads/validators/response\";\nimport { createPendingUpdateFields } from \"../../data/documents/documentSync\";\nimport type { ContainerContentsPersistence } from \"../../data/persistence/container-contents/containerContentsPersistence\";\nimport {\n type ContainerSyncLaneCheckRecord,\n type ContainerSyncWatermarkLane,\n containerContentsSyncLane,\n containerParentSyncLane,\n sqlContainerSyncWatermarkPersistence,\n} from \"../../data/persistence/containers/containerSyncWatermarkPersistence\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\n\nexport type {\n ContainerContentsPersistence,\n ContainerCreateIntentRecord,\n ContainerHydrationTombstone,\n ContainerMetadataRecord,\n ContainerMetadataRecord as ContainerDocumentRecord,\n ContainerMoveIntentRecord,\n LocalRootDescendantReparentInput,\n StoredContainerState,\n} from \"../../data/persistence/container-contents/containerContentsPersistence\";\nexport { sqlContainerContentsPersistence as defaultContainerContentsPersistence } from \"../../data/persistence/container-contents/containerContentsPersistence\";\nexport type { ContainerRecord } from \"../../data/persistence/containers/containerPersistence\";\nexport type { ContainerSyncWatermarkLane } from \"../../data/persistence/containers/containerSyncWatermarkPersistence\";\n\nexport async function enqueuePendingContainerUpdate(\n execSql: ExecSql,\n persistence: ContainerContentsPersistence,\n params: {\n containerId: string;\n sourceVersionVector?: string | null | undefined;\n update: Uint8Array;\n },\n): Promise<string | null> {\n const pendingUpdateFields = createPendingUpdateFields(\n params.update,\n params.sourceVersionVector,\n );\n if (!pendingUpdateFields) {\n return null;\n }\n\n return persistence.enqueuePendingUpdate(execSql, {\n containerId: params.containerId,\n ...pendingUpdateFields,\n });\n}\n\nexport function createContainerParentSyncLane(\n parentId: string | null,\n): ContainerSyncWatermarkLane {\n return containerParentSyncLane(parentId);\n}\n\nexport function createContainerContentsSyncLane(\n containerId: string,\n): ContainerSyncWatermarkLane {\n return containerContentsSyncLane(containerId);\n}\n\nexport async function loadContainerSyncWatermark(\n execSql: ExecSql,\n syncLane: ContainerSyncWatermarkLane,\n): Promise<SyncWatermark | null> {\n return sqlContainerSyncWatermarkPersistence.loadWatermark(execSql, syncLane);\n}\n\nexport async function saveContainerSyncWatermark(\n execSql: ExecSql,\n syncLane: ContainerSyncWatermarkLane,\n watermark: SyncWatermark,\n): Promise<void> {\n await sqlContainerSyncWatermarkPersistence.saveWatermark(\n execSql,\n syncLane,\n watermark,\n );\n}\n\nexport async function loadContainerSyncLaneCheckRecords(\n execSql: ExecSql,\n syncLanes: ReadonlyArray<ContainerSyncWatermarkLane>,\n): Promise<Array<ContainerSyncLaneCheckRecord | null>> {\n return sqlContainerSyncWatermarkPersistence.loadCheckRecords(\n execSql,\n syncLanes,\n );\n}\n\nexport async function markContainerSyncLaneChecked(\n execSql: ExecSql,\n syncLane: ContainerSyncWatermarkLane,\n): Promise<void> {\n await sqlContainerSyncWatermarkPersistence.markChecked(execSql, syncLane);\n}\n\n/**\n * Records one lane fetch in a single guarded upsert: the cursor when the page\n * carried one and the freshness marker when the lane is fully drained.\n */\nexport async function recordContainerSyncLaneFetchIfCurrent(\n execSql: ExecSql,\n syncLane: ContainerSyncWatermarkLane,\n fetch: {\n readonly checked: boolean;\n readonly watermark: SyncWatermark | null;\n },\n stillCurrent?: (() => boolean) | undefined,\n): Promise<boolean> {\n return sqlContainerSyncWatermarkPersistence.recordLaneFetch(\n execSql,\n syncLane,\n fetch,\n { stillCurrent },\n );\n}\n"]}
@@ -4,9 +4,9 @@ export type { RefreshAllContainerDocumentsFromApiOptions } from "./documentDisco
4
4
  export declare function hasUndiscoveredDocumentUpdateEvent(events: ReadonlyArray<unknown>, knownDocumentIds: ReadonlySet<string>): boolean;
5
5
  export declare function listAllRemoteContainerIds(listContainerParentLanes: ContainerDocumentDiscoveryApi["listContainerParentLanes"]): Promise<ReadonlyArray<string> | null>;
6
6
  export declare function listAllRemoteContainerIdsFromApi(apiClient: Pick<ContainerDocumentDiscoveryApi, "listContainerParentLanes">): Promise<ReadonlyArray<string> | null>;
7
- export declare function discoverContainerDocuments(options: DiscoverContainerDocumentsOptions): Promise<ReadonlyArray<DocumentSummary> | null>;
8
- export declare function discoverContainerDocumentsFromApi({ apiClient, ...input }: Omit<DiscoverContainerDocumentsOptions, "listContainerDocuments"> & {
9
- readonly apiClient: Pick<ContainerDocumentDiscoveryApi, "listContainerDocuments" | "listContainerDocumentsResult">;
7
+ export declare function discoverContainerDocuments({ beginDocumentDiscovery, listContainerDocumentLanes, loadContainerDocumentWatermark, ...lane }: DiscoverContainerDocumentsOptions): Promise<ReadonlyArray<DocumentSummary> | null>;
8
+ export declare function discoverContainerDocumentsFromApi({ apiClient, ...input }: Omit<DiscoverContainerDocumentsOptions, "listContainerDocumentLanes"> & {
9
+ readonly apiClient: Pick<ContainerDocumentDiscoveryApi, "listContainerDocumentLanes">;
10
10
  }): Promise<ReadonlyArray<DocumentSummary> | null>;
11
11
  export declare function discoverAllContainerDocuments(options: DiscoverAllContainerDocumentsOptions): Promise<ReadonlyArray<DocumentSummary> | null>;
12
12
  export declare function refreshAllContainerDocumentsFromApi({ apiClient, ...input }: RefreshAllContainerDocumentsFromApiOptions): Promise<ReadonlyArray<DocumentSummary> | null>;