@cyberart-io/engine 0.0.7 → 0.0.8
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/README.md +3 -0
- package/dist/headless.d.ts +126 -60
- package/dist/headless.js +1 -1
- package/dist/index.d.ts +86 -67
- package/dist/index.js +1 -1
- package/docs/frame-benchmark.md +107 -0
- package/docs/headless-harness.md +5 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -90,6 +90,7 @@ Full API for the event router, deterministic replay, and CI harness (so agents c
|
|
|
90
90
|
- [Events and router](docs/events.md) — mailbox, envelope, typed contracts, `createEventRouter`, hops, idempotency, rejections
|
|
91
91
|
- [Deterministic mode](docs/deterministic-mode.md) — `step` / `schedule`, clocks, `ScriptedAction`, replay diffs
|
|
92
92
|
- [Headless harness](docs/headless-harness.md) — `createHeadlessHarness` from `@cyberart-io/engine/headless`, software Canvas2D, `compareImageData` / `assertPixelsEqual`, inspect / screenshot
|
|
93
|
+
- [Frame benchmarking](docs/frame-benchmark.md) — `benchmarkCartFrames` (headless) and optional `onFrameTiming` (live); zero cost when unset
|
|
93
94
|
- [Presentation adapter](docs/presentation-adapter.md) — host-owned render model, intents, loading / error / unsupported
|
|
94
95
|
- [Asset resolver](docs/asset-resolver.md) — host-pluggable images/audio/fonts/spritesheets, cache, preload, fallbacks
|
|
95
96
|
- [Presentation cue](docs/presentation-cue.md) — deterministic `createPresentationTimeline`, duplicate policy, reduced-motion, lifecycle events
|
|
@@ -272,6 +273,8 @@ CI and agents should drive the **same** `createRuntime({ deterministic })` path.
|
|
|
272
273
|
|
|
273
274
|
Full options, `click` clock rule, Node-only `captureFrame`, remount, visual asserts, and the reproduce command: [headless harness](docs/headless-harness.md).
|
|
274
275
|
|
|
276
|
+
Hash / trait cost A/B without a browser: `benchmarkCartFrames` / `formatFrameBenchmarkResult` from the same headless entry. Live overlays: `runtime.onFrameTiming`. Details and the zero-overhead-when-off contract: [frame benchmarking](docs/frame-benchmark.md).
|
|
277
|
+
|
|
275
278
|
```ts
|
|
276
279
|
import { createHeadlessHarness } from '@cyberart-io/engine/headless';
|
|
277
280
|
|
package/dist/headless.d.ts
CHANGED
|
@@ -333,6 +333,73 @@ type CartStateBundle = {
|
|
|
333
333
|
state: unknown;
|
|
334
334
|
};
|
|
335
335
|
|
|
336
|
+
/**
|
|
337
|
+
* Copyright (c) 2026 Aaron Boyarsky
|
|
338
|
+
* SPDX-License-Identifier: LicenseRef-CyberArt-Engine
|
|
339
|
+
* See packages/engine/LICENSE
|
|
340
|
+
*
|
|
341
|
+
* Host-controlled time, input, and asset completion for deterministic replays.
|
|
342
|
+
* Production kaleidoscope / Art Blocks playback does not enable this mode.
|
|
343
|
+
* Logical asset URLs are resolved by the host preloader (`assetResolver.ts`);
|
|
344
|
+
* this module only times `ASSET_READY_EVENT` / `ASSET_FAILED_EVENT` delivery.
|
|
345
|
+
*/
|
|
346
|
+
|
|
347
|
+
type PointerKind = 'down' | 'move' | 'up';
|
|
348
|
+
type ScriptedAction = {
|
|
349
|
+
atFrame: number;
|
|
350
|
+
} & ({
|
|
351
|
+
type: 'pointer';
|
|
352
|
+
pointer: {
|
|
353
|
+
kind: PointerKind;
|
|
354
|
+
x: number;
|
|
355
|
+
y: number;
|
|
356
|
+
};
|
|
357
|
+
} | {
|
|
358
|
+
type: 'key';
|
|
359
|
+
key: string;
|
|
360
|
+
} | {
|
|
361
|
+
type: 'event';
|
|
362
|
+
event: HostEvent;
|
|
363
|
+
} | {
|
|
364
|
+
type: 'asset';
|
|
365
|
+
id: string;
|
|
366
|
+
status: 'ready' | 'failed';
|
|
367
|
+
/** Optional structured failure or resolved resource. Envelope is unchanged. */
|
|
368
|
+
detail?: unknown;
|
|
369
|
+
});
|
|
370
|
+
type DeterministicRuntimeOptions = {
|
|
371
|
+
/** Virtual clock origin in ms. Default 0. */
|
|
372
|
+
origin?: number;
|
|
373
|
+
/** Actions applied at the start of `atFrame`, before `update`. */
|
|
374
|
+
actions?: ScriptedAction[];
|
|
375
|
+
};
|
|
376
|
+
type ClockSnapshot = {
|
|
377
|
+
now: number;
|
|
378
|
+
framesElapsed: number;
|
|
379
|
+
frameRate: number;
|
|
380
|
+
};
|
|
381
|
+
type ReplayMetadata = {
|
|
382
|
+
seed: string;
|
|
383
|
+
clock: ClockSnapshot;
|
|
384
|
+
rng: RandomState;
|
|
385
|
+
actions: ScriptedAction[];
|
|
386
|
+
applied: AppliedAction[];
|
|
387
|
+
events: HostEvent[];
|
|
388
|
+
state: unknown;
|
|
389
|
+
};
|
|
390
|
+
type AppliedAction = {
|
|
391
|
+
frame: number;
|
|
392
|
+
action: ScriptedAction;
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
type FrameTimingSample = {
|
|
396
|
+
frame: number;
|
|
397
|
+
updateMs: number;
|
|
398
|
+
renderMs: number;
|
|
399
|
+
drawMs: number;
|
|
400
|
+
totalMs: number;
|
|
401
|
+
};
|
|
402
|
+
|
|
336
403
|
/**
|
|
337
404
|
* Copyright (c) 2026 Aaron Boyarsky
|
|
338
405
|
* SPDX-License-Identifier: LicenseRef-CyberArt-Engine
|
|
@@ -403,65 +470,6 @@ type ExportSnapshotOptions = {
|
|
|
403
470
|
runtimeVersion?: string;
|
|
404
471
|
};
|
|
405
472
|
|
|
406
|
-
/**
|
|
407
|
-
* Copyright (c) 2026 Aaron Boyarsky
|
|
408
|
-
* SPDX-License-Identifier: LicenseRef-CyberArt-Engine
|
|
409
|
-
* See packages/engine/LICENSE
|
|
410
|
-
*
|
|
411
|
-
* Host-controlled time, input, and asset completion for deterministic replays.
|
|
412
|
-
* Production kaleidoscope / Art Blocks playback does not enable this mode.
|
|
413
|
-
* Logical asset URLs are resolved by the host preloader (`assetResolver.ts`);
|
|
414
|
-
* this module only times `ASSET_READY_EVENT` / `ASSET_FAILED_EVENT` delivery.
|
|
415
|
-
*/
|
|
416
|
-
|
|
417
|
-
type PointerKind = 'down' | 'move' | 'up';
|
|
418
|
-
type ScriptedAction = {
|
|
419
|
-
atFrame: number;
|
|
420
|
-
} & ({
|
|
421
|
-
type: 'pointer';
|
|
422
|
-
pointer: {
|
|
423
|
-
kind: PointerKind;
|
|
424
|
-
x: number;
|
|
425
|
-
y: number;
|
|
426
|
-
};
|
|
427
|
-
} | {
|
|
428
|
-
type: 'key';
|
|
429
|
-
key: string;
|
|
430
|
-
} | {
|
|
431
|
-
type: 'event';
|
|
432
|
-
event: HostEvent;
|
|
433
|
-
} | {
|
|
434
|
-
type: 'asset';
|
|
435
|
-
id: string;
|
|
436
|
-
status: 'ready' | 'failed';
|
|
437
|
-
/** Optional structured failure or resolved resource. Envelope is unchanged. */
|
|
438
|
-
detail?: unknown;
|
|
439
|
-
});
|
|
440
|
-
type DeterministicRuntimeOptions = {
|
|
441
|
-
/** Virtual clock origin in ms. Default 0. */
|
|
442
|
-
origin?: number;
|
|
443
|
-
/** Actions applied at the start of `atFrame`, before `update`. */
|
|
444
|
-
actions?: ScriptedAction[];
|
|
445
|
-
};
|
|
446
|
-
type ClockSnapshot = {
|
|
447
|
-
now: number;
|
|
448
|
-
framesElapsed: number;
|
|
449
|
-
frameRate: number;
|
|
450
|
-
};
|
|
451
|
-
type ReplayMetadata = {
|
|
452
|
-
seed: string;
|
|
453
|
-
clock: ClockSnapshot;
|
|
454
|
-
rng: RandomState;
|
|
455
|
-
actions: ScriptedAction[];
|
|
456
|
-
applied: AppliedAction[];
|
|
457
|
-
events: HostEvent[];
|
|
458
|
-
state: unknown;
|
|
459
|
-
};
|
|
460
|
-
type AppliedAction = {
|
|
461
|
-
frame: number;
|
|
462
|
-
action: ScriptedAction;
|
|
463
|
-
};
|
|
464
|
-
|
|
465
473
|
declare const ASSET_KINDS: readonly ["image", "audio", "font", "spritesheet"];
|
|
466
474
|
type AssetKind = (typeof ASSET_KINDS)[number];
|
|
467
475
|
declare const ASSET_FAILURE_CODES: readonly ["timeout", "cors", "not-found", "invalid", "aborted", "resolver"];
|
|
@@ -630,6 +638,7 @@ type FrameErrorInfo = {
|
|
|
630
638
|
consecutive: number;
|
|
631
639
|
stopped: boolean;
|
|
632
640
|
};
|
|
641
|
+
|
|
633
642
|
type CreateRuntimeOptions = {
|
|
634
643
|
/** Required mount point. The runtime creates or adopts a canvas inside this element. */
|
|
635
644
|
container: HTMLElement;
|
|
@@ -679,6 +688,11 @@ type CreateRuntimeOptions = {
|
|
|
679
688
|
* `update`, and host-channel events still run. Default `'render'`.
|
|
680
689
|
*/
|
|
681
690
|
kind?: CartKind;
|
|
691
|
+
/**
|
|
692
|
+
* Optional per-frame update/render/draw timings. Can also be assigned later
|
|
693
|
+
* via `runtime.onFrameTiming`. Unset = no `performance.now` in the draw loop.
|
|
694
|
+
*/
|
|
695
|
+
onFrameTiming?: (sample: FrameTimingSample) => void;
|
|
682
696
|
};
|
|
683
697
|
type MountOptions<T = unknown> = {
|
|
684
698
|
/** Boot overrides passed as `customState` into `getDefaultState`. Not a live-state replay. */
|
|
@@ -760,6 +774,11 @@ type CyberArtRuntime = {
|
|
|
760
774
|
/** `'render'` (default) or `'calculation'` (no canvas / paint). */
|
|
761
775
|
readonly kind: CartKind;
|
|
762
776
|
onError?: (error: unknown, info: FrameErrorInfo) => void;
|
|
777
|
+
/**
|
|
778
|
+
* Optional per-frame update/render/draw timings. Unset = zero overhead in the
|
|
779
|
+
* draw loop (single null check). Same pattern as `onError`.
|
|
780
|
+
*/
|
|
781
|
+
onFrameTiming?: (sample: FrameTimingSample) => void;
|
|
763
782
|
};
|
|
764
783
|
|
|
765
784
|
/**
|
|
@@ -1689,6 +1708,53 @@ type VisualLayerCapture = {
|
|
|
1689
1708
|
declare function captureVisualLayers(controller: VisualLayerController): VisualLayerCapture;
|
|
1690
1709
|
declare function createVisualLayerController(options: CreateVisualLayerControllerOptions): VisualLayerController;
|
|
1691
1710
|
|
|
1711
|
+
/**
|
|
1712
|
+
* Copyright (c) 2026 Aaron Boyarsky
|
|
1713
|
+
* SPDX-License-Identifier: LicenseRef-CyberArt-Engine
|
|
1714
|
+
* See packages/engine/LICENSE
|
|
1715
|
+
*
|
|
1716
|
+
* Cart-agnostic update/render frame benchmark for CI and agent workflows.
|
|
1717
|
+
* Prefer this over ad-hoc vitest probes when evaluating hash / trait cost.
|
|
1718
|
+
*/
|
|
1719
|
+
|
|
1720
|
+
type FrameBenchmarkOptions = {
|
|
1721
|
+
/** Measured frames after warmup. Default 30. */
|
|
1722
|
+
frames?: number;
|
|
1723
|
+
/** Discarded frames before measurement. Default 5. */
|
|
1724
|
+
warmupFrames?: number;
|
|
1725
|
+
/** Simulated frame advance in ms. Default 1000/60. */
|
|
1726
|
+
frameStepMs?: number;
|
|
1727
|
+
width?: number;
|
|
1728
|
+
height?: number;
|
|
1729
|
+
tokenId?: string;
|
|
1730
|
+
/**
|
|
1731
|
+
* Mutate state after `getDefaultState` (e.g. skip Tone init in jsdom by
|
|
1732
|
+
* setting `audioContextStarted = true`).
|
|
1733
|
+
*/
|
|
1734
|
+
prepareState?: (state: unknown, featureState: unknown) => void;
|
|
1735
|
+
/** Optional stub drawing context; defaults to a no-op `putImageData`. */
|
|
1736
|
+
drawingContext?: CanvasRenderingContext2D;
|
|
1737
|
+
};
|
|
1738
|
+
type FrameBenchmarkResult = {
|
|
1739
|
+
frames: number;
|
|
1740
|
+
updateAvgMs: number;
|
|
1741
|
+
renderAvgMs: number;
|
|
1742
|
+
totalAvgMs: number;
|
|
1743
|
+
updateMaxMs: number;
|
|
1744
|
+
renderMaxMs: number;
|
|
1745
|
+
estFps: number;
|
|
1746
|
+
updatePct: number;
|
|
1747
|
+
renderPct: number;
|
|
1748
|
+
};
|
|
1749
|
+
/**
|
|
1750
|
+
* Run a cart's update/render loop headlessly and report average / max phase
|
|
1751
|
+
* timings. Does not start audio or mount a live AnimationManager — suitable
|
|
1752
|
+
* for jsdom vitest and agent hash probes.
|
|
1753
|
+
*/
|
|
1754
|
+
declare function benchmarkCartFrames<T, TFeatureState = undefined>(cart: AnimationCart<T, TFeatureState>, hash: string, rawParams: number[], options?: FrameBenchmarkOptions): FrameBenchmarkResult;
|
|
1755
|
+
/** Round timing fields for stable console / snapshot logging. */
|
|
1756
|
+
declare function formatFrameBenchmarkResult(result: FrameBenchmarkResult, digits?: number): Record<string, number>;
|
|
1757
|
+
|
|
1692
1758
|
/**
|
|
1693
1759
|
* Copyright (c) 2026 Aaron Boyarsky
|
|
1694
1760
|
* SPDX-License-Identifier: LicenseRef-CyberArt-Engine
|
|
@@ -1709,4 +1775,4 @@ type WriteComposedFrameResult = ComposedFrame & {
|
|
|
1709
1775
|
*/
|
|
1710
1776
|
declare function writeComposedFrame(compositor: Compositor, path?: string): Promise<WriteComposedFrameResult>;
|
|
1711
1777
|
|
|
1712
|
-
export { type BoundReplaySession, type CausationTreeNode, type CreateHeadlessHarnessOptions, type CreateHeadlessMultiCartHarnessOptions, type CreateReplayInspectorOptions, DEFAULT_HEADLESS_HEIGHT, DEFAULT_HEADLESS_WIDTH, type GlyphAtlas, HEADLESS_PNG_DATA_URL, type HeadlessAudioAdapter, type HeadlessCanvas2DSettings, type HeadlessFrameError, type HeadlessHarness, type HeadlessImageFixture, type HeadlessInspect, type HeadlessMultiCartHarness, HeadlessUnsupportedOperationError, type InspectorRecord, type InstallHeadlessCanvasOptions, type ReplayCompareResult, type ReplayInspector, type ReplayInspectorExport, type ReplayInspectorReport, type ReplayParticipantSummary, type ReplayTapeAction, type ReplayTraceFilter, UPDATE_GOLDEN_ENV, type VisualArtifactPaths, type VisualArtifacts, type VisualCompareOptions, type VisualCompareResult, type VisualLayerCapture, type VisualLayerController, type VisualLayerInspect, type WriteComposedFrameResult, assertPixelsEqual, assertPngDataUrlsEqual, attachHeadlessCanvas2D, captureVisualLayers, compareImageData, compareReplayTraces, createDefaultGlyphAtlas, createHeadlessAudioAdapter, createHeadlessHarness, createHeadlessMultiCartHarness, createImageFixture, createReplayInspector, createVisualLayerController, decodePng, encodePng, encodePngDataUrl, getHeadlessSurface, imageDataFromPngDataUrl, installHeadlessCanvas, makeImageData, replayExportedTrace, setDefaultGlyphAtlas, shouldUpdateGolden, writeComposedFrame, writeVisualArtifacts };
|
|
1778
|
+
export { type BoundReplaySession, type CausationTreeNode, type CreateHeadlessHarnessOptions, type CreateHeadlessMultiCartHarnessOptions, type CreateReplayInspectorOptions, DEFAULT_HEADLESS_HEIGHT, DEFAULT_HEADLESS_WIDTH, type FrameBenchmarkOptions, type FrameBenchmarkResult, type GlyphAtlas, HEADLESS_PNG_DATA_URL, type HeadlessAudioAdapter, type HeadlessCanvas2DSettings, type HeadlessFrameError, type HeadlessHarness, type HeadlessImageFixture, type HeadlessInspect, type HeadlessMultiCartHarness, HeadlessUnsupportedOperationError, type InspectorRecord, type InstallHeadlessCanvasOptions, type ReplayCompareResult, type ReplayInspector, type ReplayInspectorExport, type ReplayInspectorReport, type ReplayParticipantSummary, type ReplayTapeAction, type ReplayTraceFilter, UPDATE_GOLDEN_ENV, type VisualArtifactPaths, type VisualArtifacts, type VisualCompareOptions, type VisualCompareResult, type VisualLayerCapture, type VisualLayerController, type VisualLayerInspect, type WriteComposedFrameResult, assertPixelsEqual, assertPngDataUrlsEqual, attachHeadlessCanvas2D, benchmarkCartFrames, captureVisualLayers, compareImageData, compareReplayTraces, createDefaultGlyphAtlas, createHeadlessAudioAdapter, createHeadlessHarness, createHeadlessMultiCartHarness, createImageFixture, createReplayInspector, createVisualLayerController, decodePng, encodePng, encodePngDataUrl, formatFrameBenchmarkResult, getHeadlessSurface, imageDataFromPngDataUrl, installHeadlessCanvas, makeImageData, replayExportedTrace, setDefaultGlyphAtlas, shouldUpdateGolden, writeComposedFrame, writeVisualArtifacts };
|