@modernrelay/orbit-core 0.13.6 → 0.15.0
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/{chunk-FG6TQANI.js → chunk-TDBIVBJ3.js} +23 -7
- package/dist/chunk-TDBIVBJ3.js.map +1 -0
- package/dist/engine.d.ts +1 -1
- package/dist/{index-BoQfS8jd.d.ts → index-CPRTWpGA.d.ts} +175 -175
- package/dist/index.d.ts +629 -619
- package/dist/index.js +220 -176
- package/dist/index.js.map +1 -1
- package/dist/{lane-SZYhcrXT.d.ts → lane-_KPQ1deI.d.ts} +42 -42
- package/dist/testing.d.ts +22 -22
- package/dist/testing.js +15 -15
- package/dist/testing.js.map +1 -1
- package/dist/worker/entry.js +35 -15
- package/dist/worker/entry.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-FG6TQANI.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DIAGNOSTIC_SAMPLE_CAP, encodeStringTable, collectTransfers, deriveClusters, resolveClusterCenters, clusterCentroids, EnvelopeSequencer, RequestLedger } from './chunk-
|
|
2
|
-
export { DEFAULT_CLUSTER_CENTER_RADIUS, DEFAULT_LAYOUT_SEED, DIAGNOSTIC_SAMPLE_CAP, acceptColumnar, clusterCentroids, collectTransfers, decodeStringTable, deriveClusters, encodeStringTable, generateClusterCenters, judgeEpoch, resolveClusterCenters } from './chunk-
|
|
1
|
+
import { DIAGNOSTIC_SAMPLE_CAP, encodeStringTable, collectTransfers, deriveClusters, resolveClusterCenters, clusterCentroids, EnvelopeSequencer, RequestLedger } from './chunk-TDBIVBJ3.js';
|
|
2
|
+
export { DEFAULT_CLUSTER_CENTER_RADIUS, DEFAULT_LAYOUT_SEED, DIAGNOSTIC_SAMPLE_CAP, acceptColumnar, clusterCentroids, collectTransfers, decodeStringTable, deriveClusters, encodeStringTable, generateClusterCenters, judgeEpoch, resolveClusterCenters } from './chunk-TDBIVBJ3.js';
|
|
3
3
|
import { createStore } from 'zustand/vanilla';
|
|
4
4
|
|
|
5
5
|
// src/errors.ts
|
|
@@ -123,6 +123,10 @@ function validateSnapshot(snapshot) {
|
|
|
123
123
|
record(invalidEdge, typeof edge.id === "string" ? edge.id : `[${i}]`);
|
|
124
124
|
continue;
|
|
125
125
|
}
|
|
126
|
+
if (typeof edge.id === "string" && edge.id.includes("\0")) {
|
|
127
|
+
record(invalidEdge, `[${i}]`);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
126
130
|
if (!nodeIndex.has(source)) {
|
|
127
131
|
record(danglingEdge, source);
|
|
128
132
|
continue;
|
|
@@ -172,7 +176,7 @@ function validateSnapshot(snapshot) {
|
|
|
172
176
|
"invalid-edge",
|
|
173
177
|
"error",
|
|
174
178
|
invalidEdge,
|
|
175
|
-
`${invalidEdge.count} edge row(s) dropped: missing or non-string source/target`
|
|
179
|
+
`${invalidEdge.count} edge row(s) dropped: missing or non-string source/target, or NUL-containing explicit id`
|
|
176
180
|
);
|
|
177
181
|
pushDiagnostic(
|
|
178
182
|
diagnostics,
|
|
@@ -349,7 +353,7 @@ function validateGroupSpecs(specs, nodeIndex) {
|
|
|
349
353
|
severity: "error",
|
|
350
354
|
count,
|
|
351
355
|
sampleIds: samples,
|
|
352
|
-
message: `groups rejected before scene rewrite
|
|
356
|
+
message: `groups rejected before scene rewrite: ${kinds} \u2014 the previous group configuration stays active`
|
|
353
357
|
}
|
|
354
358
|
};
|
|
355
359
|
}
|
|
@@ -419,7 +423,7 @@ function validateGroupBySpec(spec) {
|
|
|
419
423
|
if (!expandOk) problems.push("semanticZoom.expandAbove must be a finite number");
|
|
420
424
|
if (collapseOk && expandOk && !(sz.expandAbove > sz.collapseBelow)) {
|
|
421
425
|
problems.push(
|
|
422
|
-
"semanticZoom.expandAbove must be strictly greater than collapseBelow
|
|
426
|
+
"semanticZoom.expandAbove must be strictly greater than collapseBelow to provide a hysteresis gap"
|
|
423
427
|
);
|
|
424
428
|
}
|
|
425
429
|
}
|
|
@@ -429,7 +433,7 @@ function validateGroupBySpec(spec) {
|
|
|
429
433
|
severity: "error",
|
|
430
434
|
count: problems.length,
|
|
431
435
|
sampleIds: [],
|
|
432
|
-
message: `groupBy rejected
|
|
436
|
+
message: `groupBy rejected: ${problems.join("; ")} \u2014 the previous groupBy configuration stays active`
|
|
433
437
|
};
|
|
434
438
|
}
|
|
435
439
|
function deriveGroupsByKey(nodes, by, isCollapsedKey) {
|
|
@@ -476,7 +480,7 @@ function deriveGroupsByKey(nodes, by, isCollapsedKey) {
|
|
|
476
480
|
severity: "warning",
|
|
477
481
|
count: errorCount,
|
|
478
482
|
sampleIds: errorSamples,
|
|
479
|
-
message: "groupBy.by threw; the affected nodes derived as ungrouped
|
|
483
|
+
message: "groupBy.by threw; the affected nodes derived as ungrouped"
|
|
480
484
|
}
|
|
481
485
|
};
|
|
482
486
|
}
|
|
@@ -701,27 +705,27 @@ var EMPTY_INDEX = /* @__PURE__ */ new Map();
|
|
|
701
705
|
var Reconciler = class {
|
|
702
706
|
datasetKey = null;
|
|
703
707
|
hasScene = false;
|
|
704
|
-
// Previous scene, kept for the structural diff
|
|
708
|
+
// Previous scene, kept for the structural diff.
|
|
705
709
|
prevIdByIndex = [];
|
|
706
710
|
prevIndexById = EMPTY_INDEX;
|
|
707
711
|
prevEdgeIdByIndex = [];
|
|
708
712
|
prevLinks = new Uint32Array(0);
|
|
709
713
|
/**
|
|
710
714
|
* Declared node.x/y as of the previous reconcile, for nodes that declared
|
|
711
|
-
* them. Lets Priority 1 distinguish a CHANGED declaration (caller intent
|
|
715
|
+
* them. Lets Priority 1 distinguish a CHANGED declaration (caller intent
|
|
712
716
|
* wins over any cache) from an unchanged one (defers to live drift). Fresh
|
|
713
717
|
* per pass, so departed ids drop and a re-add treats its declaration as new.
|
|
714
718
|
*/
|
|
715
719
|
prevDeclared = /* @__PURE__ */ new Map();
|
|
716
720
|
/**
|
|
717
|
-
* CPU position mirror for the CURRENT scene, indexed by slot
|
|
721
|
+
* CPU position mirror for the CURRENT scene, indexed by slot.
|
|
718
722
|
* Owned copy — never aliases a published scene's positions array, so
|
|
719
723
|
* noteEnginePositions never mutates an already-published RenderScene.
|
|
720
724
|
*/
|
|
721
725
|
livePositions = new Float32Array(0);
|
|
722
726
|
/**
|
|
723
|
-
* Ids removed from the scene → last known finite position
|
|
724
|
-
* leave-and-return guarantee
|
|
727
|
+
* Ids removed from the scene → last known finite position, preserving the
|
|
728
|
+
* leave-and-return guarantee. Map insertion order doubles as LRU order;
|
|
725
729
|
* invariant: never overlaps the current scene's id set.
|
|
726
730
|
*/
|
|
727
731
|
departed = /* @__PURE__ */ new Map();
|
|
@@ -835,7 +839,7 @@ var Reconciler = class {
|
|
|
835
839
|
}
|
|
836
840
|
/**
|
|
837
841
|
* Copy engine-read positions into the live cache for the CURRENT scene's
|
|
838
|
-
* slots
|
|
842
|
+
* slots.
|
|
839
843
|
* Extra trailing floats are ignored; a short buffer updates a prefix.
|
|
840
844
|
*/
|
|
841
845
|
noteEnginePositions(positions) {
|
|
@@ -1239,7 +1243,7 @@ function stageBatch(contribution, batch, tallies, nextOrder) {
|
|
|
1239
1243
|
for (let i = 0; i < rawNodes.length; i++) {
|
|
1240
1244
|
const row = rawNodes[i];
|
|
1241
1245
|
const id = typeof row === "object" && row !== null ? row.id : void 0;
|
|
1242
|
-
if (typeof id !== "string") {
|
|
1246
|
+
if (typeof id !== "string" || id.includes("\0")) {
|
|
1243
1247
|
recordRow(tallies.invalidNodes, `[${batch.sequence}:${i}]`);
|
|
1244
1248
|
continue;
|
|
1245
1249
|
}
|
|
@@ -1263,6 +1267,10 @@ function stageBatch(contribution, batch, tallies, nextOrder) {
|
|
|
1263
1267
|
recordRow(tallies.invalidEdges, typeof edge.id === "string" ? edge.id : `[${batch.sequence}:${i}]`);
|
|
1264
1268
|
continue;
|
|
1265
1269
|
}
|
|
1270
|
+
if (typeof edge.id === "string" && edge.id.includes("\0")) {
|
|
1271
|
+
recordRow(tallies.invalidEdges, `[${batch.sequence}:${i}]`);
|
|
1272
|
+
continue;
|
|
1273
|
+
}
|
|
1266
1274
|
const hasExplicitId = typeof edge.id === "string";
|
|
1267
1275
|
let id;
|
|
1268
1276
|
if (hasExplicitId) {
|
|
@@ -1391,7 +1399,7 @@ function mergeDiagnostics(merge) {
|
|
|
1391
1399
|
severity: "info",
|
|
1392
1400
|
count: merge.shadowedCount,
|
|
1393
1401
|
sampleIds: merge.shadowedSamples,
|
|
1394
|
-
message: `${merge.shadowedCount} overlay node row(s) shadowed by a same-id row admitted earlier
|
|
1402
|
+
message: `${merge.shadowedCount} overlay node row(s) shadowed by a same-id row admitted earlier; rows retained`
|
|
1395
1403
|
});
|
|
1396
1404
|
}
|
|
1397
1405
|
if (merge.duplicateEdgeCount > 0) {
|
|
@@ -1415,7 +1423,7 @@ function sessionCommitDiagnostics(tallies, danglingCount, danglingSamples = [])
|
|
|
1415
1423
|
"invalid-node",
|
|
1416
1424
|
"error",
|
|
1417
1425
|
tallies.invalidNodes,
|
|
1418
|
-
`${tallies.invalidNodes.count} ingested node row(s) dropped: missing
|
|
1426
|
+
`${tallies.invalidNodes.count} ingested node row(s) dropped: missing, non-string, or NUL-containing id`
|
|
1419
1427
|
);
|
|
1420
1428
|
push(
|
|
1421
1429
|
"duplicate-node-id",
|
|
@@ -1427,7 +1435,7 @@ function sessionCommitDiagnostics(tallies, danglingCount, danglingSamples = [])
|
|
|
1427
1435
|
"invalid-edge",
|
|
1428
1436
|
"error",
|
|
1429
1437
|
tallies.invalidEdges,
|
|
1430
|
-
`${tallies.invalidEdges.count} ingested edge row(s) dropped: missing or non-string source/target`
|
|
1438
|
+
`${tallies.invalidEdges.count} ingested edge row(s) dropped: missing or non-string source/target, or NUL-containing explicit id`
|
|
1431
1439
|
);
|
|
1432
1440
|
push(
|
|
1433
1441
|
"duplicate-edge-id",
|
|
@@ -1441,7 +1449,7 @@ function sessionCommitDiagnostics(tallies, danglingCount, danglingSamples = [])
|
|
|
1441
1449
|
severity: "warning",
|
|
1442
1450
|
count: danglingCount,
|
|
1443
1451
|
sampleIds: danglingSamples.slice(0, DIAGNOSTIC_SAMPLE_CAP),
|
|
1444
|
-
message: `${danglingCount} ingested edge(s) still awaiting an endpoint at commit (
|
|
1452
|
+
message: `${danglingCount} ingested edge(s) still awaiting an endpoint at commit (the endpoint was not ingested before the session committed)`
|
|
1445
1453
|
});
|
|
1446
1454
|
}
|
|
1447
1455
|
return out;
|
|
@@ -1571,7 +1579,7 @@ var IncrementalAlphaComposer = class {
|
|
|
1571
1579
|
bufB = null;
|
|
1572
1580
|
staleA = [];
|
|
1573
1581
|
staleB = [];
|
|
1574
|
-
/** Which buffer the NEXT nextBuffer
|
|
1582
|
+
/** Which buffer the NEXT nextBuffer call returns (0 = A, 1 = B). */
|
|
1575
1583
|
next = 0;
|
|
1576
1584
|
// --- seed key ---
|
|
1577
1585
|
seededBase = null;
|
|
@@ -1674,7 +1682,7 @@ var PressureSampler = class {
|
|
|
1674
1682
|
/**
|
|
1675
1683
|
* Record one onFrame tick. `settled` marks a tick that arrived while the
|
|
1676
1684
|
* scene was at rest (sim settled, no pending commit work) — the idle-
|
|
1677
|
-
* wakeup counter, which reads 0 when the
|
|
1685
|
+
* wakeup counter, which reads 0 when the gated activity clock is honest.
|
|
1678
1686
|
*/
|
|
1679
1687
|
noteFrame(timeMs, settled) {
|
|
1680
1688
|
this.frames += 1;
|
|
@@ -1844,7 +1852,7 @@ var DegradeController = class {
|
|
|
1844
1852
|
/**
|
|
1845
1853
|
* Resource-admission trigger: engage the NEXT not-yet-engaged step in the
|
|
1846
1854
|
* declared order. Null when the order is exhausted — the caller must then
|
|
1847
|
-
* REJECT before allocating
|
|
1855
|
+
* REJECT before allocating.
|
|
1848
1856
|
*/
|
|
1849
1857
|
engageNextResourceStep(visible) {
|
|
1850
1858
|
for (const step of this.limits.resourceDegradationOrder) {
|
|
@@ -2057,8 +2065,8 @@ var LinkPickIndex = class {
|
|
|
2057
2065
|
while (!r.done) r = it.next();
|
|
2058
2066
|
}
|
|
2059
2067
|
/**
|
|
2060
|
-
* Incremental build: yields whenever `now
|
|
2061
|
-
* scheduler can spread the work across idle frames
|
|
2068
|
+
* Incremental build: yields whenever `now − sliceStart ≥ budgetMs` so a
|
|
2069
|
+
* scheduler can spread the work across idle frames.
|
|
2062
2070
|
* Pure generator — no rAF/timers here; the caller owns scheduling.
|
|
2063
2071
|
*
|
|
2064
2072
|
* The previous grid stays armed and queryable until the new one commits on
|
|
@@ -2222,7 +2230,7 @@ var LinkPickIndex = class {
|
|
|
2222
2230
|
* Nearest link within `tolerance` (space units) of the space point
|
|
2223
2231
|
* `(x, y)`, or null. Scans the candidate cells covering the tolerance
|
|
2224
2232
|
* disc's bounding box (typically the 3×3 neighborhood; more when the
|
|
2225
|
-
* tolerance exceeds the cell size), applies the optional
|
|
2233
|
+
* tolerance exceeds the cell size), applies the optional visibility
|
|
2226
2234
|
* mask per candidate, then runs the exact point→segment distance test.
|
|
2227
2235
|
* Nearest wins; exact-distance ties break toward the LOWER link index.
|
|
2228
2236
|
* Returns null while unbuilt/invalidated (picking disarmed).
|
|
@@ -2374,7 +2382,7 @@ var EdgePickingFacade = class {
|
|
|
2374
2382
|
this.generation++;
|
|
2375
2383
|
this.index.invalidate();
|
|
2376
2384
|
}
|
|
2377
|
-
/**
|
|
2385
|
+
/** mask pass-through stub: applied per candidate at query time. */
|
|
2378
2386
|
setLinkVisibilityMask(mask) {
|
|
2379
2387
|
this.mask = mask;
|
|
2380
2388
|
}
|
|
@@ -2734,7 +2742,7 @@ function throwAborted2(signal, sourceId, targetId) {
|
|
|
2734
2742
|
const reason = signal.reason;
|
|
2735
2743
|
throw new OrbitOperationError(
|
|
2736
2744
|
reason === void 0 ? { code: "aborted" } : { code: "aborted", cause: reason },
|
|
2737
|
-
`local find('${sourceId}' \u2192 '${targetId}') aborted mid-scan
|
|
2745
|
+
`local find('${sourceId}' \u2192 '${targetId}') aborted mid-scan`
|
|
2738
2746
|
);
|
|
2739
2747
|
}
|
|
2740
2748
|
function createLocalPathService(getBase) {
|
|
@@ -2919,7 +2927,7 @@ function throwAborted3(signal, query) {
|
|
|
2919
2927
|
const reason = signal.reason;
|
|
2920
2928
|
throw new OrbitOperationError(
|
|
2921
2929
|
reason === void 0 ? { code: "aborted" } : { code: "aborted", cause: reason },
|
|
2922
|
-
`local search('${query}') aborted mid-scan
|
|
2930
|
+
`local search('${query}') aborted mid-scan`
|
|
2923
2931
|
);
|
|
2924
2932
|
}
|
|
2925
2933
|
function createLocalSearchService(getBase) {
|
|
@@ -2982,6 +2990,16 @@ function checkStringColumn(col, rows, where, isIds, out) {
|
|
|
2982
2990
|
out.push({ where, problem: "not-a-string-column", detail: 'expected {kind:"string", dictionary, codes}' });
|
|
2983
2991
|
return;
|
|
2984
2992
|
}
|
|
2993
|
+
for (let d = 0; d < col.dictionary.length; d++) {
|
|
2994
|
+
if (typeof col.dictionary[d] !== "string") {
|
|
2995
|
+
out.push({
|
|
2996
|
+
where,
|
|
2997
|
+
problem: "not-a-string-column",
|
|
2998
|
+
detail: `dictionary entry ${d} is not a string`
|
|
2999
|
+
});
|
|
3000
|
+
return;
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
2985
3003
|
if (col.codes.length !== rows) {
|
|
2986
3004
|
const detached = col.codes.length === 0 && col.codes.buffer.byteLength === 0;
|
|
2987
3005
|
out.push({
|
|
@@ -3230,10 +3248,12 @@ function detachColumnarBuffers(snapshot) {
|
|
|
3230
3248
|
return detached;
|
|
3231
3249
|
}
|
|
3232
3250
|
|
|
3233
|
-
// src/
|
|
3234
|
-
function
|
|
3235
|
-
return new URL("./worker/entry.js", import.meta.url);
|
|
3251
|
+
// src/workerAsset.ts
|
|
3252
|
+
function createDefaultWorker() {
|
|
3253
|
+
return new Worker(new URL("./worker/entry.js", import.meta.url), { type: "module" });
|
|
3236
3254
|
}
|
|
3255
|
+
|
|
3256
|
+
// src/worker/lane.ts
|
|
3237
3257
|
function transportFromWorker(worker) {
|
|
3238
3258
|
return {
|
|
3239
3259
|
post: (envelope, transfers) => worker.postMessage(envelope, [...transfers]),
|
|
@@ -3275,10 +3295,7 @@ var WorkerLane = class {
|
|
|
3275
3295
|
let transport = this.options.transport ?? null;
|
|
3276
3296
|
if (transport === null) {
|
|
3277
3297
|
const factory = this.options.factory;
|
|
3278
|
-
const worker = factory !== void 0 && "create" in factory ? factory.create() : new Worker(
|
|
3279
|
-
factory !== void 0 && "url" in factory ? factory.url : defaultWorkerUrl(),
|
|
3280
|
-
{ type: "module" }
|
|
3281
|
-
);
|
|
3298
|
+
const worker = factory !== void 0 && "create" in factory ? factory.create() : factory !== void 0 && "url" in factory ? new Worker(factory.url, { type: "module" }) : createDefaultWorker();
|
|
3282
3299
|
transport = transportFromWorker(worker);
|
|
3283
3300
|
}
|
|
3284
3301
|
transport.onReply((reply) => this.settle(reply));
|
|
@@ -3291,8 +3308,8 @@ var WorkerLane = class {
|
|
|
3291
3308
|
}
|
|
3292
3309
|
return this.availableState;
|
|
3293
3310
|
}
|
|
3294
|
-
/**
|
|
3295
|
-
*
|
|
3311
|
+
/** A constructed-but-dead worker must strand NOTHING: every pending
|
|
3312
|
+
* request rejects as 'worker-failed' so callers
|
|
3296
3313
|
* run their main-lane fallback, the lane goes permanently unavailable,
|
|
3297
3314
|
* and the unavailability callback fires (the instance one-shots it). */
|
|
3298
3315
|
fail(reason) {
|
|
@@ -3348,7 +3365,7 @@ var WorkerLane = class {
|
|
|
3348
3365
|
}
|
|
3349
3366
|
/** Abort everything in flight (epoch advance / detach / dataset swap).
|
|
3350
3367
|
* The ledger's controllers fire each pending promise's abort listener;
|
|
3351
|
-
* the sweep below catches anything tracked before a listener attached
|
|
3368
|
+
* the sweep below catches anything tracked before a listener attached
|
|
3352
3369
|
* rejects stay idempotent through the delete guard. */
|
|
3353
3370
|
abortAll() {
|
|
3354
3371
|
this.ledger.abortAll();
|
|
@@ -3817,7 +3834,7 @@ var MetricStore = class {
|
|
|
3817
3834
|
/** Admitted async columns by metric name; NaN encodes null. */
|
|
3818
3835
|
columns = /* @__PURE__ */ new Map();
|
|
3819
3836
|
degreePasses = 0;
|
|
3820
|
-
/**
|
|
3837
|
+
/** telemetry: estimated bytes of metric storage held. */
|
|
3821
3838
|
estimatedBytes() {
|
|
3822
3839
|
let bytes = 0;
|
|
3823
3840
|
for (const col of this.columns.values()) bytes += col.byteLength;
|
|
@@ -3848,10 +3865,10 @@ var MetricStore = class {
|
|
|
3848
3865
|
}
|
|
3849
3866
|
}
|
|
3850
3867
|
/**
|
|
3851
|
-
* Joins async metric columns against the accepted model
|
|
3868
|
+
* Joins async metric columns against the accepted model.
|
|
3852
3869
|
* Revision-gated PER COLUMN (I1): a column whose issue-time
|
|
3853
3870
|
* `forModelRevision` stamp differs from `opts.modelRevision` is discarded
|
|
3854
|
-
* (info diagnostic — a normal async race outcome
|
|
3871
|
+
* (info diagnostic — a normal async race outcome). A missing or
|
|
3855
3872
|
* mismatched stamp is never defaulted to the current revision — that
|
|
3856
3873
|
* would make the gate self-satisfying.
|
|
3857
3874
|
* Structural rejections ('index' length mismatch, missing/mismatched ids)
|
|
@@ -3869,7 +3886,7 @@ var MetricStore = class {
|
|
|
3869
3886
|
"info",
|
|
3870
3887
|
1,
|
|
3871
3888
|
[column.metric],
|
|
3872
|
-
`metric column '${column.metric}' discarded: computed for model revision ${String(column.forModelRevision)} but the update was issued at revision ${String(opts.modelRevision)}
|
|
3889
|
+
`metric column '${column.metric}' discarded: computed for model revision ${String(column.forModelRevision)} but the update was issued at revision ${String(opts.modelRevision)}`
|
|
3873
3890
|
)
|
|
3874
3891
|
);
|
|
3875
3892
|
continue;
|
|
@@ -3979,7 +3996,7 @@ var MetricStore = class {
|
|
|
3979
3996
|
}
|
|
3980
3997
|
/**
|
|
3981
3998
|
* Raw column in accepted-base order, or null when unavailable. NaN encodes
|
|
3982
|
-
* null
|
|
3999
|
+
* null — consumers exclude NaN slots from domains. Do NOT mutate:
|
|
3983
4000
|
* this is the live cache, not a copy.
|
|
3984
4001
|
*/
|
|
3985
4002
|
metricValues(metric) {
|
|
@@ -4025,7 +4042,7 @@ var MetricStore = class {
|
|
|
4025
4042
|
};
|
|
4026
4043
|
|
|
4027
4044
|
// src/capabilityPolicy.ts
|
|
4028
|
-
var CLUSTER_FORCE_DEGRADATION_REASON = "clusters requested but the engine capability record does not declare clusterForce; the cluster force is inert while membership, cluster labels, and centroids still work
|
|
4045
|
+
var CLUSTER_FORCE_DEGRADATION_REASON = "clusters requested but the engine capability record does not declare clusterForce; the cluster force is inert while membership, cluster labels, and centroids still work";
|
|
4029
4046
|
function degradation(feature, reason) {
|
|
4030
4047
|
return Object.freeze({ feature, reason });
|
|
4031
4048
|
}
|
|
@@ -4038,7 +4055,7 @@ function resolveEnginePolicy(capabilities, requested) {
|
|
|
4038
4055
|
degradations.push(
|
|
4039
4056
|
degradation(
|
|
4040
4057
|
"edgeArrows",
|
|
4041
|
-
"edgeArrows requested but the engine capability record does not declare edgeArrows; the prop is inert
|
|
4058
|
+
"edgeArrows requested but the engine capability record does not declare edgeArrows; the prop is inert"
|
|
4042
4059
|
)
|
|
4043
4060
|
);
|
|
4044
4061
|
}
|
|
@@ -4046,7 +4063,7 @@ function resolveEnginePolicy(capabilities, requested) {
|
|
|
4046
4063
|
degradations.push(
|
|
4047
4064
|
degradation(
|
|
4048
4065
|
"images",
|
|
4049
|
-
"node images requested but the engine capability record does not declare pointImages; placeholder glyphs render and image refs are retained
|
|
4066
|
+
"node images requested but the engine capability record does not declare pointImages; placeholder glyphs render and image refs are retained"
|
|
4050
4067
|
)
|
|
4051
4068
|
);
|
|
4052
4069
|
}
|
|
@@ -4069,14 +4086,14 @@ function assertCapabilityMethodParity(engine) {
|
|
|
4069
4086
|
const mismatches = [];
|
|
4070
4087
|
const capabilities = engine.capabilities;
|
|
4071
4088
|
if (capabilities === null || typeof capabilities !== "object") {
|
|
4072
|
-
mismatches.push("engine.capabilities is missing or not an object
|
|
4089
|
+
mismatches.push("engine.capabilities is missing or not an object");
|
|
4073
4090
|
return mismatches;
|
|
4074
4091
|
}
|
|
4075
4092
|
const caps = capabilities;
|
|
4076
4093
|
const getPositions = engine.getPositions;
|
|
4077
4094
|
if (caps.trackedPositions === true && typeof getPositions !== "function") {
|
|
4078
4095
|
mismatches.push(
|
|
4079
|
-
"capabilities.trackedPositions is declared but engine.getPositions is absent
|
|
4096
|
+
"capabilities.trackedPositions is declared but engine.getPositions is absent"
|
|
4080
4097
|
);
|
|
4081
4098
|
}
|
|
4082
4099
|
return mismatches;
|
|
@@ -4163,7 +4180,7 @@ var ImageAtlasPipeline = class {
|
|
|
4163
4180
|
generation = null;
|
|
4164
4181
|
flushScheduled = false;
|
|
4165
4182
|
disposed = false;
|
|
4166
|
-
/**
|
|
4183
|
+
/** Evicted-entry bitmaps awaiting close — closed AFTER the
|
|
4167
4184
|
* flush that carries their removeSlots, so the instance's recovery-replay
|
|
4168
4185
|
* map (pruned synchronously in the batch callback) can never re-send a
|
|
4169
4186
|
* closed bitmap. */
|
|
@@ -4397,7 +4414,7 @@ var ImageAtlasPipeline = class {
|
|
|
4397
4414
|
return index;
|
|
4398
4415
|
}
|
|
4399
4416
|
/**
|
|
4400
|
-
*
|
|
4417
|
+
* Roster-atomic resource mappings: the SYNCHRONOUS point→slot
|
|
4401
4418
|
* mapping for the last requested roster — a slot appears only when its ref
|
|
4402
4419
|
* is resolved AND the engine has already received the bitmap (delivered);
|
|
4403
4420
|
* pending, failed, evicted, and reused-but-undelivered refs are the −1
|
|
@@ -4477,12 +4494,12 @@ var SoftMask = class {
|
|
|
4477
4494
|
edgeHideLane;
|
|
4478
4495
|
edgeDimLane;
|
|
4479
4496
|
sources = /* @__PURE__ */ new Set();
|
|
4480
|
-
/** Dedicated internal source implementing the
|
|
4497
|
+
/** Dedicated internal source implementing the node→edge cascade. */
|
|
4481
4498
|
cascadeSource = null;
|
|
4482
4499
|
overflowedFlag = false;
|
|
4483
4500
|
/** Total memberships currently held across all sources and lanes. */
|
|
4484
4501
|
totalHeld = 0;
|
|
4485
|
-
/**
|
|
4502
|
+
/** O(Δ) op counters. */
|
|
4486
4503
|
statsBox = {
|
|
4487
4504
|
slotsVisited: 0,
|
|
4488
4505
|
zeroCrossings: 0,
|
|
@@ -4505,12 +4522,12 @@ var SoftMask = class {
|
|
|
4505
4522
|
return this.edgeCap;
|
|
4506
4523
|
}
|
|
4507
4524
|
/** One-time latch: some counter hit 0xFFFF and an increment was dropped.
|
|
4508
|
-
* Counts may drift afterwards; the caller reports it
|
|
4525
|
+
* Counts may drift afterwards; the caller reports it. */
|
|
4509
4526
|
get overflowed() {
|
|
4510
4527
|
return this.overflowedFlag;
|
|
4511
4528
|
}
|
|
4512
4529
|
// Live counter columns (read-only views by convention — do not mutate;
|
|
4513
|
-
// references are replaced on grow
|
|
4530
|
+
// references are replaced on grow).
|
|
4514
4531
|
get nodeHideFailures() {
|
|
4515
4532
|
return this.nodeHideLane.counters;
|
|
4516
4533
|
}
|
|
@@ -4525,7 +4542,7 @@ var SoftMask = class {
|
|
|
4525
4542
|
}
|
|
4526
4543
|
/**
|
|
4527
4544
|
* Grows capacities for structure changes (existing slot state is
|
|
4528
|
-
* preserved; new slots start fully visible). Capacities never shrink
|
|
4545
|
+
* preserved; new slots start fully visible). Capacities never shrink
|
|
4529
4546
|
* a smaller value is a no-op for that dimension.
|
|
4530
4547
|
*/
|
|
4531
4548
|
grow(nodeCapacity, edgeCapacity) {
|
|
@@ -4550,7 +4567,7 @@ var SoftMask = class {
|
|
|
4550
4567
|
this.edgeCap = edgeCapacity;
|
|
4551
4568
|
}
|
|
4552
4569
|
}
|
|
4553
|
-
/** Registers a new failure source. No cap on source count
|
|
4570
|
+
/** Registers a new failure source. No cap on source count. */
|
|
4554
4571
|
acquire(name) {
|
|
4555
4572
|
const state = {
|
|
4556
4573
|
name,
|
|
@@ -4605,11 +4622,11 @@ var SoftMask = class {
|
|
|
4605
4622
|
};
|
|
4606
4623
|
}
|
|
4607
4624
|
/**
|
|
4608
|
-
*
|
|
4625
|
+
* edge cascade over the mask lane: recomputes, from the CURRENT node
|
|
4609
4626
|
* hide lane, the set of edges with at least one hidden endpoint, and feeds
|
|
4610
4627
|
* it to the dedicated internal cascade source (edge hide lane only).
|
|
4611
|
-
* `links` is the flat `[src0, tgt0, src1, tgt1, …]` node-slot pair buffer
|
|
4612
|
-
*
|
|
4628
|
+
* `links` is the flat `[src0, tgt0, src1, tgt1, …]` node-slot pair buffer;
|
|
4629
|
+
* edge slot i has endpoints at links[2i]/[2i+1].
|
|
4613
4630
|
* O(E) scan per call — typically once per drain — but only edges whose
|
|
4614
4631
|
* cascade state changed produce counter deltas (and thus dirty entries).
|
|
4615
4632
|
* Edges beyond `links.length / 2` are treated as having no hidden
|
|
@@ -4640,11 +4657,11 @@ var SoftMask = class {
|
|
|
4640
4657
|
}
|
|
4641
4658
|
if (hide[source] !== 0 || hide[target] !== 0) failing.push(i);
|
|
4642
4659
|
}
|
|
4643
|
-
this.cascadeSource ??= this.acquire("
|
|
4660
|
+
this.cascadeSource ??= this.acquire("node\u2192edge cascade");
|
|
4644
4661
|
this.cascadeSource.setEdgeFailures(failing);
|
|
4645
4662
|
}
|
|
4646
4663
|
/**
|
|
4647
|
-
* O(incident-edges) delta form of the
|
|
4664
|
+
* O(incident-edges) delta form of the cascade: for each node
|
|
4648
4665
|
* whose HIDE visibility crossed zero, recompute only its incident edges'
|
|
4649
4666
|
* cascade state from the CURRENT node counters and apply the delta through
|
|
4650
4667
|
* the same internal cascade source the full form uses — the two compose
|
|
@@ -4656,7 +4673,7 @@ var SoftMask = class {
|
|
|
4656
4673
|
applyNodeCascadeToEdgesDelta(links, incidence, crossedNodes) {
|
|
4657
4674
|
if (crossedNodes.length === 0) return;
|
|
4658
4675
|
const hide = this.nodeHideLane.counters;
|
|
4659
|
-
this.cascadeSource ??= this.acquire("
|
|
4676
|
+
this.cascadeSource ??= this.acquire("node\u2192edge cascade");
|
|
4660
4677
|
const nowFailing = [];
|
|
4661
4678
|
const nowClear = [];
|
|
4662
4679
|
for (let k = 0; k < crossedNodes.length; k++) {
|
|
@@ -4686,7 +4703,7 @@ var SoftMask = class {
|
|
|
4686
4703
|
edgeVisibleCount: this.edgeHideLane.zeroCount
|
|
4687
4704
|
};
|
|
4688
4705
|
}
|
|
4689
|
-
/**
|
|
4706
|
+
/** telemetry: estimated bytes of mask storage held:
|
|
4690
4707
|
* four counter lanes (+pending trackers) and per-source flag columns. */
|
|
4691
4708
|
estimatedBytes() {
|
|
4692
4709
|
let bytes = 0;
|
|
@@ -4698,7 +4715,7 @@ var SoftMask = class {
|
|
|
4698
4715
|
}
|
|
4699
4716
|
return bytes;
|
|
4700
4717
|
}
|
|
4701
|
-
/**
|
|
4718
|
+
/** O(Δ) op counters (live object — snapshot before comparing). */
|
|
4702
4719
|
get stats() {
|
|
4703
4720
|
return this.statsBox;
|
|
4704
4721
|
}
|
|
@@ -4720,7 +4737,7 @@ var SoftMask = class {
|
|
|
4720
4737
|
isEdgeVisible(index) {
|
|
4721
4738
|
return this.edgeHideLane.counters[index] === 0;
|
|
4722
4739
|
}
|
|
4723
|
-
/** Dimmed iff visible AND dimFailures > 0
|
|
4740
|
+
/** Dimmed iff visible AND dimFailures > 0. */
|
|
4724
4741
|
isNodeDimmed(index) {
|
|
4725
4742
|
return this.isNodeVisible(index) && (this.nodeDimLane.counters[index] ?? 0) > 0;
|
|
4726
4743
|
}
|
|
@@ -4781,7 +4798,7 @@ var SoftMask = class {
|
|
|
4781
4798
|
mem.holes = 0;
|
|
4782
4799
|
}
|
|
4783
4800
|
/**
|
|
4784
|
-
* O(Δ) delta ops on one lane membership
|
|
4801
|
+
* O(Δ) delta ops on one lane membership. Adds and removes are
|
|
4785
4802
|
* idempotent per slot (adding a member / removing a non-member is a
|
|
4786
4803
|
* no-op); removed slots leave HOLES in `list` (compacted past 50%), so
|
|
4787
4804
|
* replace/clear passes must honor the bit0 guard above. `crossings`, when
|
|
@@ -4882,7 +4899,7 @@ var SoftMask = class {
|
|
|
4882
4899
|
this.applyMembership(this.edgeDimLane, state.edgeDim, null);
|
|
4883
4900
|
}
|
|
4884
4901
|
/** Debug balanced-increment assert: whenever no source holds any
|
|
4885
|
-
* membership, every counter must read zero (skipped once overflowed
|
|
4902
|
+
* membership, every counter must read zero (skipped once overflowed
|
|
4886
4903
|
* clamped increments legitimately drift the books). */
|
|
4887
4904
|
assertBalancedIfIdle() {
|
|
4888
4905
|
if (!DEBUG || this.overflowedFlag || this.totalHeld !== 0) return;
|
|
@@ -4904,7 +4921,7 @@ var SVG_MAX_ELEMENTS_DEFAULT = 5e4;
|
|
|
4904
4921
|
var SvgBudgetError = class extends Error {
|
|
4905
4922
|
constructor(elementCount, limit) {
|
|
4906
4923
|
super(
|
|
4907
|
-
`SVG export of ${elementCount} elements exceeds the ${limit}-element budget \u2014 filter/isolate first, or use the raster-hybrid fallback
|
|
4924
|
+
`SVG export of ${elementCount} elements exceeds the ${limit}-element budget \u2014 filter/isolate first, or use the raster-hybrid fallback`
|
|
4908
4925
|
);
|
|
4909
4926
|
this.elementCount = elementCount;
|
|
4910
4927
|
this.limit = limit;
|
|
@@ -5026,6 +5043,7 @@ var isNum = (v) => typeof v === "number" && Number.isFinite(v);
|
|
|
5026
5043
|
var isBool = (v) => typeof v === "boolean";
|
|
5027
5044
|
var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
5028
5045
|
var isStrArray = (v) => Array.isArray(v) && v.every(isStr);
|
|
5046
|
+
var isNumArray = (v) => Array.isArray(v) && v.every(isNum);
|
|
5029
5047
|
function validateBrush(v, at, problems) {
|
|
5030
5048
|
if (!isObj(v)) {
|
|
5031
5049
|
problems.push(`${at}: not an object`);
|
|
@@ -5044,18 +5062,43 @@ function validateBrush(v, at, problems) {
|
|
|
5044
5062
|
}
|
|
5045
5063
|
problems.push(`${at}: unknown brush kind`);
|
|
5046
5064
|
}
|
|
5047
|
-
function validateScale(v, at, problems) {
|
|
5065
|
+
function validateScale(v, at, valueKind, problems) {
|
|
5048
5066
|
if (!isObj(v)) {
|
|
5049
5067
|
problems.push(`${at}: not an object`);
|
|
5050
5068
|
return;
|
|
5051
5069
|
}
|
|
5052
|
-
|
|
5070
|
+
const isValueArray = valueKind === "string" ? isStrArray : isNumArray;
|
|
5071
|
+
const valueLabel = valueKind === "string" ? "string" : "finite number";
|
|
5072
|
+
if (v["kind"] === "sequential") {
|
|
5073
|
+
if (!isStr(v["metric"])) problems.push(`${at}: metric must be a string`);
|
|
5074
|
+
if (!isValueArray(v["range"]) || v["range"].length !== 2) {
|
|
5075
|
+
problems.push(`${at}: sequential range must be a [${valueLabel}, ${valueLabel}] tuple`);
|
|
5076
|
+
}
|
|
5077
|
+
if (v["domain"] !== void 0 && (!isNumArray(v["domain"]) || v["domain"].length !== 2)) {
|
|
5078
|
+
problems.push(`${at}: sequential domain must be a [min, max] finite-number tuple`);
|
|
5079
|
+
}
|
|
5080
|
+
return;
|
|
5081
|
+
}
|
|
5082
|
+
if (v["kind"] === "diverging") {
|
|
5053
5083
|
if (!isStr(v["metric"])) problems.push(`${at}: metric must be a string`);
|
|
5054
|
-
if (!
|
|
5084
|
+
if (!isValueArray(v["range"]) || v["range"].length !== 3) {
|
|
5085
|
+
problems.push(
|
|
5086
|
+
`${at}: diverging range must be a [${valueLabel}, ${valueLabel}, ${valueLabel}] tuple`
|
|
5087
|
+
);
|
|
5088
|
+
}
|
|
5089
|
+
if (!isNum(v["mid"])) {
|
|
5090
|
+
problems.push(`${at}: diverging mid must be a finite number`);
|
|
5091
|
+
}
|
|
5055
5092
|
return;
|
|
5056
5093
|
}
|
|
5057
5094
|
if (v["kind"] === "categorical") {
|
|
5058
5095
|
if (!isStr(v["by"])) problems.push(`${at}: categorical 'by' must be a field string`);
|
|
5096
|
+
if (v["domain"] !== void 0 && !isStrArray(v["domain"])) {
|
|
5097
|
+
problems.push(`${at}: categorical domain must be a string[]`);
|
|
5098
|
+
}
|
|
5099
|
+
if (v["palette"] !== void 0 && !isValueArray(v["palette"])) {
|
|
5100
|
+
problems.push(`${at}: categorical palette must be a ${valueLabel}[]`);
|
|
5101
|
+
}
|
|
5059
5102
|
return;
|
|
5060
5103
|
}
|
|
5061
5104
|
problems.push(`${at}: unknown scale kind`);
|
|
@@ -5180,10 +5223,10 @@ function validateViewState(raw) {
|
|
|
5180
5223
|
problems.push("styling: must be an object");
|
|
5181
5224
|
} else {
|
|
5182
5225
|
if (styling["nodeColor"] !== void 0) {
|
|
5183
|
-
validateScale(styling["nodeColor"], "styling.nodeColor", problems);
|
|
5226
|
+
validateScale(styling["nodeColor"], "styling.nodeColor", "string", problems);
|
|
5184
5227
|
}
|
|
5185
5228
|
if (styling["nodeSize"] !== void 0) {
|
|
5186
|
-
validateScale(styling["nodeSize"], "styling.nodeSize", problems);
|
|
5229
|
+
validateScale(styling["nodeSize"], "styling.nodeSize", "number", problems);
|
|
5187
5230
|
}
|
|
5188
5231
|
for (const flag of ["showLinks", "edgeArrows"]) {
|
|
5189
5232
|
if (styling[flag] !== void 0 && !isBool(styling[flag])) {
|
|
@@ -5264,7 +5307,7 @@ function upperBound(values, sorted, target) {
|
|
|
5264
5307
|
return lo;
|
|
5265
5308
|
}
|
|
5266
5309
|
var TypedColumnCrossfilter = class {
|
|
5267
|
-
/** Test instrumentation; see CrossfilterStats. Reset with resetStats
|
|
5310
|
+
/** Test instrumentation; see CrossfilterStats. Reset with resetStats. */
|
|
5268
5311
|
stats = {
|
|
5269
5312
|
slotsWalked: 0,
|
|
5270
5313
|
fullSorts: 0,
|
|
@@ -5272,7 +5315,7 @@ var TypedColumnCrossfilter = class {
|
|
|
5272
5315
|
binUpdates: 0,
|
|
5273
5316
|
filteredRecomputes: 0
|
|
5274
5317
|
};
|
|
5275
|
-
/** Count of dims whose filtered layer is live-maintained
|
|
5318
|
+
/** Count of dims whose filtered layer is live-maintained. */
|
|
5276
5319
|
liveDims = 0;
|
|
5277
5320
|
dims = [];
|
|
5278
5321
|
byKey = /* @__PURE__ */ new Map();
|
|
@@ -5298,7 +5341,7 @@ var TypedColumnCrossfilter = class {
|
|
|
5298
5341
|
this.stats.binUpdates = 0;
|
|
5299
5342
|
this.stats.filteredRecomputes = 0;
|
|
5300
5343
|
}
|
|
5301
|
-
/**
|
|
5344
|
+
/** telemetry: estimated bytes of typed-column storage held.
|
|
5302
5345
|
* Documented components: per-dim value/permutation/bin/code/pass arrays,
|
|
5303
5346
|
* the global failure counter, and the external mask. */
|
|
5304
5347
|
estimatedBytes() {
|
|
@@ -5313,7 +5356,7 @@ var TypedColumnCrossfilter = class {
|
|
|
5313
5356
|
}
|
|
5314
5357
|
return bytes;
|
|
5315
5358
|
}
|
|
5316
|
-
/**
|
|
5359
|
+
/** live-layer bookkeeping — the ONLY writer of `filteredLive`. */
|
|
5317
5360
|
setFilteredLive(dim, live) {
|
|
5318
5361
|
if (dim.filteredLive === live) return;
|
|
5319
5362
|
dim.filteredLive = live;
|
|
@@ -5376,8 +5419,8 @@ var TypedColumnCrossfilter = class {
|
|
|
5376
5419
|
}
|
|
5377
5420
|
/**
|
|
5378
5421
|
* External node-mask predicate for the joint "filtered" second layer (the
|
|
5379
|
-
* instance wires the
|
|
5380
|
-
* never selection visibility. Length must equal rowCount
|
|
5422
|
+
* instance wires the filter-prop node mask in). Affects summaries only,
|
|
5423
|
+
* never selection visibility. Length must equal rowCount. Notifies on
|
|
5381
5424
|
* observable change; does NOT advance selectionRevision.
|
|
5382
5425
|
*/
|
|
5383
5426
|
setExternalMask(passSlots) {
|
|
@@ -5402,7 +5445,7 @@ var TypedColumnCrossfilter = class {
|
|
|
5402
5445
|
this.notify();
|
|
5403
5446
|
}
|
|
5404
5447
|
/**
|
|
5405
|
-
* Immutable summary. The filtered layer is recomputed lazily when dirty
|
|
5448
|
+
* Immutable summary. The filtered layer is recomputed lazily when dirty
|
|
5406
5449
|
* O(rows) per dirty summarize (v0.7 tier; see module doc). Returned objects
|
|
5407
5450
|
* are frozen and never mutated by later operations.
|
|
5408
5451
|
*/
|
|
@@ -5454,7 +5497,7 @@ var TypedColumnCrossfilter = class {
|
|
|
5454
5497
|
return Object.freeze(domain !== void 0 ? { ...base, domain } : base);
|
|
5455
5498
|
}
|
|
5456
5499
|
/**
|
|
5457
|
-
* Incrementally extend columns with new rows
|
|
5500
|
+
* Incrementally extend columns with new rows: no full
|
|
5458
5501
|
* rebuild, no full re-argsort — the pre-sorted old permutation merges with
|
|
5459
5502
|
* the sorted new block. Brushes stay by key and are applied to the NEW slots
|
|
5460
5503
|
* only; the returned delta covers only new slots. Keeps selectionRevision;
|
|
@@ -5708,16 +5751,16 @@ var TypedColumnCrossfilter = class {
|
|
|
5708
5751
|
}
|
|
5709
5752
|
}
|
|
5710
5753
|
/**
|
|
5711
|
-
*
|
|
5754
|
+
* inline maintenance of LIVE filtered layers, dispatched from the
|
|
5712
5755
|
* one place that knows the failCount transition. `boundary` is the
|
|
5713
5756
|
* other-failures picture at the interesting side of the flip (after for
|
|
5714
5757
|
* shown, before for hidden):
|
|
5715
|
-
*
|
|
5716
|
-
*
|
|
5717
|
-
*
|
|
5718
|
-
*
|
|
5719
|
-
*
|
|
5720
|
-
*
|
|
5758
|
+
* - 0 → the slot crossed the FULLY-VISIBLE boundary: every other live
|
|
5759
|
+
* layer counts it (own layers ignore the own-dim brush, so the brushed
|
|
5760
|
+
* dim's layer is provably unchanged by its own flip);
|
|
5761
|
+
* - 1 → exactly one OTHER dim still fails the slot: only that dim's
|
|
5762
|
+
* what-if-I-cleared-mine layer flips;
|
|
5763
|
+
* - ≥2 → no layer can change.
|
|
5721
5764
|
* External-mask-excluded and hygiene-invalid slots contribute nothing
|
|
5722
5765
|
* either way and are skipped.
|
|
5723
5766
|
*/
|
|
@@ -6022,7 +6065,7 @@ function assertSerializable(value, slice) {
|
|
|
6022
6065
|
switch (typeof v) {
|
|
6023
6066
|
case "function":
|
|
6024
6067
|
throw new TypeError(
|
|
6025
|
-
`history command for slice "${slice}" carries a function at ${path} \u2014 commands are value diffs, not closures
|
|
6068
|
+
`history command for slice "${slice}" carries a function at ${path} \u2014 commands are value diffs, not closures`
|
|
6026
6069
|
);
|
|
6027
6070
|
case "symbol":
|
|
6028
6071
|
case "bigint":
|
|
@@ -6037,7 +6080,7 @@ function assertSerializable(value, slice) {
|
|
|
6037
6080
|
if (v === null) return;
|
|
6038
6081
|
if (v instanceof Map || v instanceof Set) {
|
|
6039
6082
|
throw new TypeError(
|
|
6040
|
-
`history command for slice "${slice}" carries a ${v instanceof Map ? "Map" : "Set"} at ${path} \u2014 convert to arrays/objects before recording
|
|
6083
|
+
`history command for slice "${slice}" carries a ${v instanceof Map ? "Map" : "Set"} at ${path} \u2014 convert to arrays/objects before recording`
|
|
6041
6084
|
);
|
|
6042
6085
|
}
|
|
6043
6086
|
if (seen.has(v)) {
|
|
@@ -6059,7 +6102,7 @@ var HistoryKernel = class {
|
|
|
6059
6102
|
debug;
|
|
6060
6103
|
undoStack = [];
|
|
6061
6104
|
redoStack = [];
|
|
6062
|
-
/** Nested begin
|
|
6105
|
+
/** Nested begin joins the outer transaction (depth-counted). */
|
|
6063
6106
|
txDepth = 0;
|
|
6064
6107
|
txCommands = [];
|
|
6065
6108
|
txLabel;
|
|
@@ -6125,7 +6168,7 @@ var HistoryKernel = class {
|
|
|
6125
6168
|
}
|
|
6126
6169
|
this.pushEntry({ label: void 0, commands: [command] }, null);
|
|
6127
6170
|
}
|
|
6128
|
-
/** Close a transaction; the outermost end
|
|
6171
|
+
/** Close a transaction; the outermost end pushes one stack entry. */
|
|
6129
6172
|
end() {
|
|
6130
6173
|
if (!this.enabled) return;
|
|
6131
6174
|
if (this.txDepth === 0) throw new Error("HistoryKernel.end() without a matching begin()");
|
|
@@ -6395,7 +6438,7 @@ function createGraphInstance(opts) {
|
|
|
6395
6438
|
if (accepted === null) {
|
|
6396
6439
|
throw new OrbitOperationError(
|
|
6397
6440
|
{ code: "aborted", cause: "no accepted base" },
|
|
6398
|
-
"expansion requires an accepted base dataset
|
|
6441
|
+
"expansion requires an accepted base dataset"
|
|
6399
6442
|
);
|
|
6400
6443
|
}
|
|
6401
6444
|
return { accepted, adjacency: acceptedAdjacencyOf() };
|
|
@@ -6628,7 +6671,7 @@ function createGraphInstance(opts) {
|
|
|
6628
6671
|
severity: "error",
|
|
6629
6672
|
count: 1,
|
|
6630
6673
|
sampleIds: [],
|
|
6631
|
-
message: "columnar snapshot mutated while worker acceptance was pending \u2014 rejected whole (
|
|
6674
|
+
message: "columnar snapshot mutated while worker acceptance was pending \u2014 rejected whole (source coordinates are immutable; publish a new sourceRevision)"
|
|
6632
6675
|
}
|
|
6633
6676
|
];
|
|
6634
6677
|
publish({ diagnostics: composeDiagnostics() });
|
|
@@ -6662,7 +6705,7 @@ function createGraphInstance(opts) {
|
|
|
6662
6705
|
severity: "error",
|
|
6663
6706
|
count: 1,
|
|
6664
6707
|
sampleIds: [],
|
|
6665
|
-
message: "columnar snapshot mutated while worker acceptance was pending \u2014 rejected whole (
|
|
6708
|
+
message: "columnar snapshot mutated while worker acceptance was pending \u2014 rejected whole (source coordinates are immutable; publish a new sourceRevision)"
|
|
6666
6709
|
}
|
|
6667
6710
|
];
|
|
6668
6711
|
publish({ diagnostics: composeDiagnostics() });
|
|
@@ -6754,7 +6797,7 @@ function createGraphInstance(opts) {
|
|
|
6754
6797
|
if (event.engaged && !capLabelsNudged) {
|
|
6755
6798
|
capLabelsNudged = true;
|
|
6756
6799
|
console.warn(
|
|
6757
|
-
"orbit: cap-dom-labels engaged \u2014 DOM labels are hard-capped at the label budget above limits.domLabelNodes visible nodes
|
|
6800
|
+
"orbit: cap-dom-labels engaged \u2014 DOM labels are hard-capped at the label budget above limits.domLabelNodes visible nodes. A GPU label lane (labelStrategy 'sdf') is the planned scale path."
|
|
6758
6801
|
);
|
|
6759
6802
|
}
|
|
6760
6803
|
scheduleViewportRerank();
|
|
@@ -7407,7 +7450,7 @@ function createGraphInstance(opts) {
|
|
|
7407
7450
|
severity: "warning",
|
|
7408
7451
|
count: errorCount,
|
|
7409
7452
|
sampleIds: samples,
|
|
7410
|
-
message: `filter predicate threw for ${errorCount} item(s); failing open \u2014 throwing predicates never hide data
|
|
7453
|
+
message: `filter predicate threw for ${errorCount} item(s); failing open \u2014 throwing predicates never hide data`
|
|
7411
7454
|
}
|
|
7412
7455
|
];
|
|
7413
7456
|
} else {
|
|
@@ -7918,7 +7961,7 @@ function createGraphInstance(opts) {
|
|
|
7918
7961
|
}
|
|
7919
7962
|
function requireCrossfilterEngine() {
|
|
7920
7963
|
if (crossfilterEngine === null) {
|
|
7921
|
-
throw new Error("crossfilter session is not available
|
|
7964
|
+
throw new Error("crossfilter session is not available; set the crossfilter prop first");
|
|
7922
7965
|
}
|
|
7923
7966
|
return crossfilterEngine;
|
|
7924
7967
|
}
|
|
@@ -7969,13 +8012,13 @@ function createGraphInstance(opts) {
|
|
|
7969
8012
|
const eng = crossfilterEngine;
|
|
7970
8013
|
if (eng === null) {
|
|
7971
8014
|
throw new TypeError(
|
|
7972
|
-
"playTimeline: the crossfilter prop must configure dimensions first
|
|
8015
|
+
"playTimeline: the crossfilter prop must configure dimensions first"
|
|
7973
8016
|
);
|
|
7974
8017
|
}
|
|
7975
8018
|
const summary = eng.summarize(key);
|
|
7976
8019
|
if (summary.kind === "categorical" || summary.domain === void 0) {
|
|
7977
8020
|
throw new TypeError(
|
|
7978
|
-
`playTimeline: dimension "${key}" must be numeric/temporal with a non-empty domain
|
|
8021
|
+
`playTimeline: dimension "${key}" must be numeric/temporal with a non-empty domain`
|
|
7979
8022
|
);
|
|
7980
8023
|
}
|
|
7981
8024
|
const { min, max } = summary.domain;
|
|
@@ -7997,7 +8040,7 @@ function createGraphInstance(opts) {
|
|
|
7997
8040
|
);
|
|
7998
8041
|
}
|
|
7999
8042
|
if (mode !== "sliding" && mode !== "cumulative") {
|
|
8000
|
-
throw new TypeError(`playTimeline: unsupported mode "${String(mode)}"
|
|
8043
|
+
throw new TypeError(`playTimeline: unsupported mode "${String(mode)}"`);
|
|
8001
8044
|
}
|
|
8002
8045
|
stopTimelineTimer();
|
|
8003
8046
|
timelineSessionSeq += 1;
|
|
@@ -8193,7 +8236,7 @@ function createGraphInstance(opts) {
|
|
|
8193
8236
|
simRestarted = true;
|
|
8194
8237
|
}
|
|
8195
8238
|
}
|
|
8196
|
-
eng
|
|
8239
|
+
commitToEngine(eng, commit);
|
|
8197
8240
|
revisions.appliedRender = eng.appliedRevision();
|
|
8198
8241
|
const facade = session !== null ? session.edgePicking : null;
|
|
8199
8242
|
if (facade !== null) {
|
|
@@ -8470,7 +8513,7 @@ function createGraphInstance(opts) {
|
|
|
8470
8513
|
severity: "warning",
|
|
8471
8514
|
count: overload,
|
|
8472
8515
|
sampleIds: [],
|
|
8473
|
-
message: `showLabelsFor exceeds tracked-label capacity; ${overload} forced label(s) omitted
|
|
8516
|
+
message: `showLabelsFor exceeds tracked-label capacity; ${overload} forced label(s) omitted`
|
|
8474
8517
|
}
|
|
8475
8518
|
];
|
|
8476
8519
|
diagsChanged = true;
|
|
@@ -8746,7 +8789,7 @@ function createGraphInstance(opts) {
|
|
|
8746
8789
|
severity: "warning",
|
|
8747
8790
|
count: state.streak,
|
|
8748
8791
|
sampleIds: [],
|
|
8749
|
-
message: `${channel} reprojected on ${state.streak} consecutive commits with identical sampled outputs \u2014 likely an inline lambda prop; hoist or memoize it
|
|
8792
|
+
message: `${channel} reprojected on ${state.streak} consecutive commits with identical sampled outputs \u2014 likely an inline lambda prop; hoist or memoize it`
|
|
8750
8793
|
}
|
|
8751
8794
|
];
|
|
8752
8795
|
}
|
|
@@ -9148,9 +9191,9 @@ function createGraphInstance(opts) {
|
|
|
9148
9191
|
nodeIds: orderByAcceptedBase(full.nodeIds, nodeIndexBase()),
|
|
9149
9192
|
edgeIds: orderByAcceptedBase(full.edgeIds, edgeIndexById),
|
|
9150
9193
|
// Group namespace: the raw full-state form stays TOLERANT (deduped,
|
|
9151
|
-
// not validated — pinned
|
|
9194
|
+
// not validated — pinned legacy behavior); ids that stop naming a
|
|
9152
9195
|
// resolved group prune through the ownership path whenever the groups
|
|
9153
|
-
// RESOLUTION changes
|
|
9196
|
+
// RESOLUTION changes. selectGroups is the validating
|
|
9154
9197
|
// mutator.
|
|
9155
9198
|
groupIds: dedupeFirstOccurrence(full.groupIds)
|
|
9156
9199
|
});
|
|
@@ -9429,7 +9472,7 @@ function createGraphInstance(opts) {
|
|
|
9429
9472
|
if (DEV) {
|
|
9430
9473
|
rejectGroupOp(
|
|
9431
9474
|
"warning",
|
|
9432
|
-
"groupNodes ignored: membership is derived and read-only under groupBy
|
|
9475
|
+
"groupNodes ignored: membership is derived and read-only under groupBy \u2014 change the groupBy accessor or remove it",
|
|
9433
9476
|
spec.id
|
|
9434
9477
|
);
|
|
9435
9478
|
}
|
|
@@ -9452,7 +9495,7 @@ function createGraphInstance(opts) {
|
|
|
9452
9495
|
if (DEV) {
|
|
9453
9496
|
rejectGroupOp(
|
|
9454
9497
|
"warning",
|
|
9455
|
-
"ungroup ignored: membership is derived and read-only under groupBy
|
|
9498
|
+
"ungroup ignored: membership is derived and read-only under groupBy \u2014 change the groupBy accessor or remove it",
|
|
9456
9499
|
groupId
|
|
9457
9500
|
);
|
|
9458
9501
|
}
|
|
@@ -9477,7 +9520,7 @@ function createGraphInstance(opts) {
|
|
|
9477
9520
|
if (DEV) {
|
|
9478
9521
|
rejectGroupOp(
|
|
9479
9522
|
"warning",
|
|
9480
|
-
"setGroupCollapsed ignored: groups and groupBy are both configured
|
|
9523
|
+
"setGroupCollapsed ignored: groups and groupBy are both configured \u2014 remove one",
|
|
9481
9524
|
groupId
|
|
9482
9525
|
);
|
|
9483
9526
|
}
|
|
@@ -9488,7 +9531,7 @@ function createGraphInstance(opts) {
|
|
|
9488
9531
|
if (DEV) {
|
|
9489
9532
|
rejectGroupOp(
|
|
9490
9533
|
"warning",
|
|
9491
|
-
`setGroupCollapsed ignored: no derived group '${groupId}' in the current derivation
|
|
9534
|
+
`setGroupCollapsed ignored: no derived group '${groupId}' in the current derivation`,
|
|
9492
9535
|
groupId
|
|
9493
9536
|
);
|
|
9494
9537
|
}
|
|
@@ -9702,7 +9745,7 @@ function createGraphInstance(opts) {
|
|
|
9702
9745
|
function invalidateReplaceSessions(owner) {
|
|
9703
9746
|
for (const s of [...openSessions]) {
|
|
9704
9747
|
if (s === owner || s.purpose !== "replace") continue;
|
|
9705
|
-
abortSessionInternal(s, "stale-base: the model changed outside the session
|
|
9748
|
+
abortSessionInternal(s, "stale-base: the model changed outside the session", false);
|
|
9706
9749
|
}
|
|
9707
9750
|
}
|
|
9708
9751
|
function abortAllSessions(except, cause) {
|
|
@@ -9771,7 +9814,7 @@ function createGraphInstance(opts) {
|
|
|
9771
9814
|
if (expansionHandles.size === 0 && expansionLedger.size === 0) return;
|
|
9772
9815
|
const err = new OrbitOperationError(
|
|
9773
9816
|
{ code: "aborted", cause: "dataset-changed" },
|
|
9774
|
-
"expansion aborted: the datasetKey changed
|
|
9817
|
+
"expansion aborted: the datasetKey changed"
|
|
9775
9818
|
);
|
|
9776
9819
|
for (const [requestId, handle] of expansionHandles) {
|
|
9777
9820
|
handle.abort(err);
|
|
@@ -9792,7 +9835,7 @@ function createGraphInstance(opts) {
|
|
|
9792
9835
|
scopeExtraIds.clear();
|
|
9793
9836
|
expansionOverlays.clear();
|
|
9794
9837
|
abortExpansionsForDatasetSwap();
|
|
9795
|
-
abortSearchFlight("dataset-changed", "search aborted: the datasetKey changed
|
|
9838
|
+
abortSearchFlight("dataset-changed", "search aborted: the datasetKey changed");
|
|
9796
9839
|
resetMaskState();
|
|
9797
9840
|
historyKernel.clear();
|
|
9798
9841
|
stopTimelineTimer();
|
|
@@ -9954,7 +9997,7 @@ function createGraphInstance(opts) {
|
|
|
9954
9997
|
const labelRerank = recomputeCandidates();
|
|
9955
9998
|
const patch = {
|
|
9956
9999
|
revisions,
|
|
9957
|
-
// Counts are ACCEPTED-MODEL counts; the scene may be scoped
|
|
10000
|
+
// Counts are ACCEPTED-MODEL counts; the scene may be scoped.
|
|
9958
10001
|
nodeCount: p.merged.nodes.length,
|
|
9959
10002
|
edgeCount: p.merged.edges.length,
|
|
9960
10003
|
diagnostics: composeDiagnostics()
|
|
@@ -10014,7 +10057,7 @@ function createGraphInstance(opts) {
|
|
|
10014
10057
|
cancelFlushTimer(rec);
|
|
10015
10058
|
return rejectOperation(
|
|
10016
10059
|
{ code: "overlay-id-conflict", overlayId: id },
|
|
10017
|
-
`overlayId '${id}' is already reserved by an open or committed overlay for this dataset
|
|
10060
|
+
`overlayId '${id}' is already reserved by an open or committed overlay for this dataset`
|
|
10018
10061
|
);
|
|
10019
10062
|
}
|
|
10020
10063
|
reservedOverlayIds.set(id, rec);
|
|
@@ -10086,14 +10129,14 @@ function createGraphInstance(opts) {
|
|
|
10086
10129
|
if (rec.atomic && queuedBytes > rec.maxPendingBytes) {
|
|
10087
10130
|
return rejectOperation(
|
|
10088
10131
|
{ code: "queue-overflow", queuedBytes, limit: rec.maxPendingBytes },
|
|
10089
|
-
`append(): atomic session would queue ${queuedBytes} bytes, exceeding its non-drainable budget of ${rec.maxPendingBytes}; the append was rejected and the session remains open
|
|
10132
|
+
`append(): atomic session would queue ${queuedBytes} bytes, exceeding its non-drainable budget of ${rec.maxPendingBytes}; the append was rejected and the session remains open`
|
|
10090
10133
|
);
|
|
10091
10134
|
}
|
|
10092
10135
|
const overflowLimit = rec.maxPendingBytes * INGEST_OVERFLOW_FACTOR;
|
|
10093
10136
|
if (bytes > overflowLimit) {
|
|
10094
10137
|
return rejectOperation(
|
|
10095
10138
|
{ code: "queue-overflow", queuedBytes: bytes, limit: overflowLimit },
|
|
10096
|
-
`append(): batch of ${bytes} bytes exceeds the absolute cap of ${overflowLimit}
|
|
10139
|
+
`append(): batch of ${bytes} bytes exceeds the absolute cap of ${overflowLimit}`
|
|
10097
10140
|
);
|
|
10098
10141
|
}
|
|
10099
10142
|
const counts = stageBatch(
|
|
@@ -10151,7 +10194,7 @@ function createGraphInstance(opts) {
|
|
|
10151
10194
|
};
|
|
10152
10195
|
}
|
|
10153
10196
|
const datasetChanged = baseAccepted !== null && baseAccepted.datasetKey !== rec.datasetKey;
|
|
10154
|
-
abortAllSessions(rec, "replaced: a replace session committed
|
|
10197
|
+
abortAllSessions(rec, "replaced: a replace session committed");
|
|
10155
10198
|
const overlaysCleared = clearOverlayState();
|
|
10156
10199
|
const newBase = baseFromContribution(rec.datasetKey, sourceRevision, c, []);
|
|
10157
10200
|
const commitDiags = sessionCommitDiagnostics(rec.tallies, newBase.pendingEdges.length);
|
|
@@ -10249,7 +10292,7 @@ function createGraphInstance(opts) {
|
|
|
10249
10292
|
code: "resource-limit",
|
|
10250
10293
|
detail: { reason: err instanceof Error ? err.message : String(err) }
|
|
10251
10294
|
},
|
|
10252
|
-
`commit() failed; session rolled back
|
|
10295
|
+
`commit() failed; session rolled back`
|
|
10253
10296
|
);
|
|
10254
10297
|
}
|
|
10255
10298
|
rec.state = "committed";
|
|
@@ -10273,16 +10316,16 @@ function createGraphInstance(opts) {
|
|
|
10273
10316
|
}
|
|
10274
10317
|
if (purpose === "replace") {
|
|
10275
10318
|
if (opts2.sourceRevision === void 0) {
|
|
10276
|
-
throw new TypeError("beginIngest: purpose:'replace' requires sourceRevision
|
|
10319
|
+
throw new TypeError("beginIngest: purpose:'replace' requires sourceRevision");
|
|
10277
10320
|
}
|
|
10278
10321
|
if (opts2.atomic === false) {
|
|
10279
10322
|
throw new TypeError(
|
|
10280
|
-
"beginIngest: replace sessions are always atomic \u2014 progressive replace frames would expose rows before their sourceRevision existed
|
|
10323
|
+
"beginIngest: replace sessions are always atomic \u2014 progressive replace frames would expose rows before their sourceRevision existed"
|
|
10281
10324
|
);
|
|
10282
10325
|
}
|
|
10283
10326
|
if (baseSource === "declarative") {
|
|
10284
10327
|
throw new TypeError(
|
|
10285
|
-
"beginIngest: purpose:'replace' is unavailable while a declarative data source is active; keep applying snapshots via applyHostUpdate, or use an overlay session
|
|
10328
|
+
"beginIngest: purpose:'replace' is unavailable while a declarative data source is active; keep applying snapshots via applyHostUpdate, or use an overlay session"
|
|
10286
10329
|
);
|
|
10287
10330
|
}
|
|
10288
10331
|
}
|
|
@@ -10297,7 +10340,7 @@ function createGraphInstance(opts) {
|
|
|
10297
10340
|
if (baseAccepted === null || baseAccepted.datasetKey !== opts2.datasetKey) {
|
|
10298
10341
|
throw new OrbitOperationError(
|
|
10299
10342
|
{ code: "stale-revision", expected: currentModel, actual: opts2.baseModelRevision },
|
|
10300
|
-
baseAccepted === null ? "beginIngest: overlay sessions require an accepted base dataset
|
|
10343
|
+
baseAccepted === null ? "beginIngest: overlay sessions require an accepted base dataset" : `beginIngest: overlay sessions must name the current datasetKey '${baseAccepted.datasetKey}', got '${opts2.datasetKey}'`
|
|
10301
10344
|
);
|
|
10302
10345
|
}
|
|
10303
10346
|
}
|
|
@@ -10306,7 +10349,7 @@ function createGraphInstance(opts) {
|
|
|
10306
10349
|
purpose,
|
|
10307
10350
|
datasetKey: opts2.datasetKey,
|
|
10308
10351
|
sourceRevision: opts2.sourceRevision ?? null,
|
|
10309
|
-
// replace is always atomic; overlays default atomic:true
|
|
10352
|
+
// replace is always atomic; overlays default atomic:true.
|
|
10310
10353
|
atomic: purpose === "replace" ? true : opts2.atomic ?? true,
|
|
10311
10354
|
overlayId,
|
|
10312
10355
|
maxFlushLatencyMs: opts2.maxFlushLatencyMs ?? INGEST_MAX_FLUSH_LATENCY_MS_DEFAULT,
|
|
@@ -10419,11 +10462,11 @@ function createGraphInstance(opts) {
|
|
|
10419
10462
|
pushServiceDiagnostic(
|
|
10420
10463
|
"service-aborted",
|
|
10421
10464
|
"info",
|
|
10422
|
-
`expansion result for '${id}' discarded before admission: ${why}
|
|
10465
|
+
`expansion result for '${id}' discarded before admission: ${why}`
|
|
10423
10466
|
);
|
|
10424
10467
|
throw new OrbitOperationError(
|
|
10425
10468
|
{ code: "aborted", cause: why },
|
|
10426
|
-
`expandNode('${id}') result discarded: ${why}
|
|
10469
|
+
`expandNode('${id}') result discarded: ${why}`
|
|
10427
10470
|
);
|
|
10428
10471
|
}
|
|
10429
10472
|
async function mergeExpansionSession(id, ctx, at, batches, provenance) {
|
|
@@ -10644,14 +10687,14 @@ function createGraphInstance(opts) {
|
|
|
10644
10687
|
expansionPromises.delete(id);
|
|
10645
10688
|
const err = new OrbitOperationError(
|
|
10646
10689
|
{ code: "aborted", cause: "collapsed" },
|
|
10647
|
-
`expandNode('${id}') aborted by retractExpansion()
|
|
10690
|
+
`expandNode('${id}') aborted by retractExpansion()`
|
|
10648
10691
|
);
|
|
10649
10692
|
expansionHandles.get(requestId)?.abort(err);
|
|
10650
10693
|
expansionRejectors.get(requestId)?.(err);
|
|
10651
10694
|
pushServiceDiagnostic(
|
|
10652
10695
|
"service-aborted",
|
|
10653
10696
|
"info",
|
|
10654
|
-
`pending expansion of '${id}' aborted by retractExpansion()
|
|
10697
|
+
`pending expansion of '${id}' aborted by retractExpansion()`
|
|
10655
10698
|
);
|
|
10656
10699
|
publish({ pendingExpansions: expansionLedger.ids() });
|
|
10657
10700
|
}
|
|
@@ -10858,14 +10901,14 @@ function createGraphInstance(opts) {
|
|
|
10858
10901
|
if (ctx.signal.aborted) {
|
|
10859
10902
|
throw new OrbitOperationError(
|
|
10860
10903
|
{ code: "aborted", cause: "superseded" },
|
|
10861
|
-
`search('${query}') was superseded before admission
|
|
10904
|
+
`search('${query}') was superseded before admission`
|
|
10862
10905
|
);
|
|
10863
10906
|
}
|
|
10864
10907
|
const denial = acceptanceQueue.admit(() => searchAdmissible(ctx, at));
|
|
10865
10908
|
if (denial !== null) {
|
|
10866
10909
|
throw new OrbitOperationError(
|
|
10867
10910
|
{ code: "aborted", cause: denial },
|
|
10868
|
-
`search('${query}') result discarded: ${denial}
|
|
10911
|
+
`search('${query}') result discarded: ${denial}`
|
|
10869
10912
|
);
|
|
10870
10913
|
}
|
|
10871
10914
|
if (accepted === null) return raw;
|
|
@@ -10896,7 +10939,7 @@ function createGraphInstance(opts) {
|
|
|
10896
10939
|
const at = revisionSnapshot();
|
|
10897
10940
|
const key = serviceCacheKey({
|
|
10898
10941
|
serviceId: "search",
|
|
10899
|
-
// `fields` is defensive insurance
|
|
10942
|
+
// `fields` is defensive insurance: searchIndex is
|
|
10900
10943
|
// construction-only (D7) so it cannot legitimately change, but keying
|
|
10901
10944
|
// it here guarantees a cached result can never outlive the field
|
|
10902
10945
|
// configuration it was computed under.
|
|
@@ -10910,7 +10953,7 @@ function createGraphInstance(opts) {
|
|
|
10910
10953
|
searchFlight = null;
|
|
10911
10954
|
const err = new OrbitOperationError(
|
|
10912
10955
|
{ code: "aborted", cause: "superseded" },
|
|
10913
|
-
`search('${query}') superseded the older in-flight query
|
|
10956
|
+
`search('${query}') superseded the older in-flight query`
|
|
10914
10957
|
);
|
|
10915
10958
|
flight.handle.abort(err);
|
|
10916
10959
|
flight.reject(err);
|
|
@@ -10976,7 +11019,7 @@ function createGraphInstance(opts) {
|
|
|
10976
11019
|
searchFlight = null;
|
|
10977
11020
|
const err = new OrbitOperationError(
|
|
10978
11021
|
{ code: "aborted", cause: "cleared" },
|
|
10979
|
-
"search cleared while in flight
|
|
11022
|
+
"search cleared while in flight"
|
|
10980
11023
|
);
|
|
10981
11024
|
flight.handle.abort(err);
|
|
10982
11025
|
flight.reject(err);
|
|
@@ -11043,7 +11086,7 @@ function createGraphInstance(opts) {
|
|
|
11043
11086
|
if (denial !== null) {
|
|
11044
11087
|
throw new OrbitOperationError(
|
|
11045
11088
|
{ code: "aborted", cause: "stale" },
|
|
11046
|
-
`findPath('${sourceId}' \u2192 '${targetId}') discarded: ${denial}
|
|
11089
|
+
`findPath('${sourceId}' \u2192 '${targetId}') discarded: ${denial}`
|
|
11047
11090
|
);
|
|
11048
11091
|
}
|
|
11049
11092
|
if (token !== pathSeq || result === null) return result;
|
|
@@ -11135,7 +11178,7 @@ function createGraphInstance(opts) {
|
|
|
11135
11178
|
findings.push(`mode: unknown filter mode '${String(spec.mode)}'`);
|
|
11136
11179
|
}
|
|
11137
11180
|
if (findings.length > 0) {
|
|
11138
|
-
throw new TypeError(`applyHostUpdate: invalid filter
|
|
11181
|
+
throw new TypeError(`applyHostUpdate: invalid filter: ${findings.join("; ")}`);
|
|
11139
11182
|
}
|
|
11140
11183
|
}
|
|
11141
11184
|
const prev = store.getState();
|
|
@@ -11175,7 +11218,7 @@ function createGraphInstance(opts) {
|
|
|
11175
11218
|
severity: "error",
|
|
11176
11219
|
count: issues.length,
|
|
11177
11220
|
sampleIds: issues.slice(0, DIAGNOSTIC_SAMPLE_CAP).map((i) => i.where),
|
|
11178
|
-
message: `columnar snapshot rejected whole
|
|
11221
|
+
message: `columnar snapshot rejected whole: ${issues[0].where} \u2014 ${issues[0].detail}`
|
|
11179
11222
|
}
|
|
11180
11223
|
];
|
|
11181
11224
|
columnarRejected = true;
|
|
@@ -11196,14 +11239,14 @@ function createGraphInstance(opts) {
|
|
|
11196
11239
|
const isReplay = baseAccepted !== null && baseAccepted.datasetKey === data.datasetKey && baseAccepted.sourceRevision === data.sourceRevision;
|
|
11197
11240
|
if (!isReplay) {
|
|
11198
11241
|
const nextAccepted = preAccepted ?? validateSnapshot(data);
|
|
11199
|
-
abortAllSessions(null, "replaced: a declarative snapshot was applied
|
|
11242
|
+
abortAllSessions(null, "replaced: a declarative snapshot was applied");
|
|
11200
11243
|
overlayIdsCleared = clearOverlayState();
|
|
11201
11244
|
datasetKeyChanged = baseAccepted !== null && baseAccepted.datasetKey !== data.datasetKey;
|
|
11202
11245
|
if (datasetKeyChanged) {
|
|
11203
11246
|
reconciler = new Reconciler();
|
|
11204
11247
|
engineDiags = [];
|
|
11205
11248
|
abortExpansionsForDatasetSwap();
|
|
11206
|
-
abortSearchFlight("dataset-changed", "search aborted: the datasetKey changed
|
|
11249
|
+
abortSearchFlight("dataset-changed", "search aborted: the datasetKey changed");
|
|
11207
11250
|
resetMaskState();
|
|
11208
11251
|
historyKernel.clear();
|
|
11209
11252
|
stopTimelineTimer();
|
|
@@ -11318,7 +11361,7 @@ function createGraphInstance(opts) {
|
|
|
11318
11361
|
severity: "error",
|
|
11319
11362
|
count: 1,
|
|
11320
11363
|
sampleIds: [],
|
|
11321
|
-
message: "groups and groupBy are mutually exclusive
|
|
11364
|
+
message: "groups and groupBy are mutually exclusive: NEITHER applies until the host removes one"
|
|
11322
11365
|
}
|
|
11323
11366
|
];
|
|
11324
11367
|
groupsDiagsChanged = true;
|
|
@@ -11356,7 +11399,7 @@ function createGraphInstance(opts) {
|
|
|
11356
11399
|
severity: "warning",
|
|
11357
11400
|
count: 1,
|
|
11358
11401
|
sampleIds: [],
|
|
11359
|
-
message: "parallelEdgeGrouping is inoperative: the accepted edge list has no same-endpoint-pair parallels (ids synthesized from (type,source,target)-style dedupe already collapse them) \u2014 the toggle changed nothing
|
|
11402
|
+
message: "parallelEdgeGrouping is inoperative: the accepted edge list has no same-endpoint-pair parallels (ids synthesized from (type,source,target)-style dedupe already collapse them) \u2014 the toggle changed nothing"
|
|
11360
11403
|
}
|
|
11361
11404
|
];
|
|
11362
11405
|
parallelRejected = true;
|
|
@@ -11436,7 +11479,7 @@ function createGraphInstance(opts) {
|
|
|
11436
11479
|
severity: "warning",
|
|
11437
11480
|
count: update.metrics.length,
|
|
11438
11481
|
sampleIds: update.metrics.slice(0, DIAGNOSTIC_SAMPLE_CAP).map((c) => c.metric),
|
|
11439
|
-
message: "metric columns discarded: no accepted model to join against
|
|
11482
|
+
message: "metric columns discarded: no accepted model to join against"
|
|
11440
11483
|
}
|
|
11441
11484
|
];
|
|
11442
11485
|
} else {
|
|
@@ -11445,7 +11488,7 @@ function createGraphInstance(opts) {
|
|
|
11445
11488
|
count: accepted.nodes.length,
|
|
11446
11489
|
// I1: columns must be stamped with the revision current at ISSUE
|
|
11447
11490
|
// time — never the post-update revision (that made the gate
|
|
11448
|
-
// self-satisfying
|
|
11491
|
+
// self-satisfying).
|
|
11449
11492
|
modelRevision: issuedModelSeq
|
|
11450
11493
|
});
|
|
11451
11494
|
metricDiags = res.diagnostics;
|
|
@@ -11582,7 +11625,7 @@ function createGraphInstance(opts) {
|
|
|
11582
11625
|
severity: "warning",
|
|
11583
11626
|
count: 1,
|
|
11584
11627
|
sampleIds: [],
|
|
11585
|
-
message: "searchIndex is construction-only
|
|
11628
|
+
message: "searchIndex is construction-only: pass it to createGraphInstance options (or key-remount <Graph>) \u2014 this update's searchIndex was ignored"
|
|
11586
11629
|
}
|
|
11587
11630
|
];
|
|
11588
11631
|
searchIndexRejected = true;
|
|
@@ -12019,9 +12062,9 @@ function createGraphInstance(opts) {
|
|
|
12019
12062
|
applyEmphasis(s.engine, node === null ? null : index);
|
|
12020
12063
|
emit("nodeHover", { node });
|
|
12021
12064
|
},
|
|
12022
|
-
//
|
|
12065
|
+
// native-route edge events. Host onLink* events are
|
|
12023
12066
|
// honored regardless of the committed route — an adapter that surfaces
|
|
12024
|
-
// them despite declaring linkPicking:false is a harmless pass-through
|
|
12067
|
+
// them despite declaring linkPicking:false is a harmless pass-through
|
|
12025
12068
|
// but only the 'native' route RELIES on them; the fallback route feeds
|
|
12026
12069
|
// the same typed events through the pointer samplers.
|
|
12027
12070
|
onLinkClick(linkIndex) {
|
|
@@ -12058,7 +12101,7 @@ function createGraphInstance(opts) {
|
|
|
12058
12101
|
const prevented = emit("nodeDragEnd", { node, x, y });
|
|
12059
12102
|
if (!prevented) writePin(node.id, [x, y]);
|
|
12060
12103
|
},
|
|
12061
|
-
//
|
|
12104
|
+
// Right-click or long-press → typed 'contextMenu' event. The
|
|
12062
12105
|
// core adds NO built-in follow-up — components own the menu.
|
|
12063
12106
|
onContextMenu(index, screen) {
|
|
12064
12107
|
if (!active()) return;
|
|
@@ -12072,7 +12115,7 @@ function createGraphInstance(opts) {
|
|
|
12072
12115
|
emit("contextMenu", { target: { kind: "node", node }, screen: screenPt });
|
|
12073
12116
|
},
|
|
12074
12117
|
/**
|
|
12075
|
-
*
|
|
12118
|
+
* Overlay scheduler tick (adapter activity clock). Per tick:
|
|
12076
12119
|
* (1) sim-hot degradation — while the simulation runs, ONE getPositions
|
|
12077
12120
|
* readback refreshes the CPU cache at a capped >=500ms cadence (M0:
|
|
12078
12121
|
* tracked readback stalls ~18ms, so the lane NEVER reads back per
|
|
@@ -12299,8 +12342,8 @@ function createGraphInstance(opts) {
|
|
|
12299
12342
|
route,
|
|
12300
12343
|
screenToSpace: (p) => eng.screenToSpace?.(p) ?? null,
|
|
12301
12344
|
medianLinkWidthPx: () => medianLinkWidthPx(lastLinkWidths),
|
|
12302
|
-
//
|
|
12303
|
-
//
|
|
12345
|
+
// the facade's per-candidate visibility mask reads the LIVE
|
|
12346
|
+
// mask — no re-arm needed on mask changes.
|
|
12304
12347
|
linkVisible: (linkIndex) => maskEdgeVisibleAt(linkIndex)
|
|
12305
12348
|
});
|
|
12306
12349
|
const restartAlpha = layout === "force" ? 1 : null;
|
|
@@ -12405,7 +12448,7 @@ function createGraphInstance(opts) {
|
|
|
12405
12448
|
if (g.requestAnimationFrame === wrapper) g.requestAnimationFrame = native;
|
|
12406
12449
|
if (registrations > 0 && !destroyed) {
|
|
12407
12450
|
console.warn(
|
|
12408
|
-
`orbit: ${registrations} requestAnimationFrame registration(s) observed over 500ms while this instance is quiescent
|
|
12451
|
+
`orbit: ${registrations} requestAnimationFrame registration(s) observed over 500ms while this instance is quiescent. If no app-owned animation is running, a second frame loop is leaking.`
|
|
12409
12452
|
);
|
|
12410
12453
|
}
|
|
12411
12454
|
}, 500);
|
|
@@ -12480,7 +12523,7 @@ function createGraphInstance(opts) {
|
|
|
12480
12523
|
function warnViewStateDrop(channel, why) {
|
|
12481
12524
|
if (!DEV || viewStateDropWarned.has(channel)) return;
|
|
12482
12525
|
viewStateDropWarned.add(channel);
|
|
12483
|
-
console.warn(`orbit: getViewState omitted ${channel} \u2014 ${why}
|
|
12526
|
+
console.warn(`orbit: getViewState omitted ${channel} \u2014 ${why}`);
|
|
12484
12527
|
}
|
|
12485
12528
|
function serializableScaleOf(channel, value) {
|
|
12486
12529
|
if (value === void 0) return void 0;
|
|
@@ -12489,33 +12532,34 @@ function createGraphInstance(opts) {
|
|
|
12489
12532
|
return void 0;
|
|
12490
12533
|
}
|
|
12491
12534
|
if (!isScaleValue(value)) return void 0;
|
|
12492
|
-
|
|
12535
|
+
const scale = value;
|
|
12536
|
+
if (scale.kind === "sequential") {
|
|
12493
12537
|
return {
|
|
12494
12538
|
kind: "sequential",
|
|
12495
|
-
metric:
|
|
12496
|
-
range:
|
|
12539
|
+
metric: scale.metric,
|
|
12540
|
+
range: scale.range,
|
|
12497
12541
|
// The numeric-array domain form is data; a DomainPolicy is app
|
|
12498
12542
|
// behavior config and stays with the app.
|
|
12499
|
-
...Array.isArray(
|
|
12543
|
+
...Array.isArray(scale.domain) ? { domain: scale.domain } : {}
|
|
12500
12544
|
};
|
|
12501
12545
|
}
|
|
12502
|
-
if (
|
|
12546
|
+
if (scale.kind === "diverging") {
|
|
12503
12547
|
return {
|
|
12504
12548
|
kind: "diverging",
|
|
12505
|
-
metric:
|
|
12506
|
-
range:
|
|
12507
|
-
mid:
|
|
12549
|
+
metric: scale.metric,
|
|
12550
|
+
range: scale.range,
|
|
12551
|
+
mid: scale.mid
|
|
12508
12552
|
};
|
|
12509
12553
|
}
|
|
12510
|
-
if (typeof
|
|
12554
|
+
if (typeof scale.by !== "string") {
|
|
12511
12555
|
warnViewStateDrop(channel, "a function-`by` categorical scale does not serialize");
|
|
12512
12556
|
return void 0;
|
|
12513
12557
|
}
|
|
12514
12558
|
return {
|
|
12515
12559
|
kind: "categorical",
|
|
12516
|
-
by:
|
|
12517
|
-
...
|
|
12518
|
-
...
|
|
12560
|
+
by: scale.by,
|
|
12561
|
+
...scale.palette !== void 0 ? { palette: scale.palette } : {},
|
|
12562
|
+
...scale.domain !== void 0 ? { domain: scale.domain } : {}
|
|
12519
12563
|
};
|
|
12520
12564
|
}
|
|
12521
12565
|
function serializableThemeOf() {
|
|
@@ -12597,7 +12641,7 @@ function createGraphInstance(opts) {
|
|
|
12597
12641
|
if (visible.length > limit) {
|
|
12598
12642
|
throw new OrbitOperationError(
|
|
12599
12643
|
{ code: "export-materialization-too-large", rowCount: visible.length, limit },
|
|
12600
|
-
`includePositions over ${limit} nodes \u2014 persist the layout through the export lane and reference it from dataRef
|
|
12644
|
+
`includePositions over ${limit} nodes \u2014 persist the layout through the export lane and reference it from dataRef`
|
|
12601
12645
|
);
|
|
12602
12646
|
}
|
|
12603
12647
|
const pos = eng.getPositions();
|
|
@@ -12697,7 +12741,7 @@ function createGraphInstance(opts) {
|
|
|
12697
12741
|
severity: "error",
|
|
12698
12742
|
count: verdict.problems.length,
|
|
12699
12743
|
sampleIds: [],
|
|
12700
|
-
message: `setViewState rejected (${verdict.code}): ${verdict.problems.slice(0, 3).join("; ")} \u2014 nothing was applied
|
|
12744
|
+
message: `setViewState rejected (${verdict.code}): ${verdict.problems.slice(0, 3).join("; ")} \u2014 nothing was applied`
|
|
12701
12745
|
}
|
|
12702
12746
|
];
|
|
12703
12747
|
pendingDiagnosticsRefresh = true;
|
|
@@ -12835,7 +12879,7 @@ function createGraphInstance(opts) {
|
|
|
12835
12879
|
...prevState.revisions,
|
|
12836
12880
|
render: prevState.revisions.render + 1
|
|
12837
12881
|
};
|
|
12838
|
-
eng
|
|
12882
|
+
commitToEngine(eng, {
|
|
12839
12883
|
revision: nextRevisions.render,
|
|
12840
12884
|
structure: {
|
|
12841
12885
|
pointCount: scene.count,
|
|
@@ -12843,6 +12887,7 @@ function createGraphInstance(opts) {
|
|
|
12843
12887
|
links: scene.links
|
|
12844
12888
|
}
|
|
12845
12889
|
});
|
|
12890
|
+
nextRevisions.appliedRender = eng.appliedRevision();
|
|
12846
12891
|
eng.pause();
|
|
12847
12892
|
const patch = { revisions: nextRevisions };
|
|
12848
12893
|
if (prevState.simulationRunning) patch.simulationRunning = false;
|
|
@@ -12920,7 +12965,7 @@ function createGraphInstance(opts) {
|
|
|
12920
12965
|
if (eng2 === null || eng2.captureScreenshot === void 0) {
|
|
12921
12966
|
throw new OrbitOperationError(
|
|
12922
12967
|
{ code: "aborted", cause: "no screenshot capability" },
|
|
12923
|
-
'exportImage("png") requires an engine with screenshot capture
|
|
12968
|
+
'exportImage("png") requires an engine with screenshot capture'
|
|
12924
12969
|
);
|
|
12925
12970
|
}
|
|
12926
12971
|
const blob = await eng2.captureScreenshot();
|
|
@@ -12961,7 +13006,7 @@ function createGraphInstance(opts) {
|
|
|
12961
13006
|
if (elementCount > limit && opts2?.fallback !== "raster-hybrid") {
|
|
12962
13007
|
throw new OrbitOperationError(
|
|
12963
13008
|
{ code: "export-too-large", elementCount, limit },
|
|
12964
|
-
`SVG export of ${elementCount} elements exceeds ${limit} \u2014 filter/isolate first, or pass { fallback: 'raster-hybrid' }
|
|
13009
|
+
`SVG export of ${elementCount} elements exceeds ${limit} \u2014 filter/isolate first, or pass { fallback: 'raster-hybrid' }`
|
|
12965
13010
|
);
|
|
12966
13011
|
}
|
|
12967
13012
|
let minX = Infinity;
|
|
@@ -13092,7 +13137,7 @@ function createGraphInstance(opts) {
|
|
|
13092
13137
|
if (rowCount > limit) {
|
|
13093
13138
|
throw new OrbitOperationError(
|
|
13094
13139
|
{ code: "export-materialization-too-large", rowCount, limit },
|
|
13095
|
-
`exportData would materialize ${rowCount} rows (limit ${limit}) \u2014 use exportDataStream
|
|
13140
|
+
`exportData would materialize ${rowCount} rows (limit ${limit}) \u2014 use exportDataStream`
|
|
13096
13141
|
);
|
|
13097
13142
|
}
|
|
13098
13143
|
if (scope === "accepted") return { nodes: pin.accepted.nodes, edges: pin.accepted.edges };
|
|
@@ -13135,7 +13180,7 @@ function createGraphInstance(opts) {
|
|
|
13135
13180
|
if (bound > limit) {
|
|
13136
13181
|
throw new OrbitOperationError(
|
|
13137
13182
|
{ code: "export-materialization-too-large", rowCount: bound, limit },
|
|
13138
|
-
`exportLayout would materialize ${bound} rows (limit ${limit}) \u2014 use exportLayoutStream
|
|
13183
|
+
`exportLayout would materialize ${bound} rows (limit ${limit}) \u2014 use exportLayoutStream`
|
|
13139
13184
|
);
|
|
13140
13185
|
}
|
|
13141
13186
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -13214,9 +13259,8 @@ function createGraphInstance(opts) {
|
|
|
13214
13259
|
visibleNodeCount: st.visible.nodes,
|
|
13215
13260
|
visibleEdgeCount: st.visible.edges,
|
|
13216
13261
|
estimatedCpuBytes: estimateCpuBytes(),
|
|
13217
|
-
// The acceptance queue is synchronous
|
|
13218
|
-
// reader inside a job observes 1
|
|
13219
|
-
// the worker lane (PR-E).
|
|
13262
|
+
// The acceptance queue is synchronous: depth is 0 outside a job, and a
|
|
13263
|
+
// reader inside a job observes 1.
|
|
13220
13264
|
queueDepth: acceptanceQueue.active ? 1 : 0,
|
|
13221
13265
|
modelRevision: st.revisions.model,
|
|
13222
13266
|
scopeRevision: st.revisions.scope,
|
|
@@ -13440,7 +13484,7 @@ var OverviewController = class {
|
|
|
13440
13484
|
getScene;
|
|
13441
13485
|
getVisible;
|
|
13442
13486
|
size;
|
|
13443
|
-
/** Transform of the LAST rasterization; null until rasterize
|
|
13487
|
+
/** Transform of the LAST rasterization; null until rasterize succeeds. */
|
|
13444
13488
|
frame = null;
|
|
13445
13489
|
// --- throttle state (latched by shouldRefresh when it answers true) ---
|
|
13446
13490
|
lastRefreshMs = null;
|
|
@@ -13455,9 +13499,9 @@ var OverviewController = class {
|
|
|
13455
13499
|
* when it returns true, so each `true` accounts for exactly one refresh:
|
|
13456
13500
|
*
|
|
13457
13501
|
* - hot (`simulationRunning`): time-gated only (≤ 2 Hz) — the epoch is
|
|
13458
|
-
*
|
|
13502
|
+
* ignored because positions change continuously without epoch advances;
|
|
13459
13503
|
* - idle: refresh only when the epoch ADVANCED since the last refresh,
|
|
13460
|
-
*
|
|
13504
|
+
* time-gated at ≤ 1 Hz;
|
|
13461
13505
|
* - idle + unchanged epoch: always false — zero work, forever.
|
|
13462
13506
|
*/
|
|
13463
13507
|
shouldRefresh(nowMs2, simulationRunning, epoch) {
|
|
@@ -13475,7 +13519,7 @@ var OverviewController = class {
|
|
|
13475
13519
|
/**
|
|
13476
13520
|
* Rasterizes the current scene into a fresh `size²` RGBA dot field: 1 px
|
|
13477
13521
|
* white dots whose alpha ACCUMULATES on overlap (heatmap-ish density),
|
|
13478
|
-
* dimmed for mask-hidden points; NaN pairs
|
|
13522
|
+
* dimmed for mask-hidden points; NaN pairs are skipped.
|
|
13479
13523
|
* World bounds map into the thumbnail with a 5 % edge padding at a UNIFORM
|
|
13480
13524
|
* scale (aspect preserved, centered on the short axis) and a downward pixel
|
|
13481
13525
|
* y (see module header). Returns null when there is no scene or no point.
|