@tearleads/client-sdk 0.1.101 → 0.1.102

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.
@@ -7,7 +7,7 @@ import { createDiscoveredDocumentVerifier } from "../../workflows/container-cont
7
7
  import { discoverContainerDocumentLanes, } from "../../workflows/container-contents/documentDiscovery/documentDiscoveryLanes.js";
8
8
  import { createContainerDocumentQueriesFromRuntime } from "../../workflows/container-contents/documentQueries/index.js";
9
9
  import { createContainerContentsWorkflowRuntime, } from "../../workflows/container-contents/runtime.js";
10
- import { guardDiscoveryAgainstPause } from "../../workflows/container-contents/syncPause/discoveryGuard.js";
10
+ import { guardDocumentDiscoveryWrites } from "../../workflows/container-contents/syncPause/discoveryGuard.js";
11
11
  import { createContainerDocumentTombstoneVerifier, createDocumentHeadLinkSetLoader, } from "../../workflows/container-contents/tombstones/documentTombstoneEvidence.js";
12
12
  import { createRuntimePrincipalPolicyWarmer } from "../../workflows/principals/runtimePolicyWarmer.js";
13
13
  function createDiscoveryLane(input) {
@@ -30,7 +30,7 @@ function createDiscoveryLane(input) {
30
30
  onFullListing: request.onFullListing,
31
31
  refuteContainerDocumentTombstoneHolds: (placements) => refuteContainerDocumentTombstoneHolds(execSql, placements),
32
32
  verifyContainerDocumentTombstones: createContainerDocumentTombstoneVerifier(containerOrganizationId, loadHead, loadEpoch),
33
- ...guardDiscoveryAgainstPause({
33
+ ...guardDocumentDiscoveryWrites({
34
34
  execSql,
35
35
  isPaused: input.isContainerPaused,
36
36
  verifyDiscoveredDocuments: createDiscoveredDocumentVerifier(containerOrganizationId, loadHead, loadEpoch, input.evidenceStore, request.onPendingDiscovery),
@@ -1 +1 @@
1
- {"version":3,"file":"documentDiscovery.js","sourceRoot":"","sources":["documentDiscovery.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DocumentSummary } from \"../../data/documents/documentSummary\";\nimport { sqlContainerContentsPersistence } from \"../../data/persistence/container-contents/containerContentsPersistence\";\nimport {\n holdContainerDocumentTombstones,\n listKnownContainerDocumentPlacements,\n listRetryableHeldContainerDocumentTombstones,\n loadLocalDocumentAccessEpoch,\n refuteContainerDocumentTombstoneHolds,\n} from \"../../data/persistence/documents/containerDocumentTombstoneHoldsPersistence\";\nimport { createDocumentDiscoveryEvidenceStore } from \"../../data/persistence/documents/documentDiscoveryEvidencePersistence\";\nimport { getOrCreateOrganizationSyncPauses } from \"../../data/sync/organizationSyncPauses\";\nimport { listContainerDocumentLanesFromApi } from \"../../workflows/container-contents/documentDiscovery/containerDocumentListing\";\nimport { createDiscoveredDocumentVerifier } from \"../../workflows/container-contents/documentDiscovery/documentDiscoveryEvidence\";\nimport {\n type ContainerDocumentDiscoveryLane,\n type ContainerDocumentDiscoveryOutcome,\n type ContainerDocumentDiscoveryOutcomeHandler,\n discoverContainerDocumentLanes,\n} from \"../../workflows/container-contents/documentDiscovery/documentDiscoveryLanes\";\nimport { createContainerDocumentQueriesFromRuntime } from \"../../workflows/container-contents/documentQueries\";\nimport {\n type ContainerContentsWorkflowRuntime,\n createContainerContentsWorkflowRuntime,\n} from \"../../workflows/container-contents/runtime\";\nimport { guardDiscoveryAgainstPause } from \"../../workflows/container-contents/syncPause/discoveryGuard\";\nimport {\n createContainerDocumentTombstoneVerifier,\n createDocumentHeadLinkSetLoader,\n} from \"../../workflows/container-contents/tombstones/documentTombstoneEvidence\";\nimport { createRuntimePrincipalPolicyWarmer } from \"../../workflows/principals/runtimePolicyWarmer\";\nimport type { InternalRuntime } from \"../runtime/workflowRuntime\";\n\nexport type { ContainerDocumentDiscoveryOutcome } from \"../../workflows/container-contents/documentDiscovery/documentDiscoveryLanes\";\n\n/** One container to discover, with the reconciler's per-lane callbacks. */\nexport interface ContainerDocumentDiscoveryRequest {\n readonly containerId: string;\n readonly onFullListing?:\n | ((documentIds: ReadonlyArray<string>) => void)\n | undefined;\n readonly onPendingDiscovery?: ((delayMs: number) => void) | undefined;\n}\n\ntype ExecSql = ReturnType<InternalRuntime[\"workflowInput\"]>[\"infra\"][\"execSql\"];\n\nfunction createDiscoveryLane(input: {\n readonly containerOrganizationId: string | undefined;\n readonly isContainerPaused: () => boolean;\n readonly evidenceStore: ReturnType<\n typeof createDocumentDiscoveryEvidenceStore\n >;\n readonly execSql: ExecSql;\n readonly queries: ReturnType<\n typeof createContainerDocumentQueriesFromRuntime\n >;\n readonly request: ContainerDocumentDiscoveryRequest;\n readonly runtime: ContainerContentsWorkflowRuntime;\n}): ContainerDocumentDiscoveryLane {\n const { containerOrganizationId, execSql, request, runtime } = input;\n const warmReferencedPrincipalPolicies =\n createRuntimePrincipalPolicyWarmer(runtime);\n const loadHead = createDocumentHeadLinkSetLoader(runtime);\n const loadEpoch = (documentId: string) =>\n loadLocalDocumentAccessEpoch(execSql, documentId);\n return {\n ...input.queries,\n cacheReferencedPrincipalPolicies: (references) =>\n containerOrganizationId\n ? warmReferencedPrincipalPolicies({\n organizationId: containerOrganizationId,\n references,\n })\n : Promise.resolve(),\n containerId: request.containerId,\n holdContainerDocumentTombstones: (tombstones) =>\n holdContainerDocumentTombstones(execSql, tombstones),\n listHeldContainerDocumentTombstones: (containerIds) =>\n listRetryableHeldContainerDocumentTombstones(execSql, containerIds),\n listKnownContainerDocumentPlacements: (placements) =>\n listKnownContainerDocumentPlacements(execSql, placements),\n onFullListing: request.onFullListing,\n refuteContainerDocumentTombstoneHolds: (placements) =>\n refuteContainerDocumentTombstoneHolds(execSql, placements),\n verifyContainerDocumentTombstones: createContainerDocumentTombstoneVerifier(\n containerOrganizationId,\n loadHead,\n loadEpoch,\n ),\n ...guardDiscoveryAgainstPause({\n execSql,\n isPaused: input.isContainerPaused,\n verifyDiscoveredDocuments: createDiscoveredDocumentVerifier(\n containerOrganizationId,\n loadHead,\n loadEpoch,\n input.evidenceStore,\n request.onPendingDiscovery,\n ),\n }),\n };\n}\n\n/**\n * Internal adapter shared by explicit discovery and background reconciliation:\n * one batched listing for every requested container, then per-container\n * verification scoped to each container's durable organization.\n */\nexport async function discoverContainerDocumentLanesForRuntime({\n onOutcome,\n requests,\n runtimeService,\n}: {\n readonly onOutcome?: ContainerDocumentDiscoveryOutcomeHandler | undefined;\n readonly requests: ReadonlyArray<ContainerDocumentDiscoveryRequest>;\n readonly runtimeService: InternalRuntime;\n}): Promise<ReadonlyArray<ContainerDocumentDiscoveryOutcome>> {\n const unlisted = (\n containerId: string,\n ): ContainerDocumentDiscoveryOutcome => ({\n containerId,\n status: \"fulfilled\",\n value: null,\n });\n const input = runtimeService.workflowInput();\n if (input.infra.dbStatus !== \"ready\") {\n return requests.map(({ containerId }) => unlisted(containerId));\n }\n const runtime = createContainerContentsWorkflowRuntime(input);\n const execSql = input.infra.execSql;\n const evidenceStore = createDocumentDiscoveryEvidenceStore(execSql);\n const queries = createContainerDocumentQueriesFromRuntime(runtime);\n // Capture the reset fence before reading any lane's scope, so a reset\n // between these reads and the listing cannot publish old-organization\n // evidence into its replacement.\n const generation = await evidenceStore.begin();\n const syncPauses = getOrCreateOrganizationSyncPauses(input.state.domainScope);\n await syncPauses.load(execSql);\n const lanes: ContainerDocumentDiscoveryLane[] = [];\n for (const request of requests) {\n const stored =\n await sqlContainerContentsPersistence.loadContainerMetadataState(\n execSql,\n request.containerId,\n );\n if (!stored) continue;\n const isContainerPaused = () =>\n syncPauses.isContainerPaused(\n stored.container,\n runtimeService.workflowInput().auth.organizationId ?? null,\n );\n // A paused organization's container lists like one that is not stored,\n // and a pause landing mid-listing stops its write.\n if (!isContainerPaused())\n lanes.push(\n createDiscoveryLane({\n containerOrganizationId: stored.container.organizationId,\n isContainerPaused,\n evidenceStore,\n execSql,\n queries,\n request,\n runtime,\n }),\n );\n }\n const outcomes = await discoverContainerDocumentLanes({\n generation,\n lanes,\n listContainerDocumentLanes: (listed) =>\n listContainerDocumentLanesFromApi(runtime.apiClient, listed),\n loadContainerDocumentWatermark: queries.loadContainerDocumentWatermark,\n onOutcome,\n });\n const outcomeByContainerId = new Map(\n outcomes.map((outcome) => [outcome.containerId, outcome]),\n );\n return requests.map(\n ({ containerId }) =>\n outcomeByContainerId.get(containerId) ?? unlisted(containerId),\n );\n}\n\n/** Discovers one container; a rejected lane rethrows its failure. */\nexport async function discoverContainerDocumentsForRuntime({\n runtimeService,\n ...request\n}: ContainerDocumentDiscoveryRequest & {\n readonly runtimeService: InternalRuntime;\n}): Promise<ReadonlyArray<DocumentSummary> | null> {\n const [outcome] = await discoverContainerDocumentLanesForRuntime({\n requests: [request],\n runtimeService,\n });\n if (outcome?.status === \"rejected\") throw outcome.reason;\n return outcome?.value ?? null;\n}\n"]}
1
+ {"version":3,"file":"documentDiscovery.js","sourceRoot":"","sources":["documentDiscovery.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DocumentSummary } from \"../../data/documents/documentSummary\";\nimport { sqlContainerContentsPersistence } from \"../../data/persistence/container-contents/containerContentsPersistence\";\nimport {\n holdContainerDocumentTombstones,\n listKnownContainerDocumentPlacements,\n listRetryableHeldContainerDocumentTombstones,\n loadLocalDocumentAccessEpoch,\n refuteContainerDocumentTombstoneHolds,\n} from \"../../data/persistence/documents/containerDocumentTombstoneHoldsPersistence\";\nimport { createDocumentDiscoveryEvidenceStore } from \"../../data/persistence/documents/documentDiscoveryEvidencePersistence\";\nimport { getOrCreateOrganizationSyncPauses } from \"../../data/sync/organizationSyncPauses\";\nimport { listContainerDocumentLanesFromApi } from \"../../workflows/container-contents/documentDiscovery/containerDocumentListing\";\nimport { createDiscoveredDocumentVerifier } from \"../../workflows/container-contents/documentDiscovery/documentDiscoveryEvidence\";\nimport {\n type ContainerDocumentDiscoveryLane,\n type ContainerDocumentDiscoveryOutcome,\n type ContainerDocumentDiscoveryOutcomeHandler,\n discoverContainerDocumentLanes,\n} from \"../../workflows/container-contents/documentDiscovery/documentDiscoveryLanes\";\nimport { createContainerDocumentQueriesFromRuntime } from \"../../workflows/container-contents/documentQueries\";\nimport {\n type ContainerContentsWorkflowRuntime,\n createContainerContentsWorkflowRuntime,\n} from \"../../workflows/container-contents/runtime\";\nimport { guardDocumentDiscoveryWrites } from \"../../workflows/container-contents/syncPause/discoveryGuard\";\nimport {\n createContainerDocumentTombstoneVerifier,\n createDocumentHeadLinkSetLoader,\n} from \"../../workflows/container-contents/tombstones/documentTombstoneEvidence\";\nimport { createRuntimePrincipalPolicyWarmer } from \"../../workflows/principals/runtimePolicyWarmer\";\nimport type { InternalRuntime } from \"../runtime/workflowRuntime\";\n\nexport type { ContainerDocumentDiscoveryOutcome } from \"../../workflows/container-contents/documentDiscovery/documentDiscoveryLanes\";\n\n/** One container to discover, with the reconciler's per-lane callbacks. */\nexport interface ContainerDocumentDiscoveryRequest {\n readonly containerId: string;\n readonly onFullListing?:\n | ((documentIds: ReadonlyArray<string>) => void)\n | undefined;\n readonly onPendingDiscovery?: ((delayMs: number) => void) | undefined;\n}\n\ntype ExecSql = ReturnType<InternalRuntime[\"workflowInput\"]>[\"infra\"][\"execSql\"];\n\nfunction createDiscoveryLane(input: {\n readonly containerOrganizationId: string | undefined;\n readonly isContainerPaused: () => boolean;\n readonly evidenceStore: ReturnType<\n typeof createDocumentDiscoveryEvidenceStore\n >;\n readonly execSql: ExecSql;\n readonly queries: ReturnType<\n typeof createContainerDocumentQueriesFromRuntime\n >;\n readonly request: ContainerDocumentDiscoveryRequest;\n readonly runtime: ContainerContentsWorkflowRuntime;\n}): ContainerDocumentDiscoveryLane {\n const { containerOrganizationId, execSql, request, runtime } = input;\n const warmReferencedPrincipalPolicies =\n createRuntimePrincipalPolicyWarmer(runtime);\n const loadHead = createDocumentHeadLinkSetLoader(runtime);\n const loadEpoch = (documentId: string) =>\n loadLocalDocumentAccessEpoch(execSql, documentId);\n return {\n ...input.queries,\n cacheReferencedPrincipalPolicies: (references) =>\n containerOrganizationId\n ? warmReferencedPrincipalPolicies({\n organizationId: containerOrganizationId,\n references,\n })\n : Promise.resolve(),\n containerId: request.containerId,\n holdContainerDocumentTombstones: (tombstones) =>\n holdContainerDocumentTombstones(execSql, tombstones),\n listHeldContainerDocumentTombstones: (containerIds) =>\n listRetryableHeldContainerDocumentTombstones(execSql, containerIds),\n listKnownContainerDocumentPlacements: (placements) =>\n listKnownContainerDocumentPlacements(execSql, placements),\n onFullListing: request.onFullListing,\n refuteContainerDocumentTombstoneHolds: (placements) =>\n refuteContainerDocumentTombstoneHolds(execSql, placements),\n verifyContainerDocumentTombstones: createContainerDocumentTombstoneVerifier(\n containerOrganizationId,\n loadHead,\n loadEpoch,\n ),\n ...guardDocumentDiscoveryWrites({\n execSql,\n isPaused: input.isContainerPaused,\n verifyDiscoveredDocuments: createDiscoveredDocumentVerifier(\n containerOrganizationId,\n loadHead,\n loadEpoch,\n input.evidenceStore,\n request.onPendingDiscovery,\n ),\n }),\n };\n}\n\n/**\n * Internal adapter shared by explicit discovery and background reconciliation:\n * one batched listing for every requested container, then per-container\n * verification scoped to each container's durable organization.\n */\nexport async function discoverContainerDocumentLanesForRuntime({\n onOutcome,\n requests,\n runtimeService,\n}: {\n readonly onOutcome?: ContainerDocumentDiscoveryOutcomeHandler | undefined;\n readonly requests: ReadonlyArray<ContainerDocumentDiscoveryRequest>;\n readonly runtimeService: InternalRuntime;\n}): Promise<ReadonlyArray<ContainerDocumentDiscoveryOutcome>> {\n const unlisted = (\n containerId: string,\n ): ContainerDocumentDiscoveryOutcome => ({\n containerId,\n status: \"fulfilled\",\n value: null,\n });\n const input = runtimeService.workflowInput();\n if (input.infra.dbStatus !== \"ready\") {\n return requests.map(({ containerId }) => unlisted(containerId));\n }\n const runtime = createContainerContentsWorkflowRuntime(input);\n const execSql = input.infra.execSql;\n const evidenceStore = createDocumentDiscoveryEvidenceStore(execSql);\n const queries = createContainerDocumentQueriesFromRuntime(runtime);\n // Capture the reset fence before reading any lane's scope, so a reset\n // between these reads and the listing cannot publish old-organization\n // evidence into its replacement.\n const generation = await evidenceStore.begin();\n const syncPauses = getOrCreateOrganizationSyncPauses(input.state.domainScope);\n await syncPauses.load(execSql);\n const lanes: ContainerDocumentDiscoveryLane[] = [];\n for (const request of requests) {\n const stored =\n await sqlContainerContentsPersistence.loadContainerMetadataState(\n execSql,\n request.containerId,\n );\n if (!stored) continue;\n const isContainerPaused = () =>\n syncPauses.isContainerPaused(\n stored.container,\n runtimeService.workflowInput().auth.organizationId ?? null,\n );\n // A paused organization's container lists like one that is not stored,\n // and a pause landing mid-listing stops its write.\n if (!isContainerPaused())\n lanes.push(\n createDiscoveryLane({\n containerOrganizationId: stored.container.organizationId,\n isContainerPaused,\n evidenceStore,\n execSql,\n queries,\n request,\n runtime,\n }),\n );\n }\n const outcomes = await discoverContainerDocumentLanes({\n generation,\n lanes,\n listContainerDocumentLanes: (listed) =>\n listContainerDocumentLanesFromApi(runtime.apiClient, listed),\n loadContainerDocumentWatermark: queries.loadContainerDocumentWatermark,\n onOutcome,\n });\n const outcomeByContainerId = new Map(\n outcomes.map((outcome) => [outcome.containerId, outcome]),\n );\n return requests.map(\n ({ containerId }) =>\n outcomeByContainerId.get(containerId) ?? unlisted(containerId),\n );\n}\n\n/** Discovers one container; a rejected lane rethrows its failure. */\nexport async function discoverContainerDocumentsForRuntime({\n runtimeService,\n ...request\n}: ContainerDocumentDiscoveryRequest & {\n readonly runtimeService: InternalRuntime;\n}): Promise<ReadonlyArray<DocumentSummary> | null> {\n const [outcome] = await discoverContainerDocumentLanesForRuntime({\n requests: [request],\n runtimeService,\n });\n if (outcome?.status === \"rejected\") throw outcome.reason;\n return outcome?.value ?? null;\n}\n"]}
@@ -0,0 +1,28 @@
1
+ import { and, eq } from "drizzle-orm";
2
+ import { normalizeEffectiveAccessLevel } from "../../keying/accessLevel.js";
3
+ import { containerSyncWatermarks } from "../../sqlite/schema/index.js";
4
+ import { clearDocumentDiscoveryEvidenceOnRemoteReset } from "../documents/documentDiscoveryEvidencePersistence.js";
5
+ /**
6
+ * Held remote permissions change through the metadata CAS commit, which calls
7
+ * this in its transaction. Insert and local-authoring saves have no prior
8
+ * remote permission to invalidate. Documents' edit clocks do not track grants.
9
+ */
10
+ export async function resetAccessDiscovery(tx, current, next) {
11
+ if (normalizeEffectiveAccessLevel(current.effectiveAccessLevel) ===
12
+ normalizeEffectiveAccessLevel(next.effectiveAccessLevel))
13
+ return;
14
+ const containerId = next.id;
15
+ await tx
16
+ .delete(containerSyncWatermarks)
17
+ .where(and(eq(containerSyncWatermarks.laneKind, "container_documents"), eq(containerSyncWatermarks.laneId, containerId)))
18
+ .run();
19
+ // The discovery sequence is database-wide: this cancels all in-flight
20
+ // listings and evicts all cached heads, costing a retry/re-verification even
21
+ // in other organizations. Only this folder's pending candidates and cursor
22
+ // are removed; unrelated queues and cursors survive.
23
+ await clearDocumentDiscoveryEvidenceOnRemoteReset(tx, {
24
+ containerIds: [containerId],
25
+ documentIds: [],
26
+ });
27
+ }
28
+ //# sourceMappingURL=containerAccessDiscovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"containerAccessDiscovery.js","sourceRoot":"","sources":["containerAccessDiscovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,6BAA6B,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAE9D,OAAO,EAAE,2CAA2C,EAAE,MAAM,mDAAmD,CAAC;AAGhH;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,EAAgC,EAChC,OAAwB,EACxB,IAAqB;IAErB,IACE,6BAA6B,CAAC,OAAO,CAAC,oBAAoB,CAAC;QAC3D,6BAA6B,CAAC,IAAI,CAAC,oBAAoB,CAAC;QAExD,OAAO;IACT,MAAM,WAAW,GAAG,IAAI,CAAC,EAAE,CAAC;IAC5B,MAAM,EAAE;SACL,MAAM,CAAC,uBAAuB,CAAC;SAC/B,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,uBAAuB,CAAC,QAAQ,EAAE,qBAAqB,CAAC,EAC3D,EAAE,CAAC,uBAAuB,CAAC,MAAM,EAAE,WAAW,CAAC,CAChD,CACF;SACA,GAAG,EAAE,CAAC;IACT,sEAAsE;IACtE,6EAA6E;IAC7E,2EAA2E;IAC3E,qDAAqD;IACrD,MAAM,2CAA2C,CAAC,EAAE,EAAE;QACpD,YAAY,EAAE,CAAC,WAAW,CAAC;QAC3B,WAAW,EAAE,EAAE;KAChB,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { and, eq } from \"drizzle-orm\";\nimport { normalizeEffectiveAccessLevel } from \"../../keying/accessLevel\";\nimport { containerSyncWatermarks } from \"../../sqlite/schema\";\nimport type { ClientSQLiteTransactionScope } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { clearDocumentDiscoveryEvidenceOnRemoteReset } from \"../documents/documentDiscoveryEvidencePersistence\";\nimport type { ContainerRecord } from \"./containerPersistence\";\n\n/**\n * Held remote permissions change through the metadata CAS commit, which calls\n * this in its transaction. Insert and local-authoring saves have no prior\n * remote permission to invalidate. Documents' edit clocks do not track grants.\n */\nexport async function resetAccessDiscovery(\n tx: ClientSQLiteTransactionScope,\n current: ContainerRecord,\n next: ContainerRecord,\n): Promise<void> {\n if (\n normalizeEffectiveAccessLevel(current.effectiveAccessLevel) ===\n normalizeEffectiveAccessLevel(next.effectiveAccessLevel)\n )\n return;\n const containerId = next.id;\n await tx\n .delete(containerSyncWatermarks)\n .where(\n and(\n eq(containerSyncWatermarks.laneKind, \"container_documents\"),\n eq(containerSyncWatermarks.laneId, containerId),\n ),\n )\n .run();\n // The discovery sequence is database-wide: this cancels all in-flight\n // listings and evicts all cached heads, costing a retry/re-verification even\n // in other organizations. Only this folder's pending candidates and cursor\n // are removed; unrelated queues and cursors survive.\n await clearDocumentDiscoveryEvidenceOnRemoteReset(tx, {\n containerIds: [containerId],\n documentIds: [],\n });\n}\n"]}
@@ -6,6 +6,7 @@ import { runSerializedSqlMutation } from "../../sqlite/sqlSchema.js";
6
6
  import { enqueueDocumentPendingUpdate, } from "../documents/records/index.js";
7
7
  import { hasQueuedDocumentUpdates, settleQueuedDocumentUpdatesInTransaction, } from "../documents/records/documentPendingUpdatePersistence.js";
8
8
  import { clearDocumentSyncFailureInTransaction } from "../documents/records/documentSyncFailurePersistence.js";
9
+ import { resetAccessDiscovery } from "./containerAccessDiscovery.js";
9
10
  import { assertHeldContainerBinding, heldContainerBinding, } from "./containerBinding.js";
10
11
  import { settleContainerCreateIntentRevision } from "./containerCreateIntentSettlement.js";
11
12
  import { ContainerCreateIntentSupersededError, deleteContainerMoveIntentRevision, } from "./containerIntentPersistence.js";
@@ -164,6 +165,23 @@ function assertMutationKeepsHeldBinding(currentState, input) {
164
165
  ],
165
166
  });
166
167
  }
168
+ async function settleMetadataStructuralIntents(tx, input) {
169
+ if (input.createIntentSettlement &&
170
+ (await settleContainerCreateIntentRevision({
171
+ ...input.createIntentSettlement,
172
+ tx,
173
+ })) === "superseded") {
174
+ throw new ContainerCreateIntentSupersededError();
175
+ }
176
+ if (input.moveIntentSettlement) {
177
+ if (!(await deleteContainerMoveIntentRevision({
178
+ ...input.moveIntentSettlement,
179
+ tx,
180
+ }))) {
181
+ throw new Error("Container move intent was superseded before local settlement");
182
+ }
183
+ }
184
+ }
167
185
  export async function commitStoredMetadataMutation(execSql, input) {
168
186
  return runSerializedSqlMutation(execSql, async (lockedExecSql) => {
169
187
  const outcome = await getClientSQLitePersistenceRuntime(lockedExecSql).guardedTransaction(async (tx) => {
@@ -189,7 +207,7 @@ export async function commitStoredMetadataMutation(execSql, input) {
189
207
  mutation: input,
190
208
  tx,
191
209
  });
192
- const savedContainer = await saveContainerContentsContainerRows({
210
+ const saved = await saveContainerContentsContainerRows({
193
211
  container,
194
212
  createIntent: input.saveOptions?.createIntent,
195
213
  localUpdatedAt,
@@ -198,23 +216,10 @@ export async function commitStoredMetadataMutation(execSql, input) {
198
216
  serverTimestamps: input.saveOptions?.serverTimestamps,
199
217
  tx,
200
218
  });
201
- if (input.createIntentSettlement &&
202
- (await settleContainerCreateIntentRevision({
203
- ...input.createIntentSettlement,
204
- tx,
205
- })) === "superseded") {
206
- throw new ContainerCreateIntentSupersededError();
207
- }
208
- if (input.moveIntentSettlement) {
209
- if (!(await deleteContainerMoveIntentRevision({
210
- ...input.moveIntentSettlement,
211
- tx,
212
- }))) {
213
- throw new Error("Container move intent was superseded before local settlement");
214
- }
215
- }
219
+ await resetAccessDiscovery(tx, currentState.container, saved);
220
+ await settleMetadataStructuralIntents(tx, input);
216
221
  return committedMetadataMutationResult({
217
- container: savedContainer,
222
+ container: saved,
218
223
  createIntentSettled: input.createIntentSettlement !== undefined,
219
224
  moveIntentSettled: input.moveIntentSettlement !== undefined,
220
225
  });
@@ -1 +1 @@
1
- {"version":3,"file":"containerMetadataMutationPersistence.js","sourceRoot":"","sources":["containerMetadataMutationPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, or } from \"drizzle-orm\";\nimport { documentSyncPullContinuationsEqual } from \"../../documents/shared/sync/pullContinuation\";\nimport {\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { runSerializedSqlMutation } from \"../../sqlite/sqlSchema\";\nimport {\n enqueueDocumentPendingUpdate,\n type PendingUpdateFields,\n} from \"../documents/records\";\nimport {\n hasQueuedDocumentUpdates,\n settleQueuedDocumentUpdatesInTransaction,\n} from \"../documents/records/documentPendingUpdatePersistence\";\nimport { clearDocumentSyncFailureInTransaction } from \"../documents/records/documentSyncFailurePersistence\";\nimport {\n assertHeldContainerBinding,\n heldContainerBinding,\n} from \"./containerBinding\";\nimport type {\n ContainerContentsPersistence,\n ContainerMetadataRecord,\n StoredContainerState,\n} from \"./containerContentsPersistenceTypes\";\nimport { settleContainerCreateIntentRevision } from \"./containerCreateIntentSettlement\";\nimport {\n ContainerCreateIntentSupersededError,\n deleteContainerMoveIntentRevision,\n} from \"./containerIntentPersistence\";\nimport {\n getContainerMetadataScope,\n saveContainerContentsContainerRows,\n selectContainerMetadataRecord,\n} from \"./containerMetadataRows\";\nimport {\n type ContainerRecord,\n loadContainerById,\n} from \"./containerPersistence\";\nimport { CONTAINER_METADATA_APP_KIND } from \"./dormantContainerMetadata\";\n\nfunction sameNullableValue(\n left: string | null | undefined,\n right: string | null | undefined,\n): boolean {\n return (left ?? null) === (right ?? null);\n}\n\nfunction committedMetadataMutationResult(input: {\n container: ContainerRecord;\n createIntentSettled: boolean;\n moveIntentSettled: boolean;\n}) {\n return {\n committed: true as const,\n container: input.container,\n ...(input.createIntentSettled\n ? { createIntentSettled: true as const }\n : {}),\n ...(input.moveIntentSettled ? { moveIntentSettled: true as const } : {}),\n };\n}\n\nfunction sameMetadataSecurityIdentity(\n current: ContainerMetadataRecord,\n expected: ContainerMetadataRecord,\n): boolean {\n return (\n current.id === expected.id &&\n current.documentId === expected.documentId &&\n current.accessEpoch === expected.accessEpoch &&\n sameNullableValue(current.accessStateHash, expected.accessStateHash) &&\n sameNullableValue(current.contentKeyBundle, expected.contentKeyBundle) &&\n sameNullableValue(\n current.documentKekTargets,\n expected.documentKekTargets,\n ) &&\n sameNullableValue(\n current.documentManifestBundle,\n expected.documentManifestBundle,\n )\n );\n}\n\nfunction sameMetadataRecord(\n current: ContainerMetadataRecord,\n expected: ContainerMetadataRecord,\n): boolean {\n return (\n sameMetadataSecurityIdentity(current, expected) &&\n sameNullableValue(current.lastCommitLsn, expected.lastCommitLsn) &&\n current.metadataUpdates === expected.metadataUpdates &&\n current.snapshotEndVersion === expected.snapshotEndVersion &&\n documentSyncPullContinuationsEqual(\n current.pullContinuation,\n expected.pullContinuation,\n ) &&\n Boolean(current.pullContinuationRecoveryRequired) ===\n Boolean(expected.pullContinuationRecoveryRequired)\n );\n}\n\nfunction sameContainerRecord(\n current: ContainerRecord,\n expected: ContainerRecord,\n): boolean {\n return (\n current.id === expected.id &&\n current.parentId === expected.parentId &&\n current.organizationId === expected.organizationId &&\n current.metadataDocumentId === expected.metadataDocumentId &&\n (current.systemSlot ?? null) === (expected.systemSlot ?? null) &&\n (current.effectiveAccessLevel ?? null) ===\n (expected.effectiveAccessLevel ?? null) &&\n (current.localUpdatedAt ?? null) === (expected.localUpdatedAt ?? null) &&\n (current.serverCreatedAt ?? null) === (expected.serverCreatedAt ?? null) &&\n (current.serverUpdatedAt ?? null) === (expected.serverUpdatedAt ?? null) &&\n current.name === expected.name &&\n (current.icon ?? null) === (expected.icon ?? null)\n );\n}\n\nasync function loadStoredContainerState(\n execSql: Parameters<\n ContainerContentsPersistence[\"loadContainerMetadataState\"]\n >[0],\n containerId: string,\n): Promise<StoredContainerState | null> {\n const container = await loadContainerById(execSql, containerId);\n if (!container) return null;\n return {\n container,\n record: await selectContainerMetadataRecord(execSql, containerId),\n };\n}\n\nasync function deleteAcceptedPendingUpdates(\n tx: ClientSQLiteTransactionScope,\n containerId: string,\n ids: readonly string[],\n): Promise<void> {\n await settleQueuedDocumentUpdatesInTransaction(\n tx,\n getContainerMetadataScope(containerId),\n ids,\n );\n}\n\nfunction clearMetadataSyncFailure(\n tx: ClientSQLiteTransactionScope,\n containerId: string,\n): Promise<void> {\n return clearDocumentSyncFailureInTransaction(tx, {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n });\n}\n\nasync function hasPendingUpdates(\n tx: ClientSQLiteTransactionScope,\n containerId: string,\n): Promise<boolean> {\n return hasQueuedDocumentUpdates(tx, getContainerMetadataScope(containerId));\n}\n\nasync function hasPendingStructuralIntent(\n tx: ClientSQLiteTransactionScope,\n containerId: string,\n): Promise<boolean> {\n // A pending create or any surviving (unsynced) move of this container.\n const structural = await tx\n .select({ id: localIntents.id })\n .from(localIntents)\n .where(\n and(\n eq(localIntents.subjectId, containerId),\n or(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.containerCreate),\n eq(localIntents.syncStatus, \"pending\"),\n ),\n inLocalIntentLane(LOCAL_INTENT_LANES.containerMove),\n ),\n ),\n )\n .limit(1);\n return structural.length > 0;\n}\n\nasync function enqueuePendingUpdate(\n execSql: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[0],\n containerId: string,\n pendingUpdate: PendingUpdateFields | undefined,\n): Promise<void> {\n if (!pendingUpdate) return;\n await enqueueDocumentPendingUpdate(\n execSql,\n getContainerMetadataScope(containerId),\n pendingUpdate,\n );\n}\n\ntype CommitMetadataMutationInput = Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n>[1];\n\nfunction staleServerMutationResult(\n currentState: StoredContainerState | null,\n input: CommitMetadataMutationInput,\n) {\n const incomingServerUpdatedAt =\n input.saveOptions?.serverTimestamps?.updatedAt;\n const hasOlderContainerTimestamp =\n incomingServerUpdatedAt != null &&\n currentState?.container.serverUpdatedAt != null &&\n currentState.container.serverUpdatedAt > incomingServerUpdatedAt;\n const hasOlderMetadataAccessEpoch =\n currentState?.record != null &&\n currentState.record.documentId === input.record.documentId &&\n currentState.record.accessEpoch > input.record.accessEpoch;\n return currentState?.record &&\n (hasOlderContainerTimestamp || hasOlderMetadataAccessEpoch)\n ? {\n committed: false as const,\n currentState,\n staleServerState: true as const,\n }\n : null;\n}\n\nfunction metadataMutationStateMatches(\n currentState: StoredContainerState | null,\n input: CommitMetadataMutationInput,\n): currentState is StoredContainerState & {\n record: ContainerMetadataRecord;\n} {\n return Boolean(\n currentState?.record &&\n sameContainerRecord(currentState.container, input.expectedContainer) &&\n sameMetadataRecord(currentState.record, input.expectedRecord),\n );\n}\n\nasync function rejectMetadataMutationConflict(input: {\n currentState: StoredContainerState | null;\n mutation: CommitMetadataMutationInput;\n tx: ClientSQLiteTransactionScope;\n}) {\n const { currentState, mutation, tx } = input;\n if (\n currentState?.record &&\n mutation.settleAcceptedPendingOnConflict &&\n sameMetadataSecurityIdentity(currentState.record, mutation.expectedRecord)\n ) {\n await deleteAcceptedPendingUpdates(\n tx,\n mutation.container.id,\n mutation.acceptedPendingUpdateIds,\n );\n }\n return { committed: false as const, currentState };\n}\n\nasync function prepareContainerMutationWrite(input: {\n currentContainer: ContainerRecord;\n mutation: CommitMetadataMutationInput;\n tx: ClientSQLiteTransactionScope;\n}): Promise<{ container: ContainerRecord; localUpdatedAt: string }> {\n const { currentContainer, mutation, tx } = input;\n const requestedLocalUpdatedAt =\n mutation.saveOptions?.localUpdatedAt ??\n mutation.saveOptions?.updatedAt ??\n new Date().toISOString();\n const preserveDurableStructure =\n mutation.preserveDurableStructureWhenPending === true &&\n (await hasPendingStructuralIntent(tx, mutation.container.id));\n // Syncing metadata is not a local edit. Preserve the durable edit clock,\n // including an edit another pane made while this request was in flight.\n const localUpdatedAt =\n preserveDurableStructure || mutation.settleAcceptedPendingOnConflict\n ? (currentContainer.localUpdatedAt ?? requestedLocalUpdatedAt)\n : requestedLocalUpdatedAt;\n const structurallyRebasedContainer = preserveDurableStructure\n ? { ...mutation.container, parentId: currentContainer.parentId }\n : mutation.container;\n const didSettleOutgoing =\n mutation.acceptedPendingUpdateIds.length > 0 &&\n !(await hasPendingUpdates(tx, mutation.container.id)) &&\n !(await hasPendingStructuralIntent(tx, mutation.container.id));\n // Only container responses establish the server's structural clock. A\n // metadata acknowledgement supplies no such timestamp; marking it with our\n // wall clock would make later valid listings appear stale forever.\n return {\n container: structurallyRebasedContainer,\n localUpdatedAt: didSettleOutgoing\n ? (currentContainer.serverUpdatedAt ?? localUpdatedAt)\n : localUpdatedAt,\n };\n}\n\n/** Server acknowledgements and listings cannot rebind a held folder. */\nfunction assertMutationKeepsHeldBinding(\n currentState: StoredContainerState & { record: ContainerMetadataRecord },\n input: CommitMetadataMutationInput,\n): void {\n const recordDocumentIds =\n input.record.documentId === currentState.record.documentId\n ? []\n : [input.record.documentId];\n assertHeldContainerBinding(heldContainerBinding(currentState), {\n organizationId: input.container.organizationId,\n metadataDocumentIds: [\n input.container.metadataDocumentId,\n ...recordDocumentIds,\n ],\n });\n}\n\nexport async function commitStoredMetadataMutation(\n execSql: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[0],\n input: Parameters<ContainerContentsPersistence[\"commitMetadataMutation\"]>[1],\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const outcome = await getClientSQLitePersistenceRuntime(\n lockedExecSql,\n ).guardedTransaction(\n async (tx) => {\n const currentState = await loadStoredContainerState(\n lockedExecSql,\n input.container.id,\n );\n const staleServerResult = staleServerMutationResult(\n currentState,\n input,\n );\n if (staleServerResult) return staleServerResult;\n if (!metadataMutationStateMatches(currentState, input)) {\n return rejectMetadataMutationConflict({\n currentState,\n mutation: input,\n tx,\n });\n }\n assertMutationKeepsHeldBinding(currentState, input);\n\n await enqueuePendingUpdate(\n lockedExecSql,\n input.container.id,\n input.pendingUpdate,\n );\n await deleteAcceptedPendingUpdates(\n tx,\n input.container.id,\n input.acceptedPendingUpdateIds,\n );\n if (input.clearSyncFailure) {\n await clearMetadataSyncFailure(tx, input.container.id);\n }\n const { container, localUpdatedAt } =\n await prepareContainerMutationWrite({\n currentContainer: currentState.container,\n mutation: input,\n tx,\n });\n const savedContainer = await saveContainerContentsContainerRows({\n container,\n createIntent: input.saveOptions?.createIntent,\n localUpdatedAt,\n moveIntent: input.saveOptions?.moveIntent,\n record: input.record,\n serverTimestamps: input.saveOptions?.serverTimestamps,\n tx,\n });\n if (\n input.createIntentSettlement &&\n (await settleContainerCreateIntentRevision({\n ...input.createIntentSettlement,\n tx,\n })) === \"superseded\"\n ) {\n throw new ContainerCreateIntentSupersededError();\n }\n if (input.moveIntentSettlement) {\n if (\n !(await deleteContainerMoveIntentRevision({\n ...input.moveIntentSettlement,\n tx,\n }))\n ) {\n throw new Error(\n \"Container move intent was superseded before local settlement\",\n );\n }\n }\n return committedMetadataMutationResult({\n container: savedContainer,\n createIntentSettled: input.createIntentSettlement !== undefined,\n moveIntentSettled: input.moveIntentSettlement !== undefined,\n });\n },\n () => !input.stillCurrent || input.stillCurrent(),\n { behavior: \"immediate\" },\n );\n if (outcome.committed && outcome.result) {\n return outcome.result;\n }\n return {\n committed: false as const,\n currentState: await loadStoredContainerState(\n lockedExecSql,\n input.container.id,\n ),\n };\n });\n}\n\nexport async function settleStoredMetadataPendingUpdates(\n execSql: Parameters<\n ContainerContentsPersistence[\"settleAcceptedMetadataPendingUpdates\"]\n >[0],\n input: Parameters<\n ContainerContentsPersistence[\"settleAcceptedMetadataPendingUpdates\"]\n >[1],\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const outcome = await getClientSQLitePersistenceRuntime(\n lockedExecSql,\n ).guardedTransaction(\n async (tx) => {\n const currentState = await loadStoredContainerState(\n lockedExecSql,\n input.containerId,\n );\n if (\n currentState?.record &&\n sameMetadataSecurityIdentity(\n currentState.record,\n input.expectedRecord,\n )\n ) {\n await deleteAcceptedPendingUpdates(\n tx,\n input.containerId,\n input.pendingUpdateIds,\n );\n }\n return currentState;\n },\n () => !input.stillCurrent || input.stillCurrent(),\n { behavior: \"immediate\" },\n );\n return outcome.committed ? outcome.result : null;\n });\n}\n"]}
1
+ {"version":3,"file":"containerMetadataMutationPersistence.js","sourceRoot":"","sources":["containerMetadataMutationPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, or } from \"drizzle-orm\";\nimport { documentSyncPullContinuationsEqual } from \"../../documents/shared/sync/pullContinuation\";\nimport {\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { runSerializedSqlMutation } from \"../../sqlite/sqlSchema\";\nimport {\n enqueueDocumentPendingUpdate,\n type PendingUpdateFields,\n} from \"../documents/records\";\nimport {\n hasQueuedDocumentUpdates,\n settleQueuedDocumentUpdatesInTransaction,\n} from \"../documents/records/documentPendingUpdatePersistence\";\nimport { clearDocumentSyncFailureInTransaction } from \"../documents/records/documentSyncFailurePersistence\";\nimport { resetAccessDiscovery } from \"./containerAccessDiscovery\";\nimport {\n assertHeldContainerBinding,\n heldContainerBinding,\n} from \"./containerBinding\";\nimport type {\n ContainerContentsPersistence,\n ContainerMetadataRecord,\n StoredContainerState,\n} from \"./containerContentsPersistenceTypes\";\nimport { settleContainerCreateIntentRevision } from \"./containerCreateIntentSettlement\";\nimport {\n ContainerCreateIntentSupersededError,\n deleteContainerMoveIntentRevision,\n} from \"./containerIntentPersistence\";\nimport {\n getContainerMetadataScope,\n saveContainerContentsContainerRows,\n selectContainerMetadataRecord,\n} from \"./containerMetadataRows\";\nimport {\n type ContainerRecord,\n loadContainerById,\n} from \"./containerPersistence\";\nimport { CONTAINER_METADATA_APP_KIND } from \"./dormantContainerMetadata\";\n\nfunction sameNullableValue(\n left: string | null | undefined,\n right: string | null | undefined,\n): boolean {\n return (left ?? null) === (right ?? null);\n}\n\nfunction committedMetadataMutationResult(input: {\n container: ContainerRecord;\n createIntentSettled: boolean;\n moveIntentSettled: boolean;\n}) {\n return {\n committed: true as const,\n container: input.container,\n ...(input.createIntentSettled\n ? { createIntentSettled: true as const }\n : {}),\n ...(input.moveIntentSettled ? { moveIntentSettled: true as const } : {}),\n };\n}\n\nfunction sameMetadataSecurityIdentity(\n current: ContainerMetadataRecord,\n expected: ContainerMetadataRecord,\n): boolean {\n return (\n current.id === expected.id &&\n current.documentId === expected.documentId &&\n current.accessEpoch === expected.accessEpoch &&\n sameNullableValue(current.accessStateHash, expected.accessStateHash) &&\n sameNullableValue(current.contentKeyBundle, expected.contentKeyBundle) &&\n sameNullableValue(\n current.documentKekTargets,\n expected.documentKekTargets,\n ) &&\n sameNullableValue(\n current.documentManifestBundle,\n expected.documentManifestBundle,\n )\n );\n}\n\nfunction sameMetadataRecord(\n current: ContainerMetadataRecord,\n expected: ContainerMetadataRecord,\n): boolean {\n return (\n sameMetadataSecurityIdentity(current, expected) &&\n sameNullableValue(current.lastCommitLsn, expected.lastCommitLsn) &&\n current.metadataUpdates === expected.metadataUpdates &&\n current.snapshotEndVersion === expected.snapshotEndVersion &&\n documentSyncPullContinuationsEqual(\n current.pullContinuation,\n expected.pullContinuation,\n ) &&\n Boolean(current.pullContinuationRecoveryRequired) ===\n Boolean(expected.pullContinuationRecoveryRequired)\n );\n}\n\nfunction sameContainerRecord(\n current: ContainerRecord,\n expected: ContainerRecord,\n): boolean {\n return (\n current.id === expected.id &&\n current.parentId === expected.parentId &&\n current.organizationId === expected.organizationId &&\n current.metadataDocumentId === expected.metadataDocumentId &&\n (current.systemSlot ?? null) === (expected.systemSlot ?? null) &&\n (current.effectiveAccessLevel ?? null) ===\n (expected.effectiveAccessLevel ?? null) &&\n (current.localUpdatedAt ?? null) === (expected.localUpdatedAt ?? null) &&\n (current.serverCreatedAt ?? null) === (expected.serverCreatedAt ?? null) &&\n (current.serverUpdatedAt ?? null) === (expected.serverUpdatedAt ?? null) &&\n current.name === expected.name &&\n (current.icon ?? null) === (expected.icon ?? null)\n );\n}\n\nasync function loadStoredContainerState(\n execSql: Parameters<\n ContainerContentsPersistence[\"loadContainerMetadataState\"]\n >[0],\n containerId: string,\n): Promise<StoredContainerState | null> {\n const container = await loadContainerById(execSql, containerId);\n if (!container) return null;\n return {\n container,\n record: await selectContainerMetadataRecord(execSql, containerId),\n };\n}\n\nasync function deleteAcceptedPendingUpdates(\n tx: ClientSQLiteTransactionScope,\n containerId: string,\n ids: readonly string[],\n): Promise<void> {\n await settleQueuedDocumentUpdatesInTransaction(\n tx,\n getContainerMetadataScope(containerId),\n ids,\n );\n}\n\nfunction clearMetadataSyncFailure(\n tx: ClientSQLiteTransactionScope,\n containerId: string,\n): Promise<void> {\n return clearDocumentSyncFailureInTransaction(tx, {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n });\n}\n\nasync function hasPendingUpdates(\n tx: ClientSQLiteTransactionScope,\n containerId: string,\n): Promise<boolean> {\n return hasQueuedDocumentUpdates(tx, getContainerMetadataScope(containerId));\n}\n\nasync function hasPendingStructuralIntent(\n tx: ClientSQLiteTransactionScope,\n containerId: string,\n): Promise<boolean> {\n // A pending create or any surviving (unsynced) move of this container.\n const structural = await tx\n .select({ id: localIntents.id })\n .from(localIntents)\n .where(\n and(\n eq(localIntents.subjectId, containerId),\n or(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.containerCreate),\n eq(localIntents.syncStatus, \"pending\"),\n ),\n inLocalIntentLane(LOCAL_INTENT_LANES.containerMove),\n ),\n ),\n )\n .limit(1);\n return structural.length > 0;\n}\n\nasync function enqueuePendingUpdate(\n execSql: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[0],\n containerId: string,\n pendingUpdate: PendingUpdateFields | undefined,\n): Promise<void> {\n if (!pendingUpdate) return;\n await enqueueDocumentPendingUpdate(\n execSql,\n getContainerMetadataScope(containerId),\n pendingUpdate,\n );\n}\n\ntype CommitMetadataMutationInput = Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n>[1];\n\nfunction staleServerMutationResult(\n currentState: StoredContainerState | null,\n input: CommitMetadataMutationInput,\n) {\n const incomingServerUpdatedAt =\n input.saveOptions?.serverTimestamps?.updatedAt;\n const hasOlderContainerTimestamp =\n incomingServerUpdatedAt != null &&\n currentState?.container.serverUpdatedAt != null &&\n currentState.container.serverUpdatedAt > incomingServerUpdatedAt;\n const hasOlderMetadataAccessEpoch =\n currentState?.record != null &&\n currentState.record.documentId === input.record.documentId &&\n currentState.record.accessEpoch > input.record.accessEpoch;\n return currentState?.record &&\n (hasOlderContainerTimestamp || hasOlderMetadataAccessEpoch)\n ? {\n committed: false as const,\n currentState,\n staleServerState: true as const,\n }\n : null;\n}\n\nfunction metadataMutationStateMatches(\n currentState: StoredContainerState | null,\n input: CommitMetadataMutationInput,\n): currentState is StoredContainerState & {\n record: ContainerMetadataRecord;\n} {\n return Boolean(\n currentState?.record &&\n sameContainerRecord(currentState.container, input.expectedContainer) &&\n sameMetadataRecord(currentState.record, input.expectedRecord),\n );\n}\n\nasync function rejectMetadataMutationConflict(input: {\n currentState: StoredContainerState | null;\n mutation: CommitMetadataMutationInput;\n tx: ClientSQLiteTransactionScope;\n}) {\n const { currentState, mutation, tx } = input;\n if (\n currentState?.record &&\n mutation.settleAcceptedPendingOnConflict &&\n sameMetadataSecurityIdentity(currentState.record, mutation.expectedRecord)\n ) {\n await deleteAcceptedPendingUpdates(\n tx,\n mutation.container.id,\n mutation.acceptedPendingUpdateIds,\n );\n }\n return { committed: false as const, currentState };\n}\n\nasync function prepareContainerMutationWrite(input: {\n currentContainer: ContainerRecord;\n mutation: CommitMetadataMutationInput;\n tx: ClientSQLiteTransactionScope;\n}): Promise<{ container: ContainerRecord; localUpdatedAt: string }> {\n const { currentContainer, mutation, tx } = input;\n const requestedLocalUpdatedAt =\n mutation.saveOptions?.localUpdatedAt ??\n mutation.saveOptions?.updatedAt ??\n new Date().toISOString();\n const preserveDurableStructure =\n mutation.preserveDurableStructureWhenPending === true &&\n (await hasPendingStructuralIntent(tx, mutation.container.id));\n // Syncing metadata is not a local edit. Preserve the durable edit clock,\n // including an edit another pane made while this request was in flight.\n const localUpdatedAt =\n preserveDurableStructure || mutation.settleAcceptedPendingOnConflict\n ? (currentContainer.localUpdatedAt ?? requestedLocalUpdatedAt)\n : requestedLocalUpdatedAt;\n const structurallyRebasedContainer = preserveDurableStructure\n ? { ...mutation.container, parentId: currentContainer.parentId }\n : mutation.container;\n const didSettleOutgoing =\n mutation.acceptedPendingUpdateIds.length > 0 &&\n !(await hasPendingUpdates(tx, mutation.container.id)) &&\n !(await hasPendingStructuralIntent(tx, mutation.container.id));\n // Only container responses establish the server's structural clock. A\n // metadata acknowledgement supplies no such timestamp; marking it with our\n // wall clock would make later valid listings appear stale forever.\n return {\n container: structurallyRebasedContainer,\n localUpdatedAt: didSettleOutgoing\n ? (currentContainer.serverUpdatedAt ?? localUpdatedAt)\n : localUpdatedAt,\n };\n}\n\n/** Server acknowledgements and listings cannot rebind a held folder. */\nfunction assertMutationKeepsHeldBinding(\n currentState: StoredContainerState & { record: ContainerMetadataRecord },\n input: CommitMetadataMutationInput,\n): void {\n const recordDocumentIds =\n input.record.documentId === currentState.record.documentId\n ? []\n : [input.record.documentId];\n assertHeldContainerBinding(heldContainerBinding(currentState), {\n organizationId: input.container.organizationId,\n metadataDocumentIds: [\n input.container.metadataDocumentId,\n ...recordDocumentIds,\n ],\n });\n}\n\nasync function settleMetadataStructuralIntents(\n tx: ClientSQLiteTransactionScope,\n input: CommitMetadataMutationInput,\n): Promise<void> {\n if (\n input.createIntentSettlement &&\n (await settleContainerCreateIntentRevision({\n ...input.createIntentSettlement,\n tx,\n })) === \"superseded\"\n ) {\n throw new ContainerCreateIntentSupersededError();\n }\n if (input.moveIntentSettlement) {\n if (\n !(await deleteContainerMoveIntentRevision({\n ...input.moveIntentSettlement,\n tx,\n }))\n ) {\n throw new Error(\n \"Container move intent was superseded before local settlement\",\n );\n }\n }\n}\n\nexport async function commitStoredMetadataMutation(\n execSql: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[0],\n input: Parameters<ContainerContentsPersistence[\"commitMetadataMutation\"]>[1],\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const outcome = await getClientSQLitePersistenceRuntime(\n lockedExecSql,\n ).guardedTransaction(\n async (tx) => {\n const currentState = await loadStoredContainerState(\n lockedExecSql,\n input.container.id,\n );\n const staleServerResult = staleServerMutationResult(\n currentState,\n input,\n );\n if (staleServerResult) return staleServerResult;\n if (!metadataMutationStateMatches(currentState, input)) {\n return rejectMetadataMutationConflict({\n currentState,\n mutation: input,\n tx,\n });\n }\n assertMutationKeepsHeldBinding(currentState, input);\n\n await enqueuePendingUpdate(\n lockedExecSql,\n input.container.id,\n input.pendingUpdate,\n );\n await deleteAcceptedPendingUpdates(\n tx,\n input.container.id,\n input.acceptedPendingUpdateIds,\n );\n if (input.clearSyncFailure) {\n await clearMetadataSyncFailure(tx, input.container.id);\n }\n const { container, localUpdatedAt } =\n await prepareContainerMutationWrite({\n currentContainer: currentState.container,\n mutation: input,\n tx,\n });\n const saved = await saveContainerContentsContainerRows({\n container,\n createIntent: input.saveOptions?.createIntent,\n localUpdatedAt,\n moveIntent: input.saveOptions?.moveIntent,\n record: input.record,\n serverTimestamps: input.saveOptions?.serverTimestamps,\n tx,\n });\n await resetAccessDiscovery(tx, currentState.container, saved);\n await settleMetadataStructuralIntents(tx, input);\n return committedMetadataMutationResult({\n container: saved,\n createIntentSettled: input.createIntentSettlement !== undefined,\n moveIntentSettled: input.moveIntentSettlement !== undefined,\n });\n },\n () => !input.stillCurrent || input.stillCurrent(),\n { behavior: \"immediate\" },\n );\n if (outcome.committed && outcome.result) {\n return outcome.result;\n }\n return {\n committed: false as const,\n currentState: await loadStoredContainerState(\n lockedExecSql,\n input.container.id,\n ),\n };\n });\n}\n\nexport async function settleStoredMetadataPendingUpdates(\n execSql: Parameters<\n ContainerContentsPersistence[\"settleAcceptedMetadataPendingUpdates\"]\n >[0],\n input: Parameters<\n ContainerContentsPersistence[\"settleAcceptedMetadataPendingUpdates\"]\n >[1],\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const outcome = await getClientSQLitePersistenceRuntime(\n lockedExecSql,\n ).guardedTransaction(\n async (tx) => {\n const currentState = await loadStoredContainerState(\n lockedExecSql,\n input.containerId,\n );\n if (\n currentState?.record &&\n sameMetadataSecurityIdentity(\n currentState.record,\n input.expectedRecord,\n )\n ) {\n await deleteAcceptedPendingUpdates(\n tx,\n input.containerId,\n input.pendingUpdateIds,\n );\n }\n return currentState;\n },\n () => !input.stillCurrent || input.stillCurrent(),\n { behavior: \"immediate\" },\n );\n return outcome.committed ? outcome.result : null;\n });\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import { type ExecSql } from "../../sqlite/sqlSchema.js";
2
2
  import { type DiscoveredDocumentCandidate } from "./documentDiscoveryCandidate.js";
3
3
  export type { DiscoveredDocumentCandidate } from "./documentDiscoveryCandidate.js";
4
+ export { clearDocumentDiscoveryEvidenceOnRemoteReset } from "./documentDiscoveryReset.js";
4
5
  export interface CachedDiscoveryHead {
5
6
  readonly organizationId: string | null;
6
7
  readonly accessEpoch: number;
@@ -10,6 +10,7 @@ import { loadAccessManifestCheckpoint } from "../keyingCheckpoints/keyingCheckpo
10
10
  import { heldTombstoneRetryDelayMs } from "./containerDocumentTombstoneHoldsPersistence.js";
11
11
  import { isDiscoveredDocumentCandidate, readStoredDiscoveryCandidate, } from "./documentDiscoveryCandidate.js";
12
12
  import { isDocumentDiscoveryGenerationCurrent } from "./documentDiscoveryReset.js";
13
+ export { clearDocumentDiscoveryEvidenceOnRemoteReset } from "./documentDiscoveryReset.js";
13
14
  const pending = pendingDocumentDiscoveries;
14
15
  const heads = documentDiscoveryHeads;
15
16
  const ID_BATCH_SIZE = 400;
@@ -1 +1 @@
1
- {"version":3,"file":"documentDiscoveryEvidencePersistence.js","sourceRoot":"","sources":["documentDiscoveryEvidencePersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,iCAAiC,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,qDAAqD,CAAC;AAC7D,OAAO,EAAE,iCAAiC,EAAE,MAAM,uCAAuC,CAAC;AAC1F,OAAO,EAEL,eAAe,EACf,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,2BAA2B,EAAE,MAAM,yDAAyD,CAAC;AACtG,OAAO,EAAE,4BAA4B,EAAE,MAAM,kDAAkD,CAAC;AAChG,OAAO,EAAE,yBAAyB,EAAE,MAAM,8CAA8C,CAAC;AAEzF,OAAO,EAEL,6BAA6B,EAC7B,4BAA4B,GAC7B,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAE,oCAAoC,EAAE,MAAM,0BAA0B,CAAC;AA2ChF,MAAM,OAAO,GAAG,0BAA0B,CAAC;AAC3C,MAAM,KAAK,GAAG,sBAAsB,CAAC;AACrC,MAAM,aAAa,GAAG,GAAG,CAAC;AAC1B,MAAM,OAAO,GAAG,CAAC,GAAsB,EAAE,EAAE,CACzC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,aAAa,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACrE,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,aAAa,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,aAAa,CAAC,CACtD,CAAC;AACJ,MAAM,QAAQ,GAAG,CAAC,KAAkC,EAAE,EAAE,CACtD,GAAG,CACD,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,WAAW,CAAC,EAC1C,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,EACxC,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAC7C,CAAC;AAEJ,MAAM,iCAAiC;IAIlB;IACA;IAFnB,YACmB,OAAgB,EAChB,GAAiB;QADjB,YAAO,GAAP,OAAO,CAAS;QAChB,QAAG,GAAH,GAAG,CAAc;IACjC,CAAC;IAEI,IAAI,GAAG,KAAK,IAAI,EAAE;QACxB,MAAM,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,iCAAiC,CAAC,CAAC;QACvE,OAAO,iCAAiC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;IAC5D,CAAC,CAAC;IACM,KAAK,GAAG,KAAK,EACnB,SAEe,EACf,EAAE,CACF,wBAAwB,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QACtD,MAAM,eAAe,CAAC,MAAM,EAAE,iCAAiC,CAAC,CAAC;QACjE,OAAO,SAAS,CAAC,iCAAiC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IACL,KAAK,GAAG,KAAK,IAAI,EAAE;QACjB,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;YAC5B,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE;iBACnB,MAAM,CAAC,yBAAyB,CAAC;iBACjC,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;iBACzC,kBAAkB,CAAC;gBAClB,MAAM,EAAE,yBAAyB,CAAC,EAAE;gBACpC,GAAG,EAAE;oBACH,UAAU,EAAE,GAAG,CAAA,GAAG,yBAAyB,CAAC,UAAU,MAAM;iBAC7D;aACF,CAAC;iBACD,SAAS,CAAC,EAAE,UAAU,EAAE,yBAAyB,CAAC,UAAU,EAAE,CAAC,CAAC;YACnE,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC;gBAC/C,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;YAChE,UAAU,GAAG,GAAG,CAAC,UAAU,CAAC;QAC9B,CAAC,CAAC,CAAC;QACH,OAAO,UAAU,CAAC;IACpB,CAAC,CAAC;IACF,SAAS,GAAG,KAAK,EAAE,UAAkB,EAAE,EAAE,CACvC,oCAAoC,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,UAAU,CAAC,CAAC;IACtE,KAAK,GAAG,CACN,MAA8C,EAC9C,UAAkB,EAClB,UAAgD,EAAE,EAClD,EAAE;QACF,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;YAC7B,IAAI,CAAC,CAAC,MAAM,oCAAoC,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;gBAC/D,OAAO,KAAK,CAAC;YACf,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC7D,MAAM,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;gBAChC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;oBAC3B,IAAI,CAAC,6BAA6B,CAAC,KAAK,CAAC;wBAAE,SAAS;oBACpD,KAAK,MAAM,WAAW,IAAI,KAAK,CAAC,kBAAkB,EAAE,CAAC;wBACnD,MAAM,QAAQ,GAAG;4BACf,GAAG,KAAK;4BACR,WAAW;4BACX,kBAAkB,EAAE,CAAC,WAAW,CAAC;yBAClC,CAAC;wBACF,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;wBAC3C,MAAM,EAAE;6BACL,MAAM,CAAC,OAAO,CAAC;6BACf,MAAM,CAAC;4BACN,WAAW;4BACX,UAAU,EAAE,KAAK,CAAC,UAAU;4BAC5B,WAAW,EAAE,KAAK,CAAC,WAAW;4BAC9B,UAAU;4BACV,SAAS;4BACT,OAAO,EAAE,CAAC;yBACX,CAAC;6BACD,kBAAkB,CAAC;4BAClB,MAAM,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,UAAU,CAAC;4BACjD,GAAG,EAAE;gCACH,WAAW,EAAE,KAAK,CAAC,WAAW;gCAC9B,UAAU;gCACV,SAAS;gCACT,OAAO,EAAE,GAAG,CAAA,aAAa,OAAO,CAAC,SAAS,MAAM,SAAS,SAAS,OAAO,CAAC,OAAO,aAAa;gCAC9F,QAAQ,EAAE,GAAG,CAAA,aAAa,OAAO,CAAC,SAAS,MAAM,SAAS,SAAS,OAAO,CAAC,QAAQ,aAAa;6BACjG;4BACD,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,UAAU,CAAC;yBAC9C,CAAC;6BACD,GAAG,EAAE,CAAC;oBACX,CAAC;gBACH,CAAC;gBACD,wEAAwE;gBACxE,wEAAwE;gBACxE,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;oBAC1B,MAAM,EAAE;yBACL,MAAM,CAAC,OAAO,CAAC;yBACf,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,WAAW,CAAC,EACxC,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC,EACtC,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,UAAU,CAAC,CACpC,CACF;yBACA,GAAG,EAAE,CAAC;gBACX,CAAC;YACH,CAAC,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IACF,OAAO,GAAG,KAAK,EAAE,YAA+B,EAAE,KAAa,EAAE,EAAE;QACjE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAkC,EAAE,CAAC;QACjD,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YAC1C,IAAI,MAAM,CAAC,MAAM,IAAI,KAAK;gBAAE,MAAM;YAClC,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC;iBACxC,IAAI,CAAC,OAAO,CAAC;iBACb,KAAK,CACJ,GAAG,CACD,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,EACxC,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CACjC,CACF;iBACA,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;iBACtD,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;YAChC,MAAM,CAAC,IAAI,CACT,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,4BAA4B,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAClE,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IACF,KAAK,GAAG,CAAC,KAAkC,EAAE,UAAkB,EAAE,EAAE,CACjE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QACtB,IAAI,CAAC,CAAC,MAAM,oCAAoC,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;YAAE,OAAO;QAC1E,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE;aACnB,MAAM,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;aACtC,IAAI,CAAC,OAAO,CAAC;aACb,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;aACtB,KAAK,CAAC,CAAC,CAAC,CAAC;QACZ,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAC/C,MAAM,EAAE;aACL,MAAM,CAAC,OAAO,CAAC;aACf,GAAG,CAAC;YACH,QAAQ;YACR,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,yBAAyB,CAAC,QAAQ,CAAC;SAC1D,CAAC;aACD,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;aACtB,GAAG,EAAE,CAAC;IACX,CAAC,CAAC,CAAC;IACL,WAAW,GAAG,CACZ,MAA8C,EAC9C,UAAkB,EAClB,EAAE,CACF,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QACtB,IAAI,CAAC,CAAC,MAAM,oCAAoC,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;YAAE,OAAO;QAC1E,MAAM,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;YAChC,KAAK,MAAM,KAAK,IAAI,MAAM;gBACxB,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;QAC1D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACL,UAAU,GAAG,KAAK,EAAE,YAA+B,EAAE,EAAE;QACrD,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC7B,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YAC1C,IACE,CACE,MAAM,EAAE;iBACL,MAAM,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;iBAC1C,IAAI,CAAC,OAAO,CAAC;iBACb,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;iBAC/C,KAAK,CAAC,CAAC,CAAC,CACZ,CAAC,MAAM;gBAER,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IACF,UAAU,GAAG,KAAK,EAAE,YAA+B,EAAE,EAAE;QACrD,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,GAAG,GAAkB,IAAI,CAAC;QAC9B,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YAC1C,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE;iBACnB,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;iBACrC,IAAI,CAAC,OAAO,CAAC;iBACb,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACnD,IAAI,GAAG,EAAE,GAAG,KAAK,IAAI,IAAI,GAAG,EAAE,GAAG,KAAK,SAAS;gBAC7C,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC7D,CAAC,CAAC;IACF,QAAQ,GAAG,KAAK,EACd,UAAkB,EAClB,YAAuC,EACvC,EAAE;QACF,IAAI,MAAM,2BAA2B,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC;YAC7D,OAAO;gBACL,cAAc,EAAE,IAAI;gBACpB,WAAW,EAAE,MAAM,CAAC,gBAAgB;gBACpC,kBAAkB,EAAE,EAAE;aACvB,CAAC;QACJ,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,CAAC;QAC/B,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE;aACnB,MAAM,EAAE;aACR,IAAI,CAAC,KAAK,CAAC;aACX,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,UAAU,CAAC,EAChC,EAAE,CAAC,KAAK,CAAC,YAAY,EAAE,YAAY,CAAC,CACrC,CACF;aACA,KAAK,CAAC,CAAC,CAAC,CAAC;QACZ,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,uEAAuE;QACvE,0EAA0E;QAC1E,wEAAwE;QACxE,4EAA4E;QAC5E,MAAM,UAAU,GAAG,MAAM,4BAA4B,CACnD,IAAI,CAAC,OAAO,EACZ,UAAU,EACV,GAAG,CAAC,cAAc,EAClB,UAAU,CACX,CAAC;QACF,IACE,UAAU,EAAE,YAAY,KAAK,GAAG,CAAC,YAAY;YAC7C,UAAU,CAAC,KAAK,KAAK,GAAG,CAAC,WAAW;YAEpC,OAAO,IAAI,CAAC;QACd,OAAO;YACL,cAAc,EAAE,GAAG,CAAC,cAAc;YAClC,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,eAAe,EAAE,GAAG,CAAC,YAAY;YACjC,kBAAkB,EAAE,eAAe,CACjC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,EACzB,wBAAwB,CACzB;SACF,CAAC;IACJ,CAAC,CAAC;IACF,QAAQ,GAAG,CACT,UAAkB,EAClB,IAAyB,EACzB,UAAkB,EAClB,EAAE,CACF,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QACtB,IACE,CAAC,IAAI,CAAC,eAAe;YACrB,CAAC,IAAI,CAAC,cAAc;YACpB,CAAC,CAAC,MAAM,oCAAoC,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;YAE7D,OAAO,KAAK,CAAC;QACf,MAAM,GAAG,GAAG;YACV,UAAU;YACV,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,YAAY,EAAE,IAAI,CAAC,eAAe;YAClC,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,kBAAkB,CAAC;SACnD,CAAC;QACF,MAAM,EAAE;aACL,MAAM,CAAC,KAAK,CAAC;aACb,MAAM,CAAC,GAAG,CAAC;aACX,kBAAkB,CAAC;YAClB,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,cAAc,CAAC;YAChD,GAAG,EAAE,GAAG;YACR,QAAQ,EAAE,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC;SACnD,CAAC;aACD,GAAG,EAAE,CAAC;QACT,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;CACN;AAiBD,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,mCAAmC,CACvD,OAAgB,EAChB,QAA0C;IAE1C,MAAM,EAAE,GAAG,iCAAiC,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;IACzD,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG;YACV,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,YAAY,EAAE,IAAI,CAAC,eAAe;YAClC,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;SACxE,CAAC;QACF,MAAM,EAAE;aACL,MAAM,CAAC,KAAK,CAAC;aACb,MAAM,CAAC,GAAG,CAAC;aACX,kBAAkB,CAAC;YAClB,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,cAAc,CAAC;YAChD,GAAG,EAAE,GAAG;YACR,QAAQ,EAAE,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC;SACnD,CAAC;aACD,GAAG,EAAE,CAAC;IACX,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oCAAoC,CAClD,OAAgB,EAChB,GAAG,GAAG,IAAI,CAAC,GAAG;IAEd,OAAO,IAAI,iCAAiC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AAC7D,CAAC","sourcesContent":["import { and, asc, eq, inArray, lte, min, sql } from \"drizzle-orm\";\nimport { uniqueSortedStrings } from \"../../documents/shared/readers\";\nimport { readStringArray } from \"../../shared/recordReaders\";\nimport { documentContainerProjectionTables } from \"../../sqlite/schema\";\nimport {\n documentDiscoveryHeads,\n documentDiscoverySequence,\n pendingDocumentDiscoveries,\n} from \"../../sqlite/schema/documentDiscoveryEvidenceSchema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport { loadDocumentPurgeCheckpoint } from \"../keyingCheckpoints/documentPurgeCheckpointPersistence\";\nimport { loadAccessManifestCheckpoint } from \"../keyingCheckpoints/keyingCheckpointPersistence\";\nimport { heldTombstoneRetryDelayMs } from \"./containerDocumentTombstoneHoldsPersistence\";\n\nimport {\n type DiscoveredDocumentCandidate,\n isDiscoveredDocumentCandidate,\n readStoredDiscoveryCandidate,\n} from \"./documentDiscoveryCandidate\";\n\nimport { isDocumentDiscoveryGenerationCurrent } from \"./documentDiscoveryReset\";\n\nexport type { DiscoveredDocumentCandidate } from \"./documentDiscoveryCandidate\";\nexport interface CachedDiscoveryHead {\n readonly organizationId: string | null;\n readonly accessEpoch: number;\n readonly accessStateHash?: string;\n readonly linkedContainerIds: readonly string[];\n}\ninterface RemovedDiscoveryPlacement {\n readonly containerId: string;\n readonly documentId: string;\n}\nexport interface DocumentDiscoveryEvidenceStore {\n begin(): Promise<number>;\n isCurrent(generation: number): Promise<boolean>;\n stage(\n inputs: readonly DiscoveredDocumentCandidate[],\n generation: number,\n removed?: readonly RemovedDiscoveryPlacement[],\n ): Promise<boolean>;\n pending(\n containerIds: readonly string[],\n limit: number,\n ): Promise<readonly DiscoveredDocumentCandidate[]>;\n defer(input: DiscoveredDocumentCandidate, generation: number): Promise<void>;\n acknowledge(\n inputs: readonly DiscoveredDocumentCandidate[],\n generation: number,\n ): Promise<void>;\n hasPending(containerIds: readonly string[]): Promise<boolean>;\n retryDelay(containerIds: readonly string[]): Promise<number | null>;\n loadHead(\n documentId: string,\n manifestHash: string | null | undefined,\n ): Promise<CachedDiscoveryHead | null>;\n saveHead(\n documentId: string,\n head: CachedDiscoveryHead,\n generation: number,\n ): Promise<boolean>;\n}\n\nconst pending = pendingDocumentDiscoveries;\nconst heads = documentDiscoveryHeads;\nconst ID_BATCH_SIZE = 400;\nconst batches = (ids: readonly string[]) =>\n Array.from({ length: Math.ceil(ids.length / ID_BATCH_SIZE) }, (_, i) =>\n ids.slice(i * ID_BATCH_SIZE, (i + 1) * ID_BATCH_SIZE),\n );\nconst identity = (input: DiscoveredDocumentCandidate) =>\n and(\n eq(pending.containerId, input.containerId),\n eq(pending.documentId, input.documentId),\n eq(pending.inputJson, JSON.stringify(input)),\n );\n\nclass SqlDocumentDiscoveryEvidenceStore\n implements DocumentDiscoveryEvidenceStore\n{\n constructor(\n private readonly execSql: ExecSql,\n private readonly now: () => number,\n ) {}\n\n private read = async () => {\n await ensureSqlTables(this.execSql, documentContainerProjectionTables);\n return getClientSQLitePersistenceRuntime(this.execSql).db;\n };\n private write = async <T>(\n operation: (\n db: ReturnType<typeof getClientSQLitePersistenceRuntime>[\"db\"],\n ) => Promise<T>,\n ) =>\n runSerializedSqlMutation(this.execSql, async (locked) => {\n await ensureSqlTables(locked, documentContainerProjectionTables);\n return operation(getClientSQLitePersistenceRuntime(locked).db);\n });\n begin = async () => {\n let generation = 0;\n await this.write(async (db) => {\n const [row] = await db\n .insert(documentDiscoverySequence)\n .values({ id: \"requests\", generation: 1 })\n .onConflictDoUpdate({\n target: documentDiscoverySequence.id,\n set: {\n generation: sql`${documentDiscoverySequence.generation} + 1`,\n },\n })\n .returning({ generation: documentDiscoverySequence.generation });\n if (!row || !Number.isSafeInteger(row.generation))\n throw new Error(\"Document discovery generation is exhausted\");\n generation = row.generation;\n });\n return generation;\n };\n isCurrent = async (generation: number) =>\n isDocumentDiscoveryGenerationCurrent(await this.read(), generation);\n stage = (\n inputs: readonly DiscoveredDocumentCandidate[],\n generation: number,\n removed: readonly RemovedDiscoveryPlacement[] = [],\n ) => {\n return this.write(async (db) => {\n if (!(await isDocumentDiscoveryGenerationCurrent(db, generation)))\n return false;\n if (inputs.length === 0 && removed.length === 0) return true;\n await db.transaction(async (tx) => {\n for (const input of inputs) {\n if (!isDiscoveredDocumentCandidate(input)) continue;\n for (const containerId of input.listedContainerIds) {\n const rowInput = {\n ...input,\n containerId,\n listedContainerIds: [containerId],\n };\n const inputJson = JSON.stringify(rowInput);\n await tx\n .insert(pending)\n .values({\n containerId,\n documentId: input.documentId,\n accessEpoch: input.accessEpoch,\n generation,\n inputJson,\n retryAt: 0,\n })\n .onConflictDoUpdate({\n target: [pending.containerId, pending.documentId],\n set: {\n accessEpoch: input.accessEpoch,\n generation,\n inputJson,\n retryAt: sql`case when ${pending.inputJson} = ${inputJson} then ${pending.retryAt} else 0 end`,\n attempts: sql`case when ${pending.inputJson} = ${inputJson} then ${pending.attempts} else 0 end`,\n },\n setWhere: lte(pending.generation, generation),\n })\n .run();\n }\n }\n // These are untrusted, unapplied hints only. Removing an existing local\n // placement still requires the separate signed tombstone evidence gate.\n for (const row of removed) {\n await tx\n .delete(pending)\n .where(\n and(\n eq(pending.containerId, row.containerId),\n eq(pending.documentId, row.documentId),\n lte(pending.generation, generation),\n ),\n )\n .run();\n }\n });\n return true;\n });\n };\n pending = async (containerIds: readonly string[], limit: number) => {\n const db = await this.read();\n const result: DiscoveredDocumentCandidate[] = [];\n for (const batch of batches(containerIds)) {\n if (result.length >= limit) break;\n const rows = await db\n .select({ inputJson: pending.inputJson })\n .from(pending)\n .where(\n and(\n inArray(pending.containerId, [...batch]),\n lte(pending.retryAt, this.now()),\n ),\n )\n .orderBy(asc(pending.retryAt), asc(pending.documentId))\n .limit(limit - result.length);\n result.push(\n ...rows.map((row) => readStoredDiscoveryCandidate(row.inputJson)),\n );\n }\n return result;\n };\n defer = (input: DiscoveredDocumentCandidate, generation: number) =>\n this.write(async (db) => {\n if (!(await isDocumentDiscoveryGenerationCurrent(db, generation))) return;\n const [row] = await db\n .select({ attempts: pending.attempts })\n .from(pending)\n .where(identity(input))\n .limit(1);\n if (!row) return;\n const attempts = Math.min(row.attempts + 1, 8);\n await db\n .update(pending)\n .set({\n attempts,\n retryAt: this.now() + heldTombstoneRetryDelayMs(attempts),\n })\n .where(identity(input))\n .run();\n });\n acknowledge = (\n inputs: readonly DiscoveredDocumentCandidate[],\n generation: number,\n ) =>\n this.write(async (db) => {\n if (!(await isDocumentDiscoveryGenerationCurrent(db, generation))) return;\n await db.transaction(async (tx) => {\n for (const input of inputs)\n await tx.delete(pending).where(identity(input)).run();\n });\n });\n hasPending = async (containerIds: readonly string[]) => {\n const db = await this.read();\n for (const batch of batches(containerIds)) {\n if (\n (\n await db\n .select({ documentId: pending.documentId })\n .from(pending)\n .where(inArray(pending.containerId, [...batch]))\n .limit(1)\n ).length\n )\n return true;\n }\n return false;\n };\n retryDelay = async (containerIds: readonly string[]) => {\n const db = await this.read();\n let due: number | null = null;\n for (const batch of batches(containerIds)) {\n const [row] = await db\n .select({ due: min(pending.retryAt) })\n .from(pending)\n .where(inArray(pending.containerId, [...batch]));\n if (row?.due !== null && row?.due !== undefined)\n due = Math.min(due ?? row.due, row.due);\n }\n return due === null ? null : Math.max(0, due - this.now());\n };\n loadHead = async (\n documentId: string,\n manifestHash: string | null | undefined,\n ) => {\n if (await loadDocumentPurgeCheckpoint(this.execSql, documentId))\n return {\n organizationId: null,\n accessEpoch: Number.MAX_SAFE_INTEGER,\n linkedContainerIds: [],\n };\n if (!manifestHash) return null;\n const db = await this.read();\n const [row] = await db\n .select()\n .from(heads)\n .where(\n and(\n eq(heads.documentId, documentId),\n eq(heads.manifestHash, manifestHash),\n ),\n )\n .limit(1);\n if (!row) return null;\n // A different verifier (for example tombstone removal) can advance the\n // signed checkpoint without updating documents.accessEpoch or this cache.\n // Old-organization pins survive recovery; only the cached signed head's\n // organization can authorize reuse. Missing pins require full verification.\n const checkpoint = await loadAccessManifestCheckpoint(\n this.execSql,\n \"document\",\n row.organizationId,\n documentId,\n );\n if (\n checkpoint?.manifestHash !== row.manifestHash ||\n checkpoint.epoch !== row.accessEpoch\n )\n return null;\n return {\n organizationId: row.organizationId,\n accessEpoch: row.accessEpoch,\n accessStateHash: row.manifestHash,\n linkedContainerIds: readStringArray(\n JSON.parse(row.linksJson),\n \"stored discovery links\",\n ),\n };\n };\n saveHead = (\n documentId: string,\n head: CachedDiscoveryHead,\n generation: number,\n ) =>\n this.write(async (db) => {\n if (\n !head.accessStateHash ||\n !head.organizationId ||\n !(await isDocumentDiscoveryGenerationCurrent(db, generation))\n )\n return false;\n const row = {\n documentId,\n organizationId: head.organizationId,\n manifestHash: head.accessStateHash,\n accessEpoch: head.accessEpoch,\n linksJson: JSON.stringify(head.linkedContainerIds),\n };\n await db\n .insert(heads)\n .values(row)\n .onConflictDoUpdate({\n target: [heads.documentId, heads.organizationId],\n set: row,\n setWhere: lte(heads.accessEpoch, head.accessEpoch),\n })\n .run();\n return true;\n });\n}\n\n/**\n * A document head this client signed, already checked against the server's\n * create acknowledgement and pinned as its access-manifest checkpoint in the\n * same transaction. It is the same signed head a discovery projection would\n * verify, so it seeds the exact-head cache; `loadHead` still reuses it only\n * while that checkpoint matches.\n */\nexport interface AuthoredDiscoveryHead {\n readonly documentId: string;\n readonly organizationId: string;\n readonly accessEpoch: number;\n readonly accessStateHash: string;\n readonly linkedContainerIds: readonly string[];\n}\n\n/** Writes authored heads inside the caller's serialized write transaction. */\nexport async function primeAuthoredDocumentDiscoveryHeads(\n execSql: ExecSql,\n authored: readonly AuthoredDiscoveryHead[],\n): Promise<void> {\n const db = getClientSQLitePersistenceRuntime(execSql).db;\n for (const head of authored) {\n const row = {\n documentId: head.documentId,\n organizationId: head.organizationId,\n manifestHash: head.accessStateHash,\n accessEpoch: head.accessEpoch,\n linksJson: JSON.stringify(uniqueSortedStrings(head.linkedContainerIds)),\n };\n await db\n .insert(heads)\n .values(row)\n .onConflictDoUpdate({\n target: [heads.documentId, heads.organizationId],\n set: row,\n setWhere: lte(heads.accessEpoch, head.accessEpoch),\n })\n .run();\n }\n}\n\nexport function createDocumentDiscoveryEvidenceStore(\n execSql: ExecSql,\n now = Date.now,\n): DocumentDiscoveryEvidenceStore {\n return new SqlDocumentDiscoveryEvidenceStore(execSql, now);\n}\n"]}
1
+ {"version":3,"file":"documentDiscoveryEvidencePersistence.js","sourceRoot":"","sources":["documentDiscoveryEvidencePersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, asc, eq, inArray, lte, min, sql } from \"drizzle-orm\";\nimport { uniqueSortedStrings } from \"../../documents/shared/readers\";\nimport { readStringArray } from \"../../shared/recordReaders\";\nimport { documentContainerProjectionTables } from \"../../sqlite/schema\";\nimport {\n documentDiscoveryHeads,\n documentDiscoverySequence,\n pendingDocumentDiscoveries,\n} from \"../../sqlite/schema/documentDiscoveryEvidenceSchema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport { loadDocumentPurgeCheckpoint } from \"../keyingCheckpoints/documentPurgeCheckpointPersistence\";\nimport { loadAccessManifestCheckpoint } from \"../keyingCheckpoints/keyingCheckpointPersistence\";\nimport { heldTombstoneRetryDelayMs } from \"./containerDocumentTombstoneHoldsPersistence\";\n\nimport {\n type DiscoveredDocumentCandidate,\n isDiscoveredDocumentCandidate,\n readStoredDiscoveryCandidate,\n} from \"./documentDiscoveryCandidate\";\n\nimport { isDocumentDiscoveryGenerationCurrent } from \"./documentDiscoveryReset\";\n\nexport type { DiscoveredDocumentCandidate } from \"./documentDiscoveryCandidate\";\nexport { clearDocumentDiscoveryEvidenceOnRemoteReset } from \"./documentDiscoveryReset\";\nexport interface CachedDiscoveryHead {\n readonly organizationId: string | null;\n readonly accessEpoch: number;\n readonly accessStateHash?: string;\n readonly linkedContainerIds: readonly string[];\n}\ninterface RemovedDiscoveryPlacement {\n readonly containerId: string;\n readonly documentId: string;\n}\nexport interface DocumentDiscoveryEvidenceStore {\n begin(): Promise<number>;\n isCurrent(generation: number): Promise<boolean>;\n stage(\n inputs: readonly DiscoveredDocumentCandidate[],\n generation: number,\n removed?: readonly RemovedDiscoveryPlacement[],\n ): Promise<boolean>;\n pending(\n containerIds: readonly string[],\n limit: number,\n ): Promise<readonly DiscoveredDocumentCandidate[]>;\n defer(input: DiscoveredDocumentCandidate, generation: number): Promise<void>;\n acknowledge(\n inputs: readonly DiscoveredDocumentCandidate[],\n generation: number,\n ): Promise<void>;\n hasPending(containerIds: readonly string[]): Promise<boolean>;\n retryDelay(containerIds: readonly string[]): Promise<number | null>;\n loadHead(\n documentId: string,\n manifestHash: string | null | undefined,\n ): Promise<CachedDiscoveryHead | null>;\n saveHead(\n documentId: string,\n head: CachedDiscoveryHead,\n generation: number,\n ): Promise<boolean>;\n}\n\nconst pending = pendingDocumentDiscoveries;\nconst heads = documentDiscoveryHeads;\nconst ID_BATCH_SIZE = 400;\nconst batches = (ids: readonly string[]) =>\n Array.from({ length: Math.ceil(ids.length / ID_BATCH_SIZE) }, (_, i) =>\n ids.slice(i * ID_BATCH_SIZE, (i + 1) * ID_BATCH_SIZE),\n );\nconst identity = (input: DiscoveredDocumentCandidate) =>\n and(\n eq(pending.containerId, input.containerId),\n eq(pending.documentId, input.documentId),\n eq(pending.inputJson, JSON.stringify(input)),\n );\n\nclass SqlDocumentDiscoveryEvidenceStore\n implements DocumentDiscoveryEvidenceStore\n{\n constructor(\n private readonly execSql: ExecSql,\n private readonly now: () => number,\n ) {}\n\n private read = async () => {\n await ensureSqlTables(this.execSql, documentContainerProjectionTables);\n return getClientSQLitePersistenceRuntime(this.execSql).db;\n };\n private write = async <T>(\n operation: (\n db: ReturnType<typeof getClientSQLitePersistenceRuntime>[\"db\"],\n ) => Promise<T>,\n ) =>\n runSerializedSqlMutation(this.execSql, async (locked) => {\n await ensureSqlTables(locked, documentContainerProjectionTables);\n return operation(getClientSQLitePersistenceRuntime(locked).db);\n });\n begin = async () => {\n let generation = 0;\n await this.write(async (db) => {\n const [row] = await db\n .insert(documentDiscoverySequence)\n .values({ id: \"requests\", generation: 1 })\n .onConflictDoUpdate({\n target: documentDiscoverySequence.id,\n set: {\n generation: sql`${documentDiscoverySequence.generation} + 1`,\n },\n })\n .returning({ generation: documentDiscoverySequence.generation });\n if (!row || !Number.isSafeInteger(row.generation))\n throw new Error(\"Document discovery generation is exhausted\");\n generation = row.generation;\n });\n return generation;\n };\n isCurrent = async (generation: number) =>\n isDocumentDiscoveryGenerationCurrent(await this.read(), generation);\n stage = (\n inputs: readonly DiscoveredDocumentCandidate[],\n generation: number,\n removed: readonly RemovedDiscoveryPlacement[] = [],\n ) => {\n return this.write(async (db) => {\n if (!(await isDocumentDiscoveryGenerationCurrent(db, generation)))\n return false;\n if (inputs.length === 0 && removed.length === 0) return true;\n await db.transaction(async (tx) => {\n for (const input of inputs) {\n if (!isDiscoveredDocumentCandidate(input)) continue;\n for (const containerId of input.listedContainerIds) {\n const rowInput = {\n ...input,\n containerId,\n listedContainerIds: [containerId],\n };\n const inputJson = JSON.stringify(rowInput);\n await tx\n .insert(pending)\n .values({\n containerId,\n documentId: input.documentId,\n accessEpoch: input.accessEpoch,\n generation,\n inputJson,\n retryAt: 0,\n })\n .onConflictDoUpdate({\n target: [pending.containerId, pending.documentId],\n set: {\n accessEpoch: input.accessEpoch,\n generation,\n inputJson,\n retryAt: sql`case when ${pending.inputJson} = ${inputJson} then ${pending.retryAt} else 0 end`,\n attempts: sql`case when ${pending.inputJson} = ${inputJson} then ${pending.attempts} else 0 end`,\n },\n setWhere: lte(pending.generation, generation),\n })\n .run();\n }\n }\n // These are untrusted, unapplied hints only. Removing an existing local\n // placement still requires the separate signed tombstone evidence gate.\n for (const row of removed) {\n await tx\n .delete(pending)\n .where(\n and(\n eq(pending.containerId, row.containerId),\n eq(pending.documentId, row.documentId),\n lte(pending.generation, generation),\n ),\n )\n .run();\n }\n });\n return true;\n });\n };\n pending = async (containerIds: readonly string[], limit: number) => {\n const db = await this.read();\n const result: DiscoveredDocumentCandidate[] = [];\n for (const batch of batches(containerIds)) {\n if (result.length >= limit) break;\n const rows = await db\n .select({ inputJson: pending.inputJson })\n .from(pending)\n .where(\n and(\n inArray(pending.containerId, [...batch]),\n lte(pending.retryAt, this.now()),\n ),\n )\n .orderBy(asc(pending.retryAt), asc(pending.documentId))\n .limit(limit - result.length);\n result.push(\n ...rows.map((row) => readStoredDiscoveryCandidate(row.inputJson)),\n );\n }\n return result;\n };\n defer = (input: DiscoveredDocumentCandidate, generation: number) =>\n this.write(async (db) => {\n if (!(await isDocumentDiscoveryGenerationCurrent(db, generation))) return;\n const [row] = await db\n .select({ attempts: pending.attempts })\n .from(pending)\n .where(identity(input))\n .limit(1);\n if (!row) return;\n const attempts = Math.min(row.attempts + 1, 8);\n await db\n .update(pending)\n .set({\n attempts,\n retryAt: this.now() + heldTombstoneRetryDelayMs(attempts),\n })\n .where(identity(input))\n .run();\n });\n acknowledge = (\n inputs: readonly DiscoveredDocumentCandidate[],\n generation: number,\n ) =>\n this.write(async (db) => {\n if (!(await isDocumentDiscoveryGenerationCurrent(db, generation))) return;\n await db.transaction(async (tx) => {\n for (const input of inputs)\n await tx.delete(pending).where(identity(input)).run();\n });\n });\n hasPending = async (containerIds: readonly string[]) => {\n const db = await this.read();\n for (const batch of batches(containerIds)) {\n if (\n (\n await db\n .select({ documentId: pending.documentId })\n .from(pending)\n .where(inArray(pending.containerId, [...batch]))\n .limit(1)\n ).length\n )\n return true;\n }\n return false;\n };\n retryDelay = async (containerIds: readonly string[]) => {\n const db = await this.read();\n let due: number | null = null;\n for (const batch of batches(containerIds)) {\n const [row] = await db\n .select({ due: min(pending.retryAt) })\n .from(pending)\n .where(inArray(pending.containerId, [...batch]));\n if (row?.due !== null && row?.due !== undefined)\n due = Math.min(due ?? row.due, row.due);\n }\n return due === null ? null : Math.max(0, due - this.now());\n };\n loadHead = async (\n documentId: string,\n manifestHash: string | null | undefined,\n ) => {\n if (await loadDocumentPurgeCheckpoint(this.execSql, documentId))\n return {\n organizationId: null,\n accessEpoch: Number.MAX_SAFE_INTEGER,\n linkedContainerIds: [],\n };\n if (!manifestHash) return null;\n const db = await this.read();\n const [row] = await db\n .select()\n .from(heads)\n .where(\n and(\n eq(heads.documentId, documentId),\n eq(heads.manifestHash, manifestHash),\n ),\n )\n .limit(1);\n if (!row) return null;\n // A different verifier (for example tombstone removal) can advance the\n // signed checkpoint without updating documents.accessEpoch or this cache.\n // Old-organization pins survive recovery; only the cached signed head's\n // organization can authorize reuse. Missing pins require full verification.\n const checkpoint = await loadAccessManifestCheckpoint(\n this.execSql,\n \"document\",\n row.organizationId,\n documentId,\n );\n if (\n checkpoint?.manifestHash !== row.manifestHash ||\n checkpoint.epoch !== row.accessEpoch\n )\n return null;\n return {\n organizationId: row.organizationId,\n accessEpoch: row.accessEpoch,\n accessStateHash: row.manifestHash,\n linkedContainerIds: readStringArray(\n JSON.parse(row.linksJson),\n \"stored discovery links\",\n ),\n };\n };\n saveHead = (\n documentId: string,\n head: CachedDiscoveryHead,\n generation: number,\n ) =>\n this.write(async (db) => {\n if (\n !head.accessStateHash ||\n !head.organizationId ||\n !(await isDocumentDiscoveryGenerationCurrent(db, generation))\n )\n return false;\n const row = {\n documentId,\n organizationId: head.organizationId,\n manifestHash: head.accessStateHash,\n accessEpoch: head.accessEpoch,\n linksJson: JSON.stringify(head.linkedContainerIds),\n };\n await db\n .insert(heads)\n .values(row)\n .onConflictDoUpdate({\n target: [heads.documentId, heads.organizationId],\n set: row,\n setWhere: lte(heads.accessEpoch, head.accessEpoch),\n })\n .run();\n return true;\n });\n}\n\n/**\n * A document head this client signed, already checked against the server's\n * create acknowledgement and pinned as its access-manifest checkpoint in the\n * same transaction. It is the same signed head a discovery projection would\n * verify, so it seeds the exact-head cache; `loadHead` still reuses it only\n * while that checkpoint matches.\n */\nexport interface AuthoredDiscoveryHead {\n readonly documentId: string;\n readonly organizationId: string;\n readonly accessEpoch: number;\n readonly accessStateHash: string;\n readonly linkedContainerIds: readonly string[];\n}\n\n/** Writes authored heads inside the caller's serialized write transaction. */\nexport async function primeAuthoredDocumentDiscoveryHeads(\n execSql: ExecSql,\n authored: readonly AuthoredDiscoveryHead[],\n): Promise<void> {\n const db = getClientSQLitePersistenceRuntime(execSql).db;\n for (const head of authored) {\n const row = {\n documentId: head.documentId,\n organizationId: head.organizationId,\n manifestHash: head.accessStateHash,\n accessEpoch: head.accessEpoch,\n linksJson: JSON.stringify(uniqueSortedStrings(head.linkedContainerIds)),\n };\n await db\n .insert(heads)\n .values(row)\n .onConflictDoUpdate({\n target: [heads.documentId, heads.organizationId],\n set: row,\n setWhere: lte(heads.accessEpoch, head.accessEpoch),\n })\n .run();\n }\n}\n\nexport function createDocumentDiscoveryEvidenceStore(\n execSql: ExecSql,\n now = Date.now,\n): DocumentDiscoveryEvidenceStore {\n return new SqlDocumentDiscoveryEvidenceStore(execSql, now);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tearleads/client-sdk",
3
- "version": "0.1.101",
3
+ "version": "0.1.102",
4
4
  "description": "React-free Tearleads client runtime: local SQLite persistence, keys, sync, encrypted blobs, and domain workflows.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -7,7 +7,7 @@ export async function saveAppliedContainerDocumentWatermark(input) {
7
7
  if (!listedDocuments.nextWatermark) {
8
8
  return;
9
9
  }
10
- await saveContainerDocumentWatermark(containerId, listedDocuments.nextWatermark);
10
+ await saveContainerDocumentWatermark(containerId, listedDocuments.nextWatermark, input.stillCurrent);
11
11
  }
12
12
  /**
13
13
  * Verifies and applies one container's completed listing under a discovery
@@ -31,14 +31,14 @@ export async function applyListedContainerDocuments(options, generation, listedD
31
31
  return null;
32
32
  const verifiedInputs = verification.inputs;
33
33
  const discoveredDocuments = verifiedInputs.length
34
- ? await upsertDiscoveredDocuments(verifiedInputs)
34
+ ? await upsertDiscoveredDocuments(verifiedInputs, verification.isCurrent)
35
35
  : [];
36
36
  if (verifiedInputs.length)
37
37
  await options.replaceDocumentLinksBatch(await withoutDeferredDocumentLinks(verifiedInputs.map((document) => ({
38
38
  documentId: document.documentId,
39
39
  accessEpoch: document.accessEpoch,
40
40
  containerIds: document.linkedContainerIds,
41
- })), discoveredDocuments));
41
+ })), discoveredDocuments), verification.isCurrent);
42
42
  const tombstoneDocumentSummaries = await settleContainerDocumentTombstones({
43
43
  containerIds: [containerId],
44
44
  store: options,
@@ -51,7 +51,10 @@ export async function applyListedContainerDocuments(options, generation, listedD
51
51
  containerId,
52
52
  listedDocuments,
53
53
  saveContainerDocumentWatermark: options.saveContainerDocumentWatermark,
54
+ stillCurrent: verification.isCurrent,
54
55
  });
56
+ if (!(await verification.isCurrent()))
57
+ return null;
55
58
  if (listedDocuments.isFullListing) {
56
59
  // Initial probes consume ids as discovery hints and verify their own heads.
57
60
  options.onFullListing?.(listedDocuments.items.map((document) => document.id));
@@ -1 +1 @@
1
- {"version":3,"file":"documentDiscoveryApply.js","sourceRoot":"","sources":["documentDiscoveryApply.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DocumentSummary } from \"../../../data/documents/documentSummary\";\nimport { dedupeReferencedPrincipalStates } from \"../../../data/keyingProjectionVerification/principalPolicy/principalPolicyCache\";\nimport { withoutDeferredDocumentLinks } from \"../documentLinks/deferredDocumentLinks\";\nimport { settleContainerDocumentTombstones } from \"../tombstones/documentTombstoneGate\";\nimport { getApplicableDocumentTombstones } from \"./documentDiscoveryInputs\";\nimport type {\n DiscoverContainerDocumentsOptions,\n ListedContainerDocuments,\n} from \"./documentDiscoveryTypes\";\n\n/** A discovery lane's local stores; the listing itself is supplied by the caller. */\nexport type ContainerDocumentDiscoveryLane = Omit<\n DiscoverContainerDocumentsOptions,\n | \"beginDocumentDiscovery\"\n | \"listContainerDocumentLanes\"\n | \"loadContainerDocumentWatermark\"\n>;\n\nexport async function saveAppliedContainerDocumentWatermark(input: {\n containerId: string;\n listedDocuments: ListedContainerDocuments;\n saveContainerDocumentWatermark: DiscoverContainerDocumentsOptions[\"saveContainerDocumentWatermark\"];\n}) {\n const { containerId, listedDocuments, saveContainerDocumentWatermark } =\n input;\n if (!listedDocuments.nextWatermark) {\n return;\n }\n\n await saveContainerDocumentWatermark(\n containerId,\n listedDocuments.nextWatermark,\n );\n}\n\n/**\n * Verifies and applies one container's completed listing under a discovery\n * generation captured before the listing request, then advances its\n * watermark. Returns `null` when nothing was applied.\n */\nexport async function applyListedContainerDocuments(\n options: ContainerDocumentDiscoveryLane,\n generation: number,\n listedDocuments: ListedContainerDocuments,\n): Promise<ReadonlyArray<DocumentSummary> | null> {\n const { containerId, upsertDiscoveredDocuments } = options;\n await options.cacheReferencedPrincipalPolicies?.(\n dedupeReferencedPrincipalStates(\n listedDocuments.items.flatMap(\n (document) => document.referencedPrincipals,\n ),\n ),\n );\n\n const verification = await options.verifyDiscoveredDocuments(\n listedDocuments.items.map((document) => ({\n accessEpoch: document.currentAccessEpoch,\n accessStateHash: document.currentAccessStateHash,\n containerId,\n listedContainerIds: [containerId],\n createdAt: document.createdAt,\n documentId: document.id,\n effectiveAccessLevel: document.effectiveAccessLevel,\n linkedContainerIds: document.linkedContainerIds,\n })),\n [containerId],\n generation,\n getApplicableDocumentTombstones(listedDocuments),\n );\n if (!(await verification.isCurrent())) return null;\n const verifiedInputs = verification.inputs;\n const discoveredDocuments = verifiedInputs.length\n ? await upsertDiscoveredDocuments(verifiedInputs)\n : [];\n\n if (verifiedInputs.length)\n await options.replaceDocumentLinksBatch(\n await withoutDeferredDocumentLinks(\n verifiedInputs.map((document) => ({\n documentId: document.documentId,\n accessEpoch: document.accessEpoch,\n containerIds: document.linkedContainerIds,\n })),\n discoveredDocuments,\n ),\n );\n\n const tombstoneDocumentSummaries = await settleContainerDocumentTombstones({\n containerIds: [containerId],\n store: options,\n tombstones: getApplicableDocumentTombstones(listedDocuments),\n });\n\n const complete = await verification.commit();\n if (!(await verification.isCurrent())) return null;\n await saveAppliedContainerDocumentWatermark({\n containerId,\n listedDocuments,\n saveContainerDocumentWatermark: options.saveContainerDocumentWatermark,\n });\n\n if (listedDocuments.isFullListing) {\n // Initial probes consume ids as discovery hints and verify their own heads.\n options.onFullListing?.(\n listedDocuments.items.map((document) => document.id),\n );\n }\n\n return !complete &&\n discoveredDocuments.length === 0 &&\n tombstoneDocumentSummaries.length === 0\n ? null\n : [...discoveredDocuments, ...tombstoneDocumentSummaries];\n}\n"]}
1
+ {"version":3,"file":"documentDiscoveryApply.js","sourceRoot":"","sources":["documentDiscoveryApply.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DocumentSummary } from \"../../../data/documents/documentSummary\";\nimport { dedupeReferencedPrincipalStates } from \"../../../data/keyingProjectionVerification/principalPolicy/principalPolicyCache\";\nimport { withoutDeferredDocumentLinks } from \"../documentLinks/deferredDocumentLinks\";\nimport { settleContainerDocumentTombstones } from \"../tombstones/documentTombstoneGate\";\nimport { getApplicableDocumentTombstones } from \"./documentDiscoveryInputs\";\nimport type {\n DiscoverContainerDocumentsOptions,\n ListedContainerDocuments,\n} from \"./documentDiscoveryTypes\";\n\n/** A discovery lane's local stores; the listing itself is supplied by the caller. */\nexport type ContainerDocumentDiscoveryLane = Omit<\n DiscoverContainerDocumentsOptions,\n | \"beginDocumentDiscovery\"\n | \"listContainerDocumentLanes\"\n | \"loadContainerDocumentWatermark\"\n>;\n\nexport async function saveAppliedContainerDocumentWatermark(input: {\n containerId: string;\n listedDocuments: ListedContainerDocuments;\n saveContainerDocumentWatermark: DiscoverContainerDocumentsOptions[\"saveContainerDocumentWatermark\"];\n stillCurrent: () => Promise<boolean>;\n}) {\n const { containerId, listedDocuments, saveContainerDocumentWatermark } =\n input;\n if (!listedDocuments.nextWatermark) {\n return;\n }\n\n await saveContainerDocumentWatermark(\n containerId,\n listedDocuments.nextWatermark,\n input.stillCurrent,\n );\n}\n\n/**\n * Verifies and applies one container's completed listing under a discovery\n * generation captured before the listing request, then advances its\n * watermark. Returns `null` when nothing was applied.\n */\nexport async function applyListedContainerDocuments(\n options: ContainerDocumentDiscoveryLane,\n generation: number,\n listedDocuments: ListedContainerDocuments,\n): Promise<ReadonlyArray<DocumentSummary> | null> {\n const { containerId, upsertDiscoveredDocuments } = options;\n await options.cacheReferencedPrincipalPolicies?.(\n dedupeReferencedPrincipalStates(\n listedDocuments.items.flatMap(\n (document) => document.referencedPrincipals,\n ),\n ),\n );\n\n const verification = await options.verifyDiscoveredDocuments(\n listedDocuments.items.map((document) => ({\n accessEpoch: document.currentAccessEpoch,\n accessStateHash: document.currentAccessStateHash,\n containerId,\n listedContainerIds: [containerId],\n createdAt: document.createdAt,\n documentId: document.id,\n effectiveAccessLevel: document.effectiveAccessLevel,\n linkedContainerIds: document.linkedContainerIds,\n })),\n [containerId],\n generation,\n getApplicableDocumentTombstones(listedDocuments),\n );\n if (!(await verification.isCurrent())) return null;\n const verifiedInputs = verification.inputs;\n const discoveredDocuments = verifiedInputs.length\n ? await upsertDiscoveredDocuments(verifiedInputs, verification.isCurrent)\n : [];\n\n if (verifiedInputs.length)\n await options.replaceDocumentLinksBatch(\n await withoutDeferredDocumentLinks(\n verifiedInputs.map((document) => ({\n documentId: document.documentId,\n accessEpoch: document.accessEpoch,\n containerIds: document.linkedContainerIds,\n })),\n discoveredDocuments,\n ),\n verification.isCurrent,\n );\n\n const tombstoneDocumentSummaries = await settleContainerDocumentTombstones({\n containerIds: [containerId],\n store: options,\n tombstones: getApplicableDocumentTombstones(listedDocuments),\n });\n\n const complete = await verification.commit();\n if (!(await verification.isCurrent())) return null;\n await saveAppliedContainerDocumentWatermark({\n containerId,\n listedDocuments,\n saveContainerDocumentWatermark: options.saveContainerDocumentWatermark,\n stillCurrent: verification.isCurrent,\n });\n if (!(await verification.isCurrent())) return null;\n\n if (listedDocuments.isFullListing) {\n // Initial probes consume ids as discovery hints and verify their own heads.\n options.onFullListing?.(\n listedDocuments.items.map((document) => document.id),\n );\n }\n\n return !complete &&\n discoveredDocuments.length === 0 &&\n tombstoneDocumentSummaries.length === 0\n ? null\n : [...discoveredDocuments, ...tombstoneDocumentSummaries];\n}\n"]}
@@ -131,9 +131,10 @@ export interface DiscoverContainerDocumentsOptions extends ContainerDocumentTomb
131
131
  onFullListing?: ((documentIds: ReadonlyArray<string>) => void) | undefined;
132
132
  loadContainerDocumentWatermark: (containerId: string) => Promise<SyncWatermark | null>;
133
133
  listContainerDocumentLanes: ContainerDocumentLaneLister;
134
- replaceDocumentLinksBatch: (inputs: ReadonlyArray<DocumentLinkInput>) => Promise<void>;
135
- saveContainerDocumentWatermark: (containerId: string, watermark: SyncWatermark) => Promise<void>;
136
- upsertDiscoveredDocuments: (inputs: ReadonlyArray<DiscoveredDocumentInput>) => Promise<ReadonlyArray<DocumentSummary>>;
134
+ /** Recheck stillCurrent under the mutation lock before persisting a listing. */
135
+ replaceDocumentLinksBatch: (inputs: ReadonlyArray<DocumentLinkInput>, stillCurrent: () => Promise<boolean>) => Promise<void>;
136
+ saveContainerDocumentWatermark: (containerId: string, watermark: SyncWatermark, stillCurrent: () => Promise<boolean>) => Promise<void>;
137
+ upsertDiscoveredDocuments: (inputs: ReadonlyArray<DiscoveredDocumentInput>, stillCurrent: () => Promise<boolean>) => Promise<ReadonlyArray<DocumentSummary>>;
137
138
  }
138
139
  export interface ListedContainerDocuments {
139
140
  isFullListing: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"documentDiscoveryTypes.js","sourceRoot":"","sources":["documentDiscoveryTypes.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n ListContainerDocumentLanesRequest,\n ListContainerParentLanesRequest,\n} from \"@tearleads/validators/request\";\nimport type {\n EffectiveAccessLevel,\n ReferencedPrincipalStateResponse,\n SyncWatermark,\n} from \"@tearleads/validators/response\";\nimport type {\n DiscoveredDocumentInput,\n DocumentSummary,\n} from \"../../../data/documents/documentSummary\";\nimport type { DiscoveredDocumentCandidate } from \"../../../data/persistence/documents/documentDiscoveryEvidencePersistence\";\n\nexport interface ListedContainerDocument {\n createdAt: string;\n currentAccessEpoch: number;\n currentAccessStateHash: string;\n effectiveAccessLevel?: EffectiveAccessLevel | undefined;\n id: string;\n linkedContainerIds: string[];\n referencedPrincipals: ReferencedPrincipalStateResponse[];\n updatedAt: string;\n}\n\nexport interface ListContainerDocumentsResponse {\n hasMore: boolean;\n items: ListedContainerDocument[];\n nextWatermark: SyncWatermark | null;\n tombstones: ReadonlyArray<{\n containerId: string;\n documentId: string;\n updatedAt: string;\n }>;\n}\n\nexport interface ListedContainer {\n id: string;\n}\n\nexport interface ListContainersResponse {\n hasMore: boolean;\n items: ListedContainer[];\n nextWatermark: SyncWatermark | null;\n}\n\nexport interface ListContainerParentLanesResponse {\n results: ReadonlyArray<{\n laneId: string;\n page: ListContainersResponse;\n }>;\n}\n\nexport type ContainerDocumentLaneResult =\n | { readonly laneId: string; readonly page: ListContainerDocumentsResponse }\n | {\n readonly failure: { readonly code?: string | undefined };\n readonly laneId: string;\n };\n\nexport interface ContainerDocumentDiscoveryApi {\n readonly listContainerDocumentLanes: (\n input: ListContainerDocumentLanesRequest,\n options?: { readonly reportErrors?: boolean | undefined },\n ) => Promise<{\n readonly results: ReadonlyArray<ContainerDocumentLaneResult>;\n } | null>;\n readonly listContainerParentLanes: (\n input: ListContainerParentLanesRequest,\n ) => Promise<ListContainerParentLanesResponse | null>;\n}\n\n/**\n * One page per lane, aligned with the requested lanes: `null` for a lane the\n * server rejected, or `null` overall when the request itself failed.\n */\nexport type ContainerDocumentLaneLister = (\n lanes: ReadonlyArray<{\n readonly containerId: string;\n readonly watermark: SyncWatermark | null;\n }>,\n) => Promise<ReadonlyArray<ListContainerDocumentsResponse | null> | null>;\n\nexport interface DocumentLinkInput {\n accessEpoch?: number | undefined;\n containerIds: ReadonlyArray<string>;\n documentId: string;\n}\n\nexport type ContainerDocumentTombstone =\n ListContainerDocumentsResponse[\"tombstones\"][number];\n\nexport interface VerifiedContainerDocumentTombstone\n extends ContainerDocumentTombstone {\n /** The verified head's link-set epoch, not below the local document epoch. */\n readonly accessEpoch: number;\n /** The verified head link set; it omits `containerId`. */\n readonly linkedContainerIds: ReadonlyArray<string>;\n}\n\n/**\n * What the document's signed head says about a listing tombstone.\n * - `verified`: the verified head link set omits the container; apply.\n * - `refuted`: the verified head still links the container; keep visible and retry.\n * - `unverified`: no verified head is available; hold, hide, retry later.\n * - `deferred`: not attempted this run (the per-run head-load cap); hold\n * without counting an attempt, so the retry backoff does not grow.\n */\nexport type ContainerDocumentTombstoneVerdict =\n | {\n readonly kind: \"verified\";\n readonly tombstone: VerifiedContainerDocumentTombstone;\n }\n | {\n readonly kind: \"refuted\";\n /** The verified head link set, which contains the container. */\n readonly linkedContainerIds: ReadonlyArray<string>;\n readonly tombstone: ContainerDocumentTombstone;\n }\n | {\n readonly kind: \"unverified\";\n readonly tombstone: ContainerDocumentTombstone;\n }\n | {\n readonly kind: \"deferred\";\n readonly tombstone: ContainerDocumentTombstone;\n };\n\nexport interface HeldContainerDocumentTombstoneInput\n extends ContainerDocumentTombstone {\n /** Held without a verification attempt; the backoff does not advance. */\n readonly deferred?: boolean | undefined;\n /** A verified head still links it; retry the tombstone while keeping it visible. */\n readonly refuted?: boolean | undefined;\n}\n\nexport type ContainerDocumentTombstoneVerifier = (\n tombstones: ReadonlyArray<ContainerDocumentTombstone>,\n) => Promise<ReadonlyArray<ContainerDocumentTombstoneVerdict>>;\n\nexport type ContainerDocumentPlacement = Pick<\n ContainerDocumentTombstone,\n \"containerId\" | \"documentId\"\n>;\n\nexport interface ContainerDocumentTombstoneHoldStore {\n holdContainerDocumentTombstones: (\n tombstones: ReadonlyArray<HeldContainerDocumentTombstoneInput>,\n ) => Promise<void>;\n /** Held tombstones on these containers that are due for another attempt. */\n listHeldContainerDocumentTombstones: (\n containerIds: ReadonlyArray<string>,\n ) => Promise<ReadonlyArray<ContainerDocumentTombstone>>;\n /** The subset of placements that exist locally (link row or primary). */\n listKnownContainerDocumentPlacements: (\n placements: ReadonlyArray<ContainerDocumentPlacement>,\n ) => Promise<ReadonlyArray<ContainerDocumentPlacement>>;\n refuteContainerDocumentTombstoneHolds: (\n placements: ReadonlyArray<ContainerDocumentPlacement>,\n ) => Promise<void>;\n}\n\nexport interface DiscoverContainerDocumentsOptions\n extends ContainerDocumentTombstoneHoldStore {\n applyContainerDocumentTombstones: (\n tombstones: ReadonlyArray<VerifiedContainerDocumentTombstone>,\n ) => Promise<ReadonlyArray<DocumentSummary>>;\n verifyContainerDocumentTombstones: ContainerDocumentTombstoneVerifier;\n /** Allocate durable local order before fetching a listing. */\n beginDocumentDiscovery: () => Promise<number>;\n /** Persist pending candidates and return only signed placements. */\n verifyDiscoveredDocuments: (\n inputs: ReadonlyArray<DiscoveredDocumentCandidate>,\n containerIds: ReadonlyArray<string>,\n generation: number,\n tombstones?: ReadonlyArray<ContainerDocumentTombstone>,\n ) => Promise<{\n inputs: ReadonlyArray<DiscoveredDocumentInput>;\n /** False once a remote trust reset cancels this listing pass. */\n isCurrent: () => Promise<boolean>;\n /** Acknowledge only after local apply; true means no pending candidates remain. */\n commit: () => Promise<boolean>;\n }>;\n cacheReferencedPrincipalPolicies?: (\n references: ReadonlyArray<ReferencedPrincipalStateResponse>,\n ) => Promise<void>;\n containerId: string;\n /** Completed unwatermarked listing, published only after local apply succeeds. */\n onFullListing?: ((documentIds: ReadonlyArray<string>) => void) | undefined;\n loadContainerDocumentWatermark: (\n containerId: string,\n ) => Promise<SyncWatermark | null>;\n listContainerDocumentLanes: ContainerDocumentLaneLister;\n replaceDocumentLinksBatch: (\n inputs: ReadonlyArray<DocumentLinkInput>,\n ) => Promise<void>;\n saveContainerDocumentWatermark: (\n containerId: string,\n watermark: SyncWatermark,\n ) => Promise<void>;\n upsertDiscoveredDocuments: (\n inputs: ReadonlyArray<DiscoveredDocumentInput>,\n ) => Promise<ReadonlyArray<DocumentSummary>>;\n}\n\nexport interface ListedContainerDocuments {\n isFullListing: boolean;\n items: ListedContainerDocument[];\n nextWatermark: SyncWatermark | null;\n tombstones: ContainerDocumentTombstone[];\n}\n\nexport interface ContainerParentDiscoveryLane {\n parentId: string | null;\n watermark: SyncWatermark | null;\n}\n\nexport interface ListedContainerDocumentsLane {\n containerId: string;\n listedDocuments: ListedContainerDocuments | null;\n}\n\nexport interface DiscoverAllContainerDocumentsOptions\n extends Omit<DiscoverContainerDocumentsOptions, \"containerId\"> {\n containerIds: ReadonlyArray<string>;\n}\n\nexport interface RefreshAllContainerDocumentsOptions\n extends Omit<DiscoverAllContainerDocumentsOptions, \"containerIds\"> {\n listContainerParentLanes: ContainerDocumentDiscoveryApi[\"listContainerParentLanes\"];\n}\n\nexport interface RefreshAllContainerDocumentsFromApiOptions\n extends Omit<\n RefreshAllContainerDocumentsOptions,\n \"listContainerDocumentLanes\" | \"listContainerParentLanes\"\n > {\n readonly apiClient: Pick<\n ContainerDocumentDiscoveryApi,\n \"listContainerDocumentLanes\" | \"listContainerParentLanes\"\n >;\n}\n"]}
1
+ {"version":3,"file":"documentDiscoveryTypes.js","sourceRoot":"","sources":["documentDiscoveryTypes.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n ListContainerDocumentLanesRequest,\n ListContainerParentLanesRequest,\n} from \"@tearleads/validators/request\";\nimport type {\n EffectiveAccessLevel,\n ReferencedPrincipalStateResponse,\n SyncWatermark,\n} from \"@tearleads/validators/response\";\nimport type {\n DiscoveredDocumentInput,\n DocumentSummary,\n} from \"../../../data/documents/documentSummary\";\nimport type { DiscoveredDocumentCandidate } from \"../../../data/persistence/documents/documentDiscoveryEvidencePersistence\";\n\nexport interface ListedContainerDocument {\n createdAt: string;\n currentAccessEpoch: number;\n currentAccessStateHash: string;\n effectiveAccessLevel?: EffectiveAccessLevel | undefined;\n id: string;\n linkedContainerIds: string[];\n referencedPrincipals: ReferencedPrincipalStateResponse[];\n updatedAt: string;\n}\n\nexport interface ListContainerDocumentsResponse {\n hasMore: boolean;\n items: ListedContainerDocument[];\n nextWatermark: SyncWatermark | null;\n tombstones: ReadonlyArray<{\n containerId: string;\n documentId: string;\n updatedAt: string;\n }>;\n}\n\nexport interface ListedContainer {\n id: string;\n}\n\nexport interface ListContainersResponse {\n hasMore: boolean;\n items: ListedContainer[];\n nextWatermark: SyncWatermark | null;\n}\n\nexport interface ListContainerParentLanesResponse {\n results: ReadonlyArray<{\n laneId: string;\n page: ListContainersResponse;\n }>;\n}\n\nexport type ContainerDocumentLaneResult =\n | { readonly laneId: string; readonly page: ListContainerDocumentsResponse }\n | {\n readonly failure: { readonly code?: string | undefined };\n readonly laneId: string;\n };\n\nexport interface ContainerDocumentDiscoveryApi {\n readonly listContainerDocumentLanes: (\n input: ListContainerDocumentLanesRequest,\n options?: { readonly reportErrors?: boolean | undefined },\n ) => Promise<{\n readonly results: ReadonlyArray<ContainerDocumentLaneResult>;\n } | null>;\n readonly listContainerParentLanes: (\n input: ListContainerParentLanesRequest,\n ) => Promise<ListContainerParentLanesResponse | null>;\n}\n\n/**\n * One page per lane, aligned with the requested lanes: `null` for a lane the\n * server rejected, or `null` overall when the request itself failed.\n */\nexport type ContainerDocumentLaneLister = (\n lanes: ReadonlyArray<{\n readonly containerId: string;\n readonly watermark: SyncWatermark | null;\n }>,\n) => Promise<ReadonlyArray<ListContainerDocumentsResponse | null> | null>;\n\nexport interface DocumentLinkInput {\n accessEpoch?: number | undefined;\n containerIds: ReadonlyArray<string>;\n documentId: string;\n}\n\nexport type ContainerDocumentTombstone =\n ListContainerDocumentsResponse[\"tombstones\"][number];\n\nexport interface VerifiedContainerDocumentTombstone\n extends ContainerDocumentTombstone {\n /** The verified head's link-set epoch, not below the local document epoch. */\n readonly accessEpoch: number;\n /** The verified head link set; it omits `containerId`. */\n readonly linkedContainerIds: ReadonlyArray<string>;\n}\n\n/**\n * What the document's signed head says about a listing tombstone.\n * - `verified`: the verified head link set omits the container; apply.\n * - `refuted`: the verified head still links the container; keep visible and retry.\n * - `unverified`: no verified head is available; hold, hide, retry later.\n * - `deferred`: not attempted this run (the per-run head-load cap); hold\n * without counting an attempt, so the retry backoff does not grow.\n */\nexport type ContainerDocumentTombstoneVerdict =\n | {\n readonly kind: \"verified\";\n readonly tombstone: VerifiedContainerDocumentTombstone;\n }\n | {\n readonly kind: \"refuted\";\n /** The verified head link set, which contains the container. */\n readonly linkedContainerIds: ReadonlyArray<string>;\n readonly tombstone: ContainerDocumentTombstone;\n }\n | {\n readonly kind: \"unverified\";\n readonly tombstone: ContainerDocumentTombstone;\n }\n | {\n readonly kind: \"deferred\";\n readonly tombstone: ContainerDocumentTombstone;\n };\n\nexport interface HeldContainerDocumentTombstoneInput\n extends ContainerDocumentTombstone {\n /** Held without a verification attempt; the backoff does not advance. */\n readonly deferred?: boolean | undefined;\n /** A verified head still links it; retry the tombstone while keeping it visible. */\n readonly refuted?: boolean | undefined;\n}\n\nexport type ContainerDocumentTombstoneVerifier = (\n tombstones: ReadonlyArray<ContainerDocumentTombstone>,\n) => Promise<ReadonlyArray<ContainerDocumentTombstoneVerdict>>;\n\nexport type ContainerDocumentPlacement = Pick<\n ContainerDocumentTombstone,\n \"containerId\" | \"documentId\"\n>;\n\nexport interface ContainerDocumentTombstoneHoldStore {\n holdContainerDocumentTombstones: (\n tombstones: ReadonlyArray<HeldContainerDocumentTombstoneInput>,\n ) => Promise<void>;\n /** Held tombstones on these containers that are due for another attempt. */\n listHeldContainerDocumentTombstones: (\n containerIds: ReadonlyArray<string>,\n ) => Promise<ReadonlyArray<ContainerDocumentTombstone>>;\n /** The subset of placements that exist locally (link row or primary). */\n listKnownContainerDocumentPlacements: (\n placements: ReadonlyArray<ContainerDocumentPlacement>,\n ) => Promise<ReadonlyArray<ContainerDocumentPlacement>>;\n refuteContainerDocumentTombstoneHolds: (\n placements: ReadonlyArray<ContainerDocumentPlacement>,\n ) => Promise<void>;\n}\n\nexport interface DiscoverContainerDocumentsOptions\n extends ContainerDocumentTombstoneHoldStore {\n applyContainerDocumentTombstones: (\n tombstones: ReadonlyArray<VerifiedContainerDocumentTombstone>,\n ) => Promise<ReadonlyArray<DocumentSummary>>;\n verifyContainerDocumentTombstones: ContainerDocumentTombstoneVerifier;\n /** Allocate durable local order before fetching a listing. */\n beginDocumentDiscovery: () => Promise<number>;\n /** Persist pending candidates and return only signed placements. */\n verifyDiscoveredDocuments: (\n inputs: ReadonlyArray<DiscoveredDocumentCandidate>,\n containerIds: ReadonlyArray<string>,\n generation: number,\n tombstones?: ReadonlyArray<ContainerDocumentTombstone>,\n ) => Promise<{\n inputs: ReadonlyArray<DiscoveredDocumentInput>;\n /** False once a remote trust reset cancels this listing pass. */\n isCurrent: () => Promise<boolean>;\n /** Acknowledge only after local apply; true means no pending candidates remain. */\n commit: () => Promise<boolean>;\n }>;\n cacheReferencedPrincipalPolicies?: (\n references: ReadonlyArray<ReferencedPrincipalStateResponse>,\n ) => Promise<void>;\n containerId: string;\n /** Completed unwatermarked listing, published only after local apply succeeds. */\n onFullListing?: ((documentIds: ReadonlyArray<string>) => void) | undefined;\n loadContainerDocumentWatermark: (\n containerId: string,\n ) => Promise<SyncWatermark | null>;\n listContainerDocumentLanes: ContainerDocumentLaneLister;\n /** Recheck stillCurrent under the mutation lock before persisting a listing. */\n replaceDocumentLinksBatch: (\n inputs: ReadonlyArray<DocumentLinkInput>,\n stillCurrent: () => Promise<boolean>,\n ) => Promise<void>;\n saveContainerDocumentWatermark: (\n containerId: string,\n watermark: SyncWatermark,\n stillCurrent: () => Promise<boolean>,\n ) => Promise<void>;\n upsertDiscoveredDocuments: (\n inputs: ReadonlyArray<DiscoveredDocumentInput>,\n stillCurrent: () => Promise<boolean>,\n ) => Promise<ReadonlyArray<DocumentSummary>>;\n}\n\nexport interface ListedContainerDocuments {\n isFullListing: boolean;\n items: ListedContainerDocument[];\n nextWatermark: SyncWatermark | null;\n tombstones: ContainerDocumentTombstone[];\n}\n\nexport interface ContainerParentDiscoveryLane {\n parentId: string | null;\n watermark: SyncWatermark | null;\n}\n\nexport interface ListedContainerDocumentsLane {\n containerId: string;\n listedDocuments: ListedContainerDocuments | null;\n}\n\nexport interface DiscoverAllContainerDocumentsOptions\n extends Omit<DiscoverContainerDocumentsOptions, \"containerId\"> {\n containerIds: ReadonlyArray<string>;\n}\n\nexport interface RefreshAllContainerDocumentsOptions\n extends Omit<DiscoverAllContainerDocumentsOptions, \"containerIds\"> {\n listContainerParentLanes: ContainerDocumentDiscoveryApi[\"listContainerParentLanes\"];\n}\n\nexport interface RefreshAllContainerDocumentsFromApiOptions\n extends Omit<\n RefreshAllContainerDocumentsOptions,\n \"listContainerDocumentLanes\" | \"listContainerParentLanes\"\n > {\n readonly apiClient: Pick<\n ContainerDocumentDiscoveryApi,\n \"listContainerDocumentLanes\" | \"listContainerParentLanes\"\n >;\n}\n"]}
@@ -118,6 +118,16 @@ async function listDiscoveryLanes(input) {
118
118
  listedDocuments: listed.get(containerId) ?? null,
119
119
  }));
120
120
  }
121
+ async function saveListedWatermarks(lanes, saveContainerDocumentWatermark, stillCurrent) {
122
+ await Promise.all(lanes.map(({ containerId, listedDocuments }) => listedDocuments
123
+ ? saveAppliedContainerDocumentWatermark({
124
+ containerId,
125
+ listedDocuments,
126
+ saveContainerDocumentWatermark,
127
+ stillCurrent,
128
+ })
129
+ : undefined));
130
+ }
121
131
  export async function discoverAllContainerDocuments(options) {
122
132
  const { cacheReferencedPrincipalPolicies, containerIds, loadContainerDocumentWatermark, listContainerDocumentLanes, replaceDocumentLinksBatch, saveContainerDocumentWatermark, upsertDiscoveredDocuments, } = options;
123
133
  const uniqueContainerIds = Array.from(new Set(containerIds)).filter((containerId) => typeof containerId === "string" && containerId.length > 0);
@@ -134,13 +144,13 @@ export async function discoverAllContainerDocuments(options) {
134
144
  const discoveredDocumentInputs = verification.inputs;
135
145
  const discoveredDocuments = discoveredDocumentInputs.length === 0
136
146
  ? []
137
- : await upsertDiscoveredDocuments(discoveredDocumentInputs);
147
+ : await upsertDiscoveredDocuments(discoveredDocumentInputs, verification.isCurrent);
138
148
  if (discoveredDocumentInputs.length > 0) {
139
149
  await replaceDocumentLinksBatch(await withoutDeferredDocumentLinks(discoveredDocumentInputs.map((input) => ({
140
150
  documentId: input.documentId,
141
151
  accessEpoch: input.accessEpoch,
142
152
  containerIds: input.linkedContainerIds,
143
- })), discoveredDocuments));
153
+ })), discoveredDocuments), verification.isCurrent);
144
154
  }
145
155
  const tombstoneDocumentSummaries = await settleContainerDocumentTombstones({
146
156
  containerIds: listedDocumentsByContainer.flatMap(({ containerId, listedDocuments }) => listedDocuments ? [containerId] : []),
@@ -150,13 +160,9 @@ export async function discoverAllContainerDocuments(options) {
150
160
  const complete = await verification.commit();
151
161
  if (!(await verification.isCurrent()))
152
162
  return null;
153
- await Promise.all(listedDocumentsByContainer.map(({ containerId, listedDocuments }) => listedDocuments
154
- ? saveAppliedContainerDocumentWatermark({
155
- containerId,
156
- listedDocuments,
157
- saveContainerDocumentWatermark,
158
- })
159
- : undefined));
163
+ await saveListedWatermarks(listedDocumentsByContainer, saveContainerDocumentWatermark, verification.isCurrent);
164
+ if (!(await verification.isCurrent()))
165
+ return null;
160
166
  return !complete &&
161
167
  discoveredDocuments.length === 0 &&
162
168
  tombstoneDocumentSummaries.length === 0
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DocumentSummary } from \"../../../data/documents/documentSummary\";\nimport { isDocumentUpdateCreatedEvent } from \"../../../data/documents/documentSync\";\nimport { dedupeReferencedPrincipalStates } from \"../../../data/keyingProjectionVerification/principalPolicy/principalPolicyCache\";\nimport { withoutDeferredDocumentLinks } from \"../documentLinks/deferredDocumentLinks\";\nimport { settleContainerDocumentTombstones } from \"../tombstones/documentTombstoneGate\";\nimport {\n listContainerDocumentLanesFromApi,\n listContainerDocumentsFromWatermarks,\n} from \"./containerDocumentListing\";\nimport { saveAppliedContainerDocumentWatermark } from \"./documentDiscoveryApply\";\nimport {\n collectApplicableDocumentTombstones,\n collectDiscoveredDocumentInputs,\n} from \"./documentDiscoveryInputs\";\nimport { discoverContainerDocumentLanes } from \"./documentDiscoveryLanes\";\nimport type {\n ContainerDocumentDiscoveryApi,\n ContainerParentDiscoveryLane,\n DiscoverAllContainerDocumentsOptions,\n DiscoverContainerDocumentsOptions,\n ListedContainerDocumentsLane,\n RefreshAllContainerDocumentsFromApiOptions,\n RefreshAllContainerDocumentsOptions,\n} from \"./documentDiscoveryTypes\";\n\nexport type { RefreshAllContainerDocumentsFromApiOptions } from \"./documentDiscoveryTypes\";\n\nconst CONTAINER_PARENT_DISCOVERY_BATCH_SIZE = 4;\n\ntype ContainerParentLaneRequest = Parameters<\n ContainerDocumentDiscoveryApi[\"listContainerParentLanes\"]\n>[0][\"lanes\"][number];\n\ninterface RequestedContainerParentLane {\n lane: ContainerParentDiscoveryLane;\n request: ContainerParentLaneRequest;\n}\n\nasync function listContainerParentLaneBatch(\n listContainerParentLanes: ContainerDocumentDiscoveryApi[\"listContainerParentLanes\"],\n requestedLanes: ReadonlyArray<RequestedContainerParentLane>,\n) {\n const response = await listContainerParentLanes({\n lanes: requestedLanes.map(({ request }) => request),\n });\n if (!response || response.results.length !== requestedLanes.length) {\n return null;\n }\n\n const requestedLaneIds = new Set(\n requestedLanes.map(({ request }) => request.laneId),\n );\n const pagesByLaneId = new Map<\n string,\n (typeof response.results)[number][\"page\"]\n >();\n for (const result of response.results) {\n if (\n !requestedLaneIds.has(result.laneId) ||\n pagesByLaneId.has(result.laneId)\n ) {\n return null;\n }\n pagesByLaneId.set(result.laneId, result.page);\n }\n\n return requestedLanes.map(({ lane, request }) => ({\n lane,\n response: pagesByLaneId.get(request.laneId),\n }));\n}\n\nfunction collectRemoteContainerIds(\n items: ReadonlyArray<{ id: string }>,\n seenContainerIds: Set<string>,\n containerIds: string[],\n): ReadonlyArray<string> {\n for (const { id } of items) {\n if (!seenContainerIds.has(id)) {\n seenContainerIds.add(id);\n containerIds.push(id);\n }\n }\n\n return items.map(({ id }) => id);\n}\n\nexport function hasUndiscoveredDocumentUpdateEvent(\n events: ReadonlyArray<unknown>,\n knownDocumentIds: ReadonlySet<string>,\n): boolean {\n return events.some(\n (event) =>\n isDocumentUpdateCreatedEvent(event) &&\n !knownDocumentIds.has(event.documentId),\n );\n}\n\nexport async function listAllRemoteContainerIds(\n listContainerParentLanes: ContainerDocumentDiscoveryApi[\"listContainerParentLanes\"],\n): Promise<ReadonlyArray<string> | null> {\n const containerIds: string[] = [];\n const queuedParentIds = new Set<string | null>();\n const seenContainerIds = new Set<string>();\n const lanes: ContainerParentDiscoveryLane[] = [];\n let nextLaneId = 0;\n const queueParentLane = (parentId: string | null) => {\n if (queuedParentIds.has(parentId)) {\n return;\n }\n\n queuedParentIds.add(parentId);\n lanes.push({ parentId, watermark: null });\n };\n\n queueParentLane(null);\n\n while (lanes.length > 0) {\n const laneBatch = lanes.splice(0, CONTAINER_PARENT_DISCOVERY_BATCH_SIZE);\n const requestedLanes = laneBatch.map((lane) => ({\n lane,\n request: {\n laneId: `container-parent-${nextLaneId++}`,\n parentId: lane.parentId,\n watermark: lane.watermark,\n },\n }));\n const listedLanes = await listContainerParentLaneBatch(\n listContainerParentLanes,\n requestedLanes,\n );\n if (!listedLanes) {\n return null;\n }\n const continuationLanes: ContainerParentDiscoveryLane[] = [];\n\n for (const { lane, response } of listedLanes) {\n if (!response) {\n return null;\n }\n\n for (const containerId of collectRemoteContainerIds(\n response.items,\n seenContainerIds,\n containerIds,\n )) {\n queueParentLane(containerId);\n }\n\n if (response.hasMore) {\n if (!response.nextWatermark) {\n return null;\n }\n continuationLanes.push({\n parentId: lane.parentId,\n watermark: response.nextWatermark,\n });\n }\n }\n\n lanes.unshift(...continuationLanes);\n }\n\n return containerIds;\n}\n\nexport function listAllRemoteContainerIdsFromApi(\n apiClient: Pick<ContainerDocumentDiscoveryApi, \"listContainerParentLanes\">,\n): Promise<ReadonlyArray<string> | null> {\n return listAllRemoteContainerIds((input) =>\n apiClient.listContainerParentLanes(input),\n );\n}\n\nexport async function discoverContainerDocuments({\n beginDocumentDiscovery,\n listContainerDocumentLanes,\n loadContainerDocumentWatermark,\n ...lane\n}: DiscoverContainerDocumentsOptions): Promise<ReadonlyArray<DocumentSummary> | null> {\n const [outcome] = await discoverContainerDocumentLanes({\n generation: await beginDocumentDiscovery(),\n lanes: [lane],\n listContainerDocumentLanes,\n loadContainerDocumentWatermark,\n });\n if (outcome?.status === \"rejected\") throw outcome.reason;\n return outcome?.value ?? null;\n}\n\nexport function discoverContainerDocumentsFromApi({\n apiClient,\n ...input\n}: Omit<DiscoverContainerDocumentsOptions, \"listContainerDocumentLanes\"> & {\n readonly apiClient: Pick<\n ContainerDocumentDiscoveryApi,\n \"listContainerDocumentLanes\"\n >;\n}): Promise<ReadonlyArray<DocumentSummary> | null> {\n return discoverContainerDocuments({\n ...input,\n listContainerDocumentLanes: (lanes) =>\n listContainerDocumentLanesFromApi(apiClient, lanes),\n });\n}\n\nasync function listDiscoveryLanes(\n input: Parameters<typeof listContainerDocumentsFromWatermarks>[0],\n): Promise<ListedContainerDocumentsLane[]> {\n const listed = await listContainerDocumentsFromWatermarks(input);\n return input.containerIds.map((containerId) => ({\n containerId,\n listedDocuments: listed.get(containerId) ?? null,\n }));\n}\n\nexport async function discoverAllContainerDocuments(\n options: DiscoverAllContainerDocumentsOptions,\n): Promise<ReadonlyArray<DocumentSummary> | null> {\n const {\n cacheReferencedPrincipalPolicies,\n containerIds,\n loadContainerDocumentWatermark,\n listContainerDocumentLanes,\n replaceDocumentLinksBatch,\n saveContainerDocumentWatermark,\n upsertDiscoveredDocuments,\n } = options;\n const uniqueContainerIds = Array.from(new Set(containerIds)).filter(\n (containerId): containerId is string =>\n typeof containerId === \"string\" && containerId.length > 0,\n );\n const generation = await options.beginDocumentDiscovery();\n const listedDocumentsByContainer = await listDiscoveryLanes({\n containerIds: uniqueContainerIds,\n listContainerDocumentLanes,\n loadContainerDocumentWatermark,\n });\n await cacheReferencedPrincipalPolicies?.(\n dedupeReferencedPrincipalStates(\n listedDocumentsByContainer.flatMap(\n ({ listedDocuments }) =>\n listedDocuments?.items.flatMap(\n (document) => document.referencedPrincipals,\n ) ?? [],\n ),\n ),\n );\n const verification = await options.verifyDiscoveredDocuments(\n collectDiscoveredDocumentInputs(listedDocumentsByContainer),\n uniqueContainerIds,\n generation,\n collectApplicableDocumentTombstones(listedDocumentsByContainer),\n );\n if (!(await verification.isCurrent())) return null;\n const discoveredDocumentInputs = verification.inputs;\n const discoveredDocuments =\n discoveredDocumentInputs.length === 0\n ? []\n : await upsertDiscoveredDocuments(discoveredDocumentInputs);\n\n if (discoveredDocumentInputs.length > 0) {\n await replaceDocumentLinksBatch(\n await withoutDeferredDocumentLinks(\n discoveredDocumentInputs.map((input) => ({\n documentId: input.documentId,\n accessEpoch: input.accessEpoch,\n containerIds: input.linkedContainerIds,\n })),\n discoveredDocuments,\n ),\n );\n }\n\n const tombstoneDocumentSummaries = await settleContainerDocumentTombstones({\n containerIds: listedDocumentsByContainer.flatMap(\n ({ containerId, listedDocuments }) =>\n listedDocuments ? [containerId] : [],\n ),\n store: options,\n tombstones: collectApplicableDocumentTombstones(listedDocumentsByContainer),\n });\n\n const complete = await verification.commit();\n if (!(await verification.isCurrent())) return null;\n\n await Promise.all(\n listedDocumentsByContainer.map(({ containerId, listedDocuments }) =>\n listedDocuments\n ? saveAppliedContainerDocumentWatermark({\n containerId,\n listedDocuments,\n saveContainerDocumentWatermark,\n })\n : undefined,\n ),\n );\n\n return !complete &&\n discoveredDocuments.length === 0 &&\n tombstoneDocumentSummaries.length === 0\n ? null\n : [...discoveredDocuments, ...tombstoneDocumentSummaries];\n}\n\nasync function refreshAllContainerDocuments({\n listContainerParentLanes,\n ...input\n}: RefreshAllContainerDocumentsOptions): Promise<ReadonlyArray<DocumentSummary> | null> {\n const containerIds = await listAllRemoteContainerIds(\n listContainerParentLanes,\n );\n if (!containerIds) {\n return null;\n }\n\n return discoverAllContainerDocuments({\n ...input,\n containerIds,\n });\n}\n\nexport function refreshAllContainerDocumentsFromApi({\n apiClient,\n ...input\n}: RefreshAllContainerDocumentsFromApiOptions): Promise<ReadonlyArray<DocumentSummary> | null> {\n return refreshAllContainerDocuments({\n ...input,\n listContainerDocumentLanes: (lanes) =>\n listContainerDocumentLanesFromApi(apiClient, lanes),\n listContainerParentLanes: (input) =>\n apiClient.listContainerParentLanes(input),\n });\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DocumentSummary } from \"../../../data/documents/documentSummary\";\nimport { isDocumentUpdateCreatedEvent } from \"../../../data/documents/documentSync\";\nimport { dedupeReferencedPrincipalStates } from \"../../../data/keyingProjectionVerification/principalPolicy/principalPolicyCache\";\nimport { withoutDeferredDocumentLinks } from \"../documentLinks/deferredDocumentLinks\";\nimport { settleContainerDocumentTombstones } from \"../tombstones/documentTombstoneGate\";\nimport {\n listContainerDocumentLanesFromApi,\n listContainerDocumentsFromWatermarks,\n} from \"./containerDocumentListing\";\nimport { saveAppliedContainerDocumentWatermark } from \"./documentDiscoveryApply\";\nimport {\n collectApplicableDocumentTombstones,\n collectDiscoveredDocumentInputs,\n} from \"./documentDiscoveryInputs\";\nimport { discoverContainerDocumentLanes } from \"./documentDiscoveryLanes\";\nimport type {\n ContainerDocumentDiscoveryApi,\n ContainerParentDiscoveryLane,\n DiscoverAllContainerDocumentsOptions,\n DiscoverContainerDocumentsOptions,\n ListedContainerDocumentsLane,\n RefreshAllContainerDocumentsFromApiOptions,\n RefreshAllContainerDocumentsOptions,\n} from \"./documentDiscoveryTypes\";\n\nexport type { RefreshAllContainerDocumentsFromApiOptions } from \"./documentDiscoveryTypes\";\n\nconst CONTAINER_PARENT_DISCOVERY_BATCH_SIZE = 4;\n\ntype ContainerParentLaneRequest = Parameters<\n ContainerDocumentDiscoveryApi[\"listContainerParentLanes\"]\n>[0][\"lanes\"][number];\n\ninterface RequestedContainerParentLane {\n lane: ContainerParentDiscoveryLane;\n request: ContainerParentLaneRequest;\n}\n\nasync function listContainerParentLaneBatch(\n listContainerParentLanes: ContainerDocumentDiscoveryApi[\"listContainerParentLanes\"],\n requestedLanes: ReadonlyArray<RequestedContainerParentLane>,\n) {\n const response = await listContainerParentLanes({\n lanes: requestedLanes.map(({ request }) => request),\n });\n if (!response || response.results.length !== requestedLanes.length) {\n return null;\n }\n\n const requestedLaneIds = new Set(\n requestedLanes.map(({ request }) => request.laneId),\n );\n const pagesByLaneId = new Map<\n string,\n (typeof response.results)[number][\"page\"]\n >();\n for (const result of response.results) {\n if (\n !requestedLaneIds.has(result.laneId) ||\n pagesByLaneId.has(result.laneId)\n ) {\n return null;\n }\n pagesByLaneId.set(result.laneId, result.page);\n }\n\n return requestedLanes.map(({ lane, request }) => ({\n lane,\n response: pagesByLaneId.get(request.laneId),\n }));\n}\n\nfunction collectRemoteContainerIds(\n items: ReadonlyArray<{ id: string }>,\n seenContainerIds: Set<string>,\n containerIds: string[],\n): ReadonlyArray<string> {\n for (const { id } of items) {\n if (!seenContainerIds.has(id)) {\n seenContainerIds.add(id);\n containerIds.push(id);\n }\n }\n\n return items.map(({ id }) => id);\n}\n\nexport function hasUndiscoveredDocumentUpdateEvent(\n events: ReadonlyArray<unknown>,\n knownDocumentIds: ReadonlySet<string>,\n): boolean {\n return events.some(\n (event) =>\n isDocumentUpdateCreatedEvent(event) &&\n !knownDocumentIds.has(event.documentId),\n );\n}\n\nexport async function listAllRemoteContainerIds(\n listContainerParentLanes: ContainerDocumentDiscoveryApi[\"listContainerParentLanes\"],\n): Promise<ReadonlyArray<string> | null> {\n const containerIds: string[] = [];\n const queuedParentIds = new Set<string | null>();\n const seenContainerIds = new Set<string>();\n const lanes: ContainerParentDiscoveryLane[] = [];\n let nextLaneId = 0;\n const queueParentLane = (parentId: string | null) => {\n if (queuedParentIds.has(parentId)) {\n return;\n }\n\n queuedParentIds.add(parentId);\n lanes.push({ parentId, watermark: null });\n };\n\n queueParentLane(null);\n\n while (lanes.length > 0) {\n const laneBatch = lanes.splice(0, CONTAINER_PARENT_DISCOVERY_BATCH_SIZE);\n const requestedLanes = laneBatch.map((lane) => ({\n lane,\n request: {\n laneId: `container-parent-${nextLaneId++}`,\n parentId: lane.parentId,\n watermark: lane.watermark,\n },\n }));\n const listedLanes = await listContainerParentLaneBatch(\n listContainerParentLanes,\n requestedLanes,\n );\n if (!listedLanes) {\n return null;\n }\n const continuationLanes: ContainerParentDiscoveryLane[] = [];\n\n for (const { lane, response } of listedLanes) {\n if (!response) {\n return null;\n }\n\n for (const containerId of collectRemoteContainerIds(\n response.items,\n seenContainerIds,\n containerIds,\n )) {\n queueParentLane(containerId);\n }\n\n if (response.hasMore) {\n if (!response.nextWatermark) {\n return null;\n }\n continuationLanes.push({\n parentId: lane.parentId,\n watermark: response.nextWatermark,\n });\n }\n }\n\n lanes.unshift(...continuationLanes);\n }\n\n return containerIds;\n}\n\nexport function listAllRemoteContainerIdsFromApi(\n apiClient: Pick<ContainerDocumentDiscoveryApi, \"listContainerParentLanes\">,\n): Promise<ReadonlyArray<string> | null> {\n return listAllRemoteContainerIds((input) =>\n apiClient.listContainerParentLanes(input),\n );\n}\n\nexport async function discoverContainerDocuments({\n beginDocumentDiscovery,\n listContainerDocumentLanes,\n loadContainerDocumentWatermark,\n ...lane\n}: DiscoverContainerDocumentsOptions): Promise<ReadonlyArray<DocumentSummary> | null> {\n const [outcome] = await discoverContainerDocumentLanes({\n generation: await beginDocumentDiscovery(),\n lanes: [lane],\n listContainerDocumentLanes,\n loadContainerDocumentWatermark,\n });\n if (outcome?.status === \"rejected\") throw outcome.reason;\n return outcome?.value ?? null;\n}\n\nexport function discoverContainerDocumentsFromApi({\n apiClient,\n ...input\n}: Omit<DiscoverContainerDocumentsOptions, \"listContainerDocumentLanes\"> & {\n readonly apiClient: Pick<\n ContainerDocumentDiscoveryApi,\n \"listContainerDocumentLanes\"\n >;\n}): Promise<ReadonlyArray<DocumentSummary> | null> {\n return discoverContainerDocuments({\n ...input,\n listContainerDocumentLanes: (lanes) =>\n listContainerDocumentLanesFromApi(apiClient, lanes),\n });\n}\n\nasync function listDiscoveryLanes(\n input: Parameters<typeof listContainerDocumentsFromWatermarks>[0],\n): Promise<ListedContainerDocumentsLane[]> {\n const listed = await listContainerDocumentsFromWatermarks(input);\n return input.containerIds.map((containerId) => ({\n containerId,\n listedDocuments: listed.get(containerId) ?? null,\n }));\n}\n\nasync function saveListedWatermarks(\n lanes: readonly ListedContainerDocumentsLane[],\n saveContainerDocumentWatermark: DiscoverContainerDocumentsOptions[\"saveContainerDocumentWatermark\"],\n stillCurrent: () => Promise<boolean>,\n): Promise<void> {\n await Promise.all(\n lanes.map(({ containerId, listedDocuments }) =>\n listedDocuments\n ? saveAppliedContainerDocumentWatermark({\n containerId,\n listedDocuments,\n saveContainerDocumentWatermark,\n stillCurrent,\n })\n : undefined,\n ),\n );\n}\n\nexport async function discoverAllContainerDocuments(\n options: DiscoverAllContainerDocumentsOptions,\n): Promise<ReadonlyArray<DocumentSummary> | null> {\n const {\n cacheReferencedPrincipalPolicies,\n containerIds,\n loadContainerDocumentWatermark,\n listContainerDocumentLanes,\n replaceDocumentLinksBatch,\n saveContainerDocumentWatermark,\n upsertDiscoveredDocuments,\n } = options;\n const uniqueContainerIds = Array.from(new Set(containerIds)).filter(\n (containerId): containerId is string =>\n typeof containerId === \"string\" && containerId.length > 0,\n );\n const generation = await options.beginDocumentDiscovery();\n const listedDocumentsByContainer = await listDiscoveryLanes({\n containerIds: uniqueContainerIds,\n listContainerDocumentLanes,\n loadContainerDocumentWatermark,\n });\n await cacheReferencedPrincipalPolicies?.(\n dedupeReferencedPrincipalStates(\n listedDocumentsByContainer.flatMap(\n ({ listedDocuments }) =>\n listedDocuments?.items.flatMap(\n (document) => document.referencedPrincipals,\n ) ?? [],\n ),\n ),\n );\n const verification = await options.verifyDiscoveredDocuments(\n collectDiscoveredDocumentInputs(listedDocumentsByContainer),\n uniqueContainerIds,\n generation,\n collectApplicableDocumentTombstones(listedDocumentsByContainer),\n );\n if (!(await verification.isCurrent())) return null;\n const discoveredDocumentInputs = verification.inputs;\n const discoveredDocuments =\n discoveredDocumentInputs.length === 0\n ? []\n : await upsertDiscoveredDocuments(\n discoveredDocumentInputs,\n verification.isCurrent,\n );\n\n if (discoveredDocumentInputs.length > 0) {\n await replaceDocumentLinksBatch(\n await withoutDeferredDocumentLinks(\n discoveredDocumentInputs.map((input) => ({\n documentId: input.documentId,\n accessEpoch: input.accessEpoch,\n containerIds: input.linkedContainerIds,\n })),\n discoveredDocuments,\n ),\n verification.isCurrent,\n );\n }\n\n const tombstoneDocumentSummaries = await settleContainerDocumentTombstones({\n containerIds: listedDocumentsByContainer.flatMap(\n ({ containerId, listedDocuments }) =>\n listedDocuments ? [containerId] : [],\n ),\n store: options,\n tombstones: collectApplicableDocumentTombstones(listedDocumentsByContainer),\n });\n\n const complete = await verification.commit();\n if (!(await verification.isCurrent())) return null;\n\n await saveListedWatermarks(\n listedDocumentsByContainer,\n saveContainerDocumentWatermark,\n verification.isCurrent,\n );\n if (!(await verification.isCurrent())) return null;\n\n return !complete &&\n discoveredDocuments.length === 0 &&\n tombstoneDocumentSummaries.length === 0\n ? null\n : [...discoveredDocuments, ...tombstoneDocumentSummaries];\n}\n\nasync function refreshAllContainerDocuments({\n listContainerParentLanes,\n ...input\n}: RefreshAllContainerDocumentsOptions): Promise<ReadonlyArray<DocumentSummary> | null> {\n const containerIds = await listAllRemoteContainerIds(\n listContainerParentLanes,\n );\n if (!containerIds) {\n return null;\n }\n\n return discoverAllContainerDocuments({\n ...input,\n containerIds,\n });\n}\n\nexport function refreshAllContainerDocumentsFromApi({\n apiClient,\n ...input\n}: RefreshAllContainerDocumentsFromApiOptions): Promise<ReadonlyArray<DocumentSummary> | null> {\n return refreshAllContainerDocuments({\n ...input,\n listContainerDocumentLanes: (lanes) =>\n listContainerDocumentLanesFromApi(apiClient, lanes),\n listContainerParentLanes: (input) =>\n apiClient.listContainerParentLanes(input),\n });\n}\n"]}
@@ -3,22 +3,23 @@ import { sqlDocumentContainerProjectionPersistence } from "../../../data/persist
3
3
  import { upsertDiscoveredDocuments } from "../../../data/persistence/documents/documentsPersistence.js";
4
4
  import { runSerializedSqlMutation } from "../../../data/sqlite/sqlExec.js";
5
5
  /**
6
- * Binds a discovery lane's writes to its container's organization staying
7
- * unpaused. A listing still in flight when the organization is paused stops
8
- * being current, so the apply stops at its next check, and each write it has
9
- * already started re-checks under the SQLite mutation lock the pause cleanup
10
- * holds. A stale listing therefore never restores documents, links, or the
6
+ * Binds a discovery lane's writes to its generation and its organization staying
7
+ * unpaused. Access changes and pauses invalidate in-flight listings, so the
8
+ * apply stops at its next check. Each write already started rechecks under
9
+ * the SQLite mutation lock held by the reset or pause cleanup. A stale listing therefore never restores documents, links, or the
11
10
  * watermark the cleanup removed; a watermark saved after the cleanup would
12
11
  * otherwise make the listing after a resume skip every unchanged document.
13
12
  */
14
- export function guardDiscoveryAgainstPause(input) {
15
- const unlessPaused = (skipped, write) => runSerializedSqlMutation(input.execSql, async (lockedExecSql) => input.isPaused() ? skipped : write(lockedExecSql));
13
+ export function guardDocumentDiscoveryWrites(input) {
14
+ const whileCurrent = (skipped, write, stillCurrent) => runSerializedSqlMutation(input.execSql, async (lockedExecSql) => input.isPaused() || !(await stillCurrent())
15
+ ? skipped
16
+ : write(lockedExecSql));
16
17
  return {
17
- replaceDocumentLinksBatch: (inputs) => unlessPaused(undefined, (lockedExecSql) => sqlDocumentContainerProjectionPersistence.replaceDocumentLinksBatch(lockedExecSql, inputs)),
18
- saveContainerDocumentWatermark: (containerId, watermark) => unlessPaused(undefined, async (lockedExecSql) => {
18
+ replaceDocumentLinksBatch: (inputs, stillCurrent) => whileCurrent(undefined, (lockedExecSql) => sqlDocumentContainerProjectionPersistence.replaceDocumentLinksBatch(lockedExecSql, inputs), stillCurrent),
19
+ saveContainerDocumentWatermark: (containerId, watermark, stillCurrent) => whileCurrent(undefined, async (lockedExecSql) => {
19
20
  await sqlContainerSyncWatermarkPersistence.saveWatermark(lockedExecSql, containerContentsSyncLane(containerId), watermark);
20
- }),
21
- upsertDiscoveredDocuments: (inputs) => unlessPaused([], (lockedExecSql) => upsertDiscoveredDocuments(lockedExecSql, inputs)),
21
+ }, stillCurrent),
22
+ upsertDiscoveredDocuments: (inputs, stillCurrent) => whileCurrent([], (lockedExecSql) => upsertDiscoveredDocuments(lockedExecSql, inputs), stillCurrent),
22
23
  async verifyDiscoveredDocuments(...args) {
23
24
  const verification = await input.verifyDiscoveredDocuments(...args);
24
25
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"discoveryGuard.js","sourceRoot":"","sources":["discoveryGuard.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,oCAAoC,GACrC,MAAM,gFAAgF,CAAC;AACxF,OAAO,EAAE,yCAAyC,EAAE,MAAM,qFAAqF,CAAC;AAChJ,OAAO,EAAE,yBAAyB,EAAE,MAAM,0DAA0D,CAAC;AACrG,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAYxE;;;;;;;;GAQG;AACH,MAAM,UAAU,0BAA0B,CAAC,KAI1C;IACC,MAAM,YAAY,GAAG,CACnB,OAAU,EACV,KAA6C,EACjC,EAAE,CACd,wBAAwB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,CAC9D,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAClD,CAAC;IACJ,OAAO;QACL,yBAAyB,EAAE,CAAC,MAAM,EAAE,EAAE,CACpC,YAAY,CAAC,SAAS,EAAE,CAAC,aAAa,EAAE,EAAE,CACxC,yCAAyC,CAAC,yBAAyB,CACjE,aAAa,EACb,MAAM,CACP,CACF;QACH,8BAA8B,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,EAAE,CACzD,YAAY,CAAC,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE;YAC9C,MAAM,oCAAoC,CAAC,aAAa,CACtD,aAAa,EACb,yBAAyB,CAAC,WAAW,CAAC,EACtC,SAAS,CACV,CAAC;QACJ,CAAC,CAAC;QACJ,yBAAyB,EAAE,CAAC,MAAM,EAAE,EAAE,CACpC,YAAY,CAAC,EAAE,EAAE,CAAC,aAAa,EAAE,EAAE,CACjC,yBAAyB,CAAC,aAAa,EAAE,MAAM,CAAC,CACjD;QACH,KAAK,CAAC,yBAAyB,CAAC,GAAG,IAAI;YACrC,MAAM,YAAY,GAAG,MAAM,KAAK,CAAC,yBAAyB,CAAC,GAAG,IAAI,CAAC,CAAC;YACpE,OAAO;gBACL,GAAG,YAAY;gBACf,SAAS,EAAE,KAAK,IAAI,EAAE,CACpB,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,YAAY,CAAC,SAAS,EAAE,CAAC;aACxD,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import {\n containerContentsSyncLane,\n sqlContainerSyncWatermarkPersistence,\n} from \"../../../data/persistence/container-contents/containerSyncWatermarkPersistence\";\nimport { sqlDocumentContainerProjectionPersistence } from \"../../../data/persistence/container-contents/documentContainerProjectionPersistence\";\nimport { upsertDiscoveredDocuments } from \"../../../data/persistence/documents/documentsPersistence\";\nimport { runSerializedSqlMutation } from \"../../../data/sqlite/sqlExec\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport type { DiscoverContainerDocumentsOptions } from \"../documentDiscovery/documentDiscoveryTypes\";\n\ntype PauseGuardedDiscovery = Pick<\n DiscoverContainerDocumentsOptions,\n | \"replaceDocumentLinksBatch\"\n | \"saveContainerDocumentWatermark\"\n | \"upsertDiscoveredDocuments\"\n | \"verifyDiscoveredDocuments\"\n>;\n\n/**\n * Binds a discovery lane's writes to its container's organization staying\n * unpaused. A listing still in flight when the organization is paused stops\n * being current, so the apply stops at its next check, and each write it has\n * already started re-checks under the SQLite mutation lock the pause cleanup\n * holds. A stale listing therefore never restores documents, links, or the\n * watermark the cleanup removed; a watermark saved after the cleanup would\n * otherwise make the listing after a resume skip every unchanged document.\n */\nexport function guardDiscoveryAgainstPause(input: {\n readonly execSql: ExecSql;\n readonly isPaused: () => boolean;\n readonly verifyDiscoveredDocuments: DiscoverContainerDocumentsOptions[\"verifyDiscoveredDocuments\"];\n}): PauseGuardedDiscovery {\n const unlessPaused = <T>(\n skipped: T,\n write: (lockedExecSql: ExecSql) => Promise<T>,\n ): Promise<T> =>\n runSerializedSqlMutation(input.execSql, async (lockedExecSql) =>\n input.isPaused() ? skipped : write(lockedExecSql),\n );\n return {\n replaceDocumentLinksBatch: (inputs) =>\n unlessPaused(undefined, (lockedExecSql) =>\n sqlDocumentContainerProjectionPersistence.replaceDocumentLinksBatch(\n lockedExecSql,\n inputs,\n ),\n ),\n saveContainerDocumentWatermark: (containerId, watermark) =>\n unlessPaused(undefined, async (lockedExecSql) => {\n await sqlContainerSyncWatermarkPersistence.saveWatermark(\n lockedExecSql,\n containerContentsSyncLane(containerId),\n watermark,\n );\n }),\n upsertDiscoveredDocuments: (inputs) =>\n unlessPaused([], (lockedExecSql) =>\n upsertDiscoveredDocuments(lockedExecSql, inputs),\n ),\n async verifyDiscoveredDocuments(...args) {\n const verification = await input.verifyDiscoveredDocuments(...args);\n return {\n ...verification,\n isCurrent: async () =>\n !input.isPaused() && (await verification.isCurrent()),\n };\n },\n };\n}\n"]}
1
+ {"version":3,"file":"discoveryGuard.js","sourceRoot":"","sources":["discoveryGuard.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,oCAAoC,GACrC,MAAM,gFAAgF,CAAC;AACxF,OAAO,EAAE,yCAAyC,EAAE,MAAM,qFAAqF,CAAC;AAChJ,OAAO,EAAE,yBAAyB,EAAE,MAAM,0DAA0D,CAAC;AACrG,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAYxE;;;;;;;GAOG;AACH,MAAM,UAAU,4BAA4B,CAAC,KAI5C;IACC,MAAM,YAAY,GAAG,CACnB,OAAU,EACV,KAA6C,EAC7C,YAAoC,EACxB,EAAE,CACd,wBAAwB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,CAC9D,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,MAAM,YAAY,EAAE,CAAC;QACzC,CAAC,CAAC,OAAO;QACT,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CACzB,CAAC;IACJ,OAAO;QACL,yBAAyB,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,CAClD,YAAY,CACV,SAAS,EACT,CAAC,aAAa,EAAE,EAAE,CAChB,yCAAyC,CAAC,yBAAyB,CACjE,aAAa,EACb,MAAM,CACP,EACH,YAAY,CACb;QACH,8BAA8B,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,CACvE,YAAY,CACV,SAAS,EACT,KAAK,EAAE,aAAa,EAAE,EAAE;YACtB,MAAM,oCAAoC,CAAC,aAAa,CACtD,aAAa,EACb,yBAAyB,CAAC,WAAW,CAAC,EACtC,SAAS,CACV,CAAC;QACJ,CAAC,EACD,YAAY,CACb;QACH,yBAAyB,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,CAClD,YAAY,CACV,EAAE,EACF,CAAC,aAAa,EAAE,EAAE,CAAC,yBAAyB,CAAC,aAAa,EAAE,MAAM,CAAC,EACnE,YAAY,CACb;QACH,KAAK,CAAC,yBAAyB,CAAC,GAAG,IAAI;YACrC,MAAM,YAAY,GAAG,MAAM,KAAK,CAAC,yBAAyB,CAAC,GAAG,IAAI,CAAC,CAAC;YACpE,OAAO;gBACL,GAAG,YAAY;gBACf,SAAS,EAAE,KAAK,IAAI,EAAE,CACpB,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,YAAY,CAAC,SAAS,EAAE,CAAC;aACxD,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import {\n containerContentsSyncLane,\n sqlContainerSyncWatermarkPersistence,\n} from \"../../../data/persistence/container-contents/containerSyncWatermarkPersistence\";\nimport { sqlDocumentContainerProjectionPersistence } from \"../../../data/persistence/container-contents/documentContainerProjectionPersistence\";\nimport { upsertDiscoveredDocuments } from \"../../../data/persistence/documents/documentsPersistence\";\nimport { runSerializedSqlMutation } from \"../../../data/sqlite/sqlExec\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport type { DiscoverContainerDocumentsOptions } from \"../documentDiscovery/documentDiscoveryTypes\";\n\ntype PauseGuardedDiscovery = Pick<\n DiscoverContainerDocumentsOptions,\n | \"replaceDocumentLinksBatch\"\n | \"saveContainerDocumentWatermark\"\n | \"upsertDiscoveredDocuments\"\n | \"verifyDiscoveredDocuments\"\n>;\n\n/**\n * Binds a discovery lane's writes to its generation and its organization staying\n * unpaused. Access changes and pauses invalidate in-flight listings, so the\n * apply stops at its next check. Each write already started rechecks under\n * the SQLite mutation lock held by the reset or pause cleanup. A stale listing therefore never restores documents, links, or the\n * watermark the cleanup removed; a watermark saved after the cleanup would\n * otherwise make the listing after a resume skip every unchanged document.\n */\nexport function guardDocumentDiscoveryWrites(input: {\n readonly execSql: ExecSql;\n readonly isPaused: () => boolean;\n readonly verifyDiscoveredDocuments: DiscoverContainerDocumentsOptions[\"verifyDiscoveredDocuments\"];\n}): PauseGuardedDiscovery {\n const whileCurrent = <T>(\n skipped: T,\n write: (lockedExecSql: ExecSql) => Promise<T>,\n stillCurrent: () => Promise<boolean>,\n ): Promise<T> =>\n runSerializedSqlMutation(input.execSql, async (lockedExecSql) =>\n input.isPaused() || !(await stillCurrent())\n ? skipped\n : write(lockedExecSql),\n );\n return {\n replaceDocumentLinksBatch: (inputs, stillCurrent) =>\n whileCurrent(\n undefined,\n (lockedExecSql) =>\n sqlDocumentContainerProjectionPersistence.replaceDocumentLinksBatch(\n lockedExecSql,\n inputs,\n ),\n stillCurrent,\n ),\n saveContainerDocumentWatermark: (containerId, watermark, stillCurrent) =>\n whileCurrent(\n undefined,\n async (lockedExecSql) => {\n await sqlContainerSyncWatermarkPersistence.saveWatermark(\n lockedExecSql,\n containerContentsSyncLane(containerId),\n watermark,\n );\n },\n stillCurrent,\n ),\n upsertDiscoveredDocuments: (inputs, stillCurrent) =>\n whileCurrent(\n [],\n (lockedExecSql) => upsertDiscoveredDocuments(lockedExecSql, inputs),\n stillCurrent,\n ),\n async verifyDiscoveredDocuments(...args) {\n const verification = await input.verifyDiscoveredDocuments(...args);\n return {\n ...verification,\n isCurrent: async () =>\n !input.isPaused() && (await verification.isCurrent()),\n };\n },\n };\n}\n"]}