@klum-db/lobby 0.2.0-pre.32 → 0.3.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.d.cts CHANGED
@@ -1,13 +1,13 @@
1
- import { Vault, SealingKeyProvider, PublicEnvelope, Noydb } from '@noy-db/hub';
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-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';
1
+ import { Vault, SealingKeyProvider, Noydb } from '@noy-db/hub/cargo';
2
+ export { CrossShardJoinError, CustodyApi, DataResidencyError, DeedMarker, GrantCustodianOptions, LiberateOptions, LiberateResult, ReservedVaultNameError, ShardProvisioningError, UnknownShardError, VaultTemplateNotFoundError, createDeedOwner, isDeedVault, liberateVault, loadDeedMarker } from '@noy-db/hub/cargo';
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-D0EC-D5c.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-D0EC-D5c.cjs';
5
5
  import { AsXlsxSheetOptions } from '@noy-db/as-xlsx';
6
6
  export { MultiVaultDenormColumn, MultiVaultXlsxEntry, MultiVaultXlsxOptions } from '@noy-db/as-xlsx';
7
- import { ExtractionPreview, WriteNoydbBundleOptions } from '@noy-db/hub/bundle';
8
- import { Vault as Vault$1 } from '@noy-db/hub/kernel';
9
- export { CrossShardJoinError, DataResidencyError, ReservedVaultNameError, ShardProvisioningError, UnknownShardError, VaultTemplateNotFoundError } from '@noy-db/hub/kernel';
7
+ import { ExtractionPreview } from '@noy-db/hub/bundle';
10
8
  import { InspectableContainer } from '@noy-db/in-devtools';
9
+ import { WritePodOptions } from '@noy-db/hub/pod';
10
+ import { PublicEnvelope } from '@noy-db/hub';
11
11
 
12
12
  /**
13
13
  * @klum-db/lobby interchange — cross-vault FK-closure extraction.
@@ -284,7 +284,7 @@ declare function meterGroup<T>(group: VaultGroup<T>, opts?: {
284
284
  * that embeds N standard single-vault `.noydb` bundles plus an
285
285
  * unencrypted, owner-curated **manifest**. The v1 single-vault format
286
286
  * is untouched; each compartment is a complete v1 bundle, produced by
287
- * `writeNoydbBundle` and read by `readNoydbBundle`.
287
+ * `writePod` and read by `readNoydbBundle`.
288
288
  *
289
289
  * Layout: magic 'NDBM'(4) · version(1) · reserved(1) · manifestLen(4 BE)
290
290
  * · manifest JSON · concat(inner v1 bundles, in manifest order).
@@ -338,7 +338,7 @@ declare function decodeMultiBundle(bytes: Uint8Array): {
338
338
  };
339
339
  /** Per-compartment input to {@link writeMultiVaultBundle}. */
340
340
  interface MultiVaultCompartmentInput {
341
- readonly vault: Vault$1;
341
+ readonly vault: Vault;
342
342
  /** Owner-curated classification surfaced in the manifest (e.g. 'shard', 'pool'). */
343
343
  readonly roleTag?: string;
344
344
  /** ISO timestamp for the manifest; defaults to now. */
@@ -352,8 +352,8 @@ interface MultiVaultCompartmentInput {
352
352
  /** `true` → surface the inner bundle's public envelope into the manifest. */
353
353
  readonly publicEnvelope?: boolean;
354
354
  };
355
- /** Options forwarded to `writeNoydbBundle` for this compartment's inner bundle. */
356
- readonly bundleOptions?: WriteNoydbBundleOptions;
355
+ /** Options forwarded to `writePod` for this compartment's inner bundle. */
356
+ readonly bundleOptions?: WritePodOptions;
357
357
  }
358
358
  /** Write N vaults into one `NDBM` multi-compartment bundle. */
359
359
  declare function writeMultiVaultBundle(compartments: readonly MultiVaultCompartmentInput[], opts?: {
package/dist/index.d.ts CHANGED
@@ -1,13 +1,13 @@
1
- import { Vault, SealingKeyProvider, PublicEnvelope, Noydb } from '@noy-db/hub';
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-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';
1
+ import { Vault, SealingKeyProvider, Noydb } from '@noy-db/hub/cargo';
2
+ export { CrossShardJoinError, CustodyApi, DataResidencyError, DeedMarker, GrantCustodianOptions, LiberateOptions, LiberateResult, ReservedVaultNameError, ShardProvisioningError, UnknownShardError, VaultTemplateNotFoundError, createDeedOwner, isDeedVault, liberateVault, loadDeedMarker } from '@noy-db/hub/cargo';
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-D0EC-D5c.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-D0EC-D5c.js';
5
5
  import { AsXlsxSheetOptions } from '@noy-db/as-xlsx';
6
6
  export { MultiVaultDenormColumn, MultiVaultXlsxEntry, MultiVaultXlsxOptions } from '@noy-db/as-xlsx';
7
- import { ExtractionPreview, WriteNoydbBundleOptions } from '@noy-db/hub/bundle';
8
- import { Vault as Vault$1 } from '@noy-db/hub/kernel';
9
- export { CrossShardJoinError, DataResidencyError, ReservedVaultNameError, ShardProvisioningError, UnknownShardError, VaultTemplateNotFoundError } from '@noy-db/hub/kernel';
7
+ import { ExtractionPreview } from '@noy-db/hub/bundle';
10
8
  import { InspectableContainer } from '@noy-db/in-devtools';
9
+ import { WritePodOptions } from '@noy-db/hub/pod';
10
+ import { PublicEnvelope } from '@noy-db/hub';
11
11
 
12
12
  /**
13
13
  * @klum-db/lobby interchange — cross-vault FK-closure extraction.
@@ -284,7 +284,7 @@ declare function meterGroup<T>(group: VaultGroup<T>, opts?: {
284
284
  * that embeds N standard single-vault `.noydb` bundles plus an
285
285
  * unencrypted, owner-curated **manifest**. The v1 single-vault format
286
286
  * is untouched; each compartment is a complete v1 bundle, produced by
287
- * `writeNoydbBundle` and read by `readNoydbBundle`.
287
+ * `writePod` and read by `readNoydbBundle`.
288
288
  *
289
289
  * Layout: magic 'NDBM'(4) · version(1) · reserved(1) · manifestLen(4 BE)
290
290
  * · manifest JSON · concat(inner v1 bundles, in manifest order).
@@ -338,7 +338,7 @@ declare function decodeMultiBundle(bytes: Uint8Array): {
338
338
  };
339
339
  /** Per-compartment input to {@link writeMultiVaultBundle}. */
340
340
  interface MultiVaultCompartmentInput {
341
- readonly vault: Vault$1;
341
+ readonly vault: Vault;
342
342
  /** Owner-curated classification surfaced in the manifest (e.g. 'shard', 'pool'). */
343
343
  readonly roleTag?: string;
344
344
  /** ISO timestamp for the manifest; defaults to now. */
@@ -352,8 +352,8 @@ interface MultiVaultCompartmentInput {
352
352
  /** `true` → surface the inner bundle's public envelope into the manifest. */
353
353
  readonly publicEnvelope?: boolean;
354
354
  };
355
- /** Options forwarded to `writeNoydbBundle` for this compartment's inner bundle. */
356
- readonly bundleOptions?: WriteNoydbBundleOptions;
355
+ /** Options forwarded to `writePod` for this compartment's inner bundle. */
356
+ readonly bundleOptions?: WritePodOptions;
357
357
  }
358
358
  /** Write N vaults into one `NDBM` multi-compartment bundle. */
359
359
  declare function writeMultiVaultBundle(compartments: readonly MultiVaultCompartmentInput[], opts?: {
package/dist/index.js CHANGED
@@ -25,11 +25,11 @@ var init_uint32 = __esm({
25
25
  });
26
26
 
27
27
  // src/bundle/multi-bundle.ts
28
- import { sha256Hex, generateULID } from "@noy-db/hub/kernel";
28
+ import { sha256Hex, generateULID } from "@noy-db/hub/cargo";
29
29
  import {
30
- writeNoydbBundle,
31
- readNoydbBundleHeader
32
- } from "@noy-db/hub/bundle";
30
+ writePod,
31
+ readPodHeader
32
+ } from "@noy-db/hub/pod";
33
33
  import {
34
34
  readNoydbBundlePublicEnvelope,
35
35
  hasNoydbBundleMagic
@@ -116,8 +116,8 @@ async function writeMultiVaultBundle(compartments, opts = {}) {
116
116
  const inner = [];
117
117
  const entries = [];
118
118
  for (const c of compartments) {
119
- const innerBytes = await writeNoydbBundle(c.vault, c.bundleOptions ?? {});
120
- const header = readNoydbBundleHeader(innerBytes);
119
+ const innerBytes = await writePod(c.vault, c.bundleOptions ?? {});
120
+ const header = readPodHeader(innerBytes);
121
121
  const entry = {
122
122
  handle: header.handle,
123
123
  exportedAt: c.exportedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
@@ -153,7 +153,7 @@ async function writeMultiVaultBundle(compartments, opts = {}) {
153
153
  async function readNoydbBundleManifest(bytes) {
154
154
  if (hasMultiMagic(bytes)) return [...decodeMultiBundle(bytes).manifest.compartments];
155
155
  if (hasNoydbBundleMagic(bytes)) {
156
- const header = readNoydbBundleHeader(bytes);
156
+ const header = readPodHeader(bytes);
157
157
  const env = readNoydbBundlePublicEnvelope(bytes);
158
158
  const entry = {
159
159
  handle: header.handle,
@@ -170,7 +170,7 @@ function readMultiVaultBundleCompartment(bytes, selector) {
170
170
  throw new Error(`readMultiVaultBundleCompartment: numeric selector must be an integer, got ${selector}.`);
171
171
  }
172
172
  if (hasNoydbBundleMagic(bytes) && !hasMultiMagic(bytes)) {
173
- const header = readNoydbBundleHeader(bytes);
173
+ const header = readPodHeader(bytes);
174
174
  if (selector === 0 || selector === header.handle) return bytes;
175
175
  throw new Error(`readMultiVaultBundleCompartment: single v1 bundle has only compartment "${header.handle}".`);
176
176
  }
@@ -201,10 +201,10 @@ __export(extract_cross_vault_exports, {
201
201
  import {
202
202
  walkClosure,
203
203
  extractPartition,
204
- readNoydbBundleHeader as readNoydbBundleHeader2,
205
204
  describeExtraction
206
205
  } from "@noy-db/hub/bundle";
207
- import { sha256Hex as sha256Hex2, generateULID as generateULID2 } from "@noy-db/hub/kernel";
206
+ import { readPodHeader as readPodHeader2 } from "@noy-db/hub/pod";
207
+ import { sha256Hex as sha256Hex2, generateULID as generateULID2 } from "@noy-db/hub/cargo";
208
208
  function asIdArray(v) {
209
209
  if (v === null || v === void 0) return [];
210
210
  if (Array.isArray(v)) return v.filter((x) => typeof x === "string" || typeof x === "number").map(String);
@@ -321,7 +321,7 @@ async function extractCrossVaultPartition(openVault, opts) {
321
321
  carryLedger: opts.carryLedger ?? false,
322
322
  ...opts.compression !== void 0 ? { compression: opts.compression } : {}
323
323
  });
324
- const header = readNoydbBundleHeader2(bundleBytes);
324
+ const header = readPodHeader2(bundleBytes);
325
325
  const meta = opts.compartmentMeta?.[vaultName];
326
326
  const entry = {
327
327
  handle: header.handle,
@@ -429,7 +429,7 @@ var init_field_authority = __esm({
429
429
  });
430
430
 
431
431
  // src/interchange/merge-compartment.ts
432
- import { diffVault } from "@noy-db/hub";
432
+ import { diffVault } from "@noy-db/hub/cargo";
433
433
  import { decryptExtractedPartition } from "@noy-db/hub/bundle";
434
434
  function strategyFor(opts, collection) {
435
435
  if (typeof opts === "string") return opts;
@@ -638,7 +638,7 @@ __export(graduate_exports, {
638
638
  UnitGraduationError: () => UnitGraduationError,
639
639
  graduate: () => graduate
640
640
  });
641
- import { createDeedOwner } from "@noy-db/hub";
641
+ import { createDeedOwner } from "@noy-db/hub/cargo";
642
642
  async function graduate(noydb, docked, opts) {
643
643
  const vault = await noydb.openVault(opts.vaultName);
644
644
  opts.template.configure(vault);
@@ -724,7 +724,7 @@ __export(surface_exports, {
724
724
  markSynced: () => markSynced,
725
725
  proposeSurface: () => proposeSurface
726
726
  });
727
- import { generateULID as generateULID3 } from "@noy-db/hub/kernel";
727
+ import { generateULID as generateULID3 } from "@noy-db/hub/cargo";
728
728
  import { extractPartition as extractPartition2 } from "@noy-db/hub/bundle";
729
729
  async function proposeSurface(smv, def, proposedBy, now) {
730
730
  const row = {
@@ -846,7 +846,7 @@ var init_surface = __esm({
846
846
  });
847
847
 
848
848
  // src/federation/schema-manifest.ts
849
- import { sha256Hex as sha256Hex3 } from "@noy-db/hub/kernel";
849
+ import { sha256Hex as sha256Hex3 } from "@noy-db/hub/cargo";
850
850
  function captureBlueprint(configure) {
851
851
  const recorded = [];
852
852
  const collectionStub = new Proxy(
@@ -906,7 +906,7 @@ var init_schema_manifest = __esm({
906
906
  });
907
907
 
908
908
  // src/federation/constants.ts
909
- import { STATE_VAULT_NAME } from "@noy-db/hub";
909
+ import { STATE_VAULT_NAME } from "@noy-db/hub/cargo";
910
910
  var init_constants = __esm({
911
911
  "src/federation/constants.ts"() {
912
912
  "use strict";
@@ -919,7 +919,7 @@ __export(state_vault_exports, {
919
919
  STATE_VAULT_NAME: () => STATE_VAULT_NAME,
920
920
  StateManagementVault: () => StateManagementVault
921
921
  });
922
- import { generateULID as generateULID4 } from "@noy-db/hub/kernel";
922
+ import { generateULID as generateULID4 } from "@noy-db/hub/cargo";
923
923
  var REGISTRY, MANIFEST, EVENTS, MIGRATION_STATUS, SURFACES, StateManagementVault;
924
924
  var init_state_vault = __esm({
925
925
  "src/federation/state-vault.ts"() {
@@ -1056,7 +1056,7 @@ var init_state_vault = __esm({
1056
1056
  });
1057
1057
 
1058
1058
  // src/federation/classify-skip.ts
1059
- import { NoAccessError } from "@noy-db/hub/kernel";
1059
+ import { NoAccessError } from "@noy-db/hub/cargo";
1060
1060
  function classifyShardSkip(err) {
1061
1061
  return err instanceof NoAccessError ? "no-grant" : "error";
1062
1062
  }
@@ -1067,7 +1067,7 @@ var init_classify_skip = __esm({
1067
1067
  });
1068
1068
 
1069
1069
  // src/federation/cross-shard-join.ts
1070
- import { readPath } from "@noy-db/hub/kernel";
1070
+ import { readPath } from "@noy-db/hub/cargo";
1071
1071
  function coerceKey(value) {
1072
1072
  if (value === null || value === void 0) return null;
1073
1073
  if (typeof value === "string") return value;
@@ -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
- /** Partition keys awaiting recompute. */
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.schedule();
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];
@@ -1292,8 +1320,8 @@ var init_partial_reduce = __esm({
1292
1320
  });
1293
1321
 
1294
1322
  // src/federation/aggregate-across.ts
1295
- import { reduceRecords } from "@noy-db/hub/kernel";
1296
- import { groupAndReduce } from "@noy-db/hub/kernel";
1323
+ import { reduceRecords } from "@noy-db/hub/cargo";
1324
+ import { groupAndReduce } from "@noy-db/hub/cargo";
1297
1325
  var CrossVaultAggregation, CrossVaultGroupedAggregation;
1298
1326
  var init_aggregate_across = __esm({
1299
1327
  "src/federation/aggregate-across.ts"() {
@@ -1403,7 +1431,7 @@ var init_aggregate_across = __esm({
1403
1431
  });
1404
1432
 
1405
1433
  // src/federation/retrieve-across.ts
1406
- import { fuseRetrieval } from "@noy-db/hub/kernel";
1434
+ import { fuseRetrieval } from "@noy-db/hub/cargo";
1407
1435
  async function retrieveAcross(group, collectionName, query, opts = {}) {
1408
1436
  const { eligible, skipped } = await group.resolveEligible({
1409
1437
  ...opts.minVersion !== void 0 ? { minVersion: opts.minVersion } : {},
@@ -1477,7 +1505,11 @@ __export(vault_group_exports, {
1477
1505
  ShardedQuery: () => ShardedQuery,
1478
1506
  VaultGroup: () => VaultGroup
1479
1507
  });
1480
- import { CrossShardJoinError, DataResidencyError, ReservedVaultNameError, ShardProvisioningError, UnknownShardError, ValidationError } from "@noy-db/hub/kernel";
1508
+ import { CrossShardJoinError, DataResidencyError, ReservedVaultNameError, ShardProvisioningError, UnknownShardError, ValidationError } from "@noy-db/hub/cargo";
1509
+ function autoPushConfig(spec) {
1510
+ if (!spec.autoPush) return null;
1511
+ return spec.autoPush === true ? {} : spec.autoPush;
1512
+ }
1481
1513
  function assertSafePartitionKey(partitionKey) {
1482
1514
  if (partitionKey.length === 0) {
1483
1515
  throw new ValidationError("partitionKey must be a non-empty string");
@@ -1741,9 +1773,12 @@ var init_vault_group = __esm({
1741
1773
  }
1742
1774
  this.crossVaultDerivations.push(spec);
1743
1775
  if (spec.autoPush && !this.insightAutoPush) {
1776
+ const cfg = autoPushConfig(spec);
1744
1777
  const controller = new InsightAutoPush(
1745
1778
  (pk) => this._recomputeShardInsights(pk),
1746
- (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
1747
1782
  );
1748
1783
  this.insightAutoPush = controller;
1749
1784
  const prefix = `${this.name}${SHARD_SEPARATOR}`;
@@ -1824,6 +1859,8 @@ var init_vault_group = __esm({
1824
1859
  };
1825
1860
  for (const spec of this.crossVaultDerivations) {
1826
1861
  if (!spec.autoPush) continue;
1862
+ const min = autoPushConfig(spec)?.minVersion;
1863
+ if (min !== void 0 && row.schemaVersion < min) continue;
1827
1864
  const records = await shard.collection(spec.source).list();
1828
1865
  const summary = spec.derive(records, ctx);
1829
1866
  const insight = await this.db.openVault(spec.target.vault);
@@ -2104,23 +2141,23 @@ var init_vault_group = __esm({
2104
2141
  isRelevant: (e) => e.collection === collectionName && e.vault.startsWith(`${group.name}--`)
2105
2142
  };
2106
2143
  }
2107
- /** @internal — joined queries don't support reactive/aggregate surfaces in v1. */
2108
- assertNoJoinLegs(surface) {
2109
- if (this.coPartitionedLegs.length || this.broadcastLegs.length) {
2110
- throw new CrossShardJoinError(
2111
- `${surface}() is not supported on a ShardedQuery with crossShardJoin/broadcastJoin legs in v1. Use toArray() for joined cross-shard queries.`
2112
- );
2113
- }
2114
- }
2115
- /** Returns a reactive cross-shard live query — a facade over CrossVaultLive. */
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
+ */
2116
2153
  live(options = {}) {
2117
- this.assertNoJoinLegs("live");
2118
2154
  const bind = this.liveBinding();
2119
2155
  const core = new CrossVaultLive({
2120
2156
  ...bind,
2121
2157
  compute: async () => {
2122
2158
  const { records, skippedVaults } = await this.fanoutRecords(options);
2123
- return { records, skipped: skippedVaults };
2159
+ const joined = await applyBroadcastLegs(records, this.broadcastLegs);
2160
+ return { records: joined, skipped: skippedVaults };
2124
2161
  },
2125
2162
  initialSnapshot: { records: [], skipped: [] },
2126
2163
  ...options.debounceMs !== void 0 ? { debounceMs: options.debounceMs } : {}
@@ -2140,14 +2177,28 @@ var init_vault_group = __esm({
2140
2177
  stop: () => core.stop()
2141
2178
  };
2142
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
+ }
2143
2196
  /** One-shot distributed aggregate — central reduce over all shard records. */
2144
2197
  aggregate(spec) {
2145
- this.assertNoJoinLegs("aggregate");
2146
- return new CrossVaultAggregation(this, spec, this.liveBinding());
2198
+ return new CrossVaultAggregation(this.aggregateSource(), spec, this.liveBinding());
2147
2199
  }
2148
2200
  /** Begin a grouped cross-shard aggregate. */
2149
2201
  groupBy(field) {
2150
- this.assertNoJoinLegs("groupBy");
2151
2202
  return new ShardedGroupedQuery(this, field);
2152
2203
  }
2153
2204
  };
@@ -2160,7 +2211,7 @@ var init_vault_group = __esm({
2160
2211
  field;
2161
2212
  aggregate(spec) {
2162
2213
  return new CrossVaultGroupedAggregation(
2163
- { fanoutRecords: (o) => this.query.fanoutRecords(o) },
2214
+ this.query.aggregateSource(),
2164
2215
  this.field,
2165
2216
  spec,
2166
2217
  this.query.liveBinding()
@@ -2171,8 +2222,7 @@ var init_vault_group = __esm({
2171
2222
  });
2172
2223
 
2173
2224
  // src/index.ts
2174
- import { ValidationError as ValidationError2, ReservedVaultNameError as ReservedVaultNameError2, VaultTemplateNotFoundError } from "@noy-db/hub/kernel";
2175
- import { STATE_VAULT_NAME as STATE_VAULT_NAME2 } from "@noy-db/hub";
2225
+ import { ValidationError as ValidationError2, ReservedVaultNameError as ReservedVaultNameError2, VaultTemplateNotFoundError, STATE_VAULT_NAME as STATE_VAULT_NAME2 } from "@noy-db/hub/cargo";
2176
2226
 
2177
2227
  // src/dock/docked-unit.ts
2178
2228
  var DockedUnit = class {
@@ -2264,7 +2314,7 @@ import {
2264
2314
  VaultTemplateNotFoundError as VaultTemplateNotFoundError2,
2265
2315
  ReservedVaultNameError as ReservedVaultNameError3,
2266
2316
  DataResidencyError as DataResidencyError2
2267
- } from "@noy-db/hub/kernel";
2317
+ } from "@noy-db/hub/cargo";
2268
2318
 
2269
2319
  // src/interchange/migrate-then-merge.ts
2270
2320
  init_merge_compartment();
@@ -2315,7 +2365,7 @@ async function migrateThenMerge(receiver, compartmentBytes, opts) {
2315
2365
 
2316
2366
  // src/index.ts
2317
2367
  init_field_authority();
2318
- import { CustodyApi, liberateVault, createDeedOwner as createDeedOwner2, loadDeedMarker, isDeedVault } from "@noy-db/hub";
2368
+ import { CustodyApi, liberateVault, createDeedOwner as createDeedOwner2, loadDeedMarker, isDeedVault } from "@noy-db/hub/cargo";
2319
2369
 
2320
2370
  // src/dock/unit-driver.ts
2321
2371
  var InMemoryUnitDriver = class {