@modernrelay/orbit-core 0.13.6 → 0.14.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-KRCG2JFN.js} +3 -3
- package/dist/chunk-KRCG2JFN.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 +611 -611
- package/dist/index.js +145 -146
- 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.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-FG6TQANI.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { G as GraphSnapshot, A as AcceptedGraph, R as ResolvedGroup, a as GraphDiagnostic, b as GraphNode, c as AcceptedEdge, N as NodeId, M as MetaEdge, S as SceneFold, d as GroupSpec, e as GroupBySpec, f as SceneGroups, g as RenderScene, h as SceneLinkRef, i as ScenePointRef, j as Accessor, k as RevisionAwareService, l as RequestContext, m as SearchResult, V as ViewportState, n as SelectionState, o as SubgraphSpec, J as JsonValue, E as EngineFactory, p as ExpansionService, P as PathService, q as ScaleLimits, r as GraphTheme, s as GraphStoreState, L as LabelPlacement, t as GraphHostUpdate, B as BeginIngestOptions, I as IngestSession, u as GraphEventName, v as GraphEventMap, w as GraphListenerControl, x as EdgeId, y as GraphPerfSnapshot, z as ResolvedCluster, C as PathOptions, D as PathResult, F as SearchActivation, H as CrossfilterSession, T as TimelinePlayback, K as Scale, O as MetricName, Q as Revisions, U as AccessibilityConfig, W as ThemeInput, X as LabelConfig, Y as IngestBatch, Z as RevisionDimension, _ as FilterSpec, $ as FilterExpr, a0 as DimensionSpec, a1 as BrushState, a2 as DimensionSummary, a3 as DomainPolicy, a4 as EngineBufferChannel, a5 as GraphEngine, a6 as EngineCommit, a7 as EngineCapabilities, a8 as MetricColumn, a9 as ColumnarGraphSnapshot, aa as GraphSnapshotInput } from './index-
|
|
2
|
-
export { ab as AppendReceipt, ac as CategoryBin, ad as ClusterSpec, ae as Column, af as ColumnChange, ag as DIAGNOSTIC_SAMPLE_CAP, ah as DegradeEvent, ai as DegradeStep, aj as DiagnosticCode, ak as DiagnosticSeverity, al as DimensionKind, am as ErrorPhase, an as ExpansionBatch, ao as ExpansionResponse, ap as FilterMode, aq as FilterValue, ar as GraphEdge, as as GraphError, at as GraphOperationError, au as HistogramBin, av as IngestCommitReceipt, aw as IngestSessionState, ax as InstanceStatus, ay as LayoutKind, az as NodeEventPayload, aA as OrbitOperationError, aB as ResourceAdmissionReport, aC as ResourceDegradeStep, aD as SearchUnavailableReason, aE as SimulationConfig, aF as StringColumn, aG as graphErrorToError, aH as isFatalGraphError, aI as resourceLimitFatal } from './index-
|
|
3
|
-
import { W as WorkerFactoryOption } from './lane-
|
|
4
|
-
export { C as ClusterDerivation, D as DEFAULT_CLUSTER_CENTER_RADIUS, a as DEFAULT_LAYOUT_SEED, E as EncodedStringTable, R as RequestClass, b as WorkerEntity, c as WorkerEnvelope, d as WorkerTransport, e as clusterCentroids, f as collectTransfers, g as decodeStringTable, h as deriveClusters, i as encodeStringTable, j as generateClusterCenters, k as judgeEpoch, r as resolveClusterCenters } from './lane-
|
|
1
|
+
import { G as GraphSnapshot, A as AcceptedGraph, R as ResolvedGroup, a as GraphDiagnostic, b as GraphNode, c as AcceptedEdge, N as NodeId, M as MetaEdge, S as SceneFold, d as GroupSpec, e as GroupBySpec, f as SceneGroups, g as RenderScene, h as SceneLinkRef, i as ScenePointRef, j as Accessor, k as RevisionAwareService, l as RequestContext, m as SearchResult, V as ViewportState, n as SelectionState, o as SubgraphSpec, J as JsonValue, E as EngineFactory, p as ExpansionService, P as PathService, q as ScaleLimits, r as GraphTheme, s as GraphStoreState, L as LabelPlacement, t as GraphHostUpdate, B as BeginIngestOptions, I as IngestSession, u as GraphEventName, v as GraphEventMap, w as GraphListenerControl, x as EdgeId, y as GraphPerfSnapshot, z as ResolvedCluster, C as PathOptions, D as PathResult, F as SearchActivation, H as CrossfilterSession, T as TimelinePlayback, K as Scale, O as MetricName, Q as Revisions, U as AccessibilityConfig, W as ThemeInput, X as LabelConfig, Y as IngestBatch, Z as RevisionDimension, _ as FilterSpec, $ as FilterExpr, a0 as DimensionSpec, a1 as BrushState, a2 as DimensionSummary, a3 as DomainPolicy, a4 as EngineBufferChannel, a5 as GraphEngine, a6 as EngineCommit, a7 as EngineCapabilities, a8 as MetricColumn, a9 as ColumnarGraphSnapshot, aa as GraphSnapshotInput } from './index-CPRTWpGA.js';
|
|
2
|
+
export { ab as AppendReceipt, ac as CategoryBin, ad as ClusterSpec, ae as Column, af as ColumnChange, ag as DIAGNOSTIC_SAMPLE_CAP, ah as DegradeEvent, ai as DegradeStep, aj as DiagnosticCode, ak as DiagnosticSeverity, al as DimensionKind, am as ErrorPhase, an as ExpansionBatch, ao as ExpansionResponse, ap as FilterMode, aq as FilterValue, ar as GraphEdge, as as GraphError, at as GraphOperationError, au as HistogramBin, av as IngestCommitReceipt, aw as IngestSessionState, ax as InstanceStatus, ay as LayoutKind, az as NodeEventPayload, aA as OrbitOperationError, aB as ResourceAdmissionReport, aC as ResourceDegradeStep, aD as SearchUnavailableReason, aE as SimulationConfig, aF as StringColumn, aG as graphErrorToError, aH as isFatalGraphError, aI as resourceLimitFatal } from './index-CPRTWpGA.js';
|
|
3
|
+
import { W as WorkerFactoryOption } from './lane-_KPQ1deI.js';
|
|
4
|
+
export { C as ClusterDerivation, D as DEFAULT_CLUSTER_CENTER_RADIUS, a as DEFAULT_LAYOUT_SEED, E as EncodedStringTable, R as RequestClass, b as WorkerEntity, c as WorkerEnvelope, d as WorkerTransport, e as clusterCentroids, f as collectTransfers, g as decodeStringTable, h as deriveClusters, i as encodeStringTable, j as generateClusterCenters, k as judgeEpoch, r as resolveClusterCenters } from './lane-_KPQ1deI.js';
|
|
5
5
|
import { StoreApi } from 'zustand/vanilla';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* snapshot validation — deterministic malformed-input resolution.
|
|
9
9
|
*
|
|
10
10
|
* Pure function: same input → structurally identical output; never throws on
|
|
11
11
|
* malformed input. Diagnostics are batched — at most one GraphDiagnostic per
|
|
@@ -16,7 +16,7 @@ import { StoreApi } from 'zustand/vanilla';
|
|
|
16
16
|
declare function validateSnapshot<N = Record<string, unknown>, E = Record<string, unknown>>(snapshot: GraphSnapshot<N, E>): AcceptedGraph<N, E>;
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* stage-3 containment rewrite — pure derivation, no engine,
|
|
20
20
|
* no DOM.
|
|
21
21
|
*
|
|
22
22
|
* ## The representative forest
|
|
@@ -37,35 +37,35 @@ declare function validateSnapshot<N = Record<string, unknown>, E = Record<string
|
|
|
37
37
|
* why nesting needs no special case (a fold inside a collapsed group hides
|
|
38
38
|
* with it, and its members' edges route to the group).
|
|
39
39
|
*
|
|
40
|
-
* Contract summary
|
|
40
|
+
* Contract summary:
|
|
41
41
|
* - Groups are HIERARCHICAL but singly-parented: a member id may name another
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* - `rewriteGroups` runs over the HARD-SCOPED model
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* - Group ids occupy a distinct PUBLIC namespace
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
42
|
+
* group (nesting), but no entity has two parents. `validateGroupSpecs`
|
|
43
|
+
* rejects a violating array with ONE batched 'config-error' diagnostic
|
|
44
|
+
* BEFORE any scene rewrite — a rejected array changes nothing.
|
|
45
|
+
* - `rewriteGroups` runs over the HARD-SCOPED model:
|
|
46
|
+
* collapsed representatives replace their in-scope descendants with one row
|
|
47
|
+
* (a synthetic super-node, or the fold anchor's existing physical row);
|
|
48
|
+
* descendant edges re-route into meta-edge rows carrying the underlying-edge
|
|
49
|
+
* count (the badge datum). The post-rewrite graph feeds the EXISTING
|
|
50
|
+
* structural diff, so collapse/expand is a diff, never a reload.
|
|
51
|
+
* - Group ids occupy a distinct PUBLIC namespace: synthetic rows carry
|
|
52
|
+
* INTERNAL scene keys (NUL-prefixed — outside the documented caller id
|
|
53
|
+
* contract) that never escape public payloads. A group id equal to a node
|
|
54
|
+
* id coexists without collision. A FOLD anchor is a real node, so its
|
|
55
|
+
* entity key IS its node id and it needs no synthetic slot at all.
|
|
56
56
|
* - Synthetic rows are never cast to N/E: they carry only `id`, and consumers
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
57
|
+
* identify them by slot position — synthetics are always a CONTIGUOUS
|
|
58
|
+
* SUFFIX of the node/edge lists (physical prefix ordering is preserved), so
|
|
59
|
+
* `slot >= physicalCount` is the discrimination rule.
|
|
60
60
|
*/
|
|
61
61
|
|
|
62
62
|
/** Internal scene key of a collapsed group's super-node row. NEVER public. */
|
|
63
63
|
declare function groupSceneKey(groupId: string): string;
|
|
64
64
|
/** Internal scene key of a meta-edge row (directed rewritten endpoint pair). */
|
|
65
65
|
declare function metaEdgeSceneKey(sourceKey: string, targetKey: string): string;
|
|
66
|
-
/**
|
|
66
|
+
/** PUBLIC meta-edge id: the collision-safe ordered endpoint tuple
|
|
67
67
|
* `JSON.stringify(['meta-edge', source.kind, source.id, target.kind,
|
|
68
|
-
* target.id])` — shared with the parallel-edge grouping toggle
|
|
68
|
+
* target.id])` — shared with the parallel-edge grouping toggle. */
|
|
69
69
|
declare function metaEdgePublicId(sourceKind: 'node' | 'group', sourceId: string, targetKind: 'node' | 'group', targetId: string): string;
|
|
70
70
|
/**
|
|
71
71
|
* Entity key: a physical node id, or a synthetic group scene key. A fold
|
|
@@ -102,16 +102,16 @@ interface GroupValidationResult {
|
|
|
102
102
|
/**
|
|
103
103
|
* Validates a manual `groups` array against the FULL accepted model (members
|
|
104
104
|
* may live outside the current hard scope). Violations, all collected into
|
|
105
|
-
* one diagnostic
|
|
105
|
+
* one diagnostic:
|
|
106
106
|
* - duplicate group id in the array;
|
|
107
107
|
* - self-membership (a group naming its own id — rejected even when a node
|
|
108
|
-
*
|
|
108
|
+
* with that id exists: the ambiguity itself is the error);
|
|
109
109
|
* - duplicate membership (the same node twice in one group);
|
|
110
110
|
* - overlapping membership (the same node in two groups);
|
|
111
111
|
* - CYCLIC nesting (groups that contain each other, directly or through a
|
|
112
|
-
*
|
|
112
|
+
* chain — the containment forest must stay acyclic);
|
|
113
113
|
* - unknown members (ids absent from the accepted model AND not naming
|
|
114
|
-
*
|
|
114
|
+
* another group; with no accepted model every non-group member is unknown).
|
|
115
115
|
*
|
|
116
116
|
* NESTING IS LEGAL: a member id naming another group id nests that group.
|
|
117
117
|
* Containment is a forest, not a partition — but it stays SINGLY PARENTED,
|
|
@@ -126,14 +126,14 @@ declare function validateGroupSpecs(specs: readonly GroupSpec[], nodeIndex: Read
|
|
|
126
126
|
*/
|
|
127
127
|
declare function resolveManualGroups(specs: readonly GroupSpec[], nodeIndex: ReadonlyMap<NodeId, number>): ResolvedGroup[];
|
|
128
128
|
/** Canonical structural equality for the groups HOST LANE (GroupSpec is a
|
|
129
|
-
* plain descriptor like Scale — equal literals never re-rewrite
|
|
129
|
+
* plain descriptor like Scale — equal literals never re-rewrite). */
|
|
130
130
|
declare function sameGroupSpecArrays(a: readonly GroupSpec[] | null, b: readonly GroupSpec[] | null): boolean;
|
|
131
131
|
/**
|
|
132
|
-
*
|
|
132
|
+
* collision-safe derived-group id codec: `JSON.stringify(['group',
|
|
133
133
|
* key])`. Injective over keys (JSON string encoding), never equal to the raw
|
|
134
134
|
* key itself (every output starts with `["group",`), and round-trippable via
|
|
135
135
|
* JSON.parse. Labels stay separate — the derived group's `label` is the raw
|
|
136
|
-
* key. Group ids remain a distinct public namespace
|
|
136
|
+
* key. Group ids remain a distinct public namespace, so a NODE id that
|
|
137
137
|
* happens to equal a derived id still coexists without collision.
|
|
138
138
|
*/
|
|
139
139
|
declare function groupByDerivedId(key: string): string;
|
|
@@ -142,8 +142,8 @@ declare function groupByDerivedId(key: string): string;
|
|
|
142
142
|
declare function sameGroupBySpec<N>(a: GroupBySpec<N> | null, b: GroupBySpec<N> | null): boolean;
|
|
143
143
|
/**
|
|
144
144
|
* D4 boundary validation for a groupBy spec: `by` must be a function and
|
|
145
|
-
* `semanticZoom.expandAbove` must be STRICTLY greater than `collapseBelow
|
|
146
|
-
*
|
|
145
|
+
* `semanticZoom.expandAbove` must be STRICTLY greater than `collapseBelow`.
|
|
146
|
+
* Returns ONE batched
|
|
147
147
|
* 'config-error' diagnostic (the rejected spec never lands — the previous
|
|
148
148
|
* groupBy configuration stays active) or null when valid.
|
|
149
149
|
*/
|
|
@@ -152,21 +152,21 @@ interface GroupByDerivation {
|
|
|
152
152
|
/** One derived group per distinct key, first-encounter order over the
|
|
153
153
|
* model's node list. `collapsed` comes from the caller's residue lookup. */
|
|
154
154
|
groups: readonly ResolvedGroup[];
|
|
155
|
-
/** Derived PUBLIC id → derived key (the setGroupCollapsed reverse map
|
|
156
|
-
* the residue is keyed by KEY, never by id
|
|
155
|
+
/** Derived PUBLIC id → derived key (the setGroupCollapsed reverse map;
|
|
156
|
+
* the residue is keyed by KEY, never by id). */
|
|
157
157
|
keyById: ReadonlyMap<string, string>;
|
|
158
158
|
/** ONE aggregated 'accessor-error' warning when `by` threw (the affected
|
|
159
159
|
* nodes derive as ungrouped — never silent loss, I3), else null. */
|
|
160
160
|
diagnostic: GraphDiagnostic | null;
|
|
161
161
|
}
|
|
162
162
|
/**
|
|
163
|
-
*
|
|
164
|
-
* string key returned by `by` (first-encounter order, matching
|
|
163
|
+
* groupBy derivation: one GroupSpec-shaped ResolvedGroup per distinct
|
|
164
|
+
* string key returned by `by` (first-encounter order, matching
|
|
165
165
|
* categorical-domain conventions); `null` — and any non-string value — means
|
|
166
166
|
* ungrouped. Derived ids use {@link groupByDerivedId}; `label` is the raw
|
|
167
167
|
* key; `derived: true` marks membership read-only. `collapsed` defaults to
|
|
168
168
|
* whatever `isCollapsedKey` reports — with an empty residue everything is
|
|
169
|
-
* expanded, so adding groupBy alone changes no rendering
|
|
169
|
+
* expanded, so adding groupBy alone changes no rendering.
|
|
170
170
|
*/
|
|
171
171
|
declare function deriveGroupsByKey<N>(nodes: readonly GraphNode<N>[], by: (node: GraphNode<N>) => string | null, isCollapsedKey: (key: string) => boolean): GroupByDerivation;
|
|
172
172
|
interface SuperNodeRecord {
|
|
@@ -182,15 +182,15 @@ interface SuperNodeRecord {
|
|
|
182
182
|
interface MetaEdgeRecord {
|
|
183
183
|
/** Internal scene key of the meta-edge row (never public). */
|
|
184
184
|
sceneKey: string;
|
|
185
|
-
/** Public record
|
|
185
|
+
/** Public record: endpoints are PUBLIC ids — a
|
|
186
186
|
* group id or a node id — and `count` is the badge datum. */
|
|
187
187
|
metaEdge: MetaEdge;
|
|
188
|
-
/** Indices of the rerouted edges in the PRE-rewrite model's edge list
|
|
188
|
+
/** Indices of the rerouted edges in the PRE-rewrite model's edge list
|
|
189
189
|
* the stage-5 "any underlying edge passes" rule evaluates these. */
|
|
190
190
|
underlying: readonly number[];
|
|
191
191
|
}
|
|
192
192
|
interface GroupRewrite<N = Record<string, unknown>, E = Record<string, unknown>> {
|
|
193
|
-
/** The post-rewrite scene model — what feeds the
|
|
193
|
+
/** The post-rewrite scene model — what feeds the structural diff. */
|
|
194
194
|
graph: AcceptedGraph<N, E>;
|
|
195
195
|
/** Physical rows (same objects as the input model's, members removed). */
|
|
196
196
|
physicalNodes: readonly GraphNode<N>[];
|
|
@@ -212,7 +212,7 @@ interface GroupRewrite<N = Record<string, unknown>, E = Record<string, unknown>>
|
|
|
212
212
|
}
|
|
213
213
|
/**
|
|
214
214
|
* Rewrites the collapsed representatives of `forest` over the hard-scoped
|
|
215
|
-
* model
|
|
215
|
+
* model. Returns null when nothing collapsed intersects the
|
|
216
216
|
* model — an uncollapsed group exists only in store.groups and never
|
|
217
217
|
* rewrites the scene.
|
|
218
218
|
*
|
|
@@ -224,7 +224,7 @@ interface GroupRewrite<N = Record<string, unknown>, E = Record<string, unknown>>
|
|
|
224
224
|
* cached by construction: they re-derive from the unchanged accepted model on
|
|
225
225
|
* expand. Synthetic rows carry no positions; the reconciler's departed cache
|
|
226
226
|
* restores a re-collapsed super-node (stable scene key) and returning members
|
|
227
|
-
* exactly like any other leave-and-return
|
|
227
|
+
* exactly like any other leave-and-return.
|
|
228
228
|
*/
|
|
229
229
|
declare function rewriteGroups<N, E>(model: AcceptedGraph<N, E>, forest: RepForest): GroupRewrite<N, E> | null;
|
|
230
230
|
/**
|
|
@@ -233,12 +233,12 @@ declare function rewriteGroups<N, E>(model: AcceptedGraph<N, E>, forest: RepFore
|
|
|
233
233
|
* groups-empty rewrite when none is active). Returns `base` unchanged when
|
|
234
234
|
* no pair has multiplicity > 1 — the toggle is then a scene no-op.
|
|
235
235
|
*
|
|
236
|
-
* Meta-edge identity reuses the
|
|
237
|
-
* `metaEdgePublicId('node', source, 'node', target)
|
|
236
|
+
* Meta-edge identity reuses the group codecs: public id
|
|
237
|
+
* `metaEdgePublicId('node', source, 'node', target)`,
|
|
238
238
|
* internal scene key `metaEdgeSceneKey(source, target)`. `count` (the badge
|
|
239
|
-
* datum) is the collapsed multiplicity and drives the
|
|
239
|
+
* datum) is the collapsed multiplicity and drives the aggregate width
|
|
240
240
|
* channel; `underlying` indexes the PRE-rewrite model's edge list so the
|
|
241
|
-
*
|
|
241
|
+
* "any underlying edge passes" mask rule applies unchanged.
|
|
242
242
|
*/
|
|
243
243
|
declare function collapseParallelEdges<N, E>(model: AcceptedGraph<N, E>, base: GroupRewrite<N, E> | null): GroupRewrite<N, E> | null;
|
|
244
244
|
/** The RenderScene.groups descriptor for a rewrite. */
|
|
@@ -250,9 +250,9 @@ declare function scenePointRefAt(scene: RenderScene, index: number): ScenePointR
|
|
|
250
250
|
declare function sceneLinkRefAt(scene: RenderScene, linkIndex: number): SceneLinkRef | null;
|
|
251
251
|
|
|
252
252
|
/**
|
|
253
|
-
*
|
|
253
|
+
* reconciler — id↔index model, structural diff, position cache.
|
|
254
254
|
*
|
|
255
|
-
* v0.1 implements the 'rebuild' index policy only
|
|
255
|
+
* v0.1 implements the 'rebuild' index policy only: every reconcile
|
|
256
256
|
* rebuilds the flat buffers from scratch in accepted-base order, preserving
|
|
257
257
|
* positions by id via the live/departed caches. Pure data-structure work: no
|
|
258
258
|
* DOM, no engine import.
|
|
@@ -283,27 +283,27 @@ declare class Reconciler {
|
|
|
283
283
|
private prevLinks;
|
|
284
284
|
/**
|
|
285
285
|
* Declared node.x/y as of the previous reconcile, for nodes that declared
|
|
286
|
-
* them. Lets Priority 1 distinguish a CHANGED declaration (caller intent
|
|
286
|
+
* them. Lets Priority 1 distinguish a CHANGED declaration (caller intent
|
|
287
287
|
* wins over any cache) from an unchanged one (defers to live drift). Fresh
|
|
288
288
|
* per pass, so departed ids drop and a re-add treats its declaration as new.
|
|
289
289
|
*/
|
|
290
290
|
private prevDeclared;
|
|
291
291
|
/**
|
|
292
|
-
* CPU position mirror for the CURRENT scene, indexed by slot
|
|
292
|
+
* CPU position mirror for the CURRENT scene, indexed by slot.
|
|
293
293
|
* Owned copy — never aliases a published scene's positions array, so
|
|
294
294
|
* noteEnginePositions never mutates an already-published RenderScene.
|
|
295
295
|
*/
|
|
296
296
|
private livePositions;
|
|
297
297
|
/**
|
|
298
|
-
* Ids removed from the scene → last known finite position
|
|
299
|
-
* leave-and-return guarantee
|
|
298
|
+
* Ids removed from the scene → last known finite position, preserving the
|
|
299
|
+
* leave-and-return guarantee. Map insertion order doubles as LRU order;
|
|
300
300
|
* invariant: never overlaps the current scene's id set.
|
|
301
301
|
*/
|
|
302
302
|
private readonly departed;
|
|
303
303
|
reconcile<N, E>(accepted: AcceptedGraph<N, E>): ReconcileResult;
|
|
304
304
|
/**
|
|
305
305
|
* Copy engine-read positions into the live cache for the CURRENT scene's
|
|
306
|
-
* slots
|
|
306
|
+
* slots.
|
|
307
307
|
* Extra trailing floats are ignored; a short buffer updates a prefix.
|
|
308
308
|
*/
|
|
309
309
|
noteEnginePositions(positions: Float32Array): void;
|
|
@@ -311,12 +311,12 @@ declare class Reconciler {
|
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
/**
|
|
314
|
-
*
|
|
314
|
+
* projection (v0.1 subset): styling accessors → engine-ready typed buffers.
|
|
315
315
|
*
|
|
316
316
|
* Pure and DOM-free. Color parsing covers the CSS subset orbit documents
|
|
317
|
-
* (hex, rgb
|
|
317
|
+
* (hex, rgb/rgba, hsl/hsla, small named map); anything else is a
|
|
318
318
|
* caller error surfaced as a batched 'accessor-error' diagnostic, never a
|
|
319
|
-
* throw and never a NaN in a GPU buffer
|
|
319
|
+
* throw and never a NaN in a GPU buffer.
|
|
320
320
|
*/
|
|
321
321
|
|
|
322
322
|
type RGBA = [number, number, number, number];
|
|
@@ -337,11 +337,11 @@ declare function projectSizes<T>(items: readonly T[], accessor: Accessor<T, numb
|
|
|
337
337
|
};
|
|
338
338
|
|
|
339
339
|
/**
|
|
340
|
-
*
|
|
341
|
-
* service
|
|
340
|
+
* search — the SearchService contract plus the built-in LOCAL indexed
|
|
341
|
+
* service.
|
|
342
342
|
*
|
|
343
343
|
* The default service is client-side and field-scoped: it indexes the node id
|
|
344
|
-
* ALWAYS plus `attrs[field]` (String
|
|
344
|
+
* ALWAYS plus `attrs[field]` (String-coerced) for each field the host
|
|
345
345
|
* declared via `searchIndex` — it never guesses privileged attr names, so a
|
|
346
346
|
* missing declaration leaves the service id-only. Matching is a
|
|
347
347
|
* case-insensitive SUBSTRING scan over ONE precomputed lowercase haystack per
|
|
@@ -359,25 +359,25 @@ declare function projectSizes<T>(items: readonly T[], accessor: Accessor<T, numb
|
|
|
359
359
|
*
|
|
360
360
|
* `ctx.signal` is honored between scan chunks (an awaited microtask every
|
|
361
361
|
* {@link SEARCH_SCAN_CHUNK} nodes) — abort is an optimization; the instance's
|
|
362
|
-
* admission gate is the correctness gate
|
|
362
|
+
* admission gate is the correctness gate. Nothing here touches the
|
|
363
363
|
* engine, the store, or the DOM.
|
|
364
364
|
*/
|
|
365
365
|
|
|
366
|
-
/**
|
|
367
|
-
*
|
|
366
|
+
/** Search resolver: custom services plug in server-side search; the instance
|
|
367
|
+
* owns RequestContext creation, revision-keyed
|
|
368
368
|
* caching, supersede cancellation, and stale-result rejection at admission. */
|
|
369
369
|
interface SearchService<N = Record<string, unknown>> extends RevisionAwareService {
|
|
370
370
|
search(q: string, options: {
|
|
371
371
|
limit: number;
|
|
372
372
|
}, ctx: RequestContext): Promise<readonly SearchResult<N>[]>;
|
|
373
373
|
}
|
|
374
|
-
/** Accepted-base view the local service indexes (thunked for lazy wiring
|
|
374
|
+
/** Accepted-base view the local service indexes (thunked for lazy wiring
|
|
375
375
|
* the instance re-reads it on every call, so the service can be constructed
|
|
376
376
|
* before any data arrives). */
|
|
377
377
|
interface LocalSearchBase<N = Record<string, unknown>> {
|
|
378
378
|
/** Accepted-model nodes in accepted-base order (the tie-break order). */
|
|
379
379
|
nodes: readonly GraphNode<N>[];
|
|
380
|
-
/**
|
|
380
|
+
/** declared attr fields; undefined = id-only search (the service
|
|
381
381
|
* never guesses attr names). */
|
|
382
382
|
searchIndex: readonly string[] | undefined;
|
|
383
383
|
}
|
|
@@ -393,7 +393,7 @@ declare const SEARCH_SCORE_TOKEN_START_BONUS = 0.25;
|
|
|
393
393
|
/** Nodes scanned between cooperative yields (awaited microtask + signal check). */
|
|
394
394
|
declare const SEARCH_SCAN_CHUNK = 4096;
|
|
395
395
|
/**
|
|
396
|
-
* Creates the
|
|
396
|
+
* Creates the default indexed search service over the accepted model.
|
|
397
397
|
* Declares `revisionDependencies: ['source', 'model']` — the index keys on
|
|
398
398
|
* `ctx.modelRevision` (plus the declared field list), so it builds at most
|
|
399
399
|
* once per model revision and a stale-model result is discarded by the
|
|
@@ -403,7 +403,7 @@ declare const SEARCH_SCAN_CHUNK = 4096;
|
|
|
403
403
|
declare function createLocalSearchService<N = Record<string, unknown>>(getBase: () => LocalSearchBase<N>): LocalSearchService<N>;
|
|
404
404
|
|
|
405
405
|
/**
|
|
406
|
-
*
|
|
406
|
+
* image-atlas pipeline — pure core side.
|
|
407
407
|
*
|
|
408
408
|
* Turns per-point image refs (stable strings from the synchronous `nodeImage`
|
|
409
409
|
* accessor) into engine-ready atlas resources: resolved ImageBitmaps with
|
|
@@ -411,27 +411,27 @@ declare function createLocalSearchService<N = Record<string, unknown>>(getBase:
|
|
|
411
411
|
* only ever sees the output shape (`EngineCommit.resources`); everything
|
|
412
412
|
* async — resolve, fetch, decode, retry, abort — stays here.
|
|
413
413
|
*
|
|
414
|
-
* Invariants
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
*
|
|
418
|
-
*
|
|
419
|
-
*
|
|
420
|
-
*
|
|
421
|
-
*
|
|
422
|
-
*
|
|
423
|
-
*
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
*
|
|
429
|
-
*
|
|
414
|
+
* Invariants:
|
|
415
|
+
* - Loads are deduplicated by ref: one in-flight resolve per unique ref.
|
|
416
|
+
* - Slots are allocated per unique ref from a free-list capped at
|
|
417
|
+
* `maxEntries`; over-cap refs stay placeholder and are diagnosed once per
|
|
418
|
+
* generation.
|
|
419
|
+
* - Results are admission-checked against the current request state before
|
|
420
|
+
* atlas admission; the abort signal is an optimization, admission is the
|
|
421
|
+
* gate (a resolver that ignores its signal cannot poison a newer
|
|
422
|
+
* generation).
|
|
423
|
+
* - Transient resolver/fetch failures retry up to `maxRetries`; decode
|
|
424
|
+
* failures are final.
|
|
425
|
+
* - Failures are cadence-batched: ONE `image-resolve-failed` diagnostic per
|
|
426
|
+
* flush with a count and sampled refs, each ref counted once per
|
|
427
|
+
* generation. Failures never poison the engine commit.
|
|
428
|
+
* - A resolver-returned string is a final URL/data URI fetched + decoded
|
|
429
|
+
* exactly once — never recursively passed back to the resolver.
|
|
430
430
|
*/
|
|
431
431
|
|
|
432
432
|
/**
|
|
433
433
|
* Owns authenticated fetch / caching / redirect handling for one ref
|
|
434
|
-
*
|
|
434
|
+
* under the public resolver contract. Returning a Blob hands the bytes straight to
|
|
435
435
|
* decode; returning a string names a FINAL URL or data URI that the pipeline
|
|
436
436
|
* fetches + decodes once (it is never re-resolved).
|
|
437
437
|
*/
|
|
@@ -446,7 +446,7 @@ type FetchLike = (url: string, init: {
|
|
|
446
446
|
}>;
|
|
447
447
|
/** Blob → ImageBitmap. Injectable so Node tests never touch createImageBitmap. */
|
|
448
448
|
type ImageDecode = (blob: Blob) => Promise<ImageBitmap>;
|
|
449
|
-
/** One coalesced flush of atlas work — mirrors `EngineCommit.resources
|
|
449
|
+
/** One coalesced flush of atlas work — mirrors `EngineCommit.resources`. */
|
|
450
450
|
interface ImageAtlasBatch {
|
|
451
451
|
/** Newly resolved bitmaps with their slot assignments. */
|
|
452
452
|
upserts: readonly {
|
|
@@ -508,7 +508,7 @@ declare class ImageAtlasPipeline {
|
|
|
508
508
|
private generation;
|
|
509
509
|
private flushScheduled;
|
|
510
510
|
private disposed;
|
|
511
|
-
/**
|
|
511
|
+
/** Evicted-entry bitmaps awaiting close — closed AFTER the
|
|
512
512
|
* flush that carries their removeSlots, so the instance's recovery-replay
|
|
513
513
|
* map (pruned synchronously in the batch callback) can never re-send a
|
|
514
514
|
* closed bitmap. */
|
|
@@ -542,7 +542,7 @@ declare class ImageAtlasPipeline {
|
|
|
542
542
|
/** -1 (placeholder) until the ref's bitmap is delivered or in this batch. */
|
|
543
543
|
private buildPointIndex;
|
|
544
544
|
/**
|
|
545
|
-
*
|
|
545
|
+
* Roster-atomic resource mappings: the SYNCHRONOUS point→slot
|
|
546
546
|
* mapping for the last requested roster — a slot appears only when its ref
|
|
547
547
|
* is resolved AND the engine has already received the bitmap (delivered);
|
|
548
548
|
* pending, failed, evicted, and reused-but-undelivered refs are the −1
|
|
@@ -555,7 +555,7 @@ declare class ImageAtlasPipeline {
|
|
|
555
555
|
}
|
|
556
556
|
|
|
557
557
|
/**
|
|
558
|
-
*
|
|
558
|
+
* View state — pure module: the serialized
|
|
559
559
|
* schema, the canonical-JSON encoder, the structural validator, and the
|
|
560
560
|
* version gate. No engine, no DOM, no store access; the instance wires
|
|
561
561
|
* `getViewState`/`setViewState` on top.
|
|
@@ -564,33 +564,33 @@ declare class ImageAtlasPipeline {
|
|
|
564
564
|
*
|
|
565
565
|
* Deep-links outlive library versions, so everything in this file is a
|
|
566
566
|
* COMMITMENT, not an internal:
|
|
567
|
-
*
|
|
568
|
-
*
|
|
569
|
-
*
|
|
570
|
-
*
|
|
571
|
-
*
|
|
572
|
-
*
|
|
573
|
-
*
|
|
574
|
-
*
|
|
567
|
+
* - The v1 wire shape below never changes incompatibly; breaking shape
|
|
568
|
+
* changes bump `v` and ship an in-code migration from every prior version
|
|
569
|
+
* in the same release (the registry seam is here from day one).
|
|
570
|
+
* - Additive fields never bump `v` — an equal-`v` payload with unknown
|
|
571
|
+
* fields applies cleanly with the unknowns ignored.
|
|
572
|
+
* - A payload with `v` HIGHER than this library knows, or failing structural
|
|
573
|
+
* validation (hand-edited URL, truncated paste), is rejected whole: a
|
|
574
|
+
* half-restored view misrepresents what the sender saw.
|
|
575
575
|
*
|
|
576
576
|
* ## v1 shape notes
|
|
577
577
|
*
|
|
578
|
-
*
|
|
579
|
-
*
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
*
|
|
583
|
-
*
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
*
|
|
587
|
-
*
|
|
588
|
-
*
|
|
589
|
-
*
|
|
590
|
-
*
|
|
578
|
+
* - `folds` extends the v1 schema:
|
|
579
|
+
* node folds are exploration state over stable public ids — a
|
|
580
|
+
* deep-link that silently dropped them would misrepresent the sender's
|
|
581
|
+
* view, the exact failure this feature exists to prevent. Session-local
|
|
582
|
+
* expansion records, by contrast, are NEVER serialized.
|
|
583
|
+
* - Brushes are TAGGED on the wire (`kind`) even though the runtime
|
|
584
|
+
* BrushState is untagged: the tag comes from `DimensionSpec.kind` at
|
|
585
|
+
* serialize time and lets restore validate shape-vs-dimension without
|
|
586
|
+
* consulting the live spec first. Categorical stores EXCLUSIONS, so
|
|
587
|
+
* categories that appear after the view was saved stay visible.
|
|
588
|
+
* - `layout` is the normalized OBJECT form (`{ kind }`) even though the
|
|
589
|
+
* runtime today carries only the bare kind string — the object is what
|
|
590
|
+
* makes a future static seed or a layouts package additive.
|
|
591
591
|
*/
|
|
592
592
|
|
|
593
|
-
/** Tagged wire form of a crossfilter brush
|
|
593
|
+
/** Tagged wire form of a crossfilter brush for view-state serialization. */
|
|
594
594
|
type ViewBrushState = {
|
|
595
595
|
kind: 'numeric' | 'temporal';
|
|
596
596
|
range: readonly [number, number];
|
|
@@ -603,7 +603,7 @@ type ViewBrushState = {
|
|
|
603
603
|
interface ViewLayoutSpec {
|
|
604
604
|
kind: 'force' | 'fixed';
|
|
605
605
|
}
|
|
606
|
-
/**
|
|
606
|
+
/** Scale subset that is data by construction (no functions). */
|
|
607
607
|
type SerializableScale = {
|
|
608
608
|
kind: 'sequential' | 'diverging';
|
|
609
609
|
metric: string;
|
|
@@ -630,16 +630,16 @@ interface GraphViewState {
|
|
|
630
630
|
camera: ViewportState | null;
|
|
631
631
|
selection: SelectionState;
|
|
632
632
|
hiddenNodeIds: readonly NodeId[];
|
|
633
|
-
/**
|
|
633
|
+
/** isolation; null = full scope. */
|
|
634
634
|
subgraph: SubgraphSpec | null;
|
|
635
635
|
/** Manual groups verbatim, or — under `groupBy` — `{key, collapsed}` pairs
|
|
636
|
-
* only (membership recomputes from current data on restore
|
|
636
|
+
* only (membership recomputes from current data on restore). */
|
|
637
637
|
groups: readonly GroupSpec[] | ReadonlyArray<{
|
|
638
638
|
key: string;
|
|
639
639
|
collapsed: boolean;
|
|
640
640
|
}>;
|
|
641
641
|
pinnedNodeIds: readonly NodeId[];
|
|
642
|
-
/**
|
|
642
|
+
/** node folds: anchor → declared members (v1 extension, see header). */
|
|
643
643
|
folds?: ReadonlyArray<readonly [NodeId, readonly NodeId[]]>;
|
|
644
644
|
layout: ViewLayoutSpec;
|
|
645
645
|
/** Declaration order; a key absent here has no brush. */
|
|
@@ -653,7 +653,7 @@ interface GraphViewState {
|
|
|
653
653
|
positions?: ReadonlyArray<readonly [string, number, number]>;
|
|
654
654
|
styling?: ViewStyling;
|
|
655
655
|
/** Host-owned durable source coordinate — stored verbatim, NEVER
|
|
656
|
-
* interpreted; compared canonically on restore
|
|
656
|
+
* interpreted; compared canonically on restore. */
|
|
657
657
|
dataRef?: JsonValue;
|
|
658
658
|
}
|
|
659
659
|
declare const VIEW_STATE_VERSION: 1;
|
|
@@ -664,8 +664,8 @@ declare const VIEW_STATE_VERSION: 1;
|
|
|
664
664
|
* their canonical strings are identical.
|
|
665
665
|
*/
|
|
666
666
|
declare function canonicalJson(value: JsonValue | undefined): string | undefined;
|
|
667
|
-
/** Canonical equality for dataRef values
|
|
668
|
-
* A non-JSON value (cycles included) is never equal to anything — the
|
|
667
|
+
/** Canonical equality for dataRef values: key order never matters.
|
|
668
|
+
* A non-JSON value (cycles included) is never equal to anything — the
|
|
669
669
|
* "compared, never interpreted" rule extended to malformed input. */
|
|
670
670
|
declare function sameDataRef(a: JsonValue | undefined, b: JsonValue | undefined): boolean;
|
|
671
671
|
type ViewStateVerdict = {
|
|
@@ -677,7 +677,7 @@ type ViewStateVerdict = {
|
|
|
677
677
|
problems: readonly string[];
|
|
678
678
|
};
|
|
679
679
|
/**
|
|
680
|
-
* Full structural validation + version gate. Order per
|
|
680
|
+
* Full structural validation + version gate. Order per version first
|
|
681
681
|
* (higher-than-known and non-numeric reject as `unsupported-version` /
|
|
682
682
|
* `invalid-view-state` BEFORE field checks), then per-field structure. Lower
|
|
683
683
|
* versions run the migration registry, then re-validate at the current shape.
|
|
@@ -693,7 +693,7 @@ type SetViewStateResult = {
|
|
|
693
693
|
} | {
|
|
694
694
|
status: 'rejected';
|
|
695
695
|
code: 'invalid-view-state' | 'unsupported-version'
|
|
696
|
-
/** The state touches a
|
|
696
|
+
/** The state touches a controlled slice or carries styling the
|
|
697
697
|
* host must reflect, and no aggregate restore callback exists. */
|
|
698
698
|
| 'missing-restore-callback'
|
|
699
699
|
/** Another restore/history transaction is awaiting acknowledgement. */
|
|
@@ -706,28 +706,28 @@ type SetViewStateResult = {
|
|
|
706
706
|
};
|
|
707
707
|
|
|
708
708
|
/**
|
|
709
|
-
*
|
|
709
|
+
* GraphInstance — the public headless core instance (v0.3 subset).
|
|
710
710
|
*
|
|
711
711
|
* One `applyHostUpdate` call is the atomic host boundary: it validates,
|
|
712
712
|
* reconciles, re-projects only dirty channels, and publishes EXACTLY ONE store
|
|
713
|
-
* `set
|
|
714
|
-
* controlled-state change can never tear across frames
|
|
713
|
+
* `set` and AT MOST ONE engine commit, so a simultaneous data + style +
|
|
714
|
+
* controlled-state change can never tear across frames.
|
|
715
715
|
*
|
|
716
|
-
* Vanilla zustand only — no React, no DOM access at module scope
|
|
716
|
+
* Vanilla zustand only — no React, no DOM access at module scope.
|
|
717
717
|
*/
|
|
718
718
|
|
|
719
|
-
/**
|
|
719
|
+
/** dark base theme (the default when no base is named). */
|
|
720
720
|
declare const GRAPH_THEME_DARK: GraphTheme;
|
|
721
|
-
/**
|
|
721
|
+
/** light base theme. */
|
|
722
722
|
declare const GRAPH_THEME_LIGHT: GraphTheme;
|
|
723
723
|
/**
|
|
724
|
-
* Resolve a ThemeInput to a full GraphTheme
|
|
724
|
+
* Resolve a ThemeInput to a full GraphTheme: pick the named base
|
|
725
725
|
* (default dark), then merge every defined token over it. A full GraphTheme
|
|
726
726
|
* input resolves to exactly its own tokens; `undefined` resolves to the dark
|
|
727
727
|
* base; the v0.1 `{background}` compat shorthand merges as a partial.
|
|
728
728
|
*/
|
|
729
729
|
declare function resolveTheme(input?: ThemeInput): GraphTheme;
|
|
730
|
-
/** One categorical legend row
|
|
730
|
+
/** One categorical legend row: declared-domain rows first (including
|
|
731
731
|
* currently-empty categories), then extra seen values sorted. */
|
|
732
732
|
interface ScaleInfoRow {
|
|
733
733
|
value: string;
|
|
@@ -745,50 +745,50 @@ interface ScaleChannelInfo<N = Record<string, unknown>> {
|
|
|
745
745
|
/** Categorical rows; omitted for sequential/diverging scales. */
|
|
746
746
|
rows?: readonly ScaleInfoRow[];
|
|
747
747
|
}
|
|
748
|
-
/**
|
|
748
|
+
/** Revision-aware service seam for expansion and search. */
|
|
749
749
|
interface GraphServices<N = Record<string, unknown>, E = Record<string, unknown>> {
|
|
750
750
|
/**
|
|
751
751
|
* Ego-expansion resolver for `expandNode` / `SubgraphSpec.hops`. Default:
|
|
752
|
-
* the built-in LOCAL service — it walks the core's
|
|
752
|
+
* the built-in LOCAL service — it walks the core's adjacency over the
|
|
753
753
|
* accepted model, INCLUDING currently out-of-scope nodes (zero config,
|
|
754
754
|
* zero network; the core still never fetches).
|
|
755
755
|
*/
|
|
756
756
|
expansion?: ExpansionService<N, E>;
|
|
757
757
|
/**
|
|
758
|
-
*
|
|
758
|
+
* path resolver for `findPath`. Default: the built-in LOCAL
|
|
759
759
|
* unweighted BFS over the loaded VISIBLE edge list, respecting
|
|
760
760
|
* PathOptions.direction. Revision-aware: a result arriving after a
|
|
761
|
-
* dataset replacement is discarded at admission
|
|
761
|
+
* dataset replacement is discarded at admission.
|
|
762
762
|
*/
|
|
763
763
|
path?: PathService;
|
|
764
764
|
/**
|
|
765
|
-
*
|
|
765
|
+
* search resolver for `instance.search`. Default: the built-in LOCAL
|
|
766
766
|
* indexed service over the accepted model plus the host's declared
|
|
767
767
|
* `searchIndex` fields (id-only when never declared — it never guesses
|
|
768
768
|
* attr names; zero config, zero network). Custom services plug in
|
|
769
|
-
* server-side search (Omnigraph
|
|
770
|
-
* correctness: RequestContext, revision-keyed caching, supersede
|
|
769
|
+
* server-side search (such as Omnigraph stored queries) with the same
|
|
770
|
+
* instance-side correctness: RequestContext, revision-keyed caching, supersede
|
|
771
771
|
* cancellation, stale rejection at admission.
|
|
772
772
|
*/
|
|
773
773
|
search?: SearchService<N>;
|
|
774
774
|
}
|
|
775
775
|
interface CreateGraphInstanceOptions<N = Record<string, unknown>, E = Record<string, unknown>> {
|
|
776
|
-
/** Called once per mount; a re-attach constructs a fresh engine
|
|
776
|
+
/** Called once per mount; a re-attach constructs a fresh engine. */
|
|
777
777
|
engine: EngineFactory;
|
|
778
778
|
/** Fit the camera once when the first data-bearing commit reaches a fresh engine. Default true. */
|
|
779
779
|
fitViewOnFirstData?: boolean;
|
|
780
|
-
/**
|
|
780
|
+
/** revision-aware services (expansion + search). */
|
|
781
781
|
services?: GraphServices<N, E>;
|
|
782
782
|
/**
|
|
783
|
-
*
|
|
783
|
+
* node attr fields the DEFAULT search service indexes (ids always;
|
|
784
784
|
* absent = id-only — the service never guesses attr names). CONSTRUCTION-
|
|
785
|
-
* ONLY
|
|
785
|
+
* ONLY as a host construction option: read once here;
|
|
786
786
|
* changing it requires a keyed remount / replacement instance. A runtime
|
|
787
787
|
* `applyHostUpdate` attempt is ignored with a one-shot warning.
|
|
788
788
|
*/
|
|
789
789
|
searchIndex?: readonly string[];
|
|
790
790
|
/**
|
|
791
|
-
*
|
|
791
|
+
* undo/redo. Default true. `false` makes the history
|
|
792
792
|
* surface inert (record/undo/redo no-ops, depths stay 0); an object sets
|
|
793
793
|
* the stack bound (default {@link HISTORY_LIMIT_DEFAULT} entries).
|
|
794
794
|
*/
|
|
@@ -796,42 +796,42 @@ interface CreateGraphInstanceOptions<N = Record<string, unknown>, E = Record<str
|
|
|
796
796
|
limit?: number;
|
|
797
797
|
};
|
|
798
798
|
/**
|
|
799
|
-
*
|
|
800
|
-
*
|
|
799
|
+
* Degradation-ladder thresholds. CONSTRUCTION-ONLY: read once; invalid
|
|
800
|
+
* fields fall back to the defaults with ONE
|
|
801
801
|
* config warning diagnostic. A runtime change requires a keyed remount.
|
|
802
802
|
*/
|
|
803
803
|
limits?: Partial<ScaleLimits>;
|
|
804
804
|
/**
|
|
805
|
-
*
|
|
805
|
+
* Image-atlas resolver seam: owns authenticated fetch/caching for
|
|
806
806
|
* one `nodeImage` ref. Default: plain `fetch(ref)` for public URLs.
|
|
807
807
|
* Injectable for tests and authenticated hosts.
|
|
808
808
|
*/
|
|
809
809
|
imageResolver?: ImageResolver;
|
|
810
810
|
/**
|
|
811
|
-
*
|
|
812
|
-
*
|
|
813
|
-
*
|
|
811
|
+
* Execution mode. Current worker cargo is columnar ACCEPTANCE: validation,
|
|
812
|
+
* deduplication, and link resolution run off-thread, then land through
|
|
813
|
+
* revision-gated async admission. 'main' (default) keeps every
|
|
814
814
|
* columnar ingest synchronous; 'auto' and 'worker' route columnar data
|
|
815
815
|
* through the worker lane when it boots — an unavailable lane degrades
|
|
816
816
|
* to 'main' with one `worker-unavailable` info diagnostic under 'auto'
|
|
817
|
-
* and an ERROR diagnostic under 'worker'
|
|
818
|
-
* back
|
|
819
|
-
*
|
|
817
|
+
* and an ERROR diagnostic under 'worker'. Columnar acceptance may still
|
|
818
|
+
* fall back because it has no worker-required mode; channel projection
|
|
819
|
+
* remains on the main thread.
|
|
820
820
|
*/
|
|
821
821
|
execution?: 'auto' | 'main' | 'worker';
|
|
822
|
-
/**
|
|
822
|
+
/** Worker construction tri-option (URL / factory / inline
|
|
823
823
|
* default). Testing seam: `transport` on the lane via this factory. */
|
|
824
824
|
workerFactory?: WorkerFactoryOption;
|
|
825
825
|
}
|
|
826
826
|
/**
|
|
827
|
-
* `expandNode` outcome
|
|
828
|
-
*
|
|
829
|
-
*
|
|
830
|
-
*
|
|
831
|
-
*
|
|
832
|
-
*
|
|
833
|
-
*
|
|
834
|
-
*
|
|
827
|
+
* `expandNode` outcome:
|
|
828
|
+
* - `{ added }` — the admitted result merged; `added` counts the nodes it
|
|
829
|
+
* made newly visible in the current scope.
|
|
830
|
+
* - `{ noop: true }` — every returned neighbor was already visible in the
|
|
831
|
+
* current scope; no session was opened and nothing changed.
|
|
832
|
+
* - `{ coalesced: true }` — reserved. v0.5 same-id coalescing hands the
|
|
833
|
+
* SECOND caller the IDENTICAL in-flight promise, so both callers observe
|
|
834
|
+
* the primary call's `{added}`/`{noop}` result instead of this marker.
|
|
835
835
|
*/
|
|
836
836
|
type ExpandNodeResult = {
|
|
837
837
|
added: number;
|
|
@@ -841,7 +841,7 @@ type ExpandNodeResult = {
|
|
|
841
841
|
noop: true;
|
|
842
842
|
};
|
|
843
843
|
/**
|
|
844
|
-
*
|
|
844
|
+
* expansion bookkeeping: one committed expansion overlay. Data-merging
|
|
845
845
|
* batches carry the request id (overlayId + batch ids) and provenance into
|
|
846
846
|
* ingestion so abort, rollback, and removeOverlay remove the exact
|
|
847
847
|
* contribution they own.
|
|
@@ -855,11 +855,11 @@ interface ExpansionOverlayRecord {
|
|
|
855
855
|
provenance?: unknown;
|
|
856
856
|
}
|
|
857
857
|
/**
|
|
858
|
-
*
|
|
859
|
-
*
|
|
860
|
-
*
|
|
861
|
-
*
|
|
862
|
-
*
|
|
858
|
+
* overlay label lane subscriptions. Two channels with distinct cadences:
|
|
859
|
+
* - `subscribeCandidates` fires ONLY when the candidate SET (ids/text/forced)
|
|
860
|
+
* changes — the throttled re-rank. React re-renders label content here.
|
|
861
|
+
* - `subscribePositions` fires on scheduler ticks (host `onFrame`) with fresh
|
|
862
|
+
* x/y for the SAME set — imperative transform writes, NO React re-render.
|
|
863
863
|
* Both replay the current state synchronously on subscribe. The emitted array
|
|
864
864
|
* and its placement objects are REUSED across position ticks — copy if you
|
|
865
865
|
* need a snapshot.
|
|
@@ -869,20 +869,20 @@ interface LabelSubscriptions {
|
|
|
869
869
|
subscribePositions(cb: (list: readonly LabelPlacement[]) => void): () => void;
|
|
870
870
|
}
|
|
871
871
|
interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>> {
|
|
872
|
-
/** Vanilla zustand store — the single observable state surface
|
|
872
|
+
/** Vanilla zustand store — the single observable state surface. */
|
|
873
873
|
readonly store: StoreApi<GraphStoreState>;
|
|
874
|
-
/**
|
|
874
|
+
/** DOM label lane (overlay scheduler output). */
|
|
875
875
|
readonly labels: LabelSubscriptions;
|
|
876
|
-
/** Atomic host transaction: one store publication, at most one engine commit
|
|
876
|
+
/** Atomic host transaction: one store publication, at most one engine commit. */
|
|
877
877
|
applyHostUpdate(update: GraphHostUpdate<N, E>): void;
|
|
878
878
|
/**
|
|
879
|
-
*
|
|
879
|
+
* revisioned ingestion: begin a bounded, cancellable session against an
|
|
880
880
|
* explicit `datasetKey` and `baseModelRevision` (compare-and-set; mismatch
|
|
881
881
|
* throws 'stale-revision'). Overlay sessions must name the CURRENT
|
|
882
882
|
* datasetKey; replace sessions may establish a new one and are always
|
|
883
883
|
* atomic.
|
|
884
884
|
*
|
|
885
|
-
*
|
|
885
|
+
* While a declarative data source is actively driving (a snapshot
|
|
886
886
|
* was applied through `applyHostUpdate` and has not been superseded by a
|
|
887
887
|
* committed replace session), `purpose:'replace'` is rejected at begin with
|
|
888
888
|
* a TypeError — two writers may not race for the base. Overlay ingestion
|
|
@@ -890,11 +890,11 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
890
890
|
*
|
|
891
891
|
* Every session admission/publication is serialized through the
|
|
892
892
|
* instance-local acceptance queue; arrival there is the global admission
|
|
893
|
-
* order
|
|
893
|
+
* order.
|
|
894
894
|
*/
|
|
895
895
|
beginIngest(opts: BeginIngestOptions): IngestSession<N, E>;
|
|
896
896
|
/**
|
|
897
|
-
*
|
|
897
|
+
* atomically remove exactly one committed overlay — re-runs collision
|
|
898
898
|
* and endpoint resolution, promotes formerly shadowed rows from surviving
|
|
899
899
|
* overlays, advances model/render revisions, and releases the overlayId for
|
|
900
900
|
* deliberate reuse. Unknown ids are an idempotent `{ removed: false }`.
|
|
@@ -902,14 +902,14 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
902
902
|
removeOverlay(overlayId: string): {
|
|
903
903
|
removed: boolean;
|
|
904
904
|
};
|
|
905
|
-
/** Committed overlay ids for the current dataset
|
|
905
|
+
/** Committed overlay ids for the current dataset. */
|
|
906
906
|
getOverlayIds(): readonly string[];
|
|
907
907
|
attach(container: HTMLElement): Promise<void>;
|
|
908
908
|
detach(): void;
|
|
909
909
|
destroy(): void;
|
|
910
910
|
/**
|
|
911
|
-
*
|
|
912
|
-
* control's preventDefault
|
|
911
|
+
* typed events: listeners run SYNCHRONOUSLY in registration order; the
|
|
912
|
+
* control's preventDefault cancels ONLY the built-in follow-up (click
|
|
913
913
|
* selection, drag pin), never other listeners.
|
|
914
914
|
*/
|
|
915
915
|
on<K extends GraphEventName>(name: K, cb: (payload: GraphEventMap<N, E>[K], control: GraphListenerControl) => void): () => void;
|
|
@@ -918,51 +918,51 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
918
918
|
zoomOut(): void;
|
|
919
919
|
setViewport(v: Partial<ViewportState>): void;
|
|
920
920
|
/**
|
|
921
|
-
*
|
|
921
|
+
* Focus neighborhood: keep the v0.1 camera behavior
|
|
922
922
|
* (setFocusedIndex + zoomToIndex) and RETURN the 1-hop neighbor ids
|
|
923
923
|
* (engine adjacency when available, else the core CSR adjacency).
|
|
924
924
|
*
|
|
925
|
-
* Documented compromise: the engine exposes ONE highlight channel
|
|
926
|
-
*
|
|
925
|
+
* Documented compromise: the engine exposes ONE highlight channel, so the
|
|
926
|
+
* neighbor ring is pushed through setSelectedIndices
|
|
927
927
|
* ONLY when that cannot lie about real selection state — selection empty
|
|
928
928
|
* and uncontrolled. The ring is a visual, never a store write; the next
|
|
929
929
|
* selection push overwrites it. Opt out via `highlightNeighbors: false`.
|
|
930
|
-
* `hops` is
|
|
930
|
+
* Only `hops: 1` is currently supported.
|
|
931
931
|
*/
|
|
932
932
|
focusNode(id: NodeId, opts?: {
|
|
933
933
|
highlightNeighbors?: boolean;
|
|
934
934
|
hops?: 1;
|
|
935
935
|
}): readonly NodeId[];
|
|
936
936
|
/**
|
|
937
|
-
*
|
|
937
|
+
* emphasis ring WITHOUT the camera: ring `id` (null clears). The light
|
|
938
938
|
* op keyboard navigation needs — arrowing a list must not fly the camera on
|
|
939
939
|
* every keystroke (`focusNode` stays ring + zoom + neighbors). Unknown ids
|
|
940
940
|
* are a silent no-op (a stale row racing a model swap is data, not an
|
|
941
941
|
* error); `emphasisRing: false` suppresses it entirely. The target is
|
|
942
942
|
* STICKY: it survives structural commits and context recovery until it is
|
|
943
943
|
* cleared, its id departs the model, or pointer hover supersedes it
|
|
944
|
-
* (emphasis belongs to the latest action — the
|
|
944
|
+
* (emphasis belongs to the latest action — the rule).
|
|
945
945
|
*/
|
|
946
946
|
emphasizeNode(id: NodeId | null): void;
|
|
947
947
|
/**
|
|
948
|
-
*
|
|
948
|
+
* Typed context-menu channel, opened from a DOM presenter. Label
|
|
949
949
|
* divs are `pointerEvents: 'auto'` by design (click-to-focus), so a
|
|
950
950
|
* right-click on one never reaches the engine canvas — without this seam
|
|
951
951
|
* the nodes prominent enough to carry labels are exactly the ones whose
|
|
952
952
|
* right-click falls through to the browser's native menu. Emits the SAME
|
|
953
953
|
* 'contextMenu' event the canvas gesture produces; `screen` is
|
|
954
|
-
* container-relative CSS px
|
|
954
|
+
* container-relative CSS px. Unknown ids are a
|
|
955
955
|
* silent no-op (a stale label racing a model swap is data, not an error).
|
|
956
956
|
*/
|
|
957
957
|
requestNodeContextMenu(id: NodeId, screen: readonly [number, number]): void;
|
|
958
958
|
setSelection(ids: readonly NodeId[] | SelectionState): void;
|
|
959
959
|
selectNodes(ids: readonly NodeId[]): void;
|
|
960
960
|
selectEdges(ids: readonly EdgeId[]): void;
|
|
961
|
-
/**
|
|
961
|
+
/** group namespace: validate against the CURRENT resolved
|
|
962
962
|
* groups (unknown ids dropped, duplicates collapse) and store in
|
|
963
963
|
* groups-array order — the group analog of accepted-base ordering. Never
|
|
964
964
|
* touches the node/edge namespaces; the group namespace is always
|
|
965
|
-
* instance-owned
|
|
965
|
+
* instance-owned. */
|
|
966
966
|
selectGroups(ids: readonly string[]): void;
|
|
967
967
|
/** Expand to the 1-hop neighborhood of `id` (or of the current selection). */
|
|
968
968
|
selectNeighbors(id?: NodeId): void;
|
|
@@ -970,9 +970,9 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
970
970
|
invertSelection(): void;
|
|
971
971
|
clearSelection(): void;
|
|
972
972
|
/**
|
|
973
|
-
*
|
|
973
|
+
* lasso: resolve the SCREEN-coordinate polygon to node ids
|
|
974
974
|
* via `engine.pointsInPolygon`, drop hidden ids, then replace (default) or
|
|
975
|
-
* union (`additive`) the node selection through the same
|
|
975
|
+
* union (`additive`) the node selection through the same ownership
|
|
976
976
|
* path as every other mutator (controlled → intent only). Returns the
|
|
977
977
|
* resolved lasso ids (accepted-base order) regardless of ownership; empty
|
|
978
978
|
* when the engine is not ready or lacks `pointsInPolygon`.
|
|
@@ -992,19 +992,19 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
992
992
|
* tolerance. No-op (null) on the native route. */
|
|
993
993
|
sampleEdgeClick(screen: readonly [number, number]): AcceptedEdge<E> | null;
|
|
994
994
|
/**
|
|
995
|
-
* @internal
|
|
995
|
+
* @internal Shared hit-test/overlay cadence: the count of engine
|
|
996
996
|
* onFrame ticks this session. THE one clock every sampling route throttles
|
|
997
997
|
* against (node hover and link picking share it by construction — there is
|
|
998
|
-
* no second cadence timer anywhere; the
|
|
999
|
-
* The
|
|
1000
|
-
*
|
|
998
|
+
* no second cadence timer anywhere; the lint rule enforces it).
|
|
999
|
+
* The degradation ladder's defer-link-picking step arms/disarms against this counter;
|
|
1000
|
+
* telemetry reads it for idle-wakeup accounting.
|
|
1001
1001
|
*/
|
|
1002
1002
|
getFrameCadence(): number;
|
|
1003
1003
|
/**
|
|
1004
|
-
* @internal
|
|
1005
|
-
* the perf-gate-delta suite proves the brush fast path does O(Δ) work
|
|
1006
|
-
* zero full recomposes/refreshes/cascades across a scrub.
|
|
1007
|
-
*
|
|
1004
|
+
* @internal op counters (live object — snapshot before comparing):
|
|
1005
|
+
* the perf-gate-delta suite proves the brush fast path does O(Δ) work
|
|
1006
|
+
* zero full recomposes/refreshes/cascades across a scrub. telemetry
|
|
1007
|
+
* will fold these into `lastCommitMs`-adjacent accounting.
|
|
1008
1008
|
*/
|
|
1009
1009
|
getPerfCounters(): Readonly<{
|
|
1010
1010
|
brushSlotsTranslated: number;
|
|
@@ -1014,12 +1014,12 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1014
1014
|
fullEdgeRecomposes: number;
|
|
1015
1015
|
}>;
|
|
1016
1016
|
/**
|
|
1017
|
-
*
|
|
1017
|
+
* telemetry snapshot: counts, byte estimates, queue depth,
|
|
1018
1018
|
* revisions, last-commit phase decomposition, active ladder steps,
|
|
1019
1019
|
* execution lane, range availability, and the pressure mirror — never raw
|
|
1020
1020
|
* attrs or ids. Synchronous in every lifecycle state (pre-scene fields
|
|
1021
1021
|
* read zero/absent). `validate` is folded into `derive` until the
|
|
1022
|
-
* columnar lane splits acceptance from derivation
|
|
1022
|
+
* columnar lane splits acceptance from derivation.
|
|
1023
1023
|
*/
|
|
1024
1024
|
getPerfSnapshot(): GraphPerfSnapshot;
|
|
1025
1025
|
hideNodes(ids: readonly NodeId[]): void;
|
|
@@ -1028,24 +1028,24 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1028
1028
|
pinNode(id: NodeId, xy?: readonly [number, number]): void;
|
|
1029
1029
|
unpinNode(id: NodeId): void;
|
|
1030
1030
|
clearPins(): void;
|
|
1031
|
-
/**
|
|
1031
|
+
/** PERSISTENT pins: pin ids AT THEIR CURRENT POSITION via
|
|
1032
1032
|
* engine.setPinnedIndices — no position payload in v0.10. Independent of
|
|
1033
1033
|
* transient drag pinning (`pins`): the engine receives the UNION of both
|
|
1034
|
-
* slices, so releasing a drag pin leaves a persistent pin held.
|
|
1034
|
+
* slices, so releasing a drag pin leaves a persistent pin held.
|
|
1035
1035
|
* ownership mirrors groups: once the host supplies `pinnedNodeIds` (null
|
|
1036
1036
|
* included) the ops fire the 'pinnedChange' intent instead of writing.
|
|
1037
1037
|
* Unknown ids drop; departed ids prune through the ownership path on
|
|
1038
|
-
* model changes
|
|
1038
|
+
* model changes. */
|
|
1039
1039
|
pinNodes(ids: readonly NodeId[]): void;
|
|
1040
1040
|
/** Release persistent pins (see {@link pinNodes}); unpinned ids no-op. */
|
|
1041
1041
|
unpinNodes(ids: readonly NodeId[]): void;
|
|
1042
|
-
/** Add one group definition (same
|
|
1042
|
+
/** Add one group definition (same acyclic/singly-parented validation as the
|
|
1043
1043
|
* `groups` prop — a violating spec is ONE 'config-error' and a no-op). */
|
|
1044
1044
|
groupNodes(spec: GroupSpec): void;
|
|
1045
1045
|
/** Remove one group definition; its id prunes from SelectionState.groupIds
|
|
1046
1046
|
* through the ownership path. Unknown ids no-op (dev-mode warning). */
|
|
1047
1047
|
ungroup(groupId: string): void;
|
|
1048
|
-
/** Collapse/expand one group as a
|
|
1048
|
+
/** Collapse/expand one group as a structural diff. Works on manual
|
|
1049
1049
|
* groups AND groupBy-derived groups (the residue toggle). Same-value
|
|
1050
1050
|
* calls are exact no-ops (zero publishes, zero commits). */
|
|
1051
1051
|
setGroupCollapsed(groupId: string, collapsed: boolean): void;
|
|
@@ -1059,12 +1059,12 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1059
1059
|
* rejected, since that would close a containment cycle).
|
|
1060
1060
|
*
|
|
1061
1061
|
* One publish and at most one structural commit (E1). A no-member fold is
|
|
1062
|
-
* an exact no-op. Records a
|
|
1062
|
+
* an exact no-op. Records a 'folds' history step.
|
|
1063
1063
|
*/
|
|
1064
1064
|
foldNode(id: NodeId, opts?: {
|
|
1065
1065
|
memberIds?: readonly NodeId[];
|
|
1066
1066
|
}): void;
|
|
1067
|
-
/** Unfolds `id`, returning its members to the scene as a
|
|
1067
|
+
/** Unfolds `id`, returning its members to the scene as a structural
|
|
1068
1068
|
* diff. Unknown or unfolded ids are exact no-ops. */
|
|
1069
1069
|
unfoldNode(id: NodeId): void;
|
|
1070
1070
|
/** The members `id` currently stands for, or null when it is not folded.
|
|
@@ -1075,30 +1075,30 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1075
1075
|
} | null;
|
|
1076
1076
|
/** Current stage-4 clusters over the physical scene: ordered keys, member
|
|
1077
1077
|
* ids, the force center labels anchor to while hot, and the settled
|
|
1078
|
-
* centroid (null until a
|
|
1078
|
+
* centroid (null until a readback or a fixed-layout commit). Empty
|
|
1079
1079
|
* when no `clusters` spec is active. Clusters synthesize nothing — the
|
|
1080
|
-
* scene is byte-identical with and without a spec
|
|
1080
|
+
* scene is byte-identical with and without a spec. */
|
|
1081
1081
|
getClusters(): readonly ResolvedCluster[];
|
|
1082
|
-
/**
|
|
1083
|
-
* them into SelectionState.nodeIds through the standard
|
|
1082
|
+
/** resolve a cluster (by key) to its MEMBER node ids and write
|
|
1083
|
+
* them into SelectionState.nodeIds through the standard ownership
|
|
1084
1084
|
* path — clusters have no id namespace of their own in selection.
|
|
1085
1085
|
* `additive` unions with the current node selection. Unknown keys no-op. */
|
|
1086
1086
|
selectCluster(key: string, opts?: {
|
|
1087
1087
|
additive?: boolean;
|
|
1088
1088
|
}): void;
|
|
1089
1089
|
/**
|
|
1090
|
-
*
|
|
1090
|
+
* isolate: hard-scope the graph to the CURRENT node selection
|
|
1091
1091
|
* `subgraph: { seedIds: selection.nodeIds }` through the SAME path as the
|
|
1092
1092
|
* host-update prop. No-op when nothing is selected. Ownership note (v0.5):
|
|
1093
1093
|
* `subgraph` is UNCONTROLLED-ONLY — always instance-owned; the prop and
|
|
1094
1094
|
* this method write the same state, last writer wins.
|
|
1095
1095
|
*/
|
|
1096
1096
|
isolateSelection(): void;
|
|
1097
|
-
/**
|
|
1098
|
-
* returns with cached positions
|
|
1097
|
+
/** clear the hard scope (`subgraph: null`) — the full accepted model
|
|
1098
|
+
* returns with cached positions. */
|
|
1099
1099
|
resetIsolation(): void;
|
|
1100
1100
|
/**
|
|
1101
|
-
*
|
|
1101
|
+
* Ego-expansion of `id` (default 1 hop) through the configured
|
|
1102
1102
|
* ExpansionService. The result is gated by admission (declared revision
|
|
1103
1103
|
* dependencies + dataset lineage + seed existence — abort is only an
|
|
1104
1104
|
* optimization) and merges through ONE awaited atomic overlay
|
|
@@ -1117,8 +1117,8 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1117
1117
|
* Undoes `id`'s own expansions — the navigation Back button, NOT a
|
|
1118
1118
|
* containment operation. Aborts `id`'s pending expansion AND explicitly
|
|
1119
1119
|
* removes the overlays its past expansions committed (plus their scope
|
|
1120
|
-
* accretion). Committed overlay DATA otherwise persists
|
|
1121
|
-
* removeOverlay
|
|
1120
|
+
* accretion). Committed overlay DATA otherwise persists until
|
|
1121
|
+
* `removeOverlay()` or a replacing snapshot; this IS that explicit removal for
|
|
1122
1122
|
* expansion overlays.
|
|
1123
1123
|
*
|
|
1124
1124
|
* On a node that was never expanded from, this does nothing — there is no
|
|
@@ -1127,7 +1127,7 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1127
1127
|
* different word for navigation history.
|
|
1128
1128
|
*/
|
|
1129
1129
|
retractExpansion(id: NodeId): void;
|
|
1130
|
-
/**
|
|
1130
|
+
/** expansion bookkeeping for `id`: committed overlay records with
|
|
1131
1131
|
* request id, provenance, and the ids each expansion revealed. */
|
|
1132
1132
|
getExpansionOverlays(id: NodeId): readonly ExpansionOverlayRecord[];
|
|
1133
1133
|
/**
|
|
@@ -1136,21 +1136,21 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1136
1136
|
* `serviceCacheKey` over EXACTLY the service's declared revision
|
|
1137
1137
|
* dimensions ({@link SEARCH_CACHE_LIMIT}-entry LRU; a second call with an
|
|
1138
1138
|
* equal key while one is in flight coalesces onto the same service call),
|
|
1139
|
-
* cancels superseded work (a NEWER query aborts the older in-flight call
|
|
1139
|
+
* cancels superseded work (a NEWER query aborts the older in-flight call
|
|
1140
1140
|
* the older promise rejects `OrbitOperationError {code:'aborted'}`), and
|
|
1141
1141
|
* rejects stale results at admission (declared revision drift or dataset
|
|
1142
1142
|
* lineage change → the same typed 'aborted' rejection with a distinct
|
|
1143
1143
|
* staleness message; the store is untouched). A successful search
|
|
1144
1144
|
* publishes `store.search = {query, results}` — `node` populated for
|
|
1145
1145
|
* in-model ids — in ONE store publication. Search NEVER changes
|
|
1146
|
-
* scope/filter semantics and never fetches graph data
|
|
1146
|
+
* scope/filter semantics and never fetches graph data.
|
|
1147
1147
|
*/
|
|
1148
1148
|
search(query: string, opts?: {
|
|
1149
1149
|
limit?: number;
|
|
1150
1150
|
}): Promise<readonly SearchResult<N>[]>;
|
|
1151
1151
|
/** Clear `store.search` to null (e.g. the <GraphSearch> input emptied). */
|
|
1152
1152
|
clearSearch(): void;
|
|
1153
|
-
/**
|
|
1153
|
+
/** path query + atomic emphasis: resolves via the path
|
|
1154
1154
|
* service (local BFS default); null = unreachable (a RESULT). Emphasis is
|
|
1155
1155
|
* session-local — released by clearPath, any selection mutation, undo/
|
|
1156
1156
|
* redo, or a scene rebuild; never a history step; never serialized. */
|
|
@@ -1158,21 +1158,21 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1158
1158
|
clearPath(): void;
|
|
1159
1159
|
getActivePath(): PathResult | null;
|
|
1160
1160
|
/**
|
|
1161
|
-
*
|
|
1162
|
-
* AND
|
|
1161
|
+
* Result contract: a result id in the current rendered scene
|
|
1162
|
+
* AND mask-visible is focused (`focusNode`) → `{status:'focused'}`.
|
|
1163
1163
|
* Otherwise classification ONLY — 'not-loaded' (absent from the accepted
|
|
1164
1164
|
* model), 'out-of-scope' (in the model but outside the hard scope),
|
|
1165
1165
|
* 'filtered' (in the scene but mask-hidden). Never mutates scope or
|
|
1166
|
-
* filters — the host reacts explicitly
|
|
1166
|
+
* filters — the host reacts explicitly.
|
|
1167
1167
|
*/
|
|
1168
1168
|
activateSearchResult(result: SearchResult<N>): SearchActivation;
|
|
1169
1169
|
pauseSimulation(): void;
|
|
1170
1170
|
resumeSimulation(): void;
|
|
1171
1171
|
isSimulationRunning(): boolean;
|
|
1172
|
-
/**
|
|
1172
|
+
/** screenshot: delegates to the engine; null when unsupported/not ready. */
|
|
1173
1173
|
captureScreenshot(): Promise<Blob | null>;
|
|
1174
1174
|
/**
|
|
1175
|
-
*
|
|
1175
|
+
* binding-detected reduced-motion media preference. The EFFECTIVE
|
|
1176
1176
|
* value is `accessibility.reducedMotion ?? v` — when reduced, camera
|
|
1177
1177
|
* durations (fitView/setViewport/focusNode) coerce to 0.
|
|
1178
1178
|
*/
|
|
@@ -1180,7 +1180,7 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1180
1180
|
/**
|
|
1181
1181
|
* The crossfilter session facade, or null until the `crossfilter` prop has
|
|
1182
1182
|
* configured dimensions over an accepted base. Delegates to the
|
|
1183
|
-
* typed-column engine; `setBrush` routes visibility deltas into the
|
|
1183
|
+
* typed-column engine; `setBrush` routes visibility deltas into the
|
|
1184
1184
|
* soft mask (buffers-only commit, zero relayout) and resolves after the
|
|
1185
1185
|
* publish. Brush slot deltas are BASE indices; under a hard scope
|
|
1186
1186
|
* out-of-scope rows have no scene slot and simply do not mask anything.
|
|
@@ -1200,12 +1200,12 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1200
1200
|
/** Re-apply the most recently undone entry. False when nothing to redo. */
|
|
1201
1201
|
redo(): boolean;
|
|
1202
1202
|
/**
|
|
1203
|
-
* Serialize the exploration state
|
|
1203
|
+
* Serialize the exploration state: camera, selection, hidden ids,
|
|
1204
1204
|
* isolation, groups (manual specs verbatim; under `groupBy` only collapsed
|
|
1205
1205
|
* `{key, collapsed}` pairs — membership recomputes on restore), pins,
|
|
1206
1206
|
* folds, layout, crossfilter brushes in declaration order, the Scale-valued
|
|
1207
1207
|
* styling subset, and the host's `dataRef` verbatim. The predicate `filter`
|
|
1208
|
-
* and
|
|
1208
|
+
* and expansion records are never serialized. The sync form carries
|
|
1209
1209
|
* no positions: reproduction is best-effort via the layout descriptor.
|
|
1210
1210
|
*/
|
|
1211
1211
|
getViewState(opts?: {
|
|
@@ -1213,7 +1213,7 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1213
1213
|
}): GraphViewState;
|
|
1214
1214
|
/**
|
|
1215
1215
|
* Async form: additionally embeds quantized coordinates for the VISIBLE
|
|
1216
|
-
* (post-mask) set, read once from the engine
|
|
1216
|
+
* (post-mask) set, read once from the engine as event-time readback.
|
|
1217
1217
|
* Restores as a frozen fixed-equivalent — pixel-faithful regardless of
|
|
1218
1218
|
* engine nondeterminism. Past `maxPositions` (default 100 000) the call
|
|
1219
1219
|
* rejects `export-materialization-too-large`; persist the layout through
|
|
@@ -1224,7 +1224,7 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1224
1224
|
maxPositions?: number;
|
|
1225
1225
|
}): Promise<GraphViewState>;
|
|
1226
1226
|
/**
|
|
1227
|
-
* Atomically restore a serialized view
|
|
1227
|
+
* Atomically restore a serialized view. NEVER partially applies:
|
|
1228
1228
|
* structural validation and the version gate run first (reject whole with
|
|
1229
1229
|
* one 'invalid-view-state' diagnostic); then the dataRef canonical
|
|
1230
1230
|
* comparison — a mismatch fires the 'viewStateMismatch' event INSTEAD of
|
|
@@ -1233,7 +1233,7 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1233
1233
|
* undo/redo uses, so a restore is itself undoable. Embedded positions
|
|
1234
1234
|
* apply as a frozen fixed-equivalent (one replay-style commit, then the
|
|
1235
1235
|
* simulation pauses — a later explicit layout change or reheat unfreezes).
|
|
1236
|
-
* A state touching a
|
|
1236
|
+
* A state touching a controlled slice (or styling, once a restore
|
|
1237
1237
|
* callback exists) resolves 'missing-restore-callback' until the aggregate
|
|
1238
1238
|
* protocol is registered.
|
|
1239
1239
|
*/
|
|
@@ -1272,14 +1272,14 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1272
1272
|
* entity over ONE pinned revision — a mid-stream commit never mixes
|
|
1273
1273
|
* epochs. Closing the generator releases the pin. */
|
|
1274
1274
|
exportDataStream(scope?: 'visible' | 'accepted'): AsyncGenerator<string, void, undefined>;
|
|
1275
|
-
/** Bounded id → [x, y] map from one position readback
|
|
1275
|
+
/** Bounded id → [x, y] map from one position readback. */
|
|
1276
1276
|
exportLayout(opts?: {
|
|
1277
1277
|
limit?: number;
|
|
1278
1278
|
}): Promise<ReadonlyMap<NodeId, readonly [number, number]>>;
|
|
1279
1279
|
/** Memory-bounded `{"id","x","y"}` JSONL over one pinned readback. */
|
|
1280
1280
|
exportLayoutStream(): AsyncGenerator<string, void, undefined>;
|
|
1281
1281
|
/**
|
|
1282
|
-
*
|
|
1282
|
+
* legend surface: the active Scale on a styling channel plus its
|
|
1283
1283
|
* resolved domain (sequential/diverging — resolved through the SAME frozen
|
|
1284
1284
|
* DomainStore coordinate the projection uses) or categorical legend rows
|
|
1285
1285
|
* (declared-domain order first including empty categories, then extra seen
|
|
@@ -1288,9 +1288,9 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1288
1288
|
*/
|
|
1289
1289
|
getScaleInfo(channel: 'nodeColor' | 'nodeSize'): ScaleChannelInfo<N> | null;
|
|
1290
1290
|
/**
|
|
1291
|
-
*
|
|
1292
|
-
* public million-entry Map). Null for unknown ids
|
|
1293
|
-
* values; lazily computes the degree family on first use.
|
|
1291
|
+
* metric read for one node id via the core id→index map (never a
|
|
1292
|
+
* public million-entry Map). Null for unknown ids, unknown metrics, and
|
|
1293
|
+
* null values; lazily computes the degree family on first use.
|
|
1294
1294
|
*/
|
|
1295
1295
|
getMetricValue(metric: MetricName, id: NodeId): number | null;
|
|
1296
1296
|
getRevisions(): Revisions;
|
|
@@ -1300,54 +1300,54 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1300
1300
|
* way a hover/selection consumer resolves `store.hover.edgeId` or
|
|
1301
1301
|
* `selection.edgeIds` to real records. Undefined for unknown ids. */
|
|
1302
1302
|
getEdge(id: EdgeId): AcceptedEdge<E> | undefined;
|
|
1303
|
-
/** Scene ids that are
|
|
1303
|
+
/** Scene ids that are mask-visible (scope ∧ mask), scene order. */
|
|
1304
1304
|
getVisibleNodeIds(): readonly NodeId[];
|
|
1305
|
-
/** Scene roster: scope applied, mask NOT applied — the
|
|
1305
|
+
/** Scene roster: scope applied, mask NOT applied — the navigator's
|
|
1306
1306
|
* entry list, which must still LIST masked/hidden nodes and expose their
|
|
1307
1307
|
* state in text rather than dropping them. */
|
|
1308
1308
|
getSceneNodeIds(): readonly NodeId[];
|
|
1309
|
-
/**
|
|
1309
|
+
/** accessibility config stash (navigator/live-region consumers). */
|
|
1310
1310
|
getAccessibility(): AccessibilityConfig<N> | undefined;
|
|
1311
1311
|
}
|
|
1312
|
-
/**
|
|
1312
|
+
/** consecutive same-dimension brush moves within this window merge
|
|
1313
1313
|
* into one history entry (scrub/drag coalescing). */
|
|
1314
1314
|
declare const BRUSH_HISTORY_COALESCE_MS = 500;
|
|
1315
|
-
/**
|
|
1315
|
+
/** timeline defaults. */
|
|
1316
1316
|
declare const TIMELINE_TICK_MS_DEFAULT = 100;
|
|
1317
1317
|
declare const TIMELINE_STEP_DEFAULT = 0.01;
|
|
1318
|
-
/**
|
|
1318
|
+
/** search defaults. */
|
|
1319
1319
|
declare const SEARCH_LIMIT_DEFAULT = 20;
|
|
1320
1320
|
declare const SEARCH_CACHE_LIMIT = 32;
|
|
1321
1321
|
declare function createGraphInstance<N = Record<string, unknown>, E = Record<string, unknown>>(opts: CreateGraphInstanceOptions<N, E>): GraphInstance<N, E>;
|
|
1322
1322
|
|
|
1323
1323
|
/**
|
|
1324
|
-
*
|
|
1324
|
+
* DOM label lane — pure candidate selection.
|
|
1325
1325
|
*
|
|
1326
1326
|
* `selectLabelCandidates` is a pure, deterministic ranking function: no engine,
|
|
1327
1327
|
* no DOM, no store. The instance-side overlay scheduler calls it on THROTTLED
|
|
1328
|
-
* re-rank triggers only (viewport idle, model change, config change, settle)
|
|
1328
|
+
* re-rank triggers only (viewport idle, model change, config change, settle)
|
|
1329
1329
|
* NEVER per frame. Positions are the reconciler's CPU cache (space coords);
|
|
1330
1330
|
* per-frame work elsewhere is a pure O(k) projection of the winners.
|
|
1331
1331
|
*
|
|
1332
|
-
* Selection rules
|
|
1333
|
-
*
|
|
1334
|
-
*
|
|
1335
|
-
*
|
|
1336
|
-
*
|
|
1337
|
-
*
|
|
1338
|
-
*
|
|
1339
|
-
*
|
|
1340
|
-
*
|
|
1341
|
-
*
|
|
1342
|
-
*
|
|
1343
|
-
*
|
|
1332
|
+
* Selection rules:
|
|
1333
|
+
* - Zoom-LOD: below `minZoom` the lane is empty EXCEPT `showFor` ids, which
|
|
1334
|
+
* bypass the zoom gate but stay viewport-culled.
|
|
1335
|
+
* - `showFor` claims capacity FIRST in accepted-base order. When the
|
|
1336
|
+
* in-viewport `showFor` set alone exceeds capacity k, accepted-base order
|
|
1337
|
+
* wins deterministically and `overloadCount` reports the omissions (one
|
|
1338
|
+
* `label-overload` diagnostic upstream — no winner churn).
|
|
1339
|
+
* - Remaining capacity fills with viewport-visible nodes ranked by
|
|
1340
|
+
* `getWeight` (else degree), ties broken by accepted-base order.
|
|
1341
|
+
* - Visibility comes from the engine's `pointsInRect` when available, else a
|
|
1342
|
+
* CPU cull of cached positions through the viewport transform. Nodes with
|
|
1343
|
+
* unknown (NaN) cached positions are unplaceable on the CPU path.
|
|
1344
1344
|
*/
|
|
1345
1345
|
|
|
1346
1346
|
/** A capacity winner before per-frame projection assigns screen coordinates. */
|
|
1347
1347
|
type LabelCandidate = Omit<LabelPlacement, 'x' | 'y'>;
|
|
1348
|
-
/**
|
|
1348
|
+
/** default ranked-candidate cap. */
|
|
1349
1349
|
declare const LABEL_MAX_VISIBLE_DEFAULT = 64;
|
|
1350
|
-
/**
|
|
1350
|
+
/** policy maximum for `maxVisible`. */
|
|
1351
1351
|
declare const LABEL_MAX_VISIBLE_CAP = 1024;
|
|
1352
1352
|
interface LabelCandidateViewport {
|
|
1353
1353
|
zoom: number;
|
|
@@ -1360,7 +1360,7 @@ interface SelectLabelCandidatesArgs<N = Record<string, unknown>> {
|
|
|
1360
1360
|
scene: RenderScene;
|
|
1361
1361
|
/**
|
|
1362
1362
|
* Accepted nodes in accepted-base order. Under the 'rebuild' index policy
|
|
1363
|
-
*
|
|
1363
|
+
* scene index i IS accepted-base position i, so `nodes[i]` is the
|
|
1364
1364
|
* node behind `scene.idByIndex[i]`.
|
|
1365
1365
|
*/
|
|
1366
1366
|
nodes: readonly GraphNode<N>[];
|
|
@@ -1385,11 +1385,11 @@ interface LabelCandidateResult {
|
|
|
1385
1385
|
declare function selectLabelCandidates<N = Record<string, unknown>>(args: SelectLabelCandidatesArgs<N>): LabelCandidateResult;
|
|
1386
1386
|
|
|
1387
1387
|
/**
|
|
1388
|
-
*
|
|
1388
|
+
* CSR adjacency over columnar link buffers.
|
|
1389
1389
|
*
|
|
1390
|
-
* Pure, engine-free helpers shared by local expansion
|
|
1391
|
-
* dirtying
|
|
1392
|
-
*
|
|
1390
|
+
* Pure, engine-free helpers shared by local expansion, incident-edge
|
|
1391
|
+
* dirtying, and the engine-facing `neighborIndices` interaction helper.
|
|
1392
|
+
* Build is O(L) via two counting passes over the flat
|
|
1393
1393
|
* `[src0, tgt0, src1, tgt1, …]` link buffer — no comparison sort and no
|
|
1394
1394
|
* per-link allocation (the only allocations are the CSR arrays plus one
|
|
1395
1395
|
* point-sized cursor array).
|
|
@@ -1420,9 +1420,9 @@ declare function buildAdjacency(links: Uint32Array, pointCount: number): Adjacen
|
|
|
1420
1420
|
*/
|
|
1421
1421
|
declare function neighborsOf(adj: Adjacency, index: number): Uint32Array;
|
|
1422
1422
|
/**
|
|
1423
|
-
* CSR INCIDENCE: point → incident EDGE SLOTS
|
|
1423
|
+
* CSR INCIDENCE: point → incident EDGE SLOTS. The plain Adjacency
|
|
1424
1424
|
* stores neighbor points, which cannot drive incident-edge dirtying — the
|
|
1425
|
-
*
|
|
1425
|
+
* delta cascade needs the edge slots themselves. Same two counting
|
|
1426
1426
|
* passes and the same conventions: undirected (each edge listed under both
|
|
1427
1427
|
* endpoints), parallel edges repeat, and a self-loop lists its edge slot
|
|
1428
1428
|
* twice under its point — consumers' per-edge operations are idempotent, so
|
|
@@ -1437,26 +1437,26 @@ interface Incidence {
|
|
|
1437
1437
|
}
|
|
1438
1438
|
|
|
1439
1439
|
/**
|
|
1440
|
-
*
|
|
1440
|
+
* CPU link-pick fallback: a uniform grid over ALL links in SPACE
|
|
1441
1441
|
* coordinates (cell ≈ median link length), used when the engine lacks native
|
|
1442
1442
|
* `linkAt` (`capabilities.linkPicking === false`). Backs
|
|
1443
|
-
* `onEdgeClick`/`onEdgeHover` via the
|
|
1443
|
+
* `onEdgeClick`/`onEdgeHover` via the index→edge mapping.
|
|
1444
1444
|
*
|
|
1445
1445
|
* Coordinate contract: everything here is SPACE coordinates. Callers invert
|
|
1446
1446
|
* the affine `Viewport` (space = (screen − center)/zoom + [x, y]) and pass a
|
|
1447
1447
|
* space-unit tolerance (`tolerancePx / zoom`).
|
|
1448
1448
|
*
|
|
1449
1449
|
* Invalidation contract (normative — wave 2 enforces arming):
|
|
1450
|
-
* - The grid indexes a POSITION SNAPSHOT (the
|
|
1451
|
-
*
|
|
1452
|
-
*
|
|
1453
|
-
*
|
|
1454
|
-
*
|
|
1450
|
+
* - The grid indexes a POSITION SNAPSHOT (the `posBuf` CPU mirror as of
|
|
1451
|
+
* the build call). It NEVER observes live simulation positions: while the
|
|
1452
|
+
* simulation is hot (alpha above threshold) or an animated `setLayout`
|
|
1453
|
+
* transition runs, picking is DISARMED by the caller; on settle one
|
|
1454
|
+
* per-event `getPositions` refreshes the mirror and the grid is rebuilt.
|
|
1455
1455
|
* - Rebuild ONLY on structural change (link buffer changed) or position
|
|
1456
|
-
*
|
|
1457
|
-
*
|
|
1458
|
-
*
|
|
1459
|
-
*
|
|
1456
|
+
* sync. The grid is invariant under camera moves and visibility-mask
|
|
1457
|
+
* changes: the mask is applied per candidate at QUERY time via the
|
|
1458
|
+
* optional `visible` callback (pure pass-through until mask wiring supplies the real
|
|
1459
|
+
* mask through it).
|
|
1460
1460
|
*
|
|
1461
1461
|
* Build uses two counting-sort passes into CSR grid arrays (`cellOffsets`,
|
|
1462
1462
|
* `cellLinkIds`). Each segment is inserted with a supercover grid traversal,
|
|
@@ -1464,11 +1464,11 @@ interface Incidence {
|
|
|
1464
1464
|
* every cell in its O(cols * rows) axis-aligned bounding box. There is no
|
|
1465
1465
|
* comparison sort or per-link allocation beyond the CSR arrays (plus
|
|
1466
1466
|
* O(cells) cursor and O(links) query stamps). Links with a non-finite endpoint
|
|
1467
|
-
* (NaN-tombstoned points
|
|
1467
|
+
* (NaN-tombstoned points) are excluded and can never be returned. A hard
|
|
1468
1468
|
* CSR-entry cap deterministically degrades pathological builds to exact O(L)
|
|
1469
1469
|
* query scans with no cell-link payload.
|
|
1470
1470
|
*/
|
|
1471
|
-
/**
|
|
1471
|
+
/** visibility mask applied per candidate at query time. */
|
|
1472
1472
|
type LinkVisibilityMask = (linkIndex: number) => boolean;
|
|
1473
1473
|
/**
|
|
1474
1474
|
* Read-only view of the built grid (diagnostics/tests). The typed arrays are
|
|
@@ -1514,8 +1514,8 @@ declare class LinkPickIndex {
|
|
|
1514
1514
|
*/
|
|
1515
1515
|
build(positions: Float32Array, links: Uint32Array): void;
|
|
1516
1516
|
/**
|
|
1517
|
-
* Incremental build: yields whenever `now
|
|
1518
|
-
* scheduler can spread the work across idle frames
|
|
1517
|
+
* Incremental build: yields whenever `now − sliceStart ≥ budgetMs` so a
|
|
1518
|
+
* scheduler can spread the work across idle frames.
|
|
1519
1519
|
* Pure generator — no rAF/timers here; the caller owns scheduling.
|
|
1520
1520
|
*
|
|
1521
1521
|
* The previous grid stays armed and queryable until the new one commits on
|
|
@@ -1528,7 +1528,7 @@ declare class LinkPickIndex {
|
|
|
1528
1528
|
* Nearest link within `tolerance` (space units) of the space point
|
|
1529
1529
|
* `(x, y)`, or null. Scans the candidate cells covering the tolerance
|
|
1530
1530
|
* disc's bounding box (typically the 3×3 neighborhood; more when the
|
|
1531
|
-
* tolerance exceeds the cell size), applies the optional
|
|
1531
|
+
* tolerance exceeds the cell size), applies the optional visibility
|
|
1532
1532
|
* mask per candidate, then runs the exact point→segment distance test.
|
|
1533
1533
|
* Nearest wins; exact-distance ties break toward the LOWER link index.
|
|
1534
1534
|
* Returns null while unbuilt/invalidated (picking disarmed).
|
|
@@ -1537,45 +1537,45 @@ declare class LinkPickIndex {
|
|
|
1537
1537
|
}
|
|
1538
1538
|
|
|
1539
1539
|
/**
|
|
1540
|
-
*
|
|
1540
|
+
* Edge-picking facade.
|
|
1541
1541
|
*
|
|
1542
1542
|
* The core commits to ONE route per mount session, read from
|
|
1543
1543
|
* `engine.capabilities.linkPicking` at engine-ready time — never method
|
|
1544
1544
|
* sniffing, and never re-evaluated (a capability record mutated after ready
|
|
1545
1545
|
* changes nothing):
|
|
1546
1546
|
*
|
|
1547
|
-
*
|
|
1548
|
-
*
|
|
1549
|
-
*
|
|
1550
|
-
*
|
|
1551
|
-
*
|
|
1552
|
-
*
|
|
1553
|
-
*
|
|
1554
|
-
*
|
|
1555
|
-
* Arming protocol
|
|
1556
|
-
*
|
|
1557
|
-
*
|
|
1558
|
-
*
|
|
1559
|
-
*
|
|
1560
|
-
*
|
|
1561
|
-
*
|
|
1562
|
-
*
|
|
1563
|
-
*
|
|
1564
|
-
*
|
|
1565
|
-
*
|
|
1566
|
-
*
|
|
1567
|
-
*
|
|
1568
|
-
*
|
|
1569
|
-
*
|
|
1570
|
-
*
|
|
1571
|
-
* Tolerance
|
|
1547
|
+
* - 'native': the adapter delivers `onLinkClick`/`onLinkHover` host events
|
|
1548
|
+
* itself; the facade is inert (arm/disarm/queries no-op and
|
|
1549
|
+
* `pickLinkAt` returns null) and the instance maps link indices
|
|
1550
|
+
* to typed edges straight off the host events.
|
|
1551
|
+
* - 'fallback': the instance samples the pointer on the shared throttle
|
|
1552
|
+
* cadence and resolves hits through a `LinkPickIndex` uniform
|
|
1553
|
+
* grid that is armed ONLY while the simulation is settled.
|
|
1554
|
+
*
|
|
1555
|
+
* Arming protocol for the fallback route:
|
|
1556
|
+
* - `arm(positions, links)` runs on simulation settle (fed by one per-event
|
|
1557
|
+
* `getPositions` readback) and on structural commits that do NOT restart
|
|
1558
|
+
* the simulation (fixed layout). The grid builds CHUNKED via
|
|
1559
|
+
* `LinkPickIndex.buildChunked` under a per-slice time budget so large link
|
|
1560
|
+
* sets never block; small builds complete synchronously in the first
|
|
1561
|
+
* slice. While an initial build is in flight queries return null; a
|
|
1562
|
+
* position-sync REBUILD keeps answering from the previous grid until the
|
|
1563
|
+
* new one commits.
|
|
1564
|
+
* - `disarm` runs on any commit that restarts the simulation: targets are
|
|
1565
|
+
* moving and the position mirror is stale, so queries return null until
|
|
1566
|
+
* the next settle re-arms.
|
|
1567
|
+
* - Rebuilds happen ONLY on structural change or position sync — the grid is
|
|
1568
|
+
* invariant under camera moves and mask changes (the visibility mask
|
|
1569
|
+
* is applied per candidate at query time; initially a pass-through stub).
|
|
1570
|
+
*
|
|
1571
|
+
* Tolerance: a query converts screen px to space units via
|
|
1572
1572
|
* `screenToSpace` of two points `EDGE_PICK_TOLERANCE_PX` apart, then uses
|
|
1573
1573
|
* `max(4px, half the median link width)` scaled by that factor.
|
|
1574
1574
|
*/
|
|
1575
1575
|
|
|
1576
1576
|
type EdgePickRoute = 'native' | 'fallback';
|
|
1577
1577
|
/** Screen-space pick tolerance floor, and the probe distance used to measure
|
|
1578
|
-
* the screen→space scale
|
|
1578
|
+
* the screen→space scale. */
|
|
1579
1579
|
declare const EDGE_PICK_TOLERANCE_PX = 4;
|
|
1580
1580
|
/**
|
|
1581
1581
|
* Median link width in px from a projected linkWidth buffer (deterministic
|
|
@@ -1588,9 +1588,9 @@ interface EdgePickingFacadeOptions {
|
|
|
1588
1588
|
route: EdgePickRoute;
|
|
1589
1589
|
/** Affine screen→space conversion; null = conversion unavailable. */
|
|
1590
1590
|
screenToSpace: (p: readonly [number, number]) => readonly [number, number] | null;
|
|
1591
|
-
/** Median link width in px for the tolerance floor; default
|
|
1591
|
+
/** Median link width in px for the tolerance floor; default => 0. */
|
|
1592
1592
|
medianLinkWidthPx?: () => number;
|
|
1593
|
-
/**
|
|
1593
|
+
/** visibility mask pass-through stub (mutable via the setter). */
|
|
1594
1594
|
linkVisible?: LinkVisibilityMask;
|
|
1595
1595
|
/** Chunked-build slice budget in ms. */
|
|
1596
1596
|
buildBudgetMs?: number;
|
|
@@ -1622,7 +1622,7 @@ declare class EdgePickingFacade {
|
|
|
1622
1622
|
arm(positions: Float32Array, links: Uint32Array): void;
|
|
1623
1623
|
/** Simulation went hot: drop the grid; queries null until the next settle. */
|
|
1624
1624
|
disarm(): void;
|
|
1625
|
-
/**
|
|
1625
|
+
/** mask pass-through stub: applied per candidate at query time. */
|
|
1626
1626
|
setLinkVisibilityMask(mask: LinkVisibilityMask | null): void;
|
|
1627
1627
|
/**
|
|
1628
1628
|
* Nearest link index within tolerance of a SCREEN point, or null when: on
|
|
@@ -1646,8 +1646,8 @@ declare class EdgePickingFacade {
|
|
|
1646
1646
|
type EdgePairCounters = Map<NodeId, Map<NodeId, number>>;
|
|
1647
1647
|
|
|
1648
1648
|
/**
|
|
1649
|
-
*
|
|
1650
|
-
* instance-local acceptance queue
|
|
1649
|
+
* revisioned ingestion — pure session/overlay bookkeeping plus the
|
|
1650
|
+
* instance-local acceptance queue.
|
|
1651
1651
|
*
|
|
1652
1652
|
* Everything here is data-structure work: no store, no engine, no DOM. The
|
|
1653
1653
|
* GraphInstance wires these helpers into its publication path (instance.ts).
|
|
@@ -1655,7 +1655,7 @@ type EdgePairCounters = Map<NodeId, Map<NodeId, number>>;
|
|
|
1655
1655
|
* Ordering model: every admission takes a monotonically increasing ticket from
|
|
1656
1656
|
* the instance's AcceptanceQueue. Overlay rows are stamped with their
|
|
1657
1657
|
* admission ticket, and the merge folds base + overlays strictly in ticket
|
|
1658
|
-
* order — arrival at the queue IS the global admission order
|
|
1658
|
+
* order — arrival at the queue IS the global admission order.
|
|
1659
1659
|
*/
|
|
1660
1660
|
|
|
1661
1661
|
/** Byte backpressure budget default: 8 MiB of admitted-but-unflushed payload. */
|
|
@@ -1687,7 +1687,7 @@ interface RowTally {
|
|
|
1687
1687
|
count: number;
|
|
1688
1688
|
samples: string[];
|
|
1689
1689
|
}
|
|
1690
|
-
/** Per-session staging tallies; surfaced as diagnostics ONLY at commit
|
|
1690
|
+
/** Per-session staging tallies; surfaced as diagnostics ONLY at commit. */
|
|
1691
1691
|
interface StagingTallies {
|
|
1692
1692
|
invalidNodes: RowTally;
|
|
1693
1693
|
duplicateNodes: RowTally;
|
|
@@ -1712,11 +1712,11 @@ interface SessionContribution<N = Record<string, unknown>, E = Record<string, un
|
|
|
1712
1712
|
readonly edges: StampedEdge<E>[];
|
|
1713
1713
|
readonly nodeIds: Set<NodeId>;
|
|
1714
1714
|
readonly edgeIds: Set<string>;
|
|
1715
|
-
/** Exact ordered endpoint tuple → next synthesized-parallel-edge k
|
|
1715
|
+
/** Exact ordered endpoint tuple → next synthesized-parallel-edge k. */
|
|
1716
1716
|
readonly pairCounters: EdgePairCounters;
|
|
1717
1717
|
/** Public prefix lengths: rows before these counts have been published
|
|
1718
1718
|
* (progressive flush / commit); appends only grow the arrays, so a prefix
|
|
1719
|
-
* is sufficient. Atomic sessions stay at 0 until commit
|
|
1719
|
+
* is sufficient. Atomic sessions stay at 0 until commit. */
|
|
1720
1720
|
publicNodeCount: number;
|
|
1721
1721
|
publicEdgeCount: number;
|
|
1722
1722
|
}
|
|
@@ -1728,8 +1728,8 @@ interface StageResult {
|
|
|
1728
1728
|
/**
|
|
1729
1729
|
* Validate and stage one batch's rows into a session contribution.
|
|
1730
1730
|
*
|
|
1731
|
-
*
|
|
1732
|
-
* before nodes
|
|
1731
|
+
* Object-lane-equivalent row validation WITHOUT endpoint checks — edges may arrive
|
|
1732
|
+
* before nodes: they occupy their eventual edge records here; endpoint
|
|
1733
1733
|
* resolution happens at merge. Within a session, duplicate ids are
|
|
1734
1734
|
* first-wins-dropped (tallied for commit diagnostics); cross-session
|
|
1735
1735
|
* collisions are the merge's shadowing concern, not staging's.
|
|
@@ -1745,7 +1745,7 @@ interface MergeBase<N = Record<string, unknown>, E = Record<string, unknown>> {
|
|
|
1745
1745
|
/** Endpoint-resolved base edges. */
|
|
1746
1746
|
edges: readonly AcceptedEdge<E>[];
|
|
1747
1747
|
/** Replace-session bases retain edge records whose endpoints were missing
|
|
1748
|
-
* at commit; a later overlay node can still resolve them
|
|
1748
|
+
* at commit; a later overlay node can still resolve them. */
|
|
1749
1749
|
pendingEdges: readonly StampedEdge<E>[];
|
|
1750
1750
|
diagnostics: readonly GraphDiagnostic[];
|
|
1751
1751
|
}
|
|
@@ -1761,7 +1761,7 @@ interface MergeResult<N = Record<string, unknown>, E = Record<string, unknown>>
|
|
|
1761
1761
|
duplicateEdgeSamples: readonly string[];
|
|
1762
1762
|
/** overlayId (or BASE_PENDING_KEY) → edges still awaiting an endpoint. The
|
|
1763
1763
|
* pending-endpoint index: these occupy edge records but never enter the
|
|
1764
|
-
* engine link buffer
|
|
1764
|
+
* engine link buffer. */
|
|
1765
1765
|
pendingBySource: ReadonlyMap<string, number>;
|
|
1766
1766
|
pendingEdgeCount: number;
|
|
1767
1767
|
}
|
|
@@ -1776,15 +1776,15 @@ declare function baseFromContribution<N, E>(datasetKey: string, sourceRevision:
|
|
|
1776
1776
|
* Merge the accepted base with every overlay contribution's PUBLIC rows.
|
|
1777
1777
|
*
|
|
1778
1778
|
* - Node collisions: earliest admission (ticket order; base first) wins.
|
|
1779
|
-
*
|
|
1780
|
-
*
|
|
1781
|
-
*
|
|
1779
|
+
* Later rows stay retained-but-shadowed in their contribution and are
|
|
1780
|
+
* tallied for the single count-aggregated 'overlay-node-shadowed' info
|
|
1781
|
+
* diagnostic.
|
|
1782
1782
|
* - Edge ids: first admission wins; later same-id records are dropped
|
|
1783
|
-
*
|
|
1784
|
-
*
|
|
1785
|
-
*
|
|
1786
|
-
*
|
|
1787
|
-
*
|
|
1783
|
+
* (tallied). An edge whose endpoints are not all present in the merged
|
|
1784
|
+
* node set is pending — it occupies its record and the pending-endpoint
|
|
1785
|
+
* accounting but is excluded from `accepted.edges` (and therefore from the
|
|
1786
|
+
* engine link buffer). Endpoints arriving later resolve it on the next
|
|
1787
|
+
* merge (same or different batch/overlay).
|
|
1788
1788
|
*
|
|
1789
1789
|
* Deterministic: output depends only on the base and the stamped rows.
|
|
1790
1790
|
*/
|
|
@@ -1793,20 +1793,20 @@ declare function mergeModel<N, E>(base: MergeBase<N, E>, overlays: readonly Sess
|
|
|
1793
1793
|
declare function mergeDiagnostics(merge: MergeResult<unknown, unknown>): GraphDiagnostic[];
|
|
1794
1794
|
/**
|
|
1795
1795
|
* Commit-time diagnostics for one session: staging tallies plus the dangling
|
|
1796
|
-
* (still-pending-endpoint) count — emitted ONLY at session commit
|
|
1796
|
+
* (still-pending-endpoint) count — emitted ONLY at session commit.
|
|
1797
1797
|
*/
|
|
1798
1798
|
declare function sessionCommitDiagnostics(tallies: StagingTallies, danglingCount: number, danglingSamples?: readonly string[]): GraphDiagnostic[];
|
|
1799
1799
|
|
|
1800
1800
|
/**
|
|
1801
|
-
*
|
|
1801
|
+
* hard subgraph scope — pure resolution over the accepted base.
|
|
1802
1802
|
*
|
|
1803
1803
|
* `resolveScope` turns a `SubgraphSpec` into the exact node/edge subset the
|
|
1804
1804
|
* reconciler should be fed: seeds validated against the accepted base
|
|
1805
1805
|
* (unknown ids dropped, duplicates collapsed), optional `hops` expansion via
|
|
1806
|
-
* BFS over the
|
|
1807
|
-
* default expansion service walks this same index
|
|
1808
|
-
* through `cascadeEdges`, the single
|
|
1809
|
-
* survives iff BOTH endpoints survive) that
|
|
1806
|
+
* BFS over the CSR adjacency of the ACCEPTED BASE (not the scene — the
|
|
1807
|
+
* default expansion service walks this same index), and edges cascaded
|
|
1808
|
+
* through `cascadeEdges`, the single edge-survival primitive (an edge
|
|
1809
|
+
* survives iff BOTH endpoints survive) that soft masks reuse.
|
|
1810
1810
|
*
|
|
1811
1811
|
* Everything here is synchronous and engine-free. The async
|
|
1812
1812
|
* `ExpansionService` seam (./services) exists for `expandNode`; `hops`
|
|
@@ -1814,21 +1814,21 @@ declare function sessionCommitDiagnostics(tallies: StagingTallies, danglingCount
|
|
|
1814
1814
|
*/
|
|
1815
1815
|
|
|
1816
1816
|
/**
|
|
1817
|
-
* THE edge-cascade primitive
|
|
1817
|
+
* THE edge-cascade primitive: an edge survives iff BOTH of its
|
|
1818
1818
|
* endpoints survive. O(E); the only allocation is the output array, which
|
|
1819
1819
|
* holds references to the input edge objects (never copies).
|
|
1820
1820
|
*
|
|
1821
|
-
* Exported for reuse by
|
|
1821
|
+
* Exported for reuse by soft masks and any other subset producer.
|
|
1822
1822
|
*/
|
|
1823
1823
|
declare function cascadeEdges<E>(edges: readonly AcceptedEdge<E>[], survives: (id: NodeId) => boolean): AcceptedEdge<E>[];
|
|
1824
1824
|
/**
|
|
1825
|
-
* Builds the
|
|
1825
|
+
* Builds the CSR adjacency of an accepted base: endpoints are positions
|
|
1826
1826
|
* in `accepted.nodes` (accepted-base order). Every accepted edge has resolved
|
|
1827
|
-
* endpoints by contract
|
|
1827
|
+
* endpoints by contract, so this cannot throw on
|
|
1828
1828
|
* a well-formed `AcceptedGraph`.
|
|
1829
1829
|
*/
|
|
1830
1830
|
declare function buildAcceptedAdjacency(accepted: AcceptedGraph<unknown, unknown>): Adjacency;
|
|
1831
|
-
/** Output of `resolveScope`: the exact subset to feed the reconciler
|
|
1831
|
+
/** Output of `resolveScope`: the exact subset to feed the reconciler. */
|
|
1832
1832
|
interface ResolvedScope<N = Record<string, unknown>, E = Record<string, unknown>> {
|
|
1833
1833
|
/** Surviving node ids (seeds + hop expansion), membership-query form. */
|
|
1834
1834
|
nodeIds: ReadonlySet<NodeId>;
|
|
@@ -1838,31 +1838,31 @@ interface ResolvedScope<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1838
1838
|
edges: readonly AcceptedEdge<E>[];
|
|
1839
1839
|
}
|
|
1840
1840
|
/**
|
|
1841
|
-
* Resolves a hard-scope spec against the accepted base
|
|
1841
|
+
* Resolves a hard-scope spec against the accepted base.
|
|
1842
1842
|
*
|
|
1843
1843
|
* - `spec.seedIds` are validated against `accepted.nodeIndex`: ids unknown to
|
|
1844
|
-
*
|
|
1844
|
+
* the accepted base are dropped, duplicates collapse to one seed.
|
|
1845
1845
|
* - `spec.hops` (default 0; negative/non-finite values clamp to 0, fractions
|
|
1846
|
-
*
|
|
1847
|
-
*
|
|
1848
|
-
*
|
|
1846
|
+
* floor) expands the survivor set by BFS over the accepted-base adjacency.
|
|
1847
|
+
* This is the synchronous local path — the default `ExpansionService`
|
|
1848
|
+
* walks the same index for `expandNode`.
|
|
1849
1849
|
* - `adjacency` may be a caller-cached `buildAcceptedAdjacency(accepted)`
|
|
1850
|
-
*
|
|
1851
|
-
*
|
|
1850
|
+
* result; pass `null` to have one built on demand (only when `hops > 0`
|
|
1851
|
+
* and there are surviving seeds — hop-0 resolution never builds it).
|
|
1852
1852
|
* - Output preserves accepted-base order for both nodes and edges; edges are
|
|
1853
|
-
*
|
|
1853
|
+
* cascaded through {@link cascadeEdges}.
|
|
1854
1854
|
*/
|
|
1855
1855
|
declare function resolveScope<N, E>(accepted: AcceptedGraph<N, E>, spec: SubgraphSpec, adjacency: Adjacency | null): ResolvedScope<N, E>;
|
|
1856
1856
|
|
|
1857
1857
|
/**
|
|
1858
|
-
*
|
|
1858
|
+
* revision-aware services — pure sequencing/admission/caching helpers
|
|
1859
1859
|
* plus the built-in local expansion service.
|
|
1860
1860
|
*
|
|
1861
1861
|
* The correctness gate is `admitServiceResult`: a result is admitted only if
|
|
1862
1862
|
* every revision dimension the service DECLARED is unchanged since the call
|
|
1863
1863
|
* was issued; undeclared dimensions never invalidate (so e.g. the local
|
|
1864
1864
|
* expansion service, declaring only 'source', survives unrelated model/scope
|
|
1865
|
-
* advances from other overlay publications). Abort is an optimization
|
|
1865
|
+
* advances from other overlay publications). Abort is an optimization
|
|
1866
1866
|
* admission is the gate.
|
|
1867
1867
|
*
|
|
1868
1868
|
* Cache keys include service identity, canonical-JSON request parameters,
|
|
@@ -1872,7 +1872,7 @@ declare function resolveScope<N, E>(accepted: AcceptedGraph<N, E>, spec: Subgrap
|
|
|
1872
1872
|
* these primitives to its acceptance queue.
|
|
1873
1873
|
*/
|
|
1874
1874
|
|
|
1875
|
-
/** Current value of each
|
|
1875
|
+
/** Current value of each revision dimension (a Revisions subset). */
|
|
1876
1876
|
interface RevisionSnapshot {
|
|
1877
1877
|
source: number | string | null;
|
|
1878
1878
|
model: number;
|
|
@@ -1896,13 +1896,13 @@ interface RequestContextHandle {
|
|
|
1896
1896
|
abort(reason?: unknown): void;
|
|
1897
1897
|
}
|
|
1898
1898
|
/**
|
|
1899
|
-
* Builds the `RequestContext` a service call receives
|
|
1899
|
+
* Builds the `RequestContext` a service call receives: dataset,
|
|
1900
1900
|
* the three revision dimensions at issue time, a request id, and a
|
|
1901
1901
|
* cancellation signal owned by the returned controller.
|
|
1902
1902
|
*/
|
|
1903
1903
|
declare function createRequestContext(args: CreateRequestContextArgs): RequestContextHandle;
|
|
1904
1904
|
interface AdmitServiceResultArgs {
|
|
1905
|
-
/** The service's `revisionDependencies
|
|
1905
|
+
/** The service's `revisionDependencies`. */
|
|
1906
1906
|
declared: readonly RevisionDimension[];
|
|
1907
1907
|
/** Revision values when the request was issued. */
|
|
1908
1908
|
at: RevisionSnapshot;
|
|
@@ -1910,7 +1910,7 @@ interface AdmitServiceResultArgs {
|
|
|
1910
1910
|
now: RevisionSnapshot;
|
|
1911
1911
|
}
|
|
1912
1912
|
/**
|
|
1913
|
-
*
|
|
1913
|
+
* stale-result rule: admit a service result iff EVERY declared revision
|
|
1914
1914
|
* dimension is unchanged between issue and admission. Undeclared dimensions
|
|
1915
1915
|
* never invalidate. Declaring nothing means the result is admissible under
|
|
1916
1916
|
* any drift; declaring all three restores strict point-in-model semantics.
|
|
@@ -1921,13 +1921,13 @@ interface ServiceCacheKeyArgs {
|
|
|
1921
1921
|
/** Request parameters (JSON-shaped); key order is canonicalized away. */
|
|
1922
1922
|
params: unknown;
|
|
1923
1923
|
datasetKey: string;
|
|
1924
|
-
/** The service's declared revision dependencies
|
|
1924
|
+
/** The service's declared revision dependencies. */
|
|
1925
1925
|
declared: readonly RevisionDimension[];
|
|
1926
1926
|
/** Current revision values; only declared dimensions enter the key. */
|
|
1927
1927
|
revisions: RevisionSnapshot;
|
|
1928
1928
|
}
|
|
1929
1929
|
/**
|
|
1930
|
-
*
|
|
1930
|
+
* cache-key rule: service identity + canonical-JSON params +
|
|
1931
1931
|
* `datasetKey` + EXACTLY the declared revision dimensions' current values.
|
|
1932
1932
|
* Declaration-list order and params key order do not affect the key;
|
|
1933
1933
|
* undeclared revision drift never changes it.
|
|
@@ -1936,11 +1936,11 @@ declare function serviceCacheKey(args: ServiceCacheKeyArgs): string;
|
|
|
1936
1936
|
/** Accepted-base view the local service walks (thunked for lazy wiring). */
|
|
1937
1937
|
interface LocalExpansionBase<N = Record<string, unknown>, E = Record<string, unknown>> {
|
|
1938
1938
|
accepted: AcceptedGraph<N, E>;
|
|
1939
|
-
/** The
|
|
1939
|
+
/** The CSR adjacency of `accepted` (see buildAcceptedAdjacency). */
|
|
1940
1940
|
adjacency: Adjacency;
|
|
1941
1941
|
}
|
|
1942
1942
|
/**
|
|
1943
|
-
* The built-in
|
|
1943
|
+
* The built-in expansion service: walks the core's accepted-base
|
|
1944
1944
|
* adjacency — INCLUDING currently out-of-scope nodes (it reads the base, not
|
|
1945
1945
|
* the scene) — with zero config and zero network. Declares
|
|
1946
1946
|
* `revisionDependencies: ['source']`, so unrelated model/scope advances
|
|
@@ -1954,7 +1954,7 @@ interface LocalExpansionBase<N = Record<string, unknown>, E = Record<string, unk
|
|
|
1954
1954
|
* admission gate is authoritative).
|
|
1955
1955
|
*
|
|
1956
1956
|
* The response is the CLOSED N-hop neighborhood (seeds included, plus every
|
|
1957
|
-
* edge between returned nodes);
|
|
1957
|
+
* edge between returned nodes); the overlay merger dedupes rows
|
|
1958
1958
|
* that already exist, so returning already-known seeds is correct.
|
|
1959
1959
|
*/
|
|
1960
1960
|
declare function createLocalExpansionService<N = Record<string, unknown>, E = Record<string, unknown>>(getBase: () => LocalExpansionBase<N, E>): ExpansionService<N, E>;
|
|
@@ -1967,7 +1967,7 @@ type RegisterExpansionResult = {
|
|
|
1967
1967
|
onto: string;
|
|
1968
1968
|
};
|
|
1969
1969
|
/**
|
|
1970
|
-
* Pure in-flight-expansion ledger
|
|
1970
|
+
* Pure in-flight-expansion ledger: within one valid scope revision a
|
|
1971
1971
|
* second `expandNode(id)` while one is in flight coalesces into the pending
|
|
1972
1972
|
* call; expansions of DISTINCT ids run (and complete) concurrently and
|
|
1973
1973
|
* independently. `retractExpansion(id)` uses `abort(id)` to drop that id's
|
|
@@ -2005,14 +2005,15 @@ declare class PendingExpansions {
|
|
|
2005
2005
|
}
|
|
2006
2006
|
|
|
2007
2007
|
/**
|
|
2008
|
-
*
|
|
2008
|
+
* history command-stack kernel.
|
|
2009
2009
|
*
|
|
2010
2010
|
* v0.7 implements the KERNEL only: value-diff commands, transactions,
|
|
2011
2011
|
* coalescing, and a bounded stack. The kernel never touches stores — it is a
|
|
2012
|
-
* pure application seam: `undo
|
|
2012
|
+
* pure application seam: `undo`/`redo` return the command list to apply
|
|
2013
2013
|
* (already inverted, in application order) and move the cursor; the instance
|
|
2014
|
-
*
|
|
2015
|
-
* Ownership-mode acknowledgement walks and view-state integration
|
|
2014
|
+
* applies commands to its slices and publishes.
|
|
2015
|
+
* Ownership-mode acknowledgement walks and view-state integration remain
|
|
2016
|
+
* instance-level concerns.
|
|
2016
2017
|
*
|
|
2017
2018
|
* Commands are `{ slice, before, after }` value diffs — never closures — so
|
|
2018
2019
|
* they are serializable and invert by swapping. The kernel freezes and stores
|
|
@@ -2032,7 +2033,7 @@ interface HistoryDepths {
|
|
|
2032
2033
|
readonly redoDepth: number;
|
|
2033
2034
|
}
|
|
2034
2035
|
interface HistoryKernelOptions {
|
|
2035
|
-
/** Stack bound; oldest entry evicted past it
|
|
2036
|
+
/** Stack bound; oldest entry evicted past it. */
|
|
2036
2037
|
limit?: number;
|
|
2037
2038
|
/** `false` = the `history: false` prop: everything is a no-op, depths stay 0. */
|
|
2038
2039
|
enabled?: boolean;
|
|
@@ -2050,7 +2051,7 @@ declare class HistoryKernel {
|
|
|
2050
2051
|
private readonly debug;
|
|
2051
2052
|
private readonly undoStack;
|
|
2052
2053
|
private readonly redoStack;
|
|
2053
|
-
/** Nested begin
|
|
2054
|
+
/** Nested begin joins the outer transaction (depth-counted). */
|
|
2054
2055
|
private txDepth;
|
|
2055
2056
|
private txCommands;
|
|
2056
2057
|
private txLabel;
|
|
@@ -2080,7 +2081,7 @@ declare class HistoryKernel {
|
|
|
2080
2081
|
* transaction. The kernel freezes and stores what it is given.
|
|
2081
2082
|
*/
|
|
2082
2083
|
record(slice: string, before: unknown, after: unknown): void;
|
|
2083
|
-
/** Close a transaction; the outermost end
|
|
2084
|
+
/** Close a transaction; the outermost end pushes one stack entry. */
|
|
2084
2085
|
end(): void;
|
|
2085
2086
|
/**
|
|
2086
2087
|
* Move the cursor back one entry and return its commands, already inverted
|
|
@@ -2110,28 +2111,28 @@ declare class HistoryKernel {
|
|
|
2110
2111
|
}
|
|
2111
2112
|
|
|
2112
2113
|
/**
|
|
2113
|
-
*
|
|
2114
|
+
* soft filtering — pure, engine-free expr evaluation,
|
|
2114
2115
|
* validation, compilation, and canonical keying. The instance feeds compiled
|
|
2115
2116
|
* filters into the SoftMask kernel (./mask); nothing here touches the store.
|
|
2116
2117
|
*
|
|
2117
|
-
* Semantics
|
|
2118
|
+
* Semantics:
|
|
2118
2119
|
* - `field` addresses `attrs[field]`; the literal field 'id' addresses the
|
|
2119
|
-
*
|
|
2120
|
-
* - The serializable expr path NEVER throws: junk shapes and
|
|
2121
|
-
*
|
|
2122
|
-
*
|
|
2123
|
-
*
|
|
2120
|
+
* entity id (it wins even when `attrs.id` exists).
|
|
2121
|
+
* - The serializable expr path NEVER throws: junk shapes and numeric-hygiene
|
|
2122
|
+
* failures (non-numeric / non-finite values under 'range') simply fail the
|
|
2123
|
+
* item. `validateFilterExpr` is the reporting channel — the instance turns
|
|
2124
|
+
* its findings into validation errors at applyHostUpdate.
|
|
2124
2125
|
* - eq/neq/in compare numbers with Object.is semantics EXCEPT that NaN is
|
|
2125
|
-
*
|
|
2126
|
-
*
|
|
2126
|
+
* never equal to anything (so ±0 are distinct and NaN ≠ NaN); everything
|
|
2127
|
+
* else compares with plain ===. No coercion, ever ('5' never equals 5).
|
|
2127
2128
|
* - Function predicates are black boxes: throws are caught and aggregated
|
|
2128
|
-
*
|
|
2129
|
-
*
|
|
2130
|
-
*
|
|
2131
|
-
*
|
|
2129
|
+
* into ONE {count, samples} result the caller converts to a single
|
|
2130
|
+
* 'filter-error' diagnostic (batching — O(categories), never O(bad
|
|
2131
|
+
* rows)). A throwing predicate FAILS OPEN: the item stays visible, so a
|
|
2132
|
+
* buggy predicate can never blank the graph.
|
|
2132
2133
|
* - Structural specs compare by canonical key (`canonicalFilterKey`) so
|
|
2133
|
-
*
|
|
2134
|
-
*
|
|
2134
|
+
* identity churn with equal structure never re-evaluates; function
|
|
2135
|
+
* predicates key by reference identity via a WeakMap-issued token.
|
|
2135
2136
|
*/
|
|
2136
2137
|
|
|
2137
2138
|
/**
|
|
@@ -2141,7 +2142,7 @@ declare class HistoryKernel {
|
|
|
2141
2142
|
declare function resolveFilterField(item: GraphNode<unknown> | AcceptedEdge<unknown>, field: string): unknown;
|
|
2142
2143
|
/**
|
|
2143
2144
|
* Evaluates one expr for one item via a field resolver. NEVER throws:
|
|
2144
|
-
* malformed shapes and
|
|
2145
|
+
* malformed shapes and numeric-hygiene failures fail the item (return false);
|
|
2145
2146
|
* a malformed operand under 'not' also fails (junk never passes by double
|
|
2146
2147
|
* negation). `range` requires a finite number value; missing bounds are
|
|
2147
2148
|
* unbounded; includeMin/includeMax default true. `is-null` matches null OR
|
|
@@ -2151,7 +2152,7 @@ declare function evaluateFilterExpr(expr: FilterExpr, resolve: (field: string) =
|
|
|
2151
2152
|
/**
|
|
2152
2153
|
* Structural checker for serializable exprs: unknown ops and malformed
|
|
2153
2154
|
* shapes are reported as `$`-rooted path strings ([] = valid). The instance
|
|
2154
|
-
* converts findings into validation errors at applyHostUpdate
|
|
2155
|
+
* converts findings into validation errors at applyHostUpdate
|
|
2155
2156
|
* evaluation itself never throws on the same junk, it just fails the item.
|
|
2156
2157
|
*/
|
|
2157
2158
|
declare function validateFilterExpr(expr: FilterExpr): string[];
|
|
@@ -2178,7 +2179,7 @@ declare function compileEdgeFilter<N = Record<string, unknown>, E = Record<strin
|
|
|
2178
2179
|
* Canonical structural key for a FilterSpec or FilterExpr: object keys are
|
|
2179
2180
|
* sorted, arrays preserve order, undefined-valued keys are omitted, and
|
|
2180
2181
|
* function predicates map to a unique reference-identity token. Two inputs
|
|
2181
|
-
* with equal keys are
|
|
2182
|
+
* with equal keys are semantically equivalent — the instance skips re-evaluation
|
|
2182
2183
|
* when the key of an incoming filter matches the active one (identity churn
|
|
2183
2184
|
* with equal structure never re-evaluates; swapping a function reference
|
|
2184
2185
|
* always does).
|
|
@@ -2186,14 +2187,14 @@ declare function compileEdgeFilter<N = Record<string, unknown>, E = Record<strin
|
|
|
2186
2187
|
declare function canonicalFilterKey(specOrExpr: unknown): string;
|
|
2187
2188
|
|
|
2188
2189
|
/**
|
|
2189
|
-
*
|
|
2190
|
+
* soft-mask kernel — failure COUNTERS, never Sets and
|
|
2190
2191
|
* never per-source bit positions (so there is no 32-dimension cap).
|
|
2191
2192
|
*
|
|
2192
2193
|
* The mask owns four Uint16 counter lanes: hideFailures and dimFailures for
|
|
2193
2194
|
* nodes AND edges. Every acquired {@link MaskSource} contributes +1 to a
|
|
2194
2195
|
* lane counter for each slot it currently fails and -1 when that slot
|
|
2195
2196
|
* re-enters. A slot is visible iff hideFailures === 0 and dimmed iff visible
|
|
2196
|
-
* AND dimFailures > 0
|
|
2197
|
+
* AND dimFailures > 0.
|
|
2197
2198
|
*
|
|
2198
2199
|
* Delta discipline: each source keeps its previous failing membership as
|
|
2199
2200
|
* per-source Uint8 flag columns plus a dense slot list, so replacing a
|
|
@@ -2203,9 +2204,9 @@ declare function canonicalFilterKey(specOrExpr: unknown): string;
|
|
|
2203
2204
|
* compares against the state at the previous drain, so a fail-then-restore
|
|
2204
2205
|
* inside one drain period nets to no emission.
|
|
2205
2206
|
*
|
|
2206
|
-
* {@link SoftMask.applyNodeCascadeToEdges} implements
|
|
2207
|
+
* {@link SoftMask.applyNodeCascadeToEdges} implements SINGLE edge-
|
|
2207
2208
|
* survival rule over the mask lane: an edge hide-fails iff EITHER endpoint
|
|
2208
|
-
* hide-fails (equivalently: survives iff BOTH endpoints survive
|
|
2209
|
+
* hide-fails (equivalently: survives iff BOTH endpoints survive
|
|
2209
2210
|
* scope.ts `cascadeEdges` is the hard-scope variant of the same rule). It is
|
|
2210
2211
|
* fed through one dedicated internal cascade source from the node hide lane:
|
|
2211
2212
|
* O(E) recompute per call, but only edges whose state changed produce
|
|
@@ -2213,12 +2214,12 @@ declare function canonicalFilterKey(specOrExpr: unknown): string;
|
|
|
2213
2214
|
*
|
|
2214
2215
|
* Overflow guard: increments clamp at 0xFFFF and latch a one-time
|
|
2215
2216
|
* {@link SoftMask.overflowed} flag the caller reports (an implementation may
|
|
2216
|
-
* widen to Uint32 without changing semantics
|
|
2217
|
-
* balanced increments/decrements whenever a clear
|
|
2217
|
+
* widen to Uint32 without changing semantics). Debug builds assert
|
|
2218
|
+
* balanced increments/decrements whenever a clear/release leaves zero
|
|
2218
2219
|
* held memberships.
|
|
2219
2220
|
*/
|
|
2220
2221
|
|
|
2221
|
-
/** Default muted alpha for dimmed entities
|
|
2222
|
+
/** Default muted alpha for dimmed entities. */
|
|
2222
2223
|
declare const DIM_ALPHA_DEFAULT = 0.15;
|
|
2223
2224
|
/**
|
|
2224
2225
|
* A handle contributing failure memberships to the mask. Setting a lane
|
|
@@ -2231,7 +2232,7 @@ interface MaskSource {
|
|
|
2231
2232
|
setNodeFailures(hideIdx: Iterable<number> | null, dimIdx?: Iterable<number> | null): void;
|
|
2232
2233
|
setEdgeFailures(hideIdx: Iterable<number> | null, dimIdx?: Iterable<number> | null): void;
|
|
2233
2234
|
/**
|
|
2234
|
-
* O(Δ) delta form of the HIDE lane
|
|
2235
|
+
* O(Δ) delta form of the HIDE lane: adds/removes individual slots
|
|
2235
2236
|
* from this source's membership without replaying the full set. Freely
|
|
2236
2237
|
* composable with the replace form — a later setNodeFailures re-baselines.
|
|
2237
2238
|
* `crossings`, when supplied, receives the slots whose hide counter
|
|
@@ -2242,8 +2243,8 @@ interface MaskSource {
|
|
|
2242
2243
|
updateEdgeFailures(addHide: ArrayLike<number> | null, removeHide: ArrayLike<number> | null): void;
|
|
2243
2244
|
/** Empties all four lane memberships (the source stays usable). */
|
|
2244
2245
|
clear(): void;
|
|
2245
|
-
/** clear
|
|
2246
|
-
* release
|
|
2246
|
+
/** clear + permanently retires the handle (further set/clear throws;
|
|
2247
|
+
* release itself is idempotent). */
|
|
2247
2248
|
release(): void;
|
|
2248
2249
|
}
|
|
2249
2250
|
/** Per-CALL zero-crossings reported by the delta ops (reused, callee-cleared). */
|
|
@@ -2253,7 +2254,7 @@ interface MaskCrossings {
|
|
|
2253
2254
|
/** Slots whose hide counter went nonzero → 0 in this call. */
|
|
2254
2255
|
becameClear: number[];
|
|
2255
2256
|
}
|
|
2256
|
-
/**
|
|
2257
|
+
/** O(Δ) operation counters (see resetStats/stats on SoftMask). */
|
|
2257
2258
|
interface MaskStats {
|
|
2258
2259
|
/** Slots visited by delta membership ops (add + remove attempts). */
|
|
2259
2260
|
slotsVisited: number;
|
|
@@ -2264,7 +2265,7 @@ interface MaskStats {
|
|
|
2264
2265
|
}
|
|
2265
2266
|
/**
|
|
2266
2267
|
* One drain payload. The four index arrays are REUSED across drains — copy
|
|
2267
|
-
* before the next drainDirty
|
|
2268
|
+
* before the next drainDirty call if you need to keep them.
|
|
2268
2269
|
*/
|
|
2269
2270
|
interface MaskDrain {
|
|
2270
2271
|
/** Node slots whose hide-visibility (hideFailures 0 ↔ nonzero) flipped
|
|
@@ -2289,18 +2290,18 @@ declare class SoftMask {
|
|
|
2289
2290
|
private readonly edgeHideLane;
|
|
2290
2291
|
private readonly edgeDimLane;
|
|
2291
2292
|
private readonly sources;
|
|
2292
|
-
/** Dedicated internal source implementing the
|
|
2293
|
+
/** Dedicated internal source implementing the node→edge cascade. */
|
|
2293
2294
|
private cascadeSource;
|
|
2294
2295
|
private overflowedFlag;
|
|
2295
2296
|
/** Total memberships currently held across all sources and lanes. */
|
|
2296
2297
|
private totalHeld;
|
|
2297
|
-
/**
|
|
2298
|
+
/** O(Δ) op counters. */
|
|
2298
2299
|
private readonly statsBox;
|
|
2299
2300
|
constructor(nodeCapacity: number, edgeCapacity: number);
|
|
2300
2301
|
get nodeCapacity(): number;
|
|
2301
2302
|
get edgeCapacity(): number;
|
|
2302
2303
|
/** One-time latch: some counter hit 0xFFFF and an increment was dropped.
|
|
2303
|
-
* Counts may drift afterwards; the caller reports it
|
|
2304
|
+
* Counts may drift afterwards; the caller reports it. */
|
|
2304
2305
|
get overflowed(): boolean;
|
|
2305
2306
|
get nodeHideFailures(): Uint16Array;
|
|
2306
2307
|
get nodeDimFailures(): Uint16Array;
|
|
@@ -2308,18 +2309,18 @@ declare class SoftMask {
|
|
|
2308
2309
|
get edgeDimFailures(): Uint16Array;
|
|
2309
2310
|
/**
|
|
2310
2311
|
* Grows capacities for structure changes (existing slot state is
|
|
2311
|
-
* preserved; new slots start fully visible). Capacities never shrink
|
|
2312
|
+
* preserved; new slots start fully visible). Capacities never shrink
|
|
2312
2313
|
* a smaller value is a no-op for that dimension.
|
|
2313
2314
|
*/
|
|
2314
2315
|
grow(nodeCapacity: number, edgeCapacity: number): void;
|
|
2315
|
-
/** Registers a new failure source. No cap on source count
|
|
2316
|
+
/** Registers a new failure source. No cap on source count. */
|
|
2316
2317
|
acquire(name: string): MaskSource;
|
|
2317
2318
|
/**
|
|
2318
|
-
*
|
|
2319
|
+
* edge cascade over the mask lane: recomputes, from the CURRENT node
|
|
2319
2320
|
* hide lane, the set of edges with at least one hidden endpoint, and feeds
|
|
2320
2321
|
* it to the dedicated internal cascade source (edge hide lane only).
|
|
2321
|
-
* `links` is the flat `[src0, tgt0, src1, tgt1, …]` node-slot pair buffer
|
|
2322
|
-
*
|
|
2322
|
+
* `links` is the flat `[src0, tgt0, src1, tgt1, …]` node-slot pair buffer;
|
|
2323
|
+
* edge slot i has endpoints at links[2i]/[2i+1].
|
|
2323
2324
|
* O(E) scan per call — typically once per drain — but only edges whose
|
|
2324
2325
|
* cascade state changed produce counter deltas (and thus dirty entries).
|
|
2325
2326
|
* Edges beyond `links.length / 2` are treated as having no hidden
|
|
@@ -2327,7 +2328,7 @@ declare class SoftMask {
|
|
|
2327
2328
|
*/
|
|
2328
2329
|
applyNodeCascadeToEdges(links: Uint32Array): void;
|
|
2329
2330
|
/**
|
|
2330
|
-
* O(incident-edges) delta form of the
|
|
2331
|
+
* O(incident-edges) delta form of the cascade: for each node
|
|
2331
2332
|
* whose HIDE visibility crossed zero, recompute only its incident edges'
|
|
2332
2333
|
* cascade state from the CURRENT node counters and apply the delta through
|
|
2333
2334
|
* the same internal cascade source the full form uses — the two compose
|
|
@@ -2343,10 +2344,10 @@ declare class SoftMask {
|
|
|
2343
2344
|
* drain). The returned index arrays are reused by the next drain.
|
|
2344
2345
|
*/
|
|
2345
2346
|
drainDirty(): MaskDrain;
|
|
2346
|
-
/**
|
|
2347
|
+
/** telemetry: estimated bytes of mask storage held:
|
|
2347
2348
|
* four counter lanes (+pending trackers) and per-source flag columns. */
|
|
2348
2349
|
estimatedBytes(): number;
|
|
2349
|
-
/**
|
|
2350
|
+
/** O(Δ) op counters (live object — snapshot before comparing). */
|
|
2350
2351
|
get stats(): Readonly<MaskStats>;
|
|
2351
2352
|
resetStats(): void;
|
|
2352
2353
|
visibleNodeCount(): number;
|
|
@@ -2354,7 +2355,7 @@ declare class SoftMask {
|
|
|
2354
2355
|
/** Visible iff hideFailures === 0 (out-of-range slots read not-visible). */
|
|
2355
2356
|
isNodeVisible(index: number): boolean;
|
|
2356
2357
|
isEdgeVisible(index: number): boolean;
|
|
2357
|
-
/** Dimmed iff visible AND dimFailures > 0
|
|
2358
|
+
/** Dimmed iff visible AND dimFailures > 0. */
|
|
2358
2359
|
isNodeDimmed(index: number): boolean;
|
|
2359
2360
|
isEdgeDimmed(index: number): boolean;
|
|
2360
2361
|
/** 1 (fully visible) | dimAlpha (dimmed) | 0 (hidden). */
|
|
@@ -2368,7 +2369,7 @@ declare class SoftMask {
|
|
|
2368
2369
|
*/
|
|
2369
2370
|
private applyMembership;
|
|
2370
2371
|
/**
|
|
2371
|
-
* O(Δ) delta ops on one lane membership
|
|
2372
|
+
* O(Δ) delta ops on one lane membership. Adds and removes are
|
|
2372
2373
|
* idempotent per slot (adding a member / removing a non-member is a
|
|
2373
2374
|
* no-op); removed slots leave HOLES in `list` (compacted past 50%), so
|
|
2374
2375
|
* replace/clear passes must honor the bit0 guard above. `crossings`, when
|
|
@@ -2383,65 +2384,65 @@ declare class SoftMask {
|
|
|
2383
2384
|
private drainLane;
|
|
2384
2385
|
private clearSource;
|
|
2385
2386
|
/** Debug balanced-increment assert: whenever no source holds any
|
|
2386
|
-
* membership, every counter must read zero (skipped once overflowed
|
|
2387
|
+
* membership, every counter must read zero (skipped once overflowed
|
|
2387
2388
|
* clamped increments legitimately drift the books). */
|
|
2388
2389
|
private assertBalancedIfIdle;
|
|
2389
2390
|
}
|
|
2390
2391
|
|
|
2391
2392
|
/**
|
|
2392
|
-
*
|
|
2393
|
+
* crossfilter — typed-column engine (v0.7 node-dimension subset).
|
|
2393
2394
|
*
|
|
2394
2395
|
* `TypedColumnCrossfilter` is the columnar backend the instance wraps into the
|
|
2395
2396
|
* public `CrossfilterSession`. Design (crossfilter.js lineage):
|
|
2396
2397
|
*
|
|
2397
|
-
* - **Parse once.** `build
|
|
2398
|
-
*
|
|
2399
|
-
*
|
|
2400
|
-
*
|
|
2401
|
-
*
|
|
2402
|
-
*
|
|
2403
|
-
*
|
|
2404
|
-
*
|
|
2398
|
+
* - **Parse once.** `build` extracts every dimension exactly once into typed
|
|
2399
|
+
* columns (numeric/temporal → `Float64Array` epoch-ms/values, categorical →
|
|
2400
|
+
* dictionary codes). hygiene: non-finite numerics, unparseable temporals,
|
|
2401
|
+
* and non-string/non-finite categorical values are excluded from the
|
|
2402
|
+
* dimension (tracked per dimension in `excludedRows`; the slot is marked
|
|
2403
|
+
* invalid). Temporal parsing: numbers are epoch ms verbatim; strings go
|
|
2404
|
+
* through `Date.parse` (ES2022 parses `'YYYY-MM-DD'` as UTC midnight);
|
|
2405
|
+
* `Date` instances use `getTime`.
|
|
2405
2406
|
* - **O(Δ) brushes.** Each range dimension keeps a one-time argsorted
|
|
2406
|
-
*
|
|
2407
|
-
*
|
|
2408
|
-
*
|
|
2409
|
-
*
|
|
2410
|
-
*
|
|
2411
|
-
*
|
|
2412
|
-
*
|
|
2407
|
+
* permutation. A brush move performs two binary searches and walks ONLY the
|
|
2408
|
+
* symmetric difference between the old and new in-range windows; categorical
|
|
2409
|
+
* brushes walk only the per-code slot lists whose excluded flag changed. The
|
|
2410
|
+
* brush path never re-sorts. A per-slot, per-dimension pass flag plus a
|
|
2411
|
+
* global per-slot failure counter make a row selection-visible iff it fails
|
|
2412
|
+
* zero dimensions. Hygiene-excluded rows fail any non-null brush on that
|
|
2413
|
+
* dimension (they cannot be in range / in a category) and pass a null brush.
|
|
2413
2414
|
* - **Deltas out.** `setBrush` returns the slots whose overall visibility
|
|
2414
|
-
*
|
|
2415
|
-
* - **Lazy dual layer.** `summarize
|
|
2416
|
-
*
|
|
2417
|
-
*
|
|
2418
|
-
*
|
|
2419
|
-
*
|
|
2420
|
-
*
|
|
2415
|
+
* flipped (`hidden`/`shown`) for the instance's mask source.
|
|
2416
|
+
* - **Lazy dual layer.** `summarize` returns immutable summaries whose
|
|
2417
|
+
* `filtered` layer counts rows passing every OTHER dimension's brush plus an
|
|
2418
|
+
* external node-mask predicate (`setExternalMask`). v0.7 recomputes a dirty
|
|
2419
|
+
* dimension's filtered layer lazily per `summarize` call — O(rows) per
|
|
2420
|
+
* dirty summarize, documented and acceptable at this tier (fixtures own
|
|
2421
|
+
* the perf claims; the O(Δ) guarantee covers the brush/visibility path).
|
|
2421
2422
|
* - **Revisions & notify.** `selectionRevision` starts at 0 and advances
|
|
2422
|
-
*
|
|
2423
|
-
*
|
|
2424
|
-
*
|
|
2425
|
-
*
|
|
2426
|
-
*
|
|
2427
|
-
*
|
|
2428
|
-
*
|
|
2429
|
-
*
|
|
2430
|
-
*
|
|
2423
|
+
* exactly once per observable `setBrush` (a brush state change is observable
|
|
2424
|
+
* via `getBrush` even when no row flips). v0.7 is synchronous, so
|
|
2425
|
+
* latest-call-wins degenerates to "every call applies immediately in call
|
|
2426
|
+
* order". Subscribers fire once per observable change after state is
|
|
2427
|
+
* consistent; synchronous re-entrancy (a subscriber mutating the engine) is
|
|
2428
|
+
* coalesced into one trailing notification pass. Model updates
|
|
2429
|
+
* (`appendRows`/`replaceAll`) keep the current `selectionRevision`
|
|
2430
|
+
* but do notify. External-mask changes notify (summaries changed) without
|
|
2431
|
+
* advancing the selection revision.
|
|
2431
2432
|
* - **Incremental append.** `appendRows` extends columns/codes in place and
|
|
2432
|
-
*
|
|
2433
|
-
*
|
|
2434
|
-
*
|
|
2435
|
-
*
|
|
2436
|
-
*
|
|
2437
|
-
*
|
|
2438
|
-
*
|
|
2439
|
-
*
|
|
2440
|
-
*
|
|
2441
|
-
*
|
|
2433
|
+
* merges the pre-sorted old permutation with the sorted new block
|
|
2434
|
+
* (permutation merge — never a full re-argsort); bins extend incrementally
|
|
2435
|
+
* unless the domain grew (then a re-bin, still sort-free). Brushes persist
|
|
2436
|
+
* by key and are re-applied to the NEW slots only; the returned delta covers
|
|
2437
|
+
* only new slots (`shown` = new passing, `hidden` = new failing).
|
|
2438
|
+
* `replaceAll` rebuilds columns but preserves brushes by dimension key,
|
|
2439
|
+
* re-applying them as one combined delta against an all-visible baseline of
|
|
2440
|
+
* the new roster (`shown` is always empty). `replaceAll` clears the external
|
|
2441
|
+
* mask (slot indices changed meaning); `appendRows` keeps it and treats new
|
|
2442
|
+
* slots as passing until the instance re-supplies it.
|
|
2442
2443
|
*/
|
|
2443
2444
|
|
|
2444
|
-
/** Default histogram bin count for numeric/temporal dimensions
|
|
2445
|
+
/** Default histogram bin count for numeric/temporal dimensions. */
|
|
2445
2446
|
declare const DEFAULT_BIN_COUNT = 24;
|
|
2446
2447
|
/** Slots whose overall selection-visibility flipped in one operation. */
|
|
2447
2448
|
interface BrushDelta {
|
|
@@ -2458,15 +2459,15 @@ interface CrossfilterStats {
|
|
|
2458
2459
|
fullSorts: number;
|
|
2459
2460
|
/** Permutation merges performed by appendRows. */
|
|
2460
2461
|
permutationMerges: number;
|
|
2461
|
-
/** Incremental filtered-layer bin adjustments (
|
|
2462
|
+
/** Incremental filtered-layer bin adjustments (O(Δ·D)). */
|
|
2462
2463
|
binUpdates: number;
|
|
2463
2464
|
/** Full O(rows) filtered-layer recomputes (materialization/oracle only). */
|
|
2464
2465
|
filteredRecomputes: number;
|
|
2465
2466
|
}
|
|
2466
2467
|
declare class TypedColumnCrossfilter<N = Record<string, unknown>> {
|
|
2467
|
-
/** Test instrumentation; see CrossfilterStats. Reset with resetStats
|
|
2468
|
+
/** Test instrumentation; see CrossfilterStats. Reset with resetStats. */
|
|
2468
2469
|
readonly stats: CrossfilterStats;
|
|
2469
|
-
/** Count of dims whose filtered layer is live-maintained
|
|
2470
|
+
/** Count of dims whose filtered layer is live-maintained. */
|
|
2470
2471
|
private liveDims;
|
|
2471
2472
|
private dims;
|
|
2472
2473
|
private byKey;
|
|
@@ -2484,11 +2485,11 @@ declare class TypedColumnCrossfilter<N = Record<string, unknown>> {
|
|
|
2484
2485
|
/** Monotonic from 0; advances exactly once per observable setBrush change. */
|
|
2485
2486
|
get selectionRevision(): number;
|
|
2486
2487
|
resetStats(): void;
|
|
2487
|
-
/**
|
|
2488
|
+
/** telemetry: estimated bytes of typed-column storage held.
|
|
2488
2489
|
* Documented components: per-dim value/permutation/bin/code/pass arrays,
|
|
2489
2490
|
* the global failure counter, and the external mask. */
|
|
2490
2491
|
estimatedBytes(): number;
|
|
2491
|
-
/**
|
|
2492
|
+
/** live-layer bookkeeping — the ONLY writer of `filteredLive`. */
|
|
2492
2493
|
private setFilteredLive;
|
|
2493
2494
|
/**
|
|
2494
2495
|
* (Re)initialize columns from scratch. Clears all brushes and the external
|
|
@@ -2510,19 +2511,19 @@ declare class TypedColumnCrossfilter<N = Record<string, unknown>> {
|
|
|
2510
2511
|
setBrush(key: string, brush: BrushState): BrushDelta;
|
|
2511
2512
|
/**
|
|
2512
2513
|
* External node-mask predicate for the joint "filtered" second layer (the
|
|
2513
|
-
* instance wires the
|
|
2514
|
-
* never selection visibility. Length must equal rowCount
|
|
2514
|
+
* instance wires the filter-prop node mask in). Affects summaries only,
|
|
2515
|
+
* never selection visibility. Length must equal rowCount. Notifies on
|
|
2515
2516
|
* observable change; does NOT advance selectionRevision.
|
|
2516
2517
|
*/
|
|
2517
2518
|
setExternalMask(passSlots: Uint8Array | null): void;
|
|
2518
2519
|
/**
|
|
2519
|
-
* Immutable summary. The filtered layer is recomputed lazily when dirty
|
|
2520
|
+
* Immutable summary. The filtered layer is recomputed lazily when dirty
|
|
2520
2521
|
* O(rows) per dirty summarize (v0.7 tier; see module doc). Returned objects
|
|
2521
2522
|
* are frozen and never mutated by later operations.
|
|
2522
2523
|
*/
|
|
2523
2524
|
summarize(key: string): DimensionSummary;
|
|
2524
2525
|
/**
|
|
2525
|
-
* Incrementally extend columns with new rows
|
|
2526
|
+
* Incrementally extend columns with new rows: no full
|
|
2526
2527
|
* rebuild, no full re-argsort — the pre-sorted old permutation merges with
|
|
2527
2528
|
* the sorted new block. Brushes stay by key and are applied to the NEW slots
|
|
2528
2529
|
* only; the returned delta covers only new slots. Keeps selectionRevision;
|
|
@@ -2551,16 +2552,16 @@ declare class TypedColumnCrossfilter<N = Record<string, unknown>> {
|
|
|
2551
2552
|
private binIndex;
|
|
2552
2553
|
private flip;
|
|
2553
2554
|
/**
|
|
2554
|
-
*
|
|
2555
|
+
* inline maintenance of LIVE filtered layers, dispatched from the
|
|
2555
2556
|
* one place that knows the failCount transition. `boundary` is the
|
|
2556
2557
|
* other-failures picture at the interesting side of the flip (after for
|
|
2557
2558
|
* shown, before for hidden):
|
|
2558
|
-
*
|
|
2559
|
-
*
|
|
2560
|
-
*
|
|
2561
|
-
*
|
|
2562
|
-
*
|
|
2563
|
-
*
|
|
2559
|
+
* - 0 → the slot crossed the FULLY-VISIBLE boundary: every other live
|
|
2560
|
+
* layer counts it (own layers ignore the own-dim brush, so the brushed
|
|
2561
|
+
* dim's layer is provably unchanged by its own flip);
|
|
2562
|
+
* - 1 → exactly one OTHER dim still fails the slot: only that dim's
|
|
2563
|
+
* what-if-I-cleared-mine layer flips;
|
|
2564
|
+
* - ≥2 → no layer can change.
|
|
2564
2565
|
* External-mask-excluded and hygiene-invalid slots contribute nothing
|
|
2565
2566
|
* either way and are skipped.
|
|
2566
2567
|
*/
|
|
@@ -2578,27 +2579,27 @@ declare class TypedColumnCrossfilter<N = Record<string, unknown>> {
|
|
|
2578
2579
|
}
|
|
2579
2580
|
|
|
2580
2581
|
/**
|
|
2581
|
-
*
|
|
2582
|
+
* scales & domains — canonical scale keying, default
|
|
2582
2583
|
* palettes, sRGB color interpolation, domain-state machinery, and stable
|
|
2583
2584
|
* categorical assignment. Pure and engine-free; the instance/projection
|
|
2584
2585
|
* layers consume these primitives when a styling channel carries a `Scale`.
|
|
2585
2586
|
*
|
|
2586
|
-
* Semantics
|
|
2587
|
-
* - Scales are plain descriptors compared by CANONICAL STRUCTURAL VALUE
|
|
2588
|
-
*
|
|
2589
|
-
*
|
|
2587
|
+
* Semantics:
|
|
2588
|
+
* - Scales are plain descriptors compared by CANONICAL STRUCTURAL VALUE
|
|
2589
|
+
* equal inline literals produce equal keys and never reproject. A function
|
|
2590
|
+
* `by` keys by reference identity (WeakMap token), never by source text.
|
|
2590
2591
|
* - Domains default to the whole dataset revision and stay FROZEN across
|
|
2591
|
-
*
|
|
2592
|
-
*
|
|
2593
|
-
*
|
|
2594
|
-
*
|
|
2592
|
+
* masking/brushing/isolation ('dataset' scope): the same metric value never
|
|
2593
|
+
* changes visual meaning because a user brushed. 'hard-scope'/'visible' are
|
|
2594
|
+
* explicit opt-ins that recompute when the caller's scope generation bumps;
|
|
2595
|
+
* streaming 'expand' permits monotonic domain growth on recompute.
|
|
2595
2596
|
* - Explicit numeric domains always win verbatim (never computed, cached, or
|
|
2596
|
-
*
|
|
2597
|
+
* unioned).
|
|
2597
2598
|
* - Categorical values declared in `domain` take their declared position;
|
|
2598
|
-
*
|
|
2599
|
-
*
|
|
2600
|
-
* -
|
|
2601
|
-
*
|
|
2599
|
+
* out-of-domain values take a stable fnv-1a hash slot — NEVER first-seen
|
|
2600
|
+
* order, so arrival order can never recolor a category.
|
|
2601
|
+
* - numeric hygiene: null/non-finite metric values resolve to `null`
|
|
2602
|
+
* (caller falls back to the default style) and are excluded from domains.
|
|
2602
2603
|
*/
|
|
2603
2604
|
|
|
2604
2605
|
type SequentialScale<T, N = Record<string, unknown>> = Extract<Scale<T, N>, {
|
|
@@ -2615,7 +2616,7 @@ type DivergingScale<T, N = Record<string, unknown>> = Extract<Scale<T, N>, {
|
|
|
2615
2616
|
* order preserved, undefined-valued keys omitted, and a function `by` mapped
|
|
2616
2617
|
* to a unique reference-identity token (same WeakMap approach — and canonical
|
|
2617
2618
|
* grammar — as `canonicalFilterKey`). Two scales with equal keys are
|
|
2618
|
-
*
|
|
2619
|
+
* structurally equivalent: equal inline literals MUST and DO produce equal keys, so
|
|
2619
2620
|
* identity churn never reprojects; swapping a function reference always does.
|
|
2620
2621
|
*/
|
|
2621
2622
|
declare function canonicalScaleKey<T, N>(scale: Scale<T, N>): string;
|
|
@@ -2634,7 +2635,7 @@ declare const DIVERGING_RANGE_DEFAULT: readonly [string, string, string];
|
|
|
2634
2635
|
declare function interpolateColor(a: string, b: string, t: number): string;
|
|
2635
2636
|
/**
|
|
2636
2637
|
* Sequential color: maps `value` across `domain` onto the two-stop ramp.
|
|
2637
|
-
* Returns null (caller renders the default style,
|
|
2638
|
+
* Returns null (caller renders the default style, hygiene) when the value
|
|
2638
2639
|
* is null/non-finite or the domain is null.
|
|
2639
2640
|
*/
|
|
2640
2641
|
declare function sequentialColor<N = Record<string, unknown>>(scale: SequentialScale<string, N>, value: number | null, domain: readonly [number, number] | null): string | null;
|
|
@@ -2649,11 +2650,11 @@ declare function divergingColor<N = Record<string, unknown>>(scale: DivergingSca
|
|
|
2649
2650
|
* Sequential size: linear map of `value` across `domain` onto [lo,hi],
|
|
2650
2651
|
* clamped at the range endpoints. Degenerate domains yield the range
|
|
2651
2652
|
* midpoint; null/non-finite values and null domains yield null (default
|
|
2652
|
-
* style — NaN is never handed to a size buffer
|
|
2653
|
+
* style — NaN is never handed to a size buffer).
|
|
2653
2654
|
*/
|
|
2654
2655
|
declare function sequentialSize(range: readonly [number, number], value: number | null, domain: readonly [number, number] | null): number | null;
|
|
2655
2656
|
/**
|
|
2656
|
-
* [min,max] over the finite numbers in `values` (
|
|
2657
|
+
* [min,max] over the finite numbers in `values` (hygiene: null and
|
|
2657
2658
|
* non-finite entries are excluded), or null when nothing qualifies. A single
|
|
2658
2659
|
* qualifying value yields a degenerate [v,v] domain.
|
|
2659
2660
|
*/
|
|
@@ -2663,7 +2664,7 @@ interface ResolveDomainArgs {
|
|
|
2663
2664
|
* descriptor, so the key already discriminates by metric). Opaque here. */
|
|
2664
2665
|
key: string;
|
|
2665
2666
|
/** Explicit caller domain — returned VERBATIM; never computed, cached, or
|
|
2666
|
-
* expand-unioned
|
|
2667
|
+
* expand-unioned. */
|
|
2667
2668
|
explicit?: readonly [number, number] | undefined;
|
|
2668
2669
|
/** Defaults: scope 'dataset', streaming 'freeze-per-revision'. */
|
|
2669
2670
|
policy?: DomainPolicy | undefined;
|
|
@@ -2673,7 +2674,7 @@ interface ResolveDomainArgs {
|
|
|
2673
2674
|
* (bump = recompute). IGNORED under 'dataset' scope — masking/brushing
|
|
2674
2675
|
* must never change what a color means. */
|
|
2675
2676
|
scopeGeneration?: number | undefined;
|
|
2676
|
-
/** Source lineage this resolve belongs to (
|
|
2677
|
+
/** Source lineage this resolve belongs to (dataset key + source
|
|
2677
2678
|
* revision). `streaming: 'expand'` unions only WITHIN one lineage — a
|
|
2678
2679
|
* source replacement starts fresh instead of unioning dead extrema.
|
|
2679
2680
|
* Callers that omit it keep the legacy always-union behavior. */
|
|
@@ -2683,7 +2684,7 @@ interface ResolveDomainArgs {
|
|
|
2683
2684
|
compute: () => readonly [number, number] | null;
|
|
2684
2685
|
}
|
|
2685
2686
|
/**
|
|
2686
|
-
* Per-instance domain freezer keyed by canonical scale key
|
|
2687
|
+
* Per-instance domain freezer keyed by canonical scale key.
|
|
2687
2688
|
*
|
|
2688
2689
|
* FREEZE-PER-REVISION: under scope 'dataset' the domain is computed ONCE per
|
|
2689
2690
|
* {key, datasetRevision} — repeat resolves return the frozen value without
|
|
@@ -2722,24 +2723,24 @@ declare function categoricalIndex(domain: readonly string[] | undefined, palette
|
|
|
2722
2723
|
declare function categoricalRows(domain: readonly string[] | undefined, seen: Iterable<string>): string[];
|
|
2723
2724
|
|
|
2724
2725
|
/**
|
|
2725
|
-
*
|
|
2726
|
+
* capability-policy module.
|
|
2726
2727
|
*
|
|
2727
|
-
*
|
|
2728
|
+
* (normative): for capabilities where the core owns a fallback path, the
|
|
2728
2729
|
* core selects native-vs-fallback ONCE at mount from the engine's declared
|
|
2729
2730
|
* `capabilities` record — never by sniffing method presence — and unsupported
|
|
2730
|
-
* *requested* props degrade loudly (a
|
|
2731
|
+
* *requested* props degrade loudly (a diagnostic), never as silent no-ops.
|
|
2731
2732
|
*
|
|
2732
2733
|
* This module is the single place those decisions are made:
|
|
2733
|
-
*
|
|
2734
|
-
*
|
|
2735
|
-
*
|
|
2736
|
-
*
|
|
2737
|
-
*
|
|
2738
|
-
*
|
|
2734
|
+
* - {@link resolveEnginePolicy} turns the capability record + the host's
|
|
2735
|
+
* requested features into one frozen {@link EnginePolicy} at mount.
|
|
2736
|
+
* - {@link assertCapabilityMethodParity} is the mount-time dev-mode record
|
|
2737
|
+
* vs. method-surface assertion, restricted to honestly checkable pairs.
|
|
2738
|
+
* - {@link normalizeCommitForCapabilities} strips commit payload an engine
|
|
2739
|
+
* declared it cannot honor, so incapable adapters never see it.
|
|
2739
2740
|
*/
|
|
2740
2741
|
|
|
2741
2742
|
/** One loud degradation: a feature the host requested that the mounted engine
|
|
2742
|
-
* does not declare. Feeds the
|
|
2743
|
+
* does not declare. Feeds the dev diagnostic at mount. */
|
|
2743
2744
|
interface EnginePolicyDegradation {
|
|
2744
2745
|
readonly feature: string;
|
|
2745
2746
|
readonly reason: string;
|
|
@@ -2749,29 +2750,29 @@ interface EnginePolicyDegradation {
|
|
|
2749
2750
|
interface RequestedEngineFeatures {
|
|
2750
2751
|
edgeArrows?: boolean;
|
|
2751
2752
|
images?: boolean;
|
|
2752
|
-
/**
|
|
2753
|
+
/** stage-4: a `clusters` spec is active this session. */
|
|
2753
2754
|
clusters?: boolean;
|
|
2754
2755
|
}
|
|
2755
2756
|
/**
|
|
2756
|
-
* The frozen mount-time native-vs-fallback record
|
|
2757
|
+
* The frozen mount-time native-vs-fallback record. Evaluated exactly
|
|
2757
2758
|
* once per mount from `EngineCapabilities` and never revisited — capability
|
|
2758
2759
|
* records are static declarations fixed at engine construction, so the policy
|
|
2759
2760
|
* must not drift even if a caller mutates the input record afterwards.
|
|
2760
2761
|
*/
|
|
2761
2762
|
interface EnginePolicy {
|
|
2762
|
-
/**
|
|
2763
|
+
/** arrowheads: engine-drawn, or the prop is inert (+ dev warning). */
|
|
2763
2764
|
readonly edgeArrows: 'native' | 'inert';
|
|
2764
|
-
/**
|
|
2765
|
+
/** image sprites: atlas-backed, or the placeholder glyph with refs
|
|
2765
2766
|
* retained for a future compatible engine. */
|
|
2766
2767
|
readonly images: 'native' | 'placeholder';
|
|
2767
|
-
/**
|
|
2768
|
+
/** link hover/click: engine events, or the core's CPU grid fallback. */
|
|
2768
2769
|
readonly linkPicking: 'native' | 'cpu-fallback';
|
|
2769
|
-
/**
|
|
2770
|
+
/** stage-4 cluster force: engine-applied, or inert (membership,
|
|
2770
2771
|
* labels, and centroids are core-owned and unaffected). */
|
|
2771
2772
|
readonly clusterForce: 'native' | 'inert';
|
|
2772
2773
|
/** Channels eligible for ranged (partial) uploads; empty = full replaces. */
|
|
2773
2774
|
readonly rangedChannels: ReadonlySet<EngineBufferChannel>;
|
|
2774
|
-
/**
|
|
2775
|
+
/** stop-at-rest: 'stops' when the engine declares idleFrames:'stops'.
|
|
2775
2776
|
* Observability only (telemetry/harness read it) — an idle-spinning engine
|
|
2776
2777
|
* is a documented state, never a degradation entry: nothing was
|
|
2777
2778
|
* host-REQUESTED, so there is nothing to warn about. */
|
|
@@ -2784,7 +2785,7 @@ interface EnginePolicy {
|
|
|
2784
2785
|
* Resolve the mount-time engine policy from the declared capability record.
|
|
2785
2786
|
*
|
|
2786
2787
|
* Decisions come from `capabilities` ONLY — method sniffing is forbidden by
|
|
2787
|
-
*
|
|
2788
|
+
* The result is deep-frozen and holds a defensive copy of
|
|
2788
2789
|
* `rangeUpdates`, so mutating the input record afterwards changes nothing.
|
|
2789
2790
|
* A degradation entry exists only for features the host actually requested
|
|
2790
2791
|
* that the engine does not declare; unrequested gaps stay silent.
|
|
@@ -2795,25 +2796,25 @@ declare function resolveEnginePolicy(capabilities: EngineCapabilities, requested
|
|
|
2795
2796
|
* with the engine's method surface? Returns a list of human-readable
|
|
2796
2797
|
* mismatches (empty = consistent).
|
|
2797
2798
|
*
|
|
2798
|
-
* Why so few checks?
|
|
2799
|
+
* Why so few checks? An illustrative parity assertion
|
|
2799
2800
|
* (`capabilities.linkPicking === (typeof engine.linkAt === 'function')`)
|
|
2800
|
-
* presumes
|
|
2801
|
+
* presumes a broader method surface; the v0.1 adapter contract
|
|
2801
2802
|
* deliberately narrows it, leaving most capabilities with NO honestly
|
|
2802
|
-
* checkable method pair. Those
|
|
2803
|
-
*
|
|
2804
|
-
*
|
|
2805
|
-
*
|
|
2806
|
-
*
|
|
2807
|
-
*
|
|
2808
|
-
*
|
|
2809
|
-
*
|
|
2810
|
-
*
|
|
2811
|
-
*
|
|
2812
|
-
*
|
|
2813
|
-
*
|
|
2814
|
-
*
|
|
2815
|
-
*
|
|
2816
|
-
*
|
|
2803
|
+
* checkable method pair. Those are instead covered by engine probes that
|
|
2804
|
+
* validate declared records against observed behavior:
|
|
2805
|
+
*
|
|
2806
|
+
* - `linkPicking`: native picking arrives through mount-time host events
|
|
2807
|
+
* (`onLinkClick`/`onLinkHover`), not a probeable `linkAt` method — there
|
|
2808
|
+
* is nothing on the engine object to compare the record against.
|
|
2809
|
+
* - `edgeArrows` / `pointImages` / `rangeUpdates`: honored inside
|
|
2810
|
+
* `commit()` payload handling (`config.linkArrows`, `resources`, ranged
|
|
2811
|
+
* uploads) with no distinguishing method; only behavior can validate them.
|
|
2812
|
+
* - `simulation`: `start()`/`pause()` are mandatory on every engine (static
|
|
2813
|
+
* engines no-op them), so method presence carries no signal either way.
|
|
2814
|
+
* - `pointsInPolygon`-family (`pointsInRect`, `captureScreenshot`,
|
|
2815
|
+
* `neighborIndices`, `screenToSpace`/`spaceToScreen`, `setPinnedIndices`,
|
|
2816
|
+
* `zoomToIndex`): optional-by-contract with no declaring capability bit —
|
|
2817
|
+
* absence is legitimate, so no check is fabricated for them.
|
|
2817
2818
|
*
|
|
2818
2819
|
* The one honest pair on this surface: `trackedPositions` declares position
|
|
2819
2820
|
* readback works, so `getPositions` must actually be present.
|
|
@@ -2821,10 +2822,10 @@ declare function resolveEnginePolicy(capabilities: EngineCapabilities, requested
|
|
|
2821
2822
|
declare function assertCapabilityMethodParity(engine: GraphEngine): string[];
|
|
2822
2823
|
/**
|
|
2823
2824
|
* Strip commit payload the engine's capability record says it cannot honor:
|
|
2824
|
-
*
|
|
2825
|
-
*
|
|
2826
|
-
*
|
|
2827
|
-
*
|
|
2825
|
+
* - `resources` (image atlas + per-point image index) unless `pointImages`;
|
|
2826
|
+
* - `config.linkArrows` unless `edgeArrows`;
|
|
2827
|
+
* - `config.cluster` unless `clusterForce` (stage 4 — the core keeps
|
|
2828
|
+
* membership, labels, and centroids; only the FORCE is engine-side).
|
|
2828
2829
|
* A `config` left empty by the strip is dropped entirely.
|
|
2829
2830
|
*
|
|
2830
2831
|
* IDENTITY-PRESERVING: when nothing needs stripping the SAME commit object
|
|
@@ -2837,15 +2838,15 @@ declare function normalizeCommitForCapabilities(commit: EngineCommit, capabiliti
|
|
|
2837
2838
|
};
|
|
2838
2839
|
|
|
2839
2840
|
/**
|
|
2840
|
-
*
|
|
2841
|
+
* shared numeric hygiene — THE single coercion layer for every
|
|
2841
2842
|
* numeric consumer in orbit-core.
|
|
2842
2843
|
*
|
|
2843
2844
|
* Wherever a caller-supplied value feeds a numeric sink — size/width
|
|
2844
|
-
* projection buffers
|
|
2845
|
-
* crossfilter bins
|
|
2845
|
+
* projection buffers, scale domains, metric columns,
|
|
2846
|
+
* crossfilter bins, table lanes — the value is REQUIRED to route
|
|
2846
2847
|
* through `coerceNumeric` / `coerceNumericInto`. Non-numeric and non-finite
|
|
2847
2848
|
* inputs (including the string sentinels `"NaN"` / `"Infinity"` /
|
|
2848
|
-
* `"-Infinity"` that JSON transports smuggle through
|
|
2849
|
+
* `"-Infinity"` that JSON transports smuggle through) coerce to `null`:
|
|
2849
2850
|
* the row falls back to the default style and is excluded from domain
|
|
2850
2851
|
* computation. NaN NEVER escapes this module — not as a return value and not
|
|
2851
2852
|
* into a GPU buffer.
|
|
@@ -2853,16 +2854,15 @@ declare function normalizeCommitForCapabilities(commit: EngineCommit, capabiliti
|
|
|
2853
2854
|
* Coercion rules:
|
|
2854
2855
|
* - numbers pass iff `Number.isFinite` (NaN / ±Infinity → null);
|
|
2855
2856
|
* - strings are trimmed; empty → null; the case-insensitive sentinels
|
|
2856
|
-
*
|
|
2857
|
-
*
|
|
2858
|
-
*
|
|
2857
|
+
* 'NaN' / 'Infinity' / '-Infinity' / '+Infinity' → null; anything else
|
|
2858
|
+
* parses via `Number(...)` and passes iff finite (so '1e3' → 1000 but
|
|
2859
|
+
* '12px' → null — `Number`, not `parseFloat`, so no partial prefixes);
|
|
2859
2860
|
* - booleans, objects, arrays, functions, symbols, bigints, null, and
|
|
2860
|
-
*
|
|
2861
|
+
* undefined → null (no `valueOf`/`toString` coercion side channels).
|
|
2861
2862
|
*
|
|
2862
|
-
*
|
|
2863
|
-
*
|
|
2864
|
-
*
|
|
2865
|
-
* stay defined in exactly one place.
|
|
2863
|
+
* `crossfilter.ts` predates this module and retains its own inline finite
|
|
2864
|
+
* checks. Any consolidation must route through `coerceNumeric` so the
|
|
2865
|
+
* sentinel-string rules stay defined in exactly one place.
|
|
2866
2866
|
*/
|
|
2867
2867
|
declare function coerceNumeric(value: unknown): number | null;
|
|
2868
2868
|
/**
|
|
@@ -2877,31 +2877,31 @@ declare function coerceNumeric(value: unknown): number | null;
|
|
|
2877
2877
|
declare function coerceNumericInto(target: Float32Array, index: number, value: unknown, fallback: number): boolean;
|
|
2878
2878
|
|
|
2879
2879
|
/**
|
|
2880
|
-
*
|
|
2880
|
+
* metrics (v0.8 subset): lazy degree-family primitives over the
|
|
2881
2881
|
* maintained topology plus revision-gated admission of async metric columns.
|
|
2882
2882
|
*
|
|
2883
2883
|
* - The degree family (degree / inDegree / outDegree) is computed LAZILY on
|
|
2884
|
-
*
|
|
2885
|
-
*
|
|
2884
|
+
* first request per model revision, in ONE combined O(n + L) pass, and
|
|
2885
|
+
* cached as Float64Arrays until the model revision changes.
|
|
2886
2886
|
* - SELF-LOOP SEMANTICS: a self-loop (a, a) contributes exactly 1 to each of
|
|
2887
|
-
*
|
|
2888
|
-
*
|
|
2889
|
-
*
|
|
2890
|
-
*
|
|
2891
|
-
* - Async columns join once against the accepted model
|
|
2892
|
-
*
|
|
2893
|
-
*
|
|
2894
|
-
*
|
|
2895
|
-
*
|
|
2896
|
-
*
|
|
2897
|
-
*
|
|
2887
|
+
* degree, inDegree, and outDegree of `a`. The CSR adjacency lists a
|
|
2888
|
+
* self-loop twice under its point (once per endpoint slot, see
|
|
2889
|
+
* adjacency.ts), so degree = CSR row length MINUS the point's self-loop
|
|
2890
|
+
* count; in/out come from a directed pass over the flat link pairs.
|
|
2891
|
+
* - Async columns join once against the accepted model: 'index' align
|
|
2892
|
+
* is positional over accepted-base order; 'ids' align joins by id with
|
|
2893
|
+
* unknown ids counted+sampled, duplicate ids counted (last occurrence
|
|
2894
|
+
* wins), and absent rows null. Every value routes through `coerceNumeric`.
|
|
2895
|
+
* A column computed for a stale model revision is
|
|
2896
|
+
* DISCARDED with a diagnostic — admission is the correctness gate, abort
|
|
2897
|
+
* is only an optimization.
|
|
2898
2898
|
* - Storage encodes null as NaN inside Float64Arrays; `getMetricValue`
|
|
2899
|
-
*
|
|
2899
|
+
* converts back at the boundary so NaN never escapes to callers.
|
|
2900
2900
|
* - Admitted columns SHADOW the built-in degree family under the same name
|
|
2901
|
-
*
|
|
2901
|
+
* (a precomputed/server 'degree' column wins; precomputed path).
|
|
2902
2902
|
*/
|
|
2903
2903
|
|
|
2904
|
-
/** Topology snapshot the degree family is computed from
|
|
2904
|
+
/** Topology snapshot the degree family is computed from. */
|
|
2905
2905
|
interface MetricModelInput<N = Record<string, unknown>> {
|
|
2906
2906
|
/** Accepted nodes in accepted-base order (index i ↔ point i). */
|
|
2907
2907
|
nodes: readonly GraphNode<N>[];
|
|
@@ -2933,7 +2933,7 @@ declare class MetricStore<N = Record<string, unknown>> {
|
|
|
2933
2933
|
/** Admitted async columns by metric name; NaN encodes null. */
|
|
2934
2934
|
private readonly columns;
|
|
2935
2935
|
private degreePasses;
|
|
2936
|
-
/**
|
|
2936
|
+
/** telemetry: estimated bytes of metric storage held. */
|
|
2937
2937
|
estimatedBytes(): number;
|
|
2938
2938
|
/** Number of combined degree-family compute passes (test observability). */
|
|
2939
2939
|
get degreeComputePasses(): number;
|
|
@@ -2946,10 +2946,10 @@ declare class MetricStore<N = Record<string, unknown>> {
|
|
|
2946
2946
|
*/
|
|
2947
2947
|
setModel(model: MetricModelInput<N>): void;
|
|
2948
2948
|
/**
|
|
2949
|
-
* Joins async metric columns against the accepted model
|
|
2949
|
+
* Joins async metric columns against the accepted model.
|
|
2950
2950
|
* Revision-gated PER COLUMN (I1): a column whose issue-time
|
|
2951
2951
|
* `forModelRevision` stamp differs from `opts.modelRevision` is discarded
|
|
2952
|
-
* (info diagnostic — a normal async race outcome
|
|
2952
|
+
* (info diagnostic — a normal async race outcome). A missing or
|
|
2953
2953
|
* mismatched stamp is never defaulted to the current revision — that
|
|
2954
2954
|
* would make the gate self-satisfying.
|
|
2955
2955
|
* Structural rejections ('index' length mismatch, missing/mismatched ids)
|
|
@@ -2966,7 +2966,7 @@ declare class MetricStore<N = Record<string, unknown>> {
|
|
|
2966
2966
|
getMetricValue(metric: MetricName, index: number): number | null;
|
|
2967
2967
|
/**
|
|
2968
2968
|
* Raw column in accepted-base order, or null when unavailable. NaN encodes
|
|
2969
|
-
* null
|
|
2969
|
+
* null — consumers exclude NaN slots from domains. Do NOT mutate:
|
|
2970
2970
|
* this is the live cache, not a copy.
|
|
2971
2971
|
*/
|
|
2972
2972
|
metricValues(metric: MetricName): Float64Array | null;
|
|
@@ -2978,22 +2978,22 @@ declare class MetricStore<N = Record<string, unknown>> {
|
|
|
2978
2978
|
}
|
|
2979
2979
|
|
|
2980
2980
|
/**
|
|
2981
|
-
*
|
|
2981
|
+
* minimap / overview — CPU fallback lane.
|
|
2982
2982
|
*
|
|
2983
2983
|
* v0.9 trim: no engine exposes `capabilities.overviewPass` yet (cosmos has no
|
|
2984
|
-
* second draw pass —
|
|
2985
|
-
* path: an O(n) CPU rasterization of the
|
|
2984
|
+
* second draw pass — matrix), so this controller IS the minimap thumbnail
|
|
2985
|
+
* path: an O(n) CPU rasterization of the position mirror into a small
|
|
2986
2986
|
* RGBA dot field. It is a pure controller — no DOM, no canvas — the React
|
|
2987
2987
|
* component blits the returned `Uint8ClampedArray` into an `ImageData`/canvas
|
|
2988
|
-
* and draws the viewport rectangle on top (O(1) from `getViewport
|
|
2988
|
+
* and draws the viewport rectangle on top (O(1) from `getViewport`, fully
|
|
2989
2989
|
* decoupled from thumbnail refresh cadence).
|
|
2990
2990
|
*
|
|
2991
|
-
* Refresh cadence
|
|
2991
|
+
* Refresh cadence: ≤ 2 Hz while the simulation is hot (positions
|
|
2992
2992
|
* change continuously on the GPU, so the epoch is ignored), ≤ 1 Hz after a
|
|
2993
2993
|
* change while idle, and ZERO work while idle with an unchanged positions
|
|
2994
2994
|
* epoch — `shouldRefresh` is the single throttle gate and latches its clock /
|
|
2995
2995
|
* epoch only when it answers true (a `true` must be followed by one
|
|
2996
|
-
* `rasterize
|
|
2996
|
+
* `rasterize`).
|
|
2997
2997
|
*
|
|
2998
2998
|
* Orientation: minimap pixel y grows DOWNWARD while world (space) y grows
|
|
2999
2999
|
* upward — the same flip cosmos applies in its space→screen scale (dist
|
|
@@ -3032,7 +3032,7 @@ interface OverviewRaster {
|
|
|
3032
3032
|
bitmap: Uint8ClampedArray;
|
|
3033
3033
|
bounds: OverviewBounds;
|
|
3034
3034
|
}
|
|
3035
|
-
/** Spec
|
|
3035
|
+
/** Spec the CPU fallback rasterizes into a 256² target. */
|
|
3036
3036
|
declare const OVERVIEW_SIZE_DEFAULT = 256;
|
|
3037
3037
|
/** Hot-simulation refresh floor: ≤ 2 Hz. */
|
|
3038
3038
|
declare const OVERVIEW_HOT_INTERVAL_MS = 500;
|
|
@@ -3042,7 +3042,7 @@ declare class OverviewController {
|
|
|
3042
3042
|
private readonly getScene;
|
|
3043
3043
|
private readonly getVisible;
|
|
3044
3044
|
readonly size: number;
|
|
3045
|
-
/** Transform of the LAST rasterization; null until rasterize
|
|
3045
|
+
/** Transform of the LAST rasterization; null until rasterize succeeds. */
|
|
3046
3046
|
private frame;
|
|
3047
3047
|
private lastRefreshMs;
|
|
3048
3048
|
private lastEpoch;
|
|
@@ -3052,16 +3052,16 @@ declare class OverviewController {
|
|
|
3052
3052
|
* when it returns true, so each `true` accounts for exactly one refresh:
|
|
3053
3053
|
*
|
|
3054
3054
|
* - hot (`simulationRunning`): time-gated only (≤ 2 Hz) — the epoch is
|
|
3055
|
-
*
|
|
3055
|
+
* ignored because positions change continuously without epoch advances;
|
|
3056
3056
|
* - idle: refresh only when the epoch ADVANCED since the last refresh,
|
|
3057
|
-
*
|
|
3057
|
+
* time-gated at ≤ 1 Hz;
|
|
3058
3058
|
* - idle + unchanged epoch: always false — zero work, forever.
|
|
3059
3059
|
*/
|
|
3060
3060
|
shouldRefresh(nowMs: number, simulationRunning: boolean, epoch: number): boolean;
|
|
3061
3061
|
/**
|
|
3062
3062
|
* Rasterizes the current scene into a fresh `size²` RGBA dot field: 1 px
|
|
3063
3063
|
* white dots whose alpha ACCUMULATES on overlap (heatmap-ish density),
|
|
3064
|
-
* dimmed for mask-hidden points; NaN pairs
|
|
3064
|
+
* dimmed for mask-hidden points; NaN pairs are skipped.
|
|
3065
3065
|
* World bounds map into the thumbnail with a 5 % edge padding at a UNIFORM
|
|
3066
3066
|
* scale (aspect preserved, centered on the short axis) and a downward pixel
|
|
3067
3067
|
* y (see module header). Returns null when there is no scene or no point.
|
|
@@ -3077,34 +3077,34 @@ declare class OverviewController {
|
|
|
3077
3077
|
}
|
|
3078
3078
|
|
|
3079
3079
|
/**
|
|
3080
|
-
*
|
|
3080
|
+
* SVG export — the engine-free pure module.
|
|
3081
3081
|
*
|
|
3082
3082
|
* ENGINE-INDEPENDENT BY CONSTRUCTION, not by discipline: positions and
|
|
3083
3083
|
* projected styles come IN as a plain descriptor, vector markup goes OUT as a
|
|
3084
3084
|
* string. No engine, no instance, no DOM — the post-v1 server snapshot path
|
|
3085
|
-
*
|
|
3085
|
+
* reuses this verbatim under plain Node, and the test
|
|
3086
3086
|
* suite runs it exactly that way. Keeping it engine-free is an M7
|
|
3087
3087
|
* requirement, which is why it is a standalone module rather than instance
|
|
3088
3088
|
* code that might one day be extracted.
|
|
3089
3089
|
*
|
|
3090
|
-
* Output discipline
|
|
3091
|
-
*
|
|
3092
|
-
*
|
|
3093
|
-
*
|
|
3094
|
-
*
|
|
3095
|
-
*
|
|
3096
|
-
*
|
|
3097
|
-
*
|
|
3098
|
-
*
|
|
3099
|
-
*
|
|
3100
|
-
*
|
|
3090
|
+
* Output discipline:
|
|
3091
|
+
* - One element per node/edge/label, assembled OFF-DOM by chunked
|
|
3092
|
+
* array-join — O(elements), never a live DOM node per element.
|
|
3093
|
+
* - Every label and attribute string is XML-escaped (untrusted-content
|
|
3094
|
+
* rule in its XML form): a hostile label renders as literal text in every
|
|
3095
|
+
* downstream vector tool, never as markup.
|
|
3096
|
+
* - Bounded: above `maxElements` (default 50 000 — the practical ceiling for
|
|
3097
|
+
* downstream vector editors) rendering THROWS before assembling anything;
|
|
3098
|
+
* the instance wraps that into the typed `export-too-large` rejection, and
|
|
3099
|
+
* the raster-hybrid form (a PNG base layer plus a small vector overlay) is
|
|
3100
|
+
* the sanctioned way past it.
|
|
3101
3101
|
*/
|
|
3102
3102
|
interface SvgSceneNode {
|
|
3103
3103
|
x: number;
|
|
3104
3104
|
y: number;
|
|
3105
3105
|
/** Radius in output px (the caller halves its size-buffer diameter). */
|
|
3106
3106
|
r: number;
|
|
3107
|
-
/** Any CSS color string (the caller projects RGBA buffers to rgba
|
|
3107
|
+
/** Any CSS color string (the caller projects RGBA buffers to rgba). */
|
|
3108
3108
|
color: string;
|
|
3109
3109
|
}
|
|
3110
3110
|
interface SvgSceneEdge {
|
|
@@ -3162,21 +3162,21 @@ declare function escapeXml(text: string): string;
|
|
|
3162
3162
|
declare function renderSvg(scene: SvgScene, opts?: RenderSvgOptions): string;
|
|
3163
3163
|
|
|
3164
3164
|
/**
|
|
3165
|
-
*
|
|
3166
|
-
*
|
|
3165
|
+
* Columnar-native acceptance rules — the column-oriented twin of validate.ts,
|
|
3166
|
+
* built to run INSIDE the worker over typed
|
|
3167
3167
|
* columns without materializing a single row object.
|
|
3168
3168
|
*
|
|
3169
3169
|
* Semantics mirror the object lane EXACTLY (the equivalence oracle pins
|
|
3170
3170
|
* rosters AND diagnostics, message strings included):
|
|
3171
|
-
*
|
|
3172
|
-
*
|
|
3173
|
-
*
|
|
3174
|
-
*
|
|
3175
|
-
*
|
|
3176
|
-
*
|
|
3177
|
-
*
|
|
3178
|
-
*
|
|
3179
|
-
*
|
|
3171
|
+
* - duplicate node ids drop, first occurrence wins ('duplicate-node-id',
|
|
3172
|
+
* warning) — and edges addressing a dropped duplicate ROW remap to the
|
|
3173
|
+
* surviving occurrence, because the object lane resolves endpoints by ID
|
|
3174
|
+
* STRING, which survives.
|
|
3175
|
+
* - duplicate edge ids drop, first wins ('duplicate-edge-id', warning).
|
|
3176
|
+
* - self-loops are RETAINED with 'self-loop-retained' (info).
|
|
3177
|
+
* - invalid-node / invalid-edge / dangling-edge cannot occur here: ids come
|
|
3178
|
+
* from a structurally validated dictionary column and endpoints are
|
|
3179
|
+
* in-bounds indices by prior validation (validateColumnarStructure).
|
|
3180
3180
|
*
|
|
3181
3181
|
* Duplicates hide in TWO encodings: two rows sharing a code, and two
|
|
3182
3182
|
* DISTINCT dictionary entries holding equal strings. Both are handled by
|
|
@@ -3211,21 +3211,21 @@ interface ColumnarAcceptance {
|
|
|
3211
3211
|
declare function acceptColumnar(snapshot: ColumnarGraphSnapshot<unknown, unknown>): ColumnarAcceptance;
|
|
3212
3212
|
|
|
3213
3213
|
/**
|
|
3214
|
-
*
|
|
3214
|
+
* columnar snapshot lane — structural validation and
|
|
3215
3215
|
* the materialization bridge.
|
|
3216
3216
|
*
|
|
3217
3217
|
* Validation is O(number of columns + rows) and runs BEFORE transfer or
|
|
3218
3218
|
* admission. Columnar structural corruption is intentionally not repaired
|
|
3219
|
-
* row by row — offset/index misalignment can reinterpret every later row
|
|
3219
|
+
* row by row — offset/index misalignment can reinterpret every later row
|
|
3220
3220
|
* so any structural issue rejects the WHOLE snapshot, the prior scene stays
|
|
3221
|
-
* intact, and nothing throws from a React render
|
|
3221
|
+
* intact, and nothing throws from a React render.
|
|
3222
3222
|
*
|
|
3223
3223
|
* The bridge materializes a VALID columnar snapshot into the object form and
|
|
3224
3224
|
* feeds the existing pipeline, so duplicate-id / dangling-edge / self-loop
|
|
3225
3225
|
* resolution stays in exactly one place (validate.ts) and the columnar lane
|
|
3226
|
-
* inherits every object-lane rule by construction
|
|
3227
|
-
*
|
|
3228
|
-
*
|
|
3226
|
+
* inherits every object-lane rule by construction. Under `execution: 'main'`
|
|
3227
|
+
* materialization remains synchronous; the worker lane can perform acceptance
|
|
3228
|
+
* off-thread before the same shared pipeline runs.
|
|
3229
3229
|
*
|
|
3230
3230
|
* Null semantics: `nulls` is one byte per row, nonzero = null. Null attr
|
|
3231
3231
|
* values materialize as `null` (JSON-ish; every numeric/temporal consumer
|
|
@@ -3242,7 +3242,7 @@ interface ColumnarIssue {
|
|
|
3242
3242
|
}
|
|
3243
3243
|
/**
|
|
3244
3244
|
* O(columns + rows) structural validation. Empty array ⇒ structurally sound
|
|
3245
|
-
* (duplicate ids, self-loops, and parallel-edge rules are NOT checked here
|
|
3245
|
+
* (duplicate ids, self-loops, and parallel-edge rules are NOT checked here
|
|
3246
3246
|
* they are shared object-lane semantics applied after materialization).
|
|
3247
3247
|
*/
|
|
3248
3248
|
declare function validateColumnarStructure(snapshot: ColumnarGraphSnapshot<unknown, unknown>): ColumnarIssue[];
|
|
@@ -3266,7 +3266,7 @@ declare function buildAcceptedFromColumnar<N, E>(snapshot: ColumnarGraphSnapshot
|
|
|
3266
3266
|
declare function columnarArrayBuffers(snapshot: ColumnarGraphSnapshot<unknown, unknown>): ArrayBuffer[];
|
|
3267
3267
|
/**
|
|
3268
3268
|
* `bufferOwnership: 'transfer'` — detach every underlying ArrayBuffer.
|
|
3269
|
-
* Called ONLY after validation and admission both succeeded
|
|
3269
|
+
* Called ONLY after validation and admission both succeeded:
|
|
3270
3270
|
* reject-before-allocation extends to reject-before-detach). Returns the
|
|
3271
3271
|
* number of buffers detached. SharedArrayBuffer-backed views are skipped
|
|
3272
3272
|
* (they cannot detach and remain shared by contract).
|
|
@@ -3274,8 +3274,8 @@ declare function columnarArrayBuffers(snapshot: ColumnarGraphSnapshot<unknown, u
|
|
|
3274
3274
|
declare function detachColumnarBuffers(snapshot: ColumnarGraphSnapshot<unknown, unknown>): number;
|
|
3275
3275
|
|
|
3276
3276
|
/**
|
|
3277
|
-
*
|
|
3278
|
-
* accessor forms: `FieldAccessor` + `SerializableTransform` + the `field
|
|
3277
|
+
* serializable field descriptors — the worker-lane
|
|
3278
|
+
* accessor forms: `FieldAccessor` + `SerializableTransform` + the `field`
|
|
3279
3279
|
* manual-schema helper. Descriptors are DATA, never code: they cross the
|
|
3280
3280
|
* worker boundary by structured clone, compare by canonical structural
|
|
3281
3281
|
* value, and unknown transform ops are validation errors (spec: "descriptors
|
|
@@ -3293,14 +3293,14 @@ declare function detachColumnarBuffers(snapshot: ColumnarGraphSnapshot<unknown,
|
|
|
3293
3293
|
* projection ever runs.
|
|
3294
3294
|
*/
|
|
3295
3295
|
|
|
3296
|
-
/** Branded attr path — `field<A,T
|
|
3297
|
-
* -time narrowing
|
|
3296
|
+
/** Branded attr path — `field<A,T>` is a manual-schema ASSERTION for
|
|
3297
|
+
* compile-time narrowing in codegen/schema packages, not runtime validation. */
|
|
3298
3298
|
type TypedFieldPath<A, T> = string & {
|
|
3299
3299
|
readonly __attrs?: A;
|
|
3300
3300
|
readonly __value?: T;
|
|
3301
3301
|
};
|
|
3302
3302
|
declare function field<A, T>(path: string): TypedFieldPath<A, T>;
|
|
3303
|
-
/**
|
|
3303
|
+
/** transform whitelist. Closed set — extending it is a spec amendment. */
|
|
3304
3304
|
type SerializableTransform = {
|
|
3305
3305
|
op: 'identity';
|
|
3306
3306
|
} | {
|
|
@@ -3347,7 +3347,7 @@ declare function isFieldAccessor(value: unknown): value is FieldAccessor<unknown
|
|
|
3347
3347
|
declare function descriptorKey(accessor: FieldAccessor<unknown, unknown>): string;
|
|
3348
3348
|
/**
|
|
3349
3349
|
* Evaluate a descriptor against an entity's `(id, attrs)` — the pure
|
|
3350
|
-
* function both lanes share (
|
|
3350
|
+
* function both lanes share (byte-exact parity starts with ONE
|
|
3351
3351
|
* evaluation implementation, imported by both threads, never duplicated).
|
|
3352
3352
|
*/
|
|
3353
3353
|
declare function evaluateFieldAccessor(accessor: FieldAccessor<unknown, unknown>, id: string, attrs: Readonly<Record<string, unknown>> | undefined): unknown;
|