@klum-db/lobby 0.3.0-pre.1 → 0.4.0-pre.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -30,6 +30,396 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
30
  ));
31
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
32
 
33
+ // src/portal/share-link.ts
34
+ var share_link_exports = {};
35
+ __export(share_link_exports, {
36
+ ShareLinkResolutionError: () => ShareLinkResolutionError,
37
+ buildShareLinkForClient: () => buildShareLinkForClient,
38
+ resolveShareLinkAgainstRegistry: () => resolveShareLinkAgainstRegistry
39
+ });
40
+ async function registryRows(registry) {
41
+ await registry.list();
42
+ return registry.query().toArray();
43
+ }
44
+ async function resolveShareLinkAgainstRegistry(link, registry) {
45
+ const parsed = typeof link === "string" || link instanceof URL ? (0, import_share_link.parseShareLink)(link) : link;
46
+ const rows = await registryRows(registry);
47
+ const row = rows.find((r) => r.handle === parsed.vaultHandle);
48
+ if (!row) {
49
+ throw new ShareLinkResolutionError(
50
+ "UNKNOWN_VAULT_HANDLE",
51
+ `share link addresses vault handle "${parsed.vaultHandle}", which is not registered in this fleet.`
52
+ );
53
+ }
54
+ return {
55
+ link: parsed,
56
+ row,
57
+ consoleRoute: {
58
+ vaultId: row.vaultId,
59
+ collection: parsed.collection,
60
+ recordId: parsed.recordId,
61
+ ...parsed.period !== void 0 ? { period: parsed.period } : {},
62
+ ...parsed.version !== void 0 ? { version: parsed.version } : {}
63
+ }
64
+ };
65
+ }
66
+ async function buildShareLinkForClient(opts, registry) {
67
+ const rows = await registryRows(registry);
68
+ const row = rows.find((r) => r.partitionKey === opts.client && r.group === opts.group);
69
+ if (!row) {
70
+ throw new ShareLinkResolutionError(
71
+ "UNKNOWN_CLIENT",
72
+ `client "${opts.client}" is not registered in group "${opts.group}".`
73
+ );
74
+ }
75
+ if (row.handle === void 0) {
76
+ throw new ShareLinkResolutionError(
77
+ "NO_PORTAL_HANDLE",
78
+ `client "${opts.client}" has no portal vault handle \u2014 provision the portal before minting share links.`
79
+ );
80
+ }
81
+ return (0, import_share_link.buildShareLink)(
82
+ {
83
+ vaultHandle: row.handle,
84
+ collection: opts.collection,
85
+ recordId: opts.record,
86
+ ...opts.period !== void 0 ? { period: opts.period } : {},
87
+ ...opts.version !== void 0 ? { version: opts.version } : {},
88
+ ...opts.grantToken !== void 0 ? { grantToken: opts.grantToken } : {}
89
+ },
90
+ opts.base
91
+ );
92
+ }
93
+ var import_share_link, ShareLinkResolutionError;
94
+ var init_share_link = __esm({
95
+ "src/portal/share-link.ts"() {
96
+ "use strict";
97
+ import_share_link = require("@noy-db/hub/share-link");
98
+ ShareLinkResolutionError = class extends Error {
99
+ constructor(code, message) {
100
+ super(message);
101
+ this.code = code;
102
+ }
103
+ code;
104
+ name = "ShareLinkResolutionError";
105
+ };
106
+ }
107
+ });
108
+
109
+ // src/federation/insight-auto-push.ts
110
+ var InsightAutoPush;
111
+ var init_insight_auto_push = __esm({
112
+ "src/federation/insight-auto-push.ts"() {
113
+ "use strict";
114
+ InsightAutoPush = class {
115
+ constructor(recompute, isSource, onError = (err, pk) => console.warn(`[klum-db] insight auto-push failed for shard "${pk}":`, err), debounceMs) {
116
+ this.recompute = recompute;
117
+ this.isSource = isSource;
118
+ this.onError = onError;
119
+ this.debounceMs = debounceMs;
120
+ }
121
+ recompute;
122
+ isSource;
123
+ onError;
124
+ debounceMs;
125
+ dirty = /* @__PURE__ */ new Set();
126
+ pending = null;
127
+ timer = null;
128
+ resolvePending = null;
129
+ flushing = false;
130
+ /** Called from a shard's onAfterWrite hook. Marks the shard dirty + schedules a flush. */
131
+ noteWrite(partitionKey, collection) {
132
+ if (!this.isSource(collection)) return;
133
+ this.dirty.add(partitionKey);
134
+ if (this.debounceMs !== void 0) this.scheduleDebounced();
135
+ else this.schedule();
136
+ }
137
+ /** Resolve once no flush is pending (drains rescheduled flushes too). */
138
+ async whenSettled() {
139
+ while (this.pending) await this.pending;
140
+ }
141
+ // ── microtask path (unchanged default) ──
142
+ schedule() {
143
+ if (this.pending) return;
144
+ this.pending = this.runFlush().finally(() => {
145
+ this.pending = null;
146
+ if (this.dirty.size > 0) this.schedule();
147
+ });
148
+ }
149
+ // ── debounce path (#13) ──
150
+ scheduleDebounced() {
151
+ if (!this.pending) this.pending = new Promise((r) => {
152
+ this.resolvePending = r;
153
+ });
154
+ if (this.timer) clearTimeout(this.timer);
155
+ this.timer = setTimeout(() => {
156
+ this.timer = null;
157
+ if (this.flushing) return;
158
+ this.flushing = true;
159
+ void this.runFlush().finally(() => {
160
+ this.flushing = false;
161
+ if (this.dirty.size > 0) {
162
+ this.scheduleDebounced();
163
+ } else {
164
+ const resolve = this.resolvePending;
165
+ this.resolvePending = null;
166
+ this.pending = null;
167
+ resolve?.();
168
+ }
169
+ });
170
+ }, this.debounceMs);
171
+ }
172
+ async runFlush() {
173
+ await Promise.resolve();
174
+ const pks = [...this.dirty];
175
+ this.dirty.clear();
176
+ for (const pk of pks) {
177
+ try {
178
+ await this.recompute(pk);
179
+ } catch (err) {
180
+ this.onError(err, pk);
181
+ }
182
+ }
183
+ }
184
+ };
185
+ }
186
+ });
187
+
188
+ // src/federation/read-model.ts
189
+ var read_model_exports = {};
190
+ __export(read_model_exports, {
191
+ PostureViolationError: () => PostureViolationError,
192
+ ReadModel: () => ReadModel,
193
+ deriveShardSummary: () => deriveShardSummary,
194
+ openReadModel: () => openReadModel
195
+ });
196
+ function deriveShardSummary(spec, records, row) {
197
+ const ctx = {
198
+ vaultId: row.vaultId,
199
+ partitionKey: row.partitionKey,
200
+ schemaVersion: row.schemaVersion
201
+ };
202
+ return spec.derive(records, ctx);
203
+ }
204
+ function checkPosture(model, row, shard) {
205
+ const declared = new Set(model.posture.surface);
206
+ const undeclared = Object.keys(row).filter((k) => !declared.has(k) && !PROVENANCE_FIELDS.has(k));
207
+ if (undeclared.length > 0) throw new PostureViolationError(model.name, undeclared, shard);
208
+ }
209
+ async function openReadModel(group, opts) {
210
+ const target = opts.vault;
211
+ if (target === group.name || target.startsWith(`${group.name}--`)) {
212
+ throw new import_cargo.ValidationError(
213
+ `openReadModel: vault "${target}" is the "${group.name}" group itself or one of its shards \u2014 the read-model must live outside the group it composes.`
214
+ );
215
+ }
216
+ if (opts.models.length === 0) {
217
+ throw new import_cargo.ValidationError("openReadModel: at least one model is required.");
218
+ }
219
+ const seen = /* @__PURE__ */ new Set();
220
+ for (const m of opts.models) {
221
+ if (seen.has(m.name)) throw new import_cargo.ValidationError(`openReadModel: duplicate model name "${m.name}".`);
222
+ seen.add(m.name);
223
+ if (m.posture.surface.length === 0) {
224
+ throw new import_cargo.ValidationError(`openReadModel: model "${m.name}" declares an empty posture.surface.`);
225
+ }
226
+ }
227
+ const vault = await group.db.openVault(target);
228
+ const rm = new ReadModel(group, target, vault, opts.models, opts.shards);
229
+ const autoPush = opts.freshness?.autoPush;
230
+ if (autoPush) rm._armAutoPush(autoPush === true ? {} : autoPush);
231
+ return rm;
232
+ }
233
+ var import_cargo, PostureViolationError, PROVENANCE_FIELDS, ReadModel;
234
+ var init_read_model = __esm({
235
+ "src/federation/read-model.ts"() {
236
+ "use strict";
237
+ import_cargo = require("@noy-db/hub/cargo");
238
+ init_insight_auto_push();
239
+ PostureViolationError = class extends Error {
240
+ constructor(model, fields, shard) {
241
+ super(
242
+ `read-model "${model}": derive emitted undeclared field(s) [${fields.join(", ")}] for shard "${shard}" \u2014 every emitted field must be listed in posture.surface (fail closed; nothing was written).`
243
+ );
244
+ this.model = model;
245
+ this.fields = fields;
246
+ this.shard = shard;
247
+ }
248
+ model;
249
+ fields;
250
+ shard;
251
+ name = "PostureViolationError";
252
+ };
253
+ PROVENANCE_FIELDS = /* @__PURE__ */ new Set(["_shard", "_sourceId", "_sourceVersion"]);
254
+ ReadModel = class {
255
+ /** @internal */
256
+ constructor(group, vaultName, vault, models, shards) {
257
+ this.group = group;
258
+ this.vaultName = vaultName;
259
+ this.vault = vault;
260
+ this.models = models;
261
+ this.shards = shards;
262
+ }
263
+ group;
264
+ vaultName;
265
+ vault;
266
+ models;
267
+ shards;
268
+ /** @internal — auto-push controller; armed by {@link _armAutoPush}. */
269
+ autoPush;
270
+ /**
271
+ * @internal — wire the #13 controller (spec § 3). Trigger is the
272
+ * Noydb-level change stream filtered to this group's shard vaults
273
+ * (`<group>--<pk>`); the read-model vault can never match that prefix
274
+ * (guarded at open), so its own writes can't loop back. Recompute is
275
+ * a per-shard `refresh({ only })` — rollup re-reduces and mirror
276
+ * reconciles just that shard; other shards' rows are untouched
277
+ * (registered shards are preserved by the reconcile asymmetry).
278
+ */
279
+ _armAutoPush(cfg) {
280
+ const controller = new InsightAutoPush(
281
+ async (partitionKey) => {
282
+ await this.refresh({
283
+ only: [partitionKey],
284
+ ...cfg.minVersion !== void 0 ? { minVersion: cfg.minVersion } : {}
285
+ });
286
+ },
287
+ (collection) => this.models.some((m) => m.source === collection),
288
+ void 0,
289
+ cfg.debounceMs
290
+ );
291
+ this.autoPush = controller;
292
+ const prefix = `${this.group.name}--`;
293
+ this.group.db.on("change", (e) => {
294
+ if (!e.vault.startsWith(prefix)) return;
295
+ controller.noteWrite(e.vault.slice(prefix.length), e.collection);
296
+ });
297
+ }
298
+ /**
299
+ * Await any pending auto-push flush. Resolves immediately when
300
+ * `freshness.autoPush` is off or nothing is pending.
301
+ */
302
+ async whenSettled() {
303
+ if (this.autoPush) await this.autoPush.whenSettled();
304
+ }
305
+ /** An output collection of the read-model vault (keyed by model `name`). */
306
+ collection(model) {
307
+ return this.vault.collection(model);
308
+ }
309
+ /**
310
+ * Explicit refresh: for each model, read every eligible shard's
311
+ * `source`, posture-check, and write — rollup: one summary row per
312
+ * shard (id = partitionKey); mirror: one row per source record
313
+ * (id = `${shard}:${idOf(row)}`). Every row is stamped with `_shard`
314
+ * + `_sourceVersion` (mirror rows additionally `_sourceId`).
315
+ *
316
+ * Reconciliation (spec § 4): rows whose source record is gone, and
317
+ * all rows of a shard that LEFT the group, are deleted (`retracted`).
318
+ * Rows of a merely-unreachable shard are PRESERVED — offline is not
319
+ * departure; those shards land in `skippedVaults` instead.
320
+ */
321
+ async refresh(options = {}) {
322
+ const resolved = await this.group.resolveEligible({
323
+ ...options.minVersion !== void 0 ? { minVersion: options.minVersion } : {},
324
+ ...options.only !== void 0 ? { only: options.only } : {},
325
+ ...options.failFast !== void 0 ? { failFast: options.failFast } : {}
326
+ });
327
+ const eligible = this.shards ? resolved.eligible.filter((r) => this.shards(r)) : resolved.eligible;
328
+ const skipped = resolved.skipped;
329
+ let written = 0;
330
+ let retracted = 0;
331
+ for (const model of this.models) {
332
+ const results = await this.group.db.queryAcross(
333
+ eligible.map((r) => r.vaultId),
334
+ async (vault) => {
335
+ this.group.template.configure(vault);
336
+ const coll = vault.collection(model.source);
337
+ const records = await coll.list();
338
+ if (model.kind === "rollup") return records.map((record) => ({ record, id: "", version: 0 }));
339
+ return Promise.all(records.map(async (record) => {
340
+ const id = model.idOf(record);
341
+ const meta = await coll.getMetadata(id);
342
+ return { record, id, version: meta?.version ?? 0 };
343
+ }));
344
+ },
345
+ { create: false, ...options.concurrency !== void 0 ? { concurrency: options.concurrency } : {} }
346
+ );
347
+ const out = this.vault.collection(model.name);
348
+ const expected = /* @__PURE__ */ new Set();
349
+ const healthyShards = /* @__PURE__ */ new Set();
350
+ for (let i = 0; i < eligible.length; i++) {
351
+ const row = eligible[i];
352
+ const res = results[i];
353
+ if (!res || res.result === void 0) {
354
+ if (options.failFast && res?.error) throw res.error;
355
+ if (!skipped.some((s) => s.vaultId === row.vaultId)) {
356
+ skipped.push({ vaultId: row.vaultId, reason: "error", ...res?.error ? { error: res.error } : {} });
357
+ }
358
+ continue;
359
+ }
360
+ healthyShards.add(row.partitionKey);
361
+ if (model.kind === "rollup") {
362
+ const summary = deriveShardSummary(model, res.result.map((e) => e.record), row);
363
+ checkPosture(model, summary, row.partitionKey);
364
+ await out.put(row.partitionKey, {
365
+ ...summary,
366
+ _shard: row.partitionKey,
367
+ _sourceVersion: row.schemaVersion
368
+ });
369
+ expected.add(row.partitionKey);
370
+ written++;
371
+ } else {
372
+ for (const entry of res.result) {
373
+ checkPosture(model, entry.record, row.partitionKey);
374
+ const id = `${row.partitionKey}:${entry.id}`;
375
+ await out.put(id, {
376
+ ...entry.record,
377
+ _shard: row.partitionKey,
378
+ _sourceId: entry.id,
379
+ _sourceVersion: entry.version
380
+ });
381
+ expected.add(id);
382
+ written++;
383
+ }
384
+ }
385
+ }
386
+ retracted += await this.reconcile(out, model, expected, healthyShards);
387
+ }
388
+ return { written, retracted, skippedVaults: skipped };
389
+ }
390
+ /**
391
+ * Delete stale rows: id not in `expected` AND the row's shard is
392
+ * healthy this refresh (source record deleted), no longer in the
393
+ * group registry (shard departed), or outside the audience predicate
394
+ * (scope narrowed — out-of-scope data never lingers, spec § 5).
395
+ * Unreachable-but-registered in-scope shards are preserved.
396
+ */
397
+ async reconcile(out, model, expected, healthyShards) {
398
+ await this.group.registry.list();
399
+ const registryRows2 = new Map(
400
+ this.group.registry.query().toArray().filter((r) => r.group === this.group.name).map((r) => [r.partitionKey, r])
401
+ );
402
+ await out.list();
403
+ const rows = out.query().toArray();
404
+ let retracted = 0;
405
+ for (const row of rows) {
406
+ const shard = row["_shard"];
407
+ if (shard === void 0) continue;
408
+ const id = model.kind === "rollup" ? shard : `${shard}:${String(row["_sourceId"])}`;
409
+ if (expected.has(id)) continue;
410
+ const registryRow = registryRows2.get(shard);
411
+ const outOfScope = registryRow !== void 0 && this.shards !== void 0 && !this.shards(registryRow);
412
+ if (healthyShards.has(shard) || registryRow === void 0 || outOfScope) {
413
+ await out.delete(id);
414
+ retracted++;
415
+ }
416
+ }
417
+ return retracted;
418
+ }
419
+ };
420
+ }
421
+ });
422
+
33
423
  // src/bundle/uint32.ts
34
424
  function readUint32BE(bytes, offset) {
35
425
  return (bytes[offset] << 24 | bytes[offset + 1] << 16 | bytes[offset + 2] << 8 | bytes[offset + 3]) >>> 0;
@@ -72,6 +462,11 @@ function encodeMultiBundle(manifest, inner) {
72
462
  }
73
463
  return out;
74
464
  }
465
+ function hasPodMagic(bytes) {
466
+ if (bytes.length < import_pod.NOYDB_BUNDLE_MAGIC.length) return false;
467
+ for (let i = 0; i < import_pod.NOYDB_BUNDLE_MAGIC.length; i++) if (bytes[i] !== import_pod.NOYDB_BUNDLE_MAGIC[i]) return false;
468
+ return true;
469
+ }
75
470
  function hasMultiMagic(bytes) {
76
471
  if (bytes.length < NOYDB_MULTI_BUNDLE_MAGIC.length) return false;
77
472
  for (let i = 0; i < NOYDB_MULTI_BUNDLE_MAGIC.length; i++) if (bytes[i] !== NOYDB_MULTI_BUNDLE_MAGIC[i]) return false;
@@ -135,7 +530,7 @@ async function writeMultiVaultBundle(compartments, opts = {}) {
135
530
  handle: header.handle,
136
531
  exportedAt: c.exportedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
137
532
  innerBytes: innerBytes.length,
138
- innerSha256: await (0, import_cargo.sha256Hex)(innerBytes)
533
+ innerSha256: await (0, import_cargo2.sha256Hex)(innerBytes)
139
534
  };
140
535
  if (c.roleTag !== void 0) entry.roleTag = c.roleTag;
141
536
  if (c.disclose?.name !== void 0 && c.disclose.name !== false) {
@@ -148,7 +543,7 @@ async function writeMultiVaultBundle(compartments, opts = {}) {
148
543
  );
149
544
  }
150
545
  if (c.disclose?.publicEnvelope === true) {
151
- const env = (0, import_hub.readNoydbBundlePublicEnvelope)(innerBytes);
546
+ const env = (0, import_pod.readPodCover)(innerBytes);
152
547
  if (env !== void 0) entry.publicEnvelope = env;
153
548
  }
154
549
  const fence = await c.vault.schemaFenceState();
@@ -158,20 +553,20 @@ async function writeMultiVaultBundle(compartments, opts = {}) {
158
553
  }
159
554
  const manifest = {
160
555
  multiFormatVersion: NOYDB_MULTI_BUNDLE_VERSION,
161
- handle: opts.handle ?? (0, import_cargo.generateULID)(),
556
+ handle: opts.handle ?? (0, import_cargo2.generateULID)(),
162
557
  compartments: entries
163
558
  };
164
559
  return encodeMultiBundle(manifest, inner);
165
560
  }
166
561
  async function readNoydbBundleManifest(bytes) {
167
562
  if (hasMultiMagic(bytes)) return [...decodeMultiBundle(bytes).manifest.compartments];
168
- if ((0, import_hub.hasNoydbBundleMagic)(bytes)) {
563
+ if (hasPodMagic(bytes)) {
169
564
  const header = (0, import_pod.readPodHeader)(bytes);
170
- const env = (0, import_hub.readNoydbBundlePublicEnvelope)(bytes);
565
+ const env = (0, import_pod.readPodCover)(bytes);
171
566
  const entry = {
172
567
  handle: header.handle,
173
568
  innerBytes: bytes.length,
174
- innerSha256: await (0, import_cargo.sha256Hex)(bytes)
569
+ innerSha256: await (0, import_cargo2.sha256Hex)(bytes)
175
570
  };
176
571
  if (env !== void 0) entry.publicEnvelope = env;
177
572
  return [entry];
@@ -182,7 +577,7 @@ function readMultiVaultBundleCompartment(bytes, selector) {
182
577
  if (typeof selector === "number" && !Number.isInteger(selector)) {
183
578
  throw new Error(`readMultiVaultBundleCompartment: numeric selector must be an integer, got ${selector}.`);
184
579
  }
185
- if ((0, import_hub.hasNoydbBundleMagic)(bytes) && !hasMultiMagic(bytes)) {
580
+ if (hasPodMagic(bytes) && !hasMultiMagic(bytes)) {
186
581
  const header = (0, import_pod.readPodHeader)(bytes);
187
582
  if (selector === 0 || selector === header.handle) return bytes;
188
583
  throw new Error(`readMultiVaultBundleCompartment: single v1 bundle has only compartment "${header.handle}".`);
@@ -192,13 +587,12 @@ function readMultiVaultBundleCompartment(bytes, selector) {
192
587
  if (idx < 0 || idx >= inner.length) throw new Error(`readMultiVaultBundleCompartment: no compartment ${typeof selector === "number" ? `at index ${selector}` : `"${selector}"`}.`);
193
588
  return inner[idx];
194
589
  }
195
- var import_cargo, import_pod, import_hub, NOYDB_MULTI_BUNDLE_MAGIC, NOYDB_MULTI_BUNDLE_PREFIX_BYTES, NOYDB_MULTI_BUNDLE_VERSION;
590
+ var import_cargo2, import_pod, NOYDB_MULTI_BUNDLE_MAGIC, NOYDB_MULTI_BUNDLE_PREFIX_BYTES, NOYDB_MULTI_BUNDLE_VERSION;
196
591
  var init_multi_bundle = __esm({
197
592
  "src/bundle/multi-bundle.ts"() {
198
593
  "use strict";
199
- import_cargo = require("@noy-db/hub/cargo");
594
+ import_cargo2 = require("@noy-db/hub/cargo");
200
595
  import_pod = require("@noy-db/hub/pod");
201
- import_hub = require("@noy-db/hub");
202
596
  init_uint32();
203
597
  NOYDB_MULTI_BUNDLE_MAGIC = new Uint8Array([78, 68, 66, 77]);
204
598
  NOYDB_MULTI_BUNDLE_PREFIX_BYTES = 10;
@@ -271,7 +665,7 @@ async function walkCrossVaultClosure(openVault, opts) {
271
665
  for (const vaultName of batch) {
272
666
  const seeds = perVaultSeeds.get(vaultName);
273
667
  const v = await openVault(vaultName);
274
- const { closure } = await (0, import_bundle.walkClosure)(v, {
668
+ const { closure } = await (0, import_cargo3.walkClosure)(v, {
275
669
  seeds,
276
670
  ...opts.maxDepth !== void 0 ? { maxDepth: opts.maxDepth } : {}
277
671
  });
@@ -323,7 +717,7 @@ async function extractCrossVaultPartition(openVault, opts) {
323
717
  const sealIds = {};
324
718
  for (const [vaultName, seeds] of plan.perVaultSeeds) {
325
719
  const v = await openVault(vaultName);
326
- const { bundleBytes, transferKey, sealId } = await (0, import_bundle.extractPartition)(v, {
720
+ const { bundleBytes, transferKey, sealId } = await (0, import_cargo3.extractPartition)(v, {
327
721
  seeds,
328
722
  ...opts.maxDepth !== void 0 ? { maxDepth: opts.maxDepth } : {},
329
723
  carrySchemas: opts.carrySchemas ?? true,
@@ -336,7 +730,7 @@ async function extractCrossVaultPartition(openVault, opts) {
336
730
  handle: header.handle,
337
731
  exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
338
732
  innerBytes: bundleBytes.length,
339
- innerSha256: await (0, import_cargo2.sha256Hex)(bundleBytes)
733
+ innerSha256: await (0, import_cargo4.sha256Hex)(bundleBytes)
340
734
  };
341
735
  if (meta?.roleTag !== void 0) entry.roleTag = meta.roleTag;
342
736
  if (meta?.disclose?.name !== void 0 && meta.disclose.name !== false) {
@@ -355,7 +749,7 @@ async function extractCrossVaultPartition(openVault, opts) {
355
749
  }
356
750
  const manifest = {
357
751
  multiFormatVersion: NOYDB_MULTI_BUNDLE_VERSION,
358
- handle: (0, import_cargo2.generateULID)(),
752
+ handle: (0, import_cargo4.generateULID)(),
359
753
  compartments
360
754
  };
361
755
  return { bundle: encodeMultiBundle(manifest, inner), transferKeys, sealIds };
@@ -365,7 +759,7 @@ async function describeCrossVaultExtraction(openVault, opts) {
365
759
  const compartments = [];
366
760
  for (const [vaultName, seeds] of plan.perVaultSeeds) {
367
761
  const v = await openVault(vaultName);
368
- const preview = await (0, import_bundle.describeExtraction)(v, {
762
+ const preview = await (0, import_cargo3.describeExtraction)(v, {
369
763
  seeds,
370
764
  ...opts.maxDepth !== void 0 ? { maxDepth: opts.maxDepth } : {}
371
765
  });
@@ -373,13 +767,13 @@ async function describeCrossVaultExtraction(openVault, opts) {
373
767
  }
374
768
  return { compartments, dangling: plan.dangling };
375
769
  }
376
- var import_bundle, import_pod2, import_cargo2, CrossVaultDanglingRefError;
770
+ var import_cargo3, import_pod2, import_cargo4, CrossVaultDanglingRefError;
377
771
  var init_extract_cross_vault = __esm({
378
772
  "src/interchange/extract-cross-vault.ts"() {
379
773
  "use strict";
380
- import_bundle = require("@noy-db/hub/bundle");
774
+ import_cargo3 = require("@noy-db/hub/cargo");
381
775
  import_pod2 = require("@noy-db/hub/pod");
382
- import_cargo2 = require("@noy-db/hub/cargo");
776
+ import_cargo4 = require("@noy-db/hub/cargo");
383
777
  init_multi_bundle();
384
778
  CrossVaultDanglingRefError = class extends Error {
385
779
  constructor(dangling) {
@@ -465,7 +859,7 @@ async function mergeDecryptedRecords(receiver, decrypted, opts) {
465
859
  incomingSource.set(coll, srcMap);
466
860
  incomingSourceTs.set(coll, stsMap);
467
861
  }
468
- const diff = await (0, import_cargo3.diffVault)(receiver, candidate);
862
+ const diff = await (0, import_cargo5.diffVault)(receiver, candidate);
469
863
  const byCollection = {};
470
864
  const conflicts = [];
471
865
  const writes = [];
@@ -581,15 +975,15 @@ async function mergeDecryptedRecords(receiver, decrypted, opts) {
581
975
  };
582
976
  }
583
977
  async function mergeCompartment(receiver, compartmentBytes, opts) {
584
- const incoming = await (0, import_bundle2.decryptExtractedPartition)(compartmentBytes, opts.transferKey);
978
+ const incoming = await (0, import_cargo6.decryptExtractedPartition)(compartmentBytes, opts.transferKey);
585
979
  return mergeDecryptedRecords(receiver, incoming, opts);
586
980
  }
587
- var import_cargo3, import_bundle2, FieldLevelDeferredError;
981
+ var import_cargo5, import_cargo6, FieldLevelDeferredError;
588
982
  var init_merge_compartment = __esm({
589
983
  "src/interchange/merge-compartment.ts"() {
590
984
  "use strict";
591
- import_cargo3 = require("@noy-db/hub/cargo");
592
- import_bundle2 = require("@noy-db/hub/bundle");
985
+ import_cargo5 = require("@noy-db/hub/cargo");
986
+ import_cargo6 = require("@noy-db/hub/cargo");
593
987
  init_field_authority();
594
988
  FieldLevelDeferredError = class extends Error {
595
989
  constructor(collection) {
@@ -685,7 +1079,7 @@ async function graduate(noydb, docked, opts) {
685
1079
  await mergeDecryptedRecords(vault, staged, { strategy: "take-incoming", reason: "dock:graduate" });
686
1080
  let deedSealed = false;
687
1081
  if (opts.deed) {
688
- await (0, import_cargo4.createDeedOwner)(noydb._store, opts.vaultName, opts.deed.ownerId, opts.deed.sealingProvider);
1082
+ await (0, import_cargo8.createDeedOwner)(noydb._store, opts.vaultName, opts.deed.ownerId, opts.deed.sealingProvider);
689
1083
  deedSealed = true;
690
1084
  }
691
1085
  if (opts.stateVault) {
@@ -705,11 +1099,11 @@ async function graduate(noydb, docked, opts) {
705
1099
  event: { type: "unit-graduated", unitId: docked.unitId, vault: opts.vaultName }
706
1100
  };
707
1101
  }
708
- var import_cargo4, UnitGraduationError, ISO_EPOCH;
1102
+ var import_cargo8, UnitGraduationError, ISO_EPOCH;
709
1103
  var init_graduate = __esm({
710
1104
  "src/dock/graduate.ts"() {
711
1105
  "use strict";
712
- import_cargo4 = require("@noy-db/hub/cargo");
1106
+ import_cargo8 = require("@noy-db/hub/cargo");
713
1107
  init_merge_compartment();
714
1108
  init_stage_records();
715
1109
  UnitGraduationError = class extends Error {
@@ -739,7 +1133,7 @@ __export(surface_exports, {
739
1133
  async function proposeSurface(smv, def, proposedBy, now) {
740
1134
  const row = {
741
1135
  ...def,
742
- id: def.id ?? (0, import_cargo5.generateULID)(),
1136
+ id: def.id ?? (0, import_cargo9.generateULID)(),
743
1137
  status: "proposed",
744
1138
  proposedBy,
745
1139
  createdAt: now
@@ -760,7 +1154,7 @@ async function exportSurface(source, surface) {
760
1154
  throw new SurfaceStateError(surface.id, surface.status, "agreed");
761
1155
  }
762
1156
  const seeds = Object.fromEntries(surface.collections.map((c) => [c, () => true]));
763
- const { bundleBytes, transferKey } = await (0, import_bundle4.extractPartition)(source, {
1157
+ const { bundleBytes, transferKey } = await (0, import_cargo10.extractPartition)(source, {
764
1158
  seeds,
765
1159
  maxDepth: 0,
766
1160
  ...surface.fields ? { fieldProjection: surface.fields } : {},
@@ -798,12 +1192,12 @@ async function markSynced(smv, id, now) {
798
1192
  nextSyncDueAt: now + cadenceMs
799
1193
  });
800
1194
  }
801
- var import_cargo5, import_bundle4, SurfaceNotFoundError, SurfaceStateError, SurfaceCadenceScheduler;
1195
+ var import_cargo9, import_cargo10, SurfaceNotFoundError, SurfaceStateError, SurfaceCadenceScheduler;
802
1196
  var init_surface = __esm({
803
1197
  "src/interchange/surface.ts"() {
804
1198
  "use strict";
805
- import_cargo5 = require("@noy-db/hub/cargo");
806
- import_bundle4 = require("@noy-db/hub/bundle");
1199
+ import_cargo9 = require("@noy-db/hub/cargo");
1200
+ import_cargo10 = require("@noy-db/hub/cargo");
807
1201
  init_merge_compartment();
808
1202
  SurfaceNotFoundError = class extends Error {
809
1203
  name = "SurfaceNotFoundError";
@@ -908,38 +1302,38 @@ function canonical(value) {
908
1302
  return `{${keys.map((k) => `${JSON.stringify(k)}:${canonical(obj[k])}`).join(",")}}`;
909
1303
  }
910
1304
  async function fingerprintBlueprint(bp) {
911
- return (0, import_cargo6.sha256Hex)(new TextEncoder().encode(canonical(bp)));
1305
+ return (0, import_cargo11.sha256Hex)(new TextEncoder().encode(canonical(bp)));
912
1306
  }
913
- var import_cargo6;
1307
+ var import_cargo11;
914
1308
  var init_schema_manifest = __esm({
915
1309
  "src/federation/schema-manifest.ts"() {
916
1310
  "use strict";
917
- import_cargo6 = require("@noy-db/hub/cargo");
1311
+ import_cargo11 = require("@noy-db/hub/cargo");
918
1312
  }
919
1313
  });
920
1314
 
921
1315
  // src/federation/constants.ts
922
- var import_cargo7;
1316
+ var import_cargo12;
923
1317
  var init_constants = __esm({
924
1318
  "src/federation/constants.ts"() {
925
1319
  "use strict";
926
- import_cargo7 = require("@noy-db/hub/cargo");
1320
+ import_cargo12 = require("@noy-db/hub/cargo");
927
1321
  }
928
1322
  });
929
1323
 
930
1324
  // src/federation/state-vault.ts
931
1325
  var state_vault_exports = {};
932
1326
  __export(state_vault_exports, {
933
- STATE_VAULT_NAME: () => import_cargo7.STATE_VAULT_NAME,
1327
+ STATE_VAULT_NAME: () => import_cargo12.STATE_VAULT_NAME,
934
1328
  StateManagementVault: () => StateManagementVault
935
1329
  });
936
- var import_cargo8, REGISTRY, MANIFEST, EVENTS, MIGRATION_STATUS, SURFACES, StateManagementVault;
1330
+ var import_cargo13, REGISTRY, MANIFEST, EVENTS, MIGRATION_STATUS, SURFACES, StateManagementVault;
937
1331
  var init_state_vault = __esm({
938
1332
  "src/federation/state-vault.ts"() {
939
1333
  "use strict";
940
1334
  init_schema_manifest();
941
1335
  init_constants();
942
- import_cargo8 = require("@noy-db/hub/cargo");
1336
+ import_cargo13 = require("@noy-db/hub/cargo");
943
1337
  init_constants();
944
1338
  REGISTRY = "vaultRegistry";
945
1339
  MANIFEST = "schemaManifest";
@@ -969,7 +1363,7 @@ var init_state_vault = __esm({
969
1363
  #surfaces;
970
1364
  /** Idempotently open the reserved state vault and bind the control-plane collections. */
971
1365
  static async open(db) {
972
- const vault = await db.openVault(import_cargo7.STATE_VAULT_NAME);
1366
+ const vault = await db.openVault(import_cargo12.STATE_VAULT_NAME);
973
1367
  return new _StateManagementVault(
974
1368
  vault.collection(REGISTRY),
975
1369
  vault.collection(MANIFEST),
@@ -1029,7 +1423,7 @@ var init_state_vault = __esm({
1029
1423
  */
1030
1424
  async appendEvent(event) {
1031
1425
  const ts = event.ts ?? Date.now();
1032
- const id = (0, import_cargo8.generateULID)();
1426
+ const id = (0, import_cargo13.generateULID)();
1033
1427
  await this.#events.put(id, { ...event, id, ts });
1034
1428
  }
1035
1429
  /**
@@ -1071,13 +1465,13 @@ var init_state_vault = __esm({
1071
1465
 
1072
1466
  // src/federation/classify-skip.ts
1073
1467
  function classifyShardSkip(err) {
1074
- return err instanceof import_cargo9.NoAccessError ? "no-grant" : "error";
1468
+ return err instanceof import_cargo14.NoAccessError ? "no-grant" : "error";
1075
1469
  }
1076
- var import_cargo9;
1470
+ var import_cargo14;
1077
1471
  var init_classify_skip = __esm({
1078
1472
  "src/federation/classify-skip.ts"() {
1079
1473
  "use strict";
1080
- import_cargo9 = require("@noy-db/hub/cargo");
1474
+ import_cargo14 = require("@noy-db/hub/cargo");
1081
1475
  }
1082
1476
  });
1083
1477
 
@@ -1102,7 +1496,7 @@ async function applyBroadcastLegs(rows, legs) {
1102
1496
  for (const leg of legs) {
1103
1497
  const map = /* @__PURE__ */ new Map();
1104
1498
  for (const rec of await leg.from.list()) {
1105
- const k = coerceKey((0, import_cargo10.readPath)(rec, leg.on));
1499
+ const k = coerceKey((0, import_cargo15.readPath)(rec, leg.on));
1106
1500
  if (k !== null && !map.has(k)) map.set(k, rec);
1107
1501
  }
1108
1502
  indexes.push({ leg, map });
@@ -1110,7 +1504,7 @@ async function applyBroadcastLegs(rows, legs) {
1110
1504
  return rows.map((row) => {
1111
1505
  const out = { ...row };
1112
1506
  for (const { leg, map } of indexes) {
1113
- const key = coerceKey((0, import_cargo10.readPath)(row, leg.field));
1507
+ const key = coerceKey((0, import_cargo15.readPath)(row, leg.field));
1114
1508
  const match = key === null ? null : map.get(key) ?? null;
1115
1509
  if (match === null && leg.mode === "warn") {
1116
1510
  warnOnceBroadcastMiss(leg.field, leg.as, key ?? "<null>");
@@ -1120,11 +1514,11 @@ async function applyBroadcastLegs(rows, legs) {
1120
1514
  return out;
1121
1515
  });
1122
1516
  }
1123
- var import_cargo10, warnedBroadcastKeys;
1517
+ var import_cargo15, warnedBroadcastKeys;
1124
1518
  var init_cross_shard_join = __esm({
1125
1519
  "src/federation/cross-shard-join.ts"() {
1126
1520
  "use strict";
1127
- import_cargo10 = require("@noy-db/hub/cargo");
1521
+ import_cargo15 = require("@noy-db/hub/cargo");
1128
1522
  warnedBroadcastKeys = /* @__PURE__ */ new Set();
1129
1523
  }
1130
1524
  });
@@ -1218,85 +1612,6 @@ var init_cross_vault_live = __esm({
1218
1612
  }
1219
1613
  });
1220
1614
 
1221
- // src/federation/insight-auto-push.ts
1222
- var InsightAutoPush;
1223
- var init_insight_auto_push = __esm({
1224
- "src/federation/insight-auto-push.ts"() {
1225
- "use strict";
1226
- InsightAutoPush = class {
1227
- constructor(recompute, isSource, onError = (err, pk) => console.warn(`[klum-db] insight auto-push failed for shard "${pk}":`, err), debounceMs) {
1228
- this.recompute = recompute;
1229
- this.isSource = isSource;
1230
- this.onError = onError;
1231
- this.debounceMs = debounceMs;
1232
- }
1233
- recompute;
1234
- isSource;
1235
- onError;
1236
- debounceMs;
1237
- dirty = /* @__PURE__ */ new Set();
1238
- pending = null;
1239
- timer = null;
1240
- resolvePending = null;
1241
- flushing = false;
1242
- /** Called from a shard's onAfterWrite hook. Marks the shard dirty + schedules a flush. */
1243
- noteWrite(partitionKey, collection) {
1244
- if (!this.isSource(collection)) return;
1245
- this.dirty.add(partitionKey);
1246
- if (this.debounceMs !== void 0) this.scheduleDebounced();
1247
- else this.schedule();
1248
- }
1249
- /** Resolve once no flush is pending (drains rescheduled flushes too). */
1250
- async whenSettled() {
1251
- while (this.pending) await this.pending;
1252
- }
1253
- // ── microtask path (unchanged default) ──
1254
- schedule() {
1255
- if (this.pending) return;
1256
- this.pending = this.runFlush().finally(() => {
1257
- this.pending = null;
1258
- if (this.dirty.size > 0) this.schedule();
1259
- });
1260
- }
1261
- // ── debounce path (#13) ──
1262
- scheduleDebounced() {
1263
- if (!this.pending) this.pending = new Promise((r) => {
1264
- this.resolvePending = r;
1265
- });
1266
- if (this.timer) clearTimeout(this.timer);
1267
- this.timer = setTimeout(() => {
1268
- this.timer = null;
1269
- if (this.flushing) return;
1270
- this.flushing = true;
1271
- void this.runFlush().finally(() => {
1272
- this.flushing = false;
1273
- if (this.dirty.size > 0) {
1274
- this.scheduleDebounced();
1275
- } else {
1276
- const resolve = this.resolvePending;
1277
- this.resolvePending = null;
1278
- this.pending = null;
1279
- resolve?.();
1280
- }
1281
- });
1282
- }, this.debounceMs);
1283
- }
1284
- async runFlush() {
1285
- await Promise.resolve();
1286
- const pks = [...this.dirty];
1287
- this.dirty.clear();
1288
- for (const pk of pks) {
1289
- try {
1290
- await this.recompute(pk);
1291
- } catch (err) {
1292
- this.onError(err, pk);
1293
- }
1294
- }
1295
- }
1296
- };
1297
- }
1298
- });
1299
-
1300
1615
  // src/federation/partial-reduce.ts
1301
1616
  function canPartialReduce(spec) {
1302
1617
  return Object.values(spec).every((r) => typeof r.merge === "function");
@@ -1335,12 +1650,12 @@ var init_partial_reduce = __esm({
1335
1650
  });
1336
1651
 
1337
1652
  // src/federation/aggregate-across.ts
1338
- var import_cargo11, import_cargo12, CrossVaultAggregation, CrossVaultGroupedAggregation;
1653
+ var import_cargo16, import_cargo17, CrossVaultAggregation, CrossVaultGroupedAggregation;
1339
1654
  var init_aggregate_across = __esm({
1340
1655
  "src/federation/aggregate-across.ts"() {
1341
1656
  "use strict";
1342
- import_cargo11 = require("@noy-db/hub/cargo");
1343
- import_cargo12 = require("@noy-db/hub/cargo");
1657
+ import_cargo16 = require("@noy-db/hub/cargo");
1658
+ import_cargo17 = require("@noy-db/hub/cargo");
1344
1659
  init_cross_vault_live();
1345
1660
  init_partial_reduce();
1346
1661
  CrossVaultAggregation = class {
@@ -1358,7 +1673,7 @@ var init_aggregate_across = __esm({
1358
1673
  return { result: finalizePartial(this.spec, mergePartials(this.spec, partials)), skippedVaults: skippedVaults2 };
1359
1674
  }
1360
1675
  const { records, skippedVaults } = await this.src.fanoutRecords(options);
1361
- return { result: (0, import_cargo11.reduceRecords)(records, this.spec), skippedVaults };
1676
+ return { result: (0, import_cargo16.reduceRecords)(records, this.spec), skippedVaults };
1362
1677
  }
1363
1678
  live(options = {}) {
1364
1679
  if (!this.bind) throw new Error("CrossVaultAggregation: live() requires a LiveBinding \u2014 use ShardedQuery.aggregate()");
@@ -1369,7 +1684,7 @@ var init_aggregate_across = __esm({
1369
1684
  isRelevant: this.bind.isRelevant,
1370
1685
  compute: async () => {
1371
1686
  const { records, skippedVaults } = await src.fanoutRecords(options);
1372
- return { value: (0, import_cargo11.reduceRecords)(records, spec), skipped: skippedVaults };
1687
+ return { value: (0, import_cargo16.reduceRecords)(records, spec), skipped: skippedVaults };
1373
1688
  },
1374
1689
  initialSnapshot: { value: void 0, skipped: [] },
1375
1690
  ...options.debounceMs !== void 0 ? { debounceMs: options.debounceMs } : {}
@@ -1404,7 +1719,7 @@ var init_aggregate_across = __esm({
1404
1719
  async run(options = {}) {
1405
1720
  const { records, skippedVaults } = await this.src.fanoutRecords(options);
1406
1721
  return {
1407
- results: (0, import_cargo12.groupAndReduce)(records, this.field, this.spec),
1722
+ results: (0, import_cargo17.groupAndReduce)(records, this.field, this.spec),
1408
1723
  skippedVaults
1409
1724
  };
1410
1725
  }
@@ -1419,7 +1734,7 @@ var init_aggregate_across = __esm({
1419
1734
  compute: async () => {
1420
1735
  const { records, skippedVaults } = await src.fanoutRecords(options);
1421
1736
  return {
1422
- records: (0, import_cargo12.groupAndReduce)(records, field, spec),
1737
+ records: (0, import_cargo17.groupAndReduce)(records, field, spec),
1423
1738
  skipped: skippedVaults
1424
1739
  };
1425
1740
  },
@@ -1492,7 +1807,7 @@ async function retrieveAcross(group, collectionName, query, opts = {}) {
1492
1807
  }
1493
1808
  return pv.hits.map((h) => ({ ...h, id: pv.vault + SEP + h.id }));
1494
1809
  });
1495
- const fused = (0, import_cargo13.fuseRetrieval)(lists, {
1810
+ const fused = (0, import_cargo18.fuseRetrieval)(lists, {
1496
1811
  ...opts.limit !== void 0 ? { limit: opts.limit } : {},
1497
1812
  ...opts.rrfK !== void 0 ? { k: opts.rrfK } : {}
1498
1813
  });
@@ -1502,11 +1817,11 @@ async function retrieveAcross(group, collectionName, query, opts = {}) {
1502
1817
  });
1503
1818
  return { hits, skippedVaults };
1504
1819
  }
1505
- var import_cargo13, SEP;
1820
+ var import_cargo18, SEP;
1506
1821
  var init_retrieve_across = __esm({
1507
1822
  "src/federation/retrieve-across.ts"() {
1508
1823
  "use strict";
1509
- import_cargo13 = require("@noy-db/hub/cargo");
1824
+ import_cargo18 = require("@noy-db/hub/cargo");
1510
1825
  init_classify_skip();
1511
1826
  SEP = "\0";
1512
1827
  }
@@ -1526,30 +1841,31 @@ function autoPushConfig(spec) {
1526
1841
  }
1527
1842
  function assertSafePartitionKey(partitionKey) {
1528
1843
  if (partitionKey.length === 0) {
1529
- throw new import_cargo14.ValidationError("partitionKey must be a non-empty string");
1844
+ throw new import_cargo19.ValidationError("partitionKey must be a non-empty string");
1530
1845
  }
1531
- if (partitionKey === import_cargo7.STATE_VAULT_NAME) {
1532
- throw new import_cargo14.ReservedVaultNameError(partitionKey);
1846
+ if (partitionKey === import_cargo12.STATE_VAULT_NAME) {
1847
+ throw new import_cargo19.ReservedVaultNameError(partitionKey);
1533
1848
  }
1534
1849
  if (!SAFE_PARTITION_KEY.test(partitionKey)) {
1535
- throw new import_cargo14.ValidationError(
1850
+ throw new import_cargo19.ValidationError(
1536
1851
  `partitionKey "${partitionKey}" contains characters outside [A-Za-z0-9._-]. Map your records to a store-safe key in sharding.keyOf.`
1537
1852
  );
1538
1853
  }
1539
1854
  if (partitionKey.includes(SHARD_SEPARATOR)) {
1540
- throw new import_cargo14.ValidationError(
1855
+ throw new import_cargo19.ValidationError(
1541
1856
  `partitionKey "${partitionKey}" must not contain "--" \u2014 it is reserved as the shard vault-id separator and would risk shard-id collisions.`
1542
1857
  );
1543
1858
  }
1544
1859
  }
1545
- var import_cargo14, SHARD_SEPARATOR, SAFE_PARTITION_KEY, VaultGroup, ShardedCollection, ShardedQuery, ShardedGroupedQuery;
1860
+ var import_cargo19, SHARD_SEPARATOR, SAFE_PARTITION_KEY, VaultGroup, ShardedCollection, ShardedQuery, ShardedGroupedQuery;
1546
1861
  var init_vault_group = __esm({
1547
1862
  "src/federation/vault-group.ts"() {
1548
1863
  "use strict";
1549
1864
  init_state_vault();
1550
- import_cargo14 = require("@noy-db/hub/cargo");
1865
+ import_cargo19 = require("@noy-db/hub/cargo");
1551
1866
  init_constants();
1552
1867
  init_classify_skip();
1868
+ init_read_model();
1553
1869
  init_cross_shard_join();
1554
1870
  init_cross_vault_live();
1555
1871
  init_insight_auto_push();
@@ -1568,7 +1884,7 @@ var init_vault_group = __esm({
1568
1884
  this.cutoverOnOpen = cutoverOnOpen;
1569
1885
  this.meta = meta;
1570
1886
  if (name.includes(SHARD_SEPARATOR)) {
1571
- throw new import_cargo14.ValidationError(
1887
+ throw new import_cargo19.ValidationError(
1572
1888
  `VaultGroup name "${name}" must not contain "--" (reserved shard vault-id separator).`
1573
1889
  );
1574
1890
  }
@@ -1673,11 +1989,11 @@ var init_vault_group = __esm({
1673
1989
  const vaultId = this.shardVaultId(partitionKey);
1674
1990
  const row = await this.registry.get(this.registryId(partitionKey));
1675
1991
  const provisioned = await this.db._shardVaultProvisioned(vaultId);
1676
- if (row && !provisioned) throw new import_cargo14.ShardProvisioningError(vaultId, partitionKey);
1992
+ if (row && !provisioned) throw new import_cargo19.ShardProvisioningError(vaultId, partitionKey);
1677
1993
  if (row && provisioned) return this.openShard(partitionKey);
1678
1994
  if (region !== void 0) {
1679
1995
  const backendRegion = this.db._resolveBackend(vaultId).capabilities?.region;
1680
- if (backendRegion !== region) throw new import_cargo14.DataResidencyError(vaultId, region, backendRegion);
1996
+ if (backendRegion !== region) throw new import_cargo19.DataResidencyError(vaultId, region, backendRegion);
1681
1997
  }
1682
1998
  const vault = await this.db.openVault(vaultId);
1683
1999
  this.template.configure(vault);
@@ -1711,9 +2027,9 @@ var init_vault_group = __esm({
1711
2027
  async shard(partitionKey) {
1712
2028
  const vaultId = this.shardVaultId(partitionKey);
1713
2029
  const row = await this.registry.get(this.registryId(partitionKey));
1714
- if (!row) throw new import_cargo14.UnknownShardError(partitionKey, this.name);
2030
+ if (!row) throw new import_cargo19.UnknownShardError(partitionKey, this.name);
1715
2031
  const provisioned = await this.db._shardVaultProvisioned(vaultId);
1716
- if (!provisioned) throw new import_cargo14.ShardProvisioningError(vaultId, partitionKey);
2032
+ if (!provisioned) throw new import_cargo19.ShardProvisioningError(vaultId, partitionKey);
1717
2033
  return this.openShard(partitionKey);
1718
2034
  }
1719
2035
  /** A sharded view over one logical collection across all shards. */
@@ -1745,7 +2061,7 @@ var init_vault_group = __esm({
1745
2061
  for (const p of probes) {
1746
2062
  if ("error" in p) skipped.push({ vaultId: p.row.vaultId, reason: "error", error: p.error });
1747
2063
  else if (p.provisioned) eligible.push(p.row);
1748
- else skipped.push({ vaultId: p.row.vaultId, reason: "error", error: new import_cargo14.ShardProvisioningError(p.row.vaultId, p.row.partitionKey) });
2064
+ else skipped.push({ vaultId: p.row.vaultId, reason: "error", error: new import_cargo19.ShardProvisioningError(p.row.vaultId, p.row.partitionKey) });
1749
2065
  }
1750
2066
  return { eligible, skipped };
1751
2067
  }
@@ -1782,7 +2098,7 @@ var init_vault_group = __esm({
1782
2098
  withCrossVaultDerivation(spec) {
1783
2099
  const target = spec.target.vault;
1784
2100
  if (target === this.name || target.startsWith(`${this.name}${SHARD_SEPARATOR}`)) {
1785
- throw new import_cargo14.ValidationError(
2101
+ throw new import_cargo19.ValidationError(
1786
2102
  `withCrossVaultDerivation: target.vault "${target}" is the "${this.name}" group itself or one of its shards \u2014 an Insight summary must target a SEPARATE analytics vault, never write back into client-shard data (it would breach the per-shard DEK boundary). Use a distinct vault name.`
1787
2103
  );
1788
2104
  }
@@ -1842,12 +2158,7 @@ var init_vault_group = __esm({
1842
2158
  skipped.push({ vaultId: row.vaultId, reason: "error", ...res?.error ? { error: res.error } : {} });
1843
2159
  continue;
1844
2160
  }
1845
- const ctx = {
1846
- vaultId: row.vaultId,
1847
- partitionKey: row.partitionKey,
1848
- schemaVersion: row.schemaVersion
1849
- };
1850
- const summary = spec.derive(res.result, ctx);
2161
+ const summary = deriveShardSummary(spec, res.result, row);
1851
2162
  await out.put(row.partitionKey, summary);
1852
2163
  written++;
1853
2164
  }
@@ -1867,17 +2178,12 @@ var init_vault_group = __esm({
1867
2178
  const row = await this.registry.get(this.registryId(partitionKey));
1868
2179
  if (!row) return;
1869
2180
  const shard = await this.openShard(partitionKey);
1870
- const ctx = {
1871
- vaultId: row.vaultId,
1872
- partitionKey,
1873
- schemaVersion: row.schemaVersion
1874
- };
1875
2181
  for (const spec of this.crossVaultDerivations) {
1876
2182
  if (!spec.autoPush) continue;
1877
2183
  const min = autoPushConfig(spec)?.minVersion;
1878
2184
  if (min !== void 0 && row.schemaVersion < min) continue;
1879
2185
  const records = await shard.collection(spec.source).list();
1880
- const summary = spec.derive(records, ctx);
2186
+ const summary = deriveShardSummary(spec, records, row);
1881
2187
  const insight = await this.db.openVault(spec.target.vault);
1882
2188
  await insight.collection(spec.target.collection).put(partitionKey, summary);
1883
2189
  }
@@ -1908,7 +2214,7 @@ var init_vault_group = __esm({
1908
2214
  async cutoverShard(partitionKey) {
1909
2215
  const vaultId = this.shardVaultId(partitionKey);
1910
2216
  const row = await this.registry.get(this.registryId(partitionKey));
1911
- if (!row) throw new import_cargo14.UnknownShardError(partitionKey, this.name);
2217
+ if (!row) throw new import_cargo19.UnknownShardError(partitionKey, this.name);
1912
2218
  const target = this.template.version;
1913
2219
  const sv = await this.ensureStateVault();
1914
2220
  const base = { vaultId, group: this.name, currentVersion: row.schemaVersion, targetVersion: target };
@@ -1991,7 +2297,7 @@ var init_vault_group = __esm({
1991
2297
  let vault;
1992
2298
  if (!row) {
1993
2299
  if (this.group.sharding.autoCreate === false) {
1994
- throw new import_cargo14.UnknownShardError(key, this.group.name);
2300
+ throw new import_cargo19.UnknownShardError(key, this.group.name);
1995
2301
  }
1996
2302
  vault = await this.group.createShard(key, this.group.sharding.regionOf?.(record));
1997
2303
  } else {
@@ -2074,7 +2380,7 @@ var init_vault_group = __esm({
2074
2380
  this.group.template.configure(probe);
2075
2381
  for (const leg of this.coPartitionedLegs) {
2076
2382
  if (!probe.resolveRef(this.collectionName, leg.field)) {
2077
- throw new import_cargo14.CrossShardJoinError(
2383
+ throw new import_cargo19.CrossShardJoinError(
2078
2384
  `crossShardJoin("${leg.field}"): no ref() declared for "${leg.field}" on collection "${this.collectionName}" in template "${this.group.sharding.vaultTemplate}". Add refs: { ${leg.field}: ref('<target>') } to the template's collection options.`
2079
2385
  );
2080
2386
  }
@@ -2236,13 +2542,117 @@ var init_vault_group = __esm({
2236
2542
  }
2237
2543
  });
2238
2544
 
2545
+ // src/portal/provision.ts
2546
+ var provision_exports = {};
2547
+ __export(provision_exports, {
2548
+ provisionPortal: () => provisionPortal,
2549
+ revokePortalInvite: () => revokePortalInvite
2550
+ });
2551
+ async function requireRow(group, client) {
2552
+ const row = await group.registry.get(group.registryId(client));
2553
+ if (!row) throw new Error(`provisionPortal: client "${client}" has no registry row in group.`);
2554
+ return row;
2555
+ }
2556
+ async function provisionPortal(db, group, opts) {
2557
+ const existing = await group.registry.get(group.registryId(opts.client));
2558
+ if (!existing) await group.createShard(opts.client);
2559
+ const row = existing ?? await requireRow(group, opts.client);
2560
+ const vaultId = row.vaultId;
2561
+ for (const g of opts.firmGrants ?? []) {
2562
+ await db.grant(vaultId, {
2563
+ userId: g.userId,
2564
+ displayName: g.displayName,
2565
+ role: g.role,
2566
+ passphrase: g.passphrase
2567
+ });
2568
+ }
2569
+ const { issueInvite, issuePeerRecovery } = await import("@noy-db/on-magic-link");
2570
+ const mode = opts.mode ?? "invite";
2571
+ const issued = mode === "rebind" ? await issuePeerRecovery(db, vaultId, {
2572
+ userId: opts.invite.userId,
2573
+ displayName: opts.invite.displayName,
2574
+ ...opts.invite.role !== void 0 ? { role: opts.invite.role } : {},
2575
+ ...opts.invite.ttlMs !== void 0 ? { ttlMs: opts.invite.ttlMs } : {},
2576
+ ...opts.invite.tempPhrase !== void 0 ? { tempPhrase: opts.invite.tempPhrase } : {}
2577
+ }) : await issueInvite(db, vaultId, {
2578
+ userId: opts.invite.userId,
2579
+ displayName: opts.invite.displayName,
2580
+ role: opts.invite.role ?? "client",
2581
+ ...opts.invite.ttlMs !== void 0 ? { ttlMs: opts.invite.ttlMs } : {},
2582
+ ...opts.invite.tempPhrase !== void 0 ? { tempPhrase: opts.invite.tempPhrase } : {}
2583
+ });
2584
+ const auditRef = {
2585
+ tokenId: issued.payload.tokenId,
2586
+ userId: opts.invite.userId,
2587
+ kind: mode,
2588
+ issuedAt: Date.now(),
2589
+ expiresAt: issued.payload.expiresAt
2590
+ };
2591
+ const handle = row.handle ?? (0, import_cargo20.generateULID)();
2592
+ const updated = {
2593
+ ...row,
2594
+ handle,
2595
+ portal: {
2596
+ enabledAt: row.portal?.enabledAt ?? Date.now(),
2597
+ invites: [...row.portal?.invites ?? [], auditRef]
2598
+ }
2599
+ };
2600
+ await group.registry.put(group.registryId(opts.client), updated);
2601
+ return {
2602
+ vaultId,
2603
+ handle,
2604
+ invite: {
2605
+ encoded: issued.encoded,
2606
+ tokenId: issued.payload.tokenId,
2607
+ userId: opts.invite.userId,
2608
+ expiresAt: issued.payload.expiresAt
2609
+ },
2610
+ row: updated
2611
+ };
2612
+ }
2613
+ async function revokePortalInvite(db, group, opts) {
2614
+ const row = await requireRow(group, opts.client);
2615
+ const invites = row.portal?.invites ?? [];
2616
+ const ref = invites.find((i) => i.tokenId === opts.tokenId);
2617
+ if (!ref) {
2618
+ throw new Error(`revokePortalInvite: no invite with tokenId "${opts.tokenId}" for client "${opts.client}".`);
2619
+ }
2620
+ if (ref.revokedAt !== void 0) return row;
2621
+ const { revokeInvite } = await import("@noy-db/on-magic-link");
2622
+ await revokeInvite(db, row.vaultId, {
2623
+ tokenId: ref.tokenId,
2624
+ vault: row.vaultId,
2625
+ userId: ref.userId,
2626
+ kind: ref.kind === "rebind" ? "peer-recovery" : "invite",
2627
+ issuer: "",
2628
+ tempPhrase: "",
2629
+ expiresAt: ref.expiresAt
2630
+ });
2631
+ const updated = {
2632
+ ...row,
2633
+ portal: {
2634
+ enabledAt: row.portal?.enabledAt ?? Date.now(),
2635
+ invites: invites.map((i) => i.tokenId === opts.tokenId ? { ...i, revokedAt: Date.now() } : i)
2636
+ }
2637
+ };
2638
+ await group.registry.put(group.registryId(opts.client), updated);
2639
+ return updated;
2640
+ }
2641
+ var import_cargo20;
2642
+ var init_provision = __esm({
2643
+ "src/portal/provision.ts"() {
2644
+ "use strict";
2645
+ import_cargo20 = require("@noy-db/hub/cargo");
2646
+ }
2647
+ });
2648
+
2239
2649
  // src/index.ts
2240
2650
  var index_exports = {};
2241
2651
  __export(index_exports, {
2242
- CrossShardJoinError: () => import_cargo16.CrossShardJoinError,
2652
+ CrossShardJoinError: () => import_cargo22.CrossShardJoinError,
2243
2653
  CrossVaultDanglingRefError: () => CrossVaultDanglingRefError,
2244
- CustodyApi: () => import_cargo17.CustodyApi,
2245
- DataResidencyError: () => import_cargo16.DataResidencyError,
2654
+ CustodyApi: () => import_cargo23.CustodyApi,
2655
+ DataResidencyError: () => import_cargo22.DataResidencyError,
2246
2656
  DockedUnit: () => DockedUnit,
2247
2657
  FieldAuthorityPolicyMissingError: () => FieldAuthorityPolicyMissingError,
2248
2658
  FieldLevelDeferredError: () => FieldLevelDeferredError,
@@ -2253,17 +2663,19 @@ __export(index_exports, {
2253
2663
  NOYDB_MULTI_BUNDLE_MAGIC: () => NOYDB_MULTI_BUNDLE_MAGIC,
2254
2664
  NOYDB_MULTI_BUNDLE_PREFIX_BYTES: () => NOYDB_MULTI_BUNDLE_PREFIX_BYTES,
2255
2665
  NOYDB_MULTI_BUNDLE_VERSION: () => NOYDB_MULTI_BUNDLE_VERSION,
2256
- ReservedVaultNameError: () => import_cargo16.ReservedVaultNameError,
2257
- ShardProvisioningError: () => import_cargo16.ShardProvisioningError,
2666
+ PostureViolationError: () => PostureViolationError,
2667
+ ReservedVaultNameError: () => import_cargo22.ReservedVaultNameError,
2668
+ ShardProvisioningError: () => import_cargo22.ShardProvisioningError,
2669
+ ShareLinkResolutionError: () => ShareLinkResolutionError,
2258
2670
  SurfaceCadenceScheduler: () => SurfaceCadenceScheduler,
2259
2671
  SurfaceNotFoundError: () => SurfaceNotFoundError,
2260
2672
  SurfaceStateError: () => SurfaceStateError,
2261
2673
  UnitGraduationError: () => UnitGraduationError,
2262
- UnknownShardError: () => import_cargo16.UnknownShardError,
2263
- VaultTemplateNotFoundError: () => import_cargo16.VaultTemplateNotFoundError,
2674
+ UnknownShardError: () => import_cargo22.UnknownShardError,
2675
+ VaultTemplateNotFoundError: () => import_cargo22.VaultTemplateNotFoundError,
2264
2676
  agreeSurface: () => agreeSurface,
2265
2677
  applySurface: () => applySurface,
2266
- createDeedOwner: () => import_cargo17.createDeedOwner,
2678
+ createDeedOwner: () => import_cargo23.createDeedOwner,
2267
2679
  createLobby: () => createLobby,
2268
2680
  decodeMultiBundle: () => decodeMultiBundle,
2269
2681
  describeCrossVaultExtraction: () => describeCrossVaultExtraction,
@@ -2271,11 +2683,11 @@ __export(index_exports, {
2271
2683
  exportSurface: () => exportSurface,
2272
2684
  extractCrossVaultPartition: () => extractCrossVaultPartition,
2273
2685
  groupInspector: () => groupInspector,
2274
- isDeedVault: () => import_cargo17.isDeedVault,
2686
+ isDeedVault: () => import_cargo23.isDeedVault,
2275
2687
  isSurfaceDue: () => isSurfaceDue,
2276
- liberateVault: () => import_cargo17.liberateVault,
2688
+ liberateVault: () => import_cargo23.liberateVault,
2277
2689
  listDueSurfaces: () => listDueSurfaces,
2278
- loadDeedMarker: () => import_cargo17.loadDeedMarker,
2690
+ loadDeedMarker: () => import_cargo23.loadDeedMarker,
2279
2691
  markSynced: () => markSynced,
2280
2692
  mergeCompartment: () => mergeCompartment,
2281
2693
  mergeDecryptedRecords: () => mergeDecryptedRecords,
@@ -2290,7 +2702,7 @@ __export(index_exports, {
2290
2702
  writeMultiVaultBundle: () => writeMultiVaultBundle
2291
2703
  });
2292
2704
  module.exports = __toCommonJS(index_exports);
2293
- var import_cargo15 = require("@noy-db/hub/cargo");
2705
+ var import_cargo21 = require("@noy-db/hub/cargo");
2294
2706
 
2295
2707
  // src/dock/docked-unit.ts
2296
2708
  var DockedUnit = class {
@@ -2309,6 +2721,10 @@ var DockedUnit = class {
2309
2721
  }
2310
2722
  };
2311
2723
 
2724
+ // src/index.ts
2725
+ init_share_link();
2726
+ init_read_model();
2727
+
2312
2728
  // src/federation/group-inspector.ts
2313
2729
  function groupInspector(group) {
2314
2730
  let shardIds = /* @__PURE__ */ new Set();
@@ -2372,13 +2788,13 @@ async function meterGroup(group, opts = {}) {
2372
2788
  }
2373
2789
 
2374
2790
  // src/index.ts
2375
- var import_cargo16 = require("@noy-db/hub/cargo");
2791
+ var import_cargo22 = require("@noy-db/hub/cargo");
2376
2792
  init_multi_bundle();
2377
2793
  init_extract_cross_vault();
2378
2794
  init_merge_compartment();
2379
2795
 
2380
2796
  // src/interchange/migrate-then-merge.ts
2381
- var import_bundle3 = require("@noy-db/hub/bundle");
2797
+ var import_cargo7 = require("@noy-db/hub/cargo");
2382
2798
  init_merge_compartment();
2383
2799
  init_stage_records();
2384
2800
  init_stage_records();
@@ -2403,7 +2819,7 @@ async function migrateThenMerge(receiver, compartmentBytes, opts) {
2403
2819
  );
2404
2820
  }
2405
2821
  if (fromVersion > toVersion) throw new MinVersionError(fromVersion, toVersion);
2406
- const incoming = await (0, import_bundle3.decryptExtractedPartition)(compartmentBytes, opts.transferKey);
2822
+ const incoming = await (0, import_cargo7.decryptExtractedPartition)(compartmentBytes, opts.transferKey);
2407
2823
  const transformsByCollection = {};
2408
2824
  const migrationByCollection = {};
2409
2825
  for (const [coll, recs] of Object.entries(incoming)) {
@@ -2426,7 +2842,7 @@ async function migrateThenMerge(receiver, compartmentBytes, opts) {
2426
2842
 
2427
2843
  // src/index.ts
2428
2844
  init_field_authority();
2429
- var import_cargo17 = require("@noy-db/hub/cargo");
2845
+ var import_cargo23 = require("@noy-db/hub/cargo");
2430
2846
 
2431
2847
  // src/dock/unit-driver.ts
2432
2848
  var InMemoryUnitDriver = class {
@@ -2460,10 +2876,10 @@ var Lobby = class {
2460
2876
  }
2461
2877
  async openVaultGroup(name, opts) {
2462
2878
  const db = this.noydb;
2463
- if (db.isClosed) throw new import_cargo15.ValidationError("Instance is closed");
2464
- if (name === import_cargo15.STATE_VAULT_NAME) throw new import_cargo15.ReservedVaultNameError(name);
2879
+ if (db.isClosed) throw new import_cargo21.ValidationError("Instance is closed");
2880
+ if (name === import_cargo21.STATE_VAULT_NAME) throw new import_cargo21.ReservedVaultNameError(name);
2465
2881
  const template = this.vaultTemplates.get(opts.sharding.vaultTemplate);
2466
- if (!template) throw new import_cargo15.VaultTemplateNotFoundError(opts.sharding.vaultTemplate);
2882
+ if (!template) throw new import_cargo21.VaultTemplateNotFoundError(opts.sharding.vaultTemplate);
2467
2883
  const { VaultGroup: VaultGroup2 } = await Promise.resolve().then(() => (init_vault_group(), vault_group_exports));
2468
2884
  const { StateManagementVault: StateManagementVault2 } = await Promise.resolve().then(() => (init_state_vault(), state_vault_exports));
2469
2885
  const stateVault = opts.registry ? void 0 : await StateManagementVault2.open(db);
@@ -2482,7 +2898,7 @@ var Lobby = class {
2482
2898
  }
2483
2899
  async openStateManagementVault() {
2484
2900
  const db = this.noydb;
2485
- if (db.isClosed) throw new import_cargo15.ValidationError("Instance is closed");
2901
+ if (db.isClosed) throw new import_cargo21.ValidationError("Instance is closed");
2486
2902
  const { StateManagementVault: StateManagementVault2 } = await Promise.resolve().then(() => (init_state_vault(), state_vault_exports));
2487
2903
  return StateManagementVault2.open(db);
2488
2904
  }
@@ -2502,6 +2918,64 @@ var Lobby = class {
2502
2918
  const { graduate: graduateFn } = await Promise.resolve().then(() => (init_graduate(), graduate_exports));
2503
2919
  return graduateFn(this.noydb, docked, opts);
2504
2920
  }
2921
+ // ─── Portal deep-link addressing (#43) ────────────────────────────────────
2922
+ /**
2923
+ * Resolve a portal share link (raw URL/path or a parsed `ShareLink`)
2924
+ * against the fleet registry: `vaultHandle → registry row` plus a
2925
+ * console-route descriptor the firm app turns into its own URL.
2926
+ * Unknown/foreign handles fail closed with `ShareLinkResolutionError`.
2927
+ * Delegates to `resolveShareLinkAgainstRegistry` in `portal/share-link.ts`.
2928
+ */
2929
+ async resolveShareLink(link, opts) {
2930
+ const { resolveShareLinkAgainstRegistry: resolveShareLinkAgainstRegistry2 } = await Promise.resolve().then(() => (init_share_link(), share_link_exports));
2931
+ return resolveShareLinkAgainstRegistry2(link, opts.registry);
2932
+ }
2933
+ /**
2934
+ * Mint a portal share link for a fleet client (`client` partition key +
2935
+ * `group`), delegating the link grammar to `@noy-db/hub/share-link`.
2936
+ * Fails closed when the client is unknown or has no portal handle.
2937
+ * Delegates to `buildShareLinkForClient` in `portal/share-link.ts`.
2938
+ */
2939
+ async buildShareLink(parts, opts) {
2940
+ const { buildShareLinkForClient: buildShareLinkForClient2 } = await Promise.resolve().then(() => (init_share_link(), share_link_exports));
2941
+ return buildShareLinkForClient2(parts, opts.registry);
2942
+ }
2943
+ // ─── Portal provisioning (#42) ────────────────────────────────────────────
2944
+ /**
2945
+ * Provision (or re-invite, `mode: 'rebind'`) a client portal vault in
2946
+ * one audited operation: ensure the shard, assign the portal handle
2947
+ * (#43's link target), apply firm grants, issue the magic-link invite,
2948
+ * stamp the audit trail on the fleet registry row.
2949
+ * Needs `@noy-db/on-magic-link` (optional peer) and a session created
2950
+ * with `teamStrategy: withTeam()`.
2951
+ * Delegates to `provisionPortal` in `portal/provision.ts`.
2952
+ */
2953
+ async provisionPortal(group, opts) {
2954
+ const { provisionPortal: provisionPortalFn } = await Promise.resolve().then(() => (init_provision(), provision_exports));
2955
+ return provisionPortalFn(this.noydb, group, opts);
2956
+ }
2957
+ /**
2958
+ * Revoke an outstanding portal invite and stamp `revokedAt` on the
2959
+ * fleet audit trail. Idempotent; fails closed on an unknown tokenId.
2960
+ * Delegates to `revokePortalInvite` in `portal/provision.ts`.
2961
+ */
2962
+ async revokePortalInvite(group, opts) {
2963
+ const { revokePortalInvite: revokePortalInviteFn } = await Promise.resolve().then(() => (init_provision(), provision_exports));
2964
+ return revokePortalInviteFn(this.noydb, group, opts);
2965
+ }
2966
+ // ─── Federated read-model (#44) ───────────────────────────────────────────
2967
+ /**
2968
+ * Open a maintained federated read-model over `group` (#44 S1): rollup
2969
+ * models reduce each shard's `source` into one summary row per shard in
2970
+ * the read-model vault — deterministic ids, `_shard`/`_sourceVersion`
2971
+ * provenance, fail-closed `posture.surface` allowlist, explicit
2972
+ * `refresh()`. Spec: docs/federated-read-model.md.
2973
+ * Delegates to `openReadModel` in `federation/read-model.ts`.
2974
+ */
2975
+ async openReadModel(group, opts) {
2976
+ const { openReadModel: openReadModelFn } = await Promise.resolve().then(() => (init_read_model(), read_model_exports));
2977
+ return openReadModelFn(group, opts);
2978
+ }
2505
2979
  // ─── FR-7 Surface API ─────────────────────────────────────────────────────
2506
2980
  /**
2507
2981
  * Export a scoped partition from `vaultName` bounded to the given surface.
@@ -2584,8 +3058,10 @@ function createLobby(noydb) {
2584
3058
  NOYDB_MULTI_BUNDLE_MAGIC,
2585
3059
  NOYDB_MULTI_BUNDLE_PREFIX_BYTES,
2586
3060
  NOYDB_MULTI_BUNDLE_VERSION,
3061
+ PostureViolationError,
2587
3062
  ReservedVaultNameError,
2588
3063
  ShardProvisioningError,
3064
+ ShareLinkResolutionError,
2589
3065
  SurfaceCadenceScheduler,
2590
3066
  SurfaceNotFoundError,
2591
3067
  SurfaceStateError,