@klum-db/lobby 0.2.0-pre.31 → 0.2.0-pre.33
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/bin/klum.d.cts +1 -1
- package/dist/bin/klum.d.ts +1 -1
- package/dist/index.cjs +206 -50
- 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 +178 -22
- package/dist/index.js.map +1 -1
- package/dist/{vault-group-DqEyXbN1.d.cts → vault-group-SfkQJ3CM.d.cts} +87 -7
- package/dist/{vault-group-DqEyXbN1.d.ts → vault-group-SfkQJ3CM.d.ts} +87 -7
- 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-SfkQJ3CM.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, I as InsightAutoPushConfig, L as LiveQueryOptions, z as MergeConflict, A as MergeStrategy, B as MigrationStatusRow, R as RefreshInsightsResult, E as SchemaManifestRow, H as SchemaRolloutResult, J as ShardedCollection, K as ShardedGroupedQuery, N as ShardedQuery, O as ShardingConfig, P as SurfaceStatus, Q as VaultRegistryRow, T as mergeCompartment, U as mergeDecryptedRecords, W as resolveFieldAuthority, X as resolveRecordByFieldAuthority } from './vault-group-SfkQJ3CM.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-SfkQJ3CM.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, I as InsightAutoPushConfig, L as LiveQueryOptions, z as MergeConflict, A as MergeStrategy, B as MigrationStatusRow, R as RefreshInsightsResult, E as SchemaManifestRow, H as SchemaRolloutResult, J as ShardedCollection, K as ShardedGroupedQuery, N as ShardedQuery, O as ShardingConfig, P as SurfaceStatus, Q as VaultRegistryRow, T as mergeCompartment, U as mergeDecryptedRecords, W as resolveFieldAuthority, X as resolveRecordByFieldAuthority } from './vault-group-SfkQJ3CM.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
|
@@ -1209,28 +1209,33 @@ var init_insight_auto_push = __esm({
|
|
|
1209
1209
|
"src/federation/insight-auto-push.ts"() {
|
|
1210
1210
|
"use strict";
|
|
1211
1211
|
InsightAutoPush = class {
|
|
1212
|
-
constructor(recompute, isSource, onError = (err, pk) => console.warn(`[klum-db] insight auto-push failed for shard "${pk}":`, err)) {
|
|
1212
|
+
constructor(recompute, isSource, onError = (err, pk) => console.warn(`[klum-db] insight auto-push failed for shard "${pk}":`, err), debounceMs) {
|
|
1213
1213
|
this.recompute = recompute;
|
|
1214
1214
|
this.isSource = isSource;
|
|
1215
1215
|
this.onError = onError;
|
|
1216
|
+
this.debounceMs = debounceMs;
|
|
1216
1217
|
}
|
|
1217
1218
|
recompute;
|
|
1218
1219
|
isSource;
|
|
1219
1220
|
onError;
|
|
1220
|
-
|
|
1221
|
+
debounceMs;
|
|
1221
1222
|
dirty = /* @__PURE__ */ new Set();
|
|
1222
|
-
/** The in-flight flush, or null when idle. */
|
|
1223
1223
|
pending = null;
|
|
1224
|
+
timer = null;
|
|
1225
|
+
resolvePending = null;
|
|
1226
|
+
flushing = false;
|
|
1224
1227
|
/** Called from a shard's onAfterWrite hook. Marks the shard dirty + schedules a flush. */
|
|
1225
1228
|
noteWrite(partitionKey, collection) {
|
|
1226
1229
|
if (!this.isSource(collection)) return;
|
|
1227
1230
|
this.dirty.add(partitionKey);
|
|
1228
|
-
this.
|
|
1231
|
+
if (this.debounceMs !== void 0) this.scheduleDebounced();
|
|
1232
|
+
else this.schedule();
|
|
1229
1233
|
}
|
|
1230
1234
|
/** Resolve once no flush is pending (drains rescheduled flushes too). */
|
|
1231
1235
|
async whenSettled() {
|
|
1232
1236
|
while (this.pending) await this.pending;
|
|
1233
1237
|
}
|
|
1238
|
+
// ── microtask path (unchanged default) ──
|
|
1234
1239
|
schedule() {
|
|
1235
1240
|
if (this.pending) return;
|
|
1236
1241
|
this.pending = this.runFlush().finally(() => {
|
|
@@ -1238,6 +1243,29 @@ var init_insight_auto_push = __esm({
|
|
|
1238
1243
|
if (this.dirty.size > 0) this.schedule();
|
|
1239
1244
|
});
|
|
1240
1245
|
}
|
|
1246
|
+
// ── debounce path (#13) ──
|
|
1247
|
+
scheduleDebounced() {
|
|
1248
|
+
if (!this.pending) this.pending = new Promise((r) => {
|
|
1249
|
+
this.resolvePending = r;
|
|
1250
|
+
});
|
|
1251
|
+
if (this.timer) clearTimeout(this.timer);
|
|
1252
|
+
this.timer = setTimeout(() => {
|
|
1253
|
+
this.timer = null;
|
|
1254
|
+
if (this.flushing) return;
|
|
1255
|
+
this.flushing = true;
|
|
1256
|
+
void this.runFlush().finally(() => {
|
|
1257
|
+
this.flushing = false;
|
|
1258
|
+
if (this.dirty.size > 0) {
|
|
1259
|
+
this.scheduleDebounced();
|
|
1260
|
+
} else {
|
|
1261
|
+
const resolve = this.resolvePending;
|
|
1262
|
+
this.resolvePending = null;
|
|
1263
|
+
this.pending = null;
|
|
1264
|
+
resolve?.();
|
|
1265
|
+
}
|
|
1266
|
+
});
|
|
1267
|
+
}, this.debounceMs);
|
|
1268
|
+
}
|
|
1241
1269
|
async runFlush() {
|
|
1242
1270
|
await Promise.resolve();
|
|
1243
1271
|
const pks = [...this.dirty];
|
|
@@ -1402,6 +1430,73 @@ var init_aggregate_across = __esm({
|
|
|
1402
1430
|
}
|
|
1403
1431
|
});
|
|
1404
1432
|
|
|
1433
|
+
// src/federation/retrieve-across.ts
|
|
1434
|
+
import { fuseRetrieval } from "@noy-db/hub/kernel";
|
|
1435
|
+
async function retrieveAcross(group, collectionName, query, opts = {}) {
|
|
1436
|
+
const { eligible, skipped } = await group.resolveEligible({
|
|
1437
|
+
...opts.minVersion !== void 0 ? { minVersion: opts.minVersion } : {},
|
|
1438
|
+
...opts.failFast !== void 0 ? { failFast: opts.failFast } : {}
|
|
1439
|
+
});
|
|
1440
|
+
const across = await group.db.queryAcross(
|
|
1441
|
+
eligible.map((r) => r.vaultId),
|
|
1442
|
+
async (vault) => {
|
|
1443
|
+
group.template.configure(vault);
|
|
1444
|
+
const coll = vault.collection(collectionName);
|
|
1445
|
+
let within;
|
|
1446
|
+
if (opts.where?.length) {
|
|
1447
|
+
let q = coll.query();
|
|
1448
|
+
for (const [f, op, v] of opts.where) q = q.where(f, op, v);
|
|
1449
|
+
within = q;
|
|
1450
|
+
}
|
|
1451
|
+
return coll.retrieve(query, {
|
|
1452
|
+
...opts.mode ? { mode: opts.mode } : {},
|
|
1453
|
+
...opts.limit !== void 0 ? { limit: opts.limit } : {},
|
|
1454
|
+
...opts.minScore !== void 0 ? { minScore: opts.minScore } : {},
|
|
1455
|
+
...opts.fields ? { fields: opts.fields } : {},
|
|
1456
|
+
...opts.match ? { match: opts.match } : {},
|
|
1457
|
+
...opts.prefix ? { prefix: opts.prefix } : {},
|
|
1458
|
+
...opts.snippetWindow !== void 0 ? { snippetWindow: opts.snippetWindow } : {},
|
|
1459
|
+
...opts.includeRecord ? { includeRecord: true } : {},
|
|
1460
|
+
...within ? { within } : {}
|
|
1461
|
+
});
|
|
1462
|
+
},
|
|
1463
|
+
{ concurrency: opts.concurrency ?? 1, create: false }
|
|
1464
|
+
);
|
|
1465
|
+
const skippedVaults = [...skipped];
|
|
1466
|
+
const perVault = [];
|
|
1467
|
+
for (const r of across) {
|
|
1468
|
+
if (r.error) {
|
|
1469
|
+
if (opts.failFast) throw r.error;
|
|
1470
|
+
skippedVaults.push({ vaultId: r.vault, reason: classifyShardSkip(r.error), error: r.error });
|
|
1471
|
+
} else {
|
|
1472
|
+
perVault.push({ vault: r.vault, hits: r.result });
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
const lists = perVault.map((pv) => {
|
|
1476
|
+
if (pv.vault.includes(SEP)) {
|
|
1477
|
+
throw new Error(`retrieveAcross: vault id "${pv.vault}" contains the reserved NUL separator`);
|
|
1478
|
+
}
|
|
1479
|
+
return pv.hits.map((h) => ({ ...h, id: pv.vault + SEP + h.id }));
|
|
1480
|
+
});
|
|
1481
|
+
const fused = fuseRetrieval(lists, {
|
|
1482
|
+
...opts.limit !== void 0 ? { limit: opts.limit } : {},
|
|
1483
|
+
...opts.rrfK !== void 0 ? { k: opts.rrfK } : {}
|
|
1484
|
+
});
|
|
1485
|
+
const hits = fused.map((h) => {
|
|
1486
|
+
const i = h.id.indexOf(SEP);
|
|
1487
|
+
return { ...h, vault: h.id.slice(0, i), id: h.id.slice(i + 1) };
|
|
1488
|
+
});
|
|
1489
|
+
return { hits, skippedVaults };
|
|
1490
|
+
}
|
|
1491
|
+
var SEP;
|
|
1492
|
+
var init_retrieve_across = __esm({
|
|
1493
|
+
"src/federation/retrieve-across.ts"() {
|
|
1494
|
+
"use strict";
|
|
1495
|
+
init_classify_skip();
|
|
1496
|
+
SEP = "\0";
|
|
1497
|
+
}
|
|
1498
|
+
});
|
|
1499
|
+
|
|
1405
1500
|
// src/federation/vault-group.ts
|
|
1406
1501
|
var vault_group_exports = {};
|
|
1407
1502
|
__export(vault_group_exports, {
|
|
@@ -1411,6 +1506,10 @@ __export(vault_group_exports, {
|
|
|
1411
1506
|
VaultGroup: () => VaultGroup
|
|
1412
1507
|
});
|
|
1413
1508
|
import { CrossShardJoinError, DataResidencyError, ReservedVaultNameError, ShardProvisioningError, UnknownShardError, ValidationError } from "@noy-db/hub/kernel";
|
|
1509
|
+
function autoPushConfig(spec) {
|
|
1510
|
+
if (!spec.autoPush) return null;
|
|
1511
|
+
return spec.autoPush === true ? {} : spec.autoPush;
|
|
1512
|
+
}
|
|
1414
1513
|
function assertSafePartitionKey(partitionKey) {
|
|
1415
1514
|
if (partitionKey.length === 0) {
|
|
1416
1515
|
throw new ValidationError("partitionKey must be a non-empty string");
|
|
@@ -1440,17 +1539,19 @@ var init_vault_group = __esm({
|
|
|
1440
1539
|
init_cross_vault_live();
|
|
1441
1540
|
init_insight_auto_push();
|
|
1442
1541
|
init_aggregate_across();
|
|
1542
|
+
init_retrieve_across();
|
|
1443
1543
|
init_partial_reduce();
|
|
1444
1544
|
SHARD_SEPARATOR = "--";
|
|
1445
1545
|
SAFE_PARTITION_KEY = /^[A-Za-z0-9._-]+$/;
|
|
1446
1546
|
VaultGroup = class {
|
|
1447
|
-
constructor(db, name, registry, sharding, template, cutoverOnOpen = false) {
|
|
1547
|
+
constructor(db, name, registry, sharding, template, cutoverOnOpen = false, meta) {
|
|
1448
1548
|
this.db = db;
|
|
1449
1549
|
this.name = name;
|
|
1450
1550
|
this.registry = registry;
|
|
1451
1551
|
this.sharding = sharding;
|
|
1452
1552
|
this.template = template;
|
|
1453
1553
|
this.cutoverOnOpen = cutoverOnOpen;
|
|
1554
|
+
this.meta = meta;
|
|
1454
1555
|
if (name.includes(SHARD_SEPARATOR)) {
|
|
1455
1556
|
throw new ValidationError(
|
|
1456
1557
|
`VaultGroup name "${name}" must not contain "--" (reserved shard vault-id separator).`
|
|
@@ -1463,6 +1564,7 @@ var init_vault_group = __esm({
|
|
|
1463
1564
|
sharding;
|
|
1464
1565
|
template;
|
|
1465
1566
|
cutoverOnOpen;
|
|
1567
|
+
meta;
|
|
1466
1568
|
/** @internal — set when the group is managed (no explicit registry). */
|
|
1467
1569
|
stateVault;
|
|
1468
1570
|
/** @internal */
|
|
@@ -1496,6 +1598,30 @@ var init_vault_group = __esm({
|
|
|
1496
1598
|
const rows = this.registry.query().toArray();
|
|
1497
1599
|
return rows.filter((r) => r.group === this.name);
|
|
1498
1600
|
}
|
|
1601
|
+
/**
|
|
1602
|
+
* Federation-level descriptive metadata (#27): this group's `meta` plus each
|
|
1603
|
+
* member vault's `vaultMeta` (read via `getMeta()`). Best-effort per shard — a
|
|
1604
|
+
* shard that fails to open yields `meta: undefined` for that entry, never
|
|
1605
|
+
* throwing. Member meta is transient per-open (noy-db sets it at `openVault`,
|
|
1606
|
+
* does not persist it); this surfaces whatever each vault was opened with.
|
|
1607
|
+
*/
|
|
1608
|
+
async federationMeta() {
|
|
1609
|
+
const rows = await this.allRows();
|
|
1610
|
+
const vaults = await Promise.all(
|
|
1611
|
+
rows.map(async (row) => {
|
|
1612
|
+
let meta;
|
|
1613
|
+
try {
|
|
1614
|
+
const vault = await this.db.openVault(row.vaultId);
|
|
1615
|
+
this.template.configure(vault);
|
|
1616
|
+
meta = vault.getMeta();
|
|
1617
|
+
} catch {
|
|
1618
|
+
meta = void 0;
|
|
1619
|
+
}
|
|
1620
|
+
return { vaultId: row.vaultId, partitionKey: row.partitionKey, meta };
|
|
1621
|
+
})
|
|
1622
|
+
);
|
|
1623
|
+
return { meta: this.meta, vaults };
|
|
1624
|
+
}
|
|
1499
1625
|
/**
|
|
1500
1626
|
* Open an existing shard and apply the template. When `cutoverOnOpen` is set
|
|
1501
1627
|
* (#271) and the shard's registry version is behind the template, its cutover
|
|
@@ -1647,9 +1773,12 @@ var init_vault_group = __esm({
|
|
|
1647
1773
|
}
|
|
1648
1774
|
this.crossVaultDerivations.push(spec);
|
|
1649
1775
|
if (spec.autoPush && !this.insightAutoPush) {
|
|
1776
|
+
const cfg = autoPushConfig(spec);
|
|
1650
1777
|
const controller = new InsightAutoPush(
|
|
1651
1778
|
(pk) => this._recomputeShardInsights(pk),
|
|
1652
|
-
(collection) => this.crossVaultDerivations.some((s) => s.autoPush && s.source === collection)
|
|
1779
|
+
(collection) => this.crossVaultDerivations.some((s) => s.autoPush && s.source === collection),
|
|
1780
|
+
void 0,
|
|
1781
|
+
cfg?.debounceMs
|
|
1653
1782
|
);
|
|
1654
1783
|
this.insightAutoPush = controller;
|
|
1655
1784
|
const prefix = `${this.name}${SHARD_SEPARATOR}`;
|
|
@@ -1730,6 +1859,8 @@ var init_vault_group = __esm({
|
|
|
1730
1859
|
};
|
|
1731
1860
|
for (const spec of this.crossVaultDerivations) {
|
|
1732
1861
|
if (!spec.autoPush) continue;
|
|
1862
|
+
const min = autoPushConfig(spec)?.minVersion;
|
|
1863
|
+
if (min !== void 0 && row.schemaVersion < min) continue;
|
|
1733
1864
|
const records = await shard.collection(spec.source).list();
|
|
1734
1865
|
const summary = spec.derive(records, ctx);
|
|
1735
1866
|
const insight = await this.db.openVault(spec.target.vault);
|
|
@@ -1857,6 +1988,17 @@ var init_vault_group = __esm({
|
|
|
1857
1988
|
query() {
|
|
1858
1989
|
return new ShardedQuery(this.group, this.collectionName, []);
|
|
1859
1990
|
}
|
|
1991
|
+
/**
|
|
1992
|
+
* Cross-vault federated retrieval (#26): scatter-gather across all eligible
|
|
1993
|
+
* shards — each shard runs its own trusted-tier `retrieve()`, then results are
|
|
1994
|
+
* RRF-fused by rank only (no cross-vault statistics cross the DEK boundary).
|
|
1995
|
+
* Every hit carries its originating `vault`. An unreachable, schema-drifted, or
|
|
1996
|
+
* un-indexed shard is skipped (`skippedVaults`); pass `failFast` to re-throw
|
|
1997
|
+
* the first per-shard error instead.
|
|
1998
|
+
*/
|
|
1999
|
+
async retrieve(query, opts = {}) {
|
|
2000
|
+
return retrieveAcross(this.group, this.collectionName, query, opts);
|
|
2001
|
+
}
|
|
1860
2002
|
};
|
|
1861
2003
|
ShardedQuery = class _ShardedQuery {
|
|
1862
2004
|
constructor(group, collectionName, clauses, coPartitionedLegs = [], broadcastLegs = []) {
|
|
@@ -1999,23 +2141,23 @@ var init_vault_group = __esm({
|
|
|
1999
2141
|
isRelevant: (e) => e.collection === collectionName && e.vault.startsWith(`${group.name}--`)
|
|
2000
2142
|
};
|
|
2001
2143
|
}
|
|
2002
|
-
/**
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2144
|
+
/**
|
|
2145
|
+
* Returns a reactive cross-shard live query — a facade over CrossVaultLive.
|
|
2146
|
+
*
|
|
2147
|
+
* Joined queries (crossShardJoin / broadcastJoin) are supported (#14): the live
|
|
2148
|
+
* value reflects the fully-joined rows. **v1 limitation:** recomputes only on
|
|
2149
|
+
* writes to the primary (left) collection; writes to a co-partitioned right
|
|
2150
|
+
* collection or a broadcast-dimension collection do NOT trigger a recompute —
|
|
2151
|
+
* re-run the query to pick those up.
|
|
2152
|
+
*/
|
|
2011
2153
|
live(options = {}) {
|
|
2012
|
-
this.assertNoJoinLegs("live");
|
|
2013
2154
|
const bind = this.liveBinding();
|
|
2014
2155
|
const core = new CrossVaultLive({
|
|
2015
2156
|
...bind,
|
|
2016
2157
|
compute: async () => {
|
|
2017
2158
|
const { records, skippedVaults } = await this.fanoutRecords(options);
|
|
2018
|
-
|
|
2159
|
+
const joined = await applyBroadcastLegs(records, this.broadcastLegs);
|
|
2160
|
+
return { records: joined, skipped: skippedVaults };
|
|
2019
2161
|
},
|
|
2020
2162
|
initialSnapshot: { records: [], skipped: [] },
|
|
2021
2163
|
...options.debounceMs !== void 0 ? { debounceMs: options.debounceMs } : {}
|
|
@@ -2035,14 +2177,28 @@ var init_vault_group = __esm({
|
|
|
2035
2177
|
stop: () => core.stop()
|
|
2036
2178
|
};
|
|
2037
2179
|
}
|
|
2180
|
+
/**
|
|
2181
|
+
* @internal — the FanoutRecordSource for aggregate surfaces. With no join legs,
|
|
2182
|
+
* returns `this` (partial-reduce-eligible, #8). With join legs, returns a
|
|
2183
|
+
* toArray-backed source (fully-joined rows) and NO fanoutReduce, forcing
|
|
2184
|
+
* central-reduce over the joined rows (partial-reduce can't span the central
|
|
2185
|
+
* broadcast join). Used by scalar `aggregate()` and `ShardedGroupedQuery`.
|
|
2186
|
+
*/
|
|
2187
|
+
aggregateSource() {
|
|
2188
|
+
if (!this.coPartitionedLegs.length && !this.broadcastLegs.length) return this;
|
|
2189
|
+
return {
|
|
2190
|
+
fanoutRecords: async (o) => {
|
|
2191
|
+
const { results, skippedVaults } = await this.toArray(o);
|
|
2192
|
+
return { records: results, skippedVaults };
|
|
2193
|
+
}
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2038
2196
|
/** One-shot distributed aggregate — central reduce over all shard records. */
|
|
2039
2197
|
aggregate(spec) {
|
|
2040
|
-
this.
|
|
2041
|
-
return new CrossVaultAggregation(this, spec, this.liveBinding());
|
|
2198
|
+
return new CrossVaultAggregation(this.aggregateSource(), spec, this.liveBinding());
|
|
2042
2199
|
}
|
|
2043
2200
|
/** Begin a grouped cross-shard aggregate. */
|
|
2044
2201
|
groupBy(field) {
|
|
2045
|
-
this.assertNoJoinLegs("groupBy");
|
|
2046
2202
|
return new ShardedGroupedQuery(this, field);
|
|
2047
2203
|
}
|
|
2048
2204
|
};
|
|
@@ -2055,7 +2211,7 @@ var init_vault_group = __esm({
|
|
|
2055
2211
|
field;
|
|
2056
2212
|
aggregate(spec) {
|
|
2057
2213
|
return new CrossVaultGroupedAggregation(
|
|
2058
|
-
|
|
2214
|
+
this.query.aggregateSource(),
|
|
2059
2215
|
this.field,
|
|
2060
2216
|
spec,
|
|
2061
2217
|
this.query.liveBinding()
|
|
@@ -2252,7 +2408,7 @@ var Lobby = class {
|
|
|
2252
2408
|
const { StateManagementVault: StateManagementVault2 } = await Promise.resolve().then(() => (init_state_vault(), state_vault_exports));
|
|
2253
2409
|
const stateVault = opts.registry ? void 0 : await StateManagementVault2.open(db);
|
|
2254
2410
|
const registry = opts.registry ?? stateVault.registry;
|
|
2255
|
-
const group = new VaultGroup2(db, name, registry, opts.sharding, template, opts.cutoverOnOpen ?? false);
|
|
2411
|
+
const group = new VaultGroup2(db, name, registry, opts.sharding, template, opts.cutoverOnOpen ?? false, opts.meta);
|
|
2256
2412
|
if (stateVault) {
|
|
2257
2413
|
group._attachStateVault(stateVault);
|
|
2258
2414
|
await stateVault.recordManifest(opts.sharding.vaultTemplate, template);
|