@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/README.md +55 -28
- package/dist/bin/klum.d.cts +1 -1
- package/dist/bin/klum.d.ts +1 -1
- package/dist/index.cjs +243 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +215 -12
- package/dist/index.js.map +1 -1
- package/dist/{vault-group-BXjO5kHB.d.cts → vault-group-BchRCDm5.d.cts} +110 -6
- package/dist/{vault-group-BXjO5kHB.d.ts → vault-group-BchRCDm5.d.ts} +110 -6
- package/package.json +11 -11
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-
|
|
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
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
1552
|
-
if (
|
|
1553
|
-
else
|
|
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);
|