@klum-db/lobby 0.2.0-pre.32 → 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 +71 -20
- 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 +71 -20
- package/dist/index.js.map +1 -1
- package/dist/{vault-group-BchRCDm5.d.cts → vault-group-SfkQJ3CM.d.cts} +27 -5
- package/dist/{vault-group-BchRCDm5.d.ts → vault-group-SfkQJ3CM.d.ts} +27 -5
- package/package.json +1 -1
package/dist/bin/klum.d.cts
CHANGED
package/dist/bin/klum.d.ts
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -1223,28 +1223,33 @@ var init_insight_auto_push = __esm({
|
|
|
1223
1223
|
"src/federation/insight-auto-push.ts"() {
|
|
1224
1224
|
"use strict";
|
|
1225
1225
|
InsightAutoPush = class {
|
|
1226
|
-
constructor(recompute, isSource, onError = (err, pk) => console.warn(`[klum-db] insight auto-push failed for shard "${pk}":`, err)) {
|
|
1226
|
+
constructor(recompute, isSource, onError = (err, pk) => console.warn(`[klum-db] insight auto-push failed for shard "${pk}":`, err), debounceMs) {
|
|
1227
1227
|
this.recompute = recompute;
|
|
1228
1228
|
this.isSource = isSource;
|
|
1229
1229
|
this.onError = onError;
|
|
1230
|
+
this.debounceMs = debounceMs;
|
|
1230
1231
|
}
|
|
1231
1232
|
recompute;
|
|
1232
1233
|
isSource;
|
|
1233
1234
|
onError;
|
|
1234
|
-
|
|
1235
|
+
debounceMs;
|
|
1235
1236
|
dirty = /* @__PURE__ */ new Set();
|
|
1236
|
-
/** The in-flight flush, or null when idle. */
|
|
1237
1237
|
pending = null;
|
|
1238
|
+
timer = null;
|
|
1239
|
+
resolvePending = null;
|
|
1240
|
+
flushing = false;
|
|
1238
1241
|
/** Called from a shard's onAfterWrite hook. Marks the shard dirty + schedules a flush. */
|
|
1239
1242
|
noteWrite(partitionKey, collection) {
|
|
1240
1243
|
if (!this.isSource(collection)) return;
|
|
1241
1244
|
this.dirty.add(partitionKey);
|
|
1242
|
-
this.
|
|
1245
|
+
if (this.debounceMs !== void 0) this.scheduleDebounced();
|
|
1246
|
+
else this.schedule();
|
|
1243
1247
|
}
|
|
1244
1248
|
/** Resolve once no flush is pending (drains rescheduled flushes too). */
|
|
1245
1249
|
async whenSettled() {
|
|
1246
1250
|
while (this.pending) await this.pending;
|
|
1247
1251
|
}
|
|
1252
|
+
// ── microtask path (unchanged default) ──
|
|
1248
1253
|
schedule() {
|
|
1249
1254
|
if (this.pending) return;
|
|
1250
1255
|
this.pending = this.runFlush().finally(() => {
|
|
@@ -1252,6 +1257,29 @@ var init_insight_auto_push = __esm({
|
|
|
1252
1257
|
if (this.dirty.size > 0) this.schedule();
|
|
1253
1258
|
});
|
|
1254
1259
|
}
|
|
1260
|
+
// ── debounce path (#13) ──
|
|
1261
|
+
scheduleDebounced() {
|
|
1262
|
+
if (!this.pending) this.pending = new Promise((r) => {
|
|
1263
|
+
this.resolvePending = r;
|
|
1264
|
+
});
|
|
1265
|
+
if (this.timer) clearTimeout(this.timer);
|
|
1266
|
+
this.timer = setTimeout(() => {
|
|
1267
|
+
this.timer = null;
|
|
1268
|
+
if (this.flushing) return;
|
|
1269
|
+
this.flushing = true;
|
|
1270
|
+
void this.runFlush().finally(() => {
|
|
1271
|
+
this.flushing = false;
|
|
1272
|
+
if (this.dirty.size > 0) {
|
|
1273
|
+
this.scheduleDebounced();
|
|
1274
|
+
} else {
|
|
1275
|
+
const resolve = this.resolvePending;
|
|
1276
|
+
this.resolvePending = null;
|
|
1277
|
+
this.pending = null;
|
|
1278
|
+
resolve?.();
|
|
1279
|
+
}
|
|
1280
|
+
});
|
|
1281
|
+
}, this.debounceMs);
|
|
1282
|
+
}
|
|
1255
1283
|
async runFlush() {
|
|
1256
1284
|
await Promise.resolve();
|
|
1257
1285
|
const pks = [...this.dirty];
|
|
@@ -1491,6 +1519,10 @@ __export(vault_group_exports, {
|
|
|
1491
1519
|
ShardedQuery: () => ShardedQuery,
|
|
1492
1520
|
VaultGroup: () => VaultGroup
|
|
1493
1521
|
});
|
|
1522
|
+
function autoPushConfig(spec) {
|
|
1523
|
+
if (!spec.autoPush) return null;
|
|
1524
|
+
return spec.autoPush === true ? {} : spec.autoPush;
|
|
1525
|
+
}
|
|
1494
1526
|
function assertSafePartitionKey(partitionKey) {
|
|
1495
1527
|
if (partitionKey.length === 0) {
|
|
1496
1528
|
throw new import_kernel11.ValidationError("partitionKey must be a non-empty string");
|
|
@@ -1755,9 +1787,12 @@ var init_vault_group = __esm({
|
|
|
1755
1787
|
}
|
|
1756
1788
|
this.crossVaultDerivations.push(spec);
|
|
1757
1789
|
if (spec.autoPush && !this.insightAutoPush) {
|
|
1790
|
+
const cfg = autoPushConfig(spec);
|
|
1758
1791
|
const controller = new InsightAutoPush(
|
|
1759
1792
|
(pk) => this._recomputeShardInsights(pk),
|
|
1760
|
-
(collection) => this.crossVaultDerivations.some((s) => s.autoPush && s.source === collection)
|
|
1793
|
+
(collection) => this.crossVaultDerivations.some((s) => s.autoPush && s.source === collection),
|
|
1794
|
+
void 0,
|
|
1795
|
+
cfg?.debounceMs
|
|
1761
1796
|
);
|
|
1762
1797
|
this.insightAutoPush = controller;
|
|
1763
1798
|
const prefix = `${this.name}${SHARD_SEPARATOR}`;
|
|
@@ -1838,6 +1873,8 @@ var init_vault_group = __esm({
|
|
|
1838
1873
|
};
|
|
1839
1874
|
for (const spec of this.crossVaultDerivations) {
|
|
1840
1875
|
if (!spec.autoPush) continue;
|
|
1876
|
+
const min = autoPushConfig(spec)?.minVersion;
|
|
1877
|
+
if (min !== void 0 && row.schemaVersion < min) continue;
|
|
1841
1878
|
const records = await shard.collection(spec.source).list();
|
|
1842
1879
|
const summary = spec.derive(records, ctx);
|
|
1843
1880
|
const insight = await this.db.openVault(spec.target.vault);
|
|
@@ -2118,23 +2155,23 @@ var init_vault_group = __esm({
|
|
|
2118
2155
|
isRelevant: (e) => e.collection === collectionName && e.vault.startsWith(`${group.name}--`)
|
|
2119
2156
|
};
|
|
2120
2157
|
}
|
|
2121
|
-
/**
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2158
|
+
/**
|
|
2159
|
+
* Returns a reactive cross-shard live query — a facade over CrossVaultLive.
|
|
2160
|
+
*
|
|
2161
|
+
* Joined queries (crossShardJoin / broadcastJoin) are supported (#14): the live
|
|
2162
|
+
* value reflects the fully-joined rows. **v1 limitation:** recomputes only on
|
|
2163
|
+
* writes to the primary (left) collection; writes to a co-partitioned right
|
|
2164
|
+
* collection or a broadcast-dimension collection do NOT trigger a recompute —
|
|
2165
|
+
* re-run the query to pick those up.
|
|
2166
|
+
*/
|
|
2130
2167
|
live(options = {}) {
|
|
2131
|
-
this.assertNoJoinLegs("live");
|
|
2132
2168
|
const bind = this.liveBinding();
|
|
2133
2169
|
const core = new CrossVaultLive({
|
|
2134
2170
|
...bind,
|
|
2135
2171
|
compute: async () => {
|
|
2136
2172
|
const { records, skippedVaults } = await this.fanoutRecords(options);
|
|
2137
|
-
|
|
2173
|
+
const joined = await applyBroadcastLegs(records, this.broadcastLegs);
|
|
2174
|
+
return { records: joined, skipped: skippedVaults };
|
|
2138
2175
|
},
|
|
2139
2176
|
initialSnapshot: { records: [], skipped: [] },
|
|
2140
2177
|
...options.debounceMs !== void 0 ? { debounceMs: options.debounceMs } : {}
|
|
@@ -2154,14 +2191,28 @@ var init_vault_group = __esm({
|
|
|
2154
2191
|
stop: () => core.stop()
|
|
2155
2192
|
};
|
|
2156
2193
|
}
|
|
2194
|
+
/**
|
|
2195
|
+
* @internal — the FanoutRecordSource for aggregate surfaces. With no join legs,
|
|
2196
|
+
* returns `this` (partial-reduce-eligible, #8). With join legs, returns a
|
|
2197
|
+
* toArray-backed source (fully-joined rows) and NO fanoutReduce, forcing
|
|
2198
|
+
* central-reduce over the joined rows (partial-reduce can't span the central
|
|
2199
|
+
* broadcast join). Used by scalar `aggregate()` and `ShardedGroupedQuery`.
|
|
2200
|
+
*/
|
|
2201
|
+
aggregateSource() {
|
|
2202
|
+
if (!this.coPartitionedLegs.length && !this.broadcastLegs.length) return this;
|
|
2203
|
+
return {
|
|
2204
|
+
fanoutRecords: async (o) => {
|
|
2205
|
+
const { results, skippedVaults } = await this.toArray(o);
|
|
2206
|
+
return { records: results, skippedVaults };
|
|
2207
|
+
}
|
|
2208
|
+
};
|
|
2209
|
+
}
|
|
2157
2210
|
/** One-shot distributed aggregate — central reduce over all shard records. */
|
|
2158
2211
|
aggregate(spec) {
|
|
2159
|
-
this.
|
|
2160
|
-
return new CrossVaultAggregation(this, spec, this.liveBinding());
|
|
2212
|
+
return new CrossVaultAggregation(this.aggregateSource(), spec, this.liveBinding());
|
|
2161
2213
|
}
|
|
2162
2214
|
/** Begin a grouped cross-shard aggregate. */
|
|
2163
2215
|
groupBy(field) {
|
|
2164
|
-
this.assertNoJoinLegs("groupBy");
|
|
2165
2216
|
return new ShardedGroupedQuery(this, field);
|
|
2166
2217
|
}
|
|
2167
2218
|
};
|
|
@@ -2174,7 +2225,7 @@ var init_vault_group = __esm({
|
|
|
2174
2225
|
field;
|
|
2175
2226
|
aggregate(spec) {
|
|
2176
2227
|
return new CrossVaultGroupedAggregation(
|
|
2177
|
-
|
|
2228
|
+
this.query.aggregateSource(),
|
|
2178
2229
|
this.field,
|
|
2179
2230
|
spec,
|
|
2180
2231
|
this.query.liveBinding()
|