@modernrelay/orbit-core 0.2.0 → 0.13.5
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-GUXIITKK.js +89 -0
- package/dist/chunk-GUXIITKK.js.map +1 -0
- package/dist/chunk-NIJX5NVJ.js +212 -0
- package/dist/chunk-NIJX5NVJ.js.map +1 -0
- package/dist/{chunk-Z7FOEASL.js → chunk-XAR262L3.js} +4 -5
- package/dist/chunk-XAR262L3.js.map +1 -0
- package/dist/engine.d.ts +1 -1
- package/dist/{index-BPjuELfY.d.ts → index-CFZMson3.d.ts} +201 -4
- package/dist/index.d.ts +364 -4
- package/dist/index.js +1690 -44
- package/dist/index.js.map +1 -1
- package/dist/{clusters-ExqnvobT.d.ts → lane-CHHLuxgq.d.ts} +95 -2
- package/dist/testing.d.ts +54 -3
- package/dist/testing.js +115 -7
- package/dist/testing.js.map +1 -1
- package/dist/worker/entry.d.ts +2 -0
- package/dist/worker/entry.js +12 -0
- package/dist/worker/entry.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-Z7FOEASL.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { N as NodeId,
|
|
1
|
+
import { N as NodeId, d as GraphDiagnostic, e as GraphNode } from './index-CFZMson3.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* §16.3 stage-4 non-collapsing clusters (S12-T06) — pure derivation, no
|
|
@@ -120,4 +120,97 @@ declare function deriveClusters<N>(nodes: readonly GraphNode<N>[], by: (node: Gr
|
|
|
120
120
|
*/
|
|
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,
|
|
125
|
+
* consolidated transfer lists, and request-class bookkeeping
|
|
126
|
+
* (guaranteed-vs-throttled). Pure functions and one small class; no Worker
|
|
127
|
+
* construction here — the runtime that OWNS a thread imports this, and the
|
|
128
|
+
* parity suite drives the same codec in-process (the codec is the unit
|
|
129
|
+
* under test; a live thread adds scheduling, not semantics).
|
|
130
|
+
*
|
|
131
|
+
* Contract invariants (each pinned by test):
|
|
132
|
+
* - `msgId` is monotonic PER DIRECTION; a reply names the request it answers
|
|
133
|
+
* via `inReplyTo`.
|
|
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.
|
|
138
|
+
* - Transfers are CONSOLIDATED: one ArrayBuffer per channel per message,
|
|
139
|
+
* deduplicated (two views over one buffer transfer once).
|
|
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.
|
|
144
|
+
*/
|
|
145
|
+
type WorkerEntity = 'nodes' | 'edges' | 'scene';
|
|
146
|
+
interface WorkerEnvelope {
|
|
147
|
+
msgId: number;
|
|
148
|
+
/** The request this envelope answers (results/errors only). */
|
|
149
|
+
inReplyTo?: number;
|
|
150
|
+
/** Model acceptance epoch the payload derives from. */
|
|
151
|
+
epoch: number;
|
|
152
|
+
entity: WorkerEntity;
|
|
153
|
+
op: string;
|
|
154
|
+
payload: unknown;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Boundary guard: does an arriving envelope still apply? Stale epochs are
|
|
158
|
+
* dropped silently (superseded work is EXPECTED under latest-wins, not an
|
|
159
|
+
* error); a FUTURE epoch is a protocol violation (the other side cannot
|
|
160
|
+
* know an epoch this side has not yet issued).
|
|
161
|
+
*/
|
|
162
|
+
type EpochVerdict = 'accept' | 'stale' | 'protocol-violation';
|
|
163
|
+
declare function judgeEpoch(envelope: WorkerEnvelope, currentEpoch: number): EpochVerdict;
|
|
164
|
+
/**
|
|
165
|
+
* Consolidated transfer list: every DISTINCT underlying ArrayBuffer behind
|
|
166
|
+
* the given views, in first-seen order. Two views over one buffer yield one
|
|
167
|
+
* entry (transferring twice throws in every engine). SharedArrayBuffer is
|
|
168
|
+
* excluded by construction — the D3 contract never requires shared memory.
|
|
169
|
+
*/
|
|
170
|
+
declare function collectTransfers(views: readonly ArrayBufferView[]): ArrayBuffer[];
|
|
171
|
+
/** Request classes (Mosaic split): 'guaranteed' all complete; 'throttled'
|
|
172
|
+
* coalesces latest-wins per lane. */
|
|
173
|
+
type RequestClass = 'guaranteed' | 'throttled';
|
|
174
|
+
/**
|
|
175
|
+
* UTF-8 string tables — dictionaries cross the boundary as TRANSFERABLES,
|
|
176
|
+
* never as structured-clone string arrays (cloning 1M strings serializes on
|
|
177
|
+
* the SENDING thread — the exact main-thread tax this lane exists to
|
|
178
|
+
* remove; the mapbox pattern). Layout: byte offsets (Uint32Array, length
|
|
179
|
+
* n+1) + concatenated UTF-8 bytes.
|
|
180
|
+
*/
|
|
181
|
+
interface EncodedStringTable {
|
|
182
|
+
offsets: Uint32Array;
|
|
183
|
+
bytes: Uint8Array;
|
|
184
|
+
}
|
|
185
|
+
declare function encodeStringTable(strings: readonly string[]): EncodedStringTable;
|
|
186
|
+
declare function decodeStringTable(table: EncodedStringTable): string[];
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* ADR-006 D5 — the main-side worker lane: transport ownership, the request
|
|
190
|
+
* ledger (guaranteed/throttled), and the tri-option factory with the
|
|
191
|
+
* fall-back-to-main contract (`worker-unavailable`, never a crash).
|
|
192
|
+
*
|
|
193
|
+
* The lane is TRANSPORT-agnostic: a real `Worker` and the in-process test
|
|
194
|
+
* double implement the same three-method surface, so every ordering and
|
|
195
|
+
* supersession behavior is pinned without a thread. Epoch judgment stays
|
|
196
|
+
* with the CALLER (the instance owns the acceptance epoch); the lane owns
|
|
197
|
+
* delivery, matching, and cancellation.
|
|
198
|
+
*/
|
|
199
|
+
|
|
200
|
+
interface WorkerTransport {
|
|
201
|
+
post(envelope: WorkerEnvelope, transfers: readonly ArrayBuffer[]): void;
|
|
202
|
+
onReply(cb: (reply: WorkerEnvelope) => void): void;
|
|
203
|
+
/** Async death channel: module-load failure, uncaught worker error,
|
|
204
|
+
* messageerror. A transport that cannot fail may omit it. */
|
|
205
|
+
onError?(cb: (reason: string) => void): void;
|
|
206
|
+
terminate(): void;
|
|
207
|
+
}
|
|
208
|
+
/** D5 tri-option: consumer URL, full construction control, or the inline
|
|
209
|
+
* default (a `Worker` over the built entry asset). */
|
|
210
|
+
type WorkerFactoryOption = {
|
|
211
|
+
url: URL | string;
|
|
212
|
+
} | {
|
|
213
|
+
create: () => Worker;
|
|
214
|
+
} | undefined;
|
|
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 };
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
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';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* FakeEngine — the public headless test seam (§18 /testing, §19).
|
|
@@ -160,4 +161,54 @@ declare class FakeEngine implements GraphEngine {
|
|
|
160
161
|
private apply;
|
|
161
162
|
}
|
|
162
163
|
|
|
163
|
-
|
|
164
|
+
/**
|
|
165
|
+
* §17/S13-T06 requestAnimationFrame audit — the quiescence instrument.
|
|
166
|
+
*
|
|
167
|
+
* Wraps a window's `requestAnimationFrame` and counts SCHEDULING calls
|
|
168
|
+
* (registrations) separately from delivered ticks: the stop-at-rest claim is
|
|
169
|
+
* about registrations — a truly stopped loop schedules nothing at all, while
|
|
170
|
+
* tick counts alone can read zero under fake timers even though a loop is
|
|
171
|
+
* still re-arming itself.
|
|
172
|
+
*
|
|
173
|
+
* Used three ways: core/react quiescence tests (install, drive the instance,
|
|
174
|
+
* assert `registrations() === 0` over a settled window), the demo e2e's
|
|
175
|
+
* page-side counterpart (same shape, injected via addInitScript), and the
|
|
176
|
+
* dev-mode runtime assertion in the instance. The M0 spike's instrument.ts
|
|
177
|
+
* pioneered the wrapper; this is its reusable, uninstallable form.
|
|
178
|
+
*/
|
|
179
|
+
interface RafAudit {
|
|
180
|
+
/** requestAnimationFrame CALLS observed since install (scheduling). */
|
|
181
|
+
registrations(): number;
|
|
182
|
+
/** Callbacks actually delivered since install (ticks). */
|
|
183
|
+
ticks(): number;
|
|
184
|
+
/** Zero both counters without uninstalling. */
|
|
185
|
+
reset(): void;
|
|
186
|
+
/** Restore the original requestAnimationFrame. Idempotent. */
|
|
187
|
+
uninstall(): void;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Install the audit on `win` (default: globalThis). Under jsdom + vitest
|
|
191
|
+
* fake timers, rAF is timer-backed, so `vi.advanceTimersByTime` drives
|
|
192
|
+
* deliveries — registrations count regardless. In a rAF-less environment
|
|
193
|
+
* (plain-Node core tests) a 16ms setTimeout shim delivers: without the
|
|
194
|
+
* audit, any rAF call there would simply crash — with it, the call is
|
|
195
|
+
* COUNTED, which is exactly the evidence a quiescence test wants.
|
|
196
|
+
*/
|
|
197
|
+
declare function installRafAudit(win?: Window): RafAudit;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* In-process worker transport double (ADR-006 D2/D5) — drives the REAL
|
|
201
|
+
* runtime module through the REAL codec, asynchronously (microtask), with
|
|
202
|
+
* REAL transfer semantics: payloads round through `structuredClone` with
|
|
203
|
+
* the declared transfer list, so a wrong transfer set detaches something
|
|
204
|
+
* the sender still needs and fails the suite loudly — exactly what a live
|
|
205
|
+
* thread would do, minus the scheduling.
|
|
206
|
+
*/
|
|
207
|
+
|
|
208
|
+
interface WorkerDouble extends WorkerTransport {
|
|
209
|
+
/** Requests handled so far (post-clone shapes, in arrival order). */
|
|
210
|
+
readonly handled: WorkerEnvelope[];
|
|
211
|
+
}
|
|
212
|
+
declare function createWorkerDouble(): WorkerDouble;
|
|
213
|
+
|
|
214
|
+
export { EngineDiagnostic, FakeEngine, type FakeEngineOptions, type RafAudit, type RecordedCall, type WorkerDouble, createWorkerDouble, installRafAudit };
|
package/dist/testing.js
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
export { clusterProbe, resetClusterProbe } from './chunk-
|
|
1
|
+
export { clusterProbe, resetClusterProbe } from './chunk-XAR262L3.js';
|
|
2
|
+
import { handleWorkerRequest } from './chunk-GUXIITKK.js';
|
|
3
|
+
import { EnvelopeSequencer } from './chunk-NIJX5NVJ.js';
|
|
2
4
|
|
|
3
5
|
// src/testing/FakeEngine.ts
|
|
4
6
|
var DEFAULT_CAPABILITIES = {
|
|
5
7
|
linkPicking: false,
|
|
6
8
|
rangeUpdates: [],
|
|
7
9
|
trackedPositions: false,
|
|
8
|
-
simulation: true
|
|
10
|
+
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
|
|
13
|
+
// construction, and those calls apply-then-emit, which is genuinely
|
|
14
|
+
// post-draw.
|
|
15
|
+
idleFrames: "stops",
|
|
16
|
+
postDrawFrames: true
|
|
9
17
|
};
|
|
10
18
|
var CAMERA_METHODS = /* @__PURE__ */ new Set([
|
|
11
19
|
"fitView",
|
|
@@ -92,12 +100,29 @@ var FakeEngine = class {
|
|
|
92
100
|
* read composed state; the newest commit alone is not the whole picture.
|
|
93
101
|
*/
|
|
94
102
|
lastBuffer(channel) {
|
|
103
|
+
let baseIndex = -1;
|
|
104
|
+
let base;
|
|
95
105
|
for (let i = this.commits.length - 1; i >= 0; i--) {
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
106
|
+
const value = this.commits[i].buffers?.[channel];
|
|
107
|
+
if (value !== void 0) {
|
|
108
|
+
baseIndex = i;
|
|
109
|
+
base = value;
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
99
112
|
}
|
|
100
|
-
return void 0;
|
|
113
|
+
if (base === void 0) return void 0;
|
|
114
|
+
let out = base;
|
|
115
|
+
let copied = false;
|
|
116
|
+
for (let i = baseIndex + 1; i < this.commits.length; i++) {
|
|
117
|
+
const patches = this.commits[i].bufferPatches?.[channel];
|
|
118
|
+
if (patches === void 0) continue;
|
|
119
|
+
if (!copied) {
|
|
120
|
+
out = out.slice();
|
|
121
|
+
copied = true;
|
|
122
|
+
}
|
|
123
|
+
for (const patch of patches) out.set(patch.data, patch.start);
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
101
126
|
}
|
|
102
127
|
// --- GraphEngine contract ---
|
|
103
128
|
/** Headless: never touches `container` — a dummy cast works under node. */
|
|
@@ -111,6 +136,26 @@ var FakeEngine = class {
|
|
|
111
136
|
if (this.destroyedFlag) {
|
|
112
137
|
throw new Error("FakeEngine: commit() called after destroy()");
|
|
113
138
|
}
|
|
139
|
+
if (update.bufferPatches !== void 0) {
|
|
140
|
+
for (const [channel, patches] of Object.entries(update.bufferPatches)) {
|
|
141
|
+
if (patches === void 0) continue;
|
|
142
|
+
const ch = channel;
|
|
143
|
+
if (!this.capabilities.rangeUpdates.includes(ch)) {
|
|
144
|
+
throw new Error(`FakeEngine: bufferPatches.${channel} without the declared capability`);
|
|
145
|
+
}
|
|
146
|
+
if (update.buffers?.[ch] !== void 0) {
|
|
147
|
+
throw new Error(
|
|
148
|
+
`FakeEngine: commit carries BOTH buffers.${channel} and bufferPatches.${channel}`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
const seeded = this.commits.some((c) => c.buffers?.[ch] !== void 0);
|
|
152
|
+
if (!seeded) {
|
|
153
|
+
throw new Error(
|
|
154
|
+
`FakeEngine: bufferPatches.${channel} before any full upload seeded the channel`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
114
159
|
this.record("commit", [update]);
|
|
115
160
|
this.commits.push(update);
|
|
116
161
|
if (update.structure !== void 0) this.lastLinks = update.structure.links;
|
|
@@ -379,6 +424,69 @@ var FakeEngine = class {
|
|
|
379
424
|
}
|
|
380
425
|
};
|
|
381
426
|
|
|
382
|
-
|
|
427
|
+
// src/testing/rafAudit.ts
|
|
428
|
+
function installRafAudit(win = globalThis) {
|
|
429
|
+
const target = win;
|
|
430
|
+
const original = target.requestAnimationFrame;
|
|
431
|
+
const native = original !== void 0 ? original.bind(target) : (cb) => setTimeout(() => cb(performance.now()), 16);
|
|
432
|
+
let registrations = 0;
|
|
433
|
+
let ticks = 0;
|
|
434
|
+
let installed = true;
|
|
435
|
+
const wrapper = (callback) => {
|
|
436
|
+
registrations += 1;
|
|
437
|
+
return native((time) => {
|
|
438
|
+
ticks += 1;
|
|
439
|
+
callback(time);
|
|
440
|
+
});
|
|
441
|
+
};
|
|
442
|
+
target.requestAnimationFrame = wrapper;
|
|
443
|
+
return {
|
|
444
|
+
registrations: () => registrations,
|
|
445
|
+
ticks: () => ticks,
|
|
446
|
+
reset: () => {
|
|
447
|
+
registrations = 0;
|
|
448
|
+
ticks = 0;
|
|
449
|
+
},
|
|
450
|
+
uninstall: () => {
|
|
451
|
+
if (!installed) return;
|
|
452
|
+
installed = false;
|
|
453
|
+
if (target.requestAnimationFrame === wrapper) {
|
|
454
|
+
if (original !== void 0) target.requestAnimationFrame = original;
|
|
455
|
+
else Reflect.deleteProperty(target, "requestAnimationFrame");
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// src/testing/workerDouble.ts
|
|
462
|
+
function createWorkerDouble() {
|
|
463
|
+
const sequencer = new EnvelopeSequencer();
|
|
464
|
+
const handled = [];
|
|
465
|
+
let deliver = null;
|
|
466
|
+
return {
|
|
467
|
+
handled,
|
|
468
|
+
post(envelope, transfers) {
|
|
469
|
+
const received = structuredClone(envelope, {
|
|
470
|
+
transfer: [...transfers]
|
|
471
|
+
});
|
|
472
|
+
queueMicrotask(() => {
|
|
473
|
+
handled.push(received);
|
|
474
|
+
const { reply, transfers: replyTransfers } = handleWorkerRequest(received, sequencer);
|
|
475
|
+
const delivered = structuredClone(reply, {
|
|
476
|
+
transfer: [...replyTransfers]
|
|
477
|
+
});
|
|
478
|
+
deliver?.(delivered);
|
|
479
|
+
});
|
|
480
|
+
},
|
|
481
|
+
onReply(cb) {
|
|
482
|
+
deliver = cb;
|
|
483
|
+
},
|
|
484
|
+
terminate() {
|
|
485
|
+
deliver = null;
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
export { FakeEngine, createWorkerDouble, installRafAudit };
|
|
383
491
|
//# sourceMappingURL=testing.js.map
|
|
384
492
|
//# sourceMappingURL=testing.js.map
|
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/testing/FakeEngine.ts"],"names":[],"mappings":"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","file":"testing.js","sourcesContent":["/**\n * FakeEngine — the public headless test seam (§18 /testing, §19).\n *\n * Implements the full GraphEngine adapter contract with zero DOM/WebGL access\n * so consumer suites can run under node/jsdom. Every public method call is\n * recorded to `calls` in order; commits and camera calls get typed convenience\n * views. Test-only helpers (`stepFrame`, `nudgePositions`, `inject*`) let\n * suites observe applied-revision lag, simulate position drift, and drive\n * host events.\n *\n * The state MIRRORS (`pinnedIndices`, `selectedIndices`, `lastStructure`,\n * `lastBuffer`) answer \"what does the engine currently hold?\", which the raw\n * `calls`/`commits` logs cannot: pins, highlight, structure and each buffer\n * channel are committed on independent schedules, so the newest entry is\n * rarely the newest value for a given channel.\n */\n\nimport type {\n EngineCapabilities,\n EngineCommit,\n EngineDiagnostic,\n EngineHostEvents,\n FitViewOptions,\n GraphEngine,\n} from '../engine/index';\nimport type { ViewportState } from '../types';\n\nexport interface FakeEngineOptions {\n /** Overrides merged over the defaults (linkPicking/trackedPositions false, rangeUpdates [], simulation true). */\n capabilities?: Partial<EngineCapabilities>;\n /**\n * When true, commits queue instead of applying immediately; `stepFrame()`\n * applies the oldest queued commit. Lets tests observe the lag between the\n * desired-render revision and `appliedRevision()`.\n */\n manualFrames?: boolean;\n /**\n * When set, `mount()` records the call, does NOT store the host events, and\n * rejects with this error — simulates an engine that cannot initialize.\n */\n mountError?: Error;\n /** Resolved by `captureScreenshot()`; default null (unsupported/not ready). */\n screenshot?: Blob | null;\n}\n\n/** One recorded public method invocation. */\nexport interface RecordedCall {\n method: string;\n args: readonly unknown[];\n}\n\nconst DEFAULT_CAPABILITIES: EngineCapabilities = {\n linkPicking: false,\n rangeUpdates: [],\n trackedPositions: false,\n simulation: true,\n};\n\nconst CAMERA_METHODS: ReadonlySet<string> = new Set([\n 'fitView',\n 'zoom',\n 'setViewport',\n 'zoomToIndex',\n]);\n\n/** Deterministic seed for an unknown (NaN) position: a 10px grid, 100 per row. */\nfunction seedPosition(index: number): readonly [number, number] {\n return [(index % 100) * 10, Math.floor(index / 100) * 10];\n}\n\n/** Standard even-odd ray cast (screen == space in the FakeEngine). */\nfunction pointInPolygon(x: number, y: number, polygon: readonly [number, number][]): boolean {\n let inside = false;\n for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {\n const [xi, yi] = polygon[i]!;\n const [xj, yj] = polygon[j]!;\n const crosses = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;\n if (crosses) inside = !inside;\n }\n return inside;\n}\n\nexport class FakeEngine implements GraphEngine {\n readonly capabilities: EngineCapabilities;\n\n /** Every public method call, in order. */\n readonly calls: RecordedCall[] = [];\n /** Every commit passed to `commit()`, in call order (queued or applied). */\n readonly commits: EngineCommit[] = [];\n /** The fitView/zoom/setViewport/zoomToIndex entries of `calls`, in order. */\n readonly cameraCalls: RecordedCall[] = [];\n\n private readonly manualFrames: boolean;\n private readonly mountError: Error | null;\n private readonly screenshot: Blob | null;\n private events: EngineHostEvents | null = null;\n /** Auto-advancing activity clock for emitFrame()/stepFrame() (ms). */\n private frameClock = 0;\n private destroyedFlag = false;\n private viewport: ViewportState = { x: 0, y: 0, zoom: 1 };\n private applied: number | null = null;\n private positions: Float32Array | null = null;\n /** Links of the most recent structure-bearing commit (commit-time, even in\n * manualFrames mode) — the adjacency source for neighborIndices(). */\n private lastLinks: Uint32Array | null = null;\n private pinned: readonly number[] | null = null;\n private selected: readonly number[] | null = null;\n /** Commits awaiting stepFrame() in manualFrames mode (oldest first). */\n private readonly pendingFrames: EngineCommit[] = [];\n\n constructor(options: FakeEngineOptions = {}) {\n this.capabilities = { ...DEFAULT_CAPABILITIES, ...options.capabilities };\n this.manualFrames = options.manualFrames ?? false;\n this.mountError = options.mountError ?? null;\n this.screenshot = options.screenshot ?? null;\n }\n\n /** Most recent commit, if any. */\n get lastCommit(): EngineCommit | undefined {\n return this.commits[this.commits.length - 1];\n }\n\n get destroyed(): boolean {\n return this.destroyedFlag;\n }\n\n /** Last setPinnedIndices() value (null = unpinned / never pinned). */\n get pinnedIndices(): readonly number[] | null {\n return this.pinned;\n }\n\n /** Last setSelectedIndices() value (null = cleared / never selected) — the\n * §9.1 highlight mirror of {@link pinnedIndices}. */\n get selectedIndices(): readonly number[] | null {\n return this.selected;\n }\n\n /**\n * Structure of the most recent structure-bearing commit — the geometry the\n * engine currently holds. Buffer channels commit independently of structure\n * (§9.1 mask fast path), so the newest commit often carries none.\n */\n get lastStructure(): NonNullable<EngineCommit['structure']> | undefined {\n for (let i = this.commits.length - 1; i >= 0; i--) {\n const structure = this.commits[i]!.structure;\n if (structure !== undefined) return structure;\n }\n return undefined;\n }\n\n /**\n * Most recently committed value of ONE buffer channel — the engine's current\n * content for it. Channels are committed independently (a mask drain may\n * carry `pointColor` alone), so scanning back per channel is the only way to\n * read composed state; the newest commit alone is not the whole picture.\n */\n lastBuffer(channel: keyof NonNullable<EngineCommit['buffers']>): Float32Array | undefined {\n for (let i = this.commits.length - 1; i >= 0; i--) {\n const buffers = this.commits[i]!.buffers;\n const value = buffers?.[channel];\n if (value !== undefined) return value;\n }\n return undefined;\n }\n\n // --- GraphEngine contract ---\n\n /** Headless: never touches `container` — a dummy cast works under node. */\n mount(container: HTMLElement, events: EngineHostEvents): Promise<void> {\n this.record('mount', [container, events]);\n if (this.mountError !== null) return Promise.reject(this.mountError);\n this.events = events;\n return Promise.resolve();\n }\n\n commit(update: EngineCommit): void {\n if (this.destroyedFlag) {\n throw new Error('FakeEngine: commit() called after destroy()');\n }\n this.record('commit', [update]);\n this.commits.push(update);\n if (update.structure !== undefined) this.lastLinks = update.structure.links;\n if (this.manualFrames) {\n this.pendingFrames.push(update);\n } else {\n this.apply(update);\n }\n }\n\n appliedRevision(): number | null {\n this.record('appliedRevision', []);\n return this.applied;\n }\n\n fitView(opts?: FitViewOptions): void {\n this.record('fitView', [opts]);\n }\n\n zoom(factor: number, durationMs?: number): void {\n this.record('zoom', [factor, durationMs]);\n }\n\n setViewport(v: Partial<ViewportState>, opts?: { durationMs?: number }): void {\n this.record('setViewport', [v, opts]);\n this.viewport = { ...this.viewport, ...v };\n }\n\n getViewport(): ViewportState | null {\n this.record('getViewport', []);\n return { ...this.viewport };\n }\n\n zoomToIndex(index: number, durationMs?: number): void {\n this.record('zoomToIndex', [index, durationMs]);\n }\n\n start(alpha?: number): void {\n this.record('start', [alpha]);\n }\n\n pause(): void {\n this.record('pause', []);\n }\n\n setSelectedIndices(indices: readonly number[] | null): void {\n this.record('setSelectedIndices', [indices]);\n this.selected = indices === null ? null : [...indices];\n }\n\n setFocusedIndex(index: number | null): void {\n this.record('setFocusedIndex', [index]);\n }\n\n /** Screen == space in the FakeEngine: ray-cast over current positions,\n * skipping NaN pairs (not-yet-applied structure in manualFrames mode). */\n pointsInPolygon(screenPolygon: readonly [number, number][]): number[] {\n this.record('pointsInPolygon', [screenPolygon]);\n const pos = this.positions;\n const out: number[] = [];\n if (pos === null || screenPolygon.length < 3) return out;\n for (let i = 0; i < pos.length / 2; i++) {\n const x = pos[2 * i]!;\n const y = pos[2 * i + 1]!;\n if (Number.isNaN(x) || Number.isNaN(y)) continue;\n if (pointInPolygon(x, y, screenPolygon)) out.push(i);\n }\n return out;\n }\n\n /** Screen == space: point indices inside `[x0, y0, x1, y1]` (bounds\n * normalized), skipping NaN pairs (not-yet-applied structure). */\n pointsInRect(screenRect: readonly [number, number, number, number]): number[] {\n this.record('pointsInRect', [screenRect]);\n const pos = this.positions;\n const out: number[] = [];\n if (pos === null) return out;\n const minX = Math.min(screenRect[0], screenRect[2]);\n const maxX = Math.max(screenRect[0], screenRect[2]);\n const minY = Math.min(screenRect[1], screenRect[3]);\n const maxY = Math.max(screenRect[1], screenRect[3]);\n for (let i = 0; i < pos.length / 2; i++) {\n const x = pos[2 * i]!;\n const y = pos[2 * i + 1]!;\n if (Number.isNaN(x) || Number.isNaN(y)) continue;\n if (x >= minX && x <= maxX && y >= minY && y <= maxY) out.push(i);\n }\n return out;\n }\n\n /** Resolves the constructor's `screenshot` option (null by default). */\n captureScreenshot(): Promise<Blob | null> {\n this.record('captureScreenshot', []);\n return Promise.resolve(this.screenshot);\n }\n\n /** 1-hop adjacency from the last committed links (self-loops excluded). */\n neighborIndices(index: number): number[] {\n this.record('neighborIndices', [index]);\n const links = this.lastLinks;\n if (links === null) return [];\n const out = new Set<number>();\n for (let k = 0; k < links.length; k += 2) {\n const s = links[k]!;\n const t = links[k + 1]!;\n if (s === index && t !== index) out.add(t);\n if (t === index && s !== index) out.add(s);\n }\n return [...out];\n }\n\n /** Identity transform: FakeEngine screen coordinates ARE space coordinates. */\n screenToSpace(p: readonly [number, number]): [number, number] | null {\n this.record('screenToSpace', [p]);\n return [p[0], p[1]];\n }\n\n /** Identity transform: FakeEngine screen coordinates ARE space coordinates. */\n spaceToScreen(p: readonly [number, number]): [number, number] | null {\n this.record('spaceToScreen', [p]);\n return [p[0], p[1]];\n }\n\n setPinnedIndices(indices: readonly number[] | null): void {\n this.record('setPinnedIndices', [indices]);\n this.pinned = indices === null ? null : [...indices];\n }\n\n getPositions(): Float32Array | null {\n this.record('getPositions', []);\n return this.positions === null ? null : new Float32Array(this.positions);\n }\n\n destroy(): void {\n this.record('destroy', []);\n this.destroyedFlag = true;\n this.events = null;\n }\n\n // --- test helpers (not part of GraphEngine) ---\n\n /**\n * manualFrames mode: apply the oldest queued commit, making its revision\n * visible via appliedRevision(). No-op on the queue when empty. A drawn\n * frame is also an activity-clock sample, so a mounted engine gets an\n * `onFrame` tick (§13 overlay/activity clock).\n */\n stepFrame(): void {\n this.record('stepFrame', []);\n const next = this.pendingFrames.shift();\n if (next !== undefined) this.apply(next);\n if (this.events !== null) {\n this.frameClock += 16;\n this.events.onFrame?.(this.frameClock);\n }\n }\n\n /**\n * Drive the host `onFrame` activity clock. `timeMs` sets the clock\n * explicitly; omitted, the clock auto-advances by 16ms per call.\n */\n emitFrame(timeMs?: number): void {\n const events = this.requireMounted('emitFrame');\n this.frameClock = timeMs ?? this.frameClock + 16;\n this.record('emitFrame', [this.frameClock]);\n events.onFrame?.(this.frameClock);\n }\n\n /** Simulate simulation drift: shift every current position by (dx, dy). */\n nudgePositions(dx: number, dy: number): void {\n this.record('nudgePositions', [dx, dy]);\n const pos = this.positions;\n if (pos === null) return;\n for (let i = 0; i < pos.length; i += 2) {\n pos[i] = pos[i]! + dx;\n pos[i + 1] = pos[i + 1]! + dy;\n }\n }\n\n injectPointClick(index: number | null, modifiers?: { metaKey: boolean; shiftKey: boolean }): void {\n const events = this.requireMounted('injectPointClick');\n this.record('injectPointClick', [index, modifiers]);\n events.onPointClick?.(index, modifiers);\n }\n\n injectPointHover(index: number | null): void {\n const events = this.requireMounted('injectPointHover');\n this.record('injectPointHover', [index]);\n events.onPointHover?.(index);\n }\n\n injectLinkClick(linkIndex: number): void {\n const events = this.requireMounted('injectLinkClick');\n this.record('injectLinkClick', [linkIndex]);\n events.onLinkClick?.(linkIndex);\n }\n\n injectLinkHover(linkIndex: number | null): void {\n const events = this.requireMounted('injectLinkHover');\n this.record('injectLinkHover', [linkIndex]);\n events.onLinkHover?.(linkIndex);\n }\n\n injectDragStart(index: number): void {\n const events = this.requireMounted('injectDragStart');\n this.record('injectDragStart', [index]);\n events.onDragStart?.(index);\n }\n\n injectDragEnd(index: number, x: number, y: number): void {\n const events = this.requireMounted('injectDragEnd');\n this.record('injectDragEnd', [index, x, y]);\n events.onDragEnd?.(index, x, y);\n }\n\n injectContextMenu(index: number | null, screen: readonly [number, number]): void {\n const events = this.requireMounted('injectContextMenu');\n this.record('injectContextMenu', [index, screen]);\n events.onContextMenu?.(index, screen);\n }\n\n injectViewportChange(v: ViewportState): void {\n const events = this.requireMounted('injectViewportChange');\n this.record('injectViewportChange', [v]);\n this.viewport = { ...v };\n events.onViewportChange?.(v);\n }\n\n injectSimulationEnd(): void {\n const events = this.requireMounted('injectSimulationEnd');\n this.record('injectSimulationEnd', []);\n events.onSimulationEnd?.();\n }\n\n injectError(error: Error): void {\n const events = this.requireMounted('injectError');\n this.record('injectError', [error]);\n events.onError?.(error);\n }\n\n injectContextLost(): void {\n const events = this.requireMounted('injectContextLost');\n this.record('injectContextLost', []);\n events.onContextEvent?.({ type: 'lost' });\n }\n\n injectContextRestored(): void {\n const events = this.requireMounted('injectContextRestored');\n this.record('injectContextRestored', []);\n events.onContextEvent?.({ type: 'restored' });\n }\n\n injectContextFailed(error: Error): void {\n const events = this.requireMounted('injectContextFailed');\n this.record('injectContextFailed', [error]);\n events.onContextEvent?.({ type: 'failed', error });\n }\n\n injectEngineDiagnostic(d: EngineDiagnostic): void {\n const events = this.requireMounted('injectEngineDiagnostic');\n this.record('injectEngineDiagnostic', [d]);\n events.onDiagnostic?.(d);\n }\n\n // --- internals ---\n\n private record(method: string, args: unknown[]): void {\n // Trim trailing omitted optionals so `args` matches the caller's shape.\n while (args.length > 0 && args[args.length - 1] === undefined) args.pop();\n const call: RecordedCall = { method, args };\n this.calls.push(call);\n if (CAMERA_METHODS.has(method)) this.cameraCalls.push(call);\n }\n\n private requireMounted(helper: string): EngineHostEvents {\n const events = this.events;\n if (events === null) {\n throw new Error(`FakeEngine: ${helper}() requires a mounted engine — call mount() first`);\n }\n return events;\n }\n\n /**\n * Make a commit visible: rebuild positions when structure is present (NaN\n * pairs seeded deterministically, known coordinates kept verbatim) and\n * advance the applied revision.\n */\n private apply(update: EngineCommit): void {\n const structure = update.structure;\n if (structure !== undefined) {\n const { pointCount, positions } = structure;\n const next = new Float32Array(2 * pointCount);\n for (let i = 0; i < pointCount; i++) {\n const x = positions[2 * i]!;\n const y = positions[2 * i + 1]!;\n if (Number.isNaN(x) || Number.isNaN(y)) {\n const [sx, sy] = seedPosition(i);\n next[2 * i] = sx;\n next[2 * i + 1] = sy;\n } else {\n next[2 * i] = x;\n next[2 * i + 1] = y;\n }\n }\n this.positions = next;\n }\n this.applied = update.revision;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/testing/FakeEngine.ts","../src/testing/rafAudit.ts","../src/testing/workerDouble.ts"],"names":[],"mappings":"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","file":"testing.js","sourcesContent":["/**\n * FakeEngine — the public headless test seam (§18 /testing, §19).\n *\n * Implements the full GraphEngine adapter contract with zero DOM/WebGL access\n * so consumer suites can run under node/jsdom. Every public method call is\n * recorded to `calls` in order; commits and camera calls get typed convenience\n * views. Test-only helpers (`stepFrame`, `nudgePositions`, `inject*`) let\n * suites observe applied-revision lag, simulate position drift, and drive\n * host events.\n *\n * The state MIRRORS (`pinnedIndices`, `selectedIndices`, `lastStructure`,\n * `lastBuffer`) answer \"what does the engine currently hold?\", which the raw\n * `calls`/`commits` logs cannot: pins, highlight, structure and each buffer\n * channel are committed on independent schedules, so the newest entry is\n * rarely the newest value for a given channel.\n */\n\nimport type {\n EngineCapabilities,\n EngineCommit,\n EngineDiagnostic,\n EngineHostEvents,\n FitViewOptions,\n GraphEngine,\n} from '../engine/index';\nimport type { ViewportState } from '../types';\n\nexport interface FakeEngineOptions {\n /** Overrides merged over the defaults (linkPicking/trackedPositions false, rangeUpdates [], simulation true). */\n capabilities?: Partial<EngineCapabilities>;\n /**\n * When true, commits queue instead of applying immediately; `stepFrame()`\n * applies the oldest queued commit. Lets tests observe the lag between the\n * desired-render revision and `appliedRevision()`.\n */\n manualFrames?: boolean;\n /**\n * When set, `mount()` records the call, does NOT store the host events, and\n * rejects with this error — simulates an engine that cannot initialize.\n */\n mountError?: Error;\n /** Resolved by `captureScreenshot()`; default null (unsupported/not ready). */\n screenshot?: Blob | null;\n}\n\n/** One recorded public method invocation. */\nexport interface RecordedCall {\n method: string;\n args: readonly unknown[];\n}\n\nconst DEFAULT_CAPABILITIES: EngineCapabilities = {\n linkPicking: false,\n rangeUpdates: [],\n trackedPositions: false,\n simulation: true,\n // §17: the FakeEngine schedules nothing on its own — frames exist only\n // when a test calls stepFrame()/emitFrame() — so it is quiescent by\n // construction, and those calls apply-then-emit, which is genuinely\n // post-draw.\n idleFrames: 'stops',\n postDrawFrames: true,\n};\n\nconst CAMERA_METHODS: ReadonlySet<string> = new Set([\n 'fitView',\n 'zoom',\n 'setViewport',\n 'zoomToIndex',\n]);\n\n/** Deterministic seed for an unknown (NaN) position: a 10px grid, 100 per row. */\nfunction seedPosition(index: number): readonly [number, number] {\n return [(index % 100) * 10, Math.floor(index / 100) * 10];\n}\n\n/** Standard even-odd ray cast (screen == space in the FakeEngine). */\nfunction pointInPolygon(x: number, y: number, polygon: readonly [number, number][]): boolean {\n let inside = false;\n for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {\n const [xi, yi] = polygon[i]!;\n const [xj, yj] = polygon[j]!;\n const crosses = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;\n if (crosses) inside = !inside;\n }\n return inside;\n}\n\nexport class FakeEngine implements GraphEngine {\n readonly capabilities: EngineCapabilities;\n\n /** Every public method call, in order. */\n readonly calls: RecordedCall[] = [];\n /** Every commit passed to `commit()`, in call order (queued or applied). */\n readonly commits: EngineCommit[] = [];\n /** The fitView/zoom/setViewport/zoomToIndex entries of `calls`, in order. */\n readonly cameraCalls: RecordedCall[] = [];\n\n private readonly manualFrames: boolean;\n private readonly mountError: Error | null;\n private readonly screenshot: Blob | null;\n private events: EngineHostEvents | null = null;\n /** Auto-advancing activity clock for emitFrame()/stepFrame() (ms). */\n private frameClock = 0;\n private destroyedFlag = false;\n private viewport: ViewportState = { x: 0, y: 0, zoom: 1 };\n private applied: number | null = null;\n private positions: Float32Array | null = null;\n /** Links of the most recent structure-bearing commit (commit-time, even in\n * manualFrames mode) — the adjacency source for neighborIndices(). */\n private lastLinks: Uint32Array | null = null;\n private pinned: readonly number[] | null = null;\n private selected: readonly number[] | null = null;\n /** Commits awaiting stepFrame() in manualFrames mode (oldest first). */\n private readonly pendingFrames: EngineCommit[] = [];\n\n constructor(options: FakeEngineOptions = {}) {\n this.capabilities = { ...DEFAULT_CAPABILITIES, ...options.capabilities };\n this.manualFrames = options.manualFrames ?? false;\n this.mountError = options.mountError ?? null;\n this.screenshot = options.screenshot ?? null;\n }\n\n /** Most recent commit, if any. */\n get lastCommit(): EngineCommit | undefined {\n return this.commits[this.commits.length - 1];\n }\n\n get destroyed(): boolean {\n return this.destroyedFlag;\n }\n\n /** Last setPinnedIndices() value (null = unpinned / never pinned). */\n get pinnedIndices(): readonly number[] | null {\n return this.pinned;\n }\n\n /** Last setSelectedIndices() value (null = cleared / never selected) — the\n * §9.1 highlight mirror of {@link pinnedIndices}. */\n get selectedIndices(): readonly number[] | null {\n return this.selected;\n }\n\n /**\n * Structure of the most recent structure-bearing commit — the geometry the\n * engine currently holds. Buffer channels commit independently of structure\n * (§9.1 mask fast path), so the newest commit often carries none.\n */\n get lastStructure(): NonNullable<EngineCommit['structure']> | undefined {\n for (let i = this.commits.length - 1; i >= 0; i--) {\n const structure = this.commits[i]!.structure;\n if (structure !== undefined) return structure;\n }\n return undefined;\n }\n\n /**\n * Most recently committed value of ONE buffer channel — the engine's current\n * content for it. Channels are committed independently (a mask drain may\n * carry `pointColor` alone), so scanning back per channel is the only way to\n * read composed state; the newest commit alone is not the whole picture.\n */\n lastBuffer(channel: keyof NonNullable<EngineCommit['buffers']>): Float32Array | undefined {\n // Newest FULL upload seeds the channel; any LATER ranged patches replay\n // over a copy in commit order (S13-T04) — the return is what the engine\n // currently holds either way.\n let baseIndex = -1;\n let base: Float32Array | undefined;\n for (let i = this.commits.length - 1; i >= 0; i--) {\n const value = this.commits[i]!.buffers?.[channel];\n if (value !== undefined) {\n baseIndex = i;\n base = value;\n break;\n }\n }\n if (base === undefined) return undefined;\n let out = base;\n let copied = false;\n for (let i = baseIndex + 1; i < this.commits.length; i++) {\n const patches = this.commits[i]!.bufferPatches?.[channel];\n if (patches === undefined) continue;\n if (!copied) {\n out = out.slice();\n copied = true;\n }\n for (const patch of patches) out.set(patch.data, patch.start);\n }\n return out;\n }\n\n // --- GraphEngine contract ---\n\n /** Headless: never touches `container` — a dummy cast works under node. */\n mount(container: HTMLElement, events: EngineHostEvents): Promise<void> {\n this.record('mount', [container, events]);\n if (this.mountError !== null) return Promise.reject(this.mountError);\n this.events = events;\n return Promise.resolve();\n }\n\n commit(update: EngineCommit): void {\n if (this.destroyedFlag) {\n throw new Error('FakeEngine: commit() called after destroy()');\n }\n // S13-T04 tripwires: ranged patches are legal only on DECLARED channels,\n // never alongside a full buffer for the same channel in one commit, and\n // only after a full upload seeded the channel. Each is a producer bug —\n // fail the test loudly instead of rendering stale state silently.\n if (update.bufferPatches !== undefined) {\n for (const [channel, patches] of Object.entries(update.bufferPatches)) {\n if (patches === undefined) continue;\n const ch = channel as keyof NonNullable<EngineCommit['buffers']>;\n if (!this.capabilities.rangeUpdates.includes(ch)) {\n throw new Error(`FakeEngine: bufferPatches.${channel} without the declared capability`);\n }\n if (update.buffers?.[ch] !== undefined) {\n throw new Error(\n `FakeEngine: commit carries BOTH buffers.${channel} and bufferPatches.${channel}`,\n );\n }\n const seeded = this.commits.some((c) => c.buffers?.[ch] !== undefined);\n if (!seeded) {\n throw new Error(\n `FakeEngine: bufferPatches.${channel} before any full upload seeded the channel`,\n );\n }\n }\n }\n this.record('commit', [update]);\n this.commits.push(update);\n if (update.structure !== undefined) this.lastLinks = update.structure.links;\n if (this.manualFrames) {\n this.pendingFrames.push(update);\n } else {\n this.apply(update);\n }\n }\n\n appliedRevision(): number | null {\n this.record('appliedRevision', []);\n return this.applied;\n }\n\n fitView(opts?: FitViewOptions): void {\n this.record('fitView', [opts]);\n }\n\n zoom(factor: number, durationMs?: number): void {\n this.record('zoom', [factor, durationMs]);\n }\n\n setViewport(v: Partial<ViewportState>, opts?: { durationMs?: number }): void {\n this.record('setViewport', [v, opts]);\n this.viewport = { ...this.viewport, ...v };\n }\n\n getViewport(): ViewportState | null {\n this.record('getViewport', []);\n return { ...this.viewport };\n }\n\n zoomToIndex(index: number, durationMs?: number): void {\n this.record('zoomToIndex', [index, durationMs]);\n }\n\n start(alpha?: number): void {\n this.record('start', [alpha]);\n }\n\n pause(): void {\n this.record('pause', []);\n }\n\n setSelectedIndices(indices: readonly number[] | null): void {\n this.record('setSelectedIndices', [indices]);\n this.selected = indices === null ? null : [...indices];\n }\n\n setFocusedIndex(index: number | null): void {\n this.record('setFocusedIndex', [index]);\n }\n\n /** Screen == space in the FakeEngine: ray-cast over current positions,\n * skipping NaN pairs (not-yet-applied structure in manualFrames mode). */\n pointsInPolygon(screenPolygon: readonly [number, number][]): number[] {\n this.record('pointsInPolygon', [screenPolygon]);\n const pos = this.positions;\n const out: number[] = [];\n if (pos === null || screenPolygon.length < 3) return out;\n for (let i = 0; i < pos.length / 2; i++) {\n const x = pos[2 * i]!;\n const y = pos[2 * i + 1]!;\n if (Number.isNaN(x) || Number.isNaN(y)) continue;\n if (pointInPolygon(x, y, screenPolygon)) out.push(i);\n }\n return out;\n }\n\n /** Screen == space: point indices inside `[x0, y0, x1, y1]` (bounds\n * normalized), skipping NaN pairs (not-yet-applied structure). */\n pointsInRect(screenRect: readonly [number, number, number, number]): number[] {\n this.record('pointsInRect', [screenRect]);\n const pos = this.positions;\n const out: number[] = [];\n if (pos === null) return out;\n const minX = Math.min(screenRect[0], screenRect[2]);\n const maxX = Math.max(screenRect[0], screenRect[2]);\n const minY = Math.min(screenRect[1], screenRect[3]);\n const maxY = Math.max(screenRect[1], screenRect[3]);\n for (let i = 0; i < pos.length / 2; i++) {\n const x = pos[2 * i]!;\n const y = pos[2 * i + 1]!;\n if (Number.isNaN(x) || Number.isNaN(y)) continue;\n if (x >= minX && x <= maxX && y >= minY && y <= maxY) out.push(i);\n }\n return out;\n }\n\n /** Resolves the constructor's `screenshot` option (null by default). */\n captureScreenshot(): Promise<Blob | null> {\n this.record('captureScreenshot', []);\n return Promise.resolve(this.screenshot);\n }\n\n /** 1-hop adjacency from the last committed links (self-loops excluded). */\n neighborIndices(index: number): number[] {\n this.record('neighborIndices', [index]);\n const links = this.lastLinks;\n if (links === null) return [];\n const out = new Set<number>();\n for (let k = 0; k < links.length; k += 2) {\n const s = links[k]!;\n const t = links[k + 1]!;\n if (s === index && t !== index) out.add(t);\n if (t === index && s !== index) out.add(s);\n }\n return [...out];\n }\n\n /** Identity transform: FakeEngine screen coordinates ARE space coordinates. */\n screenToSpace(p: readonly [number, number]): [number, number] | null {\n this.record('screenToSpace', [p]);\n return [p[0], p[1]];\n }\n\n /** Identity transform: FakeEngine screen coordinates ARE space coordinates. */\n spaceToScreen(p: readonly [number, number]): [number, number] | null {\n this.record('spaceToScreen', [p]);\n return [p[0], p[1]];\n }\n\n setPinnedIndices(indices: readonly number[] | null): void {\n this.record('setPinnedIndices', [indices]);\n this.pinned = indices === null ? null : [...indices];\n }\n\n getPositions(): Float32Array | null {\n this.record('getPositions', []);\n return this.positions === null ? null : new Float32Array(this.positions);\n }\n\n destroy(): void {\n this.record('destroy', []);\n this.destroyedFlag = true;\n this.events = null;\n }\n\n // --- test helpers (not part of GraphEngine) ---\n\n /**\n * manualFrames mode: apply the oldest queued commit, making its revision\n * visible via appliedRevision(). No-op on the queue when empty. A drawn\n * frame is also an activity-clock sample, so a mounted engine gets an\n * `onFrame` tick (§13 overlay/activity clock).\n */\n stepFrame(): void {\n this.record('stepFrame', []);\n const next = this.pendingFrames.shift();\n if (next !== undefined) this.apply(next);\n if (this.events !== null) {\n this.frameClock += 16;\n this.events.onFrame?.(this.frameClock);\n }\n }\n\n /**\n * Drive the host `onFrame` activity clock. `timeMs` sets the clock\n * explicitly; omitted, the clock auto-advances by 16ms per call.\n */\n emitFrame(timeMs?: number): void {\n const events = this.requireMounted('emitFrame');\n this.frameClock = timeMs ?? this.frameClock + 16;\n this.record('emitFrame', [this.frameClock]);\n events.onFrame?.(this.frameClock);\n }\n\n /** Simulate simulation drift: shift every current position by (dx, dy). */\n nudgePositions(dx: number, dy: number): void {\n this.record('nudgePositions', [dx, dy]);\n const pos = this.positions;\n if (pos === null) return;\n for (let i = 0; i < pos.length; i += 2) {\n pos[i] = pos[i]! + dx;\n pos[i + 1] = pos[i + 1]! + dy;\n }\n }\n\n injectPointClick(index: number | null, modifiers?: { metaKey: boolean; shiftKey: boolean }): void {\n const events = this.requireMounted('injectPointClick');\n this.record('injectPointClick', [index, modifiers]);\n events.onPointClick?.(index, modifiers);\n }\n\n injectPointHover(index: number | null): void {\n const events = this.requireMounted('injectPointHover');\n this.record('injectPointHover', [index]);\n events.onPointHover?.(index);\n }\n\n injectLinkClick(linkIndex: number): void {\n const events = this.requireMounted('injectLinkClick');\n this.record('injectLinkClick', [linkIndex]);\n events.onLinkClick?.(linkIndex);\n }\n\n injectLinkHover(linkIndex: number | null): void {\n const events = this.requireMounted('injectLinkHover');\n this.record('injectLinkHover', [linkIndex]);\n events.onLinkHover?.(linkIndex);\n }\n\n injectDragStart(index: number): void {\n const events = this.requireMounted('injectDragStart');\n this.record('injectDragStart', [index]);\n events.onDragStart?.(index);\n }\n\n injectDragEnd(index: number, x: number, y: number): void {\n const events = this.requireMounted('injectDragEnd');\n this.record('injectDragEnd', [index, x, y]);\n events.onDragEnd?.(index, x, y);\n }\n\n injectContextMenu(index: number | null, screen: readonly [number, number]): void {\n const events = this.requireMounted('injectContextMenu');\n this.record('injectContextMenu', [index, screen]);\n events.onContextMenu?.(index, screen);\n }\n\n injectViewportChange(v: ViewportState): void {\n const events = this.requireMounted('injectViewportChange');\n this.record('injectViewportChange', [v]);\n this.viewport = { ...v };\n events.onViewportChange?.(v);\n }\n\n injectSimulationEnd(): void {\n const events = this.requireMounted('injectSimulationEnd');\n this.record('injectSimulationEnd', []);\n events.onSimulationEnd?.();\n }\n\n injectError(error: Error): void {\n const events = this.requireMounted('injectError');\n this.record('injectError', [error]);\n events.onError?.(error);\n }\n\n injectContextLost(): void {\n const events = this.requireMounted('injectContextLost');\n this.record('injectContextLost', []);\n events.onContextEvent?.({ type: 'lost' });\n }\n\n injectContextRestored(): void {\n const events = this.requireMounted('injectContextRestored');\n this.record('injectContextRestored', []);\n events.onContextEvent?.({ type: 'restored' });\n }\n\n injectContextFailed(error: Error): void {\n const events = this.requireMounted('injectContextFailed');\n this.record('injectContextFailed', [error]);\n events.onContextEvent?.({ type: 'failed', error });\n }\n\n injectEngineDiagnostic(d: EngineDiagnostic): void {\n const events = this.requireMounted('injectEngineDiagnostic');\n this.record('injectEngineDiagnostic', [d]);\n events.onDiagnostic?.(d);\n }\n\n // --- internals ---\n\n private record(method: string, args: unknown[]): void {\n // Trim trailing omitted optionals so `args` matches the caller's shape.\n while (args.length > 0 && args[args.length - 1] === undefined) args.pop();\n const call: RecordedCall = { method, args };\n this.calls.push(call);\n if (CAMERA_METHODS.has(method)) this.cameraCalls.push(call);\n }\n\n private requireMounted(helper: string): EngineHostEvents {\n const events = this.events;\n if (events === null) {\n throw new Error(`FakeEngine: ${helper}() requires a mounted engine — call mount() first`);\n }\n return events;\n }\n\n /**\n * Make a commit visible: rebuild positions when structure is present (NaN\n * pairs seeded deterministically, known coordinates kept verbatim) and\n * advance the applied revision.\n */\n private apply(update: EngineCommit): void {\n const structure = update.structure;\n if (structure !== undefined) {\n const { pointCount, positions } = structure;\n const next = new Float32Array(2 * pointCount);\n for (let i = 0; i < pointCount; i++) {\n const x = positions[2 * i]!;\n const y = positions[2 * i + 1]!;\n if (Number.isNaN(x) || Number.isNaN(y)) {\n const [sx, sy] = seedPosition(i);\n next[2 * i] = sx;\n next[2 * i + 1] = sy;\n } else {\n next[2 * i] = x;\n next[2 * i + 1] = y;\n }\n }\n this.positions = next;\n }\n this.applied = update.revision;\n }\n}\n","/**\n * §17/S13-T06 requestAnimationFrame audit — the quiescence instrument.\n *\n * Wraps a window's `requestAnimationFrame` and counts SCHEDULING calls\n * (registrations) separately from delivered ticks: the stop-at-rest claim is\n * about registrations — a truly stopped loop schedules nothing at all, while\n * tick counts alone can read zero under fake timers even though a loop is\n * still re-arming itself.\n *\n * Used three ways: core/react quiescence tests (install, drive the instance,\n * assert `registrations() === 0` over a settled window), the demo e2e's\n * page-side counterpart (same shape, injected via addInitScript), and the\n * dev-mode runtime assertion in the instance. The M0 spike's instrument.ts\n * pioneered the wrapper; this is its reusable, uninstallable form.\n */\n\nexport interface RafAudit {\n /** requestAnimationFrame CALLS observed since install (scheduling). */\n registrations(): number;\n /** Callbacks actually delivered since install (ticks). */\n ticks(): number;\n /** Zero both counters without uninstalling. */\n reset(): void;\n /** Restore the original requestAnimationFrame. Idempotent. */\n uninstall(): void;\n}\n\n/**\n * Install the audit on `win` (default: globalThis). Under jsdom + vitest\n * fake timers, rAF is timer-backed, so `vi.advanceTimersByTime` drives\n * deliveries — registrations count regardless. In a rAF-less environment\n * (plain-Node core tests) a 16ms setTimeout shim delivers: without the\n * audit, any rAF call there would simply crash — with it, the call is\n * COUNTED, which is exactly the evidence a quiescence test wants.\n */\nexport function installRafAudit(win: Window = globalThis as unknown as Window): RafAudit {\n const target = win as Window & {\n requestAnimationFrame?: typeof requestAnimationFrame;\n };\n const original = target.requestAnimationFrame;\n const native: typeof requestAnimationFrame =\n original !== undefined\n ? original.bind(target)\n : (cb) => setTimeout(() => cb(performance.now()), 16) as unknown as number;\n let registrations = 0;\n let ticks = 0;\n let installed = true;\n\n const wrapper = (callback: FrameRequestCallback): number => {\n registrations += 1;\n return native((time) => {\n ticks += 1;\n callback(time);\n });\n };\n target.requestAnimationFrame = wrapper;\n\n return {\n registrations: () => registrations,\n ticks: () => ticks,\n reset: () => {\n registrations = 0;\n ticks = 0;\n },\n uninstall: () => {\n if (!installed) return;\n installed = false;\n // Only restore if nobody else re-wrapped after us — otherwise leave\n // the newer wrapper in place and simply stop counting (native is\n // already bound into it).\n if (target.requestAnimationFrame === wrapper) {\n if (original !== undefined) target.requestAnimationFrame = original;\n else Reflect.deleteProperty(target, 'requestAnimationFrame');\n }\n },\n };\n}\n","/**\n * In-process worker transport double (ADR-006 D2/D5) — drives the REAL\n * runtime module through the REAL codec, asynchronously (microtask), with\n * REAL transfer semantics: payloads round through `structuredClone` with\n * the declared transfer list, so a wrong transfer set detaches something\n * the sender still needs and fails the suite loudly — exactly what a live\n * thread would do, minus the scheduling.\n */\n\nimport { EnvelopeSequencer } from '../workerProtocol';\nimport type { WorkerEnvelope } from '../workerProtocol';\nimport { handleWorkerRequest } from '../worker/runtime';\nimport type { WorkerTransport } from '../worker/lane';\n\nexport interface WorkerDouble extends WorkerTransport {\n /** Requests handled so far (post-clone shapes, in arrival order). */\n readonly handled: WorkerEnvelope[];\n}\n\nexport function createWorkerDouble(): WorkerDouble {\n const sequencer = new EnvelopeSequencer();\n const handled: WorkerEnvelope[] = [];\n let deliver: ((reply: WorkerEnvelope) => void) | null = null;\n return {\n handled,\n post(envelope, transfers) {\n // REAL transfer semantics: the sender's views detach here.\n const received = structuredClone(envelope, {\n transfer: [...transfers],\n }) as WorkerEnvelope;\n queueMicrotask(() => {\n handled.push(received);\n const { reply, transfers: replyTransfers } = handleWorkerRequest(received, sequencer);\n const delivered = structuredClone(reply, {\n transfer: [...replyTransfers],\n }) as WorkerEnvelope;\n deliver?.(delivered);\n });\n },\n onReply(cb) {\n deliver = cb;\n },\n terminate() {\n deliver = null;\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { handleWorkerRequest } from '../chunk-GUXIITKK.js';
|
|
2
|
+
import { EnvelopeSequencer } from '../chunk-NIJX5NVJ.js';
|
|
3
|
+
|
|
4
|
+
// src/worker/entry.ts
|
|
5
|
+
var sequencer = new EnvelopeSequencer();
|
|
6
|
+
var scope = self;
|
|
7
|
+
scope.onmessage = (ev) => {
|
|
8
|
+
const { reply, transfers } = handleWorkerRequest(ev.data, sequencer);
|
|
9
|
+
scope.postMessage(reply, [...transfers]);
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=entry.js.map
|
|
12
|
+
//# sourceMappingURL=entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/worker/entry.ts"],"names":[],"mappings":";;;;AAUA,IAAM,SAAA,GAAY,IAAI,iBAAA,EAAkB;AACxC,IAAM,KAAA,GAAQ,IAAA;AAKd,KAAA,CAAM,SAAA,GAAY,CAAC,EAAA,KAAqB;AACtC,EAAA,MAAM,EAAE,KAAA,EAAO,SAAA,KAAc,mBAAA,CAAoB,EAAA,CAAG,MAAM,SAAS,CAAA;AACnE,EAAA,KAAA,CAAM,WAAA,CAAY,KAAA,EAAO,CAAC,GAAG,SAAS,CAAC,CAAA;AACzC,CAAA","file":"entry.js","sourcesContent":["/**\n * ADR-006 D5/D6 — the inline worker entry, built as its own asset inside\n * core (dist/worker/entry.js). All semantics live in runtime.ts (shared\n * with the in-process double — D2's one-implementation rule); this file is\n * ONLY the thread glue.\n */\n\nimport { EnvelopeSequencer } from '../workerProtocol';\nimport { handleWorkerRequest } from './runtime';\n\nconst sequencer = new EnvelopeSequencer();\nconst scope = self as unknown as {\n onmessage: ((ev: MessageEvent) => void) | null;\n postMessage: (message: unknown, transfer: Transferable[]) => void;\n};\n\nscope.onmessage = (ev: MessageEvent) => {\n const { reply, transfers } = handleWorkerRequest(ev.data, sequencer);\n scope.postMessage(reply, [...transfers]);\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modernrelay/orbit-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.5",
|
|
4
4
|
"description": "Headless graph-visualization core: typed declarative snapshots reconciled into atomic engine commits. No React, no DOM, no engine imports.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|