@klum-db/lobby 0.2.0-pre.30 → 0.2.0-pre.32

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.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Vault, SealingKeyProvider, PublicEnvelope, Noydb } from '@noy-db/hub';
2
2
  export { CustodyApi, DeedMarker, GrantCustodianOptions, LiberateOptions, LiberateResult, createDeedOwner, isDeedVault, liberateVault, loadDeedMarker } from '@noy-db/hub';
3
- import { a as VaultTemplate, S as StateManagementVault, V as VaultGroup, b as SkippedVault, M as MergeCompartmentOptions, c as MergeReport, d as SurfaceDirection, e as SurfaceConflictPolicy, f as SurfaceRow, g as VaultGroupOptions } from './vault-group-BXjO5kHB.cjs';
4
- export { C as CapturedBlueprint, h as CrossVaultAggregation, i as CrossVaultDerivationContext, j as CrossVaultDerivationSpec, k as CrossVaultGroupedAggregation, G as CrossVaultGroupedRow, l as CrossVaultLiveAggregation, m as CrossVaultLiveQuery, D as DecryptedMergeOptions, n as DeploymentEvent, F as FanoutQueryOptions, o as FanoutResult, p as FieldAuthorityInputs, q as FieldAuthorityPolicy, r as FieldAuthorityPolicyMissingError, s as FieldAuthorityRule, t as FieldLevelDeferredError, L as LiveQueryOptions, u as MergeConflict, v as MergeStrategy, w as MigrationStatusRow, R as RefreshInsightsResult, x as SchemaManifestRow, y as SchemaRolloutResult, z as ShardedCollection, A as ShardedGroupedQuery, B as ShardedQuery, E as ShardingConfig, H as SurfaceStatus, I as VaultRegistryRow, J as mergeCompartment, K as mergeDecryptedRecords, N as resolveFieldAuthority, O as resolveRecordByFieldAuthority } from './vault-group-BXjO5kHB.cjs';
3
+ import { a as VaultTemplate, S as StateManagementVault, V as VaultGroup, b as SkippedVault, M as MergeCompartmentOptions, c as MergeReport, d as SurfaceDirection, e as SurfaceConflictPolicy, f as SurfaceRow, g as VaultGroupOptions } from './vault-group-BchRCDm5.cjs';
4
+ export { C as CapturedBlueprint, h as CrossVaultAggregation, i as CrossVaultDerivationContext, j as CrossVaultDerivationSpec, k as CrossVaultGroupedAggregation, G as CrossVaultGroupedRow, l as CrossVaultLiveAggregation, m as CrossVaultLiveQuery, D as DecryptedMergeOptions, n as DeploymentEvent, F as FanoutQueryOptions, o as FanoutResult, p as FederatedRetrieveHit, q as FederatedRetrieveOptions, r as FederatedRetrieveResult, s as FederationMeta, t as FieldAuthorityInputs, u as FieldAuthorityPolicy, v as FieldAuthorityPolicyMissingError, w as FieldAuthorityRule, x as FieldLevelDeferredError, y as GroupMeta, L as LiveQueryOptions, z as MergeConflict, A as MergeStrategy, B as MigrationStatusRow, R as RefreshInsightsResult, E as SchemaManifestRow, H as SchemaRolloutResult, I as ShardedCollection, J as ShardedGroupedQuery, K as ShardedQuery, N as ShardingConfig, O as SurfaceStatus, P as VaultRegistryRow, Q as mergeCompartment, T as mergeDecryptedRecords, U as resolveFieldAuthority, W as resolveRecordByFieldAuthority } from './vault-group-BchRCDm5.cjs';
5
5
  import { AsXlsxSheetOptions } from '@noy-db/as-xlsx';
6
6
  export { MultiVaultDenormColumn, MultiVaultXlsxEntry, MultiVaultXlsxOptions } from '@noy-db/as-xlsx';
7
7
  import { ExtractionPreview, WriteNoydbBundleOptions } from '@noy-db/hub/bundle';
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Vault, SealingKeyProvider, PublicEnvelope, Noydb } from '@noy-db/hub';
2
2
  export { CustodyApi, DeedMarker, GrantCustodianOptions, LiberateOptions, LiberateResult, createDeedOwner, isDeedVault, liberateVault, loadDeedMarker } from '@noy-db/hub';
3
- import { a as VaultTemplate, S as StateManagementVault, V as VaultGroup, b as SkippedVault, M as MergeCompartmentOptions, c as MergeReport, d as SurfaceDirection, e as SurfaceConflictPolicy, f as SurfaceRow, g as VaultGroupOptions } from './vault-group-BXjO5kHB.js';
4
- export { C as CapturedBlueprint, h as CrossVaultAggregation, i as CrossVaultDerivationContext, j as CrossVaultDerivationSpec, k as CrossVaultGroupedAggregation, G as CrossVaultGroupedRow, l as CrossVaultLiveAggregation, m as CrossVaultLiveQuery, D as DecryptedMergeOptions, n as DeploymentEvent, F as FanoutQueryOptions, o as FanoutResult, p as FieldAuthorityInputs, q as FieldAuthorityPolicy, r as FieldAuthorityPolicyMissingError, s as FieldAuthorityRule, t as FieldLevelDeferredError, L as LiveQueryOptions, u as MergeConflict, v as MergeStrategy, w as MigrationStatusRow, R as RefreshInsightsResult, x as SchemaManifestRow, y as SchemaRolloutResult, z as ShardedCollection, A as ShardedGroupedQuery, B as ShardedQuery, E as ShardingConfig, H as SurfaceStatus, I as VaultRegistryRow, J as mergeCompartment, K as mergeDecryptedRecords, N as resolveFieldAuthority, O as resolveRecordByFieldAuthority } from './vault-group-BXjO5kHB.js';
3
+ import { a as VaultTemplate, S as StateManagementVault, V as VaultGroup, b as SkippedVault, M as MergeCompartmentOptions, c as MergeReport, d as SurfaceDirection, e as SurfaceConflictPolicy, f as SurfaceRow, g as VaultGroupOptions } from './vault-group-BchRCDm5.js';
4
+ export { C as CapturedBlueprint, h as CrossVaultAggregation, i as CrossVaultDerivationContext, j as CrossVaultDerivationSpec, k as CrossVaultGroupedAggregation, G as CrossVaultGroupedRow, l as CrossVaultLiveAggregation, m as CrossVaultLiveQuery, D as DecryptedMergeOptions, n as DeploymentEvent, F as FanoutQueryOptions, o as FanoutResult, p as FederatedRetrieveHit, q as FederatedRetrieveOptions, r as FederatedRetrieveResult, s as FederationMeta, t as FieldAuthorityInputs, u as FieldAuthorityPolicy, v as FieldAuthorityPolicyMissingError, w as FieldAuthorityRule, x as FieldLevelDeferredError, y as GroupMeta, L as LiveQueryOptions, z as MergeConflict, A as MergeStrategy, B as MigrationStatusRow, R as RefreshInsightsResult, E as SchemaManifestRow, H as SchemaRolloutResult, I as ShardedCollection, J as ShardedGroupedQuery, K as ShardedQuery, N as ShardingConfig, O as SurfaceStatus, P as VaultRegistryRow, Q as mergeCompartment, T as mergeDecryptedRecords, U as resolveFieldAuthority, W as resolveRecordByFieldAuthority } from './vault-group-BchRCDm5.js';
5
5
  import { AsXlsxSheetOptions } from '@noy-db/as-xlsx';
6
6
  export { MultiVaultDenormColumn, MultiVaultXlsxEntry, MultiVaultXlsxOptions } from '@noy-db/as-xlsx';
7
7
  import { ExtractionPreview, WriteNoydbBundleOptions } from '@noy-db/hub/bundle';
package/dist/index.js CHANGED
@@ -1254,6 +1254,43 @@ var init_insight_auto_push = __esm({
1254
1254
  }
1255
1255
  });
1256
1256
 
1257
+ // src/federation/partial-reduce.ts
1258
+ function canPartialReduce(spec) {
1259
+ return Object.values(spec).every((r) => typeof r.merge === "function");
1260
+ }
1261
+ function reduceToPartial(records, spec) {
1262
+ const out = {};
1263
+ for (const [key, reducer] of Object.entries(spec)) {
1264
+ const r = reducer;
1265
+ let state = r.init();
1266
+ for (const rec of records) state = r.step(state, rec);
1267
+ out[key] = state;
1268
+ }
1269
+ return out;
1270
+ }
1271
+ function mergePartials(spec, partials) {
1272
+ const out = {};
1273
+ for (const [key, reducer] of Object.entries(spec)) {
1274
+ const r = reducer;
1275
+ let acc = r.init();
1276
+ for (const p of partials) acc = r.merge(acc, p[key]);
1277
+ out[key] = acc;
1278
+ }
1279
+ return out;
1280
+ }
1281
+ function finalizePartial(spec, merged) {
1282
+ const out = {};
1283
+ for (const [key, reducer] of Object.entries(spec)) {
1284
+ out[key] = reducer.finalize(merged[key]);
1285
+ }
1286
+ return out;
1287
+ }
1288
+ var init_partial_reduce = __esm({
1289
+ "src/federation/partial-reduce.ts"() {
1290
+ "use strict";
1291
+ }
1292
+ });
1293
+
1257
1294
  // src/federation/aggregate-across.ts
1258
1295
  import { reduceRecords } from "@noy-db/hub/kernel";
1259
1296
  import { groupAndReduce } from "@noy-db/hub/kernel";
@@ -1262,6 +1299,7 @@ var init_aggregate_across = __esm({
1262
1299
  "src/federation/aggregate-across.ts"() {
1263
1300
  "use strict";
1264
1301
  init_cross_vault_live();
1302
+ init_partial_reduce();
1265
1303
  CrossVaultAggregation = class {
1266
1304
  constructor(src, spec, bind) {
1267
1305
  this.src = src;
@@ -1272,6 +1310,10 @@ var init_aggregate_across = __esm({
1272
1310
  spec;
1273
1311
  bind;
1274
1312
  async run(options = {}) {
1313
+ if (this.src.fanoutReduce && canPartialReduce(this.spec)) {
1314
+ const { partials, skippedVaults: skippedVaults2 } = await this.src.fanoutReduce(this.spec, options);
1315
+ return { result: finalizePartial(this.spec, mergePartials(this.spec, partials)), skippedVaults: skippedVaults2 };
1316
+ }
1275
1317
  const { records, skippedVaults } = await this.src.fanoutRecords(options);
1276
1318
  return { result: reduceRecords(records, this.spec), skippedVaults };
1277
1319
  }
@@ -1360,6 +1402,73 @@ var init_aggregate_across = __esm({
1360
1402
  }
1361
1403
  });
1362
1404
 
1405
+ // src/federation/retrieve-across.ts
1406
+ import { fuseRetrieval } from "@noy-db/hub/kernel";
1407
+ async function retrieveAcross(group, collectionName, query, opts = {}) {
1408
+ const { eligible, skipped } = await group.resolveEligible({
1409
+ ...opts.minVersion !== void 0 ? { minVersion: opts.minVersion } : {},
1410
+ ...opts.failFast !== void 0 ? { failFast: opts.failFast } : {}
1411
+ });
1412
+ const across = await group.db.queryAcross(
1413
+ eligible.map((r) => r.vaultId),
1414
+ async (vault) => {
1415
+ group.template.configure(vault);
1416
+ const coll = vault.collection(collectionName);
1417
+ let within;
1418
+ if (opts.where?.length) {
1419
+ let q = coll.query();
1420
+ for (const [f, op, v] of opts.where) q = q.where(f, op, v);
1421
+ within = q;
1422
+ }
1423
+ return coll.retrieve(query, {
1424
+ ...opts.mode ? { mode: opts.mode } : {},
1425
+ ...opts.limit !== void 0 ? { limit: opts.limit } : {},
1426
+ ...opts.minScore !== void 0 ? { minScore: opts.minScore } : {},
1427
+ ...opts.fields ? { fields: opts.fields } : {},
1428
+ ...opts.match ? { match: opts.match } : {},
1429
+ ...opts.prefix ? { prefix: opts.prefix } : {},
1430
+ ...opts.snippetWindow !== void 0 ? { snippetWindow: opts.snippetWindow } : {},
1431
+ ...opts.includeRecord ? { includeRecord: true } : {},
1432
+ ...within ? { within } : {}
1433
+ });
1434
+ },
1435
+ { concurrency: opts.concurrency ?? 1, create: false }
1436
+ );
1437
+ const skippedVaults = [...skipped];
1438
+ const perVault = [];
1439
+ for (const r of across) {
1440
+ if (r.error) {
1441
+ if (opts.failFast) throw r.error;
1442
+ skippedVaults.push({ vaultId: r.vault, reason: classifyShardSkip(r.error), error: r.error });
1443
+ } else {
1444
+ perVault.push({ vault: r.vault, hits: r.result });
1445
+ }
1446
+ }
1447
+ const lists = perVault.map((pv) => {
1448
+ if (pv.vault.includes(SEP)) {
1449
+ throw new Error(`retrieveAcross: vault id "${pv.vault}" contains the reserved NUL separator`);
1450
+ }
1451
+ return pv.hits.map((h) => ({ ...h, id: pv.vault + SEP + h.id }));
1452
+ });
1453
+ const fused = fuseRetrieval(lists, {
1454
+ ...opts.limit !== void 0 ? { limit: opts.limit } : {},
1455
+ ...opts.rrfK !== void 0 ? { k: opts.rrfK } : {}
1456
+ });
1457
+ const hits = fused.map((h) => {
1458
+ const i = h.id.indexOf(SEP);
1459
+ return { ...h, vault: h.id.slice(0, i), id: h.id.slice(i + 1) };
1460
+ });
1461
+ return { hits, skippedVaults };
1462
+ }
1463
+ var SEP;
1464
+ var init_retrieve_across = __esm({
1465
+ "src/federation/retrieve-across.ts"() {
1466
+ "use strict";
1467
+ init_classify_skip();
1468
+ SEP = "\0";
1469
+ }
1470
+ });
1471
+
1363
1472
  // src/federation/vault-group.ts
1364
1473
  var vault_group_exports = {};
1365
1474
  __export(vault_group_exports, {
@@ -1398,16 +1507,19 @@ var init_vault_group = __esm({
1398
1507
  init_cross_vault_live();
1399
1508
  init_insight_auto_push();
1400
1509
  init_aggregate_across();
1510
+ init_retrieve_across();
1511
+ init_partial_reduce();
1401
1512
  SHARD_SEPARATOR = "--";
1402
1513
  SAFE_PARTITION_KEY = /^[A-Za-z0-9._-]+$/;
1403
1514
  VaultGroup = class {
1404
- constructor(db, name, registry, sharding, template, cutoverOnOpen = false) {
1515
+ constructor(db, name, registry, sharding, template, cutoverOnOpen = false, meta) {
1405
1516
  this.db = db;
1406
1517
  this.name = name;
1407
1518
  this.registry = registry;
1408
1519
  this.sharding = sharding;
1409
1520
  this.template = template;
1410
1521
  this.cutoverOnOpen = cutoverOnOpen;
1522
+ this.meta = meta;
1411
1523
  if (name.includes(SHARD_SEPARATOR)) {
1412
1524
  throw new ValidationError(
1413
1525
  `VaultGroup name "${name}" must not contain "--" (reserved shard vault-id separator).`
@@ -1420,6 +1532,7 @@ var init_vault_group = __esm({
1420
1532
  sharding;
1421
1533
  template;
1422
1534
  cutoverOnOpen;
1535
+ meta;
1423
1536
  /** @internal — set when the group is managed (no explicit registry). */
1424
1537
  stateVault;
1425
1538
  /** @internal */
@@ -1453,6 +1566,30 @@ var init_vault_group = __esm({
1453
1566
  const rows = this.registry.query().toArray();
1454
1567
  return rows.filter((r) => r.group === this.name);
1455
1568
  }
1569
+ /**
1570
+ * Federation-level descriptive metadata (#27): this group's `meta` plus each
1571
+ * member vault's `vaultMeta` (read via `getMeta()`). Best-effort per shard — a
1572
+ * shard that fails to open yields `meta: undefined` for that entry, never
1573
+ * throwing. Member meta is transient per-open (noy-db sets it at `openVault`,
1574
+ * does not persist it); this surfaces whatever each vault was opened with.
1575
+ */
1576
+ async federationMeta() {
1577
+ const rows = await this.allRows();
1578
+ const vaults = await Promise.all(
1579
+ rows.map(async (row) => {
1580
+ let meta;
1581
+ try {
1582
+ const vault = await this.db.openVault(row.vaultId);
1583
+ this.template.configure(vault);
1584
+ meta = vault.getMeta();
1585
+ } catch {
1586
+ meta = void 0;
1587
+ }
1588
+ return { vaultId: row.vaultId, partitionKey: row.partitionKey, meta };
1589
+ })
1590
+ );
1591
+ return { meta: this.meta, vaults };
1592
+ }
1456
1593
  /**
1457
1594
  * Open an existing shard and apply the template. When `cutoverOnOpen` is set
1458
1595
  * (#271) and the shard's registry version is behind the template, its cutover
@@ -1536,22 +1673,33 @@ var init_vault_group = __esm({
1536
1673
  collection(collectionName) {
1537
1674
  return new ShardedCollection(this, collectionName);
1538
1675
  }
1539
- /** @internal — eligible (openable-candidate) rows + drift/divergence skips. */
1676
+ /** @internal — eligible (openable-candidate) rows + drift/divergence/unreachable skips. */
1540
1677
  async resolveEligible(options = {}) {
1541
1678
  const rows = await this.allRows();
1679
+ const candidates = options.only ? rows.filter((r) => options.only.includes(r.partitionKey)) : rows;
1542
1680
  const skipped = [];
1543
1681
  const versionOk = [];
1544
- for (const row of rows) {
1682
+ for (const row of candidates) {
1545
1683
  if (options.minVersion !== void 0 && row.schemaVersion < options.minVersion) {
1546
1684
  skipped.push({ vaultId: row.vaultId, reason: "schema-drift" });
1547
1685
  } else versionOk.push(row);
1548
1686
  }
1549
- const provisioned = await Promise.all(versionOk.map((r) => this.db._shardVaultProvisioned(r.vaultId)));
1687
+ const probes = await Promise.all(
1688
+ versionOk.map(async (row) => {
1689
+ try {
1690
+ return { row, provisioned: await this.db._shardVaultProvisioned(row.vaultId) };
1691
+ } catch (err) {
1692
+ if (options.failFast) throw err;
1693
+ return { row, error: err };
1694
+ }
1695
+ })
1696
+ );
1550
1697
  const eligible = [];
1551
- versionOk.forEach((row, i) => {
1552
- if (provisioned[i]) eligible.push(row);
1553
- else skipped.push({ vaultId: row.vaultId, reason: "error", error: new ShardProvisioningError(row.vaultId, row.partitionKey) });
1554
- });
1698
+ for (const p of probes) {
1699
+ if ("error" in p) skipped.push({ vaultId: p.row.vaultId, reason: "error", error: p.error });
1700
+ else if (p.provisioned) eligible.push(p.row);
1701
+ else skipped.push({ vaultId: p.row.vaultId, reason: "error", error: new ShardProvisioningError(p.row.vaultId, p.row.partitionKey) });
1702
+ }
1555
1703
  return { eligible, skipped };
1556
1704
  }
1557
1705
  /** @internal — registered push-model cross-vault derivations (#271 Insight Vault). */
@@ -1611,12 +1759,19 @@ var init_vault_group = __esm({
1611
1759
  * Insight Vault keyed by partition key. Shards behind `minVersion`,
1612
1760
  * unprovisioned, or whose read errors are reported in `skippedVaults` and
1613
1761
  * are not written (a stale summary is never left behind for a failed shard).
1762
+ * A shard whose backend is unreachable (provisioning probe or read throws) is
1763
+ * **skipped** (`reason: 'error'`) and the pass continues for the others — its
1764
+ * prior summary is left intact. Pass `failFast: true` for the legacy
1765
+ * all-or-nothing throw. Reconcile a lagging shard later with
1766
+ * `refreshInsights({ only: [pk] })` or `refreshDerivation(pk)`.
1614
1767
  */
1615
1768
  async refreshInsights(options = {}) {
1616
1769
  if (this.crossVaultDerivations.length === 0) return { written: 0, skippedVaults: [] };
1617
- const { eligible, skipped } = await this.resolveEligible(
1618
- options.minVersion !== void 0 ? { minVersion: options.minVersion } : {}
1619
- );
1770
+ const { eligible, skipped } = await this.resolveEligible({
1771
+ ...options.minVersion !== void 0 ? { minVersion: options.minVersion } : {},
1772
+ ...options.only !== void 0 ? { only: options.only } : {},
1773
+ ...options.failFast !== void 0 ? { failFast: options.failFast } : {}
1774
+ });
1620
1775
  let written = 0;
1621
1776
  for (const spec of this.crossVaultDerivations) {
1622
1777
  const results = await this.db.queryAcross(
@@ -1633,6 +1788,7 @@ var init_vault_group = __esm({
1633
1788
  const row = eligible[i];
1634
1789
  const res = results[i];
1635
1790
  if (!res || res.result === void 0) {
1791
+ if (options.failFast && res?.error) throw res.error;
1636
1792
  skipped.push({ vaultId: row.vaultId, reason: "error", ...res?.error ? { error: res.error } : {} });
1637
1793
  continue;
1638
1794
  }
@@ -1648,6 +1804,14 @@ var init_vault_group = __esm({
1648
1804
  }
1649
1805
  return { written, skippedVaults: skipped };
1650
1806
  }
1807
+ /**
1808
+ * Reconcile one shard's Insight summaries after its backend was unreachable.
1809
+ * Equivalent to `refreshInsights({ only: [partitionKey] })` — runs every
1810
+ * registered derivation (autoPush or not) for just this shard.
1811
+ */
1812
+ async refreshDerivation(partitionKey) {
1813
+ return this.refreshInsights({ only: [partitionKey] });
1814
+ }
1651
1815
  /** @internal — re-derive + push every autoPush derivation's summary for one shard. */
1652
1816
  async _recomputeShardInsights(partitionKey) {
1653
1817
  const row = await this.registry.get(this.registryId(partitionKey));
@@ -1787,6 +1951,17 @@ var init_vault_group = __esm({
1787
1951
  query() {
1788
1952
  return new ShardedQuery(this.group, this.collectionName, []);
1789
1953
  }
1954
+ /**
1955
+ * Cross-vault federated retrieval (#26): scatter-gather across all eligible
1956
+ * shards — each shard runs its own trusted-tier `retrieve()`, then results are
1957
+ * RRF-fused by rank only (no cross-vault statistics cross the DEK boundary).
1958
+ * Every hit carries its originating `vault`. An unreachable, schema-drifted, or
1959
+ * un-indexed shard is skipped (`skippedVaults`); pass `failFast` to re-throw
1960
+ * the first per-shard error instead.
1961
+ */
1962
+ async retrieve(query, opts = {}) {
1963
+ return retrieveAcross(this.group, this.collectionName, query, opts);
1964
+ }
1790
1965
  };
1791
1966
  ShardedQuery = class _ShardedQuery {
1792
1967
  constructor(group, collectionName, clauses, coPartitionedLegs = [], broadcastLegs = []) {
@@ -1883,6 +2058,34 @@ var init_vault_group = __esm({
1883
2058
  }
1884
2059
  return { records: results, skippedVaults: skipped };
1885
2060
  }
2061
+ /**
2062
+ * @internal — distributed partial-reduce (#8). Fan out across eligible shards,
2063
+ * but fold each shard's where-filtered records to a partial reducer STATE
2064
+ * inside the per-shard callback (never returning the rows). Only the scalar
2065
+ * `.aggregate()` path calls this, and that path rejects join legs upstream.
2066
+ */
2067
+ async fanoutReduce(spec, options = {}) {
2068
+ const { eligible, skipped } = await this.group.resolveEligible(options);
2069
+ const across = await this.group.db.queryAcross(
2070
+ eligible.map((r) => r.vaultId),
2071
+ async (vault) => {
2072
+ this.group.template.configure(vault);
2073
+ const coll = vault.collection(this.collectionName);
2074
+ await coll.list();
2075
+ let q = coll.query();
2076
+ for (const c of this.clauses) q = q.where(c.field, c.op, c.value);
2077
+ const rows = q.toArray();
2078
+ return reduceToPartial(rows, spec);
2079
+ },
2080
+ { concurrency: options.concurrency ?? 1, create: false }
2081
+ );
2082
+ const partials = [];
2083
+ for (const r of across) {
2084
+ if (r.error) skipped.push({ vaultId: r.vault, reason: classifyShardSkip(r.error), error: r.error });
2085
+ else partials.push(r.result);
2086
+ }
2087
+ return { partials, skippedVaults: skipped };
2088
+ }
1886
2089
  /** Fan out across eligible shards, merge, then apply any broadcast dimension legs. */
1887
2090
  async toArray(options = {}) {
1888
2091
  const { records, skippedVaults } = await this.fanoutRecords(options);
@@ -2154,7 +2357,7 @@ var Lobby = class {
2154
2357
  const { StateManagementVault: StateManagementVault2 } = await Promise.resolve().then(() => (init_state_vault(), state_vault_exports));
2155
2358
  const stateVault = opts.registry ? void 0 : await StateManagementVault2.open(db);
2156
2359
  const registry = opts.registry ?? stateVault.registry;
2157
- const group = new VaultGroup2(db, name, registry, opts.sharding, template, opts.cutoverOnOpen ?? false);
2360
+ const group = new VaultGroup2(db, name, registry, opts.sharding, template, opts.cutoverOnOpen ?? false, opts.meta);
2158
2361
  if (stateVault) {
2159
2362
  group._attachStateVault(stateVault);
2160
2363
  await stateVault.recordManifest(opts.sharding.vaultTemplate, template);