@tearleads/client-sdk 0.1.114 → 0.1.116

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/client/containerContents/documentDiscovery.js +5 -3
  2. package/client/containerContents/documentDiscovery.js.map +1 -1
  3. package/data/persistence/container-contents/containerRecoveryPersistence.js +152 -38
  4. package/data/persistence/container-contents/containerRecoveryPersistence.js.map +1 -1
  5. package/data/persistence/container-contents/documentContainerProjectionPersistence.js +47 -18
  6. package/data/persistence/container-contents/documentContainerProjectionPersistence.js.map +1 -1
  7. package/data/persistence/container-contents/documentPlacement.js +24 -5
  8. package/data/persistence/container-contents/documentPlacement.js.map +1 -1
  9. package/data/persistence/documents/containerDocumentTombstoneHoldsPersistence.js +1 -1
  10. package/data/persistence/documents/containerDocumentTombstoneHoldsPersistence.js.map +1 -1
  11. package/data/persistence/documents/documentDiscoveryEvidencePersistence.js +43 -35
  12. package/data/persistence/documents/documentDiscoveryEvidencePersistence.js.map +1 -1
  13. package/data/persistence/documents/documentsPersistence.js +4 -58
  14. package/data/persistence/documents/documentsPersistence.js.map +1 -1
  15. package/data/persistence/documents/internal/containerDocumentTombstoneHolds.js +15 -11
  16. package/data/persistence/documents/internal/containerDocumentTombstoneHolds.js.map +1 -1
  17. package/data/persistence/documents/internal/containerDocumentTombstones.js +87 -35
  18. package/data/persistence/documents/internal/containerDocumentTombstones.js.map +1 -1
  19. package/data/persistence/documents/internal/discoveredDocumentPlacement.js +5 -2
  20. package/data/persistence/documents/internal/discoveredDocumentPlacement.js.map +1 -1
  21. package/data/persistence/documents/internal/discoveredDocumentUpsert.js +174 -0
  22. package/data/persistence/documents/internal/discoveredDocumentUpsert.js.map +1 -0
  23. package/data/persistence/documents/internal/documentRows.js +96 -1
  24. package/data/persistence/documents/internal/documentRows.js.map +1 -1
  25. package/data/persistence/documents/internal/rowQueryPersistence.js +38 -20
  26. package/data/persistence/documents/internal/rowQueryPersistence.js.map +1 -1
  27. package/data/persistence/documents/records/documentRecordPersistence.d.ts +5 -0
  28. package/data/persistence/documents/records/documentRecordPersistence.js +41 -6
  29. package/data/persistence/documents/records/documentRecordPersistence.js.map +1 -1
  30. package/data/persistence/documents/records/index.d.ts +1 -1
  31. package/data/persistence/documents/records/index.js +1 -1
  32. package/data/persistence/documents/records/index.js.map +1 -1
  33. package/data/persistence/keyingCheckpoints/documentPurgeCheckpointPersistence.d.ts +2 -0
  34. package/data/persistence/keyingCheckpoints/documentPurgeCheckpointPersistence.js +17 -1
  35. package/data/persistence/keyingCheckpoints/documentPurgeCheckpointPersistence.js.map +1 -1
  36. package/internal/api-client/ApiClient.d.ts +118 -0
  37. package/internal/api-client/ApiClient.js +4 -0
  38. package/internal/api-client/ApiClient.js.map +1 -1
  39. package/internal/api-client/index.d.ts +1 -1
  40. package/internal/api-client/index.js.map +1 -1
  41. package/internal/api-client/principalPolicyPages.d.ts +15 -1
  42. package/internal/api-client/principalPolicyPages.js +48 -2
  43. package/internal/api-client/principalPolicyPages.js.map +1 -1
  44. package/internal/api-client/principalPolicyRequests.d.ts +118 -1
  45. package/internal/api-client/principalPolicyRequests.js +4 -1
  46. package/internal/api-client/principalPolicyRequests.js.map +1 -1
  47. package/internal/validators/operation/principalPolicyPageQuery.d.ts +9 -0
  48. package/internal/validators/operation/principalPolicyPageQuery.js +10 -0
  49. package/internal/validators/operation/principalPolicyPageQuery.js.map +1 -1
  50. package/internal/validators/operation/principals.d.ts +37 -4
  51. package/internal/validators/operation/principals.js +5 -3
  52. package/internal/validators/operation/principals.js.map +1 -1
  53. package/internal/validators/response/index.d.ts +1 -1
  54. package/internal/validators/response/index.js +1 -1
  55. package/internal/validators/response/index.js.map +1 -1
  56. package/internal/validators/response/principalPolicyPage.d.ts +497 -1
  57. package/internal/validators/response/principalPolicyPage.js +18 -0
  58. package/internal/validators/response/principalPolicyPage.js.map +1 -1
  59. package/package.json +1 -1
  60. package/workflows/container-contents/documentLinks/index.d.ts +4 -0
  61. package/workflows/container-contents/documentLinks/index.js +10 -3
  62. package/workflows/container-contents/documentLinks/index.js.map +1 -1
  63. package/workflows/container-contents/remoteHydration/remoteContainers/remoteContainerPageHydration.js +7 -2
  64. package/workflows/container-contents/remoteHydration/remoteContainers/remoteContainerPageHydration.js.map +1 -1
  65. package/workflows/container-contents/remoteReset/containerRemoteReset.js +12 -7
  66. package/workflows/container-contents/remoteReset/containerRemoteReset.js.map +1 -1
  67. package/workflows/container-contents/tombstones/documentTombstoneEvidence.js +6 -5
  68. package/workflows/container-contents/tombstones/documentTombstoneEvidence.js.map +1 -1
  69. package/workflows/documents/remoteReset/documentRemoteReset.js +31 -18
  70. package/workflows/documents/remoteReset/documentRemoteReset.js.map +1 -1
  71. package/workflows/principals/principalPolicyHistoryReader.d.ts +6 -1
  72. package/workflows/principals/principalPolicyHistoryReader.js +4 -1
  73. package/workflows/principals/principalPolicyHistoryReader.js.map +1 -1
  74. package/workflows/principals/recoveryOperation/principalRecoveryDirectory.js +108 -59
  75. package/workflows/principals/recoveryOperation/principalRecoveryDirectory.js.map +1 -1
  76. package/workflows/principals/runtimePolicyRecovery.js +26 -14
  77. package/workflows/principals/runtimePolicyRecovery.js.map +1 -1
  78. package/workflows/principals/runtimePolicyWarmer.d.ts +26 -0
  79. package/workflows/principals/runtimePolicyWarmer.js +8 -5
  80. package/workflows/principals/runtimePolicyWarmer.js.map +1 -1
@@ -1,18 +1,37 @@
1
1
  import { and, eq, inArray } from "drizzle-orm";
2
2
  import { DOCUMENTS_APP_KIND } from "../../documents/documentConstants.js";
3
3
  import { documents, inLocalIntentLane, LOCAL_INTENT_LANES, localIntents, } from "../../sqlite/schema/index.js";
4
+ import { sqliteRowsPerStatement } from "../../sqlite/sqliteBindBudget.js";
5
+ const movePlacementSelection = {
6
+ id: localIntents.id,
7
+ intentType: localIntents.intentType,
8
+ targetContainerId: localIntents.targetContainerId,
9
+ };
4
10
  export async function loadDocumentMovePlacement(tx, documentId) {
5
11
  const [intent] = await tx
6
- .select({
7
- id: localIntents.id,
8
- intentType: localIntents.intentType,
9
- targetContainerId: localIntents.targetContainerId,
10
- })
12
+ .select(movePlacementSelection)
11
13
  .from(localIntents)
12
14
  .where(and(inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement), eq(localIntents.subjectId, documentId)))
13
15
  .limit(1);
14
16
  return intent;
15
17
  }
18
+ /** `loadDocumentMovePlacement` for many documents, keyed by document id. */
19
+ export async function loadDocumentMovePlacements(tx, documentIds) {
20
+ const placements = new Map();
21
+ const unique = Array.from(new Set(documentIds));
22
+ // The lane predicate binds one value; the rest carry document ids.
23
+ const chunkSize = sqliteRowsPerStatement(1, 1);
24
+ for (let offset = 0; offset < unique.length; offset += chunkSize) {
25
+ const rows = await tx
26
+ .select({ subjectId: localIntents.subjectId, ...movePlacementSelection })
27
+ .from(localIntents)
28
+ .where(and(inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement), inArray(localIntents.subjectId, unique.slice(offset, offset + chunkSize))));
29
+ for (const { subjectId, ...placement } of rows) {
30
+ placements.set(subjectId, placement);
31
+ }
32
+ }
33
+ return placements;
34
+ }
16
35
  /** Check at commit time: neither intermediate replay nor discovery owns a pending move. */
17
36
  export async function filterWritableDocumentPlacements(tx, inputs, options) {
18
37
  const ids = inputs.map((input) => input.documentId);
@@ -1 +1 @@
1
- {"version":3,"file":"documentPlacement.js","sourceRoot":"","sources":["documentPlacement.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, inArray } from \"drizzle-orm\";\nimport { DOCUMENTS_APP_KIND } from \"../../documents/documentConstants\";\nimport {\n documents,\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../sqlite/schema\";\nimport type { ClientSQLiteTransactionScope } from \"../../sqlite/sqlitePersistenceRuntime\";\n\nexport interface DocumentPlacementInput {\n documentId: string;\n containerIds: ReadonlyArray<string>;\n accessEpoch?: number | undefined;\n}\n\nexport interface DocumentPlacementWriteOptions {\n stillCurrent?: (() => boolean) | undefined;\n moveIntentId?: string | undefined;\n}\n\nexport async function loadDocumentMovePlacement(\n tx: ClientSQLiteTransactionScope,\n documentId: string,\n) {\n const [intent] = await tx\n .select({\n id: localIntents.id,\n intentType: localIntents.intentType,\n targetContainerId: localIntents.targetContainerId,\n })\n .from(localIntents)\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n eq(localIntents.subjectId, documentId),\n ),\n )\n .limit(1);\n return intent;\n}\n\n/** Check at commit time: neither intermediate replay nor discovery owns a pending move. */\nexport async function filterWritableDocumentPlacements(\n tx: ClientSQLiteTransactionScope,\n inputs: readonly DocumentPlacementInput[],\n options?: DocumentPlacementWriteOptions,\n): Promise<readonly DocumentPlacementInput[]> {\n const ids = inputs.map((input) => input.documentId);\n const intents = new Map(\n (\n await tx\n .select({\n documentId: localIntents.subjectId,\n id: localIntents.id,\n })\n .from(localIntents)\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n inArray(localIntents.subjectId, ids),\n ),\n )\n ).map((row) => [row.documentId, row.id]),\n );\n const versionedIds = inputs\n .filter((input) => input.accessEpoch !== undefined)\n .map((input) => input.documentId);\n const epochs = new Map<string, number>();\n if (versionedIds.length > 0) {\n const rows = await tx\n .select({\n documentId: documents.documentId,\n accessEpoch: documents.accessEpoch,\n })\n .from(documents)\n .where(\n and(\n eq(documents.appKind, DOCUMENTS_APP_KIND),\n inArray(documents.documentId, versionedIds),\n ),\n );\n for (const row of rows)\n if (row.documentId)\n epochs.set(\n row.documentId,\n Math.max(epochs.get(row.documentId) ?? 0, row.accessEpoch),\n );\n }\n return inputs.filter((input) => {\n if (options?.moveIntentId)\n return intents.get(input.documentId) === options.moveIntentId;\n if (intents.has(input.documentId)) return false;\n return (\n input.accessEpoch === undefined ||\n input.accessEpoch >= (epochs.get(input.documentId) ?? 0)\n );\n });\n}\n"]}
1
+ {"version":3,"file":"documentPlacement.js","sourceRoot":"","sources":["documentPlacement.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, inArray } from \"drizzle-orm\";\nimport { DOCUMENTS_APP_KIND } from \"../../documents/documentConstants\";\nimport {\n documents,\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../sqlite/schema\";\nimport { sqliteRowsPerStatement } from \"../../sqlite/sqliteBindBudget\";\nimport type { ClientSQLiteTransactionScope } from \"../../sqlite/sqlitePersistenceRuntime\";\n\nexport interface DocumentPlacementInput {\n documentId: string;\n containerIds: ReadonlyArray<string>;\n accessEpoch?: number | undefined;\n}\n\nexport interface DocumentPlacementWriteOptions {\n stillCurrent?: (() => boolean) | undefined;\n moveIntentId?: string | undefined;\n}\n\nconst movePlacementSelection = {\n id: localIntents.id,\n intentType: localIntents.intentType,\n targetContainerId: localIntents.targetContainerId,\n};\n\nexport interface DocumentMovePlacement {\n readonly id: string | null;\n readonly intentType: string;\n readonly targetContainerId: string;\n}\n\nexport async function loadDocumentMovePlacement(\n tx: ClientSQLiteTransactionScope,\n documentId: string,\n): Promise<DocumentMovePlacement | undefined> {\n const [intent] = await tx\n .select(movePlacementSelection)\n .from(localIntents)\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n eq(localIntents.subjectId, documentId),\n ),\n )\n .limit(1);\n return intent;\n}\n\n/** `loadDocumentMovePlacement` for many documents, keyed by document id. */\nexport async function loadDocumentMovePlacements(\n tx: ClientSQLiteTransactionScope,\n documentIds: ReadonlyArray<string>,\n): Promise<Map<string, DocumentMovePlacement>> {\n const placements = new Map<string, DocumentMovePlacement>();\n const unique = Array.from(new Set(documentIds));\n // The lane predicate binds one value; the rest carry document ids.\n const chunkSize = sqliteRowsPerStatement(1, 1);\n for (let offset = 0; offset < unique.length; offset += chunkSize) {\n const rows = await tx\n .select({ subjectId: localIntents.subjectId, ...movePlacementSelection })\n .from(localIntents)\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n inArray(\n localIntents.subjectId,\n unique.slice(offset, offset + chunkSize),\n ),\n ),\n );\n for (const { subjectId, ...placement } of rows) {\n placements.set(subjectId, placement);\n }\n }\n return placements;\n}\n\n/** Check at commit time: neither intermediate replay nor discovery owns a pending move. */\nexport async function filterWritableDocumentPlacements(\n tx: ClientSQLiteTransactionScope,\n inputs: readonly DocumentPlacementInput[],\n options?: DocumentPlacementWriteOptions,\n): Promise<readonly DocumentPlacementInput[]> {\n const ids = inputs.map((input) => input.documentId);\n const intents = new Map(\n (\n await tx\n .select({\n documentId: localIntents.subjectId,\n id: localIntents.id,\n })\n .from(localIntents)\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n inArray(localIntents.subjectId, ids),\n ),\n )\n ).map((row) => [row.documentId, row.id]),\n );\n const versionedIds = inputs\n .filter((input) => input.accessEpoch !== undefined)\n .map((input) => input.documentId);\n const epochs = new Map<string, number>();\n if (versionedIds.length > 0) {\n const rows = await tx\n .select({\n documentId: documents.documentId,\n accessEpoch: documents.accessEpoch,\n })\n .from(documents)\n .where(\n and(\n eq(documents.appKind, DOCUMENTS_APP_KIND),\n inArray(documents.documentId, versionedIds),\n ),\n );\n for (const row of rows)\n if (row.documentId)\n epochs.set(\n row.documentId,\n Math.max(epochs.get(row.documentId) ?? 0, row.accessEpoch),\n );\n }\n return inputs.filter((input) => {\n if (options?.moveIntentId)\n return intents.get(input.documentId) === options.moveIntentId;\n if (intents.has(input.documentId)) return false;\n return (\n input.accessEpoch === undefined ||\n input.accessEpoch >= (epochs.get(input.documentId) ?? 0)\n );\n });\n}\n"]}
@@ -4,7 +4,7 @@ import { containerDocumentTombstoneHolds, documentContainerProjectionTables, doc
4
4
  import { getClientSQLitePersistenceRuntime } from "../../sqlite/sqlitePersistenceRuntime.js";
5
5
  import { ensureSqlTables, runSerializedSqlMutation, } from "../../sqlite/sqlSchema.js";
6
6
  import { deleteContainerDocumentTombstoneHoldRows, holdContainerDocumentTombstonesInTransaction, listContainerDocumentTombstoneHoldsForDocumentsInTransaction, listContainerDocumentTombstoneHoldsInTransaction, listKnownContainerDocumentPlacementsInTransaction, partitionContainerDocumentTombstoneHolds, } from "./internal/containerDocumentTombstoneHolds.js";
7
- export { containerDocumentPlacementKey, deleteContainerDocumentTombstoneHoldRowsForLinks, deleteContainerDocumentTombstoneHoldsForContainers, deleteContainerDocumentTombstoneHoldsForDocuments, } from "./internal/containerDocumentTombstoneHolds.js";
7
+ export { containerDocumentPlacementKey, deleteContainerDocumentTombstoneHoldRowsForLinks, deleteContainerDocumentTombstoneHoldsForContainers, deleteContainerDocumentTombstoneHoldsForDocuments, placementPairsMatch, } from "./internal/containerDocumentTombstoneHolds.js";
8
8
  /**
9
9
  * Retry backoff for a held tombstone: fifteen minutes after the first failed
10
10
  * verification, doubling on every further failure up to 32 hours. An
@@ -1 +1 @@
1
- {"version":3,"file":"containerDocumentTombstoneHoldsPersistence.js","sourceRoot":"","sources":["containerDocumentTombstoneHoldsPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, max } from \"drizzle-orm\";\nimport { DOCUMENTS_APP_KIND } from \"../../documents/documentConstants\";\nimport {\n containerDocumentTombstoneHolds,\n documentContainerProjectionTables,\n documents,\n documentTables,\n} from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport {\n type ContainerDocumentPlacementKey,\n type ContainerDocumentTombstoneHoldRow,\n deleteContainerDocumentTombstoneHoldRows,\n type HeldContainerDocumentTombstone,\n holdContainerDocumentTombstonesInTransaction,\n listContainerDocumentTombstoneHoldsForDocumentsInTransaction,\n listContainerDocumentTombstoneHoldsInTransaction,\n listKnownContainerDocumentPlacementsInTransaction,\n partitionContainerDocumentTombstoneHolds,\n} from \"./internal/containerDocumentTombstoneHolds\";\n\nexport {\n type ContainerDocumentPlacementKey,\n containerDocumentPlacementKey,\n deleteContainerDocumentTombstoneHoldRowsForLinks,\n deleteContainerDocumentTombstoneHoldsForContainers,\n deleteContainerDocumentTombstoneHoldsForDocuments,\n type HeldContainerDocumentTombstone,\n} from \"./internal/containerDocumentTombstoneHolds\";\n\n/**\n * Retry backoff for a held tombstone: fifteen minutes after the first failed\n * verification, doubling on every further failure up to 32 hours. An\n * honest hold that can never verify (the requester lost read access to the\n * document, or it was purged elsewhere) would otherwise cost a head fetch on\n * every discovery of its container for as long as the container exists.\n */\nexport const HELD_TOMBSTONE_RETRY_INTERVAL_MS = 15 * 60 * 1000;\nconst HELD_TOMBSTONE_MAX_BACKOFF_DOUBLINGS = 7;\n\nexport function heldTombstoneRetryDelayMs(attempts: number): number {\n const doublings = Math.min(\n Math.max(attempts, 1) - 1,\n HELD_TOMBSTONE_MAX_BACKOFF_DOUBLINGS,\n );\n return HELD_TOMBSTONE_RETRY_INTERVAL_MS * 2 ** doublings;\n}\n\nfunction isHoldDue(\n hold: ContainerDocumentTombstoneHoldRow,\n now: Date,\n): boolean {\n return (\n Date.parse(hold.updatedAt) + heldTombstoneRetryDelayMs(hold.attempts) <=\n now.getTime()\n );\n}\n\nexport async function holdContainerDocumentTombstones(\n execSql: ExecSql,\n tombstones: ReadonlyArray<HeldContainerDocumentTombstone>,\n now = new Date().toISOString(),\n): Promise<void> {\n if (tombstones.length === 0) return;\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureSqlTables(lockedExecSql, documentContainerProjectionTables);\n const { db } = getClientSQLitePersistenceRuntime(lockedExecSql);\n await db.transaction((tx) =>\n holdContainerDocumentTombstonesInTransaction(tx, tombstones, now),\n );\n });\n}\n\nexport async function refuteContainerDocumentTombstoneHolds(\n execSql: ExecSql,\n placements: ReadonlyArray<ContainerDocumentPlacementKey>,\n): Promise<void> {\n if (placements.length === 0) return;\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureSqlTables(lockedExecSql, documentContainerProjectionTables);\n const { db } = getClientSQLitePersistenceRuntime(lockedExecSql);\n await db.transaction(async (tx) => {\n for (const placement of placements) {\n await tx\n .update(containerDocumentTombstoneHolds)\n .set({ hidden: false })\n .where(\n and(\n eq(\n containerDocumentTombstoneHolds.containerId,\n placement.containerId,\n ),\n eq(\n containerDocumentTombstoneHolds.documentId,\n placement.documentId,\n ),\n ),\n )\n .run();\n }\n });\n });\n}\n\nexport async function listContainerDocumentTombstoneHolds(\n execSql: ExecSql,\n containerIds: ReadonlyArray<string>,\n): Promise<ContainerDocumentTombstoneHoldRow[]> {\n if (containerIds.length === 0) return [];\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n return listContainerDocumentTombstoneHoldsInTransaction(db, containerIds);\n}\n\nexport async function listContainerDocumentTombstoneHoldsForDocuments(\n execSql: ExecSql,\n documentIds: ReadonlyArray<string>,\n): Promise<ContainerDocumentPlacementKey[]> {\n if (documentIds.length === 0) return [];\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n return listContainerDocumentTombstoneHoldsForDocumentsInTransaction(\n db,\n documentIds,\n );\n}\n\nexport async function listKnownContainerDocumentPlacements(\n execSql: ExecSql,\n placements: ReadonlyArray<ContainerDocumentPlacementKey>,\n): Promise<ContainerDocumentPlacementKey[]> {\n if (placements.length === 0) return [];\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n return listKnownContainerDocumentPlacementsInTransaction(db, placements);\n}\n\n/**\n * The held tombstones on the given containers that are due for another\n * verification attempt. Holds whose placement is gone are pruned first; that\n * is the only write, and it is taken under the mutation lock only when there\n * is something to prune.\n */\nexport async function listRetryableHeldContainerDocumentTombstones(\n execSql: ExecSql,\n containerIds: ReadonlyArray<string>,\n now = new Date(),\n): Promise<HeldContainerDocumentTombstone[]> {\n if (containerIds.length === 0) return [];\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const { live, orphaned } = await partitionContainerDocumentTombstoneHolds(\n db,\n await listContainerDocumentTombstoneHoldsInTransaction(db, containerIds),\n );\n if (orphaned.length > 0) {\n // Re-check under the lock: a hold re-created between the unlocked read\n // and this write is hiding a placement again and must survive.\n await runSerializedSqlMutation(execSql, (lockedExecSql) =>\n getClientSQLitePersistenceRuntime(lockedExecSql).db.transaction(\n async (tx) =>\n deleteContainerDocumentTombstoneHoldRows(\n tx,\n (await partitionContainerDocumentTombstoneHolds(tx, orphaned))\n .orphaned,\n ),\n ),\n );\n }\n return live\n .filter((hold) => isHoldDue(hold, now))\n .map((hold) => ({\n containerId: hold.containerId,\n documentId: hold.documentId,\n updatedAt: hold.tombstonedAt,\n }));\n}\n\n/** Ordinary-document floors must exclude unsigned container-metadata epochs. */\nexport async function loadLocalDocumentAccessEpoch(\n execSql: ExecSql,\n documentId: string,\n): Promise<number> {\n await ensureSqlTables(execSql, documentTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const [row] = await db\n .select({ accessEpoch: max(documents.accessEpoch) })\n .from(documents)\n .where(\n and(\n eq(documents.appKind, DOCUMENTS_APP_KIND),\n eq(documents.documentId, documentId),\n ),\n );\n return row?.accessEpoch ?? 0;\n}\n"]}
1
+ {"version":3,"file":"containerDocumentTombstoneHoldsPersistence.js","sourceRoot":"","sources":["containerDocumentTombstoneHoldsPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, max } from \"drizzle-orm\";\nimport { DOCUMENTS_APP_KIND } from \"../../documents/documentConstants\";\nimport {\n containerDocumentTombstoneHolds,\n documentContainerProjectionTables,\n documents,\n documentTables,\n} from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport {\n type ContainerDocumentPlacementKey,\n type ContainerDocumentTombstoneHoldRow,\n deleteContainerDocumentTombstoneHoldRows,\n type HeldContainerDocumentTombstone,\n holdContainerDocumentTombstonesInTransaction,\n listContainerDocumentTombstoneHoldsForDocumentsInTransaction,\n listContainerDocumentTombstoneHoldsInTransaction,\n listKnownContainerDocumentPlacementsInTransaction,\n partitionContainerDocumentTombstoneHolds,\n} from \"./internal/containerDocumentTombstoneHolds\";\n\nexport {\n type ContainerDocumentPlacementKey,\n containerDocumentPlacementKey,\n deleteContainerDocumentTombstoneHoldRowsForLinks,\n deleteContainerDocumentTombstoneHoldsForContainers,\n deleteContainerDocumentTombstoneHoldsForDocuments,\n type HeldContainerDocumentTombstone,\n placementPairsMatch,\n} from \"./internal/containerDocumentTombstoneHolds\";\n\n/**\n * Retry backoff for a held tombstone: fifteen minutes after the first failed\n * verification, doubling on every further failure up to 32 hours. An\n * honest hold that can never verify (the requester lost read access to the\n * document, or it was purged elsewhere) would otherwise cost a head fetch on\n * every discovery of its container for as long as the container exists.\n */\nexport const HELD_TOMBSTONE_RETRY_INTERVAL_MS = 15 * 60 * 1000;\nconst HELD_TOMBSTONE_MAX_BACKOFF_DOUBLINGS = 7;\n\nexport function heldTombstoneRetryDelayMs(attempts: number): number {\n const doublings = Math.min(\n Math.max(attempts, 1) - 1,\n HELD_TOMBSTONE_MAX_BACKOFF_DOUBLINGS,\n );\n return HELD_TOMBSTONE_RETRY_INTERVAL_MS * 2 ** doublings;\n}\n\nfunction isHoldDue(\n hold: ContainerDocumentTombstoneHoldRow,\n now: Date,\n): boolean {\n return (\n Date.parse(hold.updatedAt) + heldTombstoneRetryDelayMs(hold.attempts) <=\n now.getTime()\n );\n}\n\nexport async function holdContainerDocumentTombstones(\n execSql: ExecSql,\n tombstones: ReadonlyArray<HeldContainerDocumentTombstone>,\n now = new Date().toISOString(),\n): Promise<void> {\n if (tombstones.length === 0) return;\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureSqlTables(lockedExecSql, documentContainerProjectionTables);\n const { db } = getClientSQLitePersistenceRuntime(lockedExecSql);\n await db.transaction((tx) =>\n holdContainerDocumentTombstonesInTransaction(tx, tombstones, now),\n );\n });\n}\n\nexport async function refuteContainerDocumentTombstoneHolds(\n execSql: ExecSql,\n placements: ReadonlyArray<ContainerDocumentPlacementKey>,\n): Promise<void> {\n if (placements.length === 0) return;\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureSqlTables(lockedExecSql, documentContainerProjectionTables);\n const { db } = getClientSQLitePersistenceRuntime(lockedExecSql);\n await db.transaction(async (tx) => {\n for (const placement of placements) {\n await tx\n .update(containerDocumentTombstoneHolds)\n .set({ hidden: false })\n .where(\n and(\n eq(\n containerDocumentTombstoneHolds.containerId,\n placement.containerId,\n ),\n eq(\n containerDocumentTombstoneHolds.documentId,\n placement.documentId,\n ),\n ),\n )\n .run();\n }\n });\n });\n}\n\nexport async function listContainerDocumentTombstoneHolds(\n execSql: ExecSql,\n containerIds: ReadonlyArray<string>,\n): Promise<ContainerDocumentTombstoneHoldRow[]> {\n if (containerIds.length === 0) return [];\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n return listContainerDocumentTombstoneHoldsInTransaction(db, containerIds);\n}\n\nexport async function listContainerDocumentTombstoneHoldsForDocuments(\n execSql: ExecSql,\n documentIds: ReadonlyArray<string>,\n): Promise<ContainerDocumentPlacementKey[]> {\n if (documentIds.length === 0) return [];\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n return listContainerDocumentTombstoneHoldsForDocumentsInTransaction(\n db,\n documentIds,\n );\n}\n\nexport async function listKnownContainerDocumentPlacements(\n execSql: ExecSql,\n placements: ReadonlyArray<ContainerDocumentPlacementKey>,\n): Promise<ContainerDocumentPlacementKey[]> {\n if (placements.length === 0) return [];\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n return listKnownContainerDocumentPlacementsInTransaction(db, placements);\n}\n\n/**\n * The held tombstones on the given containers that are due for another\n * verification attempt. Holds whose placement is gone are pruned first; that\n * is the only write, and it is taken under the mutation lock only when there\n * is something to prune.\n */\nexport async function listRetryableHeldContainerDocumentTombstones(\n execSql: ExecSql,\n containerIds: ReadonlyArray<string>,\n now = new Date(),\n): Promise<HeldContainerDocumentTombstone[]> {\n if (containerIds.length === 0) return [];\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const { live, orphaned } = await partitionContainerDocumentTombstoneHolds(\n db,\n await listContainerDocumentTombstoneHoldsInTransaction(db, containerIds),\n );\n if (orphaned.length > 0) {\n // Re-check under the lock: a hold re-created between the unlocked read\n // and this write is hiding a placement again and must survive.\n await runSerializedSqlMutation(execSql, (lockedExecSql) =>\n getClientSQLitePersistenceRuntime(lockedExecSql).db.transaction(\n async (tx) =>\n deleteContainerDocumentTombstoneHoldRows(\n tx,\n (await partitionContainerDocumentTombstoneHolds(tx, orphaned))\n .orphaned,\n ),\n ),\n );\n }\n return live\n .filter((hold) => isHoldDue(hold, now))\n .map((hold) => ({\n containerId: hold.containerId,\n documentId: hold.documentId,\n updatedAt: hold.tombstonedAt,\n }));\n}\n\n/** Ordinary-document floors must exclude unsigned container-metadata epochs. */\nexport async function loadLocalDocumentAccessEpoch(\n execSql: ExecSql,\n documentId: string,\n): Promise<number> {\n await ensureSqlTables(execSql, documentTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const [row] = await db\n .select({ accessEpoch: max(documents.accessEpoch) })\n .from(documents)\n .where(\n and(\n eq(documents.appKind, DOCUMENTS_APP_KIND),\n eq(documents.documentId, documentId),\n ),\n );\n return row?.accessEpoch ?? 0;\n}\n"]}
@@ -1,20 +1,24 @@
1
- import { and, asc, eq, inArray, lte, min, sql } from "drizzle-orm";
1
+ import { and, asc, eq, inArray, lte, min, or, sql } from "drizzle-orm";
2
2
  import { uniqueSortedStrings } from "../../documents/shared/readers.js";
3
3
  import { readStringArray } from "../../shared/recordReaders.js";
4
4
  import { documentContainerProjectionTables } from "../../sqlite/schema/index.js";
5
5
  import { documentDiscoveryHeads, documentDiscoverySequence, pendingDocumentDiscoveries, } from "../../sqlite/schema/documentDiscoveryEvidenceSchema.js";
6
+ import { remoteResetBatches, sqliteInsertBatchSize, } from "../../sqlite/sqliteBindBudget.js";
6
7
  import { getClientSQLitePersistenceRuntime } from "../../sqlite/sqlitePersistenceRuntime.js";
8
+ import { excludedValue } from "../../sqlite/sqliteUpsert.js";
7
9
  import { ensureSqlTables, runSerializedSqlMutation, } from "../../sqlite/sqlSchema.js";
8
10
  import { loadDocumentPurgeCheckpoint } from "../keyingCheckpoints/documentPurgeCheckpointPersistence.js";
9
11
  import { loadAccessManifestCheckpoint } from "../keyingCheckpoints/keyingCheckpointPersistence.js";
10
12
  import { heldTombstoneRetryDelayMs } from "./containerDocumentTombstoneHoldsPersistence.js";
11
13
  import { isDiscoveredDocumentCandidate, readStoredDiscoveryCandidate, } from "./documentDiscoveryCandidate.js";
12
14
  import { isDocumentDiscoveryGenerationCurrent } from "./documentDiscoveryReset.js";
15
+ import { placementPairsMatch } from "./internal/containerDocumentTombstoneHolds.js";
13
16
  export { clearDocumentDiscoveryEvidenceOnRemoteReset } from "./documentDiscoveryReset.js";
14
17
  const pending = pendingDocumentDiscoveries;
15
18
  const heads = documentDiscoveryHeads;
16
19
  const ID_BATCH_SIZE = 400;
17
20
  const batches = (ids) => Array.from({ length: Math.ceil(ids.length / ID_BATCH_SIZE) }, (_, i) => ids.slice(i * ID_BATCH_SIZE, (i + 1) * ID_BATCH_SIZE));
21
+ const chunked = (rows, size) => Array.from({ length: Math.ceil(rows.length / size) }, (_, i) => rows.slice(i * size, (i + 1) * size));
18
22
  const identity = (input) => and(eq(pending.containerId, input.containerId), eq(pending.documentId, input.documentId), eq(pending.inputJson, JSON.stringify(input)));
19
23
  class SqlDocumentDiscoveryEvidenceStore {
20
24
  execSql;
@@ -58,46 +62,47 @@ class SqlDocumentDiscoveryEvidenceStore {
58
62
  if (inputs.length === 0 && removed.length === 0)
59
63
  return true;
60
64
  await db.transaction(async (tx) => {
61
- for (const input of inputs) {
62
- if (!isDiscoveredDocumentCandidate(input))
63
- continue;
64
- for (const containerId of input.listedContainerIds) {
65
- const rowInput = {
65
+ const rows = inputs.flatMap((input) => isDiscoveredDocumentCandidate(input)
66
+ ? input.listedContainerIds.map((containerId) => ({
67
+ containerId,
68
+ documentId: input.documentId,
69
+ accessEpoch: input.accessEpoch,
70
+ generation,
71
+ inputJson: JSON.stringify({
66
72
  ...input,
67
73
  containerId,
68
74
  listedContainerIds: [containerId],
69
- };
70
- const inputJson = JSON.stringify(rowInput);
71
- await tx
72
- .insert(pending)
73
- .values({
74
- containerId,
75
- documentId: input.documentId,
76
- accessEpoch: input.accessEpoch,
77
- generation,
78
- inputJson,
79
- retryAt: 0,
80
- })
81
- .onConflictDoUpdate({
82
- target: [pending.containerId, pending.documentId],
83
- set: {
84
- accessEpoch: input.accessEpoch,
85
- generation,
86
- inputJson,
87
- retryAt: sql `case when ${pending.inputJson} = ${inputJson} then ${pending.retryAt} else 0 end`,
88
- attempts: sql `case when ${pending.inputJson} = ${inputJson} then ${pending.attempts} else 0 end`,
89
- },
90
- setWhere: lte(pending.generation, generation),
91
- })
92
- .run();
93
- }
75
+ }),
76
+ retryAt: 0,
77
+ }))
78
+ : []);
79
+ // One multi-row upsert per chunk. SQLite applies a statement's rows
80
+ // in order, so a repeated placement updates the row the previous
81
+ // entry wrote, exactly as one statement per entry did.
82
+ const unchanged = sql `${pending.inputJson} = excluded.input_json`;
83
+ for (const chunk of chunked(rows, sqliteInsertBatchSize(pending))) {
84
+ await tx
85
+ .insert(pending)
86
+ .values(chunk)
87
+ .onConflictDoUpdate({
88
+ target: [pending.containerId, pending.documentId],
89
+ set: {
90
+ accessEpoch: excludedValue(pending.accessEpoch),
91
+ generation: excludedValue(pending.generation),
92
+ inputJson: excludedValue(pending.inputJson),
93
+ retryAt: sql `case when ${unchanged} then ${pending.retryAt} else 0 end`,
94
+ attempts: sql `case when ${unchanged} then ${pending.attempts} else 0 end`,
95
+ },
96
+ setWhere: lte(pending.generation, generation),
97
+ })
98
+ .run();
94
99
  }
95
100
  // These are untrusted, unapplied hints only. Removing an existing local
96
101
  // placement still requires the separate signed tombstone evidence gate.
97
- for (const row of removed) {
102
+ for (const batch of remoteResetBatches(removed)) {
98
103
  await tx
99
104
  .delete(pending)
100
- .where(and(eq(pending.containerId, row.containerId), eq(pending.documentId, row.documentId), lte(pending.generation, generation)))
105
+ .where(and(placementPairsMatch(batch, pending.documentId, pending.containerId), lte(pending.generation, generation)))
101
106
  .run();
102
107
  }
103
108
  });
@@ -144,8 +149,11 @@ class SqlDocumentDiscoveryEvidenceStore {
144
149
  if (!(await isDocumentDiscoveryGenerationCurrent(db, generation)))
145
150
  return;
146
151
  await db.transaction(async (tx) => {
147
- for (const input of inputs)
148
- await tx.delete(pending).where(identity(input)).run();
152
+ for (const batch of remoteResetBatches(inputs))
153
+ await tx
154
+ .delete(pending)
155
+ .where(or(...batch.map(identity)))
156
+ .run();
149
157
  });
150
158
  });
151
159
  hasPending = async (containerIds) => {
@@ -1 +1 @@
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"]}
1
+ {"version":3,"file":"documentDiscoveryEvidencePersistence.js","sourceRoot":"","sources":["documentDiscoveryEvidencePersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, asc, eq, inArray, lte, min, or, 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 {\n remoteResetBatches,\n sqliteInsertBatchSize,\n} from \"../../sqlite/sqliteBindBudget\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { excludedValue } from \"../../sqlite/sqliteUpsert\";\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\";\nimport {\n type DiscoveredDocumentCandidate,\n isDiscoveredDocumentCandidate,\n readStoredDiscoveryCandidate,\n} from \"./documentDiscoveryCandidate\";\nimport { isDocumentDiscoveryGenerationCurrent } from \"./documentDiscoveryReset\";\nimport { placementPairsMatch } from \"./internal/containerDocumentTombstoneHolds\";\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 chunked = <T>(rows: readonly T[], size: number): T[][] =>\n Array.from({ length: Math.ceil(rows.length / size) }, (_, i) =>\n rows.slice(i * size, (i + 1) * 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 const rows = inputs.flatMap((input) =>\n isDiscoveredDocumentCandidate(input)\n ? input.listedContainerIds.map((containerId) => ({\n containerId,\n documentId: input.documentId,\n accessEpoch: input.accessEpoch,\n generation,\n inputJson: JSON.stringify({\n ...input,\n containerId,\n listedContainerIds: [containerId],\n }),\n retryAt: 0,\n }))\n : [],\n );\n // One multi-row upsert per chunk. SQLite applies a statement's rows\n // in order, so a repeated placement updates the row the previous\n // entry wrote, exactly as one statement per entry did.\n const unchanged = sql`${pending.inputJson} = excluded.input_json`;\n for (const chunk of chunked(rows, sqliteInsertBatchSize(pending))) {\n await tx\n .insert(pending)\n .values(chunk)\n .onConflictDoUpdate({\n target: [pending.containerId, pending.documentId],\n set: {\n accessEpoch: excludedValue(pending.accessEpoch),\n generation: excludedValue(pending.generation),\n inputJson: excludedValue(pending.inputJson),\n retryAt: sql`case when ${unchanged} then ${pending.retryAt} else 0 end`,\n attempts: sql`case when ${unchanged} then ${pending.attempts} else 0 end`,\n },\n setWhere: lte(pending.generation, generation),\n })\n .run();\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 batch of remoteResetBatches(removed)) {\n await tx\n .delete(pending)\n .where(\n and(\n placementPairsMatch(\n batch,\n pending.documentId,\n pending.containerId,\n ),\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 batch of remoteResetBatches(inputs))\n await tx\n .delete(pending)\n .where(or(...batch.map(identity)))\n .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,15 +1,12 @@
1
- import { KeyingVerificationError } from "../../../internal/crypto/index.js";
2
1
  import { and, desc, eq, inArray, notInArray, or } from "drizzle-orm";
3
- import { DEFAULT_DOCUMENT_KIND, DOCUMENTS_APP_KIND, } from "../../documents/documentConstants.js";
4
2
  import { HIDDEN_DOCUMENT_SUMMARY_KINDS, } from "../../documents/documentSummary.js";
5
3
  import { documentProjection, documents } from "../../sqlite/schema/index.js";
6
- import { getClientSQLitePersistenceRuntime, } from "../../sqlite/sqlitePersistenceRuntime.js";
4
+ import { getClientSQLitePersistenceRuntime } from "../../sqlite/sqlitePersistenceRuntime.js";
7
5
  import { runSerializedSqlMutation } from "../../sqlite/sqlSchema.js";
8
- import { hasDocumentPurgeCheckpointInTransaction } from "../keyingCheckpoints/documentPurgeCheckpointPersistence.js";
9
6
  import { applyContainerDocumentTombstonesWithExec } from "./internal/containerDocumentTombstones.js";
10
7
  import { createStoredDocumentWithHistoryCheckpoint } from "./internal/createDocumentWithHistoryCheckpoint.js";
11
8
  import { discardStoredDocumentToShell } from "./internal/discardDocument.js";
12
- import { resolveDiscoveredDocumentPlacement } from "./internal/discoveredDocumentPlacement.js";
9
+ import { upsertDiscoveredDocumentsInTransaction } from "./internal/discoveredDocumentUpsert.js";
13
10
  import { deleteStoredDocument, deleteStoredDocumentIfMatches, deleteStoredDocumentSideRowsIfAbsent, } from "./internal/documentDeletionPersistence.js";
14
11
  import { loadStoredDocumentWithHistoryRestoreState } from "./internal/documentHistoryStatePersistence.js";
15
12
  import { commitStoredDocumentMutation, settleStoredDocumentPendingUpdates, } from "./internal/documentMutationPersistence.js";
@@ -22,58 +19,11 @@ import { listDocumentSummaries } from "./internal/documentSummaryQueries.js";
22
19
  import { ensureDocumentsSchema } from "./internal/ensureDocumentsSchema.js";
23
20
  import { saveHydratedAttachment } from "./internal/hydratedAttachmentPersistence.js";
24
21
  import { acknowledgeDocumentOrphanBlobReclaim, deleteOrphanedDocumentSideRows, isDocumentBlobStorageKeyReferenced, listDocumentOrphanBlobReclaims, } from "./internal/orphanSideRows.js";
25
- import { loadPendingCreateSummary, mapPendingCreateLocalIds, } from "./internal/pendingCreateAdoption.js";
22
+ import { mapPendingCreateLocalIds } from "./internal/pendingCreateAdoption.js";
26
23
  import { documentRowQueryPersistence } from "./internal/rowQueryPersistence.js";
27
24
  import { documentSyncQueuePersistence } from "./internal/syncQueuePersistence.js";
28
- import { findLocalIdByDocumentId } from "./records/index.js";
29
25
  import { settleQueuedDocumentUpdatesInTransaction } from "./records/documentPendingUpdatePersistence.js";
30
26
  export { DOCUMENTS_APP_KIND } from "../../documents/documentConstants.js";
31
- const DISCOVERED_DOCUMENT_PLACEHOLDER_TITLE = "Syncing document...";
32
- async function upsertDiscoveredDocumentWithExec(execSql, input, pendingCreates, tx) {
33
- if (await hasDocumentPurgeCheckpointInTransaction(tx, input.documentId)) {
34
- throw new KeyingVerificationError("rollback", "Document discovery targets a permanently purged document");
35
- }
36
- const existingLocalId = await findLocalIdByDocumentId(execSql, DOCUMENTS_APP_KIND, input.documentId);
37
- const localId = existingLocalId ?? input.documentId;
38
- const pendingLocalId = pendingCreates.get(input.documentId);
39
- if (!existingLocalId && pendingLocalId) {
40
- const pending = await loadPendingCreateSummary(execSql, pendingLocalId);
41
- if (!pending)
42
- throw new Error("Pending create projection is unavailable");
43
- return pending;
44
- }
45
- const existingDocument = await sqlDocumentsPersistence.loadDocument(execSql, localId);
46
- const placement = await resolveDiscoveredDocumentPlacement(tx, existingDocument, input);
47
- const nextDocument = {
48
- id: localId,
49
- documentId: input.documentId,
50
- documentKind: existingDocument?.documentKind ?? DEFAULT_DOCUMENT_KIND,
51
- text: existingDocument?.text ?? "",
52
- title: existingDocument?.title ?? DISCOVERED_DOCUMENT_PLACEHOLDER_TITLE,
53
- snapshotEndVersion: existingDocument?.snapshotEndVersion ?? "",
54
- recoveryGeneration: existingDocument?.recoveryGeneration ?? 0,
55
- ...placement,
56
- effectiveAccessLevel: input.effectiveAccessLevel ??
57
- existingDocument?.effectiveAccessLevel ??
58
- null,
59
- ...resolvePersistedDocumentRuntimeState(existingDocument, {
60
- accessEpoch: placement.accessEpoch,
61
- documentId: input.documentId,
62
- }),
63
- };
64
- const saveOptions = existingDocument === null || existingDocument === undefined
65
- ? { updatedAt: input.createdAt }
66
- : undefined;
67
- const updatedAt = await resolveDocumentSaveTimestamp({
68
- document: nextDocument,
69
- options: saveOptions,
70
- tx,
71
- });
72
- if (!(await saveDocumentRows({ document: nextDocument, tx, updatedAt }))) {
73
- throw new Error("Document recovery generation changed during discovery");
74
- }
75
- return toDocumentSummary(nextDocument, updatedAt);
76
- }
77
27
  async function relinkPersistedDocumentWithExec(execSql, input) {
78
28
  const existingDocument = await sqlDocumentsPersistence.loadDocument(execSql, input.localId);
79
29
  if (!existingDocument) {
@@ -109,11 +59,7 @@ export async function upsertDiscoveredDocuments(execSql, inputs) {
109
59
  await sqlDocumentsPersistence.ensureSchema(lockedExecSql);
110
60
  return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(async (tx) => {
111
61
  const pendingCreates = await mapPendingCreateLocalIds(lockedExecSql);
112
- const nextSummaries = [];
113
- for (const input of inputs) {
114
- nextSummaries.push(await upsertDiscoveredDocumentWithExec(lockedExecSql, input, pendingCreates, tx));
115
- }
116
- return nextSummaries;
62
+ return upsertDiscoveredDocumentsInTransaction(lockedExecSql, inputs, pendingCreates, tx);
117
63
  }, { behavior: "immediate" });
118
64
  });
119
65
  }
@@ -1 +1 @@
1
- {"version":3,"file":"documentsPersistence.js","sourceRoot":"","sources":["documentsPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport { and, desc, eq, inArray, notInArray, or, type SQL } from \"drizzle-orm\";\nimport {\n DEFAULT_DOCUMENT_KIND,\n DOCUMENTS_APP_KIND,\n} from \"../../documents/documentConstants\";\nimport {\n type DiscoveredDocumentInput,\n type DocumentSummary,\n HIDDEN_DOCUMENT_SUMMARY_KINDS,\n} from \"../../documents/documentSummary\";\nimport { documentProjection, documents } from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, runSerializedSqlMutation } from \"../../sqlite/sqlSchema\";\nimport { hasDocumentPurgeCheckpointInTransaction } from \"../keyingCheckpoints/documentPurgeCheckpointPersistence\";\nimport { applyContainerDocumentTombstonesWithExec } from \"./internal/containerDocumentTombstones\";\nimport { createStoredDocumentWithHistoryCheckpoint } from \"./internal/createDocumentWithHistoryCheckpoint\";\nimport { discardStoredDocumentToShell } from \"./internal/discardDocument\";\nimport { resolveDiscoveredDocumentPlacement } from \"./internal/discoveredDocumentPlacement\";\nimport {\n deleteStoredDocument,\n deleteStoredDocumentIfMatches,\n deleteStoredDocumentSideRowsIfAbsent,\n} from \"./internal/documentDeletionPersistence\";\nimport { loadStoredDocumentWithHistoryRestoreState } from \"./internal/documentHistoryStatePersistence\";\nimport {\n commitStoredDocumentMutation,\n settleStoredDocumentPendingUpdates,\n} from \"./internal/documentMutationPersistence\";\nimport {\n documentSummaryJoin,\n documentSummarySelection,\n getProjectionUpdatedAt,\n mapDocumentSummary,\n toDocumentSummary,\n} from \"./internal/documentProjectionRows\";\nimport { invalidateStoredDocumentPullContinuation } from \"./internal/documentPullContinuationPersistence\";\nimport {\n getDocumentScope,\n resolveDocumentSaveTimestamp,\n saveDocumentRows,\n} from \"./internal/documentRows\";\nimport {\n resolvePersistedAccessStateHash,\n resolvePersistedDocumentRuntimeState,\n} from \"./internal/documentRuntimeState\";\nimport { loadStoredDocumentStoreState } from \"./internal/documentStoreStatePersistence\";\nimport { listDocumentSummaries } from \"./internal/documentSummaryQueries\";\nimport { ensureDocumentsSchema } from \"./internal/ensureDocumentsSchema\";\nimport { saveHydratedAttachment } from \"./internal/hydratedAttachmentPersistence\";\nimport {\n acknowledgeDocumentOrphanBlobReclaim,\n deleteOrphanedDocumentSideRows,\n isDocumentBlobStorageKeyReferenced,\n listDocumentOrphanBlobReclaims,\n} from \"./internal/orphanSideRows\";\nimport {\n loadPendingCreateSummary,\n mapPendingCreateLocalIds,\n} from \"./internal/pendingCreateAdoption\";\nimport { documentRowQueryPersistence } from \"./internal/rowQueryPersistence\";\nimport { documentSyncQueuePersistence } from \"./internal/syncQueuePersistence\";\nimport { findLocalIdByDocumentId } from \"./records\";\nimport { settleQueuedDocumentUpdatesInTransaction } from \"./records/documentPendingUpdatePersistence\";\nimport type {\n ContainerDocumentTombstoneInput,\n DiscardDocumentToShellResult,\n DocumentsPersistence,\n RelinkPersistedDocumentInput,\n StoredDocumentRecord,\n} from \"./types\";\n\nexport { DOCUMENTS_APP_KIND } from \"../../documents/documentConstants\";\nexport type {\n AttachmentRemovalRows,\n AttachmentStagingRows,\n ContainerDocumentTombstoneInput,\n DiscardDocumentToShellResult,\n DocumentsPersistence,\n LocalAttachmentRecord,\n PendingAttachmentRecord,\n PendingAttachmentUploadIdentity,\n PendingUpdateInsert,\n PendingUpdateRecord,\n RelinkPersistedDocumentInput,\n StoredDocumentRecord,\n} from \"./types\";\n\nconst DISCOVERED_DOCUMENT_PLACEHOLDER_TITLE = \"Syncing document...\";\n\nasync function upsertDiscoveredDocumentWithExec(\n execSql: ExecSql,\n input: DiscoveredDocumentInput,\n pendingCreates: ReadonlyMap<string, string>,\n tx: ClientSQLiteTransactionScope,\n): Promise<DocumentSummary> {\n if (await hasDocumentPurgeCheckpointInTransaction(tx, input.documentId)) {\n throw new KeyingVerificationError(\n \"rollback\",\n \"Document discovery targets a permanently purged document\",\n );\n }\n const existingLocalId = await findLocalIdByDocumentId(\n execSql,\n DOCUMENTS_APP_KIND,\n input.documentId,\n );\n const localId = existingLocalId ?? input.documentId;\n const pendingLocalId = pendingCreates.get(input.documentId);\n if (!existingLocalId && pendingLocalId) {\n const pending = await loadPendingCreateSummary(execSql, pendingLocalId);\n if (!pending) throw new Error(\"Pending create projection is unavailable\");\n return pending;\n }\n const existingDocument = await sqlDocumentsPersistence.loadDocument(\n execSql,\n localId,\n );\n const placement = await resolveDiscoveredDocumentPlacement(\n tx,\n existingDocument,\n input,\n );\n\n const nextDocument: StoredDocumentRecord = {\n id: localId,\n documentId: input.documentId,\n documentKind: existingDocument?.documentKind ?? DEFAULT_DOCUMENT_KIND,\n text: existingDocument?.text ?? \"\",\n title: existingDocument?.title ?? DISCOVERED_DOCUMENT_PLACEHOLDER_TITLE,\n snapshotEndVersion: existingDocument?.snapshotEndVersion ?? \"\",\n recoveryGeneration: existingDocument?.recoveryGeneration ?? 0,\n ...placement,\n effectiveAccessLevel:\n input.effectiveAccessLevel ??\n existingDocument?.effectiveAccessLevel ??\n null,\n ...resolvePersistedDocumentRuntimeState(existingDocument, {\n accessEpoch: placement.accessEpoch,\n documentId: input.documentId,\n }),\n };\n\n const saveOptions =\n existingDocument === null || existingDocument === undefined\n ? { updatedAt: input.createdAt }\n : undefined;\n const updatedAt = await resolveDocumentSaveTimestamp({\n document: nextDocument,\n options: saveOptions,\n tx,\n });\n if (!(await saveDocumentRows({ document: nextDocument, tx, updatedAt }))) {\n throw new Error(\"Document recovery generation changed during discovery\");\n }\n\n return toDocumentSummary(nextDocument, updatedAt);\n}\n\nasync function relinkPersistedDocumentWithExec(\n execSql: ExecSql,\n input: RelinkPersistedDocumentInput,\n): Promise<DocumentSummary | null> {\n const existingDocument = await sqlDocumentsPersistence.loadDocument(\n execSql,\n input.localId,\n );\n if (!existingDocument) {\n return null;\n }\n\n const nextAccessEpoch = Math.max(\n existingDocument.accessEpoch,\n input.accessEpoch,\n );\n const nextDocument: StoredDocumentRecord = {\n ...existingDocument,\n accessEpoch: nextAccessEpoch,\n accessStateHash: resolvePersistedAccessStateHash(existingDocument, {\n accessEpoch: nextAccessEpoch,\n accessStateHash: input.accessStateHash,\n documentId: input.documentId,\n }),\n containerId: input.containerId,\n documentId: input.documentId,\n ...resolvePersistedDocumentRuntimeState(existingDocument, {\n accessEpoch: nextAccessEpoch,\n documentId: input.documentId,\n }),\n };\n\n await sqlDocumentsPersistence.saveDocument(execSql, nextDocument);\n\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const updatedAtRows = await db\n .select({ updatedAt: documentProjection.updatedAt })\n .from(documentProjection)\n .where(eq(documentProjection.localId, input.localId))\n .limit(1);\n\n return toDocumentSummary(\n nextDocument,\n getProjectionUpdatedAt(updatedAtRows[0]),\n );\n}\n\nexport async function upsertDiscoveredDocuments(\n execSql: ExecSql,\n inputs: ReadonlyArray<DiscoveredDocumentInput>,\n): Promise<DocumentSummary[]> {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await sqlDocumentsPersistence.ensureSchema(lockedExecSql);\n return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const pendingCreates = await mapPendingCreateLocalIds(lockedExecSql);\n const nextSummaries: DocumentSummary[] = [];\n\n for (const input of inputs) {\n nextSummaries.push(\n await upsertDiscoveredDocumentWithExec(\n lockedExecSql,\n input,\n pendingCreates,\n tx,\n ),\n );\n }\n\n return nextSummaries;\n },\n { behavior: \"immediate\" },\n );\n });\n}\n\nexport async function applyContainerDocumentTombstones(\n execSql: ExecSql,\n tombstones: ReadonlyArray<ContainerDocumentTombstoneInput>,\n): Promise<DocumentSummary[]> {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await sqlDocumentsPersistence.ensureSchema(lockedExecSql);\n return applyContainerDocumentTombstonesWithExec(lockedExecSql, tombstones);\n });\n}\n\nexport async function listDocumentsByContainerIdsOrDocumentIds(\n execSql: ExecSql,\n input: {\n containerIds: ReadonlyArray<string>;\n documentIds: ReadonlyArray<string>;\n },\n): Promise<DocumentSummary[]> {\n const uniqueContainerIds = Array.from(new Set(input.containerIds));\n const uniqueDocumentIds = Array.from(new Set(input.documentIds));\n if (uniqueContainerIds.length === 0 && uniqueDocumentIds.length === 0) {\n return [];\n }\n\n const filters: SQL[] = [];\n if (uniqueContainerIds.length > 0) {\n filters.push(inArray(documentProjection.containerId, uniqueContainerIds));\n }\n\n if (uniqueDocumentIds.length > 0) {\n filters.push(inArray(documentProjection.documentId, uniqueDocumentIds));\n }\n\n const whereCondition = filters.length === 1 ? filters[0] : or(...filters);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select(documentSummarySelection)\n .from(documentProjection)\n .leftJoin(documents, documentSummaryJoin)\n .where(\n and(\n whereCondition,\n notInArray(documentProjection.documentKind, [\n ...HIDDEN_DOCUMENT_SUMMARY_KINDS,\n ]),\n ),\n )\n .orderBy(\n desc(documentProjection.updatedAt),\n desc(documentProjection.localId),\n );\n\n return rows.map(mapDocumentSummary);\n}\n\nexport const sqlDocumentsPersistence: DocumentsPersistence = {\n orphanBlobs: {\n acknowledge: acknowledgeDocumentOrphanBlobReclaim,\n isReferenced: isDocumentBlobStorageKeyReferenced,\n list: listDocumentOrphanBlobReclaims,\n sweep: deleteOrphanedDocumentSideRows,\n },\n ...documentRowQueryPersistence,\n ...documentSyncQueuePersistence,\n supportsAtomicRecoveryHistoryPruning: true,\n saveHydratedAttachment,\n createDocumentWithHistoryCheckpoint:\n createStoredDocumentWithHistoryCheckpoint,\n commitDocumentMutation: (execSql, input, saveClientProjection) =>\n commitStoredDocumentMutation(\n execSql,\n input,\n saveClientProjection,\n sqlDocumentsPersistence.loadDocument,\n ),\n settleAcceptedPendingUpdates: (execSql, input) =>\n settleStoredDocumentPendingUpdates(\n execSql,\n input,\n sqlDocumentsPersistence.loadDocument,\n ),\n ensureSchema: ensureDocumentsSchema,\n listDocumentSummaries,\n listDocumentsByContainerIdsOrDocumentIds,\n invalidatePullContinuation: (execSql, input) =>\n invalidateStoredDocumentPullContinuation(\n execSql,\n input,\n sqlDocumentsPersistence.loadDocument,\n sqlDocumentsPersistence.loadHistoryRestoreState,\n ),\n loadDocumentWithHistoryRestoreState: (execSql, localId) =>\n loadStoredDocumentWithHistoryRestoreState(\n execSql,\n localId,\n sqlDocumentsPersistence,\n ),\n loadDocumentStoreState: (execSql, localId) =>\n loadStoredDocumentStoreState(execSql, localId, sqlDocumentsPersistence),\n async saveDocument(execSql, document, options) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) =>\n getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const updatedAt = await resolveDocumentSaveTimestamp({\n document,\n options,\n tx,\n });\n await saveDocumentRows({\n document,\n tx,\n updatedAt,\n });\n\n return updatedAt;\n },\n { behavior: \"immediate\" },\n ),\n );\n },\n async saveDocumentAndDeletePendingUpdates(\n execSql,\n document,\n pendingUpdateIds,\n options,\n ) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) =>\n getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const updatedAt = await resolveDocumentSaveTimestamp({\n document,\n options,\n tx,\n });\n const saved = await saveDocumentRows({\n document,\n tx,\n updatedAt,\n });\n if (!saved) return updatedAt;\n await settleQueuedDocumentUpdatesInTransaction(\n tx,\n getDocumentScope(document.id),\n pendingUpdateIds,\n );\n return updatedAt;\n },\n { behavior: \"immediate\" },\n ),\n );\n },\n deleteDocument: deleteStoredDocument,\n deleteDocumentIfMatches: deleteStoredDocumentIfMatches,\n deleteDocumentSideRowsIfAbsent: deleteStoredDocumentSideRowsIfAbsent,\n async discardDocumentToShell(\n execSql,\n localId,\n expectedDocumentId,\n documentProjectors,\n ): Promise<DiscardDocumentToShellResult> {\n return discardStoredDocumentToShell({\n documentProjectors,\n execSql,\n expectedDocumentId,\n localId,\n persistence: sqlDocumentsPersistence,\n });\n },\n async upsertDiscoveredDocument(execSql, input) {\n const [nextSummary] = await upsertDiscoveredDocuments(execSql, [input]);\n if (!nextSummary) {\n throw new Error(\"Failed to upsert discovered document\");\n }\n\n return nextSummary;\n },\n async relinkPersistedDocument(execSql, input) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await sqlDocumentsPersistence.ensureSchema(lockedExecSql);\n return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n () => relinkPersistedDocumentWithExec(lockedExecSql, input),\n { behavior: \"immediate\" },\n );\n });\n },\n};\n"]}
1
+ {"version":3,"file":"documentsPersistence.js","sourceRoot":"","sources":["documentsPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, desc, eq, inArray, notInArray, or, type SQL } from \"drizzle-orm\";\nimport {\n type DiscoveredDocumentInput,\n type DocumentSummary,\n HIDDEN_DOCUMENT_SUMMARY_KINDS,\n} from \"../../documents/documentSummary\";\nimport { documentProjection, documents } from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, runSerializedSqlMutation } from \"../../sqlite/sqlSchema\";\nimport { applyContainerDocumentTombstonesWithExec } from \"./internal/containerDocumentTombstones\";\nimport { createStoredDocumentWithHistoryCheckpoint } from \"./internal/createDocumentWithHistoryCheckpoint\";\nimport { discardStoredDocumentToShell } from \"./internal/discardDocument\";\nimport { upsertDiscoveredDocumentsInTransaction } from \"./internal/discoveredDocumentUpsert\";\nimport {\n deleteStoredDocument,\n deleteStoredDocumentIfMatches,\n deleteStoredDocumentSideRowsIfAbsent,\n} from \"./internal/documentDeletionPersistence\";\nimport { loadStoredDocumentWithHistoryRestoreState } from \"./internal/documentHistoryStatePersistence\";\nimport {\n commitStoredDocumentMutation,\n settleStoredDocumentPendingUpdates,\n} from \"./internal/documentMutationPersistence\";\nimport {\n documentSummaryJoin,\n documentSummarySelection,\n getProjectionUpdatedAt,\n mapDocumentSummary,\n toDocumentSummary,\n} from \"./internal/documentProjectionRows\";\nimport { invalidateStoredDocumentPullContinuation } from \"./internal/documentPullContinuationPersistence\";\nimport {\n getDocumentScope,\n resolveDocumentSaveTimestamp,\n saveDocumentRows,\n} from \"./internal/documentRows\";\nimport {\n resolvePersistedAccessStateHash,\n resolvePersistedDocumentRuntimeState,\n} from \"./internal/documentRuntimeState\";\nimport { loadStoredDocumentStoreState } from \"./internal/documentStoreStatePersistence\";\nimport { listDocumentSummaries } from \"./internal/documentSummaryQueries\";\nimport { ensureDocumentsSchema } from \"./internal/ensureDocumentsSchema\";\nimport { saveHydratedAttachment } from \"./internal/hydratedAttachmentPersistence\";\nimport {\n acknowledgeDocumentOrphanBlobReclaim,\n deleteOrphanedDocumentSideRows,\n isDocumentBlobStorageKeyReferenced,\n listDocumentOrphanBlobReclaims,\n} from \"./internal/orphanSideRows\";\nimport { mapPendingCreateLocalIds } from \"./internal/pendingCreateAdoption\";\nimport { documentRowQueryPersistence } from \"./internal/rowQueryPersistence\";\nimport { documentSyncQueuePersistence } from \"./internal/syncQueuePersistence\";\nimport { settleQueuedDocumentUpdatesInTransaction } from \"./records/documentPendingUpdatePersistence\";\nimport type {\n ContainerDocumentTombstoneInput,\n DiscardDocumentToShellResult,\n DocumentsPersistence,\n RelinkPersistedDocumentInput,\n StoredDocumentRecord,\n} from \"./types\";\n\nexport { DOCUMENTS_APP_KIND } from \"../../documents/documentConstants\";\nexport type {\n AttachmentRemovalRows,\n AttachmentStagingRows,\n ContainerDocumentTombstoneInput,\n DiscardDocumentToShellResult,\n DocumentsPersistence,\n LocalAttachmentRecord,\n PendingAttachmentRecord,\n PendingAttachmentUploadIdentity,\n PendingUpdateInsert,\n PendingUpdateRecord,\n RelinkPersistedDocumentInput,\n StoredDocumentRecord,\n} from \"./types\";\n\nasync function relinkPersistedDocumentWithExec(\n execSql: ExecSql,\n input: RelinkPersistedDocumentInput,\n): Promise<DocumentSummary | null> {\n const existingDocument = await sqlDocumentsPersistence.loadDocument(\n execSql,\n input.localId,\n );\n if (!existingDocument) {\n return null;\n }\n\n const nextAccessEpoch = Math.max(\n existingDocument.accessEpoch,\n input.accessEpoch,\n );\n const nextDocument: StoredDocumentRecord = {\n ...existingDocument,\n accessEpoch: nextAccessEpoch,\n accessStateHash: resolvePersistedAccessStateHash(existingDocument, {\n accessEpoch: nextAccessEpoch,\n accessStateHash: input.accessStateHash,\n documentId: input.documentId,\n }),\n containerId: input.containerId,\n documentId: input.documentId,\n ...resolvePersistedDocumentRuntimeState(existingDocument, {\n accessEpoch: nextAccessEpoch,\n documentId: input.documentId,\n }),\n };\n\n await sqlDocumentsPersistence.saveDocument(execSql, nextDocument);\n\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const updatedAtRows = await db\n .select({ updatedAt: documentProjection.updatedAt })\n .from(documentProjection)\n .where(eq(documentProjection.localId, input.localId))\n .limit(1);\n\n return toDocumentSummary(\n nextDocument,\n getProjectionUpdatedAt(updatedAtRows[0]),\n );\n}\n\nexport async function upsertDiscoveredDocuments(\n execSql: ExecSql,\n inputs: ReadonlyArray<DiscoveredDocumentInput>,\n): Promise<DocumentSummary[]> {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await sqlDocumentsPersistence.ensureSchema(lockedExecSql);\n return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const pendingCreates = await mapPendingCreateLocalIds(lockedExecSql);\n return upsertDiscoveredDocumentsInTransaction(\n lockedExecSql,\n inputs,\n pendingCreates,\n tx,\n );\n },\n { behavior: \"immediate\" },\n );\n });\n}\n\nexport async function applyContainerDocumentTombstones(\n execSql: ExecSql,\n tombstones: ReadonlyArray<ContainerDocumentTombstoneInput>,\n): Promise<DocumentSummary[]> {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await sqlDocumentsPersistence.ensureSchema(lockedExecSql);\n return applyContainerDocumentTombstonesWithExec(lockedExecSql, tombstones);\n });\n}\n\nexport async function listDocumentsByContainerIdsOrDocumentIds(\n execSql: ExecSql,\n input: {\n containerIds: ReadonlyArray<string>;\n documentIds: ReadonlyArray<string>;\n },\n): Promise<DocumentSummary[]> {\n const uniqueContainerIds = Array.from(new Set(input.containerIds));\n const uniqueDocumentIds = Array.from(new Set(input.documentIds));\n if (uniqueContainerIds.length === 0 && uniqueDocumentIds.length === 0) {\n return [];\n }\n\n const filters: SQL[] = [];\n if (uniqueContainerIds.length > 0) {\n filters.push(inArray(documentProjection.containerId, uniqueContainerIds));\n }\n\n if (uniqueDocumentIds.length > 0) {\n filters.push(inArray(documentProjection.documentId, uniqueDocumentIds));\n }\n\n const whereCondition = filters.length === 1 ? filters[0] : or(...filters);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select(documentSummarySelection)\n .from(documentProjection)\n .leftJoin(documents, documentSummaryJoin)\n .where(\n and(\n whereCondition,\n notInArray(documentProjection.documentKind, [\n ...HIDDEN_DOCUMENT_SUMMARY_KINDS,\n ]),\n ),\n )\n .orderBy(\n desc(documentProjection.updatedAt),\n desc(documentProjection.localId),\n );\n\n return rows.map(mapDocumentSummary);\n}\n\nexport const sqlDocumentsPersistence: DocumentsPersistence = {\n orphanBlobs: {\n acknowledge: acknowledgeDocumentOrphanBlobReclaim,\n isReferenced: isDocumentBlobStorageKeyReferenced,\n list: listDocumentOrphanBlobReclaims,\n sweep: deleteOrphanedDocumentSideRows,\n },\n ...documentRowQueryPersistence,\n ...documentSyncQueuePersistence,\n supportsAtomicRecoveryHistoryPruning: true,\n saveHydratedAttachment,\n createDocumentWithHistoryCheckpoint:\n createStoredDocumentWithHistoryCheckpoint,\n commitDocumentMutation: (execSql, input, saveClientProjection) =>\n commitStoredDocumentMutation(\n execSql,\n input,\n saveClientProjection,\n sqlDocumentsPersistence.loadDocument,\n ),\n settleAcceptedPendingUpdates: (execSql, input) =>\n settleStoredDocumentPendingUpdates(\n execSql,\n input,\n sqlDocumentsPersistence.loadDocument,\n ),\n ensureSchema: ensureDocumentsSchema,\n listDocumentSummaries,\n listDocumentsByContainerIdsOrDocumentIds,\n invalidatePullContinuation: (execSql, input) =>\n invalidateStoredDocumentPullContinuation(\n execSql,\n input,\n sqlDocumentsPersistence.loadDocument,\n sqlDocumentsPersistence.loadHistoryRestoreState,\n ),\n loadDocumentWithHistoryRestoreState: (execSql, localId) =>\n loadStoredDocumentWithHistoryRestoreState(\n execSql,\n localId,\n sqlDocumentsPersistence,\n ),\n loadDocumentStoreState: (execSql, localId) =>\n loadStoredDocumentStoreState(execSql, localId, sqlDocumentsPersistence),\n async saveDocument(execSql, document, options) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) =>\n getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const updatedAt = await resolveDocumentSaveTimestamp({\n document,\n options,\n tx,\n });\n await saveDocumentRows({\n document,\n tx,\n updatedAt,\n });\n\n return updatedAt;\n },\n { behavior: \"immediate\" },\n ),\n );\n },\n async saveDocumentAndDeletePendingUpdates(\n execSql,\n document,\n pendingUpdateIds,\n options,\n ) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) =>\n getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const updatedAt = await resolveDocumentSaveTimestamp({\n document,\n options,\n tx,\n });\n const saved = await saveDocumentRows({\n document,\n tx,\n updatedAt,\n });\n if (!saved) return updatedAt;\n await settleQueuedDocumentUpdatesInTransaction(\n tx,\n getDocumentScope(document.id),\n pendingUpdateIds,\n );\n return updatedAt;\n },\n { behavior: \"immediate\" },\n ),\n );\n },\n deleteDocument: deleteStoredDocument,\n deleteDocumentIfMatches: deleteStoredDocumentIfMatches,\n deleteDocumentSideRowsIfAbsent: deleteStoredDocumentSideRowsIfAbsent,\n async discardDocumentToShell(\n execSql,\n localId,\n expectedDocumentId,\n documentProjectors,\n ): Promise<DiscardDocumentToShellResult> {\n return discardStoredDocumentToShell({\n documentProjectors,\n execSql,\n expectedDocumentId,\n localId,\n persistence: sqlDocumentsPersistence,\n });\n },\n async upsertDiscoveredDocument(execSql, input) {\n const [nextSummary] = await upsertDiscoveredDocuments(execSql, [input]);\n if (!nextSummary) {\n throw new Error(\"Failed to upsert discovered document\");\n }\n\n return nextSummary;\n },\n async relinkPersistedDocument(execSql, input) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await sqlDocumentsPersistence.ensureSchema(lockedExecSql);\n return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n () => relinkPersistedDocumentWithExec(lockedExecSql, input),\n { behavior: \"immediate\" },\n );\n });\n },\n};\n"]}