@tearleads/client-sdk 0.1.114 → 0.1.115
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/persistence/container-contents/containerRecoveryPersistence.js +152 -38
- package/data/persistence/container-contents/containerRecoveryPersistence.js.map +1 -1
- package/data/persistence/container-contents/documentContainerProjectionPersistence.js +47 -18
- package/data/persistence/container-contents/documentContainerProjectionPersistence.js.map +1 -1
- package/data/persistence/container-contents/documentPlacement.js +24 -5
- package/data/persistence/container-contents/documentPlacement.js.map +1 -1
- package/data/persistence/documents/containerDocumentTombstoneHoldsPersistence.js +1 -1
- package/data/persistence/documents/containerDocumentTombstoneHoldsPersistence.js.map +1 -1
- package/data/persistence/documents/documentDiscoveryEvidencePersistence.js +43 -35
- package/data/persistence/documents/documentDiscoveryEvidencePersistence.js.map +1 -1
- package/data/persistence/documents/documentsPersistence.js +4 -58
- package/data/persistence/documents/documentsPersistence.js.map +1 -1
- package/data/persistence/documents/internal/containerDocumentTombstoneHolds.js +15 -11
- package/data/persistence/documents/internal/containerDocumentTombstoneHolds.js.map +1 -1
- package/data/persistence/documents/internal/containerDocumentTombstones.js +87 -35
- package/data/persistence/documents/internal/containerDocumentTombstones.js.map +1 -1
- package/data/persistence/documents/internal/discoveredDocumentPlacement.js +5 -2
- package/data/persistence/documents/internal/discoveredDocumentPlacement.js.map +1 -1
- package/data/persistence/documents/internal/discoveredDocumentUpsert.js +174 -0
- package/data/persistence/documents/internal/discoveredDocumentUpsert.js.map +1 -0
- package/data/persistence/documents/internal/documentRows.js +96 -1
- package/data/persistence/documents/internal/documentRows.js.map +1 -1
- package/data/persistence/documents/internal/rowQueryPersistence.js +38 -20
- package/data/persistence/documents/internal/rowQueryPersistence.js.map +1 -1
- package/data/persistence/documents/records/documentRecordPersistence.d.ts +5 -0
- package/data/persistence/documents/records/documentRecordPersistence.js +41 -6
- package/data/persistence/documents/records/documentRecordPersistence.js.map +1 -1
- package/data/persistence/documents/records/index.d.ts +1 -1
- package/data/persistence/documents/records/index.js +1 -1
- package/data/persistence/documents/records/index.js.map +1 -1
- package/data/persistence/keyingCheckpoints/documentPurgeCheckpointPersistence.d.ts +2 -0
- package/data/persistence/keyingCheckpoints/documentPurgeCheckpointPersistence.js +17 -1
- package/data/persistence/keyingCheckpoints/documentPurgeCheckpointPersistence.js.map +1 -1
- package/package.json +1 -1
- package/workflows/container-contents/remoteReset/containerRemoteReset.js +12 -7
- package/workflows/container-contents/remoteReset/containerRemoteReset.js.map +1 -1
- package/workflows/documents/remoteReset/documentRemoteReset.js +31 -18
- package/workflows/documents/remoteReset/documentRemoteReset.js.map +1 -1
|
@@ -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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
|
102
|
+
for (const batch of remoteResetBatches(removed)) {
|
|
98
103
|
await tx
|
|
99
104
|
.delete(pending)
|
|
100
|
-
.where(and(
|
|
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
|
|
148
|
-
await tx
|
|
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":"AAAA,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,iCAAiC,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,qDAAqD,CAAC;AAC7D,OAAO,EACL,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,iCAAiC,EAAE,MAAM,uCAAuC,CAAC;AAC1F,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAEL,eAAe,EACf,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,2BAA2B,EAAE,MAAM,yDAAyD,CAAC;AACtG,OAAO,EAAE,4BAA4B,EAAE,MAAM,kDAAkD,CAAC;AAChG,OAAO,EAAE,yBAAyB,EAAE,MAAM,8CAA8C,CAAC;AACzF,OAAO,EAEL,6BAA6B,EAC7B,4BAA4B,GAC7B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,oCAAoC,EAAE,MAAM,0BAA0B,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AAGjF,OAAO,EAAE,2CAA2C,EAAE,MAAM,0BAA0B,CAAC;AAyCvF,MAAM,OAAO,GAAG,0BAA0B,CAAC;AAC3C,MAAM,KAAK,GAAG,sBAAsB,CAAC;AACrC,MAAM,aAAa,GAAG,GAAG,CAAC;AAC1B,MAAM,OAAO,GAAG,CAAC,GAAsB,EAAE,EAAE,CACzC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,aAAa,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACrE,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,aAAa,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,aAAa,CAAC,CACtD,CAAC;AACJ,MAAM,OAAO,GAAG,CAAI,IAAkB,EAAE,IAAY,EAAS,EAAE,CAC7D,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAC7D,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CACrC,CAAC;AACJ,MAAM,QAAQ,GAAG,CAAC,KAAkC,EAAE,EAAE,CACtD,GAAG,CACD,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,WAAW,CAAC,EAC1C,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,EACxC,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAC7C,CAAC;AAEJ,MAAM,iCAAiC;IAIlB;IACA;IAFnB,YACmB,OAAgB,EAChB,GAAiB;QADjB,YAAO,GAAP,OAAO,CAAS;QAChB,QAAG,GAAH,GAAG,CAAc;IACjC,CAAC;IAEI,IAAI,GAAG,KAAK,IAAI,EAAE;QACxB,MAAM,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,iCAAiC,CAAC,CAAC;QACvE,OAAO,iCAAiC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;IAC5D,CAAC,CAAC;IACM,KAAK,GAAG,KAAK,EACnB,SAEe,EACf,EAAE,CACF,wBAAwB,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QACtD,MAAM,eAAe,CAAC,MAAM,EAAE,iCAAiC,CAAC,CAAC;QACjE,OAAO,SAAS,CAAC,iCAAiC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IACL,KAAK,GAAG,KAAK,IAAI,EAAE;QACjB,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;YAC5B,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE;iBACnB,MAAM,CAAC,yBAAyB,CAAC;iBACjC,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;iBACzC,kBAAkB,CAAC;gBAClB,MAAM,EAAE,yBAAyB,CAAC,EAAE;gBACpC,GAAG,EAAE;oBACH,UAAU,EAAE,GAAG,CAAA,GAAG,yBAAyB,CAAC,UAAU,MAAM;iBAC7D;aACF,CAAC;iBACD,SAAS,CAAC,EAAE,UAAU,EAAE,yBAAyB,CAAC,UAAU,EAAE,CAAC,CAAC;YACnE,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC;gBAC/C,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;YAChE,UAAU,GAAG,GAAG,CAAC,UAAU,CAAC;QAC9B,CAAC,CAAC,CAAC;QACH,OAAO,UAAU,CAAC;IACpB,CAAC,CAAC;IACF,SAAS,GAAG,KAAK,EAAE,UAAkB,EAAE,EAAE,CACvC,oCAAoC,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,UAAU,CAAC,CAAC;IACtE,KAAK,GAAG,CACN,MAA8C,EAC9C,UAAkB,EAClB,UAAgD,EAAE,EAClD,EAAE;QACF,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;YAC7B,IAAI,CAAC,CAAC,MAAM,oCAAoC,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;gBAC/D,OAAO,KAAK,CAAC;YACf,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC7D,MAAM,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;gBAChC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CACpC,6BAA6B,CAAC,KAAK,CAAC;oBAClC,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;wBAC7C,WAAW;wBACX,UAAU,EAAE,KAAK,CAAC,UAAU;wBAC5B,WAAW,EAAE,KAAK,CAAC,WAAW;wBAC9B,UAAU;wBACV,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC;4BACxB,GAAG,KAAK;4BACR,WAAW;4BACX,kBAAkB,EAAE,CAAC,WAAW,CAAC;yBAClC,CAAC;wBACF,OAAO,EAAE,CAAC;qBACX,CAAC,CAAC;oBACL,CAAC,CAAC,EAAE,CACP,CAAC;gBACF,oEAAoE;gBACpE,iEAAiE;gBACjE,uDAAuD;gBACvD,MAAM,SAAS,GAAG,GAAG,CAAA,GAAG,OAAO,CAAC,SAAS,wBAAwB,CAAC;gBAClE,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,IAAI,EAAE,qBAAqB,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;oBAClE,MAAM,EAAE;yBACL,MAAM,CAAC,OAAO,CAAC;yBACf,MAAM,CAAC,KAAK,CAAC;yBACb,kBAAkB,CAAC;wBAClB,MAAM,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,UAAU,CAAC;wBACjD,GAAG,EAAE;4BACH,WAAW,EAAE,aAAa,CAAC,OAAO,CAAC,WAAW,CAAC;4BAC/C,UAAU,EAAE,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC;4BAC7C,SAAS,EAAE,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC;4BAC3C,OAAO,EAAE,GAAG,CAAA,aAAa,SAAS,SAAS,OAAO,CAAC,OAAO,aAAa;4BACvE,QAAQ,EAAE,GAAG,CAAA,aAAa,SAAS,SAAS,OAAO,CAAC,QAAQ,aAAa;yBAC1E;wBACD,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,UAAU,CAAC;qBAC9C,CAAC;yBACD,GAAG,EAAE,CAAC;gBACX,CAAC;gBACD,wEAAwE;gBACxE,wEAAwE;gBACxE,KAAK,MAAM,KAAK,IAAI,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;oBAChD,MAAM,EAAE;yBACL,MAAM,CAAC,OAAO,CAAC;yBACf,KAAK,CACJ,GAAG,CACD,mBAAmB,CACjB,KAAK,EACL,OAAO,CAAC,UAAU,EAClB,OAAO,CAAC,WAAW,CACpB,EACD,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,UAAU,CAAC,CACpC,CACF;yBACA,GAAG,EAAE,CAAC;gBACX,CAAC;YACH,CAAC,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IACF,OAAO,GAAG,KAAK,EAAE,YAA+B,EAAE,KAAa,EAAE,EAAE;QACjE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAkC,EAAE,CAAC;QACjD,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YAC1C,IAAI,MAAM,CAAC,MAAM,IAAI,KAAK;gBAAE,MAAM;YAClC,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC;iBACxC,IAAI,CAAC,OAAO,CAAC;iBACb,KAAK,CACJ,GAAG,CACD,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,EACxC,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CACjC,CACF;iBACA,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;iBACtD,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;YAChC,MAAM,CAAC,IAAI,CACT,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,4BAA4B,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAClE,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IACF,KAAK,GAAG,CAAC,KAAkC,EAAE,UAAkB,EAAE,EAAE,CACjE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QACtB,IAAI,CAAC,CAAC,MAAM,oCAAoC,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;YAAE,OAAO;QAC1E,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE;aACnB,MAAM,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;aACtC,IAAI,CAAC,OAAO,CAAC;aACb,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;aACtB,KAAK,CAAC,CAAC,CAAC,CAAC;QACZ,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAC/C,MAAM,EAAE;aACL,MAAM,CAAC,OAAO,CAAC;aACf,GAAG,CAAC;YACH,QAAQ;YACR,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,yBAAyB,CAAC,QAAQ,CAAC;SAC1D,CAAC;aACD,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;aACtB,GAAG,EAAE,CAAC;IACX,CAAC,CAAC,CAAC;IACL,WAAW,GAAG,CACZ,MAA8C,EAC9C,UAAkB,EAClB,EAAE,CACF,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QACtB,IAAI,CAAC,CAAC,MAAM,oCAAoC,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;YAAE,OAAO;QAC1E,MAAM,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;YAChC,KAAK,MAAM,KAAK,IAAI,kBAAkB,CAAC,MAAM,CAAC;gBAC5C,MAAM,EAAE;qBACL,MAAM,CAAC,OAAO,CAAC;qBACf,KAAK,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;qBACjC,GAAG,EAAE,CAAC;QACb,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACL,UAAU,GAAG,KAAK,EAAE,YAA+B,EAAE,EAAE;QACrD,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC7B,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YAC1C,IACE,CACE,MAAM,EAAE;iBACL,MAAM,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;iBAC1C,IAAI,CAAC,OAAO,CAAC;iBACb,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;iBAC/C,KAAK,CAAC,CAAC,CAAC,CACZ,CAAC,MAAM;gBAER,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IACF,UAAU,GAAG,KAAK,EAAE,YAA+B,EAAE,EAAE;QACrD,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,GAAG,GAAkB,IAAI,CAAC;QAC9B,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YAC1C,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE;iBACnB,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;iBACrC,IAAI,CAAC,OAAO,CAAC;iBACb,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACnD,IAAI,GAAG,EAAE,GAAG,KAAK,IAAI,IAAI,GAAG,EAAE,GAAG,KAAK,SAAS;gBAC7C,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC7D,CAAC,CAAC;IACF,QAAQ,GAAG,KAAK,EACd,UAAkB,EAClB,YAAuC,EACvC,EAAE;QACF,IAAI,MAAM,2BAA2B,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC;YAC7D,OAAO;gBACL,cAAc,EAAE,IAAI;gBACpB,WAAW,EAAE,MAAM,CAAC,gBAAgB;gBACpC,kBAAkB,EAAE,EAAE;aACvB,CAAC;QACJ,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,CAAC;QAC/B,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE;aACnB,MAAM,EAAE;aACR,IAAI,CAAC,KAAK,CAAC;aACX,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,UAAU,CAAC,EAChC,EAAE,CAAC,KAAK,CAAC,YAAY,EAAE,YAAY,CAAC,CACrC,CACF;aACA,KAAK,CAAC,CAAC,CAAC,CAAC;QACZ,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,uEAAuE;QACvE,0EAA0E;QAC1E,wEAAwE;QACxE,4EAA4E;QAC5E,MAAM,UAAU,GAAG,MAAM,4BAA4B,CACnD,IAAI,CAAC,OAAO,EACZ,UAAU,EACV,GAAG,CAAC,cAAc,EAClB,UAAU,CACX,CAAC;QACF,IACE,UAAU,EAAE,YAAY,KAAK,GAAG,CAAC,YAAY;YAC7C,UAAU,CAAC,KAAK,KAAK,GAAG,CAAC,WAAW;YAEpC,OAAO,IAAI,CAAC;QACd,OAAO;YACL,cAAc,EAAE,GAAG,CAAC,cAAc;YAClC,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,eAAe,EAAE,GAAG,CAAC,YAAY;YACjC,kBAAkB,EAAE,eAAe,CACjC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,EACzB,wBAAwB,CACzB;SACF,CAAC;IACJ,CAAC,CAAC;IACF,QAAQ,GAAG,CACT,UAAkB,EAClB,IAAyB,EACzB,UAAkB,EAClB,EAAE,CACF,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QACtB,IACE,CAAC,IAAI,CAAC,eAAe;YACrB,CAAC,IAAI,CAAC,cAAc;YACpB,CAAC,CAAC,MAAM,oCAAoC,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;YAE7D,OAAO,KAAK,CAAC;QACf,MAAM,GAAG,GAAG;YACV,UAAU;YACV,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,YAAY,EAAE,IAAI,CAAC,eAAe;YAClC,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,kBAAkB,CAAC;SACnD,CAAC;QACF,MAAM,EAAE;aACL,MAAM,CAAC,KAAK,CAAC;aACb,MAAM,CAAC,GAAG,CAAC;aACX,kBAAkB,CAAC;YAClB,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,cAAc,CAAC;YAChD,GAAG,EAAE,GAAG;YACR,QAAQ,EAAE,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC;SACnD,CAAC;aACD,GAAG,EAAE,CAAC;QACT,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;CACN;AAiBD,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,mCAAmC,CACvD,OAAgB,EAChB,QAA0C;IAE1C,MAAM,EAAE,GAAG,iCAAiC,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;IACzD,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG;YACV,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,YAAY,EAAE,IAAI,CAAC,eAAe;YAClC,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;SACxE,CAAC;QACF,MAAM,EAAE;aACL,MAAM,CAAC,KAAK,CAAC;aACb,MAAM,CAAC,GAAG,CAAC;aACX,kBAAkB,CAAC;YAClB,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,cAAc,CAAC;YAChD,GAAG,EAAE,GAAG;YACR,QAAQ,EAAE,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC;SACnD,CAAC;aACD,GAAG,EAAE,CAAC;IACX,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oCAAoC,CAClD,OAAgB,EAChB,GAAG,GAAG,IAAI,CAAC,GAAG;IAEd,OAAO,IAAI,iCAAiC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AAC7D,CAAC","sourcesContent":["import { and, asc, eq, inArray, lte, min, 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
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
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"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { and, asc, eq, inArray, sql } from "drizzle-orm";
|
|
1
|
+
import { and, asc, eq, inArray, or, sql } from "drizzle-orm";
|
|
2
2
|
import { containerDocumentTombstoneHolds, documentContainerProjection, documentProjection, } from "../../../sqlite/schema/index.js";
|
|
3
|
+
import { remoteResetBatches } from "../../../sqlite/sqliteBindBudget.js";
|
|
3
4
|
import { filterWritableDocumentPlacements } from "../../container-contents/documentPlacement.js";
|
|
4
5
|
/** Keep `IN (...)` lists well under SQLite's bound-parameter limit. */
|
|
5
6
|
const HOLD_SQL_ID_BATCH_SIZE = 400;
|
|
@@ -15,11 +16,18 @@ export function containerDocumentPlacementKey(placement) {
|
|
|
15
16
|
return `${placement.documentId}\u0000${placement.containerId}`;
|
|
16
17
|
}
|
|
17
18
|
const placementKey = containerDocumentPlacementKey;
|
|
19
|
+
/** Matches the listed (document, container) placements by primary key. */
|
|
20
|
+
export function placementPairsMatch(placements, documentIdColumn, containerIdColumn) {
|
|
21
|
+
return or(...placements.map((placement) => and(eq(documentIdColumn, placement.documentId), eq(containerIdColumn, placement.containerId))));
|
|
22
|
+
}
|
|
23
|
+
function uniquePlacements(placements) {
|
|
24
|
+
return Array.from(new Map(placements.map((placement) => [placementKey(placement), placement])).values());
|
|
25
|
+
}
|
|
18
26
|
export async function deleteContainerDocumentTombstoneHoldRows(tx, placements) {
|
|
19
|
-
for (const
|
|
27
|
+
for (const batch of remoteResetBatches(uniquePlacements(placements))) {
|
|
20
28
|
await tx
|
|
21
29
|
.delete(containerDocumentTombstoneHolds)
|
|
22
|
-
.where(
|
|
30
|
+
.where(placementPairsMatch(batch, containerDocumentTombstoneHolds.documentId, containerDocumentTombstoneHolds.containerId))
|
|
23
31
|
.run();
|
|
24
32
|
}
|
|
25
33
|
}
|
|
@@ -126,14 +134,10 @@ export async function holdContainerDocumentTombstonesInTransaction(tx, tombstone
|
|
|
126
134
|
}
|
|
127
135
|
/** Release the holds on placements a listing has re-asserted as linked. */
|
|
128
136
|
export async function deleteContainerDocumentTombstoneHoldRowsForLinks(tx, links) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
.where(and(eq(containerDocumentTombstoneHolds.documentId, link.documentId), inArray(containerDocumentTombstoneHolds.containerId, batch)))
|
|
134
|
-
.run();
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
+
await deleteContainerDocumentTombstoneHoldRows(tx, links.flatMap((link) => link.containerIds.map((containerId) => ({
|
|
138
|
+
containerId,
|
|
139
|
+
documentId: link.documentId,
|
|
140
|
+
}))));
|
|
137
141
|
}
|
|
138
142
|
const holdRowSelection = {
|
|
139
143
|
attempts: containerDocumentTombstoneHolds.attempts,
|