@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.
@@ -1,29 +1,29 @@
1
- import { N as NodeId, d as GraphDiagnostic, e as GraphNode } from './index-CFZMson3.js';
1
+ import { N as NodeId, a as GraphDiagnostic, b as GraphNode } from './index-CPRTWpGA.js';
2
2
 
3
3
  /**
4
- * §16.3 stage-4 non-collapsing clusters (S12-T06) — pure derivation, no
4
+ * stage-4 non-collapsing clusters — pure derivation, no
5
5
  * engine, no DOM, no instance.
6
6
  *
7
- * Contract summary (spec §16.3 / §7.6 stage 4, R-16.3-17..21):
7
+ * Contract summary:
8
8
  * - Clusters are a categorical PARTITION of the current PHYSICAL scene, never
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 (R-16.3-17/19) — clusters coexist
12
- * with the stage-3 group rewrite by construction.
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
- * NaN in {@link ClusterDerivation.slotOrdinals} (the engine contract's
15
- * "not in any cluster" value, mirroring the NaN-position convention).
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
- * keys plus the layout seed (R-16.3-20). {@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 (R-16.3-21):
23
- * {@link clusterCentroids} is the ONLY member-scanning anchor path and runs
24
- * at settle (from the single permitted §7.1 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".
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 (R-16.3-21) and a stage-5 soft-mask change must
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 (S12-T06 investigation, recorded here because it is a contract
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 §13 config contract is
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`). Spec §10's `LayoutSpec.seed` (the
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 §10's normalized layout object lands and can supply
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 (R-16.3-20): a
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 (R-16.3-20). A non-finite explicit pair is
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 §11
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 (§16.3 super-nodes/meta-edges) are
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
- * §16.3 stage-4 derivation over the PHYSICAL prefix of the current scene.
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 (R-16.3-17/19).
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 (R-16.3-21). Called at settle (over the single
116
- * permitted §7.1 readback) and at commit under a FIXED layout; never per
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
- * ADR-006 D3 — the worker-lane wire contract: envelope codec, epoch guards,
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
- * via `inReplyTo`.
133
+ * via `inReplyTo`.
134
134
  * - Every envelope carries the model acceptance `epoch` it derived from
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.
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
- * deduplicated (two views over one buffer transfer once).
139
+ * deduplicated (two views over one buffer transfer once).
140
140
  * - Request classes: 'guaranteed' requests all complete (structural
141
- * derivation); 'throttled' requests coalesce LATEST-WINS per lane key
142
- * (styling reprojection) — superseding a pending throttled request aborts
143
- * the old one.
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
- * ADR-006 D5 — the main-side worker lane: transport ownership, the request
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 WorkerTransport as W, type WorkerEnvelope as a, type WorkerFactoryOption as b, clusterProbe as c, DEFAULT_LAYOUT_SEED as d, type WorkerEntity as e, clusterCentroids as f, collectTransfers as g, decodeStringTable as h, deriveClusters as i, encodeStringTable as j, generateClusterCenters as k, judgeEpoch as l, resolveClusterCenters as m, resetClusterProbe as r };
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 { G as GraphEngine, E as EngineCapabilities, a as EngineCommit, b as EngineHostEvents, F as FitViewOptions, V as ViewportState, c as EngineDiagnostic } from './index-CFZMson3.js';
2
- import { W as WorkerTransport, a as WorkerEnvelope } from './lane-CHHLuxgq.js';
3
- export { c as clusterProbe, r as resetClusterProbe } from './lane-CHHLuxgq.js';
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 (§18 /testing, §19).
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()` records the call, does NOT store the host events, and
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()`; default null (unsupported/not ready). */
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()`, in call order (queued or applied). */
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()/stepFrame() (ms). */
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() in manualFrames mode (oldest first). */
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() value (null = unpinned / never pinned). */
73
+ /** Last setPinnedIndices value (null = unpinned / never pinned). */
74
74
  get pinnedIndices(): readonly number[] | null;
75
- /** Last setSelectedIndices() value (null = cleared / never selected) — the
76
- * §9.1 highlight mirror of {@link pinnedIndices}. */
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
- * (§9.1 mask fast path), so the newest commit often carries none.
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(). No-op on the queue when empty. A drawn
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 (§13 overlay/activity clock).
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
- * §17/S13-T06 requestAnimationFrame audit — the quiescence instrument.
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() === 0` over a settled window), the demo e2e's
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 (ADR-006 D2/D5) — drives the REAL
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
- export { clusterProbe, resetClusterProbe } from './chunk-XAR262L3.js';
2
- import { handleWorkerRequest } from './chunk-GUXIITKK.js';
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
- // §17: the FakeEngine schedules nothing on its own — frames exist only
12
- // when a test calls stepFrame()/emitFrame() — so it is quiescent by
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()`, in call order (queued or applied). */
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()/stepFrame() (ms). */
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() in manualFrames mode (oldest first). */
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() value (null = unpinned / never pinned). */
74
+ /** Last setPinnedIndices value (null = unpinned / never pinned). */
76
75
  get pinnedIndices() {
77
76
  return this.pinned;
78
77
  }
79
- /** Last setSelectedIndices() value (null = cleared / never selected) — the
80
- * §9.1 highlight mirror of {@link pinnedIndices}. */
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
- * (§9.1 mask fast path), so the newest commit often carries none.
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(). No-op on the queue when empty. A drawn
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 (§13 overlay/activity clock).
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();