@tearleads/client-sdk 0.1.87 → 0.1.88

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 (120) hide show
  1. package/Tearleads.js +1 -1
  2. package/Tearleads.js.map +1 -1
  3. package/client/containerContents/documentDiscovery.js +16 -2
  4. package/client/containerContents/documentDiscovery.js.map +1 -1
  5. package/client/containerContents/index.d.ts +1 -1
  6. package/client/containerContents/index.js +1 -0
  7. package/client/containerContents/index.js.map +1 -1
  8. package/client/index.d.ts +1 -1
  9. package/client/index.js.map +1 -1
  10. package/client/organizations/index.d.ts +4 -2
  11. package/client/organizations/index.js +6 -3
  12. package/client/organizations/index.js.map +1 -1
  13. package/client/organizations/organizationMemberships.d.ts +30 -0
  14. package/client/organizations/organizationMemberships.js +132 -0
  15. package/client/organizations/organizationMemberships.js.map +1 -0
  16. package/client/organizations/organizationsTypes.d.ts +3 -0
  17. package/client/organizations/organizationsTypes.js.map +1 -1
  18. package/data/persistence/container-contents/containerStructuralRepair.js +19 -14
  19. package/data/persistence/container-contents/containerStructuralRepair.js.map +1 -1
  20. package/data/persistence/container-contents/exclusiveDocumentPlacements.js +60 -0
  21. package/data/persistence/container-contents/exclusiveDocumentPlacements.js.map +1 -0
  22. package/data/sqlite/schema/index.d.ts +71 -0
  23. package/data/sqlite/schema/index.js +3 -0
  24. package/data/sqlite/schema/index.js.map +1 -1
  25. package/data/sqlite/schema/organizationSyncPauseSchema.js +32 -0
  26. package/data/sqlite/schema/organizationSyncPauseSchema.js.map +1 -0
  27. package/data/sync/organizationSyncPauses.d.ts +59 -0
  28. package/data/sync/organizationSyncPauses.js +149 -0
  29. package/data/sync/organizationSyncPauses.js.map +1 -0
  30. package/index.d.ts +2 -2
  31. package/index.js.map +1 -1
  32. package/internal/api-client/ApiClient.d.ts +11 -2
  33. package/internal/api-client/ApiClient.js +9 -1
  34. package/internal/api-client/ApiClient.js.map +1 -1
  35. package/internal/api-client/routes/organizations/memberships.js +17 -0
  36. package/internal/api-client/routes/organizations/memberships.js.map +1 -0
  37. package/internal/validators/operation/index.d.ts +1 -0
  38. package/internal/validators/operation/index.js +1 -0
  39. package/internal/validators/operation/index.js.map +1 -1
  40. package/internal/validators/operation/organizationMemberships.d.ts +240 -0
  41. package/internal/validators/operation/organizationMemberships.js +51 -0
  42. package/internal/validators/operation/organizationMemberships.js.map +1 -0
  43. package/internal/validators/operation/registry.d.ts +3 -0
  44. package/internal/validators/operation/registry.js +3 -0
  45. package/internal/validators/operation/registry.js.map +1 -1
  46. package/internal/validators/realtime/index.js +1 -1
  47. package/internal/validators/realtime/index.js.map +1 -1
  48. package/internal/validators/realtime/wsInvalidationHints.js +8 -0
  49. package/internal/validators/realtime/wsInvalidationHints.js.map +1 -1
  50. package/internal/validators/request/index.d.ts +1 -1
  51. package/internal/validators/request/index.js +1 -1
  52. package/internal/validators/request/index.js.map +1 -1
  53. package/internal/validators/request/organization.d.ts +6 -0
  54. package/internal/validators/request/organization.js +7 -0
  55. package/internal/validators/request/organization.js.map +1 -1
  56. package/internal/validators/response/index.d.ts +1 -0
  57. package/internal/validators/response/index.js +1 -0
  58. package/internal/validators/response/index.js.map +1 -1
  59. package/internal/validators/response/organizationMembership.d.ts +56 -0
  60. package/internal/validators/response/organizationMembership.js +26 -0
  61. package/internal/validators/response/organizationMembership.js.map +1 -0
  62. package/package.json +1 -1
  63. package/stores/container-contents/containerContentsStore.d.ts +1 -1
  64. package/stores/container-contents/containerContentsStore.js +2 -0
  65. package/stores/container-contents/containerContentsStore.js.map +1 -1
  66. package/stores/container-contents/operations/forgetOrganizationContentOperation.js +76 -0
  67. package/stores/container-contents/operations/forgetOrganizationContentOperation.js.map +1 -0
  68. package/stores/container-contents/types.d.ts +6 -0
  69. package/stores/container-contents/types.js.map +1 -1
  70. package/stores/documents/documentStore/attachments/attachmentRemoval.js +3 -4
  71. package/stores/documents/documentStore/attachments/attachmentRemoval.js.map +1 -1
  72. package/stores/documents/documentStore/attachments/index.js +5 -7
  73. package/stores/documents/documentStore/attachments/index.js.map +1 -1
  74. package/stores/documents/documentStore/index.js +16 -12
  75. package/stores/documents/documentStore/index.js.map +1 -1
  76. package/stores/documents/documentStore/initialization/index.js +9 -0
  77. package/stores/documents/documentStore/initialization/index.js.map +1 -1
  78. package/stores/documents/documentStore/mutations.js +5 -7
  79. package/stores/documents/documentStore/mutations.js.map +1 -1
  80. package/stores/documents/documentStore/rotation/index.js +2 -1
  81. package/stores/documents/documentStore/rotation/index.js.map +1 -1
  82. package/stores/documents/documentStore/rows/index.js +3 -4
  83. package/stores/documents/documentStore/rows/index.js.map +1 -1
  84. package/stores/documents/documentStore/state.js +18 -0
  85. package/stores/documents/documentStore/state.js.map +1 -1
  86. package/stores/documents/documentStore/sync/index.js +5 -2
  87. package/stores/documents/documentStore/sync/index.js.map +1 -1
  88. package/stores/documents/registry.d.ts +32 -0
  89. package/stores/documents/registry.js +79 -4
  90. package/stores/documents/registry.js.map +1 -1
  91. package/stores/documents/removedDocumentStore.js +38 -0
  92. package/stores/documents/removedDocumentStore.js.map +1 -0
  93. package/stores/documents/types.d.ts +2 -0
  94. package/stores/documents/types.js.map +1 -1
  95. package/sync/reconciliation/service.js +2 -2
  96. package/sync/reconciliation/service.js.map +1 -1
  97. package/sync/reconciliation/serviceTypes.d.ts +8 -1
  98. package/sync/reconciliation/serviceTypes.js.map +1 -1
  99. package/workflows/container-contents/pendingWrites/index.d.ts +5 -0
  100. package/workflows/container-contents/pendingWrites/index.js +7 -0
  101. package/workflows/container-contents/pendingWrites/index.js.map +1 -1
  102. package/workflows/container-contents/remoteHydration/index.js +7 -1
  103. package/workflows/container-contents/remoteHydration/index.js.map +1 -1
  104. package/workflows/container-contents/remoteHydration/remoteContainers/organizationSyncPauseFilter.js +13 -0
  105. package/workflows/container-contents/remoteHydration/remoteContainers/organizationSyncPauseFilter.js.map +1 -0
  106. package/workflows/container-contents/remoteHydration/remoteContainers/remoteContainerPageHydration.js +13 -2
  107. package/workflows/container-contents/remoteHydration/remoteContainers/remoteContainerPageHydration.js.map +1 -1
  108. package/workflows/container-contents/syncPause/discoveryGuard.js +31 -0
  109. package/workflows/container-contents/syncPause/discoveryGuard.js.map +1 -0
  110. package/workflows/container-contents/syncPause/documentRemoval.js +154 -0
  111. package/workflows/container-contents/syncPause/documentRemoval.js.map +1 -0
  112. package/workflows/organizations/index.d.ts +1 -0
  113. package/workflows/organizations/index.js +1 -0
  114. package/workflows/organizations/index.js.map +1 -1
  115. package/workflows/organizations/memberships/organizationMemberships.d.ts +84 -0
  116. package/workflows/organizations/memberships/organizationMemberships.js +95 -0
  117. package/workflows/organizations/memberships/organizationMemberships.js.map +1 -0
  118. package/workflows/sync/index.d.ts +1 -0
  119. package/workflows/sync/index.js +1 -0
  120. package/workflows/sync/index.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"AAcA,MAAM,CAAC,MAAM,mBAAmB,GAAG,SAAS,CAAC","sourcesContent":["import type { ContainerAccessLevel } from \"@tearleads/crypto\";\nimport type { DocumentAttachment } from \"../../data/documents/documentContent\";\nimport type {\n DocumentFieldValidationIssue,\n StoredDocumentKind,\n} from \"../../data/documents/documentKinds\";\nimport type { DocumentRow } from \"../../data/documents/documentRowList\";\nimport type { DocumentSummary } from \"../../data/documents/documentSummary\";\nimport type { BlobByteSourceInput } from \"../../workflows/blobs\";\nimport type {\n DocumentsWorkflowRuntime,\n RelinkPersistedDocumentInput,\n} from \"../../workflows/documents\";\n\nexport const DEFAULT_DOCUMENT_ID = \"default\";\n\nexport type DocumentsRuntime = DocumentsWorkflowRuntime;\nexport type DocumentStructuredFieldPatch = Readonly<\n Record<string, string | undefined>\n>;\n\nexport interface DocumentMutationOptions {\n deferRemoteSync?: boolean | undefined;\n}\n\nexport interface DocumentStoreOpenOptions {\n /**\n * The caller attaches a file immediately after creating the document. The\n * birth commits locally without its own pending update, so the attach's\n * delta carries it; a failed attach enqueues the birth on its own.\n */\n deferInitialUpdate?: boolean | undefined;\n /**\n * Rows and structured fields a newly created record starts with. They are\n * written into its birth update with the initial kind and text, so a\n * document whose content is known up front queues one update. Fields need a\n * non-note `initialDocumentKind`. Both are ignored when the record exists.\n */\n initialRows?: ReadonlyArray<Readonly<Record<string, string>>> | undefined;\n initialStructuredFields?: DocumentStructuredFieldPatch | undefined;\n remoteSyncMode?: \"on-demand\" | \"startup\" | undefined;\n}\n\nexport interface DocumentAttachmentUpload {\n bytes: BlobByteSourceInput;\n name: string;\n mimeType: string | null;\n}\n\n// `syncing`: the slot's upload is still queued. `intent-mismatch`: the held\n// bytes are a validly signed served binding whose digest differs from the one\n// the document content records (its content update has not arrived).\nexport type DocumentAttachmentStatus = \"syncing\" | \"intent-mismatch\";\n\n// Add a row to the document's repeated-row list, returning the new row id.\nexport type AddDocumentRow = (\n fields: Readonly<Record<string, string>>,\n) => Promise<string>;\n// Patch one or more cells of a single row (merges per-cell).\nexport type UpdateDocumentRowFields = (\n id: string,\n patch: Readonly<Record<string, string>>,\n) => Promise<void>;\nexport type RemoveDocumentRow = (id: string) => Promise<void>;\n\n// The store surface exposed through context: the snapshot fields plus the\n// store's mutation methods, spelled as a Pick so the two can't drift.\nexport type DocumentContextValue = DocumentSnapshot &\n Pick<\n DocumentStore,\n | \"addRow\"\n | \"attachFiles\"\n | \"relink\"\n | \"removeAttachment\"\n | \"removeRow\"\n | \"replaceAttachment\"\n | \"requestSync\"\n | \"setStructuredFields\"\n | \"setText\"\n | \"updateRowFields\"\n >;\n\nexport interface DocumentSnapshot {\n attachments: ReadonlyArray<DocumentAttachment>;\n attachmentStatusBySlotId: Readonly<Record<string, DocumentAttachmentStatus>>;\n attachmentStorageKeyBySlotId: Readonly<Record<string, string>>;\n canAttach: boolean;\n canWrite: boolean;\n currentAuthorId: string | null;\n documentId: string | null;\n documentKind: StoredDocumentKind;\n effectiveAccessLevel: ContainerAccessLevel;\n fieldValidationIssues: ReadonlyArray<DocumentFieldValidationIssue>;\n ready: boolean;\n rows: ReadonlyArray<DocumentRow>;\n structuredFields: Readonly<Record<string, string>>;\n text: string;\n title: string;\n syncing: boolean;\n}\n\nexport interface DocumentStore {\n addRow: AddDocumentRow;\n /** Fail unless this store can produce a mergeable full-history checkpoint. */\n assertCanRotateContentKey: () => Promise<Uint8Array>;\n attachFiles: (files: ReadonlyArray<DocumentAttachmentUpload>) => void;\n /**\n * Convert this document's persisted local state to the discovered-share\n * shell so the store re-pulls the server copy — the escape hatch for\n * queued local writes that can no longer sync. `expectedDocumentId` is\n * revalidated against the persisted record inside the teardown's\n * serialized mutation, so a stale caller can never discard a different\n * identity's edits. Refuses (returns false) local-only, unlinked,\n * move-pending, and identity-mismatched documents.\n */\n discardLocalState: (expectedDocumentId: string) => Promise<boolean>;\n ensureInitialized: () => Promise<boolean>;\n getSnapshot: () => DocumentSnapshot;\n removeAttachment: (slotId: string) => void;\n removeRow: RemoveDocumentRow;\n replaceAttachment: (\n slotId: string,\n file: DocumentAttachmentUpload,\n ) => Promise<void>;\n /** Pull remote document updates even when no websocket event marked it dirty. */\n requestRemoteSync: () => void;\n /** Pull remote updates and report whether the requested sync pass completed. */\n requestRemoteSyncAndWait: (\n signal?: AbortSignal | undefined,\n ) => Promise<boolean>;\n requestSync: () => void;\n relink: (input: DocumentStoreRelinkInput) => Promise<DocumentSummary | null>;\n setStructuredFields: (\n kind: Exclude<StoredDocumentKind, \"note\">,\n patch: DocumentStructuredFieldPatch,\n options?: DocumentMutationOptions | undefined,\n ) => Promise<void>;\n setText: (value: string) => Promise<void>;\n subscribe: (listener: () => void) => () => void;\n updateRowFields: UpdateDocumentRowFields;\n updateRuntime: (runtime: DocumentsRuntime) => void;\n}\n\nexport interface DocumentStoreFacade extends DocumentStore {\n rebindTo: (store: DocumentStore) => void;\n}\n\nexport type PersistedDocumentListener = (\n document: DocumentSummary,\n change: { readonly placementChanged: boolean },\n) => void;\n\nexport interface DocumentStoreRelinkInput extends RelinkPersistedDocumentInput {\n contentKeyBundle?: string | null | undefined;\n documentKekTargets?: string | null | undefined;\n documentManifestBundle?: string | null | undefined;\n stillCurrent?: (() => boolean) | undefined;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"AAcA,MAAM,CAAC,MAAM,mBAAmB,GAAG,SAAS,CAAC","sourcesContent":["import type { ContainerAccessLevel } from \"@tearleads/crypto\";\nimport type { DocumentAttachment } from \"../../data/documents/documentContent\";\nimport type {\n DocumentFieldValidationIssue,\n StoredDocumentKind,\n} from \"../../data/documents/documentKinds\";\nimport type { DocumentRow } from \"../../data/documents/documentRowList\";\nimport type { DocumentSummary } from \"../../data/documents/documentSummary\";\nimport type { BlobByteSourceInput } from \"../../workflows/blobs\";\nimport type {\n DocumentsWorkflowRuntime,\n RelinkPersistedDocumentInput,\n} from \"../../workflows/documents\";\n\nexport const DEFAULT_DOCUMENT_ID = \"default\";\n\nexport type DocumentsRuntime = DocumentsWorkflowRuntime;\nexport type DocumentStructuredFieldPatch = Readonly<\n Record<string, string | undefined>\n>;\n\nexport interface DocumentMutationOptions {\n deferRemoteSync?: boolean | undefined;\n}\n\nexport interface DocumentStoreOpenOptions {\n /**\n * The caller attaches a file immediately after creating the document. The\n * birth commits locally without its own pending update, so the attach's\n * delta carries it; a failed attach enqueues the birth on its own.\n */\n deferInitialUpdate?: boolean | undefined;\n /**\n * Rows and structured fields a newly created record starts with. They are\n * written into its birth update with the initial kind and text, so a\n * document whose content is known up front queues one update. Fields need a\n * non-note `initialDocumentKind`. Both are ignored when the record exists.\n */\n initialRows?: ReadonlyArray<Readonly<Record<string, string>>> | undefined;\n initialStructuredFields?: DocumentStructuredFieldPatch | undefined;\n remoteSyncMode?: \"on-demand\" | \"startup\" | undefined;\n}\n\nexport interface DocumentAttachmentUpload {\n bytes: BlobByteSourceInput;\n name: string;\n mimeType: string | null;\n}\n\n// `syncing`: the slot's upload is still queued. `intent-mismatch`: the held\n// bytes are a validly signed served binding whose digest differs from the one\n// the document content records (its content update has not arrived).\nexport type DocumentAttachmentStatus = \"syncing\" | \"intent-mismatch\";\n\n// Add a row to the document's repeated-row list, returning the new row id.\nexport type AddDocumentRow = (\n fields: Readonly<Record<string, string>>,\n) => Promise<string>;\n// Patch one or more cells of a single row (merges per-cell).\nexport type UpdateDocumentRowFields = (\n id: string,\n patch: Readonly<Record<string, string>>,\n) => Promise<void>;\nexport type RemoveDocumentRow = (id: string) => Promise<void>;\n\n// The store surface exposed through context: the snapshot fields plus the\n// store's mutation methods, spelled as a Pick so the two can't drift.\nexport type DocumentContextValue = DocumentSnapshot &\n Pick<\n DocumentStore,\n | \"addRow\"\n | \"attachFiles\"\n | \"relink\"\n | \"removeAttachment\"\n | \"removeRow\"\n | \"replaceAttachment\"\n | \"requestSync\"\n | \"setStructuredFields\"\n | \"setText\"\n | \"updateRowFields\"\n >;\n\nexport interface DocumentSnapshot {\n attachments: ReadonlyArray<DocumentAttachment>;\n attachmentStatusBySlotId: Readonly<Record<string, DocumentAttachmentStatus>>;\n attachmentStorageKeyBySlotId: Readonly<Record<string, string>>;\n canAttach: boolean;\n canWrite: boolean;\n currentAuthorId: string | null;\n documentId: string | null;\n documentKind: StoredDocumentKind;\n effectiveAccessLevel: ContainerAccessLevel;\n fieldValidationIssues: ReadonlyArray<DocumentFieldValidationIssue>;\n ready: boolean;\n rows: ReadonlyArray<DocumentRow>;\n structuredFields: Readonly<Record<string, string>>;\n text: string;\n title: string;\n syncing: boolean;\n}\n\nexport interface DocumentStore {\n addRow: AddDocumentRow;\n /** Fail unless this store can produce a mergeable full-history checkpoint. */\n assertCanRotateContentKey: () => Promise<Uint8Array>;\n attachFiles: (files: ReadonlyArray<DocumentAttachmentUpload>) => void;\n /**\n * Convert this document's persisted local state to the discovered-share\n * shell so the store re-pulls the server copy — the escape hatch for\n * queued local writes that can no longer sync. `expectedDocumentId` is\n * revalidated against the persisted record inside the teardown's\n * serialized mutation, so a stale caller can never discard a different\n * identity's edits. Refuses (returns false) local-only, unlinked,\n * move-pending, and identity-mismatched documents.\n */\n discardLocalState: (expectedDocumentId: string) => Promise<boolean>;\n ensureInitialized: () => Promise<boolean>;\n getSnapshot: () => DocumentSnapshot;\n /** Whether local writes are queued that have not reached SQLite yet. */\n hasQueuedWrites: () => boolean;\n removeAttachment: (slotId: string) => void;\n removeRow: RemoveDocumentRow;\n replaceAttachment: (\n slotId: string,\n file: DocumentAttachmentUpload,\n ) => Promise<void>;\n /** Pull remote document updates even when no websocket event marked it dirty. */\n requestRemoteSync: () => void;\n /** Pull remote updates and report whether the requested sync pass completed. */\n requestRemoteSyncAndWait: (\n signal?: AbortSignal | undefined,\n ) => Promise<boolean>;\n requestSync: () => void;\n relink: (input: DocumentStoreRelinkInput) => Promise<DocumentSummary | null>;\n setStructuredFields: (\n kind: Exclude<StoredDocumentKind, \"note\">,\n patch: DocumentStructuredFieldPatch,\n options?: DocumentMutationOptions | undefined,\n ) => Promise<void>;\n setText: (value: string) => Promise<void>;\n subscribe: (listener: () => void) => () => void;\n updateRowFields: UpdateDocumentRowFields;\n updateRuntime: (runtime: DocumentsRuntime) => void;\n}\n\nexport interface DocumentStoreFacade extends DocumentStore {\n rebindTo: (store: DocumentStore) => void;\n}\n\nexport type PersistedDocumentListener = (\n document: DocumentSummary,\n change: { readonly placementChanged: boolean },\n) => void;\n\nexport interface DocumentStoreRelinkInput extends RelinkPersistedDocumentInput {\n contentKeyBundle?: string | null | undefined;\n documentKekTargets?: string | null | undefined;\n documentManifestBundle?: string | null | undefined;\n stillCurrent?: (() => boolean) | undefined;\n}\n"]}
@@ -309,8 +309,8 @@ export function createReconciliationService(host) {
309
309
  listContainerIds: host.listAutomaticRootCatchupContainerIds,
310
310
  scope: "root",
311
311
  }),
312
- reconcileNow: () => requestContainerRefresh({
313
- freshListing: false,
312
+ reconcileNow: (options) => requestContainerRefresh({
313
+ freshListing: options?.freshListing === true,
314
314
  host,
315
315
  state,
316
316
  refreshTree: host.refreshTree,
@@ -1 +1 @@
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
+ {"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: (options) =>\n requestContainerRefresh({\n freshListing: options?.freshListing === true,\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"]}
@@ -102,6 +102,13 @@ export interface ReconciliationService {
102
102
  reconcileRootContainersNow: (options?: {
103
103
  readonly freshListing?: boolean;
104
104
  }) => Promise<void>;
105
- reconcileNow: () => Promise<void>;
105
+ /**
106
+ * Refresh the whole tree and reconcile every known container. With
107
+ * `freshListing`, never join a sweep that already started listing: one
108
+ * queues behind it instead, so the listing reflects a change made after.
109
+ */
110
+ reconcileNow: (options?: {
111
+ readonly freshListing?: boolean;
112
+ }) => Promise<void>;
106
113
  stop: () => void;
107
114
  }
@@ -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/shared/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
+ {"version":3,"file":"serviceTypes.js","sourceRoot":"","sources":["serviceTypes.ts"],"names":[],"mappings":"","sourcesContent":["import type { DocumentSummary } from \"../../data/documents/documentSummary\";\nimport type { DomainScope } from \"../../data/shared/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 /**\n * Refresh the whole tree and reconcile every known container. With\n * `freshListing`, never join a sweep that already started listing: one\n * queues behind it instead, so the listing reflects a change made after.\n */\n reconcileNow: (options?: {\n readonly freshListing?: boolean;\n }) => Promise<void>;\n stop: () => void;\n}\n"]}
@@ -1,6 +1,11 @@
1
1
  import type { ExecSql } from "../../../data/sqlite/sqlSchema.js";
2
2
  import type { PendingWriteQueueItem } from "./pendingWritesTypes.js";
3
3
  export type { PendingWriteQueueItem, PendingWriteQueueItemStatus, PendingWriteQueueObjectKind, PendingWriteQueueOperation, PendingWriteQueueOperationKind, PendingWriteQueueOperationStatus, } from "./pendingWritesTypes.js";
4
+ /**
5
+ * Whether an item holds local work that still has to upload. A revalidation
6
+ * item only reports a failed read and carries no local writes.
7
+ */
8
+ export declare function carriesLocalWrites(item: PendingWriteQueueItem): boolean;
4
9
  /**
5
10
  * List identity-wide local state that still requires a remote write. The
6
11
  * returned diagnostic contains only navigation/display metadata and aggregate
@@ -9,6 +9,13 @@ import { defaultContainerContentsPersistence } from "../containerPersistence.js"
9
9
  import { groupPendingWriteCandidates } from "./aggregation.js";
10
10
  import { hasDeferredTailScopeWork, listDeferredPendingWriteCandidates, } from "./deferredTails.js";
11
11
  import { listPersistedPendingWriteCandidates } from "./sources.js";
12
+ /**
13
+ * Whether an item holds local work that still has to upload. A revalidation
14
+ * item only reports a failed read and carries no local writes.
15
+ */
16
+ export function carriesLocalWrites(item) {
17
+ return item.operations.some((operation) => operation.kind !== "revalidation");
18
+ }
12
19
  /**
13
20
  * List identity-wide local state that still requires a remote write. The
14
21
  * returned diagnostic contains only navigation/display metadata and aggregate
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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","sourcesContent":["import { DOCUMENTS_APP_KIND } from \"../../../data/documents/documentConstants\";\nimport { CONTAINER_METADATA_APP_KIND } from \"../../../data/persistence/container-contents/containerContentsPersistence\";\nimport { sqlDocumentMoveIntentPersistence } from \"../../../data/persistence/container-contents/documentMoveIntentPersistence\";\nimport { sqlDocumentsPersistence } from \"../../../data/persistence/documents/documentsPersistence\";\nimport {\n clearDocumentSyncFailure,\n resetDocumentPendingUpdateRekeyBudget,\n} from \"../../../data/persistence/documents/records\";\nimport {\n documentPlacementIntentTables,\n documentTables,\n LOCAL_INTENT_LANES,\n} from \"../../../data/sqlite/schema\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport { ensureSqlTables } from \"../../../data/sqlite/sqlSchema\";\nimport { defaultContainerContentsPersistence } from \"../containerPersistence\";\nimport { groupPendingWriteCandidates } from \"./aggregation\";\nimport {\n hasDeferredTailScopeWork,\n listDeferredPendingWriteCandidates,\n} from \"./deferredTails\";\nimport type { PendingWriteQueueItem } from \"./pendingWritesTypes\";\nimport { listPersistedPendingWriteCandidates } from \"./sources\";\n\nexport type {\n PendingWriteQueueItem,\n PendingWriteQueueItemStatus,\n PendingWriteQueueObjectKind,\n PendingWriteQueueOperation,\n PendingWriteQueueOperationKind,\n PendingWriteQueueOperationStatus,\n} from \"./pendingWritesTypes\";\n\n/**\n * List identity-wide local state that still requires a remote write. The\n * returned diagnostic contains only navigation/display metadata and aggregate\n * counts; serialized updates, local storage keys, and upload crypto stay below\n * the workflow boundary.\n */\nexport async function listPendingWrites(\n execSql: ExecSql,\n): Promise<ReadonlyArray<PendingWriteQueueItem>> {\n await defaultContainerContentsPersistence.ensureSchema(execSql);\n await sqlDocumentsPersistence.ensureSchema(execSql);\n await sqlDocumentMoveIntentPersistence.ensureSchema(execSql);\n\n const [persisted, deferred] = await Promise.all([\n listPersistedPendingWriteCandidates(execSql),\n listDeferredPendingWriteCandidates(execSql),\n ]);\n return groupPendingWriteCandidates([...persisted, ...deferred]);\n}\n\n/**\n * Explicit manual retry for one write-queue item: reset the durable re-key\n * budget for the item's ACTUAL pending-update scope (container metadata and\n * unknown document namespaces re-key through the same recovery path as\n * ordinary documents) and clear the recorded terminal failure — the next\n * pass re-records both if they still hold. The cap protects against a\n * server driving a network-speed re-key loop; a deliberate user retry is\n * the rate-limited signal that conditions have changed (e.g. an outage that\n * burned the budget has healed). The caller re-arms the sync lanes.\n */\nexport async function resetPendingWriteRetryState(\n execSql: ExecSql,\n input: {\n localId: string;\n /** The item's `namespace` — the unrecognized documents.app_kind, if any. */\n namespace: string | null;\n objectKind: PendingWriteQueueItem[\"objectKind\"];\n },\n): Promise<void> {\n const appKind =\n input.objectKind === \"container\"\n ? CONTAINER_METADATA_APP_KIND\n : (input.namespace ??\n (input.objectKind === \"document\" ? DOCUMENTS_APP_KIND : null));\n if (appKind === null) {\n // An unknown object kind with no namespace: there is no scope to reset,\n // and guessing one could clear an unrelated scope sharing the localId.\n return;\n }\n const scope = { appKind, localId: input.localId };\n await resetDocumentPendingUpdateRekeyBudget(execSql, scope);\n if (appKind === DOCUMENTS_APP_KIND) {\n // A deliberate retry is the rate-limited signal that conditions changed:\n // a parked permission-denied move (row 7) replays once, like row 11's\n // re-key budget reset.\n await sqlDocumentMoveIntentPersistence.resetDeniedMoveIntents(execSql, {\n localId: input.localId,\n });\n }\n // Clearing the failure row is part of the re-key budget reset (edge-case\n // row 11) and only makes sense alongside queued work. A failure-only item\n // (a refused revalidation, row 13) has no queued work: its row is the\n // priming ticket that re-drives the store, so it must survive the retry\n // and clear on the next clean pass instead of vanishing with no request.\n if (await hasQueuedScopeWork(execSql, scope)) {\n await clearDocumentSyncFailure(execSql, scope);\n }\n}\n\n/**\n * Whether ANY durable outbound work exists for the scope — CRDT updates,\n * staged attachment uploads, move intents, a never-created local document, or\n * a deferred write whose only durable content is the history tail ahead of\n * its covered frontier. Mirrors the queue's own document candidate sources,\n * so \"failure-only\" here agrees with the item the panel rendered.\n */\nasync function hasQueuedScopeWork(\n execSql: ExecSql,\n scope: { appKind: string; localId: string },\n): Promise<boolean> {\n await ensureSqlTables(execSql, [\n ...documentTables,\n ...documentPlacementIntentTables,\n ]);\n const rows = await execSql(\n `SELECT 1 AS present\n WHERE EXISTS (\n SELECT 1 FROM document_history_updates pending\n WHERE pending.app_kind = ? AND pending.local_id = ?\n AND pending.update_id IS NOT NULL\n )\n OR EXISTS (\n SELECT 1 FROM document_pending_attachments attachment\n WHERE attachment.local_id = ?\n )\n OR EXISTS (\n SELECT 1 FROM local_intents intent\n WHERE intent.lane = '${LOCAL_INTENT_LANES.documentPlacement}'\n AND intent.local_id = ?\n AND intent.intent_type IN ('document.move', 'document.link')\n )\n OR EXISTS (\n SELECT 1 FROM documents stored\n WHERE stored.app_kind = ? AND stored.local_id = ?\n AND stored.document_id IS NULL\n )\n LIMIT 1`,\n [\n scope.appKind,\n scope.localId,\n scope.localId,\n scope.localId,\n scope.appKind,\n scope.localId,\n ],\n );\n if (rows.length > 0) {\n return true;\n }\n // The queue's deferred-update source has no queued rows to probe: the\n // write lives in the durable history tail, detectable only by the\n // version-vector coverage check.\n return hasDeferredTailScopeWork(execSql, scope);\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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","sourcesContent":["import { DOCUMENTS_APP_KIND } from \"../../../data/documents/documentConstants\";\nimport { CONTAINER_METADATA_APP_KIND } from \"../../../data/persistence/container-contents/containerContentsPersistence\";\nimport { sqlDocumentMoveIntentPersistence } from \"../../../data/persistence/container-contents/documentMoveIntentPersistence\";\nimport { sqlDocumentsPersistence } from \"../../../data/persistence/documents/documentsPersistence\";\nimport {\n clearDocumentSyncFailure,\n resetDocumentPendingUpdateRekeyBudget,\n} from \"../../../data/persistence/documents/records\";\nimport {\n documentPlacementIntentTables,\n documentTables,\n LOCAL_INTENT_LANES,\n} from \"../../../data/sqlite/schema\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport { ensureSqlTables } from \"../../../data/sqlite/sqlSchema\";\nimport { defaultContainerContentsPersistence } from \"../containerPersistence\";\nimport { groupPendingWriteCandidates } from \"./aggregation\";\nimport {\n hasDeferredTailScopeWork,\n listDeferredPendingWriteCandidates,\n} from \"./deferredTails\";\nimport type { PendingWriteQueueItem } from \"./pendingWritesTypes\";\nimport { listPersistedPendingWriteCandidates } from \"./sources\";\n\nexport type {\n PendingWriteQueueItem,\n PendingWriteQueueItemStatus,\n PendingWriteQueueObjectKind,\n PendingWriteQueueOperation,\n PendingWriteQueueOperationKind,\n PendingWriteQueueOperationStatus,\n} from \"./pendingWritesTypes\";\n\n/**\n * Whether an item holds local work that still has to upload. A revalidation\n * item only reports a failed read and carries no local writes.\n */\nexport function carriesLocalWrites(item: PendingWriteQueueItem): boolean {\n return item.operations.some((operation) => operation.kind !== \"revalidation\");\n}\n\n/**\n * List identity-wide local state that still requires a remote write. The\n * returned diagnostic contains only navigation/display metadata and aggregate\n * counts; serialized updates, local storage keys, and upload crypto stay below\n * the workflow boundary.\n */\nexport async function listPendingWrites(\n execSql: ExecSql,\n): Promise<ReadonlyArray<PendingWriteQueueItem>> {\n await defaultContainerContentsPersistence.ensureSchema(execSql);\n await sqlDocumentsPersistence.ensureSchema(execSql);\n await sqlDocumentMoveIntentPersistence.ensureSchema(execSql);\n\n const [persisted, deferred] = await Promise.all([\n listPersistedPendingWriteCandidates(execSql),\n listDeferredPendingWriteCandidates(execSql),\n ]);\n return groupPendingWriteCandidates([...persisted, ...deferred]);\n}\n\n/**\n * Explicit manual retry for one write-queue item: reset the durable re-key\n * budget for the item's ACTUAL pending-update scope (container metadata and\n * unknown document namespaces re-key through the same recovery path as\n * ordinary documents) and clear the recorded terminal failure — the next\n * pass re-records both if they still hold. The cap protects against a\n * server driving a network-speed re-key loop; a deliberate user retry is\n * the rate-limited signal that conditions have changed (e.g. an outage that\n * burned the budget has healed). The caller re-arms the sync lanes.\n */\nexport async function resetPendingWriteRetryState(\n execSql: ExecSql,\n input: {\n localId: string;\n /** The item's `namespace` — the unrecognized documents.app_kind, if any. */\n namespace: string | null;\n objectKind: PendingWriteQueueItem[\"objectKind\"];\n },\n): Promise<void> {\n const appKind =\n input.objectKind === \"container\"\n ? CONTAINER_METADATA_APP_KIND\n : (input.namespace ??\n (input.objectKind === \"document\" ? DOCUMENTS_APP_KIND : null));\n if (appKind === null) {\n // An unknown object kind with no namespace: there is no scope to reset,\n // and guessing one could clear an unrelated scope sharing the localId.\n return;\n }\n const scope = { appKind, localId: input.localId };\n await resetDocumentPendingUpdateRekeyBudget(execSql, scope);\n if (appKind === DOCUMENTS_APP_KIND) {\n // A deliberate retry is the rate-limited signal that conditions changed:\n // a parked permission-denied move (row 7) replays once, like row 11's\n // re-key budget reset.\n await sqlDocumentMoveIntentPersistence.resetDeniedMoveIntents(execSql, {\n localId: input.localId,\n });\n }\n // Clearing the failure row is part of the re-key budget reset (edge-case\n // row 11) and only makes sense alongside queued work. A failure-only item\n // (a refused revalidation, row 13) has no queued work: its row is the\n // priming ticket that re-drives the store, so it must survive the retry\n // and clear on the next clean pass instead of vanishing with no request.\n if (await hasQueuedScopeWork(execSql, scope)) {\n await clearDocumentSyncFailure(execSql, scope);\n }\n}\n\n/**\n * Whether ANY durable outbound work exists for the scope — CRDT updates,\n * staged attachment uploads, move intents, a never-created local document, or\n * a deferred write whose only durable content is the history tail ahead of\n * its covered frontier. Mirrors the queue's own document candidate sources,\n * so \"failure-only\" here agrees with the item the panel rendered.\n */\nasync function hasQueuedScopeWork(\n execSql: ExecSql,\n scope: { appKind: string; localId: string },\n): Promise<boolean> {\n await ensureSqlTables(execSql, [\n ...documentTables,\n ...documentPlacementIntentTables,\n ]);\n const rows = await execSql(\n `SELECT 1 AS present\n WHERE EXISTS (\n SELECT 1 FROM document_history_updates pending\n WHERE pending.app_kind = ? AND pending.local_id = ?\n AND pending.update_id IS NOT NULL\n )\n OR EXISTS (\n SELECT 1 FROM document_pending_attachments attachment\n WHERE attachment.local_id = ?\n )\n OR EXISTS (\n SELECT 1 FROM local_intents intent\n WHERE intent.lane = '${LOCAL_INTENT_LANES.documentPlacement}'\n AND intent.local_id = ?\n AND intent.intent_type IN ('document.move', 'document.link')\n )\n OR EXISTS (\n SELECT 1 FROM documents stored\n WHERE stored.app_kind = ? AND stored.local_id = ?\n AND stored.document_id IS NULL\n )\n LIMIT 1`,\n [\n scope.appKind,\n scope.localId,\n scope.localId,\n scope.localId,\n scope.appKind,\n scope.localId,\n ],\n );\n if (rows.length > 0) {\n return true;\n }\n // The queue's deferred-update source has no queued rows to probe: the\n // write lives in the durable history tail, detectable only by the\n // version-vector coverage check.\n return hasDeferredTailScopeWork(execSql, scope);\n}\n"]}
@@ -3,6 +3,7 @@ import { finishRemoteHydration } from "./completeHydration.js";
3
3
  import { createContainerChildIndex } from "./lanes/childIndex.js";
4
4
  import { createContainerParentHydrationQueue } from "./lanes/parentLaneQueue.js";
5
5
  import { cacheRemoteContainerPrincipalPolicies } from "./principalPolicyCache.js";
6
+ import { loadPausedContainerFilter } from "./remoteContainers/organizationSyncPauseFilter.js";
6
7
  import { canHydrateRemoteContainers, hydrateContainerParentLanes, } from "./remoteContainers/remoteContainerPageHydration.js";
7
8
  import { listRemoteContainerIdsWithPendingMetadataUpdates, listRemoteContainerIdsWithPendingStructuralIntents, upsertIsolatedRemoteContainerState, } from "./remoteContainers/remoteContainerState.js";
8
9
  export class StaleRemoteHydrationError extends Error {
@@ -61,6 +62,11 @@ async function upsertQueuedRemoteContainer(input) {
61
62
  !isCurrentQueuedRemoteContainer(generation, generationByContainerId, queue, queuedRemoteContainer)) {
62
63
  return false;
63
64
  }
65
+ // A paused organization's container is acknowledged without being stored,
66
+ // including when the pause lands while the commit is in flight.
67
+ const isPaused = await loadPausedContainerFilter(state);
68
+ if (isPaused(queuedRemoteContainer))
69
+ return true;
64
70
  const tombstones = await state.persistence.loadContainerHydrationTombstones(state.runtime.infra.execSql);
65
71
  // A refused folder is reported and acknowledged; the batch continues.
66
72
  const upserted = await upsertIsolatedRemoteContainerState({
@@ -69,7 +75,7 @@ async function upsertQueuedRemoteContainer(input) {
69
75
  containerIdsWithPendingMetadataUpdates,
70
76
  containerIdsWithPendingStructuralIntents,
71
77
  host,
72
- isCurrent: input.isCurrent,
78
+ isCurrent: () => input.isCurrent() && !isPaused(queuedRemoteContainer),
73
79
  remoteContainer: queuedRemoteContainer,
74
80
  state,
75
81
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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","sourcesContent":["import { createRuntimePrincipalPolicyWarmer } from \"../../principals/runtimePolicyWarmer\";\nimport { finishRemoteHydration } from \"./completeHydration\";\nimport { createContainerChildIndex } from \"./lanes/childIndex\";\nimport { createContainerParentHydrationQueue } from \"./lanes/parentLaneQueue\";\nimport { cacheRemoteContainerPrincipalPolicies } from \"./principalPolicyCache\";\nimport {\n canHydrateRemoteContainers,\n hydrateContainerParentLanes,\n} from \"./remoteContainers/remoteContainerPageHydration\";\nimport {\n listRemoteContainerIdsWithPendingMetadataUpdates,\n listRemoteContainerIdsWithPendingStructuralIntents,\n upsertIsolatedRemoteContainerState,\n} from \"./remoteContainers/remoteContainerState\";\nimport type {\n QueueContainerParentLane,\n RemoteContainer,\n RemoteContainerHydrationHost,\n RemoteContainerHydrationState,\n RemoteContainerIngestQueue,\n} from \"./types\";\n\nexport type {\n ContainerState,\n PersistContainerStateResult,\n RemoteContainer,\n RemoteContainerHydrationHost,\n} from \"./types\";\n\nexport class StaleRemoteHydrationError extends Error {}\n\nexport function createGenerationGuardedHydrationHost(input: {\n host: RemoteContainerHydrationHost;\n isCurrent: () => boolean;\n}): RemoteContainerHydrationHost {\n const assertCurrent = () => {\n if (!input.isCurrent()) {\n throw new StaleRemoteHydrationError();\n }\n };\n\n return {\n persistContainerState: async (\n containerState,\n patch,\n updateView,\n saveOptions,\n mutationOptions,\n ) => {\n const callerIsCurrent = mutationOptions?.isCurrent;\n const isCurrent = callerIsCurrent\n ? () => input.isCurrent() && callerIsCurrent()\n : input.isCurrent;\n assertCurrent();\n const record = await input.host.persistContainerState(\n containerState,\n patch,\n updateView,\n saveOptions,\n { ...mutationOptions, isCurrent },\n );\n assertCurrent();\n return record;\n },\n ...(input.host.requestDocumentPriming\n ? {\n requestDocumentPriming: () => {\n if (input.isCurrent()) {\n input.host.requestDocumentPriming?.();\n }\n },\n }\n : {}),\n updateSnapshot: () => {\n if (input.isCurrent()) {\n input.host.updateSnapshot();\n }\n },\n };\n}\n\ninterface RemoteContainerIngestGeneration {\n readonly lifecycle: number | undefined;\n readonly structural: number | undefined;\n}\ntype RemoteContainerIngestGenerationMap = Map<\n string,\n RemoteContainerIngestGeneration\n>;\n\nfunction remoteContainerIngestGeneration(\n state: RemoteContainerHydrationState,\n): RemoteContainerIngestGeneration {\n return {\n lifecycle: state.lifecycleGeneration,\n structural: state.structuralGeneration,\n };\n}\n\nfunction remoteContainerIngestGenerationsMatch(\n left: RemoteContainerIngestGeneration | undefined,\n right: RemoteContainerIngestGeneration,\n): boolean {\n return (\n left !== undefined &&\n left.lifecycle === right.lifecycle &&\n left.structural === right.structural\n );\n}\n\nfunction isCurrentQueuedRemoteContainer(\n generation: RemoteContainerIngestGeneration,\n generationByContainerId: RemoteContainerIngestGenerationMap,\n queue: RemoteContainerIngestQueue,\n remoteContainer: RemoteContainer,\n): boolean {\n return (\n queue.get(remoteContainer.id) === remoteContainer &&\n remoteContainerIngestGenerationsMatch(\n generationByContainerId.get(remoteContainer.id),\n generation,\n )\n );\n}\n\nasync function upsertQueuedRemoteContainer(input: {\n containerIdsWithPendingMetadataUpdates: ReadonlySet<string>;\n containerIdsWithPendingStructuralIntents: ReadonlySet<string>;\n generation: RemoteContainerIngestGeneration;\n generationByContainerId: RemoteContainerIngestGenerationMap;\n host: RemoteContainerHydrationHost;\n isCurrent: () => boolean;\n queue: RemoteContainerIngestQueue;\n queuedRemoteContainer: RemoteContainer;\n state: RemoteContainerHydrationState;\n}): Promise<boolean> {\n const {\n containerIdsWithPendingMetadataUpdates,\n containerIdsWithPendingStructuralIntents,\n generation,\n generationByContainerId,\n host,\n queue,\n queuedRemoteContainer,\n state,\n } = input;\n if (\n !input.isCurrent() ||\n !isCurrentQueuedRemoteContainer(\n generation,\n generationByContainerId,\n queue,\n queuedRemoteContainer,\n )\n ) {\n return false;\n }\n\n const tombstones = await state.persistence.loadContainerHydrationTombstones(\n state.runtime.infra.execSql,\n );\n // A refused folder is reported and acknowledged; the batch continues.\n const upserted = await upsertIsolatedRemoteContainerState({\n expectedHydrationTombstone:\n tombstones.find((row) => row.containerId === queuedRemoteContainer.id) ??\n null,\n containerIdsWithPendingMetadataUpdates,\n containerIdsWithPendingStructuralIntents,\n host,\n isCurrent: input.isCurrent,\n remoteContainer: queuedRemoteContainer,\n state,\n });\n if (!upserted) {\n return false;\n }\n return true;\n}\n\nfunction acknowledgeRemoteContainerIngestBatch(input: {\n generation: RemoteContainerIngestGeneration;\n generationByContainerId: RemoteContainerIngestGenerationMap;\n isCurrent: () => boolean;\n queue: RemoteContainerIngestQueue;\n queuedRemoteContainers: ReadonlyArray<RemoteContainer>;\n}): void {\n if (!input.isCurrent()) {\n return;\n }\n for (const queuedRemoteContainer of input.queuedRemoteContainers) {\n if (\n isCurrentQueuedRemoteContainer(\n input.generation,\n input.generationByContainerId,\n input.queue,\n queuedRemoteContainer,\n )\n ) {\n input.queue.delete(queuedRemoteContainer.id);\n input.generationByContainerId.delete(queuedRemoteContainer.id);\n }\n }\n}\n\nasync function drainRemoteContainerIngestQueue(input: {\n generation: RemoteContainerIngestGeneration;\n generationByContainerId: RemoteContainerIngestGenerationMap;\n host: RemoteContainerHydrationHost;\n isCurrent: () => boolean;\n queue: RemoteContainerIngestQueue;\n state: RemoteContainerHydrationState;\n}) {\n const { generation, generationByContainerId, host, queue, state } = input;\n let shouldUpdateSnapshot = false;\n\n try {\n while (input.isCurrent()) {\n const queuedRemoteContainers = Array.from(queue.values()).filter(\n (remoteContainer) =>\n remoteContainerIngestGenerationsMatch(\n generationByContainerId.get(remoteContainer.id),\n generation,\n ),\n );\n if (queuedRemoteContainers.length === 0) {\n return;\n }\n await cacheRemoteContainerPrincipalPolicies({\n cacheReferencedPrincipalPolicies: createRuntimePrincipalPolicyWarmer(\n state.runtime,\n ),\n remoteContainers: queuedRemoteContainers,\n stillCurrent: input.isCurrent,\n });\n if (!input.isCurrent()) {\n return;\n }\n const [\n containerIdsWithPendingMetadataUpdates,\n containerIdsWithPendingStructuralIntents,\n ] = await Promise.all([\n listRemoteContainerIdsWithPendingMetadataUpdates({\n remoteContainers: queuedRemoteContainers,\n state,\n }),\n listRemoteContainerIdsWithPendingStructuralIntents({\n remoteContainers: queuedRemoteContainers,\n state,\n }),\n ]);\n if (!input.isCurrent()) {\n return;\n }\n\n for (const queuedRemoteContainer of queuedRemoteContainers) {\n shouldUpdateSnapshot =\n (await upsertQueuedRemoteContainer({\n containerIdsWithPendingMetadataUpdates,\n containerIdsWithPendingStructuralIntents,\n generation,\n generationByContainerId,\n host,\n isCurrent: input.isCurrent,\n queue,\n queuedRemoteContainer,\n state,\n })) || shouldUpdateSnapshot;\n }\n acknowledgeRemoteContainerIngestBatch({\n generation,\n generationByContainerId,\n isCurrent: input.isCurrent,\n queue,\n queuedRemoteContainers,\n });\n\n if (shouldUpdateSnapshot) {\n host.updateSnapshot();\n shouldUpdateSnapshot = false;\n }\n }\n } catch (error) {\n if (shouldUpdateSnapshot) {\n host.updateSnapshot();\n }\n throw error;\n }\n}\n\nfunction isRemoteContainerIngestGenerationCurrent(\n state: RemoteContainerHydrationState,\n generation: RemoteContainerIngestGeneration,\n): boolean {\n return remoteContainerIngestGenerationsMatch(\n remoteContainerIngestGeneration(state),\n generation,\n );\n}\n\nfunction settleStaleRemoteContainerIngestGeneration(input: {\n generation: RemoteContainerIngestGeneration;\n generationByContainerId: RemoteContainerIngestGenerationMap;\n queue: RemoteContainerIngestQueue;\n state: RemoteContainerHydrationState;\n}): void {\n const currentGeneration = remoteContainerIngestGeneration(input.state);\n const shouldReplay =\n currentGeneration.lifecycle !== input.generation.lifecycle &&\n currentGeneration.structural === input.generation.structural;\n for (const [containerId, generation] of input.generationByContainerId) {\n if (!remoteContainerIngestGenerationsMatch(generation, input.generation)) {\n continue;\n }\n if (shouldReplay) {\n input.generationByContainerId.set(containerId, currentGeneration);\n } else {\n input.generationByContainerId.delete(containerId);\n input.queue.delete(containerId);\n }\n }\n}\n\ninterface RemoteContainerIngestor {\n (remoteContainer: RemoteContainer): Promise<void>;\n hasPending: () => boolean;\n resume: () => Promise<void>;\n}\n\nexport function createRemoteContainerIngestor(input: {\n getSerializationBarrier?: (() => Promise<void> | null) | undefined;\n host: RemoteContainerHydrationHost;\n state: RemoteContainerHydrationState;\n}): RemoteContainerIngestor {\n const { host, state } = input;\n const pendingRemoteContainersById: RemoteContainerIngestQueue = new Map();\n const generationByContainerId: RemoteContainerIngestGenerationMap = new Map();\n let ingestRemoteContainersPromise: Promise<void> | null = null;\n\n const runNextGeneration = (): Promise<void> => {\n if (ingestRemoteContainersPromise) {\n return ingestRemoteContainersPromise.then(\n runNextGeneration,\n runNextGeneration,\n );\n }\n if (\n state.runtime.infra.dbStatus !== undefined &&\n state.runtime.infra.dbStatus !== \"ready\"\n ) {\n return Promise.resolve();\n }\n const serializationBarrier = input.getSerializationBarrier?.();\n if (serializationBarrier) {\n return serializationBarrier.then(runNextGeneration);\n }\n const nextContainerId = pendingRemoteContainersById.keys().next().value;\n if (typeof nextContainerId !== \"string\") {\n return Promise.resolve();\n }\n const generation = generationByContainerId.get(nextContainerId);\n if (!generation) {\n pendingRemoteContainersById.delete(nextContainerId);\n return runNextGeneration();\n }\n const isCurrent = () =>\n isRemoteContainerIngestGenerationCurrent(state, generation);\n const guardedHost = createGenerationGuardedHydrationHost({\n host,\n isCurrent,\n });\n const nextPromise = Promise.resolve()\n .then(() =>\n drainRemoteContainerIngestQueue({\n generation,\n generationByContainerId,\n host: guardedHost,\n isCurrent,\n queue: pendingRemoteContainersById,\n state,\n }),\n )\n .then(() => {\n if (!isCurrent()) {\n settleStaleRemoteContainerIngestGeneration({\n generation,\n generationByContainerId,\n queue: pendingRemoteContainersById,\n state,\n });\n }\n })\n .catch((error: unknown) => {\n if (!isCurrent() || error instanceof StaleRemoteHydrationError) {\n settleStaleRemoteContainerIngestGeneration({\n generation,\n generationByContainerId,\n queue: pendingRemoteContainersById,\n state,\n });\n return;\n }\n throw error;\n })\n .finally(() => {\n if (ingestRemoteContainersPromise === nextPromise) {\n ingestRemoteContainersPromise = null;\n }\n });\n ingestRemoteContainersPromise = nextPromise;\n return nextPromise.then(() =>\n pendingRemoteContainersById.size > 0 ? runNextGeneration() : undefined,\n );\n };\n\n const ingestRemoteContainer = (remoteContainer: RemoteContainer) => {\n pendingRemoteContainersById.set(remoteContainer.id, remoteContainer);\n generationByContainerId.set(\n remoteContainer.id,\n remoteContainerIngestGeneration(state),\n );\n return runNextGeneration();\n };\n ingestRemoteContainer.hasPending = () => pendingRemoteContainersById.size > 0;\n ingestRemoteContainer.resume = runNextGeneration;\n return ingestRemoteContainer;\n}\n\nexport async function hydrateRemoteContainers(input: {\n followDiscoveredParentLanes?: boolean | undefined;\n host: RemoteContainerHydrationHost;\n isCurrent?: (() => boolean) | undefined;\n onFullyHydrated?: (() => Promise<void> | void) | undefined;\n parentIds?: ReadonlyArray<string | null> | undefined;\n resetAllLaneWatermarks?: boolean | undefined;\n resetRootLaneWatermark?: boolean | undefined;\n state: RemoteContainerHydrationState;\n}): Promise<number> {\n const { host, state } = input;\n if (!canHydrateRemoteContainers(state) || input.isCurrent?.() === false) {\n return 0;\n }\n\n const seenContainerIds = new Set<string>();\n const containerIdsBeforeHydration = new Set(state.containersById.keys());\n const childIdsByParentId = createContainerChildIndex(state.containersById);\n const { lanes, queueParentLane } = createContainerParentHydrationQueue({\n containerIds: state.containersById.keys(),\n parentIds: input.parentIds,\n resetAllLaneWatermarks: input.resetAllLaneWatermarks,\n resetRootLaneWatermark: input.resetRootLaneWatermark,\n });\n // Follow child lanes during an explicit recursive crawl or for containers first\n // discovered in this pass. The latter lets a newly authorized root populate its\n // descendants immediately, while routine ticks keep known subtrees cache-first.\n const queueDiscoveredParentLane: QueueContainerParentLane = (containerId) => {\n if (\n input.followDiscoveredParentLanes ||\n (containerId !== null && !containerIdsBeforeHydration.has(containerId))\n ) {\n queueParentLane(containerId);\n }\n };\n const result = await hydrateContainerParentLanes({\n childIdsByParentId,\n host,\n isCurrent: input.isCurrent,\n lanes,\n queueParentLane: queueDiscoveredParentLane,\n seenContainerIds,\n state,\n });\n const { changedCount } = result;\n await finishRemoteHydration({\n changedCount,\n complete:\n result.complete &&\n canHydrateRemoteContainers(state) &&\n input.isCurrent?.() !== false,\n containerIdsBeforeHydration,\n host,\n isCurrent: input.isCurrent,\n onFullyHydrated: input.onFullyHydrated,\n seenContainerIds,\n state,\n });\n return changedCount;\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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","sourcesContent":["import { createRuntimePrincipalPolicyWarmer } from \"../../principals/runtimePolicyWarmer\";\nimport { finishRemoteHydration } from \"./completeHydration\";\nimport { createContainerChildIndex } from \"./lanes/childIndex\";\nimport { createContainerParentHydrationQueue } from \"./lanes/parentLaneQueue\";\nimport { cacheRemoteContainerPrincipalPolicies } from \"./principalPolicyCache\";\nimport { loadPausedContainerFilter } from \"./remoteContainers/organizationSyncPauseFilter\";\nimport {\n canHydrateRemoteContainers,\n hydrateContainerParentLanes,\n} from \"./remoteContainers/remoteContainerPageHydration\";\nimport {\n listRemoteContainerIdsWithPendingMetadataUpdates,\n listRemoteContainerIdsWithPendingStructuralIntents,\n upsertIsolatedRemoteContainerState,\n} from \"./remoteContainers/remoteContainerState\";\nimport type {\n QueueContainerParentLane,\n RemoteContainer,\n RemoteContainerHydrationHost,\n RemoteContainerHydrationState,\n RemoteContainerIngestQueue,\n} from \"./types\";\n\nexport type {\n ContainerState,\n PersistContainerStateResult,\n RemoteContainer,\n RemoteContainerHydrationHost,\n} from \"./types\";\n\nexport class StaleRemoteHydrationError extends Error {}\n\nexport function createGenerationGuardedHydrationHost(input: {\n host: RemoteContainerHydrationHost;\n isCurrent: () => boolean;\n}): RemoteContainerHydrationHost {\n const assertCurrent = () => {\n if (!input.isCurrent()) {\n throw new StaleRemoteHydrationError();\n }\n };\n\n return {\n persistContainerState: async (\n containerState,\n patch,\n updateView,\n saveOptions,\n mutationOptions,\n ) => {\n const callerIsCurrent = mutationOptions?.isCurrent;\n const isCurrent = callerIsCurrent\n ? () => input.isCurrent() && callerIsCurrent()\n : input.isCurrent;\n assertCurrent();\n const record = await input.host.persistContainerState(\n containerState,\n patch,\n updateView,\n saveOptions,\n { ...mutationOptions, isCurrent },\n );\n assertCurrent();\n return record;\n },\n ...(input.host.requestDocumentPriming\n ? {\n requestDocumentPriming: () => {\n if (input.isCurrent()) {\n input.host.requestDocumentPriming?.();\n }\n },\n }\n : {}),\n updateSnapshot: () => {\n if (input.isCurrent()) {\n input.host.updateSnapshot();\n }\n },\n };\n}\n\ninterface RemoteContainerIngestGeneration {\n readonly lifecycle: number | undefined;\n readonly structural: number | undefined;\n}\ntype RemoteContainerIngestGenerationMap = Map<\n string,\n RemoteContainerIngestGeneration\n>;\n\nfunction remoteContainerIngestGeneration(\n state: RemoteContainerHydrationState,\n): RemoteContainerIngestGeneration {\n return {\n lifecycle: state.lifecycleGeneration,\n structural: state.structuralGeneration,\n };\n}\n\nfunction remoteContainerIngestGenerationsMatch(\n left: RemoteContainerIngestGeneration | undefined,\n right: RemoteContainerIngestGeneration,\n): boolean {\n return (\n left !== undefined &&\n left.lifecycle === right.lifecycle &&\n left.structural === right.structural\n );\n}\n\nfunction isCurrentQueuedRemoteContainer(\n generation: RemoteContainerIngestGeneration,\n generationByContainerId: RemoteContainerIngestGenerationMap,\n queue: RemoteContainerIngestQueue,\n remoteContainer: RemoteContainer,\n): boolean {\n return (\n queue.get(remoteContainer.id) === remoteContainer &&\n remoteContainerIngestGenerationsMatch(\n generationByContainerId.get(remoteContainer.id),\n generation,\n )\n );\n}\n\nasync function upsertQueuedRemoteContainer(input: {\n containerIdsWithPendingMetadataUpdates: ReadonlySet<string>;\n containerIdsWithPendingStructuralIntents: ReadonlySet<string>;\n generation: RemoteContainerIngestGeneration;\n generationByContainerId: RemoteContainerIngestGenerationMap;\n host: RemoteContainerHydrationHost;\n isCurrent: () => boolean;\n queue: RemoteContainerIngestQueue;\n queuedRemoteContainer: RemoteContainer;\n state: RemoteContainerHydrationState;\n}): Promise<boolean> {\n const {\n containerIdsWithPendingMetadataUpdates,\n containerIdsWithPendingStructuralIntents,\n generation,\n generationByContainerId,\n host,\n queue,\n queuedRemoteContainer,\n state,\n } = input;\n if (\n !input.isCurrent() ||\n !isCurrentQueuedRemoteContainer(\n generation,\n generationByContainerId,\n queue,\n queuedRemoteContainer,\n )\n ) {\n return false;\n }\n\n // A paused organization's container is acknowledged without being stored,\n // including when the pause lands while the commit is in flight.\n const isPaused = await loadPausedContainerFilter(state);\n if (isPaused(queuedRemoteContainer)) return true;\n const tombstones = await state.persistence.loadContainerHydrationTombstones(\n state.runtime.infra.execSql,\n );\n // A refused folder is reported and acknowledged; the batch continues.\n const upserted = await upsertIsolatedRemoteContainerState({\n expectedHydrationTombstone:\n tombstones.find((row) => row.containerId === queuedRemoteContainer.id) ??\n null,\n containerIdsWithPendingMetadataUpdates,\n containerIdsWithPendingStructuralIntents,\n host,\n isCurrent: () => input.isCurrent() && !isPaused(queuedRemoteContainer),\n remoteContainer: queuedRemoteContainer,\n state,\n });\n if (!upserted) {\n return false;\n }\n return true;\n}\n\nfunction acknowledgeRemoteContainerIngestBatch(input: {\n generation: RemoteContainerIngestGeneration;\n generationByContainerId: RemoteContainerIngestGenerationMap;\n isCurrent: () => boolean;\n queue: RemoteContainerIngestQueue;\n queuedRemoteContainers: ReadonlyArray<RemoteContainer>;\n}): void {\n if (!input.isCurrent()) {\n return;\n }\n for (const queuedRemoteContainer of input.queuedRemoteContainers) {\n if (\n isCurrentQueuedRemoteContainer(\n input.generation,\n input.generationByContainerId,\n input.queue,\n queuedRemoteContainer,\n )\n ) {\n input.queue.delete(queuedRemoteContainer.id);\n input.generationByContainerId.delete(queuedRemoteContainer.id);\n }\n }\n}\n\nasync function drainRemoteContainerIngestQueue(input: {\n generation: RemoteContainerIngestGeneration;\n generationByContainerId: RemoteContainerIngestGenerationMap;\n host: RemoteContainerHydrationHost;\n isCurrent: () => boolean;\n queue: RemoteContainerIngestQueue;\n state: RemoteContainerHydrationState;\n}) {\n const { generation, generationByContainerId, host, queue, state } = input;\n let shouldUpdateSnapshot = false;\n\n try {\n while (input.isCurrent()) {\n const queuedRemoteContainers = Array.from(queue.values()).filter(\n (remoteContainer) =>\n remoteContainerIngestGenerationsMatch(\n generationByContainerId.get(remoteContainer.id),\n generation,\n ),\n );\n if (queuedRemoteContainers.length === 0) {\n return;\n }\n await cacheRemoteContainerPrincipalPolicies({\n cacheReferencedPrincipalPolicies: createRuntimePrincipalPolicyWarmer(\n state.runtime,\n ),\n remoteContainers: queuedRemoteContainers,\n stillCurrent: input.isCurrent,\n });\n if (!input.isCurrent()) {\n return;\n }\n const [\n containerIdsWithPendingMetadataUpdates,\n containerIdsWithPendingStructuralIntents,\n ] = await Promise.all([\n listRemoteContainerIdsWithPendingMetadataUpdates({\n remoteContainers: queuedRemoteContainers,\n state,\n }),\n listRemoteContainerIdsWithPendingStructuralIntents({\n remoteContainers: queuedRemoteContainers,\n state,\n }),\n ]);\n if (!input.isCurrent()) {\n return;\n }\n\n for (const queuedRemoteContainer of queuedRemoteContainers) {\n shouldUpdateSnapshot =\n (await upsertQueuedRemoteContainer({\n containerIdsWithPendingMetadataUpdates,\n containerIdsWithPendingStructuralIntents,\n generation,\n generationByContainerId,\n host,\n isCurrent: input.isCurrent,\n queue,\n queuedRemoteContainer,\n state,\n })) || shouldUpdateSnapshot;\n }\n acknowledgeRemoteContainerIngestBatch({\n generation,\n generationByContainerId,\n isCurrent: input.isCurrent,\n queue,\n queuedRemoteContainers,\n });\n\n if (shouldUpdateSnapshot) {\n host.updateSnapshot();\n shouldUpdateSnapshot = false;\n }\n }\n } catch (error) {\n if (shouldUpdateSnapshot) {\n host.updateSnapshot();\n }\n throw error;\n }\n}\n\nfunction isRemoteContainerIngestGenerationCurrent(\n state: RemoteContainerHydrationState,\n generation: RemoteContainerIngestGeneration,\n): boolean {\n return remoteContainerIngestGenerationsMatch(\n remoteContainerIngestGeneration(state),\n generation,\n );\n}\n\nfunction settleStaleRemoteContainerIngestGeneration(input: {\n generation: RemoteContainerIngestGeneration;\n generationByContainerId: RemoteContainerIngestGenerationMap;\n queue: RemoteContainerIngestQueue;\n state: RemoteContainerHydrationState;\n}): void {\n const currentGeneration = remoteContainerIngestGeneration(input.state);\n const shouldReplay =\n currentGeneration.lifecycle !== input.generation.lifecycle &&\n currentGeneration.structural === input.generation.structural;\n for (const [containerId, generation] of input.generationByContainerId) {\n if (!remoteContainerIngestGenerationsMatch(generation, input.generation)) {\n continue;\n }\n if (shouldReplay) {\n input.generationByContainerId.set(containerId, currentGeneration);\n } else {\n input.generationByContainerId.delete(containerId);\n input.queue.delete(containerId);\n }\n }\n}\n\ninterface RemoteContainerIngestor {\n (remoteContainer: RemoteContainer): Promise<void>;\n hasPending: () => boolean;\n resume: () => Promise<void>;\n}\n\nexport function createRemoteContainerIngestor(input: {\n getSerializationBarrier?: (() => Promise<void> | null) | undefined;\n host: RemoteContainerHydrationHost;\n state: RemoteContainerHydrationState;\n}): RemoteContainerIngestor {\n const { host, state } = input;\n const pendingRemoteContainersById: RemoteContainerIngestQueue = new Map();\n const generationByContainerId: RemoteContainerIngestGenerationMap = new Map();\n let ingestRemoteContainersPromise: Promise<void> | null = null;\n\n const runNextGeneration = (): Promise<void> => {\n if (ingestRemoteContainersPromise) {\n return ingestRemoteContainersPromise.then(\n runNextGeneration,\n runNextGeneration,\n );\n }\n if (\n state.runtime.infra.dbStatus !== undefined &&\n state.runtime.infra.dbStatus !== \"ready\"\n ) {\n return Promise.resolve();\n }\n const serializationBarrier = input.getSerializationBarrier?.();\n if (serializationBarrier) {\n return serializationBarrier.then(runNextGeneration);\n }\n const nextContainerId = pendingRemoteContainersById.keys().next().value;\n if (typeof nextContainerId !== \"string\") {\n return Promise.resolve();\n }\n const generation = generationByContainerId.get(nextContainerId);\n if (!generation) {\n pendingRemoteContainersById.delete(nextContainerId);\n return runNextGeneration();\n }\n const isCurrent = () =>\n isRemoteContainerIngestGenerationCurrent(state, generation);\n const guardedHost = createGenerationGuardedHydrationHost({\n host,\n isCurrent,\n });\n const nextPromise = Promise.resolve()\n .then(() =>\n drainRemoteContainerIngestQueue({\n generation,\n generationByContainerId,\n host: guardedHost,\n isCurrent,\n queue: pendingRemoteContainersById,\n state,\n }),\n )\n .then(() => {\n if (!isCurrent()) {\n settleStaleRemoteContainerIngestGeneration({\n generation,\n generationByContainerId,\n queue: pendingRemoteContainersById,\n state,\n });\n }\n })\n .catch((error: unknown) => {\n if (!isCurrent() || error instanceof StaleRemoteHydrationError) {\n settleStaleRemoteContainerIngestGeneration({\n generation,\n generationByContainerId,\n queue: pendingRemoteContainersById,\n state,\n });\n return;\n }\n throw error;\n })\n .finally(() => {\n if (ingestRemoteContainersPromise === nextPromise) {\n ingestRemoteContainersPromise = null;\n }\n });\n ingestRemoteContainersPromise = nextPromise;\n return nextPromise.then(() =>\n pendingRemoteContainersById.size > 0 ? runNextGeneration() : undefined,\n );\n };\n\n const ingestRemoteContainer = (remoteContainer: RemoteContainer) => {\n pendingRemoteContainersById.set(remoteContainer.id, remoteContainer);\n generationByContainerId.set(\n remoteContainer.id,\n remoteContainerIngestGeneration(state),\n );\n return runNextGeneration();\n };\n ingestRemoteContainer.hasPending = () => pendingRemoteContainersById.size > 0;\n ingestRemoteContainer.resume = runNextGeneration;\n return ingestRemoteContainer;\n}\n\nexport async function hydrateRemoteContainers(input: {\n followDiscoveredParentLanes?: boolean | undefined;\n host: RemoteContainerHydrationHost;\n isCurrent?: (() => boolean) | undefined;\n onFullyHydrated?: (() => Promise<void> | void) | undefined;\n parentIds?: ReadonlyArray<string | null> | undefined;\n resetAllLaneWatermarks?: boolean | undefined;\n resetRootLaneWatermark?: boolean | undefined;\n state: RemoteContainerHydrationState;\n}): Promise<number> {\n const { host, state } = input;\n if (!canHydrateRemoteContainers(state) || input.isCurrent?.() === false) {\n return 0;\n }\n\n const seenContainerIds = new Set<string>();\n const containerIdsBeforeHydration = new Set(state.containersById.keys());\n const childIdsByParentId = createContainerChildIndex(state.containersById);\n const { lanes, queueParentLane } = createContainerParentHydrationQueue({\n containerIds: state.containersById.keys(),\n parentIds: input.parentIds,\n resetAllLaneWatermarks: input.resetAllLaneWatermarks,\n resetRootLaneWatermark: input.resetRootLaneWatermark,\n });\n // Follow child lanes during an explicit recursive crawl or for containers first\n // discovered in this pass. The latter lets a newly authorized root populate its\n // descendants immediately, while routine ticks keep known subtrees cache-first.\n const queueDiscoveredParentLane: QueueContainerParentLane = (containerId) => {\n if (\n input.followDiscoveredParentLanes ||\n (containerId !== null && !containerIdsBeforeHydration.has(containerId))\n ) {\n queueParentLane(containerId);\n }\n };\n const result = await hydrateContainerParentLanes({\n childIdsByParentId,\n host,\n isCurrent: input.isCurrent,\n lanes,\n queueParentLane: queueDiscoveredParentLane,\n seenContainerIds,\n state,\n });\n const { changedCount } = result;\n await finishRemoteHydration({\n changedCount,\n complete:\n result.complete &&\n canHydrateRemoteContainers(state) &&\n input.isCurrent?.() !== false,\n containerIdsBeforeHydration,\n host,\n isCurrent: input.isCurrent,\n onFullyHydrated: input.onFullyHydrated,\n seenContainerIds,\n state,\n });\n return changedCount;\n}\n"]}
@@ -0,0 +1,13 @@
1
+ import { getOrCreateOrganizationSyncPauses } from "../../../../data/sync/organizationSyncPauses.js";
2
+ /**
3
+ * Loads the scope's paused organizations and returns the predicate remote
4
+ * hydration uses to leave their containers off the device. The session's
5
+ * active organization is never skipped.
6
+ */
7
+ export async function loadPausedContainerFilter(state) {
8
+ const syncPauses = getOrCreateOrganizationSyncPauses(state.runtime.state.domainScope);
9
+ await syncPauses.load(state.runtime.infra.execSql);
10
+ const activeOrganizationId = state.runtime.auth.organizationId ?? null;
11
+ return (container) => syncPauses.isContainerPaused(container, activeOrganizationId);
12
+ }
13
+ //# sourceMappingURL=organizationSyncPauseFilter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"organizationSyncPauseFilter.js","sourceRoot":"","sources":["organizationSyncPauseFilter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iCAAiC,EAAE,MAAM,8CAA8C,CAAC;AAGjG;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,KAAoC;IAQpC,MAAM,UAAU,GAAG,iCAAiC,CAClD,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAChC,CAAC;IACF,MAAM,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnD,MAAM,oBAAoB,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC;IACvE,OAAO,CAAC,SAAS,EAAE,EAAE,CACnB,UAAU,CAAC,iBAAiB,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;AAClE,CAAC","sourcesContent":["import { getOrCreateOrganizationSyncPauses } from \"../../../../data/sync/organizationSyncPauses\";\nimport type { RemoteContainerHydrationState } from \"../types\";\n\n/**\n * Loads the scope's paused organizations and returns the predicate remote\n * hydration uses to leave their containers off the device. The session's\n * active organization is never skipped.\n */\nexport async function loadPausedContainerFilter(\n state: RemoteContainerHydrationState,\n): Promise<\n (container: {\n organizationId: string;\n parentId: string | null;\n systemSlot?: string | null | undefined;\n }) => boolean\n> {\n const syncPauses = getOrCreateOrganizationSyncPauses(\n state.runtime.state.domainScope,\n );\n await syncPauses.load(state.runtime.infra.execSql);\n const activeOrganizationId = state.runtime.auth.organizationId ?? null;\n return (container) =>\n syncPauses.isContainerPaused(container, activeOrganizationId);\n}\n"]}
@@ -6,8 +6,14 @@ import { fetchContainerParentLaneBatch } from "../lanes/parentLaneFetch.js";
6
6
  import { cacheRemoteContainerPrincipalPolicies } from "../principalPolicyCache.js";
7
7
  import { applyContainerTombstones, getApplicableRemoteContainerItems, } from "../tombstoneApplication.js";
8
8
  import { containerStateMatchesFingerprint } from "./containerStateFingerprint.js";
9
+ import { loadPausedContainerFilter } from "./organizationSyncPauseFilter.js";
9
10
  import { listRemoteContainerIdsWithPendingMetadataUpdates, listRemoteContainerIdsWithPendingStructuralIntents, upsertIsolatedRemoteContainerState, } from "./remoteContainerState.js";
10
11
  const CONTAINER_PARENT_HYDRATION_CONCURRENCY = 4;
12
+ // Re-checked inside the commit: a pause from another device can land after
13
+ // the page was filtered, and its cleanup may already have run.
14
+ function currentUnlessPaused(input, container) {
15
+ return () => input.isCurrent?.() !== false && !input.isPaused(container);
16
+ }
11
17
  async function applyRemoteContainerPage(input) {
12
18
  const { childIdsByParentId, expectedContainerStates, expectedHydrationTombstones, host, items, queueParentLane, seenContainerIds, state, } = input;
13
19
  let hydratedCount = 0;
@@ -61,7 +67,7 @@ async function applyRemoteContainerPage(input) {
61
67
  containerIdsWithPendingMetadataUpdates,
62
68
  containerIdsWithPendingStructuralIntents,
63
69
  host,
64
- isCurrent: input.isCurrent,
70
+ isCurrent: currentUnlessPaused(input, container),
65
71
  expectedHydrationTombstone: expectedHydrationTombstones.get(container.id) ?? null,
66
72
  prefetchedProjection: prefetchedProjections.get(container.id),
67
73
  remoteContainer: container,
@@ -115,13 +121,18 @@ async function applyContainerParentLanePage(input) {
115
121
  // the next startup.
116
122
  host.requestDocumentPriming?.();
117
123
  }
124
+ // A paused organization's containers stay off the device. Skipping them
125
+ // here, after tombstones saw the full page, still lets the lane complete
126
+ // and its watermark advance; their children are never queued.
127
+ const isPaused = await loadPausedContainerFilter(state);
118
128
  const appliedPage = await applyRemoteContainerPage({
119
129
  childIdsByParentId,
120
130
  expectedContainerStates,
121
131
  expectedHydrationTombstones,
122
132
  host,
123
133
  isCurrent: input.isCurrent,
124
- items: remoteContainerItems,
134
+ isPaused,
135
+ items: remoteContainerItems.filter((item) => !isPaused(item)),
125
136
  queueParentLane,
126
137
  seenContainerIds,
127
138
  state,