@modernrelay/orbit-core 0.13.5 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-NIJX5NVJ.js → chunk-KRCG2JFN.js} +169 -3
- package/dist/chunk-KRCG2JFN.js.map +1 -0
- package/dist/engine.d.ts +1 -1
- package/dist/{index-CFZMson3.d.ts → index-CPRTWpGA.d.ts} +176 -176
- package/dist/index.d.ts +611 -611
- package/dist/index.js +191 -154
- package/dist/index.js.map +1 -1
- package/dist/{lane-CHHLuxgq.d.ts → lane-_KPQ1deI.d.ts} +43 -43
- package/dist/testing.d.ts +22 -22
- package/dist/testing.js +99 -16
- package/dist/testing.js.map +1 -1
- package/dist/worker/entry.js +259 -2
- package/dist/worker/entry.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-GUXIITKK.js +0 -89
- package/dist/chunk-GUXIITKK.js.map +0 -1
- package/dist/chunk-NIJX5NVJ.js.map +0 -1
- package/dist/chunk-XAR262L3.js +0 -140
- package/dist/chunk-XAR262L3.js.map +0 -1
- package/dist/worker/entry.d.ts +0 -2
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import { N as NodeId,
|
|
1
|
+
import { N as NodeId, a as GraphDiagnostic, b as GraphNode } from './index-CPRTWpGA.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* stage-4 non-collapsing clusters — pure derivation, no
|
|
5
5
|
* engine, no DOM, no instance.
|
|
6
6
|
*
|
|
7
|
-
* Contract summary
|
|
7
|
+
* Contract summary:
|
|
8
8
|
* - Clusters are a categorical PARTITION of the current PHYSICAL scene, never
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* a rewrite: `deriveClusters` returns membership only. It synthesizes no
|
|
10
|
+
* super-nodes and no meta-edges, so node and edge counts are identical
|
|
11
|
+
* before and after a cluster spec lands — clusters coexist
|
|
12
|
+
* with the stage-3 group rewrite by construction.
|
|
13
13
|
* - `null` (and any non-string) from `by` means UNCLUSTERED: the slot carries
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* NaN in {@link ClusterDerivation.slotOrdinals} (the engine contract's
|
|
15
|
+
* "not in any cluster" value, mirroring the NaN-position convention).
|
|
16
16
|
* - Missing force centers generate deterministically from the ORDERED cluster
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* - Overlay anchoring must never scan members per frame
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
17
|
+
* keys plus the layout seed. {@link generateClusterCenters} uses
|
|
18
|
+
* only IEEE-754 correctly-rounded operations (+ - * / and integer ops) — no
|
|
19
|
+
* transcendentals — so the same ordered keys and seed produce BIT-IDENTICAL
|
|
20
|
+
* centers on any conforming engine, and a different seed produces different
|
|
21
|
+
* ones.
|
|
22
|
+
* - Overlay anchoring must never scan members per frame:
|
|
23
|
+
* {@link clusterCentroids} is the ONLY member-scanning anchor path and runs
|
|
24
|
+
* at settle (from the single permitted readback) or at commit under a
|
|
25
|
+
* fixed layout. {@link clusterProbe} counts every member visit so tests can
|
|
26
|
+
* pin "zero per-frame member iterations".
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -32,7 +32,7 @@ import { N as NodeId, d as GraphDiagnostic, e as GraphNode } from './index-CFZMs
|
|
|
32
32
|
* O(members) passes); `derivations` counts stage-4 recomputations. Both are
|
|
33
33
|
* unconditional O(1) increments so the instrumented and production paths are
|
|
34
34
|
* identical code. Tests snapshot, act, and compare: a per-frame overlay tick
|
|
35
|
-
* must add ZERO member visits
|
|
35
|
+
* must add ZERO member visits and a stage-5 soft-mask change must
|
|
36
36
|
* add ZERO derivations.
|
|
37
37
|
*/
|
|
38
38
|
declare const clusterProbe: {
|
|
@@ -44,15 +44,15 @@ declare function resetClusterProbe(): void;
|
|
|
44
44
|
/**
|
|
45
45
|
* The layout seed generated cluster centers key on.
|
|
46
46
|
*
|
|
47
|
-
* FINDING (
|
|
47
|
+
* FINDING (investigation, recorded here because it is a contract
|
|
48
48
|
* decision): the core owns NO numeric layout seed in v0.10. The only
|
|
49
|
-
* layout-seeding knob on the
|
|
49
|
+
* layout-seeding knob on the config contract is
|
|
50
50
|
* `EngineConfigUpdate.seedRadius` (the ring radius unknown/NaN positions seed
|
|
51
51
|
* onto) and `instance.ts` never sets it — every adapter defaults it itself
|
|
52
|
-
* (`CosmosEngine` → `spaceSize / 4`).
|
|
52
|
+
* (`CosmosEngine` → `spaceSize / 4`). `LayoutSpec.seed` (the
|
|
53
53
|
* `static` layout's reproducibility seed) is not implemented in v0.10 either.
|
|
54
54
|
* So this constant is the single named place the instance keys generated
|
|
55
|
-
* centers on until
|
|
55
|
+
* centers on until normalized layout object lands and can supply
|
|
56
56
|
* `LayoutSpec.seed` here.
|
|
57
57
|
*/
|
|
58
58
|
declare const DEFAULT_LAYOUT_SEED = 1592594996;
|
|
@@ -64,7 +64,7 @@ declare const DEFAULT_LAYOUT_SEED = 1592594996;
|
|
|
64
64
|
*/
|
|
65
65
|
declare const DEFAULT_CLUSTER_CENTER_RADIUS = 1024;
|
|
66
66
|
/**
|
|
67
|
-
* Deterministic force centers for ORDERED cluster keys
|
|
67
|
+
* Deterministic force centers for ORDERED cluster keys: a
|
|
68
68
|
* low-discrepancy (Halton base 2/3) spread offset per key by a seeded hash,
|
|
69
69
|
* mapped into `[-radius, radius]²`.
|
|
70
70
|
*
|
|
@@ -77,13 +77,13 @@ declare const DEFAULT_CLUSTER_CENTER_RADIUS = 1024;
|
|
|
77
77
|
declare function generateClusterCenters(keys: readonly string[], seed?: number, radius?: number): Float32Array;
|
|
78
78
|
/**
|
|
79
79
|
* Force centers for ordered keys with caller-supplied entries winning:
|
|
80
|
-
* ONLY the missing keys generate
|
|
80
|
+
* ONLY the missing keys generate. A non-finite explicit pair is
|
|
81
81
|
* treated as missing (D4 boundary hygiene — a NaN center would poison the
|
|
82
82
|
* engine's force field).
|
|
83
83
|
*/
|
|
84
84
|
declare function resolveClusterCenters(keys: readonly string[], explicit: ReadonlyMap<string, readonly [number, number]> | undefined, seed?: number, radius?: number): Float32Array;
|
|
85
85
|
interface ClusterDerivation {
|
|
86
|
-
/** Distinct keys in FIRST-ENCOUNTER order over the physical scene (the
|
|
86
|
+
/** Distinct keys in FIRST-ENCOUNTER order over the physical scene (the
|
|
87
87
|
* categorical-domain convention, shared with groupBy). Ordinal i ↔ keys[i]. */
|
|
88
88
|
keys: readonly string[];
|
|
89
89
|
/** key → ordinal (the reverse of `keys`). */
|
|
@@ -92,7 +92,7 @@ interface ClusterDerivation {
|
|
|
92
92
|
membersByKey: ReadonlyMap<string, readonly NodeId[]>;
|
|
93
93
|
/**
|
|
94
94
|
* PHYSICAL slot → cluster ordinal, NaN = unclustered. Length is the caller's
|
|
95
|
-
* `sceneCount`, so synthetic suffix slots
|
|
95
|
+
* `sceneCount`, so synthetic suffix slots are
|
|
96
96
|
* always NaN: aggregates are never members of a cluster. This IS the engine
|
|
97
97
|
* `config.cluster.pointClusters` payload — no copy at the sink.
|
|
98
98
|
*/
|
|
@@ -102,18 +102,18 @@ interface ClusterDerivation {
|
|
|
102
102
|
diagnostic: GraphDiagnostic | null;
|
|
103
103
|
}
|
|
104
104
|
/**
|
|
105
|
-
*
|
|
105
|
+
* stage-4 derivation over the PHYSICAL prefix of the current scene.
|
|
106
106
|
*
|
|
107
107
|
* `nodes` are the physical rows (post-group-rewrite when a rewrite is live)
|
|
108
108
|
* aligned to scene slots `0..nodes.length-1`; `sceneCount` is the FULL scene
|
|
109
109
|
* point count so the returned `slotOrdinals` covers the synthetic suffix too.
|
|
110
|
-
* Preserves everything and synthesizes nothing
|
|
110
|
+
* Preserves everything and synthesizes nothing.
|
|
111
111
|
*/
|
|
112
112
|
declare function deriveClusters<N>(nodes: readonly GraphNode<N>[], by: (node: GraphNode<N>) => string | null, sceneCount?: number): ClusterDerivation;
|
|
113
113
|
/**
|
|
114
114
|
* Centroids of every cluster from a slot-aligned position buffer — the ONLY
|
|
115
|
-
* member-scanning anchor path
|
|
116
|
-
* permitted
|
|
115
|
+
* member-scanning anchor path. Called at settle (over the single
|
|
116
|
+
* permitted readback) and at commit under a FIXED layout; never per
|
|
117
117
|
* frame. Slots with unknown (NaN) positions are skipped; a cluster with no
|
|
118
118
|
* placeable member keeps its `fallback` entry (its force center), so a label
|
|
119
119
|
* never jumps to the origin.
|
|
@@ -121,7 +121,7 @@ declare function deriveClusters<N>(nodes: readonly GraphNode<N>[], by: (node: Gr
|
|
|
121
121
|
declare function clusterCentroids(slotOrdinals: Float32Array, positions: Float32Array, clusterCount: number, fallback: Float32Array): Float32Array;
|
|
122
122
|
|
|
123
123
|
/**
|
|
124
|
-
*
|
|
124
|
+
* Worker-lane wire protocol: envelope codec, epoch guards,
|
|
125
125
|
* consolidated transfer lists, and request-class bookkeeping
|
|
126
126
|
* (guaranteed-vs-throttled). Pure functions and one small class; no Worker
|
|
127
127
|
* construction here — the runtime that OWNS a thread imports this, and the
|
|
@@ -130,17 +130,17 @@ declare function clusterCentroids(slotOrdinals: Float32Array, positions: Float32
|
|
|
130
130
|
*
|
|
131
131
|
* Contract invariants (each pinned by test):
|
|
132
132
|
* - `msgId` is monotonic PER DIRECTION; a reply names the request it answers
|
|
133
|
-
*
|
|
133
|
+
* via `inReplyTo`.
|
|
134
134
|
* - Every envelope carries the model acceptance `epoch` it derived from
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
135
|
+
* (I1 discipline: references do not cross threads — epochs replace them).
|
|
136
|
+
* Results for a superseded epoch are dropped AT THE BOUNDARY, before the
|
|
137
|
+
* acceptance queue ever sees them.
|
|
138
138
|
* - Transfers are CONSOLIDATED: one ArrayBuffer per channel per message,
|
|
139
|
-
*
|
|
139
|
+
* deduplicated (two views over one buffer transfer once).
|
|
140
140
|
* - Request classes: 'guaranteed' requests all complete (structural
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
141
|
+
* derivation); 'throttled' requests coalesce LATEST-WINS per lane key
|
|
142
|
+
* (styling reprojection) — superseding a pending throttled request aborts
|
|
143
|
+
* the old one.
|
|
144
144
|
*/
|
|
145
145
|
type WorkerEntity = 'nodes' | 'edges' | 'scene';
|
|
146
146
|
interface WorkerEnvelope {
|
|
@@ -186,7 +186,7 @@ declare function encodeStringTable(strings: readonly string[]): EncodedStringTab
|
|
|
186
186
|
declare function decodeStringTable(table: EncodedStringTable): string[];
|
|
187
187
|
|
|
188
188
|
/**
|
|
189
|
-
*
|
|
189
|
+
* Main-side worker lane: transport ownership, the request
|
|
190
190
|
* ledger (guaranteed/throttled), and the tri-option factory with the
|
|
191
191
|
* fall-back-to-main contract (`worker-unavailable`, never a crash).
|
|
192
192
|
*
|
|
@@ -213,4 +213,4 @@ type WorkerFactoryOption = {
|
|
|
213
213
|
create: () => Worker;
|
|
214
214
|
} | undefined;
|
|
215
215
|
|
|
216
|
-
export { type ClusterDerivation as C, DEFAULT_CLUSTER_CENTER_RADIUS as D, type EncodedStringTable as E, type RequestClass as R, type
|
|
216
|
+
export { type ClusterDerivation as C, DEFAULT_CLUSTER_CENTER_RADIUS as D, type EncodedStringTable as E, type RequestClass as R, type WorkerFactoryOption as W, DEFAULT_LAYOUT_SEED as a, type WorkerEntity as b, type WorkerEnvelope as c, type WorkerTransport as d, clusterCentroids as e, collectTransfers as f, decodeStringTable as g, deriveClusters as h, encodeStringTable as i, generateClusterCenters as j, judgeEpoch as k, clusterProbe as l, resetClusterProbe as m, resolveClusterCenters as r };
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
export {
|
|
1
|
+
import { a5 as GraphEngine, a7 as EngineCapabilities, a6 as EngineCommit, aJ as EngineHostEvents, aK as FitViewOptions, V as ViewportState, aL as EngineDiagnostic } from './index-CPRTWpGA.js';
|
|
2
|
+
import { d as WorkerTransport, c as WorkerEnvelope } from './lane-_KPQ1deI.js';
|
|
3
|
+
export { l as clusterProbe, m as resetClusterProbe } from './lane-_KPQ1deI.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* FakeEngine — the public headless test seam
|
|
6
|
+
* FakeEngine — the public headless test seam.
|
|
7
7
|
*
|
|
8
8
|
* Implements the full GraphEngine adapter contract with zero DOM/WebGL access
|
|
9
9
|
* so consumer suites can run under node/jsdom. Every public method call is
|
|
@@ -23,17 +23,17 @@ interface FakeEngineOptions {
|
|
|
23
23
|
/** Overrides merged over the defaults (linkPicking/trackedPositions false, rangeUpdates [], simulation true). */
|
|
24
24
|
capabilities?: Partial<EngineCapabilities>;
|
|
25
25
|
/**
|
|
26
|
-
* When true, commits queue instead of applying immediately; `stepFrame
|
|
26
|
+
* When true, commits queue instead of applying immediately; `stepFrame`
|
|
27
27
|
* applies the oldest queued commit. Lets tests observe the lag between the
|
|
28
|
-
* desired-render revision and `appliedRevision
|
|
28
|
+
* desired-render revision and `appliedRevision`.
|
|
29
29
|
*/
|
|
30
30
|
manualFrames?: boolean;
|
|
31
31
|
/**
|
|
32
|
-
* When set, `mount
|
|
32
|
+
* When set, `mount` records the call, does NOT store the host events, and
|
|
33
33
|
* rejects with this error — simulates an engine that cannot initialize.
|
|
34
34
|
*/
|
|
35
35
|
mountError?: Error;
|
|
36
|
-
/** Resolved by `captureScreenshot
|
|
36
|
+
/** Resolved by `captureScreenshot`; default null (unsupported/not ready). */
|
|
37
37
|
screenshot?: Blob | null;
|
|
38
38
|
}
|
|
39
39
|
/** One recorded public method invocation. */
|
|
@@ -45,7 +45,7 @@ declare class FakeEngine implements GraphEngine {
|
|
|
45
45
|
readonly capabilities: EngineCapabilities;
|
|
46
46
|
/** Every public method call, in order. */
|
|
47
47
|
readonly calls: RecordedCall[];
|
|
48
|
-
/** Every commit passed to `commit
|
|
48
|
+
/** Every commit passed to `commit`, in call order (queued or applied). */
|
|
49
49
|
readonly commits: EngineCommit[];
|
|
50
50
|
/** The fitView/zoom/setViewport/zoomToIndex entries of `calls`, in order. */
|
|
51
51
|
readonly cameraCalls: RecordedCall[];
|
|
@@ -53,32 +53,32 @@ declare class FakeEngine implements GraphEngine {
|
|
|
53
53
|
private readonly mountError;
|
|
54
54
|
private readonly screenshot;
|
|
55
55
|
private events;
|
|
56
|
-
/** Auto-advancing activity clock for emitFrame
|
|
56
|
+
/** Auto-advancing activity clock for emitFrame/stepFrame (ms). */
|
|
57
57
|
private frameClock;
|
|
58
58
|
private destroyedFlag;
|
|
59
59
|
private viewport;
|
|
60
60
|
private applied;
|
|
61
61
|
private positions;
|
|
62
62
|
/** Links of the most recent structure-bearing commit (commit-time, even in
|
|
63
|
-
* manualFrames mode) — the adjacency source for neighborIndices
|
|
63
|
+
* manualFrames mode) — the adjacency source for neighborIndices. */
|
|
64
64
|
private lastLinks;
|
|
65
65
|
private pinned;
|
|
66
66
|
private selected;
|
|
67
|
-
/** Commits awaiting stepFrame
|
|
67
|
+
/** Commits awaiting stepFrame in manualFrames mode (oldest first). */
|
|
68
68
|
private readonly pendingFrames;
|
|
69
69
|
constructor(options?: FakeEngineOptions);
|
|
70
70
|
/** Most recent commit, if any. */
|
|
71
71
|
get lastCommit(): EngineCommit | undefined;
|
|
72
72
|
get destroyed(): boolean;
|
|
73
|
-
/** Last setPinnedIndices
|
|
73
|
+
/** Last setPinnedIndices value (null = unpinned / never pinned). */
|
|
74
74
|
get pinnedIndices(): readonly number[] | null;
|
|
75
|
-
/** Last setSelectedIndices
|
|
76
|
-
*
|
|
75
|
+
/** Last setSelectedIndices value (null = cleared / never selected) — the
|
|
76
|
+
* highlight mirror of {@link pinnedIndices}. */
|
|
77
77
|
get selectedIndices(): readonly number[] | null;
|
|
78
78
|
/**
|
|
79
79
|
* Structure of the most recent structure-bearing commit — the geometry the
|
|
80
|
-
* engine currently holds. Buffer channels commit independently of structure
|
|
81
|
-
*
|
|
80
|
+
* engine currently holds. Buffer channels commit independently of structure,
|
|
81
|
+
* so the newest commit often carries none.
|
|
82
82
|
*/
|
|
83
83
|
get lastStructure(): NonNullable<EngineCommit['structure']> | undefined;
|
|
84
84
|
/**
|
|
@@ -122,9 +122,9 @@ declare class FakeEngine implements GraphEngine {
|
|
|
122
122
|
destroy(): void;
|
|
123
123
|
/**
|
|
124
124
|
* manualFrames mode: apply the oldest queued commit, making its revision
|
|
125
|
-
* visible via appliedRevision
|
|
125
|
+
* visible via appliedRevision. No-op on the queue when empty. A drawn
|
|
126
126
|
* frame is also an activity-clock sample, so a mounted engine gets an
|
|
127
|
-
* `onFrame` tick
|
|
127
|
+
* `onFrame` tick.
|
|
128
128
|
*/
|
|
129
129
|
stepFrame(): void;
|
|
130
130
|
/**
|
|
@@ -162,7 +162,7 @@ declare class FakeEngine implements GraphEngine {
|
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
/**
|
|
165
|
-
*
|
|
165
|
+
* requestAnimationFrame audit — the quiescence instrument.
|
|
166
166
|
*
|
|
167
167
|
* Wraps a window's `requestAnimationFrame` and counts SCHEDULING calls
|
|
168
168
|
* (registrations) separately from delivered ticks: the stop-at-rest claim is
|
|
@@ -171,7 +171,7 @@ declare class FakeEngine implements GraphEngine {
|
|
|
171
171
|
* still re-arming itself.
|
|
172
172
|
*
|
|
173
173
|
* Used three ways: core/react quiescence tests (install, drive the instance,
|
|
174
|
-
* assert `registrations
|
|
174
|
+
* assert `registrations === 0` over a settled window), the demo e2e's
|
|
175
175
|
* page-side counterpart (same shape, injected via addInitScript), and the
|
|
176
176
|
* dev-mode runtime assertion in the instance. The M0 spike's instrument.ts
|
|
177
177
|
* pioneered the wrapper; this is its reusable, uninstallable form.
|
|
@@ -197,7 +197,7 @@ interface RafAudit {
|
|
|
197
197
|
declare function installRafAudit(win?: Window): RafAudit;
|
|
198
198
|
|
|
199
199
|
/**
|
|
200
|
-
* In-process worker transport double
|
|
200
|
+
* In-process worker transport double — drives the REAL
|
|
201
201
|
* runtime module through the REAL codec, asynchronously (microtask), with
|
|
202
202
|
* REAL transfer semantics: payloads round through `structuredClone` with
|
|
203
203
|
* the declared transfer list, so a wrong transfer set detaches something
|
package/dist/testing.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { EnvelopeSequencer } from './chunk-NIJX5NVJ.js';
|
|
1
|
+
import { EnvelopeSequencer, isWellFormedEnvelope, decodeStringTable, acceptColumnar, collectTransfers } from './chunk-KRCG2JFN.js';
|
|
2
|
+
export { clusterProbe, resetClusterProbe } from './chunk-KRCG2JFN.js';
|
|
4
3
|
|
|
5
4
|
// src/testing/FakeEngine.ts
|
|
6
5
|
var DEFAULT_CAPABILITIES = {
|
|
@@ -8,8 +7,8 @@ var DEFAULT_CAPABILITIES = {
|
|
|
8
7
|
rangeUpdates: [],
|
|
9
8
|
trackedPositions: false,
|
|
10
9
|
simulation: true,
|
|
11
|
-
//
|
|
12
|
-
// when a test calls stepFrame
|
|
10
|
+
// the FakeEngine schedules nothing on its own — frames exist only
|
|
11
|
+
// when a test calls stepFrame/emitFrame — so it is quiescent by
|
|
13
12
|
// construction, and those calls apply-then-emit, which is genuinely
|
|
14
13
|
// post-draw.
|
|
15
14
|
idleFrames: "stops",
|
|
@@ -38,7 +37,7 @@ var FakeEngine = class {
|
|
|
38
37
|
capabilities;
|
|
39
38
|
/** Every public method call, in order. */
|
|
40
39
|
calls = [];
|
|
41
|
-
/** Every commit passed to `commit
|
|
40
|
+
/** Every commit passed to `commit`, in call order (queued or applied). */
|
|
42
41
|
commits = [];
|
|
43
42
|
/** The fitView/zoom/setViewport/zoomToIndex entries of `calls`, in order. */
|
|
44
43
|
cameraCalls = [];
|
|
@@ -46,18 +45,18 @@ var FakeEngine = class {
|
|
|
46
45
|
mountError;
|
|
47
46
|
screenshot;
|
|
48
47
|
events = null;
|
|
49
|
-
/** Auto-advancing activity clock for emitFrame
|
|
48
|
+
/** Auto-advancing activity clock for emitFrame/stepFrame (ms). */
|
|
50
49
|
frameClock = 0;
|
|
51
50
|
destroyedFlag = false;
|
|
52
51
|
viewport = { x: 0, y: 0, zoom: 1 };
|
|
53
52
|
applied = null;
|
|
54
53
|
positions = null;
|
|
55
54
|
/** Links of the most recent structure-bearing commit (commit-time, even in
|
|
56
|
-
* manualFrames mode) — the adjacency source for neighborIndices
|
|
55
|
+
* manualFrames mode) — the adjacency source for neighborIndices. */
|
|
57
56
|
lastLinks = null;
|
|
58
57
|
pinned = null;
|
|
59
58
|
selected = null;
|
|
60
|
-
/** Commits awaiting stepFrame
|
|
59
|
+
/** Commits awaiting stepFrame in manualFrames mode (oldest first). */
|
|
61
60
|
pendingFrames = [];
|
|
62
61
|
constructor(options = {}) {
|
|
63
62
|
this.capabilities = { ...DEFAULT_CAPABILITIES, ...options.capabilities };
|
|
@@ -72,19 +71,19 @@ var FakeEngine = class {
|
|
|
72
71
|
get destroyed() {
|
|
73
72
|
return this.destroyedFlag;
|
|
74
73
|
}
|
|
75
|
-
/** Last setPinnedIndices
|
|
74
|
+
/** Last setPinnedIndices value (null = unpinned / never pinned). */
|
|
76
75
|
get pinnedIndices() {
|
|
77
76
|
return this.pinned;
|
|
78
77
|
}
|
|
79
|
-
/** Last setSelectedIndices
|
|
80
|
-
*
|
|
78
|
+
/** Last setSelectedIndices value (null = cleared / never selected) — the
|
|
79
|
+
* highlight mirror of {@link pinnedIndices}. */
|
|
81
80
|
get selectedIndices() {
|
|
82
81
|
return this.selected;
|
|
83
82
|
}
|
|
84
83
|
/**
|
|
85
84
|
* Structure of the most recent structure-bearing commit — the geometry the
|
|
86
|
-
* engine currently holds. Buffer channels commit independently of structure
|
|
87
|
-
*
|
|
85
|
+
* engine currently holds. Buffer channels commit independently of structure,
|
|
86
|
+
* so the newest commit often carries none.
|
|
88
87
|
*/
|
|
89
88
|
get lastStructure() {
|
|
90
89
|
for (let i = this.commits.length - 1; i >= 0; i--) {
|
|
@@ -278,9 +277,9 @@ var FakeEngine = class {
|
|
|
278
277
|
// --- test helpers (not part of GraphEngine) ---
|
|
279
278
|
/**
|
|
280
279
|
* manualFrames mode: apply the oldest queued commit, making its revision
|
|
281
|
-
* visible via appliedRevision
|
|
280
|
+
* visible via appliedRevision. No-op on the queue when empty. A drawn
|
|
282
281
|
* frame is also an activity-clock sample, so a mounted engine gets an
|
|
283
|
-
* `onFrame` tick
|
|
282
|
+
* `onFrame` tick.
|
|
284
283
|
*/
|
|
285
284
|
stepFrame() {
|
|
286
285
|
this.record("stepFrame", []);
|
|
@@ -458,6 +457,90 @@ function installRafAudit(win = globalThis) {
|
|
|
458
457
|
};
|
|
459
458
|
}
|
|
460
459
|
|
|
460
|
+
// src/worker/runtime.ts
|
|
461
|
+
function handleWorkerRequest(request, sequencer) {
|
|
462
|
+
if (!isWellFormedEnvelope(request)) {
|
|
463
|
+
const reply2 = sequencer.make(0, "scene", "error", {
|
|
464
|
+
message: "malformed envelope (protocol violation)"
|
|
465
|
+
});
|
|
466
|
+
return { reply: reply2, transfers: [] };
|
|
467
|
+
}
|
|
468
|
+
if (request.op === "derive-columnar") {
|
|
469
|
+
try {
|
|
470
|
+
const p = request.payload;
|
|
471
|
+
const snapshot = {
|
|
472
|
+
kind: "columnar",
|
|
473
|
+
datasetKey: "worker",
|
|
474
|
+
// acceptance rules never read the coordinate
|
|
475
|
+
sourceRevision: 0,
|
|
476
|
+
nodes: {
|
|
477
|
+
ids: {
|
|
478
|
+
kind: "string",
|
|
479
|
+
dictionary: decodeStringTable(p.nodeIdTable),
|
|
480
|
+
codes: p.nodeIdCodes
|
|
481
|
+
},
|
|
482
|
+
columns: {},
|
|
483
|
+
length: p.nodeCount
|
|
484
|
+
},
|
|
485
|
+
edges: {
|
|
486
|
+
ids: {
|
|
487
|
+
kind: "string",
|
|
488
|
+
dictionary: decodeStringTable(p.edgeIdTable),
|
|
489
|
+
codes: p.edgeIdCodes
|
|
490
|
+
},
|
|
491
|
+
source: p.edgeSource,
|
|
492
|
+
target: p.edgeTarget,
|
|
493
|
+
columns: {},
|
|
494
|
+
length: p.edgeCount
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
const acceptance = acceptColumnar(snapshot);
|
|
498
|
+
const result = {
|
|
499
|
+
keepNodes: acceptance.keepNodes,
|
|
500
|
+
keepEdges: acceptance.keepEdges,
|
|
501
|
+
acceptedNodeCount: acceptance.acceptedNodeCount,
|
|
502
|
+
acceptedEdgeCount: acceptance.acceptedEdgeCount,
|
|
503
|
+
nodeAcceptedIndex: acceptance.nodeAcceptedIndex,
|
|
504
|
+
links: acceptance.links,
|
|
505
|
+
diagnostics: acceptance.diagnostics
|
|
506
|
+
};
|
|
507
|
+
const reply2 = sequencer.make(
|
|
508
|
+
request.epoch,
|
|
509
|
+
request.entity,
|
|
510
|
+
"result",
|
|
511
|
+
result,
|
|
512
|
+
request.msgId
|
|
513
|
+
);
|
|
514
|
+
return {
|
|
515
|
+
reply: reply2,
|
|
516
|
+
transfers: collectTransfers([
|
|
517
|
+
result.keepNodes,
|
|
518
|
+
result.keepEdges,
|
|
519
|
+
result.nodeAcceptedIndex,
|
|
520
|
+
result.links
|
|
521
|
+
])
|
|
522
|
+
};
|
|
523
|
+
} catch (err) {
|
|
524
|
+
const reply2 = sequencer.make(
|
|
525
|
+
request.epoch,
|
|
526
|
+
request.entity,
|
|
527
|
+
"error",
|
|
528
|
+
{ message: err instanceof Error ? err.message : String(err) },
|
|
529
|
+
request.msgId
|
|
530
|
+
);
|
|
531
|
+
return { reply: reply2, transfers: [] };
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
const reply = sequencer.make(
|
|
535
|
+
request.epoch,
|
|
536
|
+
request.entity,
|
|
537
|
+
"error",
|
|
538
|
+
{ message: `unknown op '${request.op}'` },
|
|
539
|
+
request.msgId
|
|
540
|
+
);
|
|
541
|
+
return { reply, transfers: [] };
|
|
542
|
+
}
|
|
543
|
+
|
|
461
544
|
// src/testing/workerDouble.ts
|
|
462
545
|
function createWorkerDouble() {
|
|
463
546
|
const sequencer = new EnvelopeSequencer();
|