@tearleads/client-sdk 0.1.40 → 0.1.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/client/Tearleads.js +3 -0
  2. package/client/Tearleads.js.map +1 -1
  3. package/data/documents/shared/syncResponses.js +5 -7
  4. package/data/documents/shared/syncResponses.js.map +1 -1
  5. package/data/keyingProjectionVerification/accessManifestCheckpointEnforcement.js +2 -0
  6. package/data/keyingProjectionVerification/accessManifestCheckpointEnforcement.js.map +1 -1
  7. package/data/keyingProjectionVerification/checkpointContext.d.ts +12 -0
  8. package/data/keyingProjectionVerification/checkpointContext.js +16 -0
  9. package/data/keyingProjectionVerification/checkpointContext.js.map +1 -1
  10. package/data/keyingProjectionVerification/containerDestinationVerification.js +3 -1
  11. package/data/keyingProjectionVerification/containerDestinationVerification.js.map +1 -1
  12. package/data/keyingProjectionVerification/containerProjectionVerification.js +12 -6
  13. package/data/keyingProjectionVerification/containerProjectionVerification.js.map +1 -1
  14. package/data/keyingProjectionVerification/documentProjectionVerification.js +10 -4
  15. package/data/keyingProjectionVerification/documentProjectionVerification.js.map +1 -1
  16. package/data/persistence/keyingCheckpointAdvancePersistence.js +27 -8
  17. package/data/persistence/keyingCheckpointAdvancePersistence.js.map +1 -1
  18. package/data/persistence/verifiedManifestBundlePersistence.d.ts +45 -0
  19. package/data/persistence/verifiedManifestBundlePersistence.js +282 -0
  20. package/data/persistence/verifiedManifestBundlePersistence.js.map +1 -0
  21. package/data/sqlite/schema.d.ts +157 -0
  22. package/data/sqlite/schema.js +3 -0
  23. package/data/sqlite/schema.js.map +1 -1
  24. package/data/sqlite/verifiedManifestBundleSchema.d.ts +174 -0
  25. package/data/sqlite/verifiedManifestBundleSchema.js +36 -0
  26. package/data/sqlite/verifiedManifestBundleSchema.js.map +1 -0
  27. package/internal/api-client/ApiClient.d.ts +4 -11
  28. package/internal/api-client/ApiClient.js +8 -6
  29. package/internal/api-client/ApiClient.js.map +1 -1
  30. package/internal/api-client/index.d.ts +1 -1
  31. package/internal/api-client/index.js +0 -1
  32. package/internal/api-client/index.js.map +1 -1
  33. package/internal/api-client/principalHistoryRequest.js +8 -2
  34. package/internal/api-client/principalHistoryRequest.js.map +1 -1
  35. package/internal/api-client/principalHistoryRequestContext.js +15 -5
  36. package/internal/api-client/principalHistoryRequestContext.js.map +1 -1
  37. package/internal/api-client/routes/writerProjections.js +6 -3
  38. package/internal/api-client/routes/writerProjections.js.map +1 -1
  39. package/internal/api-client/verifiedManifestCitations.d.ts +23 -0
  40. package/internal/api-client/verifiedManifestCitations.js +60 -0
  41. package/internal/api-client/verifiedManifestCitations.js.map +1 -0
  42. package/internal/api-client/writerProjectionRequests.js +75 -14
  43. package/internal/api-client/writerProjectionRequests.js.map +1 -1
  44. package/internal/crypto/index.d.ts +0 -1
  45. package/internal/crypto/index.js +0 -1
  46. package/internal/crypto/index.js.map +1 -1
  47. package/internal/validators/documentSyncRefinements.js +5 -0
  48. package/internal/validators/documentSyncRefinements.js.map +1 -1
  49. package/internal/validators/knownManifestsRefinements.js +5 -0
  50. package/internal/validators/knownManifestsRefinements.js.map +1 -0
  51. package/internal/validators/operation/documentSync.d.ts +3 -0
  52. package/internal/validators/operation/writerProjections.d.ts +85 -319
  53. package/internal/validators/operation/writerProjections.js +11 -11
  54. package/internal/validators/operation/writerProjections.js.map +1 -1
  55. package/internal/validators/response/container.d.ts +429 -20
  56. package/internal/validators/response/container.js +40 -5
  57. package/internal/validators/response/container.js.map +1 -1
  58. package/internal/validators/response/documentMutation.d.ts +440 -20
  59. package/internal/validators/response/documentMutation.js +33 -3
  60. package/internal/validators/response/documentMutation.js.map +1 -1
  61. package/internal/validators/response/documentSyncSchema.js +35 -1
  62. package/internal/validators/response/documentSyncSchema.js.map +1 -1
  63. package/internal/validators/response/index.d.ts +3 -2
  64. package/internal/validators/response/index.js +3 -2
  65. package/internal/validators/response/index.js.map +1 -1
  66. package/internal/validators/response/manifestBundleReferences.js +79 -0
  67. package/internal/validators/response/manifestBundleReferences.js.map +1 -0
  68. package/internal/validators/response/writerProjectionWire.d.ts +19 -0
  69. package/internal/validators/response/writerProjectionWire.js +132 -0
  70. package/internal/validators/response/writerProjectionWire.js.map +1 -0
  71. package/internal/validators/schema.js +20 -0
  72. package/internal/validators/schema.js.map +1 -1
  73. package/internal/validators/util/index.d.ts +1 -1
  74. package/internal/validators/util/index.js +1 -1
  75. package/internal/validators/util/index.js.map +1 -1
  76. package/internal/validators/util/knownManifestWire.d.ts +11 -0
  77. package/internal/validators/util/knownManifestWire.js +28 -0
  78. package/internal/validators/util/knownManifestWire.js.map +1 -0
  79. package/internal/validators/writerProjectionRefinements.js +5 -0
  80. package/internal/validators/writerProjectionRefinements.js.map +1 -1
  81. package/package.json +1 -1
  82. package/workflows/documents/syncContentKeys.js +4 -9
  83. package/workflows/documents/syncContentKeys.js.map +1 -1
  84. package/workflows/registration/provisioningBootstrapBuilders.d.ts +0 -5
  85. package/workflows/sync/remoteReset.js +2 -0
  86. package/workflows/sync/remoteReset.js.map +1 -1
  87. package/internal/api-client/projectionHistoryTransport.js +0 -107
  88. package/internal/api-client/projectionHistoryTransport.js.map +0 -1
  89. package/internal/api-client/verifiedProjectionHistory.d.ts +0 -4
  90. package/internal/api-client/verifiedProjectionHistory.js +0 -12
  91. package/internal/api-client/verifiedProjectionHistory.js.map +0 -1
  92. package/internal/crypto/projectionHistory.d.ts +0 -14
  93. package/internal/crypto/projectionHistory.js +0 -90
  94. package/internal/crypto/projectionHistory.js.map +0 -1
  95. package/internal/crypto/projectionHistorySlots.d.ts +0 -13
  96. package/internal/crypto/projectionHistorySlots.js +0 -81
  97. package/internal/crypto/projectionHistorySlots.js.map +0 -1
  98. package/internal/validators/projectionHistoryRefinements.js +0 -5
  99. package/internal/validators/projectionHistoryRefinements.js.map +0 -1
  100. package/internal/validators/util/projectionHistoryWire.d.ts +0 -23
  101. package/internal/validators/util/projectionHistoryWire.js +0 -33
  102. package/internal/validators/util/projectionHistoryWire.js.map +0 -1
@@ -0,0 +1,282 @@
1
+ import { toFingerprint } from "../../internal/crypto/index.js";
2
+ import { isAccessManifestBundleWireResponse, } from "../../internal/validators/response/index.js";
3
+ import { KNOWN_MANIFEST_HINT_LIMIT, MAX_CONTAINER_PATH_LENGTH, } from "../../internal/validators/util/index.js";
4
+ import { and, asc, desc, eq, inArray, sql } from "drizzle-orm";
5
+ import { containers } from "../sqlite/containerSchema.js";
6
+ import { documentDiscoveryHeads, pendingDocumentDiscoveries, } from "../sqlite/documentDiscoveryEvidenceSchema.js";
7
+ import { containerTables, documentContainerProjection, documentContainerProjectionTables, } from "../sqlite/schema.js";
8
+ import { getClientSQLitePersistenceRuntime, } from "../sqlite/sqlitePersistenceRuntime.js";
9
+ import { ensureSqlTables } from "../sqlite/sqlSchema.js";
10
+ import { verifiedAccessManifestBundles as bundles, verifiedManifestBundleTables, } from "../sqlite/verifiedManifestBundleSchema.js";
11
+ /**
12
+ * Bounds local bytes (a bundle is ~10–20 KB). Evicting the least recently
13
+ * verified rows only makes a later read carry those bundles again.
14
+ */
15
+ export const MAX_VERIFIED_MANIFEST_BUNDLES = 1024;
16
+ const STATEMENT_BATCH = 200;
17
+ function sha256Hex(text) {
18
+ return toFingerprint(new TextEncoder().encode(text));
19
+ }
20
+ function batches(values) {
21
+ const result = [];
22
+ for (let index = 0; index < values.length; index += STATEMENT_BATCH)
23
+ result.push(values.slice(index, index + STATEMENT_BATCH));
24
+ return result;
25
+ }
26
+ async function pruneInTransaction(tx) {
27
+ const [row] = await tx.select({ count: sql `count(*)` }).from(bundles);
28
+ const excess = (row?.count ?? 0) - MAX_VERIFIED_MANIFEST_BUNDLES;
29
+ if (excess <= 0)
30
+ return;
31
+ const evicted = await tx
32
+ .select({ manifestHash: bundles.manifestHash })
33
+ .from(bundles)
34
+ .orderBy(asc(bundles.verifiedAt), asc(bundles.manifestHash))
35
+ .limit(excess);
36
+ for (const batch of batches(evicted.map((entry) => entry.manifestHash)))
37
+ await tx.delete(bundles).where(inArray(bundles.manifestHash, batch)).run();
38
+ }
39
+ /**
40
+ * Prepares rows for bundles that just passed projection verification. The
41
+ * digests are computed here, before the checkpoint transaction that writes
42
+ * them begins.
43
+ */
44
+ export function verifiedManifestBundleRows(entries) {
45
+ const unique = new Map(entries.map((entry) => [entry.bundle.manifestHash, entry]));
46
+ return Promise.all([...unique.values()].map(async ({ bundle, checkpoint }) => {
47
+ const bundleJson = JSON.stringify(bundle);
48
+ return {
49
+ manifestHash: bundle.manifestHash,
50
+ organizationId: checkpoint.organizationId,
51
+ objectKind: checkpoint.objectKind,
52
+ objectId: checkpoint.objectId,
53
+ epoch: checkpoint.epoch,
54
+ bundleJson,
55
+ bundleDigest: await sha256Hex(bundleJson),
56
+ };
57
+ }));
58
+ }
59
+ /**
60
+ * Records verified bundles inside the transaction that finalizes their
61
+ * projection's checkpoints, so they commit or roll back with it and no cache
62
+ * work outlives the verification. Freshly verified bytes replace any stored
63
+ * copy.
64
+ */
65
+ export async function writeVerifiedManifestBundlesInTransaction(tx, rows, verifiedAt) {
66
+ if (rows.length === 0)
67
+ return;
68
+ const stored = await storedDigests(tx, rows.map((row) => row.manifestHash));
69
+ // An identical stored row only needs its recency refreshed.
70
+ const unchanged = rows.filter((row) => stored.get(row.manifestHash) === row.bundleDigest);
71
+ for (const batch of batches(unchanged.map((row) => row.manifestHash)))
72
+ await tx
73
+ .update(bundles)
74
+ .set({ verifiedAt })
75
+ .where(inArray(bundles.manifestHash, batch))
76
+ .run();
77
+ for (const row of rows) {
78
+ if (stored.get(row.manifestHash) === row.bundleDigest)
79
+ continue;
80
+ const verified = { ...row, verifiedAt };
81
+ await tx
82
+ .insert(bundles)
83
+ .values(verified)
84
+ .onConflictDoUpdate({ target: bundles.manifestHash, set: verified })
85
+ .run();
86
+ }
87
+ await pruneInTransaction(tx);
88
+ }
89
+ async function storedDigests(tx, manifestHashes) {
90
+ const digests = new Map();
91
+ for (const batch of batches(manifestHashes)) {
92
+ const rows = await tx
93
+ .select({
94
+ bundleDigest: bundles.bundleDigest,
95
+ manifestHash: bundles.manifestHash,
96
+ })
97
+ .from(bundles)
98
+ .where(inArray(bundles.manifestHash, batch));
99
+ for (const row of rows)
100
+ digests.set(row.manifestHash, row.bundleDigest);
101
+ }
102
+ return digests;
103
+ }
104
+ /** The requested object first, then each container whose lineage it cites. */
105
+ async function containerLineage(db, containerId) {
106
+ const lineage = [containerId];
107
+ while (lineage.length < MAX_CONTAINER_PATH_LENGTH) {
108
+ const [row] = await db
109
+ .select({ parentId: containers.parentId })
110
+ .from(containers)
111
+ .where(eq(containers.id, lineage.at(-1) ?? ""))
112
+ .limit(1);
113
+ const parentId = row?.parentId;
114
+ if (!parentId || lineage.includes(parentId))
115
+ break;
116
+ lineage.push(parentId);
117
+ }
118
+ return lineage;
119
+ }
120
+ async function citedObjects(db, target) {
121
+ if (target.kind === "container")
122
+ return (await containerLineage(db, target.id)).map((id) => ({
123
+ kind: "container",
124
+ id,
125
+ }));
126
+ // Linked containers, containers listing the document before its signed
127
+ // placement is checked, and a container whose metadata document it is.
128
+ const placements = [
129
+ ...(await db
130
+ .select({ containerId: documentContainerProjection.containerId })
131
+ .from(documentContainerProjection)
132
+ .where(eq(documentContainerProjection.documentId, target.id))),
133
+ ...(await db
134
+ .select({ containerId: pendingDocumentDiscoveries.containerId })
135
+ .from(pendingDocumentDiscoveries)
136
+ .where(eq(pendingDocumentDiscoveries.documentId, target.id))),
137
+ ...(await db
138
+ .select({ containerId: containers.id })
139
+ .from(containers)
140
+ .where(eq(containers.metadataDocumentId, target.id))),
141
+ ];
142
+ const containerIds = new Set();
143
+ for (const { containerId } of placements)
144
+ if (containerId)
145
+ for (const id of await containerLineage(db, containerId))
146
+ containerIds.add(id);
147
+ return [
148
+ { kind: "document", id: target.id },
149
+ ...[...containerIds].map((id) => ({ kind: "container", id })),
150
+ ];
151
+ }
152
+ /**
153
+ * The one organization every local source places the read in. Verified
154
+ * ownership (stored verified bundles of the target and of the containers on
155
+ * its walk, and a document's verified discovery head) and local placement
156
+ * (listed container rows reached through links, pending listings, metadata,
157
+ * or parents) must all agree: any disagreement, or no source at all, yields
158
+ * null, so the read is sent with no hints rather than another
159
+ * organization's.
160
+ */
161
+ async function targetOrganization(db, target, objects) {
162
+ const containerIds = objects
163
+ .filter((object) => object.kind === "container")
164
+ .map((object) => object.id);
165
+ const listed = containerIds.length === 0
166
+ ? []
167
+ : await db
168
+ .select({ organizationId: containers.organizationId })
169
+ .from(containers)
170
+ .where(inArray(containers.id, containerIds));
171
+ const verifiedContainers = containerIds.length === 0
172
+ ? []
173
+ : await db
174
+ .select({ organizationId: bundles.organizationId })
175
+ .from(bundles)
176
+ .where(and(eq(bundles.objectKind, "container"), inArray(bundles.objectId, containerIds)));
177
+ const verifiedTarget = await db
178
+ .select({ organizationId: bundles.organizationId })
179
+ .from(bundles)
180
+ .where(and(eq(bundles.objectKind, target.kind), eq(bundles.objectId, target.id)));
181
+ const discovered = target.kind === "document"
182
+ ? await db
183
+ .select({ organizationId: documentDiscoveryHeads.organizationId })
184
+ .from(documentDiscoveryHeads)
185
+ .where(eq(documentDiscoveryHeads.documentId, target.id))
186
+ : [];
187
+ const sources = [listed, verifiedContainers, verifiedTarget, discovered];
188
+ const organizations = new Set(sources.flat().map((row) => row.organizationId));
189
+ const [organizationId] = organizations;
190
+ return organizations.size === 1 && organizationId ? organizationId : null;
191
+ }
192
+ const storedColumns = {
193
+ bundleDigest: bundles.bundleDigest,
194
+ bundleJson: bundles.bundleJson,
195
+ manifestHash: bundles.manifestHash,
196
+ };
197
+ async function readStoredBundle(row) {
198
+ if ((await sha256Hex(row.bundleJson)) !== row.bundleDigest)
199
+ return null;
200
+ try {
201
+ const bundle = JSON.parse(row.bundleJson);
202
+ return isAccessManifestBundleWireResponse(bundle) &&
203
+ bundle.manifestHash === row.manifestHash
204
+ ? bundle
205
+ : null;
206
+ }
207
+ catch {
208
+ return null;
209
+ }
210
+ }
211
+ function objectRows(db, organizationId, object, limit) {
212
+ return db
213
+ .select(storedColumns)
214
+ .from(bundles)
215
+ .where(and(eq(bundles.organizationId, organizationId), eq(bundles.objectKind, object.kind), eq(bundles.objectId, object.id)))
216
+ .orderBy(desc(bundles.epoch))
217
+ .limit(limit);
218
+ }
219
+ function recentRows(db, organizationId, limit) {
220
+ return db
221
+ .select(storedColumns)
222
+ .from(bundles)
223
+ .where(eq(bundles.organizationId, organizationId))
224
+ .orderBy(desc(bundles.verifiedAt), asc(bundles.manifestHash))
225
+ .limit(limit);
226
+ }
227
+ /**
228
+ * Verified bundles of the target's object and of the containers its
229
+ * projection walks, newest epochs first; then the most recently verified rows
230
+ * of its organization, since a just-verified parent is the likeliest part of
231
+ * a new child's projection. Citations never leave the target's organization:
232
+ * an object whose organization the device cannot place cites nothing.
233
+ * Damaged rows are dropped.
234
+ */
235
+ export async function loadVerifiedManifestCitations(execSql, target, limit = KNOWN_MANIFEST_HINT_LIMIT) {
236
+ await ensureSqlTables(execSql, [
237
+ ...verifiedManifestBundleTables,
238
+ ...containerTables,
239
+ ...documentContainerProjectionTables,
240
+ ]);
241
+ const { db } = getClientSQLitePersistenceRuntime(execSql);
242
+ const cited = new Map();
243
+ const damaged = [];
244
+ const collect = async (rows) => {
245
+ for (const row of rows) {
246
+ if (cited.size >= limit || cited.has(row.manifestHash))
247
+ continue;
248
+ const bundle = await readStoredBundle(row);
249
+ if (bundle)
250
+ cited.set(row.manifestHash, bundle);
251
+ else
252
+ damaged.push(row.manifestHash);
253
+ }
254
+ };
255
+ const objects = await citedObjects(db, target);
256
+ const organizationId = await targetOrganization(db, target, objects);
257
+ if (organizationId === null)
258
+ return [];
259
+ for (const object of objects)
260
+ if (cited.size < limit)
261
+ await collect(await objectRows(db, organizationId, object, limit - cited.size));
262
+ if (cited.size < limit)
263
+ await collect(await recentRows(db, organizationId, limit + cited.size));
264
+ if (damaged.length > 0)
265
+ await getClientSQLitePersistenceRuntime(execSql)
266
+ .runMutation((handle) => handle
267
+ .delete(bundles)
268
+ .where(inArray(bundles.manifestHash, damaged))
269
+ .run())
270
+ .catch(() => undefined);
271
+ return [...cited.values()];
272
+ }
273
+ /** Cites from whichever database the signed-in identity currently uses. */
274
+ export function createVerifiedManifestCitations(resolveExecSql) {
275
+ return {
276
+ cite: async (target) => {
277
+ const execSql = resolveExecSql();
278
+ return execSql ? loadVerifiedManifestCitations(execSql, target) : [];
279
+ },
280
+ };
281
+ }
282
+ //# sourceMappingURL=verifiedManifestBundlePersistence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifiedManifestBundlePersistence.js","sourceRoot":"","sources":["verifiedManifestBundlePersistence.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAEL,kCAAkC,GACnC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EACL,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,2CAA2C,CAAC;AACnD,OAAO,EACL,eAAe,EACf,2BAA2B,EAC3B,iCAAiC,GAClC,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAGL,iCAAiC,GAClC,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAgB,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EACL,6BAA6B,IAAI,OAAO,EACxC,4BAA4B,GAC7B,MAAM,wCAAwC,CAAC;AAEhD;;;GAGG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,IAAI,CAAC;AAClD,MAAM,eAAe,GAAG,GAAG,CAAC;AAY5B,SAAS,SAAS,CAAC,IAAY;IAC7B,OAAO,aAAa,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;AACvD,CAAC;AAED,SAAS,OAAO,CAAI,MAAoB;IACtC,MAAM,MAAM,GAAU,EAAE,CAAC;IACzB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,IAAI,eAAe;QACjE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,eAAe,CAAC,CAAC,CAAC;IAC5D,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,kBAAkB,CAC/B,EAAgC;IAEhC,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,GAAG,CAAQ,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9E,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC,CAAC,GAAG,6BAA6B,CAAC;IACjE,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO;IACxB,MAAM,OAAO,GAAG,MAAM,EAAE;SACrB,MAAM,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC;SAC9C,IAAI,CAAC,OAAO,CAAC;SACb,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;SAC3D,KAAK,CAAC,MAAM,CAAC,CAAC;IACjB,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACrE,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;AAC/E,CAAC;AAQD;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CACxC,OAA0C;IAE1C,MAAM,MAAM,GAAG,IAAI,GAAG,CACpB,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,CAC3D,CAAC;IACF,OAAO,OAAO,CAAC,GAAG,CAChB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE;QACxD,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC1C,OAAO;YACL,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,cAAc,EAAE,UAAU,CAAC,cAAc;YACzC,UAAU,EAAE,UAAU,CAAC,UAAU;YACjC,QAAQ,EAAE,UAAU,CAAC,QAAQ;YAC7B,KAAK,EAAE,UAAU,CAAC,KAAK;YACvB,UAAU;YACV,YAAY,EAAE,MAAM,SAAS,CAAC,UAAU,CAAC;SAC1C,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,yCAAyC,CAC7D,EAAgC,EAChC,IAA0C,EAC1C,UAAkB;IAElB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAC9B,MAAM,MAAM,GAAG,MAAM,aAAa,CAChC,EAAE,EACF,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,CACpC,CAAC;IACF,4DAA4D;IAC5D,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAC3B,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,GAAG,CAAC,YAAY,CAC3D,CAAC;IACF,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACnE,MAAM,EAAE;aACL,MAAM,CAAC,OAAO,CAAC;aACf,GAAG,CAAC,EAAE,UAAU,EAAE,CAAC;aACnB,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;aAC3C,GAAG,EAAE,CAAC;IACX,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,GAAG,CAAC,YAAY;YAAE,SAAS;QAChE,MAAM,QAAQ,GAAG,EAAE,GAAG,GAAG,EAAE,UAAU,EAAE,CAAC;QACxC,MAAM,EAAE;aACL,MAAM,CAAC,OAAO,CAAC;aACf,MAAM,CAAC,QAAQ,CAAC;aAChB,kBAAkB,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,YAAY,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC;aACnE,GAAG,EAAE,CAAC;IACX,CAAC;IACD,MAAM,kBAAkB,CAAC,EAAE,CAAC,CAAC;AAC/B,CAAC;AAED,KAAK,UAAU,aAAa,CAC1B,EAAgC,EAChC,cAAiC;IAEjC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,MAAM,EAAE;aAClB,MAAM,CAAC;YACN,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,YAAY,EAAE,OAAO,CAAC,YAAY;SACnC,CAAC;aACD,IAAI,CAAC,OAAO,CAAC;aACb,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC;QAC/C,KAAK,MAAM,GAAG,IAAI,IAAI;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,8EAA8E;AAC9E,KAAK,UAAU,gBAAgB,CAC7B,EAAwB,EACxB,WAAmB;IAEnB,MAAM,OAAO,GAAG,CAAC,WAAW,CAAC,CAAC;IAC9B,OAAO,OAAO,CAAC,MAAM,GAAG,yBAAyB,EAAE,CAAC;QAClD,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE;aACnB,MAAM,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC;aACzC,IAAI,CAAC,UAAU,CAAC;aAChB,KAAK,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;aAC9C,KAAK,CAAC,CAAC,CAAC,CAAC;QACZ,MAAM,QAAQ,GAAG,GAAG,EAAE,QAAQ,CAAC;QAC/B,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAAE,MAAM;QACnD,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,YAAY,CACzB,EAAwB,EACxB,MAA8B;IAE9B,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW;QAC7B,OAAO,CAAC,MAAM,gBAAgB,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YAC1D,IAAI,EAAE,WAAW;YACjB,EAAE;SACH,CAAC,CAAC,CAAC;IACN,uEAAuE;IACvE,uEAAuE;IACvE,MAAM,UAAU,GAAG;QACjB,GAAG,CAAC,MAAM,EAAE;aACT,MAAM,CAAC,EAAE,WAAW,EAAE,2BAA2B,CAAC,WAAW,EAAE,CAAC;aAChE,IAAI,CAAC,2BAA2B,CAAC;aACjC,KAAK,CAAC,EAAE,CAAC,2BAA2B,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAChE,GAAG,CAAC,MAAM,EAAE;aACT,MAAM,CAAC,EAAE,WAAW,EAAE,0BAA0B,CAAC,WAAW,EAAE,CAAC;aAC/D,IAAI,CAAC,0BAA0B,CAAC;aAChC,KAAK,CAAC,EAAE,CAAC,0BAA0B,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAC/D,GAAG,CAAC,MAAM,EAAE;aACT,MAAM,CAAC,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC;aACtC,IAAI,CAAC,UAAU,CAAC;aAChB,KAAK,CAAC,EAAE,CAAC,UAAU,CAAC,kBAAkB,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;KACxD,CAAC;IACF,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IACvC,KAAK,MAAM,EAAE,WAAW,EAAE,IAAI,UAAU;QACtC,IAAI,WAAW;YACb,KAAK,MAAM,EAAE,IAAI,MAAM,gBAAgB,CAAC,EAAE,EAAE,WAAW,CAAC;gBACtD,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC3B,OAAO;QACL,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE;QACnC,GAAG,CAAC,GAAG,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC;KAC9D,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,KAAK,UAAU,kBAAkB,CAC/B,EAAwB,EACxB,MAA8B,EAC9B,OAAkE;IAElE,MAAM,YAAY,GAAG,OAAO;SACzB,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,WAAW,CAAC;SAC/C,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC9B,MAAM,MAAM,GACV,YAAY,CAAC,MAAM,KAAK,CAAC;QACvB,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,MAAM,EAAE;aACL,MAAM,CAAC,EAAE,cAAc,EAAE,UAAU,CAAC,cAAc,EAAE,CAAC;aACrD,IAAI,CAAC,UAAU,CAAC;aAChB,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC;IACrD,MAAM,kBAAkB,GACtB,YAAY,CAAC,MAAM,KAAK,CAAC;QACvB,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,MAAM,EAAE;aACL,MAAM,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,cAAc,EAAE,CAAC;aAClD,IAAI,CAAC,OAAO,CAAC;aACb,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,WAAW,CAAC,EACnC,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,YAAY,CAAC,CACxC,CACF,CAAC;IACV,MAAM,cAAc,GAAG,MAAM,EAAE;SAC5B,MAAM,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,cAAc,EAAE,CAAC;SAClD,IAAI,CAAC,OAAO,CAAC;SACb,KAAK,CACJ,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAC1E,CAAC;IACJ,MAAM,UAAU,GACd,MAAM,CAAC,IAAI,KAAK,UAAU;QACxB,CAAC,CAAC,MAAM,EAAE;aACL,MAAM,CAAC,EAAE,cAAc,EAAE,sBAAsB,CAAC,cAAc,EAAE,CAAC;aACjE,IAAI,CAAC,sBAAsB,CAAC;aAC5B,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QAC5D,CAAC,CAAC,EAAE,CAAC;IACT,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,kBAAkB,EAAE,cAAc,EAAE,UAAU,CAAC,CAAC;IACzE,MAAM,aAAa,GAAG,IAAI,GAAG,CAC3B,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,cAAc,CAAC,CAChD,CAAC;IACF,MAAM,CAAC,cAAc,CAAC,GAAG,aAAa,CAAC;IACvC,OAAO,aAAa,CAAC,IAAI,KAAK,CAAC,IAAI,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5E,CAAC;AAQD,MAAM,aAAa,GAAG;IACpB,YAAY,EAAE,OAAO,CAAC,YAAY;IAClC,UAAU,EAAE,OAAO,CAAC,UAAU;IAC9B,YAAY,EAAE,OAAO,CAAC,YAAY;CACnC,CAAC;AAEF,KAAK,UAAU,gBAAgB,CAC7B,GAAoB;IAEpB,IAAI,CAAC,MAAM,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,KAAK,GAAG,CAAC,YAAY;QAAE,OAAO,IAAI,CAAC;IACxE,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACnD,OAAO,kCAAkC,CAAC,MAAM,CAAC;YAC/C,MAAM,CAAC,YAAY,KAAK,GAAG,CAAC,YAAY;YACxC,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,IAAI,CAAC;IACX,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CACjB,EAAwB,EACxB,cAAsB,EACtB,MAAsD,EACtD,KAAa;IAEb,OAAO,EAAE;SACN,MAAM,CAAC,aAAa,CAAC;SACrB,IAAI,CAAC,OAAO,CAAC;SACb,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,EAC1C,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,EACnC,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,CAChC,CACF;SACA,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;SAC5B,KAAK,CAAC,KAAK,CAAC,CAAC;AAClB,CAAC;AAED,SAAS,UAAU,CACjB,EAAwB,EACxB,cAAsB,EACtB,KAAa;IAEb,OAAO,EAAE;SACN,MAAM,CAAC,aAAa,CAAC;SACrB,IAAI,CAAC,OAAO,CAAC;SACb,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;SACjD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;SAC5D,KAAK,CAAC,KAAK,CAAC,CAAC;AAClB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,OAAgB,EAChB,MAA8B,EAC9B,KAAK,GAAG,yBAAyB;IAEjC,MAAM,eAAe,CAAC,OAAO,EAAE;QAC7B,GAAG,4BAA4B;QAC/B,GAAG,eAAe;QAClB,GAAG,iCAAiC;KACrC,CAAC,CAAC;IACH,MAAM,EAAE,EAAE,EAAE,GAAG,iCAAiC,CAAC,OAAO,CAAC,CAAC;IAC1D,MAAM,KAAK,GAAG,IAAI,GAAG,EAA4C,CAAC;IAClE,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAG,KAAK,EAAE,IAAgC,EAAE,EAAE;QACzD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC;gBAAE,SAAS;YACjE,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,CAAC;YAC3C,IAAI,MAAM;gBAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;;gBAC3C,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,CAAC;IACF,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAC/C,MAAM,cAAc,GAAG,MAAM,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IACrE,IAAI,cAAc,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IACvC,KAAK,MAAM,MAAM,IAAI,OAAO;QAC1B,IAAI,KAAK,CAAC,IAAI,GAAG,KAAK;YACpB,MAAM,OAAO,CACX,MAAM,UAAU,CAAC,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CACjE,CAAC;IACN,IAAI,KAAK,CAAC,IAAI,GAAG,KAAK;QACpB,MAAM,OAAO,CAAC,MAAM,UAAU,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1E,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QACpB,MAAM,iCAAiC,CAAC,OAAO,CAAC;aAC7C,WAAW,CAAC,CAAC,MAAM,EAAE,EAAE,CACtB,MAAM;aACH,MAAM,CAAC,OAAO,CAAC;aACf,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;aAC7C,GAAG,EAAE,CACT;aACA,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC5B,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,+BAA+B,CAC7C,cAAoC;IAEpC,OAAO;QACL,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;YACrB,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;YACjC,OAAO,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type {\n VerifiedManifestCitations,\n WriterProjectionTarget,\n} from \"@tearleads/api-client\";\nimport { toFingerprint } from \"@tearleads/crypto\";\nimport {\n type AccessManifestBundleWireResponse,\n isAccessManifestBundleWireResponse,\n} from \"@tearleads/validators/response\";\nimport {\n KNOWN_MANIFEST_HINT_LIMIT,\n MAX_CONTAINER_PATH_LENGTH,\n} from \"@tearleads/validators/util\";\nimport { and, asc, desc, eq, inArray, sql } from \"drizzle-orm\";\nimport { containers } from \"../sqlite/containerSchema\";\nimport {\n documentDiscoveryHeads,\n pendingDocumentDiscoveries,\n} from \"../sqlite/documentDiscoveryEvidenceSchema\";\nimport {\n containerTables,\n documentContainerProjection,\n documentContainerProjectionTables,\n} from \"../sqlite/schema\";\nimport {\n type ClientSQLiteDatabase,\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../sqlite/sqlSchema\";\nimport {\n verifiedAccessManifestBundles as bundles,\n verifiedManifestBundleTables,\n} from \"../sqlite/verifiedManifestBundleSchema\";\n\n/**\n * Bounds local bytes (a bundle is ~10–20 KB). Evicting the least recently\n * verified rows only makes a later read carry those bundles again.\n */\nexport const MAX_VERIFIED_MANIFEST_BUNDLES = 1024;\nconst STATEMENT_BATCH = 200;\n\nexport interface VerifiedManifestBundle {\n readonly bundle: AccessManifestBundleWireResponse;\n readonly checkpoint: {\n readonly objectKind: string;\n readonly organizationId: string;\n readonly objectId: string;\n readonly epoch: number;\n };\n}\n\nfunction sha256Hex(text: string): Promise<string> {\n return toFingerprint(new TextEncoder().encode(text));\n}\n\nfunction batches<T>(values: readonly T[]): T[][] {\n const result: T[][] = [];\n for (let index = 0; index < values.length; index += STATEMENT_BATCH)\n result.push(values.slice(index, index + STATEMENT_BATCH));\n return result;\n}\n\nasync function pruneInTransaction(\n tx: ClientSQLiteTransactionScope,\n): Promise<void> {\n const [row] = await tx.select({ count: sql<number>`count(*)` }).from(bundles);\n const excess = (row?.count ?? 0) - MAX_VERIFIED_MANIFEST_BUNDLES;\n if (excess <= 0) return;\n const evicted = await tx\n .select({ manifestHash: bundles.manifestHash })\n .from(bundles)\n .orderBy(asc(bundles.verifiedAt), asc(bundles.manifestHash))\n .limit(excess);\n for (const batch of batches(evicted.map((entry) => entry.manifestHash)))\n await tx.delete(bundles).where(inArray(bundles.manifestHash, batch)).run();\n}\n\n/** A verified bundle's exact JSON and that JSON's digest. */\nexport type VerifiedManifestBundleRow = Omit<\n typeof bundles.$inferInsert,\n \"verifiedAt\"\n>;\n\n/**\n * Prepares rows for bundles that just passed projection verification. The\n * digests are computed here, before the checkpoint transaction that writes\n * them begins.\n */\nexport function verifiedManifestBundleRows(\n entries: readonly VerifiedManifestBundle[],\n): Promise<VerifiedManifestBundleRow[]> {\n const unique = new Map(\n entries.map((entry) => [entry.bundle.manifestHash, entry]),\n );\n return Promise.all(\n [...unique.values()].map(async ({ bundle, checkpoint }) => {\n const bundleJson = JSON.stringify(bundle);\n return {\n manifestHash: bundle.manifestHash,\n organizationId: checkpoint.organizationId,\n objectKind: checkpoint.objectKind,\n objectId: checkpoint.objectId,\n epoch: checkpoint.epoch,\n bundleJson,\n bundleDigest: await sha256Hex(bundleJson),\n };\n }),\n );\n}\n\n/**\n * Records verified bundles inside the transaction that finalizes their\n * projection's checkpoints, so they commit or roll back with it and no cache\n * work outlives the verification. Freshly verified bytes replace any stored\n * copy.\n */\nexport async function writeVerifiedManifestBundlesInTransaction(\n tx: ClientSQLiteTransactionScope,\n rows: readonly VerifiedManifestBundleRow[],\n verifiedAt: string,\n): Promise<void> {\n if (rows.length === 0) return;\n const stored = await storedDigests(\n tx,\n rows.map((row) => row.manifestHash),\n );\n // An identical stored row only needs its recency refreshed.\n const unchanged = rows.filter(\n (row) => stored.get(row.manifestHash) === row.bundleDigest,\n );\n for (const batch of batches(unchanged.map((row) => row.manifestHash)))\n await tx\n .update(bundles)\n .set({ verifiedAt })\n .where(inArray(bundles.manifestHash, batch))\n .run();\n for (const row of rows) {\n if (stored.get(row.manifestHash) === row.bundleDigest) continue;\n const verified = { ...row, verifiedAt };\n await tx\n .insert(bundles)\n .values(verified)\n .onConflictDoUpdate({ target: bundles.manifestHash, set: verified })\n .run();\n }\n await pruneInTransaction(tx);\n}\n\nasync function storedDigests(\n tx: ClientSQLiteTransactionScope,\n manifestHashes: readonly string[],\n): Promise<Map<string, string>> {\n const digests = new Map<string, string>();\n for (const batch of batches(manifestHashes)) {\n const rows = await tx\n .select({\n bundleDigest: bundles.bundleDigest,\n manifestHash: bundles.manifestHash,\n })\n .from(bundles)\n .where(inArray(bundles.manifestHash, batch));\n for (const row of rows) digests.set(row.manifestHash, row.bundleDigest);\n }\n return digests;\n}\n\n/** The requested object first, then each container whose lineage it cites. */\nasync function containerLineage(\n db: ClientSQLiteDatabase,\n containerId: string,\n): Promise<string[]> {\n const lineage = [containerId];\n while (lineage.length < MAX_CONTAINER_PATH_LENGTH) {\n const [row] = await db\n .select({ parentId: containers.parentId })\n .from(containers)\n .where(eq(containers.id, lineage.at(-1) ?? \"\"))\n .limit(1);\n const parentId = row?.parentId;\n if (!parentId || lineage.includes(parentId)) break;\n lineage.push(parentId);\n }\n return lineage;\n}\n\nasync function citedObjects(\n db: ClientSQLiteDatabase,\n target: WriterProjectionTarget,\n): Promise<{ readonly kind: string; readonly id: string }[]> {\n if (target.kind === \"container\")\n return (await containerLineage(db, target.id)).map((id) => ({\n kind: \"container\",\n id,\n }));\n // Linked containers, containers listing the document before its signed\n // placement is checked, and a container whose metadata document it is.\n const placements = [\n ...(await db\n .select({ containerId: documentContainerProjection.containerId })\n .from(documentContainerProjection)\n .where(eq(documentContainerProjection.documentId, target.id))),\n ...(await db\n .select({ containerId: pendingDocumentDiscoveries.containerId })\n .from(pendingDocumentDiscoveries)\n .where(eq(pendingDocumentDiscoveries.documentId, target.id))),\n ...(await db\n .select({ containerId: containers.id })\n .from(containers)\n .where(eq(containers.metadataDocumentId, target.id))),\n ];\n const containerIds = new Set<string>();\n for (const { containerId } of placements)\n if (containerId)\n for (const id of await containerLineage(db, containerId))\n containerIds.add(id);\n return [\n { kind: \"document\", id: target.id },\n ...[...containerIds].map((id) => ({ kind: \"container\", id })),\n ];\n}\n\n/**\n * The one organization every local source places the read in. Verified\n * ownership (stored verified bundles of the target and of the containers on\n * its walk, and a document's verified discovery head) and local placement\n * (listed container rows reached through links, pending listings, metadata,\n * or parents) must all agree: any disagreement, or no source at all, yields\n * null, so the read is sent with no hints rather than another\n * organization's.\n */\nasync function targetOrganization(\n db: ClientSQLiteDatabase,\n target: WriterProjectionTarget,\n objects: readonly { readonly kind: string; readonly id: string }[],\n): Promise<string | null> {\n const containerIds = objects\n .filter((object) => object.kind === \"container\")\n .map((object) => object.id);\n const listed =\n containerIds.length === 0\n ? []\n : await db\n .select({ organizationId: containers.organizationId })\n .from(containers)\n .where(inArray(containers.id, containerIds));\n const verifiedContainers =\n containerIds.length === 0\n ? []\n : await db\n .select({ organizationId: bundles.organizationId })\n .from(bundles)\n .where(\n and(\n eq(bundles.objectKind, \"container\"),\n inArray(bundles.objectId, containerIds),\n ),\n );\n const verifiedTarget = await db\n .select({ organizationId: bundles.organizationId })\n .from(bundles)\n .where(\n and(eq(bundles.objectKind, target.kind), eq(bundles.objectId, target.id)),\n );\n const discovered =\n target.kind === \"document\"\n ? await db\n .select({ organizationId: documentDiscoveryHeads.organizationId })\n .from(documentDiscoveryHeads)\n .where(eq(documentDiscoveryHeads.documentId, target.id))\n : [];\n const sources = [listed, verifiedContainers, verifiedTarget, discovered];\n const organizations = new Set(\n sources.flat().map((row) => row.organizationId),\n );\n const [organizationId] = organizations;\n return organizations.size === 1 && organizationId ? organizationId : null;\n}\n\ninterface StoredBundleRow {\n readonly bundleDigest: string;\n readonly bundleJson: string;\n readonly manifestHash: string;\n}\n\nconst storedColumns = {\n bundleDigest: bundles.bundleDigest,\n bundleJson: bundles.bundleJson,\n manifestHash: bundles.manifestHash,\n};\n\nasync function readStoredBundle(\n row: StoredBundleRow,\n): Promise<AccessManifestBundleWireResponse | null> {\n if ((await sha256Hex(row.bundleJson)) !== row.bundleDigest) return null;\n try {\n const bundle: unknown = JSON.parse(row.bundleJson);\n return isAccessManifestBundleWireResponse(bundle) &&\n bundle.manifestHash === row.manifestHash\n ? bundle\n : null;\n } catch {\n return null;\n }\n}\n\nfunction objectRows(\n db: ClientSQLiteDatabase,\n organizationId: string,\n object: { readonly kind: string; readonly id: string },\n limit: number,\n) {\n return db\n .select(storedColumns)\n .from(bundles)\n .where(\n and(\n eq(bundles.organizationId, organizationId),\n eq(bundles.objectKind, object.kind),\n eq(bundles.objectId, object.id),\n ),\n )\n .orderBy(desc(bundles.epoch))\n .limit(limit);\n}\n\nfunction recentRows(\n db: ClientSQLiteDatabase,\n organizationId: string,\n limit: number,\n) {\n return db\n .select(storedColumns)\n .from(bundles)\n .where(eq(bundles.organizationId, organizationId))\n .orderBy(desc(bundles.verifiedAt), asc(bundles.manifestHash))\n .limit(limit);\n}\n\n/**\n * Verified bundles of the target's object and of the containers its\n * projection walks, newest epochs first; then the most recently verified rows\n * of its organization, since a just-verified parent is the likeliest part of\n * a new child's projection. Citations never leave the target's organization:\n * an object whose organization the device cannot place cites nothing.\n * Damaged rows are dropped.\n */\nexport async function loadVerifiedManifestCitations(\n execSql: ExecSql,\n target: WriterProjectionTarget,\n limit = KNOWN_MANIFEST_HINT_LIMIT,\n): Promise<AccessManifestBundleWireResponse[]> {\n await ensureSqlTables(execSql, [\n ...verifiedManifestBundleTables,\n ...containerTables,\n ...documentContainerProjectionTables,\n ]);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const cited = new Map<string, AccessManifestBundleWireResponse>();\n const damaged: string[] = [];\n const collect = async (rows: readonly StoredBundleRow[]) => {\n for (const row of rows) {\n if (cited.size >= limit || cited.has(row.manifestHash)) continue;\n const bundle = await readStoredBundle(row);\n if (bundle) cited.set(row.manifestHash, bundle);\n else damaged.push(row.manifestHash);\n }\n };\n const objects = await citedObjects(db, target);\n const organizationId = await targetOrganization(db, target, objects);\n if (organizationId === null) return [];\n for (const object of objects)\n if (cited.size < limit)\n await collect(\n await objectRows(db, organizationId, object, limit - cited.size),\n );\n if (cited.size < limit)\n await collect(await recentRows(db, organizationId, limit + cited.size));\n if (damaged.length > 0)\n await getClientSQLitePersistenceRuntime(execSql)\n .runMutation((handle) =>\n handle\n .delete(bundles)\n .where(inArray(bundles.manifestHash, damaged))\n .run(),\n )\n .catch(() => undefined);\n return [...cited.values()];\n}\n\n/** Cites from whichever database the signed-in identity currently uses. */\nexport function createVerifiedManifestCitations(\n resolveExecSql: () => ExecSql | null,\n): VerifiedManifestCitations {\n return {\n cite: async (target) => {\n const execSql = resolveExecSql();\n return execSql ? loadVerifiedManifestCitations(execSql, target) : [];\n },\n };\n}\n"]}
@@ -9001,6 +9001,163 @@ export declare const clientSQLiteSchema: {
9001
9001
  };
9002
9002
  dialect: "sqlite";
9003
9003
  }>;
9004
+ verifiedAccessManifestBundles: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
9005
+ name: "verified_access_manifest_bundles";
9006
+ schema: undefined;
9007
+ columns: {
9008
+ manifestHash: import("drizzle-orm/sqlite-core").SQLiteColumn<{
9009
+ name: "manifest_hash";
9010
+ tableName: "verified_access_manifest_bundles";
9011
+ dataType: "string";
9012
+ columnType: "SQLiteText";
9013
+ data: string;
9014
+ driverParam: string;
9015
+ notNull: true;
9016
+ hasDefault: false;
9017
+ isPrimaryKey: true;
9018
+ isAutoincrement: false;
9019
+ hasRuntimeDefault: false;
9020
+ enumValues: [string, ...string[]];
9021
+ baseColumn: never;
9022
+ identity: undefined;
9023
+ generated: undefined;
9024
+ }, {}, {
9025
+ length: number | undefined;
9026
+ }>;
9027
+ organizationId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
9028
+ name: "organization_id";
9029
+ tableName: "verified_access_manifest_bundles";
9030
+ dataType: "string";
9031
+ columnType: "SQLiteText";
9032
+ data: string;
9033
+ driverParam: string;
9034
+ notNull: true;
9035
+ hasDefault: false;
9036
+ isPrimaryKey: false;
9037
+ isAutoincrement: false;
9038
+ hasRuntimeDefault: false;
9039
+ enumValues: [string, ...string[]];
9040
+ baseColumn: never;
9041
+ identity: undefined;
9042
+ generated: undefined;
9043
+ }, {}, {
9044
+ length: number | undefined;
9045
+ }>;
9046
+ objectKind: import("drizzle-orm/sqlite-core").SQLiteColumn<{
9047
+ name: "object_kind";
9048
+ tableName: "verified_access_manifest_bundles";
9049
+ dataType: "string";
9050
+ columnType: "SQLiteText";
9051
+ data: string;
9052
+ driverParam: string;
9053
+ notNull: true;
9054
+ hasDefault: false;
9055
+ isPrimaryKey: false;
9056
+ isAutoincrement: false;
9057
+ hasRuntimeDefault: false;
9058
+ enumValues: [string, ...string[]];
9059
+ baseColumn: never;
9060
+ identity: undefined;
9061
+ generated: undefined;
9062
+ }, {}, {
9063
+ length: number | undefined;
9064
+ }>;
9065
+ objectId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
9066
+ name: "object_id";
9067
+ tableName: "verified_access_manifest_bundles";
9068
+ dataType: "string";
9069
+ columnType: "SQLiteText";
9070
+ data: string;
9071
+ driverParam: string;
9072
+ notNull: true;
9073
+ hasDefault: false;
9074
+ isPrimaryKey: false;
9075
+ isAutoincrement: false;
9076
+ hasRuntimeDefault: false;
9077
+ enumValues: [string, ...string[]];
9078
+ baseColumn: never;
9079
+ identity: undefined;
9080
+ generated: undefined;
9081
+ }, {}, {
9082
+ length: number | undefined;
9083
+ }>;
9084
+ epoch: import("drizzle-orm/sqlite-core").SQLiteColumn<{
9085
+ name: "epoch";
9086
+ tableName: "verified_access_manifest_bundles";
9087
+ dataType: "number";
9088
+ columnType: "SQLiteInteger";
9089
+ data: number;
9090
+ driverParam: number;
9091
+ notNull: true;
9092
+ hasDefault: false;
9093
+ isPrimaryKey: false;
9094
+ isAutoincrement: false;
9095
+ hasRuntimeDefault: false;
9096
+ enumValues: undefined;
9097
+ baseColumn: never;
9098
+ identity: undefined;
9099
+ generated: undefined;
9100
+ }, {}, {}>;
9101
+ bundleJson: import("drizzle-orm/sqlite-core").SQLiteColumn<{
9102
+ name: "bundle_json";
9103
+ tableName: "verified_access_manifest_bundles";
9104
+ dataType: "string";
9105
+ columnType: "SQLiteText";
9106
+ data: string;
9107
+ driverParam: string;
9108
+ notNull: true;
9109
+ hasDefault: false;
9110
+ isPrimaryKey: false;
9111
+ isAutoincrement: false;
9112
+ hasRuntimeDefault: false;
9113
+ enumValues: [string, ...string[]];
9114
+ baseColumn: never;
9115
+ identity: undefined;
9116
+ generated: undefined;
9117
+ }, {}, {
9118
+ length: number | undefined;
9119
+ }>;
9120
+ bundleDigest: import("drizzle-orm/sqlite-core").SQLiteColumn<{
9121
+ name: "bundle_digest";
9122
+ tableName: "verified_access_manifest_bundles";
9123
+ dataType: "string";
9124
+ columnType: "SQLiteText";
9125
+ data: string;
9126
+ driverParam: string;
9127
+ notNull: true;
9128
+ hasDefault: false;
9129
+ isPrimaryKey: false;
9130
+ isAutoincrement: false;
9131
+ hasRuntimeDefault: false;
9132
+ enumValues: [string, ...string[]];
9133
+ baseColumn: never;
9134
+ identity: undefined;
9135
+ generated: undefined;
9136
+ }, {}, {
9137
+ length: number | undefined;
9138
+ }>;
9139
+ verifiedAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
9140
+ name: "verified_at";
9141
+ tableName: "verified_access_manifest_bundles";
9142
+ dataType: "string";
9143
+ columnType: "SQLiteText";
9144
+ data: string;
9145
+ driverParam: string;
9146
+ notNull: true;
9147
+ hasDefault: false;
9148
+ isPrimaryKey: false;
9149
+ isAutoincrement: false;
9150
+ hasRuntimeDefault: false;
9151
+ enumValues: [string, ...string[]];
9152
+ baseColumn: never;
9153
+ identity: undefined;
9154
+ generated: undefined;
9155
+ }, {}, {
9156
+ length: number | undefined;
9157
+ }>;
9158
+ };
9159
+ dialect: "sqlite";
9160
+ }>;
9004
9161
  trustedUserIdentityPins: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
9005
9162
  name: "trusted_user_identity_pins";
9006
9163
  schema: undefined;
@@ -19,6 +19,7 @@ import { principalMutationJournal, principalMutationJournalTables, } from "./pri
19
19
  import { principalPolicies, principalPolicyBundleHistory, principalPolicyBundleReferences, principalPolicyCheckpoints, principalPolicyOrganizations, } from "./principalPolicySchema.js";
20
20
  import { securityIncidents, securityIncidentTable, } from "./securityIncidentSchema.js";
21
21
  import { defineSqlTableSchema } from "./sqlTableSchema.js";
22
+ import { verifiedAccessManifestBundles, verifiedManifestBundleTables, } from "./verifiedManifestBundleSchema.js";
22
23
  export { containerHydrationTombstones, containerProjection, containers, dormantContainerMetadata, dormantMetadataSweepRequests, supersededContainerBindings, } from "./containerSchema.js";
23
24
  export { documentAttachmentBlobProjection, documentPendingAttachments, } from "./documentAttachmentSchema.js";
24
25
  export { documentMoveIntents } from "./documentPlacementIntentSchema.js";
@@ -588,6 +589,7 @@ export const clientSqlTables = [
588
589
  ...documentTables,
589
590
  ...principalPolicyTables,
590
591
  ...keyingCheckpointTables,
592
+ ...verifiedManifestBundleTables,
591
593
  ...trustedUserIdentityPinTables,
592
594
  ...securityIncidentTables,
593
595
  ...containerTables,
@@ -623,6 +625,7 @@ export const clientSQLiteSchema = {
623
625
  accessManifestCheckpoints,
624
626
  documentPurgeCheckpoints,
625
627
  principalPolicyCheckpoints,
628
+ verifiedAccessManifestBundles,
626
629
  trustedUserIdentityPins,
627
630
  securityIncidents,
628
631
  ...containerSQLiteSchema,