@substrat-run/kernel 0.148.0 → 0.150.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/attachment-sweep.d.ts +281 -0
  2. package/dist/attachment-sweep.d.ts.map +1 -0
  3. package/dist/attachment-sweep.js +480 -0
  4. package/dist/attachment-sweep.js.map +1 -0
  5. package/dist/index.d.ts +15 -9
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +11 -7
  8. package/dist/index.js.map +1 -1
  9. package/dist/invocation-line.d.ts +85 -0
  10. package/dist/invocation-line.d.ts.map +1 -1
  11. package/dist/invocation-line.js +43 -0
  12. package/dist/invocation-line.js.map +1 -1
  13. package/dist/migration-digest.d.ts +56 -0
  14. package/dist/migration-digest.d.ts.map +1 -1
  15. package/dist/migration-digest.js +92 -0
  16. package/dist/migration-digest.js.map +1 -1
  17. package/dist/migration-fence.d.ts +14 -0
  18. package/dist/migration-fence.d.ts.map +1 -0
  19. package/dist/migration-fence.js +515 -0
  20. package/dist/migration-fence.js.map +1 -0
  21. package/dist/migration-replay.d.ts +52 -0
  22. package/dist/migration-replay.d.ts.map +1 -0
  23. package/dist/migration-replay.js +140 -0
  24. package/dist/migration-replay.js.map +1 -0
  25. package/dist/module-migrations.d.ts +3 -3
  26. package/dist/module-migrations.js +3 -3
  27. package/dist/peer.d.ts +32 -2
  28. package/dist/peer.d.ts.map +1 -1
  29. package/dist/peer.js +99 -3
  30. package/dist/peer.js.map +1 -1
  31. package/dist/permission-eval.d.ts +8 -0
  32. package/dist/permission-eval.d.ts.map +1 -1
  33. package/dist/permission-eval.js +14 -0
  34. package/dist/permission-eval.js.map +1 -1
  35. package/dist/platform-sweep.d.ts +116 -2
  36. package/dist/platform-sweep.d.ts.map +1 -1
  37. package/dist/platform-sweep.js +142 -0
  38. package/dist/platform-sweep.js.map +1 -1
  39. package/dist/promote-review.d.ts +125 -0
  40. package/dist/promote-review.d.ts.map +1 -0
  41. package/dist/promote-review.js +181 -0
  42. package/dist/promote-review.js.map +1 -0
  43. package/dist/scope-host.d.ts +204 -42
  44. package/dist/scope-host.d.ts.map +1 -1
  45. package/dist/scope-host.js.map +1 -1
  46. package/dist/scope-store.d.ts +5 -5
  47. package/dist/scope-store.d.ts.map +1 -1
  48. package/dist/spine-restore.d.ts +2 -0
  49. package/dist/spine-restore.d.ts.map +1 -1
  50. package/dist/spine-restore.js +5 -0
  51. package/dist/spine-restore.js.map +1 -1
  52. package/dist/store-driver-releases.d.ts +306 -0
  53. package/dist/store-driver-releases.d.ts.map +1 -0
  54. package/dist/store-driver-releases.js +581 -0
  55. package/dist/store-driver-releases.js.map +1 -0
  56. package/dist/table-ownership.d.ts +5 -2
  57. package/dist/table-ownership.d.ts.map +1 -1
  58. package/dist/table-ownership.js +19 -10
  59. package/dist/table-ownership.js.map +1 -1
  60. package/dist/testing.d.ts +2 -0
  61. package/dist/testing.d.ts.map +1 -1
  62. package/dist/testing.js +2 -0
  63. package/dist/testing.js.map +1 -1
  64. package/dist/upload-link.d.ts +60 -7
  65. package/dist/upload-link.d.ts.map +1 -1
  66. package/dist/upload-link.js +189 -25
  67. package/dist/upload-link.js.map +1 -1
  68. package/dist/version-migrations.d.ts +63 -1
  69. package/dist/version-migrations.d.ts.map +1 -1
  70. package/dist/version-migrations.js +174 -8
  71. package/dist/version-migrations.js.map +1 -1
  72. package/dist/vertical-scope-stores.d.ts +9 -9
  73. package/dist/vertical-scope-stores.d.ts.map +1 -1
  74. package/dist/vertical-scope-stores.js +29 -29
  75. package/dist/vertical-scope-stores.js.map +1 -1
  76. package/package.json +2 -2
  77. package/dist/store-kind-releases.d.ts +0 -137
  78. package/dist/store-kind-releases.d.ts.map +0 -1
  79. package/dist/store-kind-releases.js +0 -252
  80. package/dist/store-kind-releases.js.map +0 -1
@@ -0,0 +1,480 @@
1
+ import { substratError } from '@substrat-run/contracts';
2
+ import { attachmentBlobKey } from './scope-host.js';
3
+ import { ulidTime } from './ulid.js';
4
+ /**
5
+ * The orphan-byte sweep (#2314): delete attachment bytes that no `_substrat_attachments` row
6
+ * points at, written ONCE for both adapters.
7
+ *
8
+ * An attachment write puts the bytes first and the row second, so a crash between the two, a
9
+ * transport failure whose commit is unknown (#2311 keeps the bytes on doubt, K-54) and a PITR
10
+ * rewind past a row all leave an object nobody reads. The one thing this sweep must never do is
11
+ * delete bytes whose row committed, or whose row a restore can still bring back. Four guards
12
+ * hold that, each on its own:
13
+ *
14
+ * 1. **A tombstone is the linearization point.** The scope does not delete; it CONDEMNS. In one
15
+ * scope transaction it records each candidate that has no row in `_substrat_attachment_condemned`,
16
+ * and the attachment insert refuses a condemned id in ITS transaction
17
+ * (`refuseCondemnedAttachment`). The scope serializes the two, so either the row commits first
18
+ * and the condemnation sees it and skips the id, or the condemnation commits first and the row
19
+ * is refused: the upload fails and nothing commits without its bytes. Only a condemned id's
20
+ * object is ever deleted. No clock and no timing is involved, so a slow upload, a retried call
21
+ * or a skewed clock cannot turn a committed row into a deleted object.
22
+ * 2. **An age floor.** Only an object whose id (a ULID minted just before its `put`) is older than
23
+ * `graceMs` is a candidate, so an upload still in flight is never condemned in the first place
24
+ * and therefore never refused. The floor is courtesy, guard 1 is safety.
25
+ * 3. **The restore fence.** A row a rewind or a dump restore took away can come back: PITR keeps
26
+ * every state of the last 30 days. Every path that replaces a scope's state writes a fence
27
+ * object into the tenant's blob store BEFORE it does (`fenceAttachmentSweep`), and the sweep
28
+ * deletes nothing of that scope until the fence is `fenceMs` old. The fence lives in the blob
29
+ * store, never in the scope, because the rewind it records would erase it there. It is read
30
+ * before the pass and again after the condemnation, immediately before the first delete, so a
31
+ * rewind that lands mid-pass stops the deletes. A fence that cannot be read is a fence.
32
+ * 4. **The serving copy only, and only while it is live.** The judgement is only as good as the
33
+ * copy that makes it. A host refuses a scope that is not active, a store that holds nothing, a
34
+ * copy a carry wiped or kept, a fork, and a scope not provisioned for the tenant. It also
35
+ * refuses while another script's copy of the scope is unsettled in the copy ledger
36
+ * (any state but `SETTLED_COPY_STATES`), because that copy may hold rows the serving one lacks. Which
37
+ * deployment is asked is the driver's business: the platform asks the one serving the scope.
38
+ *
39
+ * The instant a pass judges age and the fence against is the HOST's clock, never a caller's
40
+ * (`OrphanSweepRequest` carries none), so no caller can step past guard 2 or guard 3.
41
+ *
42
+ * A dry run condemns nothing and deletes nothing: it reports what a pass would condemn, and the
43
+ * scope records that as its licence (#2350). A destructive pass needs one in force, and deletes only
44
+ * what it lists (`ATTACHMENT_SWEEP_LICENCE_DDL`).
45
+ */
46
+ /** The tombstone table, shared by both adapters so the two cannot part company. */
47
+ export const ATTACHMENT_CONDEMNED_DDL = `
48
+ CREATE TABLE IF NOT EXISTS _substrat_attachment_condemned (
49
+ -- The attachment id whose object the sweep deleted, or is about to. No row may ever be
50
+ -- inserted for it again: the insert path refuses it (#2314).
51
+ id TEXT PRIMARY KEY,
52
+ condemned_at TEXT NOT NULL
53
+ );
54
+ `;
55
+ /** The refusal reason an upload meets when its id was condemned before its row could commit. */
56
+ export const ATTACHMENT_CONDEMNED = 'attachment_condemned';
57
+ /** How old an object must be before a pass looks at it, by default: seven days. */
58
+ export const ORPHAN_SWEEP_GRACE_MS = 7 * 86_400_000;
59
+ /**
60
+ * How long a scope's bytes are left alone after its state was replaced, by default: 35 days. A
61
+ * Durable Object keeps 30 days of point-in-time history, so a row a rewind took away can be
62
+ * restored for that long; five more days absorb the clocks and the time a rewind takes to arm.
63
+ */
64
+ export const ORPHAN_SWEEP_FENCE_MS = 35 * 86_400_000;
65
+ /** The most objects one pass deletes, by default. */
66
+ export const ORPHAN_SWEEP_LIMIT = 500;
67
+ /** The most ids one condemnation call carries: one JSON parameter, so no SQL limit applies. */
68
+ export const ORPHAN_CONDEMN_BATCH = 100;
69
+ /**
70
+ * How long a tombstone is kept. Past this no upload that minted the id can still be in flight
71
+ * (an upload holds its body in memory, and its call to the scope is one request), so the
72
+ * tombstone guards nothing any more.
73
+ */
74
+ export const ORPHAN_TOMBSTONE_RETENTION_MS = 30 * 86_400_000;
75
+ /**
76
+ * Refuse an insert of a condemned attachment id. Called by both adapters inside the
77
+ * attachment write's own transaction, before its `INSERT`, so it and the condemnation are
78
+ * serialized by the scope (guard 1). A `conflict` is a refusal the scope ANSWERS, so the host
79
+ * compensates the object away (it is already gone, or about to be) and the caller is told.
80
+ */
81
+ export function refuseCondemnedAttachment(sql, attachmentId) {
82
+ const condemned = sql.query('SELECT 1 AS hit FROM _substrat_attachment_condemned WHERE id = ?', [attachmentId]);
83
+ if (condemned.length > 0) {
84
+ throw substratError('conflict', `attachment ${attachmentId}: its bytes were swept as an orphan before this row could commit; upload the file again`, { reason: ATTACHMENT_CONDEMNED });
85
+ }
86
+ }
87
+ /**
88
+ * Refuse a dump load that would put back a row for an id this store has condemned (#2314). A
89
+ * load is the other way an `_substrat_attachments` row is written, so it meets the same
90
+ * tombstone the insert does: a restore that raced a pass must not land a row whose bytes that
91
+ * pass is deleting. Called inside the load's transaction, BEFORE its first drop, against the
92
+ * store as it is. A store with no tombstone table (from before #2314) has condemned nothing.
93
+ */
94
+ export function refuseCondemnedInDump(sql, tables) {
95
+ const has = sql.query(`SELECT 1 AS hit FROM sqlite_master WHERE type = 'table' AND name = '_substrat_attachment_condemned'`);
96
+ if (has.length === 0)
97
+ return;
98
+ const ids = [];
99
+ for (const table of tables) {
100
+ if (table.name.toLowerCase() !== '_substrat_attachments')
101
+ continue;
102
+ const at = table.columns.indexOf('id');
103
+ if (at < 0)
104
+ continue;
105
+ for (const row of table.rows)
106
+ if (typeof row[at] === 'string')
107
+ ids.push(row[at]);
108
+ }
109
+ for (let i = 0; i < ids.length; i += 500) {
110
+ const hit = sql.query(`SELECT c.id AS id FROM _substrat_attachment_condemned c
111
+ WHERE c.id IN (SELECT value FROM json_each(?)) ORDER BY c.id LIMIT 5`, [JSON.stringify(ids.slice(i, i + 500))]);
112
+ if (hit.length > 0) {
113
+ throw substratError('conflict', `restore refused: the dump holds attachment rows whose bytes the orphan sweep condemned in this ` +
114
+ `scope (${hit.map((h) => h.id).join(', ')}). Their bytes are gone or going; restore a dump taken ` +
115
+ `after they were swept, or remove those rows from the dump`, { reason: ATTACHMENT_CONDEMNED });
116
+ }
117
+ }
118
+ }
119
+ /** The ids in `ids` that no `_substrat_attachments` row names. Read-only. */
120
+ export function rowlessAttachmentIds(sql, ids) {
121
+ if (ids.length === 0)
122
+ return [];
123
+ return sql
124
+ .query(`SELECT j.value AS value FROM json_each(?) AS j
125
+ WHERE NOT EXISTS (SELECT 1 FROM _substrat_attachments a WHERE a.id = j.value)
126
+ ORDER BY j.value`, [JSON.stringify(ids)])
127
+ .map((r) => r.value);
128
+ }
129
+ /**
130
+ * Condemn the ids in `ids` that no row names, inside the caller's scope transaction (guard 1),
131
+ * and return every id that is condemned and rowless now: the ones condemned here and the ones an
132
+ * earlier pass condemned but did not get to delete. Only those may be deleted. Idempotent.
133
+ *
134
+ * Each returned id's tombstone is stamped `now`, re-stamped if an earlier pass condemned it, so
135
+ * the prune of tombstones past `ORPHAN_TOMBSTONE_RETENTION_MS` that follows can never take the
136
+ * tombstone of an id this pass is about to delete.
137
+ */
138
+ export function condemnOrphanAttachments(sql, ids, now) {
139
+ if (ids.length > ORPHAN_CONDEMN_BATCH) {
140
+ throw new RangeError(`at most ${ORPHAN_CONDEMN_BATCH} ids are condemned at once, got ${ids.length}`);
141
+ }
142
+ const rowless = rowlessAttachmentIds(sql, ids);
143
+ for (const id of rowless) {
144
+ sql.exec(`INSERT INTO _substrat_attachment_condemned (id, condemned_at) VALUES (?, ?)
145
+ ON CONFLICT (id) DO UPDATE SET condemned_at = excluded.condemned_at`, [id, now]);
146
+ }
147
+ // Only a call that condemned something writes at all, so a pass that finds nothing leaves the
148
+ // scope's write revision where it was (a carry or a kept-copy release is fenced on it). The prune
149
+ // still never reaches this call's own tombstones, stamped `now`, past a horizon well before it.
150
+ if (rowless.length > 0) {
151
+ const horizon = new Date(Date.parse(now) - ORPHAN_TOMBSTONE_RETENTION_MS).toISOString();
152
+ sql.exec('DELETE FROM _substrat_attachment_condemned WHERE condemned_at < ?', [horizon]);
153
+ }
154
+ return rowless;
155
+ }
156
+ /**
157
+ * The dry-run-first licence (#2350), held by the scope's own storage so the rule holds whoever asks
158
+ * for a pass: one row per attachment id the scope's last dry run listed, stamped with the host's
159
+ * clock at that run. A destructive pass is refused without one in force
160
+ * (`refuseUnlicensedOrphanSweep`), condemns only ids it lists (`condemnLicensedOrphanAttachments`),
161
+ * and takes each id it deleted out of it (`consumeOrphanSweepLicence`).
162
+ *
163
+ * It only ever NARROWS what a pass may delete: every guard of the pass still runs on the ids it
164
+ * lists. So a licence that a rewind brought back, or that outlived the rows it was judged against,
165
+ * can permit nothing the pass would not permit without it. A dump never carries its rows and every
166
+ * load drops them, so a restore, a carry or a fork leaves the next pass a dry run.
167
+ */
168
+ export const ATTACHMENT_SWEEP_LICENCE_DDL = `
169
+ CREATE TABLE IF NOT EXISTS _substrat_attachment_sweep_licence (
170
+ -- An attachment id the scope's last orphan-sweep dry run listed, and that no destructive
171
+ -- pass has deleted since (#2350). Store-local: never dumped, and cleared by every load.
172
+ id TEXT PRIMARY KEY,
173
+ licensed_at TEXT NOT NULL
174
+ );
175
+ `;
176
+ /** The licence table's name, for the dump export that leaves its rows out and the load that drops them. */
177
+ export const ATTACHMENT_SWEEP_LICENCE_TABLE = '_substrat_attachment_sweep_licence';
178
+ /** The refusal reason a destructive pass meets when the scope holds no dry-run licence in force. */
179
+ export const ORPHAN_SWEEP_UNLICENSED = 'orphan_sweep_unlicensed';
180
+ /** How long a scope's dry-run licence stays in force, judged against the host's clock: seven days. */
181
+ export const ORPHAN_SWEEP_DRY_RUN_WINDOW_MS = 7 * 86_400_000;
182
+ /** The most ids one licence holds. A dry run that lists more licenses its first this many. */
183
+ export const ORPHAN_SWEEP_LICENCE_CAP = 1000;
184
+ /**
185
+ * How far past `now` a licence's stamp may read and still be in force: the jitter between two
186
+ * reads of a host's clock. Beyond it the clock went back, and the licence is not in force.
187
+ */
188
+ const LICENCE_SKEW_MS = 60_000;
189
+ const hasLicenceTable = (sql) => sql.query(`SELECT 1 AS hit FROM sqlite_master WHERE type = 'table' AND name = '_substrat_attachment_sweep_licence'`).length > 0;
190
+ /**
191
+ * When the scope's licence was recorded, if it holds one in force at `now`, else null. A licence
192
+ * stamped more than a minute later than `now` (a clock that went back) is not in force: the next
193
+ * pass is a dry run.
194
+ */
195
+ export function orphanSweepLicensedAt(sql, now) {
196
+ if (!hasLicenceTable(sql))
197
+ return null;
198
+ const [row] = sql.query(`SELECT MIN(licensed_at) AS at FROM _substrat_attachment_sweep_licence`);
199
+ const at = row?.at ?? null;
200
+ if (at === null)
201
+ return null;
202
+ const atMs = Date.parse(at);
203
+ const nowMs = Date.parse(now);
204
+ if (!Number.isFinite(atMs) || atMs > nowMs + LICENCE_SKEW_MS || nowMs - atMs > ORPHAN_SWEEP_DRY_RUN_WINDOW_MS)
205
+ return null;
206
+ return at;
207
+ }
208
+ /** Refuse a destructive pass over a scope that holds no dry-run licence in force at `now`. */
209
+ export function refuseUnlicensedOrphanSweep(sql, now) {
210
+ if (orphanSweepLicensedAt(sql, now) !== null)
211
+ return;
212
+ throw substratError('conflict', `refused: this scope holds no orphan-sweep dry run from the last ` +
213
+ `${ORPHAN_SWEEP_DRY_RUN_WINDOW_MS / 86_400_000} days, so a destructive pass has nothing it may delete. Run a dry run first`, { reason: ORPHAN_SWEEP_UNLICENSED });
214
+ }
215
+ /**
216
+ * Record what a dry run found as the scope's licence, inside the caller's scope transaction: the
217
+ * first `ORPHAN_SWEEP_LICENCE_CAP` of `ids`, stamped `now`. Writes only when that set differs from
218
+ * the one held, or the one held is older than half the window, so a dry run that keeps finding the
219
+ * same orphans moves no write revision (a carry and a kept-copy release are fenced on it). An empty
220
+ * set clears a held licence, and writes nothing when there is none. Returns whether it wrote.
221
+ */
222
+ export function recordOrphanSweepLicence(sql, ids, now) {
223
+ const next = [...new Set(ids)].slice(0, ORPHAN_SWEEP_LICENCE_CAP);
224
+ const held = sql.query(`SELECT id, licensed_at FROM _substrat_attachment_sweep_licence`);
225
+ if (held.length === 0 && next.length === 0)
226
+ return false;
227
+ if (held.length === next.length && held.length > 0) {
228
+ const heldIds = new Set(held.map((r) => r.id));
229
+ const oldest = Math.min(...held.map((r) => Date.parse(r.licensed_at)));
230
+ const age = Date.parse(now) - oldest;
231
+ const fresh = Number.isFinite(age) && age >= -LICENCE_SKEW_MS && age <= ORPHAN_SWEEP_DRY_RUN_WINDOW_MS / 2;
232
+ if (fresh && next.every((id) => heldIds.has(id)))
233
+ return false;
234
+ }
235
+ sql.exec(`DELETE FROM _substrat_attachment_sweep_licence`);
236
+ if (next.length > 0) {
237
+ sql.exec(`INSERT INTO _substrat_attachment_sweep_licence (id, licensed_at) SELECT value, ? FROM json_each(?)`, [now, JSON.stringify(next)]);
238
+ }
239
+ return true;
240
+ }
241
+ /**
242
+ * `condemnOrphanAttachments` over only the ids in `ids` the scope's licence lists, while it is in
243
+ * force at `now`: a destructive pass deletes nothing its dry run did not list. Inside the caller's
244
+ * scope transaction, so a load that dropped the licence meanwhile leaves nothing to condemn.
245
+ */
246
+ export function condemnLicensedOrphanAttachments(sql, ids, now) {
247
+ if (ids.length === 0 || orphanSweepLicensedAt(sql, now) === null)
248
+ return [];
249
+ const licensed = sql
250
+ .query(`SELECT j.value AS value FROM json_each(?) AS j
251
+ WHERE EXISTS (SELECT 1 FROM _substrat_attachment_sweep_licence l WHERE l.id = j.value)
252
+ ORDER BY j.value`, [JSON.stringify(ids)])
253
+ .map((r) => r.value);
254
+ return condemnOrphanAttachments(sql, licensed, now);
255
+ }
256
+ /** The ids the scope's licence lists, held or expired: at most `ORPHAN_SWEEP_LICENCE_CAP`. */
257
+ export function licensedOrphanIds(sql) {
258
+ if (!hasLicenceTable(sql))
259
+ return [];
260
+ return sql.query(`SELECT id FROM _substrat_attachment_sweep_licence ORDER BY id`).map((r) => r.id);
261
+ }
262
+ /**
263
+ * Take ids out of the licence: the ones a destructive pass deleted, and the ones whose objects are
264
+ * gone some other way, so its next pass in the window goes on with the ids it has not reached and
265
+ * never back to what is gone. A licence left with nothing is no licence, so the next destructive
266
+ * pass is refused and the scope is dry-run again. Writes only when one of `ids` is held.
267
+ */
268
+ export function consumeOrphanSweepLicence(sql, ids) {
269
+ if (ids.length === 0 || !hasLicenceTable(sql))
270
+ return;
271
+ const json = JSON.stringify(ids);
272
+ const held = sql.query(`SELECT 1 AS hit FROM _substrat_attachment_sweep_licence WHERE id IN (SELECT value FROM json_each(?)) LIMIT 1`, [json]);
273
+ if (held.length === 0)
274
+ return;
275
+ sql.exec(`DELETE FROM _substrat_attachment_sweep_licence WHERE id IN (SELECT value FROM json_each(?))`, [json]);
276
+ }
277
+ /** Drop the whole licence: every dump load does, so a restored, carried or forked store starts with none. */
278
+ export function clearOrphanSweepLicence(sql) {
279
+ if (hasLicenceTable(sql))
280
+ sql.exec(`DELETE FROM _substrat_attachment_sweep_licence`);
281
+ }
282
+ /**
283
+ * The copy-ledger states (#1722) in which another script's copy of a scope can no longer hold a
284
+ * row the serving copy lacks, nor discover one. An ALLOWLIST: every other state, a future one
285
+ * included, holds the pass. Only `done` qualifies: the copy was wiped (or found already wiped),
286
+ * reaped with its scope, or is the move's destination, the copy that serves. Every other state can
287
+ * still hold, or still turn up, a write the serving copy never saw:
288
+ *
289
+ * - `pending`: a move in flight; its outcome is not known yet.
290
+ * - `eligible`: a carry's source awaiting its fenced wipe. The wipe is what discovers a write that
291
+ * landed after the export and turns the copy `kept`, and it is best effort, so until it has run
292
+ * the copy may hold an upload whose bytes share this scope's prefix.
293
+ * - `kept`: such a write was found; staff resolve it.
294
+ * - `retained`: a copy kept on purpose (a bind that did not land, a copy from before the ledger),
295
+ * whose contents nobody checked against the serving copy.
296
+ */
297
+ export const SETTLED_COPY_STATES = ['done'];
298
+ /** The refusal of a pass over a scope with a copy not yet settled, or null when there is none. */
299
+ export function unsettledCopyRefusal(scopeId, copies) {
300
+ const unsettled = copies.filter((c) => !SETTLED_COPY_STATES.includes(c.state));
301
+ if (unsettled.length === 0)
302
+ return null;
303
+ return substratError('conflict', `refused: scope ${scopeId} has a copy in another script that may hold rows this copy lacks ` +
304
+ `(${unsettled.map((c) => `${c.scriptRef}: ${c.state}`).join(', ')}). Settle it (#1722) before its bytes are swept`);
305
+ }
306
+ /**
307
+ * What a pass throws when the deployment holds no attachment store for the tenant, yet the scope
308
+ * holds attachment rows (#2314): its store binding is missing, which is a failure to fix, never the
309
+ * `bucket: 'none'` of a scope that keeps no files.
310
+ */
311
+ export function missingAttachmentStore(tenantId, scopeId) {
312
+ return new Error(`scope ${scopeId} holds attachment rows, but this deployment holds no attachment store for tenant ${tenantId}: ` +
313
+ 'its store binding is missing. Re-push the vertical so the store is declared, then sweep again');
314
+ }
315
+ /** The blob key of a scope's restore fence (guard 3). Outside `att/`, so no pass ever lists it. */
316
+ export function attachmentSweepFenceKey(scopeId) {
317
+ return `scope/${scopeId}/sweep-fence`;
318
+ }
319
+ /**
320
+ * Record that a scope's state is about to be replaced (a PITR rewind, a dump restore), so no pass
321
+ * deletes its bytes for `ORPHAN_SWEEP_FENCE_MS`. Call it BEFORE the replacement and let a throw
322
+ * refuse the replacement: a rewind whose fence was not written could hand the sweep a state that
323
+ * lacks rows a later restore brings back.
324
+ */
325
+ export async function fenceAttachmentSweep(store, scopeId, now) {
326
+ await store.put(attachmentSweepFenceKey(scopeId), new TextEncoder().encode(now), { contentType: 'text/plain' });
327
+ }
328
+ /**
329
+ * Until when the fence holds a scope's bytes, or null when there is no fence. A fence that
330
+ * cannot be read or parsed is held to hold forever: the sweep fails closed.
331
+ */
332
+ async function fencedUntil(store, scopeId, fenceMs) {
333
+ const fence = await store.get(attachmentSweepFenceKey(scopeId));
334
+ if (!fence)
335
+ return null;
336
+ const at = Date.parse(new TextDecoder().decode(fence.body));
337
+ if (!Number.isFinite(at))
338
+ return Number.POSITIVE_INFINITY;
339
+ return at + fenceMs;
340
+ }
341
+ /**
342
+ * Why a pass may not run in a script deployed as `deployed`, when the platform routed it to the
343
+ * script running `expected` (#2314), or null. A script deployed as another version refuses any
344
+ * pass. A deployed script that carries no version identity cannot be told it is the one routed to,
345
+ * so it refuses a destructive pass and lets a dry run proceed, since a dry run deletes nothing.
346
+ * No `expected` (a host the platform does not route to, a self-host) asks nothing.
347
+ */
348
+ export function orphanSweepVersionRefusal(deployed, expected, destructive) {
349
+ if (expected === undefined)
350
+ return null;
351
+ if (deployed === null) {
352
+ return destructive
353
+ ? substratError('conflict', `refused: this script carries no version identity, so a destructive pass cannot be told it reached ` +
354
+ `the script running version ${expected}, the one the platform routed it to`)
355
+ : null;
356
+ }
357
+ if (deployed !== expected) {
358
+ return substratError('conflict', `refused: this script runs version ${deployed}, not ${expected}, the one the platform routed this pass to`);
359
+ }
360
+ return null;
361
+ }
362
+ /** The smallest grace a caller may ask for: an upload is one request, never a day. */
363
+ const MIN_GRACE_MS = 86_400_000;
364
+ /** The smallest fence a caller may ask for: the PITR window plus a day. */
365
+ const MIN_FENCE_MS = 31 * 86_400_000;
366
+ /**
367
+ * One pass over one scope's attachment bytes. The host supplies the store and the scope side;
368
+ * this decides, in this order: the fence, the candidates by age, the condemnation in batches,
369
+ * the fence again, the deletes. Every failure before a delete leaves every object in place, and a
370
+ * delete that fails leaves its tombstone, so the next pass finishes it.
371
+ */
372
+ export async function sweepOrphanAttachments(store, scopeId, scope, opts) {
373
+ const nowMs = Date.parse(opts.now);
374
+ if (!Number.isFinite(nowMs))
375
+ throw new RangeError(`not an instant: ${opts.now}`);
376
+ const graceMs = opts.graceMs ?? ORPHAN_SWEEP_GRACE_MS;
377
+ const fenceMs = opts.fenceMs ?? ORPHAN_SWEEP_FENCE_MS;
378
+ const limit = opts.limit ?? ORPHAN_SWEEP_LIMIT;
379
+ if (!(graceMs >= MIN_GRACE_MS))
380
+ throw new RangeError(`graceMs must be at least ${MIN_GRACE_MS}, got ${graceMs}`);
381
+ if (!(fenceMs >= MIN_FENCE_MS))
382
+ throw new RangeError(`fenceMs must be at least ${MIN_FENCE_MS}, got ${fenceMs}`);
383
+ if (!Number.isSafeInteger(limit) || limit < 0)
384
+ throw new RangeError(`limit must be a non-negative integer, got ${limit}`);
385
+ const dryRun = opts.dryRun === true;
386
+ const report = { listed: 0, candidates: 0, orphans: 0, deleted: 0, keys: [], dryRun };
387
+ const fenced = async () => {
388
+ const until = await fencedUntil(store, scopeId, fenceMs);
389
+ if (until === null || until <= nowMs)
390
+ return false;
391
+ report.fencedUntil = Number.isFinite(until) ? new Date(until).toISOString() : 'never';
392
+ return true;
393
+ };
394
+ if (await fenced())
395
+ return report;
396
+ const prefix = attachmentBlobKey(scopeId, '');
397
+ const keys = await store.list(prefix);
398
+ report.listed = keys.length;
399
+ const cutoff = nowMs - graceMs;
400
+ const candidates = [];
401
+ for (const key of keys) {
402
+ const id = key.slice(prefix.length);
403
+ // Only a key this platform wrote for an attachment is judged: anything else is left alone.
404
+ if (key !== attachmentBlobKey(scopeId, id))
405
+ continue;
406
+ let minted;
407
+ try {
408
+ minted = ulidTime(id);
409
+ }
410
+ catch {
411
+ continue;
412
+ }
413
+ if (minted <= cutoff)
414
+ candidates.push(id);
415
+ }
416
+ report.candidates = candidates.length;
417
+ // Every id this scope's prefix still holds, whatever its age: a licensed id not among them is gone.
418
+ const present = new Set(keys.map((key) => key.slice(prefix.length)));
419
+ const orphans = [];
420
+ for (let i = 0; i < candidates.length && orphans.length < limit; i += ORPHAN_CONDEMN_BATCH) {
421
+ const batch = candidates.slice(i, i + ORPHAN_CONDEMN_BATCH);
422
+ const judged = dryRun ? await scope.rowless(batch) : await scope.condemn(batch, opts.now);
423
+ // The scope may only ever answer with ids it was asked about.
424
+ const asked = new Set(batch);
425
+ for (const id of judged)
426
+ if (asked.has(id))
427
+ orphans.push(id);
428
+ }
429
+ report.orphans = orphans.length;
430
+ const doomed = orphans.slice(0, limit);
431
+ if (dryRun) {
432
+ report.keys = doomed.map((id) => attachmentBlobKey(scopeId, id));
433
+ // The licence a destructive pass needs (#2350): what this run listed, nothing more.
434
+ await scope.license(doomed, opts.now);
435
+ return report;
436
+ }
437
+ /**
438
+ * Bring the licence up to date once the pass is done (#2350): take out the ids it deleted, so a
439
+ * backlog past `limit` drains over the next passes in the window, and every licensed id whose
440
+ * object is gone some other way, so a licence that lists nothing deletable does not hold the
441
+ * scope away from its next dry run until it expires. Taking ids out only narrows the licence.
442
+ *
443
+ * The bytes may be gone by now, so a failure here (the scope suspended or carried away mid-pass)
444
+ * must not cost the report: it is the only record of which keys were deleted. A licence row left
445
+ * for a gone id licenses nothing a pass could delete.
446
+ */
447
+ const settleLicence = async (gone) => {
448
+ try {
449
+ const stale = (await scope.licensed()).filter((id) => !present.has(id));
450
+ const drop = [...gone, ...stale];
451
+ if (drop.length > 0)
452
+ await scope.consume(drop);
453
+ }
454
+ catch (err) {
455
+ report.consumeFailed = true;
456
+ console.error(`orphan-attachment sweep: scope ${scopeId} deleted ${report.deleted} object(s) but could not bring its ` +
457
+ `dry-run licence up to date (${err instanceof Error ? err.name : 'Error'}); the report names the deleted keys`);
458
+ }
459
+ };
460
+ if (doomed.length === 0) {
461
+ await settleLicence([]);
462
+ return report;
463
+ }
464
+ // Guard 3 again, after the condemnation: a rewind that armed mid-pass wrote its fence first.
465
+ if (await fenced())
466
+ return report;
467
+ const gone = [];
468
+ for (const id of doomed) {
469
+ const key = attachmentBlobKey(scopeId, id);
470
+ await store.delete(key);
471
+ gone.push(id);
472
+ report.keys.push(key);
473
+ report.deleted++;
474
+ }
475
+ // A delete that throws ends the pass before this, so every id stays licensed and condemned, and
476
+ // the next pass deletes them again (a delete of what is gone is a no-op).
477
+ await settleLicence(gone);
478
+ return report;
479
+ }
480
+ //# sourceMappingURL=attachment-sweep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachment-sweep.js","sourceRoot":"","sources":["../src/attachment-sweep.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { AccessLogFilter, AttachmentSearchOptions, AttachmentUploadInput, UploadLinkRedeemInput, AuditLogFilter, BlobStoreProvisionInput, BlobStoreRecord, ConsumerHandler, ImportHandler, ExecutorDeadLetter, ExecutorDrainReport, ExecutorHandler, ExecutorOutcome, ExecutorRetryPolicy, ExecutorScope, MembershipChange, MembershipChangeResult, ConnectorConnection, ScopedConnectorConnection, ConnectorContext, ConnectorHandler, ConnectorOptions, ConnectorRequestInit, ConnectorResponse, Clock, FetchLike, GuardPredicate, HostAdmin, MigrateScopeOutcome, ReconcileOutcome, MigrationFrontier, ModuleRegistration, ConsumersOf, EventContract, EventPayloadOf, EventTypeOf, TypedConsumerHandler, TypedConsumers, OpenedAttachment, OperationContext, PageParams, OperationHandler, AppliedMigration, OpsFailureFilter, OpsFailureInput, KeptCopyResolution, ScopeScriptCopy, ScopeCopyRole, ScopeCopyMoveConfirmation, IssueFilter, TelemetryPruneReport, SweepRunFilter, SweepRunInput, ProvisionScopeInput, RoleFilter, FreshnessRegistration, FreshnessReport, LiveChange, LiveFrame, LiveNudge, LiveReadSurface, LiveUpgradeRequest, OperationEntitlement, ScheduleRegistration, ScheduleRunReport, ScopeAttachments, ScopedSql, ScopeFilter, ScopeHost, ScopeStub, ScopeStubOptions, InvokeOptions, EmittedEvent, EmittedReport, SqlMigration, SqlValue, TenantBlobStore, TenantRelationalStore, TenantStoreProvisionInput, TenantStoreRecord, } from './scope-host.js';
1
+ export type { AccessLogFilter, AttachmentSearchOptions, AttachmentUploadInput, UploadLinkRedeemInput, RedeemedUploadLink, AuditLogFilter, BlobStoreProvisionInput, BlobStoreRecord, ConsumerHandler, ImportHandler, ExecutorDeadLetter, ExecutorDrainReport, ExecutorHandler, ExecutorOutcome, ExecutorRetryPolicy, ExecutorScope, MembershipChange, MembershipChangeResult, ConnectorConnection, ScopedConnectorConnection, ConnectorContext, ConnectorHandler, ConnectorOptions, ConnectorRequestInit, ConnectorResponse, Clock, FetchLike, GuardPredicate, HostAdmin, MigrateScopeOutcome, ReconcileOutcome, MigrationFrontier, ModuleRegistration, ConsumersOf, EventContract, EventPayloadOf, EventTypeOf, TypedConsumerHandler, TypedConsumers, OpenedAttachment, OperationContext, PageParams, OperationHandler, AppliedMigration, OpsFailureFilter, OpsFailureInput, KeptCopyResolution, ScopeScriptCopy, ScopeCopyRole, ScopeCopyMoveConfirmation, IssueFilter, TelemetryPruneReport, SweepRunFilter, SweepRunInput, ProvisionScopeInput, RoleFilter, FreshnessRegistration, FreshnessReport, LiveChange, LiveFrame, LiveNudge, LiveReadSurface, LiveUpgradeRequest, OperationEntitlement, ScheduleRegistration, ScheduleRunReport, ScopeAttachments, ScopedSql, ScopeFilter, ScopeHost, ScopeStub, ScopeStubOptions, InvokeOptions, EmittedEvent, EmittedReport, SqlMigration, SqlValue, TenantBlobStore, TenantRelationalStore, TenantStoreProvisionInput, TenantStoreRecord, } from './scope-host.js';
2
2
  export { GRANT_CHILDREN_INDEX_DDL } from './grant-scoped-read.js';
3
3
  export { callReservedStoreOperation, callScopeStore, observeStoreCall, replayStoreErasures, scopeStoreLifecycleOf, scopeStoreRunnersOf, type ReservedStoreResult, type ScopeStoreAddress, type ScopeStoreHostOptions, type ScopeStoreLifecycle, type ScopeStoreRunner, type ScopeStoreStubOptions, type StoreCallReport, type StoreErasureReplay, type StoreStub, } from './scope-store.js';
4
4
  export type { GrantedEntitiesPage, GrantedEntityIdsMayRepeatPage } from './grant-scoped-read.js';
@@ -25,7 +25,7 @@ export { actorOf, asPrincipal, unknownRoleError } from './permission-checker.js'
25
25
  export type { Holdings, PermissionChecker } from './permission-checker.js';
26
26
  export { ancestorsWithin, createTupleEvaluator, grantedEntitiesForContext, memberAddedAudit, orgChangeBound, reachesWithin, tenantCoverage, } from './permission-eval.js';
27
27
  export type { PermissionTupleReader, TenantDirectoryReader, PermissionTupleRow, ScopeTupleReader, } from './permission-eval.js';
28
- export { UPLOAD_LINK_DDL, UPLOAD_LINK_GONE, UPLOAD_LINK_TOO_LARGE, UPLOAD_LINK_TYPE_REFUSED, UPLOAD_LINK_WRONG_TARGET, createAttachmentVerbs, inspectUploadLink, redeemUploadLinkInTransaction, uploadContentTypeAllowed, } from './upload-link.js';
28
+ export { ACTIVE_CONTENT_TYPES, UPLOAD_LINK_DDL, UPLOAD_LINK_GONE, UPLOAD_LINK_REPLAY_MISMATCH, UPLOAD_LINK_TOO_LARGE, UPLOAD_LINK_TYPE_REFUSED, UPLOAD_LINK_WRONG_TARGET, createAttachmentVerbs, inspectUploadLink, isActiveContentType, isMediaTypeEssence, normalizeUploadMediaType, redeemOrReplayUploadLink, redeemUploadLinkInTransaction, uploadContentTypeAllowed, } from './upload-link.js';
29
29
  export type { AttachmentVerbDeps, AttachmentVerbs, UploadLinkInspection, UploadTarget, } from './upload-link.js';
30
30
  export { CAPABILITY_DDL, CAPABILITY_EXCHANGE_OPERATION, CAPABILITY_SESSION_PRUNE_BATCH, WITHHELD_SECRET, assertNoSecret, capabilityAttachmentWriteRefused, capabilityByIdQuery, capabilityTokenHash, checkBecomeInput, createCapabilityVerbs, exchangeCapability, guardSecrets, mintBecomeCapability, mintCapabilitySecret, readCapabilityPage, plausibleSessionToken, plausibleCapabilitySecret, redactSecrets, redactSecretText, resolveCapabilitySession, revokeCapabilityAsPlatform, CAPABILITY_BECOME_MINT_OPERATION, becomeMintCheck, readBecomeLinkStates, assertBecomeLinkStateIds, mintBecomeCapabilityAsPrincipal, revokeBecomeCapabilityAsPrincipal, allowedFor, } from './capability.js';
31
31
  export type { CapabilityGrantView, CapabilityRow, CapabilityVerbDeps, CapabilityVerbs, } from './capability.js';
@@ -45,6 +45,8 @@ export { createAtomic } from './sub-transaction.js';
45
45
  export type { RunSub, AtomicMarks } from './sub-transaction.js';
46
46
  export { DEFAULT_SEARCH_LIMIT, MAX_SEARCH_LIMIT, NotSearchable, SearchTermTooShort, isSearchIndexTable, searchIndexMigrations, searchIndexPlans, searchLimit, searchMatchExpression, searchQuery, } from './search-index.js';
47
47
  export type { SearchHit, SearchIndexPlan, SearchOptions, SearchTokenizer, SearchableDeclaration, } from './search-index.js';
48
+ export { ATTACHMENT_CONDEMNED, ATTACHMENT_CONDEMNED_DDL, ATTACHMENT_SWEEP_LICENCE_DDL, ATTACHMENT_SWEEP_LICENCE_TABLE, ORPHAN_SWEEP_LICENCE_CAP, ORPHAN_SWEEP_UNLICENSED, clearOrphanSweepLicence, condemnLicensedOrphanAttachments, consumeOrphanSweepLicence, licensedOrphanIds, orphanSweepLicensedAt, recordOrphanSweepLicence, refuseUnlicensedOrphanSweep, ORPHAN_SWEEP_FENCE_MS, ORPHAN_SWEEP_GRACE_MS, SETTLED_COPY_STATES, attachmentSweepFenceKey, condemnOrphanAttachments, fenceAttachmentSweep, refuseCondemnedAttachment, refuseCondemnedInDump, rowlessAttachmentIds, sweepOrphanAttachments, unsettledCopyRefusal, missingAttachmentStore, orphanSweepVersionRefusal, } from './attachment-sweep.js';
49
+ export type { OrphanSweepOptions, OrphanSweepReport, OrphanSweepRequest, OrphanSweepScope } from './attachment-sweep.js';
48
50
  export { ATTACHMENT_SEARCH_OWNER_MAX, ATTACHMENT_SEARCH_OWNERS_SQL, ATTACHMENT_SEARCH_SQL, ATTACHMENT_SEARCH_TOO_MANY_OWNERS, ATTACHMENT_TEXT_BACKFILL_BATCH, ATTACHMENT_TEXT_BACKFILL_JOB, ATTACHMENT_TEXT_DDL, ATTACHMENT_TEXT_JOB, ATTACHMENT_TEXT_MODULE, assertJobRegistrable, attachmentRecordOfRow, attachmentTextBackfillJob, attachmentTextJob, enqueueAttachmentText, kernelJobFor, queueAttachmentTextBackfill, readAttachmentText, reconcileAttachmentText, recordAttachmentText, searchAttachments, startAttachmentTextBackfill, } from './attachment-text.js';
49
51
  export type { AttachmentRowShape, AttachmentTextBackfillBatch, KernelJobHandlers, AttachmentSearchGate, AttachmentTextSource, AttachmentTextState, AttachmentTextStatus, } from './attachment-text.js';
50
52
  export { DEFAULT_ATTACHMENT_TEXT_BOUNDS, EXTRACTION_STRIDE, assertAttachmentExtractors, chooseAttachmentExtractor, resolveAttachmentTextBounds, runAttachmentExtractor, } from './attachment-extractor.js';
@@ -52,7 +54,7 @@ export type { AttachmentExtractor, AttachmentExtractorInput, AttachmentExtractor
52
54
  export { CursorMismatch, FilterNotDeclared, NotListable, SortNotDeclared, cursorOf, listIndexMigrations, listIndexPlans, listQuery, } from './list-index.js';
53
55
  export type { ComposedListQuery, ListDeclaration, ListIndexPlan, ListQueryParams, } from './list-index.js';
54
56
  export { moduleMigrations } from './module-migrations.js';
55
- export { MIGRATION_DIGEST_FENCE_DDL, MIGRATION_DIGEST_MARK_LEGACY, assertJournalDumpCoherent, assertMigrationSql, migrationDigest, migrationDivergence, migrationFailedError, migrationSteps, planMigrations, type MigrationPlan, type MigrationStep, type PendingMigration, } from './migration-digest.js';
57
+ export { MIGRATION_DIGEST_FENCE_DDL, MIGRATION_DIGEST_MARK_LEGACY, MIGRATIONS_DIGEST_BINDING, assertJournalDumpCoherent, assertMigrationSql, isDerivedMigrationVersion, migrationDigest, migrationDivergence, migrationFailedError, migrationSetDigest, migrationSetDivergence, migrationSteps, planMigrations, type MigrationPlan, type MigrationStep, type PendingMigration, } from './migration-digest.js';
56
58
  export { frozenClock, manualClock } from './clock.js';
57
59
  export type { ManualClock } from './clock.js';
58
60
  export { createUlid, ulid, ulidTime, type UlidMint } from './ulid.js';
@@ -70,6 +72,9 @@ export { eraseSubjectFromModules, isModuleErasureCounts, moduleErasurePlan, modu
70
72
  export { TABLE_OWNERS_DDL, assertMigrationLeavesLedgerAlone, recordOwnershipSteps, runMigrationStatements, } from './table-ownership.js';
71
73
  export type { OwnerStore, TableChange, TableStatement, TableStep } from './table-ownership.js';
72
74
  export { blankSqlComments, executableSqlStatements, splitSqlStatements } from './sql-statements.js';
75
+ export { fencedTables } from './migration-fence.js';
76
+ export { MIGRATION_REPLAY_API, replayMigrations } from './migration-replay.js';
77
+ export type { ReplayDatabase, ReplayModule, ReplayOutcome } from './migration-replay.js';
73
78
  export type { ModuleErasureCounts, ModuleErasurePlan, OnSubjectErased, SubjectErasureContext, } from './module-erasure.js';
74
79
  export { FINDINGS_DDL, FINDING_RETENTION_DAYS, createFindingRule, findingOfOpsFailure, findingOfSweepRun, pruneFindings, listFindingRules, listFindings, observeFinding, revokeFindingRule, setFindingStatus, type FindingPruneReport, type FindingAudit, type FindingChange, type FindingObservation, } from './findings.js';
75
80
  export { effectiveRoleGrantQuery, seatScopeTuple } from './scope-tuple-seat.js';
@@ -90,11 +95,11 @@ export type { InUnitReport, SwitchKind, SwitchRecordPrior, SwitchRecordWrite, Sy
90
95
  export { ADMIN_LOG_INDEX_DDL, ADMIN_LOG_INDEXES_SQL } from './admin-log-ddl.js';
91
96
  export { AUDITED_CHANGE_ACTIONS, SETTLE_INTENT_SQL, SETTLE_OUTCOME_SQL, settleOutcomeParamsOf, operationKeyOf, auditedOperationsSql, effectiveOutcomes, isSupersededOutcome, readAuditedOperations, unknownOutcomeOf, UNRECORDED_OUTCOME_LOG, recordAuditOutcome, auditedCapabilityRevoke, auditWarningOf, } from './audit-outcome.js';
92
97
  export type { AuditLogError, AuditedChangeAction, AuditedOperationRef, AuditedOperationRow, AuditedOperationSqlRow, AuditedPhase, EffectiveOutcome, OperationKey, SettleIntentRow, UnknownOutcome, } from './audit-outcome.js';
93
- export { VERSION_MIGRATIONS_DDL, splitManifestMigrations, splitVersionMigrationsBatch, versionMigrationsOf, versionsAwaitSplit, writeVersionMigrations, } from './version-migrations.js';
94
- export { VERTICAL_SCOPE_STORES_DDL, forgetVerticalScopeStores, indexVersionScopeStores, kindDeclarationsOf, verticalScopeStoresOf, type VerticalScopeStore, } from './vertical-scope-stores.js';
95
- export { STORE_KIND_RELEASES_DDL, beginStoreKindRolloutIn, finishStoreKindRolloutIn, holdStoreKindIn, recordStoreKindReleaseIn, releaseStoreKindHoldIn, storeKindReleaseIn, storeKindStateIn, type StoreKindHoldMode, type StoreKindReleaseRecord, type StoreKindRolloutAction, type StoreKindRolloutRecord, type StoreKindRolloutState, type StoreKindState, } from './store-kind-releases.js';
98
+ export { VERSION_MIGRATIONS_DDL, MIGRATION_DIGEST_MISMATCH_REASON, deriveMigrationDigestsBatch, deriveVersionMigrationDigest, derivedMigrationDigest, migrationChangeRefusal, publishedMigrationDigest, versionsAwaitDigest, splitManifestMigrations, splitVersionMigrationsBatch, versionMigrationsOf, versionsAwaitSplit, writeVersionMigrations, } from './version-migrations.js';
99
+ export { VERTICAL_SCOPE_STORES_DDL, forgetVerticalScopeStores, indexVersionScopeStores, driverDeclarationsOf, verticalScopeStoresOf, type VerticalScopeStore, } from './vertical-scope-stores.js';
100
+ export { STORE_DRIVER_RELEASES_DDL, advanceStoreDriverRebuildIn, beginStoreDriverRolloutIn, claimStoreDriverRebuildIn, finishStoreDriverRolloutIn, holdStoreDriverIn, legacyStoreDriverConflict, legacyStoreDriverRenames, recordStoreDriverReleaseIn, releaseStoreDriverHoldIn, storeDriverRebuildIn, storeDriverReleaseIn, storeDriverStateIn, storeDriverRolloutPendingIn, storeDriverRolloutWalkedIn, type StoreDriverHoldMode, type StoreDriverRebuildFailure, type StoreDriverRebuildRecord, type StoreDriverReleaseRecord, type StoreDriverRolloutAction, type StoreDriverRolloutRecord, type StoreDriverRolloutState, type StoreDriverRolloutTarget, type StoreDriverState, } from './store-driver-releases.js';
96
101
  export type { SplitManifest } from './version-migrations.js';
97
- export { admitPeer, collectPeers, peerGrantsStatus, peerSeats, peerSubjectRef, resolveVerticalInstanceFrom, switchPeer, } from './peer.js';
102
+ export { admitPeer, collectPeers, peerGrantsStatus, peerSeats, boundToPeerDeclarations, peerSubjectRef, resolveVerticalInstanceFrom, switchPeer, } from './peer.js';
98
103
  export { PEER_BINDINGS_DDL, isPeerInstanceOf, peerCallerAt, peerVerticalSpellings, resolvePeerInstanceFrom, samePeerVertical } from './peer.js';
99
104
  export type { PeerDeclaration, PeerDeclarations, PeerGrantsRow, PeerSeat, PeerTenant, VerticalInstanceCandidate } from './peer.js';
100
105
  export { DENIAL_WINDOW_QUERY, denialListQuery, denialSummaryQuery, denialTotalsQuery, mapDenialRow, mapDenialSummaryBuckets, type DenialRow, type DenialBucketRow, type DenialOperationBucketRow, type DenialSummaryBuckets, type DenialWindowRow, } from './denial-query.js';
@@ -113,13 +118,14 @@ export type { OutboxEnvelopeRow, OutboxDrainRow, UndrainedEvents, UndrainedRead,
113
118
  export { IDEMPOTENCY_DDL, assertIdempotencyKey, idempotencyLookupQuery, idempotencyPruneStatement, idempotencyRecordStatement, idempotencyOptedOutMessage, replayFor, type IdempotencyRow, type IdempotentReplay, } from './idempotency.js';
114
119
  export { JOB_RUN_DDL, JOB_RUN_PATCH_SQL, JOB_RUN_CLAIM_SQL, JOB_RUN_RENEW_SQL, JOB_RUN_BEGIN_SQL, JOB_RUN_MISS_SQL, JOB_RUN_MISS_SETTLE_SQL, admissionMissOutcome, JOB_STEP_RECORD_SQL, JOB_LEASE_MS, JOB_LEASE_MIN_MS, JOB_ADMISSION_MISS_MAX, JOB_LEASE_TOO_SHORT_NOTE, admissionBackoffMs, JOB_LEASE_EXPIRED_NOTE, assertLeaseMs, jobRunListLimit, SYSTEM_DOOR_WAIT, JOB_DEFER_MS, JOB_RUN_DUE_AT, jobRunOf, runDueJobRuns, runJobPass, startJobRun, } from './job-run.js';
115
120
  export type { JobDueKey, JobDriveReport, JobHandler, JobPassContext, JobPassOutcome, JobPassResult, JobRun, JobRegistration, JobRunClaim, JobRunFilter, JobRunKey, JobRunPatch, JobRunRow, JobRunStatus, JobRunStore, JobStepRow, StartJobRunInput, } from './job-run.js';
116
- export { runPlatformSweep, startPlatformSweeper, StorageReadUnsupported, PROVISION_RECONCILE_BATCH, CROSS_VERTICAL_CONSUMERS_PER_PASS, runCrossVerticalFrom, registryImportCandidates, crossVerticalHealth, } from './platform-sweep.js';
121
+ export { runPlatformSweep, startPlatformSweeper, StorageReadUnsupported, PROVISION_RECONCILE_BATCH, CROSS_VERTICAL_CONSUMERS_PER_PASS, ORPHAN_ATTACHMENT_SWEEP_BATCH, ORPHAN_SWEEP_DRY_RUN_WINDOW_MS, runCrossVerticalFrom, registryImportCandidates, crossVerticalHealth, } from './platform-sweep.js';
117
122
  export { SCHEDULE_STATE_DDL, SCHEDULE_STATE_REBUILD, scheduleStateHasKind } from './schedule-state.js';
118
123
  export type { ScheduleSweepReport, ScheduleStateKind } from './schedule-state.js';
119
- export type { AccessLogSink, EventSink, EventDrainReport, EventDrainSkipped, PlatformRequestUnsettleable, AccessLogSweepReport, ConnectorSweeper, MigrationSweepReport, PlatformSweepOptions, PlatformSweepReport, PlatformSweeperHandle, StartPlatformSweeperOptions, StorageGaugeSweepOptions, StorageGaugeSweepReport, CandidatesHint, CrossVerticalEdge, CrossVerticalOptions, CrossVerticalReach, CrossVerticalReport, ProvisionReconcileReport, } from './platform-sweep.js';
124
+ export type { AccessLogSink, EventSink, EventDrainReport, EventDrainSkipped, PlatformRequestUnsettleable, AccessLogSweepReport, ConnectorSweeper, MigrationSweepReport, PlatformSweepOptions, PlatformSweepReport, PlatformSweeperHandle, StartPlatformSweeperOptions, StorageGaugeSweepOptions, StorageGaugeSweepReport, CandidatesHint, CrossVerticalEdge, CrossVerticalOptions, CrossVerticalReach, CrossVerticalReport, ProvisionReconcileReport, OrphanAttachmentOutcome, OrphanAttachmentSweepOptions, OrphanAttachmentSweepReport, } from './platform-sweep.js';
120
125
  export { isPrimaryScope, isPrimaryScopeRow, INERT_SCOPE_REASON, runningVersionOf } from './scope-identity.js';
121
126
  export type { ServingPointer } from './scope-identity.js';
122
127
  export { exportBreaksOf, exportBreakRefusal, isExportBreakRefusal, bindExportBreaksOf, scopeMoveOf, bindExportBreakRefusal, BIND_EXPORT_BREAK_REFUSAL, isBindExportBreakRefusal, } from './export-break.js';
128
+ export { promoteReviewDigest, assertPromoteReviewCurrent, promoteReviewRefusalOf, promoteRaceRefusal, rebindReviewDigest, assertRebindReviewCurrent, rebindReviewRefusalOf, rebindRaceRefusal, SERVES_NOW_CONDITION, SERVES_NOW_PREDICATE, servesNowParams, type ExpectedServing, type ReviewedVersion, } from './promote-review.js';
123
129
  export { PROVISIONED_VERSIONS_SQL, installsReconciledOf, installsReconciledNote, installReconciled, boundEverOf, boundShapesColumn, boundShapesOfColumn, provisionedVersionsParams, strandJudgmentsOf, } from './install-receipts.js';
124
130
  export type { InstallsReconciled, StrandJudgment } from './install-receipts.js';
125
131
  export { migrationProgress, } from './migration-progress.js';