@modernrelay/orbit-core 0.13.6 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-FG6TQANI.js → chunk-TDBIVBJ3.js} +23 -7
- package/dist/chunk-TDBIVBJ3.js.map +1 -0
- package/dist/engine.d.ts +1 -1
- package/dist/{index-BoQfS8jd.d.ts → index-CPRTWpGA.d.ts} +175 -175
- package/dist/index.d.ts +629 -619
- package/dist/index.js +220 -176
- package/dist/index.js.map +1 -1
- package/dist/{lane-SZYhcrXT.d.ts → lane-_KPQ1deI.d.ts} +42 -42
- package/dist/testing.d.ts +22 -22
- package/dist/testing.js +15 -15
- package/dist/testing.js.map +1 -1
- package/dist/worker/entry.js +35 -15
- package/dist/worker/entry.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-FG6TQANI.js.map +0 -1
package/dist/index.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,23 +603,31 @@ type ViewBrushState = {
|
|
|
603
603
|
interface ViewLayoutSpec {
|
|
604
604
|
kind: 'force' | 'fixed';
|
|
605
605
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
606
|
+
type SerializableScaleFor<T extends string | number> = {
|
|
607
|
+
kind: 'sequential';
|
|
608
|
+
metric: string;
|
|
609
|
+
range: readonly [T, T];
|
|
610
|
+
domain?: readonly [number, number];
|
|
611
|
+
} | {
|
|
612
|
+
kind: 'diverging';
|
|
609
613
|
metric: string;
|
|
610
|
-
range: readonly
|
|
611
|
-
|
|
612
|
-
mid?: number;
|
|
614
|
+
range: readonly [T, T, T];
|
|
615
|
+
mid: number;
|
|
613
616
|
} | {
|
|
614
617
|
kind: 'categorical';
|
|
615
618
|
/** Field-descriptor form only — a function `by` is omitted upstream. */
|
|
616
619
|
by: string;
|
|
617
|
-
palette?: readonly
|
|
620
|
+
palette?: readonly T[];
|
|
618
621
|
domain?: readonly string[];
|
|
619
622
|
};
|
|
623
|
+
/** Scale subset that is data by construction (no functions). `T` is bound
|
|
624
|
+
* by the styling channel: CSS strings for nodeColor, finite numbers for
|
|
625
|
+
* nodeSize. Tuple arity mirrors the runtime Scale<T> contract exactly. The
|
|
626
|
+
* conditional preserves homogeneous value collections when `T` is a union. */
|
|
627
|
+
type SerializableScale<T extends string | number = string | number> = T extends unknown ? SerializableScaleFor<T> : never;
|
|
620
628
|
interface ViewStyling {
|
|
621
|
-
nodeColor?: SerializableScale
|
|
622
|
-
nodeSize?: SerializableScale
|
|
629
|
+
nodeColor?: SerializableScale<string>;
|
|
630
|
+
nodeSize?: SerializableScale<number>;
|
|
623
631
|
showLinks?: boolean;
|
|
624
632
|
edgeArrows?: boolean;
|
|
625
633
|
/** Named themes only; a custom GraphTheme object is omitted upstream. */
|
|
@@ -630,16 +638,16 @@ interface GraphViewState {
|
|
|
630
638
|
camera: ViewportState | null;
|
|
631
639
|
selection: SelectionState;
|
|
632
640
|
hiddenNodeIds: readonly NodeId[];
|
|
633
|
-
/**
|
|
641
|
+
/** isolation; null = full scope. */
|
|
634
642
|
subgraph: SubgraphSpec | null;
|
|
635
643
|
/** Manual groups verbatim, or — under `groupBy` — `{key, collapsed}` pairs
|
|
636
|
-
* only (membership recomputes from current data on restore
|
|
644
|
+
* only (membership recomputes from current data on restore). */
|
|
637
645
|
groups: readonly GroupSpec[] | ReadonlyArray<{
|
|
638
646
|
key: string;
|
|
639
647
|
collapsed: boolean;
|
|
640
648
|
}>;
|
|
641
649
|
pinnedNodeIds: readonly NodeId[];
|
|
642
|
-
/**
|
|
650
|
+
/** node folds: anchor → declared members (v1 extension, see header). */
|
|
643
651
|
folds?: ReadonlyArray<readonly [NodeId, readonly NodeId[]]>;
|
|
644
652
|
layout: ViewLayoutSpec;
|
|
645
653
|
/** Declaration order; a key absent here has no brush. */
|
|
@@ -653,7 +661,7 @@ interface GraphViewState {
|
|
|
653
661
|
positions?: ReadonlyArray<readonly [string, number, number]>;
|
|
654
662
|
styling?: ViewStyling;
|
|
655
663
|
/** Host-owned durable source coordinate — stored verbatim, NEVER
|
|
656
|
-
* interpreted; compared canonically on restore
|
|
664
|
+
* interpreted; compared canonically on restore. */
|
|
657
665
|
dataRef?: JsonValue;
|
|
658
666
|
}
|
|
659
667
|
declare const VIEW_STATE_VERSION: 1;
|
|
@@ -664,8 +672,8 @@ declare const VIEW_STATE_VERSION: 1;
|
|
|
664
672
|
* their canonical strings are identical.
|
|
665
673
|
*/
|
|
666
674
|
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
|
|
675
|
+
/** Canonical equality for dataRef values: key order never matters.
|
|
676
|
+
* A non-JSON value (cycles included) is never equal to anything — the
|
|
669
677
|
* "compared, never interpreted" rule extended to malformed input. */
|
|
670
678
|
declare function sameDataRef(a: JsonValue | undefined, b: JsonValue | undefined): boolean;
|
|
671
679
|
type ViewStateVerdict = {
|
|
@@ -677,7 +685,7 @@ type ViewStateVerdict = {
|
|
|
677
685
|
problems: readonly string[];
|
|
678
686
|
};
|
|
679
687
|
/**
|
|
680
|
-
* Full structural validation + version gate. Order per
|
|
688
|
+
* Full structural validation + version gate. Order per version first
|
|
681
689
|
* (higher-than-known and non-numeric reject as `unsupported-version` /
|
|
682
690
|
* `invalid-view-state` BEFORE field checks), then per-field structure. Lower
|
|
683
691
|
* versions run the migration registry, then re-validate at the current shape.
|
|
@@ -693,7 +701,7 @@ type SetViewStateResult = {
|
|
|
693
701
|
} | {
|
|
694
702
|
status: 'rejected';
|
|
695
703
|
code: 'invalid-view-state' | 'unsupported-version'
|
|
696
|
-
/** The state touches a
|
|
704
|
+
/** The state touches a controlled slice or carries styling the
|
|
697
705
|
* host must reflect, and no aggregate restore callback exists. */
|
|
698
706
|
| 'missing-restore-callback'
|
|
699
707
|
/** Another restore/history transaction is awaiting acknowledgement. */
|
|
@@ -706,28 +714,28 @@ type SetViewStateResult = {
|
|
|
706
714
|
};
|
|
707
715
|
|
|
708
716
|
/**
|
|
709
|
-
*
|
|
717
|
+
* GraphInstance — the public headless core instance (v0.3 subset).
|
|
710
718
|
*
|
|
711
719
|
* One `applyHostUpdate` call is the atomic host boundary: it validates,
|
|
712
720
|
* reconciles, re-projects only dirty channels, and publishes EXACTLY ONE store
|
|
713
|
-
* `set
|
|
714
|
-
* controlled-state change can never tear across frames
|
|
721
|
+
* `set` and AT MOST ONE engine commit, so a simultaneous data + style +
|
|
722
|
+
* controlled-state change can never tear across frames.
|
|
715
723
|
*
|
|
716
|
-
* Vanilla zustand only — no React, no DOM access at module scope
|
|
724
|
+
* Vanilla zustand only — no React, no DOM access at module scope.
|
|
717
725
|
*/
|
|
718
726
|
|
|
719
|
-
/**
|
|
727
|
+
/** dark base theme (the default when no base is named). */
|
|
720
728
|
declare const GRAPH_THEME_DARK: GraphTheme;
|
|
721
|
-
/**
|
|
729
|
+
/** light base theme. */
|
|
722
730
|
declare const GRAPH_THEME_LIGHT: GraphTheme;
|
|
723
731
|
/**
|
|
724
|
-
* Resolve a ThemeInput to a full GraphTheme
|
|
732
|
+
* Resolve a ThemeInput to a full GraphTheme: pick the named base
|
|
725
733
|
* (default dark), then merge every defined token over it. A full GraphTheme
|
|
726
734
|
* input resolves to exactly its own tokens; `undefined` resolves to the dark
|
|
727
735
|
* base; the v0.1 `{background}` compat shorthand merges as a partial.
|
|
728
736
|
*/
|
|
729
737
|
declare function resolveTheme(input?: ThemeInput): GraphTheme;
|
|
730
|
-
/** One categorical legend row
|
|
738
|
+
/** One categorical legend row: declared-domain rows first (including
|
|
731
739
|
* currently-empty categories), then extra seen values sorted. */
|
|
732
740
|
interface ScaleInfoRow {
|
|
733
741
|
value: string;
|
|
@@ -745,50 +753,50 @@ interface ScaleChannelInfo<N = Record<string, unknown>> {
|
|
|
745
753
|
/** Categorical rows; omitted for sequential/diverging scales. */
|
|
746
754
|
rows?: readonly ScaleInfoRow[];
|
|
747
755
|
}
|
|
748
|
-
/**
|
|
756
|
+
/** Revision-aware service seam for expansion and search. */
|
|
749
757
|
interface GraphServices<N = Record<string, unknown>, E = Record<string, unknown>> {
|
|
750
758
|
/**
|
|
751
759
|
* Ego-expansion resolver for `expandNode` / `SubgraphSpec.hops`. Default:
|
|
752
|
-
* the built-in LOCAL service — it walks the core's
|
|
760
|
+
* the built-in LOCAL service — it walks the core's adjacency over the
|
|
753
761
|
* accepted model, INCLUDING currently out-of-scope nodes (zero config,
|
|
754
762
|
* zero network; the core still never fetches).
|
|
755
763
|
*/
|
|
756
764
|
expansion?: ExpansionService<N, E>;
|
|
757
765
|
/**
|
|
758
|
-
*
|
|
766
|
+
* path resolver for `findPath`. Default: the built-in LOCAL
|
|
759
767
|
* unweighted BFS over the loaded VISIBLE edge list, respecting
|
|
760
768
|
* PathOptions.direction. Revision-aware: a result arriving after a
|
|
761
|
-
* dataset replacement is discarded at admission
|
|
769
|
+
* dataset replacement is discarded at admission.
|
|
762
770
|
*/
|
|
763
771
|
path?: PathService;
|
|
764
772
|
/**
|
|
765
|
-
*
|
|
773
|
+
* search resolver for `instance.search`. Default: the built-in LOCAL
|
|
766
774
|
* indexed service over the accepted model plus the host's declared
|
|
767
775
|
* `searchIndex` fields (id-only when never declared — it never guesses
|
|
768
776
|
* attr names; zero config, zero network). Custom services plug in
|
|
769
|
-
* server-side search (Omnigraph
|
|
770
|
-
* correctness: RequestContext, revision-keyed caching, supersede
|
|
777
|
+
* server-side search (such as Omnigraph stored queries) with the same
|
|
778
|
+
* instance-side correctness: RequestContext, revision-keyed caching, supersede
|
|
771
779
|
* cancellation, stale rejection at admission.
|
|
772
780
|
*/
|
|
773
781
|
search?: SearchService<N>;
|
|
774
782
|
}
|
|
775
783
|
interface CreateGraphInstanceOptions<N = Record<string, unknown>, E = Record<string, unknown>> {
|
|
776
|
-
/** Called once per mount; a re-attach constructs a fresh engine
|
|
784
|
+
/** Called once per mount; a re-attach constructs a fresh engine. */
|
|
777
785
|
engine: EngineFactory;
|
|
778
786
|
/** Fit the camera once when the first data-bearing commit reaches a fresh engine. Default true. */
|
|
779
787
|
fitViewOnFirstData?: boolean;
|
|
780
|
-
/**
|
|
788
|
+
/** revision-aware services (expansion + search). */
|
|
781
789
|
services?: GraphServices<N, E>;
|
|
782
790
|
/**
|
|
783
|
-
*
|
|
791
|
+
* node attr fields the DEFAULT search service indexes (ids always;
|
|
784
792
|
* absent = id-only — the service never guesses attr names). CONSTRUCTION-
|
|
785
|
-
* ONLY
|
|
793
|
+
* ONLY as a host construction option: read once here;
|
|
786
794
|
* changing it requires a keyed remount / replacement instance. A runtime
|
|
787
795
|
* `applyHostUpdate` attempt is ignored with a one-shot warning.
|
|
788
796
|
*/
|
|
789
797
|
searchIndex?: readonly string[];
|
|
790
798
|
/**
|
|
791
|
-
*
|
|
799
|
+
* undo/redo. Default true. `false` makes the history
|
|
792
800
|
* surface inert (record/undo/redo no-ops, depths stay 0); an object sets
|
|
793
801
|
* the stack bound (default {@link HISTORY_LIMIT_DEFAULT} entries).
|
|
794
802
|
*/
|
|
@@ -796,42 +804,42 @@ interface CreateGraphInstanceOptions<N = Record<string, unknown>, E = Record<str
|
|
|
796
804
|
limit?: number;
|
|
797
805
|
};
|
|
798
806
|
/**
|
|
799
|
-
*
|
|
800
|
-
*
|
|
807
|
+
* Degradation-ladder thresholds. CONSTRUCTION-ONLY: read once; invalid
|
|
808
|
+
* fields fall back to the defaults with ONE
|
|
801
809
|
* config warning diagnostic. A runtime change requires a keyed remount.
|
|
802
810
|
*/
|
|
803
811
|
limits?: Partial<ScaleLimits>;
|
|
804
812
|
/**
|
|
805
|
-
*
|
|
813
|
+
* Image-atlas resolver seam: owns authenticated fetch/caching for
|
|
806
814
|
* one `nodeImage` ref. Default: plain `fetch(ref)` for public URLs.
|
|
807
815
|
* Injectable for tests and authenticated hosts.
|
|
808
816
|
*/
|
|
809
817
|
imageResolver?: ImageResolver;
|
|
810
818
|
/**
|
|
811
|
-
*
|
|
812
|
-
*
|
|
813
|
-
*
|
|
819
|
+
* Execution mode. Current worker cargo is columnar ACCEPTANCE: validation,
|
|
820
|
+
* deduplication, and link resolution run off-thread, then land through
|
|
821
|
+
* revision-gated async admission. 'main' (default) keeps every
|
|
814
822
|
* columnar ingest synchronous; 'auto' and 'worker' route columnar data
|
|
815
823
|
* through the worker lane when it boots — an unavailable lane degrades
|
|
816
824
|
* to 'main' with one `worker-unavailable` info diagnostic under 'auto'
|
|
817
|
-
* and an ERROR diagnostic under 'worker'
|
|
818
|
-
* back
|
|
819
|
-
*
|
|
825
|
+
* and an ERROR diagnostic under 'worker'. Columnar acceptance may still
|
|
826
|
+
* fall back because it has no worker-required mode; channel projection
|
|
827
|
+
* remains on the main thread.
|
|
820
828
|
*/
|
|
821
829
|
execution?: 'auto' | 'main' | 'worker';
|
|
822
|
-
/**
|
|
830
|
+
/** Worker construction tri-option (URL / factory / inline
|
|
823
831
|
* default). Testing seam: `transport` on the lane via this factory. */
|
|
824
832
|
workerFactory?: WorkerFactoryOption;
|
|
825
833
|
}
|
|
826
834
|
/**
|
|
827
|
-
* `expandNode` outcome
|
|
828
|
-
*
|
|
829
|
-
*
|
|
830
|
-
*
|
|
831
|
-
*
|
|
832
|
-
*
|
|
833
|
-
*
|
|
834
|
-
*
|
|
835
|
+
* `expandNode` outcome:
|
|
836
|
+
* - `{ added }` — the admitted result merged; `added` counts the nodes it
|
|
837
|
+
* made newly visible in the current scope.
|
|
838
|
+
* - `{ noop: true }` — every returned neighbor was already visible in the
|
|
839
|
+
* current scope; no session was opened and nothing changed.
|
|
840
|
+
* - `{ coalesced: true }` — reserved. v0.5 same-id coalescing hands the
|
|
841
|
+
* SECOND caller the IDENTICAL in-flight promise, so both callers observe
|
|
842
|
+
* the primary call's `{added}`/`{noop}` result instead of this marker.
|
|
835
843
|
*/
|
|
836
844
|
type ExpandNodeResult = {
|
|
837
845
|
added: number;
|
|
@@ -841,7 +849,7 @@ type ExpandNodeResult = {
|
|
|
841
849
|
noop: true;
|
|
842
850
|
};
|
|
843
851
|
/**
|
|
844
|
-
*
|
|
852
|
+
* expansion bookkeeping: one committed expansion overlay. Data-merging
|
|
845
853
|
* batches carry the request id (overlayId + batch ids) and provenance into
|
|
846
854
|
* ingestion so abort, rollback, and removeOverlay remove the exact
|
|
847
855
|
* contribution they own.
|
|
@@ -855,11 +863,11 @@ interface ExpansionOverlayRecord {
|
|
|
855
863
|
provenance?: unknown;
|
|
856
864
|
}
|
|
857
865
|
/**
|
|
858
|
-
*
|
|
859
|
-
*
|
|
860
|
-
*
|
|
861
|
-
*
|
|
862
|
-
*
|
|
866
|
+
* overlay label lane subscriptions. Two channels with distinct cadences:
|
|
867
|
+
* - `subscribeCandidates` fires ONLY when the candidate SET (ids/text/forced)
|
|
868
|
+
* changes — the throttled re-rank. React re-renders label content here.
|
|
869
|
+
* - `subscribePositions` fires on scheduler ticks (host `onFrame`) with fresh
|
|
870
|
+
* x/y for the SAME set — imperative transform writes, NO React re-render.
|
|
863
871
|
* Both replay the current state synchronously on subscribe. The emitted array
|
|
864
872
|
* and its placement objects are REUSED across position ticks — copy if you
|
|
865
873
|
* need a snapshot.
|
|
@@ -869,20 +877,20 @@ interface LabelSubscriptions {
|
|
|
869
877
|
subscribePositions(cb: (list: readonly LabelPlacement[]) => void): () => void;
|
|
870
878
|
}
|
|
871
879
|
interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>> {
|
|
872
|
-
/** Vanilla zustand store — the single observable state surface
|
|
880
|
+
/** Vanilla zustand store — the single observable state surface. */
|
|
873
881
|
readonly store: StoreApi<GraphStoreState>;
|
|
874
|
-
/**
|
|
882
|
+
/** DOM label lane (overlay scheduler output). */
|
|
875
883
|
readonly labels: LabelSubscriptions;
|
|
876
|
-
/** Atomic host transaction: one store publication, at most one engine commit
|
|
884
|
+
/** Atomic host transaction: one store publication, at most one engine commit. */
|
|
877
885
|
applyHostUpdate(update: GraphHostUpdate<N, E>): void;
|
|
878
886
|
/**
|
|
879
|
-
*
|
|
887
|
+
* revisioned ingestion: begin a bounded, cancellable session against an
|
|
880
888
|
* explicit `datasetKey` and `baseModelRevision` (compare-and-set; mismatch
|
|
881
889
|
* throws 'stale-revision'). Overlay sessions must name the CURRENT
|
|
882
890
|
* datasetKey; replace sessions may establish a new one and are always
|
|
883
891
|
* atomic.
|
|
884
892
|
*
|
|
885
|
-
*
|
|
893
|
+
* While a declarative data source is actively driving (a snapshot
|
|
886
894
|
* was applied through `applyHostUpdate` and has not been superseded by a
|
|
887
895
|
* committed replace session), `purpose:'replace'` is rejected at begin with
|
|
888
896
|
* a TypeError — two writers may not race for the base. Overlay ingestion
|
|
@@ -890,11 +898,11 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
890
898
|
*
|
|
891
899
|
* Every session admission/publication is serialized through the
|
|
892
900
|
* instance-local acceptance queue; arrival there is the global admission
|
|
893
|
-
* order
|
|
901
|
+
* order.
|
|
894
902
|
*/
|
|
895
903
|
beginIngest(opts: BeginIngestOptions): IngestSession<N, E>;
|
|
896
904
|
/**
|
|
897
|
-
*
|
|
905
|
+
* atomically remove exactly one committed overlay — re-runs collision
|
|
898
906
|
* and endpoint resolution, promotes formerly shadowed rows from surviving
|
|
899
907
|
* overlays, advances model/render revisions, and releases the overlayId for
|
|
900
908
|
* deliberate reuse. Unknown ids are an idempotent `{ removed: false }`.
|
|
@@ -902,14 +910,14 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
902
910
|
removeOverlay(overlayId: string): {
|
|
903
911
|
removed: boolean;
|
|
904
912
|
};
|
|
905
|
-
/** Committed overlay ids for the current dataset
|
|
913
|
+
/** Committed overlay ids for the current dataset. */
|
|
906
914
|
getOverlayIds(): readonly string[];
|
|
907
915
|
attach(container: HTMLElement): Promise<void>;
|
|
908
916
|
detach(): void;
|
|
909
917
|
destroy(): void;
|
|
910
918
|
/**
|
|
911
|
-
*
|
|
912
|
-
* control's preventDefault
|
|
919
|
+
* typed events: listeners run SYNCHRONOUSLY in registration order; the
|
|
920
|
+
* control's preventDefault cancels ONLY the built-in follow-up (click
|
|
913
921
|
* selection, drag pin), never other listeners.
|
|
914
922
|
*/
|
|
915
923
|
on<K extends GraphEventName>(name: K, cb: (payload: GraphEventMap<N, E>[K], control: GraphListenerControl) => void): () => void;
|
|
@@ -918,51 +926,51 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
918
926
|
zoomOut(): void;
|
|
919
927
|
setViewport(v: Partial<ViewportState>): void;
|
|
920
928
|
/**
|
|
921
|
-
*
|
|
929
|
+
* Focus neighborhood: keep the v0.1 camera behavior
|
|
922
930
|
* (setFocusedIndex + zoomToIndex) and RETURN the 1-hop neighbor ids
|
|
923
931
|
* (engine adjacency when available, else the core CSR adjacency).
|
|
924
932
|
*
|
|
925
|
-
* Documented compromise: the engine exposes ONE highlight channel
|
|
926
|
-
*
|
|
933
|
+
* Documented compromise: the engine exposes ONE highlight channel, so the
|
|
934
|
+
* neighbor ring is pushed through setSelectedIndices
|
|
927
935
|
* ONLY when that cannot lie about real selection state — selection empty
|
|
928
936
|
* and uncontrolled. The ring is a visual, never a store write; the next
|
|
929
937
|
* selection push overwrites it. Opt out via `highlightNeighbors: false`.
|
|
930
|
-
* `hops` is
|
|
938
|
+
* Only `hops: 1` is currently supported.
|
|
931
939
|
*/
|
|
932
940
|
focusNode(id: NodeId, opts?: {
|
|
933
941
|
highlightNeighbors?: boolean;
|
|
934
942
|
hops?: 1;
|
|
935
943
|
}): readonly NodeId[];
|
|
936
944
|
/**
|
|
937
|
-
*
|
|
945
|
+
* emphasis ring WITHOUT the camera: ring `id` (null clears). The light
|
|
938
946
|
* op keyboard navigation needs — arrowing a list must not fly the camera on
|
|
939
947
|
* every keystroke (`focusNode` stays ring + zoom + neighbors). Unknown ids
|
|
940
948
|
* are a silent no-op (a stale row racing a model swap is data, not an
|
|
941
949
|
* error); `emphasisRing: false` suppresses it entirely. The target is
|
|
942
950
|
* STICKY: it survives structural commits and context recovery until it is
|
|
943
951
|
* cleared, its id departs the model, or pointer hover supersedes it
|
|
944
|
-
* (emphasis belongs to the latest action — the
|
|
952
|
+
* (emphasis belongs to the latest action — the rule).
|
|
945
953
|
*/
|
|
946
954
|
emphasizeNode(id: NodeId | null): void;
|
|
947
955
|
/**
|
|
948
|
-
*
|
|
956
|
+
* Typed context-menu channel, opened from a DOM presenter. Label
|
|
949
957
|
* divs are `pointerEvents: 'auto'` by design (click-to-focus), so a
|
|
950
958
|
* right-click on one never reaches the engine canvas — without this seam
|
|
951
959
|
* the nodes prominent enough to carry labels are exactly the ones whose
|
|
952
960
|
* right-click falls through to the browser's native menu. Emits the SAME
|
|
953
961
|
* 'contextMenu' event the canvas gesture produces; `screen` is
|
|
954
|
-
* container-relative CSS px
|
|
962
|
+
* container-relative CSS px. Unknown ids are a
|
|
955
963
|
* silent no-op (a stale label racing a model swap is data, not an error).
|
|
956
964
|
*/
|
|
957
965
|
requestNodeContextMenu(id: NodeId, screen: readonly [number, number]): void;
|
|
958
966
|
setSelection(ids: readonly NodeId[] | SelectionState): void;
|
|
959
967
|
selectNodes(ids: readonly NodeId[]): void;
|
|
960
968
|
selectEdges(ids: readonly EdgeId[]): void;
|
|
961
|
-
/**
|
|
969
|
+
/** group namespace: validate against the CURRENT resolved
|
|
962
970
|
* groups (unknown ids dropped, duplicates collapse) and store in
|
|
963
971
|
* groups-array order — the group analog of accepted-base ordering. Never
|
|
964
972
|
* touches the node/edge namespaces; the group namespace is always
|
|
965
|
-
* instance-owned
|
|
973
|
+
* instance-owned. */
|
|
966
974
|
selectGroups(ids: readonly string[]): void;
|
|
967
975
|
/** Expand to the 1-hop neighborhood of `id` (or of the current selection). */
|
|
968
976
|
selectNeighbors(id?: NodeId): void;
|
|
@@ -970,9 +978,9 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
970
978
|
invertSelection(): void;
|
|
971
979
|
clearSelection(): void;
|
|
972
980
|
/**
|
|
973
|
-
*
|
|
981
|
+
* lasso: resolve the SCREEN-coordinate polygon to node ids
|
|
974
982
|
* via `engine.pointsInPolygon`, drop hidden ids, then replace (default) or
|
|
975
|
-
* union (`additive`) the node selection through the same
|
|
983
|
+
* union (`additive`) the node selection through the same ownership
|
|
976
984
|
* path as every other mutator (controlled → intent only). Returns the
|
|
977
985
|
* resolved lasso ids (accepted-base order) regardless of ownership; empty
|
|
978
986
|
* when the engine is not ready or lacks `pointsInPolygon`.
|
|
@@ -992,19 +1000,19 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
992
1000
|
* tolerance. No-op (null) on the native route. */
|
|
993
1001
|
sampleEdgeClick(screen: readonly [number, number]): AcceptedEdge<E> | null;
|
|
994
1002
|
/**
|
|
995
|
-
* @internal
|
|
1003
|
+
* @internal Shared hit-test/overlay cadence: the count of engine
|
|
996
1004
|
* onFrame ticks this session. THE one clock every sampling route throttles
|
|
997
1005
|
* against (node hover and link picking share it by construction — there is
|
|
998
|
-
* no second cadence timer anywhere; the
|
|
999
|
-
* The
|
|
1000
|
-
*
|
|
1006
|
+
* no second cadence timer anywhere; the lint rule enforces it).
|
|
1007
|
+
* The degradation ladder's defer-link-picking step arms/disarms against this counter;
|
|
1008
|
+
* telemetry reads it for idle-wakeup accounting.
|
|
1001
1009
|
*/
|
|
1002
1010
|
getFrameCadence(): number;
|
|
1003
1011
|
/**
|
|
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
|
-
*
|
|
1012
|
+
* @internal op counters (live object — snapshot before comparing):
|
|
1013
|
+
* the perf-gate-delta suite proves the brush fast path does O(Δ) work
|
|
1014
|
+
* zero full recomposes/refreshes/cascades across a scrub. telemetry
|
|
1015
|
+
* will fold these into `lastCommitMs`-adjacent accounting.
|
|
1008
1016
|
*/
|
|
1009
1017
|
getPerfCounters(): Readonly<{
|
|
1010
1018
|
brushSlotsTranslated: number;
|
|
@@ -1014,12 +1022,12 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1014
1022
|
fullEdgeRecomposes: number;
|
|
1015
1023
|
}>;
|
|
1016
1024
|
/**
|
|
1017
|
-
*
|
|
1025
|
+
* telemetry snapshot: counts, byte estimates, queue depth,
|
|
1018
1026
|
* revisions, last-commit phase decomposition, active ladder steps,
|
|
1019
1027
|
* execution lane, range availability, and the pressure mirror — never raw
|
|
1020
1028
|
* attrs or ids. Synchronous in every lifecycle state (pre-scene fields
|
|
1021
1029
|
* read zero/absent). `validate` is folded into `derive` until the
|
|
1022
|
-
* columnar lane splits acceptance from derivation
|
|
1030
|
+
* columnar lane splits acceptance from derivation.
|
|
1023
1031
|
*/
|
|
1024
1032
|
getPerfSnapshot(): GraphPerfSnapshot;
|
|
1025
1033
|
hideNodes(ids: readonly NodeId[]): void;
|
|
@@ -1028,24 +1036,24 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1028
1036
|
pinNode(id: NodeId, xy?: readonly [number, number]): void;
|
|
1029
1037
|
unpinNode(id: NodeId): void;
|
|
1030
1038
|
clearPins(): void;
|
|
1031
|
-
/**
|
|
1039
|
+
/** PERSISTENT pins: pin ids AT THEIR CURRENT POSITION via
|
|
1032
1040
|
* engine.setPinnedIndices — no position payload in v0.10. Independent of
|
|
1033
1041
|
* transient drag pinning (`pins`): the engine receives the UNION of both
|
|
1034
|
-
* slices, so releasing a drag pin leaves a persistent pin held.
|
|
1042
|
+
* slices, so releasing a drag pin leaves a persistent pin held.
|
|
1035
1043
|
* ownership mirrors groups: once the host supplies `pinnedNodeIds` (null
|
|
1036
1044
|
* included) the ops fire the 'pinnedChange' intent instead of writing.
|
|
1037
1045
|
* Unknown ids drop; departed ids prune through the ownership path on
|
|
1038
|
-
* model changes
|
|
1046
|
+
* model changes. */
|
|
1039
1047
|
pinNodes(ids: readonly NodeId[]): void;
|
|
1040
1048
|
/** Release persistent pins (see {@link pinNodes}); unpinned ids no-op. */
|
|
1041
1049
|
unpinNodes(ids: readonly NodeId[]): void;
|
|
1042
|
-
/** Add one group definition (same
|
|
1050
|
+
/** Add one group definition (same acyclic/singly-parented validation as the
|
|
1043
1051
|
* `groups` prop — a violating spec is ONE 'config-error' and a no-op). */
|
|
1044
1052
|
groupNodes(spec: GroupSpec): void;
|
|
1045
1053
|
/** Remove one group definition; its id prunes from SelectionState.groupIds
|
|
1046
1054
|
* through the ownership path. Unknown ids no-op (dev-mode warning). */
|
|
1047
1055
|
ungroup(groupId: string): void;
|
|
1048
|
-
/** Collapse/expand one group as a
|
|
1056
|
+
/** Collapse/expand one group as a structural diff. Works on manual
|
|
1049
1057
|
* groups AND groupBy-derived groups (the residue toggle). Same-value
|
|
1050
1058
|
* calls are exact no-ops (zero publishes, zero commits). */
|
|
1051
1059
|
setGroupCollapsed(groupId: string, collapsed: boolean): void;
|
|
@@ -1059,12 +1067,12 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1059
1067
|
* rejected, since that would close a containment cycle).
|
|
1060
1068
|
*
|
|
1061
1069
|
* One publish and at most one structural commit (E1). A no-member fold is
|
|
1062
|
-
* an exact no-op. Records a
|
|
1070
|
+
* an exact no-op. Records a 'folds' history step.
|
|
1063
1071
|
*/
|
|
1064
1072
|
foldNode(id: NodeId, opts?: {
|
|
1065
1073
|
memberIds?: readonly NodeId[];
|
|
1066
1074
|
}): void;
|
|
1067
|
-
/** Unfolds `id`, returning its members to the scene as a
|
|
1075
|
+
/** Unfolds `id`, returning its members to the scene as a structural
|
|
1068
1076
|
* diff. Unknown or unfolded ids are exact no-ops. */
|
|
1069
1077
|
unfoldNode(id: NodeId): void;
|
|
1070
1078
|
/** The members `id` currently stands for, or null when it is not folded.
|
|
@@ -1075,30 +1083,30 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1075
1083
|
} | null;
|
|
1076
1084
|
/** Current stage-4 clusters over the physical scene: ordered keys, member
|
|
1077
1085
|
* ids, the force center labels anchor to while hot, and the settled
|
|
1078
|
-
* centroid (null until a
|
|
1086
|
+
* centroid (null until a readback or a fixed-layout commit). Empty
|
|
1079
1087
|
* when no `clusters` spec is active. Clusters synthesize nothing — the
|
|
1080
|
-
* scene is byte-identical with and without a spec
|
|
1088
|
+
* scene is byte-identical with and without a spec. */
|
|
1081
1089
|
getClusters(): readonly ResolvedCluster[];
|
|
1082
|
-
/**
|
|
1083
|
-
* them into SelectionState.nodeIds through the standard
|
|
1090
|
+
/** resolve a cluster (by key) to its MEMBER node ids and write
|
|
1091
|
+
* them into SelectionState.nodeIds through the standard ownership
|
|
1084
1092
|
* path — clusters have no id namespace of their own in selection.
|
|
1085
1093
|
* `additive` unions with the current node selection. Unknown keys no-op. */
|
|
1086
1094
|
selectCluster(key: string, opts?: {
|
|
1087
1095
|
additive?: boolean;
|
|
1088
1096
|
}): void;
|
|
1089
1097
|
/**
|
|
1090
|
-
*
|
|
1098
|
+
* isolate: hard-scope the graph to the CURRENT node selection
|
|
1091
1099
|
* `subgraph: { seedIds: selection.nodeIds }` through the SAME path as the
|
|
1092
1100
|
* host-update prop. No-op when nothing is selected. Ownership note (v0.5):
|
|
1093
1101
|
* `subgraph` is UNCONTROLLED-ONLY — always instance-owned; the prop and
|
|
1094
1102
|
* this method write the same state, last writer wins.
|
|
1095
1103
|
*/
|
|
1096
1104
|
isolateSelection(): void;
|
|
1097
|
-
/**
|
|
1098
|
-
* returns with cached positions
|
|
1105
|
+
/** clear the hard scope (`subgraph: null`) — the full accepted model
|
|
1106
|
+
* returns with cached positions. */
|
|
1099
1107
|
resetIsolation(): void;
|
|
1100
1108
|
/**
|
|
1101
|
-
*
|
|
1109
|
+
* Ego-expansion of `id` (default 1 hop) through the configured
|
|
1102
1110
|
* ExpansionService. The result is gated by admission (declared revision
|
|
1103
1111
|
* dependencies + dataset lineage + seed existence — abort is only an
|
|
1104
1112
|
* optimization) and merges through ONE awaited atomic overlay
|
|
@@ -1117,8 +1125,8 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1117
1125
|
* Undoes `id`'s own expansions — the navigation Back button, NOT a
|
|
1118
1126
|
* containment operation. Aborts `id`'s pending expansion AND explicitly
|
|
1119
1127
|
* removes the overlays its past expansions committed (plus their scope
|
|
1120
|
-
* accretion). Committed overlay DATA otherwise persists
|
|
1121
|
-
* removeOverlay
|
|
1128
|
+
* accretion). Committed overlay DATA otherwise persists until
|
|
1129
|
+
* `removeOverlay()` or a replacing snapshot; this IS that explicit removal for
|
|
1122
1130
|
* expansion overlays.
|
|
1123
1131
|
*
|
|
1124
1132
|
* On a node that was never expanded from, this does nothing — there is no
|
|
@@ -1127,7 +1135,7 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1127
1135
|
* different word for navigation history.
|
|
1128
1136
|
*/
|
|
1129
1137
|
retractExpansion(id: NodeId): void;
|
|
1130
|
-
/**
|
|
1138
|
+
/** expansion bookkeeping for `id`: committed overlay records with
|
|
1131
1139
|
* request id, provenance, and the ids each expansion revealed. */
|
|
1132
1140
|
getExpansionOverlays(id: NodeId): readonly ExpansionOverlayRecord[];
|
|
1133
1141
|
/**
|
|
@@ -1136,21 +1144,21 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1136
1144
|
* `serviceCacheKey` over EXACTLY the service's declared revision
|
|
1137
1145
|
* dimensions ({@link SEARCH_CACHE_LIMIT}-entry LRU; a second call with an
|
|
1138
1146
|
* 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
|
|
1147
|
+
* cancels superseded work (a NEWER query aborts the older in-flight call
|
|
1140
1148
|
* the older promise rejects `OrbitOperationError {code:'aborted'}`), and
|
|
1141
1149
|
* rejects stale results at admission (declared revision drift or dataset
|
|
1142
1150
|
* lineage change → the same typed 'aborted' rejection with a distinct
|
|
1143
1151
|
* staleness message; the store is untouched). A successful search
|
|
1144
1152
|
* publishes `store.search = {query, results}` — `node` populated for
|
|
1145
1153
|
* in-model ids — in ONE store publication. Search NEVER changes
|
|
1146
|
-
* scope/filter semantics and never fetches graph data
|
|
1154
|
+
* scope/filter semantics and never fetches graph data.
|
|
1147
1155
|
*/
|
|
1148
1156
|
search(query: string, opts?: {
|
|
1149
1157
|
limit?: number;
|
|
1150
1158
|
}): Promise<readonly SearchResult<N>[]>;
|
|
1151
1159
|
/** Clear `store.search` to null (e.g. the <GraphSearch> input emptied). */
|
|
1152
1160
|
clearSearch(): void;
|
|
1153
|
-
/**
|
|
1161
|
+
/** path query + atomic emphasis: resolves via the path
|
|
1154
1162
|
* service (local BFS default); null = unreachable (a RESULT). Emphasis is
|
|
1155
1163
|
* session-local — released by clearPath, any selection mutation, undo/
|
|
1156
1164
|
* redo, or a scene rebuild; never a history step; never serialized. */
|
|
@@ -1158,21 +1166,21 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1158
1166
|
clearPath(): void;
|
|
1159
1167
|
getActivePath(): PathResult | null;
|
|
1160
1168
|
/**
|
|
1161
|
-
*
|
|
1162
|
-
* AND
|
|
1169
|
+
* Result contract: a result id in the current rendered scene
|
|
1170
|
+
* AND mask-visible is focused (`focusNode`) → `{status:'focused'}`.
|
|
1163
1171
|
* Otherwise classification ONLY — 'not-loaded' (absent from the accepted
|
|
1164
1172
|
* model), 'out-of-scope' (in the model but outside the hard scope),
|
|
1165
1173
|
* 'filtered' (in the scene but mask-hidden). Never mutates scope or
|
|
1166
|
-
* filters — the host reacts explicitly
|
|
1174
|
+
* filters — the host reacts explicitly.
|
|
1167
1175
|
*/
|
|
1168
1176
|
activateSearchResult(result: SearchResult<N>): SearchActivation;
|
|
1169
1177
|
pauseSimulation(): void;
|
|
1170
1178
|
resumeSimulation(): void;
|
|
1171
1179
|
isSimulationRunning(): boolean;
|
|
1172
|
-
/**
|
|
1180
|
+
/** screenshot: delegates to the engine; null when unsupported/not ready. */
|
|
1173
1181
|
captureScreenshot(): Promise<Blob | null>;
|
|
1174
1182
|
/**
|
|
1175
|
-
*
|
|
1183
|
+
* binding-detected reduced-motion media preference. The EFFECTIVE
|
|
1176
1184
|
* value is `accessibility.reducedMotion ?? v` — when reduced, camera
|
|
1177
1185
|
* durations (fitView/setViewport/focusNode) coerce to 0.
|
|
1178
1186
|
*/
|
|
@@ -1180,7 +1188,7 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1180
1188
|
/**
|
|
1181
1189
|
* The crossfilter session facade, or null until the `crossfilter` prop has
|
|
1182
1190
|
* configured dimensions over an accepted base. Delegates to the
|
|
1183
|
-
* typed-column engine; `setBrush` routes visibility deltas into the
|
|
1191
|
+
* typed-column engine; `setBrush` routes visibility deltas into the
|
|
1184
1192
|
* soft mask (buffers-only commit, zero relayout) and resolves after the
|
|
1185
1193
|
* publish. Brush slot deltas are BASE indices; under a hard scope
|
|
1186
1194
|
* out-of-scope rows have no scene slot and simply do not mask anything.
|
|
@@ -1200,12 +1208,12 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1200
1208
|
/** Re-apply the most recently undone entry. False when nothing to redo. */
|
|
1201
1209
|
redo(): boolean;
|
|
1202
1210
|
/**
|
|
1203
|
-
* Serialize the exploration state
|
|
1211
|
+
* Serialize the exploration state: camera, selection, hidden ids,
|
|
1204
1212
|
* isolation, groups (manual specs verbatim; under `groupBy` only collapsed
|
|
1205
1213
|
* `{key, collapsed}` pairs — membership recomputes on restore), pins,
|
|
1206
1214
|
* folds, layout, crossfilter brushes in declaration order, the Scale-valued
|
|
1207
1215
|
* styling subset, and the host's `dataRef` verbatim. The predicate `filter`
|
|
1208
|
-
* and
|
|
1216
|
+
* and expansion records are never serialized. The sync form carries
|
|
1209
1217
|
* no positions: reproduction is best-effort via the layout descriptor.
|
|
1210
1218
|
*/
|
|
1211
1219
|
getViewState(opts?: {
|
|
@@ -1213,7 +1221,7 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1213
1221
|
}): GraphViewState;
|
|
1214
1222
|
/**
|
|
1215
1223
|
* Async form: additionally embeds quantized coordinates for the VISIBLE
|
|
1216
|
-
* (post-mask) set, read once from the engine
|
|
1224
|
+
* (post-mask) set, read once from the engine as event-time readback.
|
|
1217
1225
|
* Restores as a frozen fixed-equivalent — pixel-faithful regardless of
|
|
1218
1226
|
* engine nondeterminism. Past `maxPositions` (default 100 000) the call
|
|
1219
1227
|
* rejects `export-materialization-too-large`; persist the layout through
|
|
@@ -1224,7 +1232,7 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1224
1232
|
maxPositions?: number;
|
|
1225
1233
|
}): Promise<GraphViewState>;
|
|
1226
1234
|
/**
|
|
1227
|
-
* Atomically restore a serialized view
|
|
1235
|
+
* Atomically restore a serialized view. NEVER partially applies:
|
|
1228
1236
|
* structural validation and the version gate run first (reject whole with
|
|
1229
1237
|
* one 'invalid-view-state' diagnostic); then the dataRef canonical
|
|
1230
1238
|
* comparison — a mismatch fires the 'viewStateMismatch' event INSTEAD of
|
|
@@ -1233,7 +1241,7 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1233
1241
|
* undo/redo uses, so a restore is itself undoable. Embedded positions
|
|
1234
1242
|
* apply as a frozen fixed-equivalent (one replay-style commit, then the
|
|
1235
1243
|
* simulation pauses — a later explicit layout change or reheat unfreezes).
|
|
1236
|
-
* A state touching a
|
|
1244
|
+
* A state touching a controlled slice (or styling, once a restore
|
|
1237
1245
|
* callback exists) resolves 'missing-restore-callback' until the aggregate
|
|
1238
1246
|
* protocol is registered.
|
|
1239
1247
|
*/
|
|
@@ -1272,14 +1280,14 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1272
1280
|
* entity over ONE pinned revision — a mid-stream commit never mixes
|
|
1273
1281
|
* epochs. Closing the generator releases the pin. */
|
|
1274
1282
|
exportDataStream(scope?: 'visible' | 'accepted'): AsyncGenerator<string, void, undefined>;
|
|
1275
|
-
/** Bounded id → [x, y] map from one position readback
|
|
1283
|
+
/** Bounded id → [x, y] map from one position readback. */
|
|
1276
1284
|
exportLayout(opts?: {
|
|
1277
1285
|
limit?: number;
|
|
1278
1286
|
}): Promise<ReadonlyMap<NodeId, readonly [number, number]>>;
|
|
1279
1287
|
/** Memory-bounded `{"id","x","y"}` JSONL over one pinned readback. */
|
|
1280
1288
|
exportLayoutStream(): AsyncGenerator<string, void, undefined>;
|
|
1281
1289
|
/**
|
|
1282
|
-
*
|
|
1290
|
+
* legend surface: the active Scale on a styling channel plus its
|
|
1283
1291
|
* resolved domain (sequential/diverging — resolved through the SAME frozen
|
|
1284
1292
|
* DomainStore coordinate the projection uses) or categorical legend rows
|
|
1285
1293
|
* (declared-domain order first including empty categories, then extra seen
|
|
@@ -1288,9 +1296,9 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1288
1296
|
*/
|
|
1289
1297
|
getScaleInfo(channel: 'nodeColor' | 'nodeSize'): ScaleChannelInfo<N> | null;
|
|
1290
1298
|
/**
|
|
1291
|
-
*
|
|
1292
|
-
* public million-entry Map). Null for unknown ids
|
|
1293
|
-
* values; lazily computes the degree family on first use.
|
|
1299
|
+
* metric read for one node id via the core id→index map (never a
|
|
1300
|
+
* public million-entry Map). Null for unknown ids, unknown metrics, and
|
|
1301
|
+
* null values; lazily computes the degree family on first use.
|
|
1294
1302
|
*/
|
|
1295
1303
|
getMetricValue(metric: MetricName, id: NodeId): number | null;
|
|
1296
1304
|
getRevisions(): Revisions;
|
|
@@ -1300,54 +1308,54 @@ interface GraphInstance<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1300
1308
|
* way a hover/selection consumer resolves `store.hover.edgeId` or
|
|
1301
1309
|
* `selection.edgeIds` to real records. Undefined for unknown ids. */
|
|
1302
1310
|
getEdge(id: EdgeId): AcceptedEdge<E> | undefined;
|
|
1303
|
-
/** Scene ids that are
|
|
1311
|
+
/** Scene ids that are mask-visible (scope ∧ mask), scene order. */
|
|
1304
1312
|
getVisibleNodeIds(): readonly NodeId[];
|
|
1305
|
-
/** Scene roster: scope applied, mask NOT applied — the
|
|
1313
|
+
/** Scene roster: scope applied, mask NOT applied — the navigator's
|
|
1306
1314
|
* entry list, which must still LIST masked/hidden nodes and expose their
|
|
1307
1315
|
* state in text rather than dropping them. */
|
|
1308
1316
|
getSceneNodeIds(): readonly NodeId[];
|
|
1309
|
-
/**
|
|
1317
|
+
/** accessibility config stash (navigator/live-region consumers). */
|
|
1310
1318
|
getAccessibility(): AccessibilityConfig<N> | undefined;
|
|
1311
1319
|
}
|
|
1312
|
-
/**
|
|
1320
|
+
/** consecutive same-dimension brush moves within this window merge
|
|
1313
1321
|
* into one history entry (scrub/drag coalescing). */
|
|
1314
1322
|
declare const BRUSH_HISTORY_COALESCE_MS = 500;
|
|
1315
|
-
/**
|
|
1323
|
+
/** timeline defaults. */
|
|
1316
1324
|
declare const TIMELINE_TICK_MS_DEFAULT = 100;
|
|
1317
1325
|
declare const TIMELINE_STEP_DEFAULT = 0.01;
|
|
1318
|
-
/**
|
|
1326
|
+
/** search defaults. */
|
|
1319
1327
|
declare const SEARCH_LIMIT_DEFAULT = 20;
|
|
1320
1328
|
declare const SEARCH_CACHE_LIMIT = 32;
|
|
1321
1329
|
declare function createGraphInstance<N = Record<string, unknown>, E = Record<string, unknown>>(opts: CreateGraphInstanceOptions<N, E>): GraphInstance<N, E>;
|
|
1322
1330
|
|
|
1323
1331
|
/**
|
|
1324
|
-
*
|
|
1332
|
+
* DOM label lane — pure candidate selection.
|
|
1325
1333
|
*
|
|
1326
1334
|
* `selectLabelCandidates` is a pure, deterministic ranking function: no engine,
|
|
1327
1335
|
* 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)
|
|
1336
|
+
* re-rank triggers only (viewport idle, model change, config change, settle)
|
|
1329
1337
|
* NEVER per frame. Positions are the reconciler's CPU cache (space coords);
|
|
1330
1338
|
* per-frame work elsewhere is a pure O(k) projection of the winners.
|
|
1331
1339
|
*
|
|
1332
|
-
* Selection rules
|
|
1333
|
-
*
|
|
1334
|
-
*
|
|
1335
|
-
*
|
|
1336
|
-
*
|
|
1337
|
-
*
|
|
1338
|
-
*
|
|
1339
|
-
*
|
|
1340
|
-
*
|
|
1341
|
-
*
|
|
1342
|
-
*
|
|
1343
|
-
*
|
|
1340
|
+
* Selection rules:
|
|
1341
|
+
* - Zoom-LOD: below `minZoom` the lane is empty EXCEPT `showFor` ids, which
|
|
1342
|
+
* bypass the zoom gate but stay viewport-culled.
|
|
1343
|
+
* - `showFor` claims capacity FIRST in accepted-base order. When the
|
|
1344
|
+
* in-viewport `showFor` set alone exceeds capacity k, accepted-base order
|
|
1345
|
+
* wins deterministically and `overloadCount` reports the omissions (one
|
|
1346
|
+
* `label-overload` diagnostic upstream — no winner churn).
|
|
1347
|
+
* - Remaining capacity fills with viewport-visible nodes ranked by
|
|
1348
|
+
* `getWeight` (else degree), ties broken by accepted-base order.
|
|
1349
|
+
* - Visibility comes from the engine's `pointsInRect` when available, else a
|
|
1350
|
+
* CPU cull of cached positions through the viewport transform. Nodes with
|
|
1351
|
+
* unknown (NaN) cached positions are unplaceable on the CPU path.
|
|
1344
1352
|
*/
|
|
1345
1353
|
|
|
1346
1354
|
/** A capacity winner before per-frame projection assigns screen coordinates. */
|
|
1347
1355
|
type LabelCandidate = Omit<LabelPlacement, 'x' | 'y'>;
|
|
1348
|
-
/**
|
|
1356
|
+
/** default ranked-candidate cap. */
|
|
1349
1357
|
declare const LABEL_MAX_VISIBLE_DEFAULT = 64;
|
|
1350
|
-
/**
|
|
1358
|
+
/** policy maximum for `maxVisible`. */
|
|
1351
1359
|
declare const LABEL_MAX_VISIBLE_CAP = 1024;
|
|
1352
1360
|
interface LabelCandidateViewport {
|
|
1353
1361
|
zoom: number;
|
|
@@ -1360,7 +1368,7 @@ interface SelectLabelCandidatesArgs<N = Record<string, unknown>> {
|
|
|
1360
1368
|
scene: RenderScene;
|
|
1361
1369
|
/**
|
|
1362
1370
|
* Accepted nodes in accepted-base order. Under the 'rebuild' index policy
|
|
1363
|
-
*
|
|
1371
|
+
* scene index i IS accepted-base position i, so `nodes[i]` is the
|
|
1364
1372
|
* node behind `scene.idByIndex[i]`.
|
|
1365
1373
|
*/
|
|
1366
1374
|
nodes: readonly GraphNode<N>[];
|
|
@@ -1385,11 +1393,11 @@ interface LabelCandidateResult {
|
|
|
1385
1393
|
declare function selectLabelCandidates<N = Record<string, unknown>>(args: SelectLabelCandidatesArgs<N>): LabelCandidateResult;
|
|
1386
1394
|
|
|
1387
1395
|
/**
|
|
1388
|
-
*
|
|
1396
|
+
* CSR adjacency over columnar link buffers.
|
|
1389
1397
|
*
|
|
1390
|
-
* Pure, engine-free helpers shared by local expansion
|
|
1391
|
-
* dirtying
|
|
1392
|
-
*
|
|
1398
|
+
* Pure, engine-free helpers shared by local expansion, incident-edge
|
|
1399
|
+
* dirtying, and the engine-facing `neighborIndices` interaction helper.
|
|
1400
|
+
* Build is O(L) via two counting passes over the flat
|
|
1393
1401
|
* `[src0, tgt0, src1, tgt1, …]` link buffer — no comparison sort and no
|
|
1394
1402
|
* per-link allocation (the only allocations are the CSR arrays plus one
|
|
1395
1403
|
* point-sized cursor array).
|
|
@@ -1420,9 +1428,9 @@ declare function buildAdjacency(links: Uint32Array, pointCount: number): Adjacen
|
|
|
1420
1428
|
*/
|
|
1421
1429
|
declare function neighborsOf(adj: Adjacency, index: number): Uint32Array;
|
|
1422
1430
|
/**
|
|
1423
|
-
* CSR INCIDENCE: point → incident EDGE SLOTS
|
|
1431
|
+
* CSR INCIDENCE: point → incident EDGE SLOTS. The plain Adjacency
|
|
1424
1432
|
* stores neighbor points, which cannot drive incident-edge dirtying — the
|
|
1425
|
-
*
|
|
1433
|
+
* delta cascade needs the edge slots themselves. Same two counting
|
|
1426
1434
|
* passes and the same conventions: undirected (each edge listed under both
|
|
1427
1435
|
* endpoints), parallel edges repeat, and a self-loop lists its edge slot
|
|
1428
1436
|
* twice under its point — consumers' per-edge operations are idempotent, so
|
|
@@ -1437,26 +1445,26 @@ interface Incidence {
|
|
|
1437
1445
|
}
|
|
1438
1446
|
|
|
1439
1447
|
/**
|
|
1440
|
-
*
|
|
1448
|
+
* CPU link-pick fallback: a uniform grid over ALL links in SPACE
|
|
1441
1449
|
* coordinates (cell ≈ median link length), used when the engine lacks native
|
|
1442
1450
|
* `linkAt` (`capabilities.linkPicking === false`). Backs
|
|
1443
|
-
* `onEdgeClick`/`onEdgeHover` via the
|
|
1451
|
+
* `onEdgeClick`/`onEdgeHover` via the index→edge mapping.
|
|
1444
1452
|
*
|
|
1445
1453
|
* Coordinate contract: everything here is SPACE coordinates. Callers invert
|
|
1446
1454
|
* the affine `Viewport` (space = (screen − center)/zoom + [x, y]) and pass a
|
|
1447
1455
|
* space-unit tolerance (`tolerancePx / zoom`).
|
|
1448
1456
|
*
|
|
1449
1457
|
* Invalidation contract (normative — wave 2 enforces arming):
|
|
1450
|
-
* - The grid indexes a POSITION SNAPSHOT (the
|
|
1451
|
-
*
|
|
1452
|
-
*
|
|
1453
|
-
*
|
|
1454
|
-
*
|
|
1458
|
+
* - The grid indexes a POSITION SNAPSHOT (the `posBuf` CPU mirror as of
|
|
1459
|
+
* the build call). It NEVER observes live simulation positions: while the
|
|
1460
|
+
* simulation is hot (alpha above threshold) or an animated `setLayout`
|
|
1461
|
+
* transition runs, picking is DISARMED by the caller; on settle one
|
|
1462
|
+
* per-event `getPositions` refreshes the mirror and the grid is rebuilt.
|
|
1455
1463
|
* - Rebuild ONLY on structural change (link buffer changed) or position
|
|
1456
|
-
*
|
|
1457
|
-
*
|
|
1458
|
-
*
|
|
1459
|
-
*
|
|
1464
|
+
* sync. The grid is invariant under camera moves and visibility-mask
|
|
1465
|
+
* changes: the mask is applied per candidate at QUERY time via the
|
|
1466
|
+
* optional `visible` callback (pure pass-through until mask wiring supplies the real
|
|
1467
|
+
* mask through it).
|
|
1460
1468
|
*
|
|
1461
1469
|
* Build uses two counting-sort passes into CSR grid arrays (`cellOffsets`,
|
|
1462
1470
|
* `cellLinkIds`). Each segment is inserted with a supercover grid traversal,
|
|
@@ -1464,11 +1472,11 @@ interface Incidence {
|
|
|
1464
1472
|
* every cell in its O(cols * rows) axis-aligned bounding box. There is no
|
|
1465
1473
|
* comparison sort or per-link allocation beyond the CSR arrays (plus
|
|
1466
1474
|
* O(cells) cursor and O(links) query stamps). Links with a non-finite endpoint
|
|
1467
|
-
* (NaN-tombstoned points
|
|
1475
|
+
* (NaN-tombstoned points) are excluded and can never be returned. A hard
|
|
1468
1476
|
* CSR-entry cap deterministically degrades pathological builds to exact O(L)
|
|
1469
1477
|
* query scans with no cell-link payload.
|
|
1470
1478
|
*/
|
|
1471
|
-
/**
|
|
1479
|
+
/** visibility mask applied per candidate at query time. */
|
|
1472
1480
|
type LinkVisibilityMask = (linkIndex: number) => boolean;
|
|
1473
1481
|
/**
|
|
1474
1482
|
* Read-only view of the built grid (diagnostics/tests). The typed arrays are
|
|
@@ -1514,8 +1522,8 @@ declare class LinkPickIndex {
|
|
|
1514
1522
|
*/
|
|
1515
1523
|
build(positions: Float32Array, links: Uint32Array): void;
|
|
1516
1524
|
/**
|
|
1517
|
-
* Incremental build: yields whenever `now
|
|
1518
|
-
* scheduler can spread the work across idle frames
|
|
1525
|
+
* Incremental build: yields whenever `now − sliceStart ≥ budgetMs` so a
|
|
1526
|
+
* scheduler can spread the work across idle frames.
|
|
1519
1527
|
* Pure generator — no rAF/timers here; the caller owns scheduling.
|
|
1520
1528
|
*
|
|
1521
1529
|
* The previous grid stays armed and queryable until the new one commits on
|
|
@@ -1528,7 +1536,7 @@ declare class LinkPickIndex {
|
|
|
1528
1536
|
* Nearest link within `tolerance` (space units) of the space point
|
|
1529
1537
|
* `(x, y)`, or null. Scans the candidate cells covering the tolerance
|
|
1530
1538
|
* disc's bounding box (typically the 3×3 neighborhood; more when the
|
|
1531
|
-
* tolerance exceeds the cell size), applies the optional
|
|
1539
|
+
* tolerance exceeds the cell size), applies the optional visibility
|
|
1532
1540
|
* mask per candidate, then runs the exact point→segment distance test.
|
|
1533
1541
|
* Nearest wins; exact-distance ties break toward the LOWER link index.
|
|
1534
1542
|
* Returns null while unbuilt/invalidated (picking disarmed).
|
|
@@ -1537,45 +1545,45 @@ declare class LinkPickIndex {
|
|
|
1537
1545
|
}
|
|
1538
1546
|
|
|
1539
1547
|
/**
|
|
1540
|
-
*
|
|
1548
|
+
* Edge-picking facade.
|
|
1541
1549
|
*
|
|
1542
1550
|
* The core commits to ONE route per mount session, read from
|
|
1543
1551
|
* `engine.capabilities.linkPicking` at engine-ready time — never method
|
|
1544
1552
|
* sniffing, and never re-evaluated (a capability record mutated after ready
|
|
1545
1553
|
* changes nothing):
|
|
1546
1554
|
*
|
|
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
|
|
1555
|
+
* - 'native': the adapter delivers `onLinkClick`/`onLinkHover` host events
|
|
1556
|
+
* itself; the facade is inert (arm/disarm/queries no-op and
|
|
1557
|
+
* `pickLinkAt` returns null) and the instance maps link indices
|
|
1558
|
+
* to typed edges straight off the host events.
|
|
1559
|
+
* - 'fallback': the instance samples the pointer on the shared throttle
|
|
1560
|
+
* cadence and resolves hits through a `LinkPickIndex` uniform
|
|
1561
|
+
* grid that is armed ONLY while the simulation is settled.
|
|
1562
|
+
*
|
|
1563
|
+
* Arming protocol for the fallback route:
|
|
1564
|
+
* - `arm(positions, links)` runs on simulation settle (fed by one per-event
|
|
1565
|
+
* `getPositions` readback) and on structural commits that do NOT restart
|
|
1566
|
+
* the simulation (fixed layout). The grid builds CHUNKED via
|
|
1567
|
+
* `LinkPickIndex.buildChunked` under a per-slice time budget so large link
|
|
1568
|
+
* sets never block; small builds complete synchronously in the first
|
|
1569
|
+
* slice. While an initial build is in flight queries return null; a
|
|
1570
|
+
* position-sync REBUILD keeps answering from the previous grid until the
|
|
1571
|
+
* new one commits.
|
|
1572
|
+
* - `disarm` runs on any commit that restarts the simulation: targets are
|
|
1573
|
+
* moving and the position mirror is stale, so queries return null until
|
|
1574
|
+
* the next settle re-arms.
|
|
1575
|
+
* - Rebuilds happen ONLY on structural change or position sync — the grid is
|
|
1576
|
+
* invariant under camera moves and mask changes (the visibility mask
|
|
1577
|
+
* is applied per candidate at query time; initially a pass-through stub).
|
|
1578
|
+
*
|
|
1579
|
+
* Tolerance: a query converts screen px to space units via
|
|
1572
1580
|
* `screenToSpace` of two points `EDGE_PICK_TOLERANCE_PX` apart, then uses
|
|
1573
1581
|
* `max(4px, half the median link width)` scaled by that factor.
|
|
1574
1582
|
*/
|
|
1575
1583
|
|
|
1576
1584
|
type EdgePickRoute = 'native' | 'fallback';
|
|
1577
1585
|
/** Screen-space pick tolerance floor, and the probe distance used to measure
|
|
1578
|
-
* the screen→space scale
|
|
1586
|
+
* the screen→space scale. */
|
|
1579
1587
|
declare const EDGE_PICK_TOLERANCE_PX = 4;
|
|
1580
1588
|
/**
|
|
1581
1589
|
* Median link width in px from a projected linkWidth buffer (deterministic
|
|
@@ -1588,9 +1596,9 @@ interface EdgePickingFacadeOptions {
|
|
|
1588
1596
|
route: EdgePickRoute;
|
|
1589
1597
|
/** Affine screen→space conversion; null = conversion unavailable. */
|
|
1590
1598
|
screenToSpace: (p: readonly [number, number]) => readonly [number, number] | null;
|
|
1591
|
-
/** Median link width in px for the tolerance floor; default
|
|
1599
|
+
/** Median link width in px for the tolerance floor; default => 0. */
|
|
1592
1600
|
medianLinkWidthPx?: () => number;
|
|
1593
|
-
/**
|
|
1601
|
+
/** visibility mask pass-through stub (mutable via the setter). */
|
|
1594
1602
|
linkVisible?: LinkVisibilityMask;
|
|
1595
1603
|
/** Chunked-build slice budget in ms. */
|
|
1596
1604
|
buildBudgetMs?: number;
|
|
@@ -1622,7 +1630,7 @@ declare class EdgePickingFacade {
|
|
|
1622
1630
|
arm(positions: Float32Array, links: Uint32Array): void;
|
|
1623
1631
|
/** Simulation went hot: drop the grid; queries null until the next settle. */
|
|
1624
1632
|
disarm(): void;
|
|
1625
|
-
/**
|
|
1633
|
+
/** mask pass-through stub: applied per candidate at query time. */
|
|
1626
1634
|
setLinkVisibilityMask(mask: LinkVisibilityMask | null): void;
|
|
1627
1635
|
/**
|
|
1628
1636
|
* Nearest link index within tolerance of a SCREEN point, or null when: on
|
|
@@ -1646,8 +1654,8 @@ declare class EdgePickingFacade {
|
|
|
1646
1654
|
type EdgePairCounters = Map<NodeId, Map<NodeId, number>>;
|
|
1647
1655
|
|
|
1648
1656
|
/**
|
|
1649
|
-
*
|
|
1650
|
-
* instance-local acceptance queue
|
|
1657
|
+
* revisioned ingestion — pure session/overlay bookkeeping plus the
|
|
1658
|
+
* instance-local acceptance queue.
|
|
1651
1659
|
*
|
|
1652
1660
|
* Everything here is data-structure work: no store, no engine, no DOM. The
|
|
1653
1661
|
* GraphInstance wires these helpers into its publication path (instance.ts).
|
|
@@ -1655,7 +1663,7 @@ type EdgePairCounters = Map<NodeId, Map<NodeId, number>>;
|
|
|
1655
1663
|
* Ordering model: every admission takes a monotonically increasing ticket from
|
|
1656
1664
|
* the instance's AcceptanceQueue. Overlay rows are stamped with their
|
|
1657
1665
|
* admission ticket, and the merge folds base + overlays strictly in ticket
|
|
1658
|
-
* order — arrival at the queue IS the global admission order
|
|
1666
|
+
* order — arrival at the queue IS the global admission order.
|
|
1659
1667
|
*/
|
|
1660
1668
|
|
|
1661
1669
|
/** Byte backpressure budget default: 8 MiB of admitted-but-unflushed payload. */
|
|
@@ -1687,7 +1695,7 @@ interface RowTally {
|
|
|
1687
1695
|
count: number;
|
|
1688
1696
|
samples: string[];
|
|
1689
1697
|
}
|
|
1690
|
-
/** Per-session staging tallies; surfaced as diagnostics ONLY at commit
|
|
1698
|
+
/** Per-session staging tallies; surfaced as diagnostics ONLY at commit. */
|
|
1691
1699
|
interface StagingTallies {
|
|
1692
1700
|
invalidNodes: RowTally;
|
|
1693
1701
|
duplicateNodes: RowTally;
|
|
@@ -1712,11 +1720,11 @@ interface SessionContribution<N = Record<string, unknown>, E = Record<string, un
|
|
|
1712
1720
|
readonly edges: StampedEdge<E>[];
|
|
1713
1721
|
readonly nodeIds: Set<NodeId>;
|
|
1714
1722
|
readonly edgeIds: Set<string>;
|
|
1715
|
-
/** Exact ordered endpoint tuple → next synthesized-parallel-edge k
|
|
1723
|
+
/** Exact ordered endpoint tuple → next synthesized-parallel-edge k. */
|
|
1716
1724
|
readonly pairCounters: EdgePairCounters;
|
|
1717
1725
|
/** Public prefix lengths: rows before these counts have been published
|
|
1718
1726
|
* (progressive flush / commit); appends only grow the arrays, so a prefix
|
|
1719
|
-
* is sufficient. Atomic sessions stay at 0 until commit
|
|
1727
|
+
* is sufficient. Atomic sessions stay at 0 until commit. */
|
|
1720
1728
|
publicNodeCount: number;
|
|
1721
1729
|
publicEdgeCount: number;
|
|
1722
1730
|
}
|
|
@@ -1728,8 +1736,8 @@ interface StageResult {
|
|
|
1728
1736
|
/**
|
|
1729
1737
|
* Validate and stage one batch's rows into a session contribution.
|
|
1730
1738
|
*
|
|
1731
|
-
*
|
|
1732
|
-
* before nodes
|
|
1739
|
+
* Object-lane-equivalent row validation WITHOUT endpoint checks — edges may arrive
|
|
1740
|
+
* before nodes: they occupy their eventual edge records here; endpoint
|
|
1733
1741
|
* resolution happens at merge. Within a session, duplicate ids are
|
|
1734
1742
|
* first-wins-dropped (tallied for commit diagnostics); cross-session
|
|
1735
1743
|
* collisions are the merge's shadowing concern, not staging's.
|
|
@@ -1745,7 +1753,7 @@ interface MergeBase<N = Record<string, unknown>, E = Record<string, unknown>> {
|
|
|
1745
1753
|
/** Endpoint-resolved base edges. */
|
|
1746
1754
|
edges: readonly AcceptedEdge<E>[];
|
|
1747
1755
|
/** Replace-session bases retain edge records whose endpoints were missing
|
|
1748
|
-
* at commit; a later overlay node can still resolve them
|
|
1756
|
+
* at commit; a later overlay node can still resolve them. */
|
|
1749
1757
|
pendingEdges: readonly StampedEdge<E>[];
|
|
1750
1758
|
diagnostics: readonly GraphDiagnostic[];
|
|
1751
1759
|
}
|
|
@@ -1761,7 +1769,7 @@ interface MergeResult<N = Record<string, unknown>, E = Record<string, unknown>>
|
|
|
1761
1769
|
duplicateEdgeSamples: readonly string[];
|
|
1762
1770
|
/** overlayId (or BASE_PENDING_KEY) → edges still awaiting an endpoint. The
|
|
1763
1771
|
* pending-endpoint index: these occupy edge records but never enter the
|
|
1764
|
-
* engine link buffer
|
|
1772
|
+
* engine link buffer. */
|
|
1765
1773
|
pendingBySource: ReadonlyMap<string, number>;
|
|
1766
1774
|
pendingEdgeCount: number;
|
|
1767
1775
|
}
|
|
@@ -1776,15 +1784,15 @@ declare function baseFromContribution<N, E>(datasetKey: string, sourceRevision:
|
|
|
1776
1784
|
* Merge the accepted base with every overlay contribution's PUBLIC rows.
|
|
1777
1785
|
*
|
|
1778
1786
|
* - Node collisions: earliest admission (ticket order; base first) wins.
|
|
1779
|
-
*
|
|
1780
|
-
*
|
|
1781
|
-
*
|
|
1787
|
+
* Later rows stay retained-but-shadowed in their contribution and are
|
|
1788
|
+
* tallied for the single count-aggregated 'overlay-node-shadowed' info
|
|
1789
|
+
* diagnostic.
|
|
1782
1790
|
* - Edge ids: first admission wins; later same-id records are dropped
|
|
1783
|
-
*
|
|
1784
|
-
*
|
|
1785
|
-
*
|
|
1786
|
-
*
|
|
1787
|
-
*
|
|
1791
|
+
* (tallied). An edge whose endpoints are not all present in the merged
|
|
1792
|
+
* node set is pending — it occupies its record and the pending-endpoint
|
|
1793
|
+
* accounting but is excluded from `accepted.edges` (and therefore from the
|
|
1794
|
+
* engine link buffer). Endpoints arriving later resolve it on the next
|
|
1795
|
+
* merge (same or different batch/overlay).
|
|
1788
1796
|
*
|
|
1789
1797
|
* Deterministic: output depends only on the base and the stamped rows.
|
|
1790
1798
|
*/
|
|
@@ -1793,20 +1801,20 @@ declare function mergeModel<N, E>(base: MergeBase<N, E>, overlays: readonly Sess
|
|
|
1793
1801
|
declare function mergeDiagnostics(merge: MergeResult<unknown, unknown>): GraphDiagnostic[];
|
|
1794
1802
|
/**
|
|
1795
1803
|
* Commit-time diagnostics for one session: staging tallies plus the dangling
|
|
1796
|
-
* (still-pending-endpoint) count — emitted ONLY at session commit
|
|
1804
|
+
* (still-pending-endpoint) count — emitted ONLY at session commit.
|
|
1797
1805
|
*/
|
|
1798
1806
|
declare function sessionCommitDiagnostics(tallies: StagingTallies, danglingCount: number, danglingSamples?: readonly string[]): GraphDiagnostic[];
|
|
1799
1807
|
|
|
1800
1808
|
/**
|
|
1801
|
-
*
|
|
1809
|
+
* hard subgraph scope — pure resolution over the accepted base.
|
|
1802
1810
|
*
|
|
1803
1811
|
* `resolveScope` turns a `SubgraphSpec` into the exact node/edge subset the
|
|
1804
1812
|
* reconciler should be fed: seeds validated against the accepted base
|
|
1805
1813
|
* (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
|
|
1814
|
+
* BFS over the CSR adjacency of the ACCEPTED BASE (not the scene — the
|
|
1815
|
+
* default expansion service walks this same index), and edges cascaded
|
|
1816
|
+
* through `cascadeEdges`, the single edge-survival primitive (an edge
|
|
1817
|
+
* survives iff BOTH endpoints survive) that soft masks reuse.
|
|
1810
1818
|
*
|
|
1811
1819
|
* Everything here is synchronous and engine-free. The async
|
|
1812
1820
|
* `ExpansionService` seam (./services) exists for `expandNode`; `hops`
|
|
@@ -1814,21 +1822,21 @@ declare function sessionCommitDiagnostics(tallies: StagingTallies, danglingCount
|
|
|
1814
1822
|
*/
|
|
1815
1823
|
|
|
1816
1824
|
/**
|
|
1817
|
-
* THE edge-cascade primitive
|
|
1825
|
+
* THE edge-cascade primitive: an edge survives iff BOTH of its
|
|
1818
1826
|
* endpoints survive. O(E); the only allocation is the output array, which
|
|
1819
1827
|
* holds references to the input edge objects (never copies).
|
|
1820
1828
|
*
|
|
1821
|
-
* Exported for reuse by
|
|
1829
|
+
* Exported for reuse by soft masks and any other subset producer.
|
|
1822
1830
|
*/
|
|
1823
1831
|
declare function cascadeEdges<E>(edges: readonly AcceptedEdge<E>[], survives: (id: NodeId) => boolean): AcceptedEdge<E>[];
|
|
1824
1832
|
/**
|
|
1825
|
-
* Builds the
|
|
1833
|
+
* Builds the CSR adjacency of an accepted base: endpoints are positions
|
|
1826
1834
|
* in `accepted.nodes` (accepted-base order). Every accepted edge has resolved
|
|
1827
|
-
* endpoints by contract
|
|
1835
|
+
* endpoints by contract, so this cannot throw on
|
|
1828
1836
|
* a well-formed `AcceptedGraph`.
|
|
1829
1837
|
*/
|
|
1830
1838
|
declare function buildAcceptedAdjacency(accepted: AcceptedGraph<unknown, unknown>): Adjacency;
|
|
1831
|
-
/** Output of `resolveScope`: the exact subset to feed the reconciler
|
|
1839
|
+
/** Output of `resolveScope`: the exact subset to feed the reconciler. */
|
|
1832
1840
|
interface ResolvedScope<N = Record<string, unknown>, E = Record<string, unknown>> {
|
|
1833
1841
|
/** Surviving node ids (seeds + hop expansion), membership-query form. */
|
|
1834
1842
|
nodeIds: ReadonlySet<NodeId>;
|
|
@@ -1838,31 +1846,31 @@ interface ResolvedScope<N = Record<string, unknown>, E = Record<string, unknown>
|
|
|
1838
1846
|
edges: readonly AcceptedEdge<E>[];
|
|
1839
1847
|
}
|
|
1840
1848
|
/**
|
|
1841
|
-
* Resolves a hard-scope spec against the accepted base
|
|
1849
|
+
* Resolves a hard-scope spec against the accepted base.
|
|
1842
1850
|
*
|
|
1843
1851
|
* - `spec.seedIds` are validated against `accepted.nodeIndex`: ids unknown to
|
|
1844
|
-
*
|
|
1852
|
+
* the accepted base are dropped, duplicates collapse to one seed.
|
|
1845
1853
|
* - `spec.hops` (default 0; negative/non-finite values clamp to 0, fractions
|
|
1846
|
-
*
|
|
1847
|
-
*
|
|
1848
|
-
*
|
|
1854
|
+
* floor) expands the survivor set by BFS over the accepted-base adjacency.
|
|
1855
|
+
* This is the synchronous local path — the default `ExpansionService`
|
|
1856
|
+
* walks the same index for `expandNode`.
|
|
1849
1857
|
* - `adjacency` may be a caller-cached `buildAcceptedAdjacency(accepted)`
|
|
1850
|
-
*
|
|
1851
|
-
*
|
|
1858
|
+
* result; pass `null` to have one built on demand (only when `hops > 0`
|
|
1859
|
+
* and there are surviving seeds — hop-0 resolution never builds it).
|
|
1852
1860
|
* - Output preserves accepted-base order for both nodes and edges; edges are
|
|
1853
|
-
*
|
|
1861
|
+
* cascaded through {@link cascadeEdges}.
|
|
1854
1862
|
*/
|
|
1855
1863
|
declare function resolveScope<N, E>(accepted: AcceptedGraph<N, E>, spec: SubgraphSpec, adjacency: Adjacency | null): ResolvedScope<N, E>;
|
|
1856
1864
|
|
|
1857
1865
|
/**
|
|
1858
|
-
*
|
|
1866
|
+
* revision-aware services — pure sequencing/admission/caching helpers
|
|
1859
1867
|
* plus the built-in local expansion service.
|
|
1860
1868
|
*
|
|
1861
1869
|
* The correctness gate is `admitServiceResult`: a result is admitted only if
|
|
1862
1870
|
* every revision dimension the service DECLARED is unchanged since the call
|
|
1863
1871
|
* was issued; undeclared dimensions never invalidate (so e.g. the local
|
|
1864
1872
|
* expansion service, declaring only 'source', survives unrelated model/scope
|
|
1865
|
-
* advances from other overlay publications). Abort is an optimization
|
|
1873
|
+
* advances from other overlay publications). Abort is an optimization
|
|
1866
1874
|
* admission is the gate.
|
|
1867
1875
|
*
|
|
1868
1876
|
* Cache keys include service identity, canonical-JSON request parameters,
|
|
@@ -1872,7 +1880,7 @@ declare function resolveScope<N, E>(accepted: AcceptedGraph<N, E>, spec: Subgrap
|
|
|
1872
1880
|
* these primitives to its acceptance queue.
|
|
1873
1881
|
*/
|
|
1874
1882
|
|
|
1875
|
-
/** Current value of each
|
|
1883
|
+
/** Current value of each revision dimension (a Revisions subset). */
|
|
1876
1884
|
interface RevisionSnapshot {
|
|
1877
1885
|
source: number | string | null;
|
|
1878
1886
|
model: number;
|
|
@@ -1896,13 +1904,13 @@ interface RequestContextHandle {
|
|
|
1896
1904
|
abort(reason?: unknown): void;
|
|
1897
1905
|
}
|
|
1898
1906
|
/**
|
|
1899
|
-
* Builds the `RequestContext` a service call receives
|
|
1907
|
+
* Builds the `RequestContext` a service call receives: dataset,
|
|
1900
1908
|
* the three revision dimensions at issue time, a request id, and a
|
|
1901
1909
|
* cancellation signal owned by the returned controller.
|
|
1902
1910
|
*/
|
|
1903
1911
|
declare function createRequestContext(args: CreateRequestContextArgs): RequestContextHandle;
|
|
1904
1912
|
interface AdmitServiceResultArgs {
|
|
1905
|
-
/** The service's `revisionDependencies
|
|
1913
|
+
/** The service's `revisionDependencies`. */
|
|
1906
1914
|
declared: readonly RevisionDimension[];
|
|
1907
1915
|
/** Revision values when the request was issued. */
|
|
1908
1916
|
at: RevisionSnapshot;
|
|
@@ -1910,7 +1918,7 @@ interface AdmitServiceResultArgs {
|
|
|
1910
1918
|
now: RevisionSnapshot;
|
|
1911
1919
|
}
|
|
1912
1920
|
/**
|
|
1913
|
-
*
|
|
1921
|
+
* stale-result rule: admit a service result iff EVERY declared revision
|
|
1914
1922
|
* dimension is unchanged between issue and admission. Undeclared dimensions
|
|
1915
1923
|
* never invalidate. Declaring nothing means the result is admissible under
|
|
1916
1924
|
* any drift; declaring all three restores strict point-in-model semantics.
|
|
@@ -1921,13 +1929,13 @@ interface ServiceCacheKeyArgs {
|
|
|
1921
1929
|
/** Request parameters (JSON-shaped); key order is canonicalized away. */
|
|
1922
1930
|
params: unknown;
|
|
1923
1931
|
datasetKey: string;
|
|
1924
|
-
/** The service's declared revision dependencies
|
|
1932
|
+
/** The service's declared revision dependencies. */
|
|
1925
1933
|
declared: readonly RevisionDimension[];
|
|
1926
1934
|
/** Current revision values; only declared dimensions enter the key. */
|
|
1927
1935
|
revisions: RevisionSnapshot;
|
|
1928
1936
|
}
|
|
1929
1937
|
/**
|
|
1930
|
-
*
|
|
1938
|
+
* cache-key rule: service identity + canonical-JSON params +
|
|
1931
1939
|
* `datasetKey` + EXACTLY the declared revision dimensions' current values.
|
|
1932
1940
|
* Declaration-list order and params key order do not affect the key;
|
|
1933
1941
|
* undeclared revision drift never changes it.
|
|
@@ -1936,11 +1944,11 @@ declare function serviceCacheKey(args: ServiceCacheKeyArgs): string;
|
|
|
1936
1944
|
/** Accepted-base view the local service walks (thunked for lazy wiring). */
|
|
1937
1945
|
interface LocalExpansionBase<N = Record<string, unknown>, E = Record<string, unknown>> {
|
|
1938
1946
|
accepted: AcceptedGraph<N, E>;
|
|
1939
|
-
/** The
|
|
1947
|
+
/** The CSR adjacency of `accepted` (see buildAcceptedAdjacency). */
|
|
1940
1948
|
adjacency: Adjacency;
|
|
1941
1949
|
}
|
|
1942
1950
|
/**
|
|
1943
|
-
* The built-in
|
|
1951
|
+
* The built-in expansion service: walks the core's accepted-base
|
|
1944
1952
|
* adjacency — INCLUDING currently out-of-scope nodes (it reads the base, not
|
|
1945
1953
|
* the scene) — with zero config and zero network. Declares
|
|
1946
1954
|
* `revisionDependencies: ['source']`, so unrelated model/scope advances
|
|
@@ -1954,7 +1962,7 @@ interface LocalExpansionBase<N = Record<string, unknown>, E = Record<string, unk
|
|
|
1954
1962
|
* admission gate is authoritative).
|
|
1955
1963
|
*
|
|
1956
1964
|
* The response is the CLOSED N-hop neighborhood (seeds included, plus every
|
|
1957
|
-
* edge between returned nodes);
|
|
1965
|
+
* edge between returned nodes); the overlay merger dedupes rows
|
|
1958
1966
|
* that already exist, so returning already-known seeds is correct.
|
|
1959
1967
|
*/
|
|
1960
1968
|
declare function createLocalExpansionService<N = Record<string, unknown>, E = Record<string, unknown>>(getBase: () => LocalExpansionBase<N, E>): ExpansionService<N, E>;
|
|
@@ -1967,7 +1975,7 @@ type RegisterExpansionResult = {
|
|
|
1967
1975
|
onto: string;
|
|
1968
1976
|
};
|
|
1969
1977
|
/**
|
|
1970
|
-
* Pure in-flight-expansion ledger
|
|
1978
|
+
* Pure in-flight-expansion ledger: within one valid scope revision a
|
|
1971
1979
|
* second `expandNode(id)` while one is in flight coalesces into the pending
|
|
1972
1980
|
* call; expansions of DISTINCT ids run (and complete) concurrently and
|
|
1973
1981
|
* independently. `retractExpansion(id)` uses `abort(id)` to drop that id's
|
|
@@ -2005,14 +2013,15 @@ declare class PendingExpansions {
|
|
|
2005
2013
|
}
|
|
2006
2014
|
|
|
2007
2015
|
/**
|
|
2008
|
-
*
|
|
2016
|
+
* history command-stack kernel.
|
|
2009
2017
|
*
|
|
2010
2018
|
* v0.7 implements the KERNEL only: value-diff commands, transactions,
|
|
2011
2019
|
* coalescing, and a bounded stack. The kernel never touches stores — it is a
|
|
2012
|
-
* pure application seam: `undo
|
|
2020
|
+
* pure application seam: `undo`/`redo` return the command list to apply
|
|
2013
2021
|
* (already inverted, in application order) and move the cursor; the instance
|
|
2014
|
-
*
|
|
2015
|
-
* Ownership-mode acknowledgement walks and view-state integration
|
|
2022
|
+
* applies commands to its slices and publishes.
|
|
2023
|
+
* Ownership-mode acknowledgement walks and view-state integration remain
|
|
2024
|
+
* instance-level concerns.
|
|
2016
2025
|
*
|
|
2017
2026
|
* Commands are `{ slice, before, after }` value diffs — never closures — so
|
|
2018
2027
|
* they are serializable and invert by swapping. The kernel freezes and stores
|
|
@@ -2032,7 +2041,7 @@ interface HistoryDepths {
|
|
|
2032
2041
|
readonly redoDepth: number;
|
|
2033
2042
|
}
|
|
2034
2043
|
interface HistoryKernelOptions {
|
|
2035
|
-
/** Stack bound; oldest entry evicted past it
|
|
2044
|
+
/** Stack bound; oldest entry evicted past it. */
|
|
2036
2045
|
limit?: number;
|
|
2037
2046
|
/** `false` = the `history: false` prop: everything is a no-op, depths stay 0. */
|
|
2038
2047
|
enabled?: boolean;
|
|
@@ -2050,7 +2059,7 @@ declare class HistoryKernel {
|
|
|
2050
2059
|
private readonly debug;
|
|
2051
2060
|
private readonly undoStack;
|
|
2052
2061
|
private readonly redoStack;
|
|
2053
|
-
/** Nested begin
|
|
2062
|
+
/** Nested begin joins the outer transaction (depth-counted). */
|
|
2054
2063
|
private txDepth;
|
|
2055
2064
|
private txCommands;
|
|
2056
2065
|
private txLabel;
|
|
@@ -2080,7 +2089,7 @@ declare class HistoryKernel {
|
|
|
2080
2089
|
* transaction. The kernel freezes and stores what it is given.
|
|
2081
2090
|
*/
|
|
2082
2091
|
record(slice: string, before: unknown, after: unknown): void;
|
|
2083
|
-
/** Close a transaction; the outermost end
|
|
2092
|
+
/** Close a transaction; the outermost end pushes one stack entry. */
|
|
2084
2093
|
end(): void;
|
|
2085
2094
|
/**
|
|
2086
2095
|
* Move the cursor back one entry and return its commands, already inverted
|
|
@@ -2110,28 +2119,28 @@ declare class HistoryKernel {
|
|
|
2110
2119
|
}
|
|
2111
2120
|
|
|
2112
2121
|
/**
|
|
2113
|
-
*
|
|
2122
|
+
* soft filtering — pure, engine-free expr evaluation,
|
|
2114
2123
|
* validation, compilation, and canonical keying. The instance feeds compiled
|
|
2115
2124
|
* filters into the SoftMask kernel (./mask); nothing here touches the store.
|
|
2116
2125
|
*
|
|
2117
|
-
* Semantics
|
|
2126
|
+
* Semantics:
|
|
2118
2127
|
* - `field` addresses `attrs[field]`; the literal field 'id' addresses the
|
|
2119
|
-
*
|
|
2120
|
-
* - The serializable expr path NEVER throws: junk shapes and
|
|
2121
|
-
*
|
|
2122
|
-
*
|
|
2123
|
-
*
|
|
2128
|
+
* entity id (it wins even when `attrs.id` exists).
|
|
2129
|
+
* - The serializable expr path NEVER throws: junk shapes and numeric-hygiene
|
|
2130
|
+
* failures (non-numeric / non-finite values under 'range') simply fail the
|
|
2131
|
+
* item. `validateFilterExpr` is the reporting channel — the instance turns
|
|
2132
|
+
* its findings into validation errors at applyHostUpdate.
|
|
2124
2133
|
* - eq/neq/in compare numbers with Object.is semantics EXCEPT that NaN is
|
|
2125
|
-
*
|
|
2126
|
-
*
|
|
2134
|
+
* never equal to anything (so ±0 are distinct and NaN ≠ NaN); everything
|
|
2135
|
+
* else compares with plain ===. No coercion, ever ('5' never equals 5).
|
|
2127
2136
|
* - Function predicates are black boxes: throws are caught and aggregated
|
|
2128
|
-
*
|
|
2129
|
-
*
|
|
2130
|
-
*
|
|
2131
|
-
*
|
|
2137
|
+
* into ONE {count, samples} result the caller converts to a single
|
|
2138
|
+
* 'filter-error' diagnostic (batching — O(categories), never O(bad
|
|
2139
|
+
* rows)). A throwing predicate FAILS OPEN: the item stays visible, so a
|
|
2140
|
+
* buggy predicate can never blank the graph.
|
|
2132
2141
|
* - Structural specs compare by canonical key (`canonicalFilterKey`) so
|
|
2133
|
-
*
|
|
2134
|
-
*
|
|
2142
|
+
* identity churn with equal structure never re-evaluates; function
|
|
2143
|
+
* predicates key by reference identity via a WeakMap-issued token.
|
|
2135
2144
|
*/
|
|
2136
2145
|
|
|
2137
2146
|
/**
|
|
@@ -2141,7 +2150,7 @@ declare class HistoryKernel {
|
|
|
2141
2150
|
declare function resolveFilterField(item: GraphNode<unknown> | AcceptedEdge<unknown>, field: string): unknown;
|
|
2142
2151
|
/**
|
|
2143
2152
|
* Evaluates one expr for one item via a field resolver. NEVER throws:
|
|
2144
|
-
* malformed shapes and
|
|
2153
|
+
* malformed shapes and numeric-hygiene failures fail the item (return false);
|
|
2145
2154
|
* a malformed operand under 'not' also fails (junk never passes by double
|
|
2146
2155
|
* negation). `range` requires a finite number value; missing bounds are
|
|
2147
2156
|
* unbounded; includeMin/includeMax default true. `is-null` matches null OR
|
|
@@ -2151,7 +2160,7 @@ declare function evaluateFilterExpr(expr: FilterExpr, resolve: (field: string) =
|
|
|
2151
2160
|
/**
|
|
2152
2161
|
* Structural checker for serializable exprs: unknown ops and malformed
|
|
2153
2162
|
* shapes are reported as `$`-rooted path strings ([] = valid). The instance
|
|
2154
|
-
* converts findings into validation errors at applyHostUpdate
|
|
2163
|
+
* converts findings into validation errors at applyHostUpdate
|
|
2155
2164
|
* evaluation itself never throws on the same junk, it just fails the item.
|
|
2156
2165
|
*/
|
|
2157
2166
|
declare function validateFilterExpr(expr: FilterExpr): string[];
|
|
@@ -2178,7 +2187,7 @@ declare function compileEdgeFilter<N = Record<string, unknown>, E = Record<strin
|
|
|
2178
2187
|
* Canonical structural key for a FilterSpec or FilterExpr: object keys are
|
|
2179
2188
|
* sorted, arrays preserve order, undefined-valued keys are omitted, and
|
|
2180
2189
|
* function predicates map to a unique reference-identity token. Two inputs
|
|
2181
|
-
* with equal keys are
|
|
2190
|
+
* with equal keys are semantically equivalent — the instance skips re-evaluation
|
|
2182
2191
|
* when the key of an incoming filter matches the active one (identity churn
|
|
2183
2192
|
* with equal structure never re-evaluates; swapping a function reference
|
|
2184
2193
|
* always does).
|
|
@@ -2186,14 +2195,14 @@ declare function compileEdgeFilter<N = Record<string, unknown>, E = Record<strin
|
|
|
2186
2195
|
declare function canonicalFilterKey(specOrExpr: unknown): string;
|
|
2187
2196
|
|
|
2188
2197
|
/**
|
|
2189
|
-
*
|
|
2198
|
+
* soft-mask kernel — failure COUNTERS, never Sets and
|
|
2190
2199
|
* never per-source bit positions (so there is no 32-dimension cap).
|
|
2191
2200
|
*
|
|
2192
2201
|
* The mask owns four Uint16 counter lanes: hideFailures and dimFailures for
|
|
2193
2202
|
* nodes AND edges. Every acquired {@link MaskSource} contributes +1 to a
|
|
2194
2203
|
* lane counter for each slot it currently fails and -1 when that slot
|
|
2195
2204
|
* re-enters. A slot is visible iff hideFailures === 0 and dimmed iff visible
|
|
2196
|
-
* AND dimFailures > 0
|
|
2205
|
+
* AND dimFailures > 0.
|
|
2197
2206
|
*
|
|
2198
2207
|
* Delta discipline: each source keeps its previous failing membership as
|
|
2199
2208
|
* per-source Uint8 flag columns plus a dense slot list, so replacing a
|
|
@@ -2203,9 +2212,9 @@ declare function canonicalFilterKey(specOrExpr: unknown): string;
|
|
|
2203
2212
|
* compares against the state at the previous drain, so a fail-then-restore
|
|
2204
2213
|
* inside one drain period nets to no emission.
|
|
2205
2214
|
*
|
|
2206
|
-
* {@link SoftMask.applyNodeCascadeToEdges} implements
|
|
2215
|
+
* {@link SoftMask.applyNodeCascadeToEdges} implements SINGLE edge-
|
|
2207
2216
|
* survival rule over the mask lane: an edge hide-fails iff EITHER endpoint
|
|
2208
|
-
* hide-fails (equivalently: survives iff BOTH endpoints survive
|
|
2217
|
+
* hide-fails (equivalently: survives iff BOTH endpoints survive
|
|
2209
2218
|
* scope.ts `cascadeEdges` is the hard-scope variant of the same rule). It is
|
|
2210
2219
|
* fed through one dedicated internal cascade source from the node hide lane:
|
|
2211
2220
|
* O(E) recompute per call, but only edges whose state changed produce
|
|
@@ -2213,12 +2222,12 @@ declare function canonicalFilterKey(specOrExpr: unknown): string;
|
|
|
2213
2222
|
*
|
|
2214
2223
|
* Overflow guard: increments clamp at 0xFFFF and latch a one-time
|
|
2215
2224
|
* {@link SoftMask.overflowed} flag the caller reports (an implementation may
|
|
2216
|
-
* widen to Uint32 without changing semantics
|
|
2217
|
-
* balanced increments/decrements whenever a clear
|
|
2225
|
+
* widen to Uint32 without changing semantics). Debug builds assert
|
|
2226
|
+
* balanced increments/decrements whenever a clear/release leaves zero
|
|
2218
2227
|
* held memberships.
|
|
2219
2228
|
*/
|
|
2220
2229
|
|
|
2221
|
-
/** Default muted alpha for dimmed entities
|
|
2230
|
+
/** Default muted alpha for dimmed entities. */
|
|
2222
2231
|
declare const DIM_ALPHA_DEFAULT = 0.15;
|
|
2223
2232
|
/**
|
|
2224
2233
|
* A handle contributing failure memberships to the mask. Setting a lane
|
|
@@ -2231,7 +2240,7 @@ interface MaskSource {
|
|
|
2231
2240
|
setNodeFailures(hideIdx: Iterable<number> | null, dimIdx?: Iterable<number> | null): void;
|
|
2232
2241
|
setEdgeFailures(hideIdx: Iterable<number> | null, dimIdx?: Iterable<number> | null): void;
|
|
2233
2242
|
/**
|
|
2234
|
-
* O(Δ) delta form of the HIDE lane
|
|
2243
|
+
* O(Δ) delta form of the HIDE lane: adds/removes individual slots
|
|
2235
2244
|
* from this source's membership without replaying the full set. Freely
|
|
2236
2245
|
* composable with the replace form — a later setNodeFailures re-baselines.
|
|
2237
2246
|
* `crossings`, when supplied, receives the slots whose hide counter
|
|
@@ -2242,8 +2251,8 @@ interface MaskSource {
|
|
|
2242
2251
|
updateEdgeFailures(addHide: ArrayLike<number> | null, removeHide: ArrayLike<number> | null): void;
|
|
2243
2252
|
/** Empties all four lane memberships (the source stays usable). */
|
|
2244
2253
|
clear(): void;
|
|
2245
|
-
/** clear
|
|
2246
|
-
* release
|
|
2254
|
+
/** clear + permanently retires the handle (further set/clear throws;
|
|
2255
|
+
* release itself is idempotent). */
|
|
2247
2256
|
release(): void;
|
|
2248
2257
|
}
|
|
2249
2258
|
/** Per-CALL zero-crossings reported by the delta ops (reused, callee-cleared). */
|
|
@@ -2253,7 +2262,7 @@ interface MaskCrossings {
|
|
|
2253
2262
|
/** Slots whose hide counter went nonzero → 0 in this call. */
|
|
2254
2263
|
becameClear: number[];
|
|
2255
2264
|
}
|
|
2256
|
-
/**
|
|
2265
|
+
/** O(Δ) operation counters (see resetStats/stats on SoftMask). */
|
|
2257
2266
|
interface MaskStats {
|
|
2258
2267
|
/** Slots visited by delta membership ops (add + remove attempts). */
|
|
2259
2268
|
slotsVisited: number;
|
|
@@ -2264,7 +2273,7 @@ interface MaskStats {
|
|
|
2264
2273
|
}
|
|
2265
2274
|
/**
|
|
2266
2275
|
* One drain payload. The four index arrays are REUSED across drains — copy
|
|
2267
|
-
* before the next drainDirty
|
|
2276
|
+
* before the next drainDirty call if you need to keep them.
|
|
2268
2277
|
*/
|
|
2269
2278
|
interface MaskDrain {
|
|
2270
2279
|
/** Node slots whose hide-visibility (hideFailures 0 ↔ nonzero) flipped
|
|
@@ -2289,18 +2298,18 @@ declare class SoftMask {
|
|
|
2289
2298
|
private readonly edgeHideLane;
|
|
2290
2299
|
private readonly edgeDimLane;
|
|
2291
2300
|
private readonly sources;
|
|
2292
|
-
/** Dedicated internal source implementing the
|
|
2301
|
+
/** Dedicated internal source implementing the node→edge cascade. */
|
|
2293
2302
|
private cascadeSource;
|
|
2294
2303
|
private overflowedFlag;
|
|
2295
2304
|
/** Total memberships currently held across all sources and lanes. */
|
|
2296
2305
|
private totalHeld;
|
|
2297
|
-
/**
|
|
2306
|
+
/** O(Δ) op counters. */
|
|
2298
2307
|
private readonly statsBox;
|
|
2299
2308
|
constructor(nodeCapacity: number, edgeCapacity: number);
|
|
2300
2309
|
get nodeCapacity(): number;
|
|
2301
2310
|
get edgeCapacity(): number;
|
|
2302
2311
|
/** One-time latch: some counter hit 0xFFFF and an increment was dropped.
|
|
2303
|
-
* Counts may drift afterwards; the caller reports it
|
|
2312
|
+
* Counts may drift afterwards; the caller reports it. */
|
|
2304
2313
|
get overflowed(): boolean;
|
|
2305
2314
|
get nodeHideFailures(): Uint16Array;
|
|
2306
2315
|
get nodeDimFailures(): Uint16Array;
|
|
@@ -2308,18 +2317,18 @@ declare class SoftMask {
|
|
|
2308
2317
|
get edgeDimFailures(): Uint16Array;
|
|
2309
2318
|
/**
|
|
2310
2319
|
* Grows capacities for structure changes (existing slot state is
|
|
2311
|
-
* preserved; new slots start fully visible). Capacities never shrink
|
|
2320
|
+
* preserved; new slots start fully visible). Capacities never shrink
|
|
2312
2321
|
* a smaller value is a no-op for that dimension.
|
|
2313
2322
|
*/
|
|
2314
2323
|
grow(nodeCapacity: number, edgeCapacity: number): void;
|
|
2315
|
-
/** Registers a new failure source. No cap on source count
|
|
2324
|
+
/** Registers a new failure source. No cap on source count. */
|
|
2316
2325
|
acquire(name: string): MaskSource;
|
|
2317
2326
|
/**
|
|
2318
|
-
*
|
|
2327
|
+
* edge cascade over the mask lane: recomputes, from the CURRENT node
|
|
2319
2328
|
* hide lane, the set of edges with at least one hidden endpoint, and feeds
|
|
2320
2329
|
* 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
|
-
*
|
|
2330
|
+
* `links` is the flat `[src0, tgt0, src1, tgt1, …]` node-slot pair buffer;
|
|
2331
|
+
* edge slot i has endpoints at links[2i]/[2i+1].
|
|
2323
2332
|
* O(E) scan per call — typically once per drain — but only edges whose
|
|
2324
2333
|
* cascade state changed produce counter deltas (and thus dirty entries).
|
|
2325
2334
|
* Edges beyond `links.length / 2` are treated as having no hidden
|
|
@@ -2327,7 +2336,7 @@ declare class SoftMask {
|
|
|
2327
2336
|
*/
|
|
2328
2337
|
applyNodeCascadeToEdges(links: Uint32Array): void;
|
|
2329
2338
|
/**
|
|
2330
|
-
* O(incident-edges) delta form of the
|
|
2339
|
+
* O(incident-edges) delta form of the cascade: for each node
|
|
2331
2340
|
* whose HIDE visibility crossed zero, recompute only its incident edges'
|
|
2332
2341
|
* cascade state from the CURRENT node counters and apply the delta through
|
|
2333
2342
|
* the same internal cascade source the full form uses — the two compose
|
|
@@ -2343,10 +2352,10 @@ declare class SoftMask {
|
|
|
2343
2352
|
* drain). The returned index arrays are reused by the next drain.
|
|
2344
2353
|
*/
|
|
2345
2354
|
drainDirty(): MaskDrain;
|
|
2346
|
-
/**
|
|
2355
|
+
/** telemetry: estimated bytes of mask storage held:
|
|
2347
2356
|
* four counter lanes (+pending trackers) and per-source flag columns. */
|
|
2348
2357
|
estimatedBytes(): number;
|
|
2349
|
-
/**
|
|
2358
|
+
/** O(Δ) op counters (live object — snapshot before comparing). */
|
|
2350
2359
|
get stats(): Readonly<MaskStats>;
|
|
2351
2360
|
resetStats(): void;
|
|
2352
2361
|
visibleNodeCount(): number;
|
|
@@ -2354,7 +2363,7 @@ declare class SoftMask {
|
|
|
2354
2363
|
/** Visible iff hideFailures === 0 (out-of-range slots read not-visible). */
|
|
2355
2364
|
isNodeVisible(index: number): boolean;
|
|
2356
2365
|
isEdgeVisible(index: number): boolean;
|
|
2357
|
-
/** Dimmed iff visible AND dimFailures > 0
|
|
2366
|
+
/** Dimmed iff visible AND dimFailures > 0. */
|
|
2358
2367
|
isNodeDimmed(index: number): boolean;
|
|
2359
2368
|
isEdgeDimmed(index: number): boolean;
|
|
2360
2369
|
/** 1 (fully visible) | dimAlpha (dimmed) | 0 (hidden). */
|
|
@@ -2368,7 +2377,7 @@ declare class SoftMask {
|
|
|
2368
2377
|
*/
|
|
2369
2378
|
private applyMembership;
|
|
2370
2379
|
/**
|
|
2371
|
-
* O(Δ) delta ops on one lane membership
|
|
2380
|
+
* O(Δ) delta ops on one lane membership. Adds and removes are
|
|
2372
2381
|
* idempotent per slot (adding a member / removing a non-member is a
|
|
2373
2382
|
* no-op); removed slots leave HOLES in `list` (compacted past 50%), so
|
|
2374
2383
|
* replace/clear passes must honor the bit0 guard above. `crossings`, when
|
|
@@ -2383,65 +2392,65 @@ declare class SoftMask {
|
|
|
2383
2392
|
private drainLane;
|
|
2384
2393
|
private clearSource;
|
|
2385
2394
|
/** Debug balanced-increment assert: whenever no source holds any
|
|
2386
|
-
* membership, every counter must read zero (skipped once overflowed
|
|
2395
|
+
* membership, every counter must read zero (skipped once overflowed
|
|
2387
2396
|
* clamped increments legitimately drift the books). */
|
|
2388
2397
|
private assertBalancedIfIdle;
|
|
2389
2398
|
}
|
|
2390
2399
|
|
|
2391
2400
|
/**
|
|
2392
|
-
*
|
|
2401
|
+
* crossfilter — typed-column engine (v0.7 node-dimension subset).
|
|
2393
2402
|
*
|
|
2394
2403
|
* `TypedColumnCrossfilter` is the columnar backend the instance wraps into the
|
|
2395
2404
|
* public `CrossfilterSession`. Design (crossfilter.js lineage):
|
|
2396
2405
|
*
|
|
2397
|
-
* - **Parse once.** `build
|
|
2398
|
-
*
|
|
2399
|
-
*
|
|
2400
|
-
*
|
|
2401
|
-
*
|
|
2402
|
-
*
|
|
2403
|
-
*
|
|
2404
|
-
*
|
|
2406
|
+
* - **Parse once.** `build` extracts every dimension exactly once into typed
|
|
2407
|
+
* columns (numeric/temporal → `Float64Array` epoch-ms/values, categorical →
|
|
2408
|
+
* dictionary codes). hygiene: non-finite numerics, unparseable temporals,
|
|
2409
|
+
* and non-string/non-finite categorical values are excluded from the
|
|
2410
|
+
* dimension (tracked per dimension in `excludedRows`; the slot is marked
|
|
2411
|
+
* invalid). Temporal parsing: numbers are epoch ms verbatim; strings go
|
|
2412
|
+
* through `Date.parse` (ES2022 parses `'YYYY-MM-DD'` as UTC midnight);
|
|
2413
|
+
* `Date` instances use `getTime`.
|
|
2405
2414
|
* - **O(Δ) brushes.** Each range dimension keeps a one-time argsorted
|
|
2406
|
-
*
|
|
2407
|
-
*
|
|
2408
|
-
*
|
|
2409
|
-
*
|
|
2410
|
-
*
|
|
2411
|
-
*
|
|
2412
|
-
*
|
|
2415
|
+
* permutation. A brush move performs two binary searches and walks ONLY the
|
|
2416
|
+
* symmetric difference between the old and new in-range windows; categorical
|
|
2417
|
+
* brushes walk only the per-code slot lists whose excluded flag changed. The
|
|
2418
|
+
* brush path never re-sorts. A per-slot, per-dimension pass flag plus a
|
|
2419
|
+
* global per-slot failure counter make a row selection-visible iff it fails
|
|
2420
|
+
* zero dimensions. Hygiene-excluded rows fail any non-null brush on that
|
|
2421
|
+
* dimension (they cannot be in range / in a category) and pass a null brush.
|
|
2413
2422
|
* - **Deltas out.** `setBrush` returns the slots whose overall visibility
|
|
2414
|
-
*
|
|
2415
|
-
* - **Lazy dual layer.** `summarize
|
|
2416
|
-
*
|
|
2417
|
-
*
|
|
2418
|
-
*
|
|
2419
|
-
*
|
|
2420
|
-
*
|
|
2423
|
+
* flipped (`hidden`/`shown`) for the instance's mask source.
|
|
2424
|
+
* - **Lazy dual layer.** `summarize` returns immutable summaries whose
|
|
2425
|
+
* `filtered` layer counts rows passing every OTHER dimension's brush plus an
|
|
2426
|
+
* external node-mask predicate (`setExternalMask`). v0.7 recomputes a dirty
|
|
2427
|
+
* dimension's filtered layer lazily per `summarize` call — O(rows) per
|
|
2428
|
+
* dirty summarize, documented and acceptable at this tier (fixtures own
|
|
2429
|
+
* the perf claims; the O(Δ) guarantee covers the brush/visibility path).
|
|
2421
2430
|
* - **Revisions & notify.** `selectionRevision` starts at 0 and advances
|
|
2422
|
-
*
|
|
2423
|
-
*
|
|
2424
|
-
*
|
|
2425
|
-
*
|
|
2426
|
-
*
|
|
2427
|
-
*
|
|
2428
|
-
*
|
|
2429
|
-
*
|
|
2430
|
-
*
|
|
2431
|
+
* exactly once per observable `setBrush` (a brush state change is observable
|
|
2432
|
+
* via `getBrush` even when no row flips). v0.7 is synchronous, so
|
|
2433
|
+
* latest-call-wins degenerates to "every call applies immediately in call
|
|
2434
|
+
* order". Subscribers fire once per observable change after state is
|
|
2435
|
+
* consistent; synchronous re-entrancy (a subscriber mutating the engine) is
|
|
2436
|
+
* coalesced into one trailing notification pass. Model updates
|
|
2437
|
+
* (`appendRows`/`replaceAll`) keep the current `selectionRevision`
|
|
2438
|
+
* but do notify. External-mask changes notify (summaries changed) without
|
|
2439
|
+
* advancing the selection revision.
|
|
2431
2440
|
* - **Incremental append.** `appendRows` extends columns/codes in place and
|
|
2432
|
-
*
|
|
2433
|
-
*
|
|
2434
|
-
*
|
|
2435
|
-
*
|
|
2436
|
-
*
|
|
2437
|
-
*
|
|
2438
|
-
*
|
|
2439
|
-
*
|
|
2440
|
-
*
|
|
2441
|
-
*
|
|
2441
|
+
* merges the pre-sorted old permutation with the sorted new block
|
|
2442
|
+
* (permutation merge — never a full re-argsort); bins extend incrementally
|
|
2443
|
+
* unless the domain grew (then a re-bin, still sort-free). Brushes persist
|
|
2444
|
+
* by key and are re-applied to the NEW slots only; the returned delta covers
|
|
2445
|
+
* only new slots (`shown` = new passing, `hidden` = new failing).
|
|
2446
|
+
* `replaceAll` rebuilds columns but preserves brushes by dimension key,
|
|
2447
|
+
* re-applying them as one combined delta against an all-visible baseline of
|
|
2448
|
+
* the new roster (`shown` is always empty). `replaceAll` clears the external
|
|
2449
|
+
* mask (slot indices changed meaning); `appendRows` keeps it and treats new
|
|
2450
|
+
* slots as passing until the instance re-supplies it.
|
|
2442
2451
|
*/
|
|
2443
2452
|
|
|
2444
|
-
/** Default histogram bin count for numeric/temporal dimensions
|
|
2453
|
+
/** Default histogram bin count for numeric/temporal dimensions. */
|
|
2445
2454
|
declare const DEFAULT_BIN_COUNT = 24;
|
|
2446
2455
|
/** Slots whose overall selection-visibility flipped in one operation. */
|
|
2447
2456
|
interface BrushDelta {
|
|
@@ -2458,15 +2467,15 @@ interface CrossfilterStats {
|
|
|
2458
2467
|
fullSorts: number;
|
|
2459
2468
|
/** Permutation merges performed by appendRows. */
|
|
2460
2469
|
permutationMerges: number;
|
|
2461
|
-
/** Incremental filtered-layer bin adjustments (
|
|
2470
|
+
/** Incremental filtered-layer bin adjustments (O(Δ·D)). */
|
|
2462
2471
|
binUpdates: number;
|
|
2463
2472
|
/** Full O(rows) filtered-layer recomputes (materialization/oracle only). */
|
|
2464
2473
|
filteredRecomputes: number;
|
|
2465
2474
|
}
|
|
2466
2475
|
declare class TypedColumnCrossfilter<N = Record<string, unknown>> {
|
|
2467
|
-
/** Test instrumentation; see CrossfilterStats. Reset with resetStats
|
|
2476
|
+
/** Test instrumentation; see CrossfilterStats. Reset with resetStats. */
|
|
2468
2477
|
readonly stats: CrossfilterStats;
|
|
2469
|
-
/** Count of dims whose filtered layer is live-maintained
|
|
2478
|
+
/** Count of dims whose filtered layer is live-maintained. */
|
|
2470
2479
|
private liveDims;
|
|
2471
2480
|
private dims;
|
|
2472
2481
|
private byKey;
|
|
@@ -2484,11 +2493,11 @@ declare class TypedColumnCrossfilter<N = Record<string, unknown>> {
|
|
|
2484
2493
|
/** Monotonic from 0; advances exactly once per observable setBrush change. */
|
|
2485
2494
|
get selectionRevision(): number;
|
|
2486
2495
|
resetStats(): void;
|
|
2487
|
-
/**
|
|
2496
|
+
/** telemetry: estimated bytes of typed-column storage held.
|
|
2488
2497
|
* Documented components: per-dim value/permutation/bin/code/pass arrays,
|
|
2489
2498
|
* the global failure counter, and the external mask. */
|
|
2490
2499
|
estimatedBytes(): number;
|
|
2491
|
-
/**
|
|
2500
|
+
/** live-layer bookkeeping — the ONLY writer of `filteredLive`. */
|
|
2492
2501
|
private setFilteredLive;
|
|
2493
2502
|
/**
|
|
2494
2503
|
* (Re)initialize columns from scratch. Clears all brushes and the external
|
|
@@ -2510,19 +2519,19 @@ declare class TypedColumnCrossfilter<N = Record<string, unknown>> {
|
|
|
2510
2519
|
setBrush(key: string, brush: BrushState): BrushDelta;
|
|
2511
2520
|
/**
|
|
2512
2521
|
* External node-mask predicate for the joint "filtered" second layer (the
|
|
2513
|
-
* instance wires the
|
|
2514
|
-
* never selection visibility. Length must equal rowCount
|
|
2522
|
+
* instance wires the filter-prop node mask in). Affects summaries only,
|
|
2523
|
+
* never selection visibility. Length must equal rowCount. Notifies on
|
|
2515
2524
|
* observable change; does NOT advance selectionRevision.
|
|
2516
2525
|
*/
|
|
2517
2526
|
setExternalMask(passSlots: Uint8Array | null): void;
|
|
2518
2527
|
/**
|
|
2519
|
-
* Immutable summary. The filtered layer is recomputed lazily when dirty
|
|
2528
|
+
* Immutable summary. The filtered layer is recomputed lazily when dirty
|
|
2520
2529
|
* O(rows) per dirty summarize (v0.7 tier; see module doc). Returned objects
|
|
2521
2530
|
* are frozen and never mutated by later operations.
|
|
2522
2531
|
*/
|
|
2523
2532
|
summarize(key: string): DimensionSummary;
|
|
2524
2533
|
/**
|
|
2525
|
-
* Incrementally extend columns with new rows
|
|
2534
|
+
* Incrementally extend columns with new rows: no full
|
|
2526
2535
|
* rebuild, no full re-argsort — the pre-sorted old permutation merges with
|
|
2527
2536
|
* the sorted new block. Brushes stay by key and are applied to the NEW slots
|
|
2528
2537
|
* only; the returned delta covers only new slots. Keeps selectionRevision;
|
|
@@ -2551,16 +2560,16 @@ declare class TypedColumnCrossfilter<N = Record<string, unknown>> {
|
|
|
2551
2560
|
private binIndex;
|
|
2552
2561
|
private flip;
|
|
2553
2562
|
/**
|
|
2554
|
-
*
|
|
2563
|
+
* inline maintenance of LIVE filtered layers, dispatched from the
|
|
2555
2564
|
* one place that knows the failCount transition. `boundary` is the
|
|
2556
2565
|
* other-failures picture at the interesting side of the flip (after for
|
|
2557
2566
|
* shown, before for hidden):
|
|
2558
|
-
*
|
|
2559
|
-
*
|
|
2560
|
-
*
|
|
2561
|
-
*
|
|
2562
|
-
*
|
|
2563
|
-
*
|
|
2567
|
+
* - 0 → the slot crossed the FULLY-VISIBLE boundary: every other live
|
|
2568
|
+
* layer counts it (own layers ignore the own-dim brush, so the brushed
|
|
2569
|
+
* dim's layer is provably unchanged by its own flip);
|
|
2570
|
+
* - 1 → exactly one OTHER dim still fails the slot: only that dim's
|
|
2571
|
+
* what-if-I-cleared-mine layer flips;
|
|
2572
|
+
* - ≥2 → no layer can change.
|
|
2564
2573
|
* External-mask-excluded and hygiene-invalid slots contribute nothing
|
|
2565
2574
|
* either way and are skipped.
|
|
2566
2575
|
*/
|
|
@@ -2578,27 +2587,27 @@ declare class TypedColumnCrossfilter<N = Record<string, unknown>> {
|
|
|
2578
2587
|
}
|
|
2579
2588
|
|
|
2580
2589
|
/**
|
|
2581
|
-
*
|
|
2590
|
+
* scales & domains — canonical scale keying, default
|
|
2582
2591
|
* palettes, sRGB color interpolation, domain-state machinery, and stable
|
|
2583
2592
|
* categorical assignment. Pure and engine-free; the instance/projection
|
|
2584
2593
|
* layers consume these primitives when a styling channel carries a `Scale`.
|
|
2585
2594
|
*
|
|
2586
|
-
* Semantics
|
|
2587
|
-
* - Scales are plain descriptors compared by CANONICAL STRUCTURAL VALUE
|
|
2588
|
-
*
|
|
2589
|
-
*
|
|
2595
|
+
* Semantics:
|
|
2596
|
+
* - Scales are plain descriptors compared by CANONICAL STRUCTURAL VALUE
|
|
2597
|
+
* equal inline literals produce equal keys and never reproject. A function
|
|
2598
|
+
* `by` keys by reference identity (WeakMap token), never by source text.
|
|
2590
2599
|
* - Domains default to the whole dataset revision and stay FROZEN across
|
|
2591
|
-
*
|
|
2592
|
-
*
|
|
2593
|
-
*
|
|
2594
|
-
*
|
|
2600
|
+
* masking/brushing/isolation ('dataset' scope): the same metric value never
|
|
2601
|
+
* changes visual meaning because a user brushed. 'hard-scope'/'visible' are
|
|
2602
|
+
* explicit opt-ins that recompute when the caller's scope generation bumps;
|
|
2603
|
+
* streaming 'expand' permits monotonic domain growth on recompute.
|
|
2595
2604
|
* - Explicit numeric domains always win verbatim (never computed, cached, or
|
|
2596
|
-
*
|
|
2605
|
+
* unioned).
|
|
2597
2606
|
* - Categorical values declared in `domain` take their declared position;
|
|
2598
|
-
*
|
|
2599
|
-
*
|
|
2600
|
-
* -
|
|
2601
|
-
*
|
|
2607
|
+
* out-of-domain values take a stable fnv-1a hash slot — NEVER first-seen
|
|
2608
|
+
* order, so arrival order can never recolor a category.
|
|
2609
|
+
* - numeric hygiene: null/non-finite metric values resolve to `null`
|
|
2610
|
+
* (caller falls back to the default style) and are excluded from domains.
|
|
2602
2611
|
*/
|
|
2603
2612
|
|
|
2604
2613
|
type SequentialScale<T, N = Record<string, unknown>> = Extract<Scale<T, N>, {
|
|
@@ -2615,7 +2624,7 @@ type DivergingScale<T, N = Record<string, unknown>> = Extract<Scale<T, N>, {
|
|
|
2615
2624
|
* order preserved, undefined-valued keys omitted, and a function `by` mapped
|
|
2616
2625
|
* to a unique reference-identity token (same WeakMap approach — and canonical
|
|
2617
2626
|
* grammar — as `canonicalFilterKey`). Two scales with equal keys are
|
|
2618
|
-
*
|
|
2627
|
+
* structurally equivalent: equal inline literals MUST and DO produce equal keys, so
|
|
2619
2628
|
* identity churn never reprojects; swapping a function reference always does.
|
|
2620
2629
|
*/
|
|
2621
2630
|
declare function canonicalScaleKey<T, N>(scale: Scale<T, N>): string;
|
|
@@ -2634,7 +2643,7 @@ declare const DIVERGING_RANGE_DEFAULT: readonly [string, string, string];
|
|
|
2634
2643
|
declare function interpolateColor(a: string, b: string, t: number): string;
|
|
2635
2644
|
/**
|
|
2636
2645
|
* Sequential color: maps `value` across `domain` onto the two-stop ramp.
|
|
2637
|
-
* Returns null (caller renders the default style,
|
|
2646
|
+
* Returns null (caller renders the default style, hygiene) when the value
|
|
2638
2647
|
* is null/non-finite or the domain is null.
|
|
2639
2648
|
*/
|
|
2640
2649
|
declare function sequentialColor<N = Record<string, unknown>>(scale: SequentialScale<string, N>, value: number | null, domain: readonly [number, number] | null): string | null;
|
|
@@ -2649,11 +2658,11 @@ declare function divergingColor<N = Record<string, unknown>>(scale: DivergingSca
|
|
|
2649
2658
|
* Sequential size: linear map of `value` across `domain` onto [lo,hi],
|
|
2650
2659
|
* clamped at the range endpoints. Degenerate domains yield the range
|
|
2651
2660
|
* midpoint; null/non-finite values and null domains yield null (default
|
|
2652
|
-
* style — NaN is never handed to a size buffer
|
|
2661
|
+
* style — NaN is never handed to a size buffer).
|
|
2653
2662
|
*/
|
|
2654
2663
|
declare function sequentialSize(range: readonly [number, number], value: number | null, domain: readonly [number, number] | null): number | null;
|
|
2655
2664
|
/**
|
|
2656
|
-
* [min,max] over the finite numbers in `values` (
|
|
2665
|
+
* [min,max] over the finite numbers in `values` (hygiene: null and
|
|
2657
2666
|
* non-finite entries are excluded), or null when nothing qualifies. A single
|
|
2658
2667
|
* qualifying value yields a degenerate [v,v] domain.
|
|
2659
2668
|
*/
|
|
@@ -2663,7 +2672,7 @@ interface ResolveDomainArgs {
|
|
|
2663
2672
|
* descriptor, so the key already discriminates by metric). Opaque here. */
|
|
2664
2673
|
key: string;
|
|
2665
2674
|
/** Explicit caller domain — returned VERBATIM; never computed, cached, or
|
|
2666
|
-
* expand-unioned
|
|
2675
|
+
* expand-unioned. */
|
|
2667
2676
|
explicit?: readonly [number, number] | undefined;
|
|
2668
2677
|
/** Defaults: scope 'dataset', streaming 'freeze-per-revision'. */
|
|
2669
2678
|
policy?: DomainPolicy | undefined;
|
|
@@ -2673,7 +2682,7 @@ interface ResolveDomainArgs {
|
|
|
2673
2682
|
* (bump = recompute). IGNORED under 'dataset' scope — masking/brushing
|
|
2674
2683
|
* must never change what a color means. */
|
|
2675
2684
|
scopeGeneration?: number | undefined;
|
|
2676
|
-
/** Source lineage this resolve belongs to (
|
|
2685
|
+
/** Source lineage this resolve belongs to (dataset key + source
|
|
2677
2686
|
* revision). `streaming: 'expand'` unions only WITHIN one lineage — a
|
|
2678
2687
|
* source replacement starts fresh instead of unioning dead extrema.
|
|
2679
2688
|
* Callers that omit it keep the legacy always-union behavior. */
|
|
@@ -2683,7 +2692,7 @@ interface ResolveDomainArgs {
|
|
|
2683
2692
|
compute: () => readonly [number, number] | null;
|
|
2684
2693
|
}
|
|
2685
2694
|
/**
|
|
2686
|
-
* Per-instance domain freezer keyed by canonical scale key
|
|
2695
|
+
* Per-instance domain freezer keyed by canonical scale key.
|
|
2687
2696
|
*
|
|
2688
2697
|
* FREEZE-PER-REVISION: under scope 'dataset' the domain is computed ONCE per
|
|
2689
2698
|
* {key, datasetRevision} — repeat resolves return the frozen value without
|
|
@@ -2722,24 +2731,24 @@ declare function categoricalIndex(domain: readonly string[] | undefined, palette
|
|
|
2722
2731
|
declare function categoricalRows(domain: readonly string[] | undefined, seen: Iterable<string>): string[];
|
|
2723
2732
|
|
|
2724
2733
|
/**
|
|
2725
|
-
*
|
|
2734
|
+
* capability-policy module.
|
|
2726
2735
|
*
|
|
2727
|
-
*
|
|
2736
|
+
* (normative): for capabilities where the core owns a fallback path, the
|
|
2728
2737
|
* core selects native-vs-fallback ONCE at mount from the engine's declared
|
|
2729
2738
|
* `capabilities` record — never by sniffing method presence — and unsupported
|
|
2730
|
-
* *requested* props degrade loudly (a
|
|
2739
|
+
* *requested* props degrade loudly (a diagnostic), never as silent no-ops.
|
|
2731
2740
|
*
|
|
2732
2741
|
* This module is the single place those decisions are made:
|
|
2733
|
-
*
|
|
2734
|
-
*
|
|
2735
|
-
*
|
|
2736
|
-
*
|
|
2737
|
-
*
|
|
2738
|
-
*
|
|
2742
|
+
* - {@link resolveEnginePolicy} turns the capability record + the host's
|
|
2743
|
+
* requested features into one frozen {@link EnginePolicy} at mount.
|
|
2744
|
+
* - {@link assertCapabilityMethodParity} is the mount-time dev-mode record
|
|
2745
|
+
* vs. method-surface assertion, restricted to honestly checkable pairs.
|
|
2746
|
+
* - {@link normalizeCommitForCapabilities} strips commit payload an engine
|
|
2747
|
+
* declared it cannot honor, so incapable adapters never see it.
|
|
2739
2748
|
*/
|
|
2740
2749
|
|
|
2741
2750
|
/** One loud degradation: a feature the host requested that the mounted engine
|
|
2742
|
-
* does not declare. Feeds the
|
|
2751
|
+
* does not declare. Feeds the dev diagnostic at mount. */
|
|
2743
2752
|
interface EnginePolicyDegradation {
|
|
2744
2753
|
readonly feature: string;
|
|
2745
2754
|
readonly reason: string;
|
|
@@ -2749,29 +2758,29 @@ interface EnginePolicyDegradation {
|
|
|
2749
2758
|
interface RequestedEngineFeatures {
|
|
2750
2759
|
edgeArrows?: boolean;
|
|
2751
2760
|
images?: boolean;
|
|
2752
|
-
/**
|
|
2761
|
+
/** stage-4: a `clusters` spec is active this session. */
|
|
2753
2762
|
clusters?: boolean;
|
|
2754
2763
|
}
|
|
2755
2764
|
/**
|
|
2756
|
-
* The frozen mount-time native-vs-fallback record
|
|
2765
|
+
* The frozen mount-time native-vs-fallback record. Evaluated exactly
|
|
2757
2766
|
* once per mount from `EngineCapabilities` and never revisited — capability
|
|
2758
2767
|
* records are static declarations fixed at engine construction, so the policy
|
|
2759
2768
|
* must not drift even if a caller mutates the input record afterwards.
|
|
2760
2769
|
*/
|
|
2761
2770
|
interface EnginePolicy {
|
|
2762
|
-
/**
|
|
2771
|
+
/** arrowheads: engine-drawn, or the prop is inert (+ dev warning). */
|
|
2763
2772
|
readonly edgeArrows: 'native' | 'inert';
|
|
2764
|
-
/**
|
|
2773
|
+
/** image sprites: atlas-backed, or the placeholder glyph with refs
|
|
2765
2774
|
* retained for a future compatible engine. */
|
|
2766
2775
|
readonly images: 'native' | 'placeholder';
|
|
2767
|
-
/**
|
|
2776
|
+
/** link hover/click: engine events, or the core's CPU grid fallback. */
|
|
2768
2777
|
readonly linkPicking: 'native' | 'cpu-fallback';
|
|
2769
|
-
/**
|
|
2778
|
+
/** stage-4 cluster force: engine-applied, or inert (membership,
|
|
2770
2779
|
* labels, and centroids are core-owned and unaffected). */
|
|
2771
2780
|
readonly clusterForce: 'native' | 'inert';
|
|
2772
2781
|
/** Channels eligible for ranged (partial) uploads; empty = full replaces. */
|
|
2773
2782
|
readonly rangedChannels: ReadonlySet<EngineBufferChannel>;
|
|
2774
|
-
/**
|
|
2783
|
+
/** stop-at-rest: 'stops' when the engine declares idleFrames:'stops'.
|
|
2775
2784
|
* Observability only (telemetry/harness read it) — an idle-spinning engine
|
|
2776
2785
|
* is a documented state, never a degradation entry: nothing was
|
|
2777
2786
|
* host-REQUESTED, so there is nothing to warn about. */
|
|
@@ -2784,7 +2793,7 @@ interface EnginePolicy {
|
|
|
2784
2793
|
* Resolve the mount-time engine policy from the declared capability record.
|
|
2785
2794
|
*
|
|
2786
2795
|
* Decisions come from `capabilities` ONLY — method sniffing is forbidden by
|
|
2787
|
-
*
|
|
2796
|
+
* The result is deep-frozen and holds a defensive copy of
|
|
2788
2797
|
* `rangeUpdates`, so mutating the input record afterwards changes nothing.
|
|
2789
2798
|
* A degradation entry exists only for features the host actually requested
|
|
2790
2799
|
* that the engine does not declare; unrequested gaps stay silent.
|
|
@@ -2795,25 +2804,25 @@ declare function resolveEnginePolicy(capabilities: EngineCapabilities, requested
|
|
|
2795
2804
|
* with the engine's method surface? Returns a list of human-readable
|
|
2796
2805
|
* mismatches (empty = consistent).
|
|
2797
2806
|
*
|
|
2798
|
-
* Why so few checks?
|
|
2807
|
+
* Why so few checks? An illustrative parity assertion
|
|
2799
2808
|
* (`capabilities.linkPicking === (typeof engine.linkAt === 'function')`)
|
|
2800
|
-
* presumes
|
|
2809
|
+
* presumes a broader method surface; the v0.1 adapter contract
|
|
2801
2810
|
* 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
|
-
*
|
|
2811
|
+
* checkable method pair. Those are instead covered by engine probes that
|
|
2812
|
+
* validate declared records against observed behavior:
|
|
2813
|
+
*
|
|
2814
|
+
* - `linkPicking`: native picking arrives through mount-time host events
|
|
2815
|
+
* (`onLinkClick`/`onLinkHover`), not a probeable `linkAt` method — there
|
|
2816
|
+
* is nothing on the engine object to compare the record against.
|
|
2817
|
+
* - `edgeArrows` / `pointImages` / `rangeUpdates`: honored inside
|
|
2818
|
+
* `commit()` payload handling (`config.linkArrows`, `resources`, ranged
|
|
2819
|
+
* uploads) with no distinguishing method; only behavior can validate them.
|
|
2820
|
+
* - `simulation`: `start()`/`pause()` are mandatory on every engine (static
|
|
2821
|
+
* engines no-op them), so method presence carries no signal either way.
|
|
2822
|
+
* - `pointsInPolygon`-family (`pointsInRect`, `captureScreenshot`,
|
|
2823
|
+
* `neighborIndices`, `screenToSpace`/`spaceToScreen`, `setPinnedIndices`,
|
|
2824
|
+
* `zoomToIndex`): optional-by-contract with no declaring capability bit —
|
|
2825
|
+
* absence is legitimate, so no check is fabricated for them.
|
|
2817
2826
|
*
|
|
2818
2827
|
* The one honest pair on this surface: `trackedPositions` declares position
|
|
2819
2828
|
* readback works, so `getPositions` must actually be present.
|
|
@@ -2821,10 +2830,10 @@ declare function resolveEnginePolicy(capabilities: EngineCapabilities, requested
|
|
|
2821
2830
|
declare function assertCapabilityMethodParity(engine: GraphEngine): string[];
|
|
2822
2831
|
/**
|
|
2823
2832
|
* Strip commit payload the engine's capability record says it cannot honor:
|
|
2824
|
-
*
|
|
2825
|
-
*
|
|
2826
|
-
*
|
|
2827
|
-
*
|
|
2833
|
+
* - `resources` (image atlas + per-point image index) unless `pointImages`;
|
|
2834
|
+
* - `config.linkArrows` unless `edgeArrows`;
|
|
2835
|
+
* - `config.cluster` unless `clusterForce` (stage 4 — the core keeps
|
|
2836
|
+
* membership, labels, and centroids; only the FORCE is engine-side).
|
|
2828
2837
|
* A `config` left empty by the strip is dropped entirely.
|
|
2829
2838
|
*
|
|
2830
2839
|
* IDENTITY-PRESERVING: when nothing needs stripping the SAME commit object
|
|
@@ -2837,15 +2846,15 @@ declare function normalizeCommitForCapabilities(commit: EngineCommit, capabiliti
|
|
|
2837
2846
|
};
|
|
2838
2847
|
|
|
2839
2848
|
/**
|
|
2840
|
-
*
|
|
2849
|
+
* shared numeric hygiene — THE single coercion layer for every
|
|
2841
2850
|
* numeric consumer in orbit-core.
|
|
2842
2851
|
*
|
|
2843
2852
|
* Wherever a caller-supplied value feeds a numeric sink — size/width
|
|
2844
|
-
* projection buffers
|
|
2845
|
-
* crossfilter bins
|
|
2853
|
+
* projection buffers, scale domains, metric columns,
|
|
2854
|
+
* crossfilter bins, table lanes — the value is REQUIRED to route
|
|
2846
2855
|
* through `coerceNumeric` / `coerceNumericInto`. Non-numeric and non-finite
|
|
2847
2856
|
* inputs (including the string sentinels `"NaN"` / `"Infinity"` /
|
|
2848
|
-
* `"-Infinity"` that JSON transports smuggle through
|
|
2857
|
+
* `"-Infinity"` that JSON transports smuggle through) coerce to `null`:
|
|
2849
2858
|
* the row falls back to the default style and is excluded from domain
|
|
2850
2859
|
* computation. NaN NEVER escapes this module — not as a return value and not
|
|
2851
2860
|
* into a GPU buffer.
|
|
@@ -2853,16 +2862,15 @@ declare function normalizeCommitForCapabilities(commit: EngineCommit, capabiliti
|
|
|
2853
2862
|
* Coercion rules:
|
|
2854
2863
|
* - numbers pass iff `Number.isFinite` (NaN / ±Infinity → null);
|
|
2855
2864
|
* - strings are trimmed; empty → null; the case-insensitive sentinels
|
|
2856
|
-
*
|
|
2857
|
-
*
|
|
2858
|
-
*
|
|
2865
|
+
* 'NaN' / 'Infinity' / '-Infinity' / '+Infinity' → null; anything else
|
|
2866
|
+
* parses via `Number(...)` and passes iff finite (so '1e3' → 1000 but
|
|
2867
|
+
* '12px' → null — `Number`, not `parseFloat`, so no partial prefixes);
|
|
2859
2868
|
* - booleans, objects, arrays, functions, symbols, bigints, null, and
|
|
2860
|
-
*
|
|
2869
|
+
* undefined → null (no `valueOf`/`toString` coercion side channels).
|
|
2861
2870
|
*
|
|
2862
|
-
*
|
|
2863
|
-
*
|
|
2864
|
-
*
|
|
2865
|
-
* stay defined in exactly one place.
|
|
2871
|
+
* `crossfilter.ts` predates this module and retains its own inline finite
|
|
2872
|
+
* checks. Any consolidation must route through `coerceNumeric` so the
|
|
2873
|
+
* sentinel-string rules stay defined in exactly one place.
|
|
2866
2874
|
*/
|
|
2867
2875
|
declare function coerceNumeric(value: unknown): number | null;
|
|
2868
2876
|
/**
|
|
@@ -2877,31 +2885,31 @@ declare function coerceNumeric(value: unknown): number | null;
|
|
|
2877
2885
|
declare function coerceNumericInto(target: Float32Array, index: number, value: unknown, fallback: number): boolean;
|
|
2878
2886
|
|
|
2879
2887
|
/**
|
|
2880
|
-
*
|
|
2888
|
+
* metrics (v0.8 subset): lazy degree-family primitives over the
|
|
2881
2889
|
* maintained topology plus revision-gated admission of async metric columns.
|
|
2882
2890
|
*
|
|
2883
2891
|
* - The degree family (degree / inDegree / outDegree) is computed LAZILY on
|
|
2884
|
-
*
|
|
2885
|
-
*
|
|
2892
|
+
* first request per model revision, in ONE combined O(n + L) pass, and
|
|
2893
|
+
* cached as Float64Arrays until the model revision changes.
|
|
2886
2894
|
* - 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
|
-
*
|
|
2895
|
+
* degree, inDegree, and outDegree of `a`. The CSR adjacency lists a
|
|
2896
|
+
* self-loop twice under its point (once per endpoint slot, see
|
|
2897
|
+
* adjacency.ts), so degree = CSR row length MINUS the point's self-loop
|
|
2898
|
+
* count; in/out come from a directed pass over the flat link pairs.
|
|
2899
|
+
* - Async columns join once against the accepted model: 'index' align
|
|
2900
|
+
* is positional over accepted-base order; 'ids' align joins by id with
|
|
2901
|
+
* unknown ids counted+sampled, duplicate ids counted (last occurrence
|
|
2902
|
+
* wins), and absent rows null. Every value routes through `coerceNumeric`.
|
|
2903
|
+
* A column computed for a stale model revision is
|
|
2904
|
+
* DISCARDED with a diagnostic — admission is the correctness gate, abort
|
|
2905
|
+
* is only an optimization.
|
|
2898
2906
|
* - Storage encodes null as NaN inside Float64Arrays; `getMetricValue`
|
|
2899
|
-
*
|
|
2907
|
+
* converts back at the boundary so NaN never escapes to callers.
|
|
2900
2908
|
* - Admitted columns SHADOW the built-in degree family under the same name
|
|
2901
|
-
*
|
|
2909
|
+
* (a precomputed/server 'degree' column wins; precomputed path).
|
|
2902
2910
|
*/
|
|
2903
2911
|
|
|
2904
|
-
/** Topology snapshot the degree family is computed from
|
|
2912
|
+
/** Topology snapshot the degree family is computed from. */
|
|
2905
2913
|
interface MetricModelInput<N = Record<string, unknown>> {
|
|
2906
2914
|
/** Accepted nodes in accepted-base order (index i ↔ point i). */
|
|
2907
2915
|
nodes: readonly GraphNode<N>[];
|
|
@@ -2933,7 +2941,7 @@ declare class MetricStore<N = Record<string, unknown>> {
|
|
|
2933
2941
|
/** Admitted async columns by metric name; NaN encodes null. */
|
|
2934
2942
|
private readonly columns;
|
|
2935
2943
|
private degreePasses;
|
|
2936
|
-
/**
|
|
2944
|
+
/** telemetry: estimated bytes of metric storage held. */
|
|
2937
2945
|
estimatedBytes(): number;
|
|
2938
2946
|
/** Number of combined degree-family compute passes (test observability). */
|
|
2939
2947
|
get degreeComputePasses(): number;
|
|
@@ -2946,10 +2954,10 @@ declare class MetricStore<N = Record<string, unknown>> {
|
|
|
2946
2954
|
*/
|
|
2947
2955
|
setModel(model: MetricModelInput<N>): void;
|
|
2948
2956
|
/**
|
|
2949
|
-
* Joins async metric columns against the accepted model
|
|
2957
|
+
* Joins async metric columns against the accepted model.
|
|
2950
2958
|
* Revision-gated PER COLUMN (I1): a column whose issue-time
|
|
2951
2959
|
* `forModelRevision` stamp differs from `opts.modelRevision` is discarded
|
|
2952
|
-
* (info diagnostic — a normal async race outcome
|
|
2960
|
+
* (info diagnostic — a normal async race outcome). A missing or
|
|
2953
2961
|
* mismatched stamp is never defaulted to the current revision — that
|
|
2954
2962
|
* would make the gate self-satisfying.
|
|
2955
2963
|
* Structural rejections ('index' length mismatch, missing/mismatched ids)
|
|
@@ -2966,7 +2974,7 @@ declare class MetricStore<N = Record<string, unknown>> {
|
|
|
2966
2974
|
getMetricValue(metric: MetricName, index: number): number | null;
|
|
2967
2975
|
/**
|
|
2968
2976
|
* Raw column in accepted-base order, or null when unavailable. NaN encodes
|
|
2969
|
-
* null
|
|
2977
|
+
* null — consumers exclude NaN slots from domains. Do NOT mutate:
|
|
2970
2978
|
* this is the live cache, not a copy.
|
|
2971
2979
|
*/
|
|
2972
2980
|
metricValues(metric: MetricName): Float64Array | null;
|
|
@@ -2978,22 +2986,22 @@ declare class MetricStore<N = Record<string, unknown>> {
|
|
|
2978
2986
|
}
|
|
2979
2987
|
|
|
2980
2988
|
/**
|
|
2981
|
-
*
|
|
2989
|
+
* minimap / overview — CPU fallback lane.
|
|
2982
2990
|
*
|
|
2983
2991
|
* 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
|
|
2992
|
+
* second draw pass — matrix), so this controller IS the minimap thumbnail
|
|
2993
|
+
* path: an O(n) CPU rasterization of the position mirror into a small
|
|
2986
2994
|
* RGBA dot field. It is a pure controller — no DOM, no canvas — the React
|
|
2987
2995
|
* component blits the returned `Uint8ClampedArray` into an `ImageData`/canvas
|
|
2988
|
-
* and draws the viewport rectangle on top (O(1) from `getViewport
|
|
2996
|
+
* and draws the viewport rectangle on top (O(1) from `getViewport`, fully
|
|
2989
2997
|
* decoupled from thumbnail refresh cadence).
|
|
2990
2998
|
*
|
|
2991
|
-
* Refresh cadence
|
|
2999
|
+
* Refresh cadence: ≤ 2 Hz while the simulation is hot (positions
|
|
2992
3000
|
* change continuously on the GPU, so the epoch is ignored), ≤ 1 Hz after a
|
|
2993
3001
|
* change while idle, and ZERO work while idle with an unchanged positions
|
|
2994
3002
|
* epoch — `shouldRefresh` is the single throttle gate and latches its clock /
|
|
2995
3003
|
* epoch only when it answers true (a `true` must be followed by one
|
|
2996
|
-
* `rasterize
|
|
3004
|
+
* `rasterize`).
|
|
2997
3005
|
*
|
|
2998
3006
|
* Orientation: minimap pixel y grows DOWNWARD while world (space) y grows
|
|
2999
3007
|
* upward — the same flip cosmos applies in its space→screen scale (dist
|
|
@@ -3032,7 +3040,7 @@ interface OverviewRaster {
|
|
|
3032
3040
|
bitmap: Uint8ClampedArray;
|
|
3033
3041
|
bounds: OverviewBounds;
|
|
3034
3042
|
}
|
|
3035
|
-
/** Spec
|
|
3043
|
+
/** Spec the CPU fallback rasterizes into a 256² target. */
|
|
3036
3044
|
declare const OVERVIEW_SIZE_DEFAULT = 256;
|
|
3037
3045
|
/** Hot-simulation refresh floor: ≤ 2 Hz. */
|
|
3038
3046
|
declare const OVERVIEW_HOT_INTERVAL_MS = 500;
|
|
@@ -3042,7 +3050,7 @@ declare class OverviewController {
|
|
|
3042
3050
|
private readonly getScene;
|
|
3043
3051
|
private readonly getVisible;
|
|
3044
3052
|
readonly size: number;
|
|
3045
|
-
/** Transform of the LAST rasterization; null until rasterize
|
|
3053
|
+
/** Transform of the LAST rasterization; null until rasterize succeeds. */
|
|
3046
3054
|
private frame;
|
|
3047
3055
|
private lastRefreshMs;
|
|
3048
3056
|
private lastEpoch;
|
|
@@ -3052,16 +3060,16 @@ declare class OverviewController {
|
|
|
3052
3060
|
* when it returns true, so each `true` accounts for exactly one refresh:
|
|
3053
3061
|
*
|
|
3054
3062
|
* - hot (`simulationRunning`): time-gated only (≤ 2 Hz) — the epoch is
|
|
3055
|
-
*
|
|
3063
|
+
* ignored because positions change continuously without epoch advances;
|
|
3056
3064
|
* - idle: refresh only when the epoch ADVANCED since the last refresh,
|
|
3057
|
-
*
|
|
3065
|
+
* time-gated at ≤ 1 Hz;
|
|
3058
3066
|
* - idle + unchanged epoch: always false — zero work, forever.
|
|
3059
3067
|
*/
|
|
3060
3068
|
shouldRefresh(nowMs: number, simulationRunning: boolean, epoch: number): boolean;
|
|
3061
3069
|
/**
|
|
3062
3070
|
* Rasterizes the current scene into a fresh `size²` RGBA dot field: 1 px
|
|
3063
3071
|
* white dots whose alpha ACCUMULATES on overlap (heatmap-ish density),
|
|
3064
|
-
* dimmed for mask-hidden points; NaN pairs
|
|
3072
|
+
* dimmed for mask-hidden points; NaN pairs are skipped.
|
|
3065
3073
|
* World bounds map into the thumbnail with a 5 % edge padding at a UNIFORM
|
|
3066
3074
|
* scale (aspect preserved, centered on the short axis) and a downward pixel
|
|
3067
3075
|
* y (see module header). Returns null when there is no scene or no point.
|
|
@@ -3077,34 +3085,34 @@ declare class OverviewController {
|
|
|
3077
3085
|
}
|
|
3078
3086
|
|
|
3079
3087
|
/**
|
|
3080
|
-
*
|
|
3088
|
+
* SVG export — the engine-free pure module.
|
|
3081
3089
|
*
|
|
3082
3090
|
* ENGINE-INDEPENDENT BY CONSTRUCTION, not by discipline: positions and
|
|
3083
3091
|
* projected styles come IN as a plain descriptor, vector markup goes OUT as a
|
|
3084
3092
|
* string. No engine, no instance, no DOM — the post-v1 server snapshot path
|
|
3085
|
-
*
|
|
3093
|
+
* reuses this verbatim under plain Node, and the test
|
|
3086
3094
|
* suite runs it exactly that way. Keeping it engine-free is an M7
|
|
3087
3095
|
* requirement, which is why it is a standalone module rather than instance
|
|
3088
3096
|
* code that might one day be extracted.
|
|
3089
3097
|
*
|
|
3090
|
-
* Output discipline
|
|
3091
|
-
*
|
|
3092
|
-
*
|
|
3093
|
-
*
|
|
3094
|
-
*
|
|
3095
|
-
*
|
|
3096
|
-
*
|
|
3097
|
-
*
|
|
3098
|
-
*
|
|
3099
|
-
*
|
|
3100
|
-
*
|
|
3098
|
+
* Output discipline:
|
|
3099
|
+
* - One element per node/edge/label, assembled OFF-DOM by chunked
|
|
3100
|
+
* array-join — O(elements), never a live DOM node per element.
|
|
3101
|
+
* - Every label and attribute string is XML-escaped (untrusted-content
|
|
3102
|
+
* rule in its XML form): a hostile label renders as literal text in every
|
|
3103
|
+
* downstream vector tool, never as markup.
|
|
3104
|
+
* - Bounded: above `maxElements` (default 50 000 — the practical ceiling for
|
|
3105
|
+
* downstream vector editors) rendering THROWS before assembling anything;
|
|
3106
|
+
* the instance wraps that into the typed `export-too-large` rejection, and
|
|
3107
|
+
* the raster-hybrid form (a PNG base layer plus a small vector overlay) is
|
|
3108
|
+
* the sanctioned way past it.
|
|
3101
3109
|
*/
|
|
3102
3110
|
interface SvgSceneNode {
|
|
3103
3111
|
x: number;
|
|
3104
3112
|
y: number;
|
|
3105
3113
|
/** Radius in output px (the caller halves its size-buffer diameter). */
|
|
3106
3114
|
r: number;
|
|
3107
|
-
/** Any CSS color string (the caller projects RGBA buffers to rgba
|
|
3115
|
+
/** Any CSS color string (the caller projects RGBA buffers to rgba). */
|
|
3108
3116
|
color: string;
|
|
3109
3117
|
}
|
|
3110
3118
|
interface SvgSceneEdge {
|
|
@@ -3162,21 +3170,23 @@ declare function escapeXml(text: string): string;
|
|
|
3162
3170
|
declare function renderSvg(scene: SvgScene, opts?: RenderSvgOptions): string;
|
|
3163
3171
|
|
|
3164
3172
|
/**
|
|
3165
|
-
*
|
|
3166
|
-
*
|
|
3173
|
+
* Columnar-native acceptance rules — the column-oriented twin of validate.ts,
|
|
3174
|
+
* built to run INSIDE the worker over typed
|
|
3167
3175
|
* columns without materializing a single row object.
|
|
3168
3176
|
*
|
|
3169
3177
|
* Semantics mirror the object lane EXACTLY (the equivalence oracle pins
|
|
3170
3178
|
* rosters AND diagnostics, message strings included):
|
|
3171
|
-
*
|
|
3172
|
-
*
|
|
3173
|
-
*
|
|
3174
|
-
*
|
|
3175
|
-
*
|
|
3176
|
-
*
|
|
3177
|
-
*
|
|
3178
|
-
*
|
|
3179
|
-
*
|
|
3179
|
+
* - duplicate node ids drop, first occurrence wins ('duplicate-node-id',
|
|
3180
|
+
* warning) — and edges addressing a dropped duplicate ROW remap to the
|
|
3181
|
+
* surviving occurrence, because the object lane resolves endpoints by ID
|
|
3182
|
+
* STRING, which survives.
|
|
3183
|
+
* - duplicate edge ids drop, first wins ('duplicate-edge-id', warning).
|
|
3184
|
+
* - self-loops are RETAINED with 'self-loop-retained' (info).
|
|
3185
|
+
* - invalid-node / invalid-edge rows with NUL-reserved ids drop here;
|
|
3186
|
+
* dangling edges can then arise when an endpoint names a dropped invalid
|
|
3187
|
+
* node. Other structural corruption cannot occur because ids come from a
|
|
3188
|
+
* structurally validated dictionary column and endpoints are in-bounds
|
|
3189
|
+
* indices by prior validation (validateColumnarStructure).
|
|
3180
3190
|
*
|
|
3181
3191
|
* Duplicates hide in TWO encodings: two rows sharing a code, and two
|
|
3182
3192
|
* DISTINCT dictionary entries holding equal strings. Both are handled by
|
|
@@ -3211,21 +3221,21 @@ interface ColumnarAcceptance {
|
|
|
3211
3221
|
declare function acceptColumnar(snapshot: ColumnarGraphSnapshot<unknown, unknown>): ColumnarAcceptance;
|
|
3212
3222
|
|
|
3213
3223
|
/**
|
|
3214
|
-
*
|
|
3224
|
+
* columnar snapshot lane — structural validation and
|
|
3215
3225
|
* the materialization bridge.
|
|
3216
3226
|
*
|
|
3217
3227
|
* Validation is O(number of columns + rows) and runs BEFORE transfer or
|
|
3218
3228
|
* admission. Columnar structural corruption is intentionally not repaired
|
|
3219
|
-
* row by row — offset/index misalignment can reinterpret every later row
|
|
3229
|
+
* row by row — offset/index misalignment can reinterpret every later row
|
|
3220
3230
|
* so any structural issue rejects the WHOLE snapshot, the prior scene stays
|
|
3221
|
-
* intact, and nothing throws from a React render
|
|
3231
|
+
* intact, and nothing throws from a React render.
|
|
3222
3232
|
*
|
|
3223
3233
|
* The bridge materializes a VALID columnar snapshot into the object form and
|
|
3224
3234
|
* feeds the existing pipeline, so duplicate-id / dangling-edge / self-loop
|
|
3225
3235
|
* resolution stays in exactly one place (validate.ts) and the columnar lane
|
|
3226
|
-
* inherits every object-lane rule by construction
|
|
3227
|
-
*
|
|
3228
|
-
*
|
|
3236
|
+
* inherits every object-lane rule by construction. Under `execution: 'main'`
|
|
3237
|
+
* materialization remains synchronous; the worker lane can perform acceptance
|
|
3238
|
+
* off-thread before the same shared pipeline runs.
|
|
3229
3239
|
*
|
|
3230
3240
|
* Null semantics: `nulls` is one byte per row, nonzero = null. Null attr
|
|
3231
3241
|
* values materialize as `null` (JSON-ish; every numeric/temporal consumer
|
|
@@ -3242,7 +3252,7 @@ interface ColumnarIssue {
|
|
|
3242
3252
|
}
|
|
3243
3253
|
/**
|
|
3244
3254
|
* O(columns + rows) structural validation. Empty array ⇒ structurally sound
|
|
3245
|
-
* (duplicate ids, self-loops, and parallel-edge rules are NOT checked here
|
|
3255
|
+
* (duplicate ids, self-loops, and parallel-edge rules are NOT checked here
|
|
3246
3256
|
* they are shared object-lane semantics applied after materialization).
|
|
3247
3257
|
*/
|
|
3248
3258
|
declare function validateColumnarStructure(snapshot: ColumnarGraphSnapshot<unknown, unknown>): ColumnarIssue[];
|
|
@@ -3266,7 +3276,7 @@ declare function buildAcceptedFromColumnar<N, E>(snapshot: ColumnarGraphSnapshot
|
|
|
3266
3276
|
declare function columnarArrayBuffers(snapshot: ColumnarGraphSnapshot<unknown, unknown>): ArrayBuffer[];
|
|
3267
3277
|
/**
|
|
3268
3278
|
* `bufferOwnership: 'transfer'` — detach every underlying ArrayBuffer.
|
|
3269
|
-
* Called ONLY after validation and admission both succeeded
|
|
3279
|
+
* Called ONLY after validation and admission both succeeded:
|
|
3270
3280
|
* reject-before-allocation extends to reject-before-detach). Returns the
|
|
3271
3281
|
* number of buffers detached. SharedArrayBuffer-backed views are skipped
|
|
3272
3282
|
* (they cannot detach and remain shared by contract).
|
|
@@ -3274,8 +3284,8 @@ declare function columnarArrayBuffers(snapshot: ColumnarGraphSnapshot<unknown, u
|
|
|
3274
3284
|
declare function detachColumnarBuffers(snapshot: ColumnarGraphSnapshot<unknown, unknown>): number;
|
|
3275
3285
|
|
|
3276
3286
|
/**
|
|
3277
|
-
*
|
|
3278
|
-
* accessor forms: `FieldAccessor` + `SerializableTransform` + the `field
|
|
3287
|
+
* serializable field descriptors — the worker-lane
|
|
3288
|
+
* accessor forms: `FieldAccessor` + `SerializableTransform` + the `field`
|
|
3279
3289
|
* manual-schema helper. Descriptors are DATA, never code: they cross the
|
|
3280
3290
|
* worker boundary by structured clone, compare by canonical structural
|
|
3281
3291
|
* value, and unknown transform ops are validation errors (spec: "descriptors
|
|
@@ -3293,14 +3303,14 @@ declare function detachColumnarBuffers(snapshot: ColumnarGraphSnapshot<unknown,
|
|
|
3293
3303
|
* projection ever runs.
|
|
3294
3304
|
*/
|
|
3295
3305
|
|
|
3296
|
-
/** Branded attr path — `field<A,T
|
|
3297
|
-
* -time narrowing
|
|
3306
|
+
/** Branded attr path — `field<A,T>` is a manual-schema ASSERTION for
|
|
3307
|
+
* compile-time narrowing in codegen/schema packages, not runtime validation. */
|
|
3298
3308
|
type TypedFieldPath<A, T> = string & {
|
|
3299
3309
|
readonly __attrs?: A;
|
|
3300
3310
|
readonly __value?: T;
|
|
3301
3311
|
};
|
|
3302
3312
|
declare function field<A, T>(path: string): TypedFieldPath<A, T>;
|
|
3303
|
-
/**
|
|
3313
|
+
/** transform whitelist. Closed set — extending it is a spec amendment. */
|
|
3304
3314
|
type SerializableTransform = {
|
|
3305
3315
|
op: 'identity';
|
|
3306
3316
|
} | {
|
|
@@ -3347,7 +3357,7 @@ declare function isFieldAccessor(value: unknown): value is FieldAccessor<unknown
|
|
|
3347
3357
|
declare function descriptorKey(accessor: FieldAccessor<unknown, unknown>): string;
|
|
3348
3358
|
/**
|
|
3349
3359
|
* Evaluate a descriptor against an entity's `(id, attrs)` — the pure
|
|
3350
|
-
* function both lanes share (
|
|
3360
|
+
* function both lanes share (byte-exact parity starts with ONE
|
|
3351
3361
|
* evaluation implementation, imported by both threads, never duplicated).
|
|
3352
3362
|
*/
|
|
3353
3363
|
declare function evaluateFieldAccessor(accessor: FieldAccessor<unknown, unknown>, id: string, attrs: Readonly<Record<string, unknown>> | undefined): unknown;
|