@vgai/engine 0.5.15 → 0.5.16

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.
Files changed (174) hide show
  1. package/dist/adapter/adapter-module.d.ts +228 -17
  2. package/dist/adapter/adapter-module.d.ts.map +1 -1
  3. package/dist/adapter/adapter-module.js +224 -42
  4. package/dist/adapter/authoring-seam-contract.d.ts +530 -0
  5. package/dist/adapter/authoring-seam-contract.d.ts.map +1 -0
  6. package/dist/adapter/authoring-seam-contract.js +162 -0
  7. package/dist/adapter/authoring.d.ts +237 -50
  8. package/dist/adapter/authoring.d.ts.map +1 -1
  9. package/dist/adapter/authoring.js +2 -1
  10. package/dist/adapter/entrypoint-selection-source.d.ts +35 -0
  11. package/dist/adapter/entrypoint-selection-source.d.ts.map +1 -0
  12. package/dist/adapter/entrypoint-selection-source.js +151 -0
  13. package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts +7 -1
  14. package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts.map +1 -1
  15. package/dist/adapter/finders/scenes-from-entrypoint-selection.js +142 -21
  16. package/dist/adapter/first-party-systems.d.ts +13 -3
  17. package/dist/adapter/first-party-systems.d.ts.map +1 -1
  18. package/dist/adapter/first-party-systems.js +10 -3
  19. package/dist/adapter/index.d.ts +7 -2
  20. package/dist/adapter/index.d.ts.map +1 -1
  21. package/dist/adapter/index.js +4 -0
  22. package/dist/adapter/ingest/contract-system-adapters.d.ts +1 -1
  23. package/dist/adapter/ingest/contract-system-adapters.d.ts.map +1 -1
  24. package/dist/adapter/ingest/contract-system-adapters.js +13 -25
  25. package/dist/adapter/ingest/game-contract-seams.d.ts +214 -0
  26. package/dist/adapter/ingest/game-contract-seams.d.ts.map +1 -0
  27. package/dist/adapter/ingest/game-contract-seams.js +62 -0
  28. package/dist/adapter/ingest/game-contract.d.ts +5 -3
  29. package/dist/adapter/ingest/game-contract.d.ts.map +1 -1
  30. package/dist/adapter/ingest/game-contract.js +4 -1
  31. package/dist/adapter/ingest/merge-debug-adapters.d.ts +52 -0
  32. package/dist/adapter/ingest/merge-debug-adapters.d.ts.map +1 -0
  33. package/dist/adapter/ingest/merge-debug-adapters.js +141 -0
  34. package/dist/adapter/ingest/observation-debug-adapter.d.ts +56 -0
  35. package/dist/adapter/ingest/observation-debug-adapter.d.ts.map +1 -0
  36. package/dist/adapter/ingest/observation-debug-adapter.js +131 -0
  37. package/dist/adapter/ingest/scene-capture.d.ts +38 -13
  38. package/dist/adapter/ingest/scene-capture.d.ts.map +1 -1
  39. package/dist/adapter/ingest/scene-capture.js +101 -24
  40. package/dist/adapter/ingest/visible-capture-window.d.ts +22 -5
  41. package/dist/adapter/ingest/visible-capture-window.d.ts.map +1 -1
  42. package/dist/adapter/ingest/visible-capture-window.js +46 -7
  43. package/dist/adapter/native-debug-module.d.ts +43 -0
  44. package/dist/adapter/native-debug-module.d.ts.map +1 -0
  45. package/dist/adapter/native-debug-module.js +230 -0
  46. package/dist/adapter/object3d-authoring-subject.d.ts +32 -0
  47. package/dist/adapter/object3d-authoring-subject.d.ts.map +1 -0
  48. package/dist/adapter/object3d-authoring-subject.js +25 -0
  49. package/dist/adapter/root-adapter.d.ts +21 -56
  50. package/dist/adapter/root-adapter.d.ts.map +1 -1
  51. package/dist/adapter/root-adapter.js +4 -20
  52. package/dist/adapter/root-seam-contract.d.ts +124 -0
  53. package/dist/adapter/root-seam-contract.d.ts.map +1 -0
  54. package/dist/adapter/root-seam-contract.js +36 -0
  55. package/dist/adapter/seam-evidence.d.ts +118 -0
  56. package/dist/adapter/seam-evidence.d.ts.map +1 -0
  57. package/dist/adapter/seam-evidence.js +231 -0
  58. package/dist/adapter/setup-three-root-adapter.d.ts +2 -3
  59. package/dist/adapter/setup-three-root-adapter.d.ts.map +1 -1
  60. package/dist/adapter/setup-three-root-adapter.js +2 -3
  61. package/dist/adapter/system-adapter.d.ts +6 -1
  62. package/dist/adapter/system-adapter.d.ts.map +1 -1
  63. package/dist/adapter/system-seam-contract.d.ts +354 -0
  64. package/dist/adapter/system-seam-contract.d.ts.map +1 -0
  65. package/dist/adapter/system-seam-contract.js +95 -0
  66. package/dist/animation/xstate-animation-binding.d.ts +6 -0
  67. package/dist/animation/xstate-animation-binding.d.ts.map +1 -1
  68. package/dist/animation/xstate-animation-binding.js +142 -4
  69. package/dist/asset-formats/collider.d.ts +1 -1
  70. package/dist/asset-formats/mesh.d.ts +2 -2
  71. package/dist/asset-formats/particles.d.ts +1 -1
  72. package/dist/canvas-react/engine-bridge.d.ts +6 -6
  73. package/dist/canvas-react/engine-bridge.d.ts.map +1 -1
  74. package/dist/canvas-react/engine-bridge.js +2 -2
  75. package/dist/canvas-react/index.d.ts +2 -2
  76. package/dist/canvas-react/index.d.ts.map +1 -1
  77. package/dist/canvas-react/index.js +1 -1
  78. package/dist/canvas-react/pixi-react-adapter.d.ts +15 -9
  79. package/dist/canvas-react/pixi-react-adapter.d.ts.map +1 -1
  80. package/dist/canvas-react/pixi-react-adapter.js +43 -29
  81. package/dist/canvas-react/pixi-react-root-factory.d.ts.map +1 -1
  82. package/dist/canvas-react/pixi-react-root-factory.js +4 -0
  83. package/dist/canvas-react/world-context.d.ts +22 -12
  84. package/dist/canvas-react/world-context.d.ts.map +1 -1
  85. package/dist/canvas-react/world-context.js +12 -51
  86. package/dist/ecs/user-data.d.ts +5 -0
  87. package/dist/ecs/user-data.d.ts.map +1 -1
  88. package/dist/ecs/user-data.js +3 -0
  89. package/dist/input/input-manager.d.ts +49 -0
  90. package/dist/input/input-manager.d.ts.map +1 -1
  91. package/dist/input/input-manager.js +127 -0
  92. package/dist/manifest/schema.d.ts +9 -9
  93. package/dist/pixi/authoring.d.ts.map +1 -1
  94. package/dist/pixi/authoring.js +220 -1
  95. package/dist/pixi/ingest.d.ts +2 -1
  96. package/dist/pixi/ingest.d.ts.map +1 -1
  97. package/dist/pixi/ingest.js +4 -2
  98. package/dist/pixi/scene-capture.d.ts +9 -1
  99. package/dist/pixi/scene-capture.d.ts.map +1 -1
  100. package/dist/pixi/scene-capture.js +9 -6
  101. package/dist/render/particles-factory.d.ts +13 -6
  102. package/dist/render/particles-factory.d.ts.map +1 -1
  103. package/dist/render/particles-factory.js +6 -3
  104. package/dist/runtime/create-runtime.d.ts +23 -46
  105. package/dist/runtime/create-runtime.d.ts.map +1 -1
  106. package/dist/runtime/create-runtime.js +22 -67
  107. package/dist/runtime/game.d.ts +29 -71
  108. package/dist/runtime/game.d.ts.map +1 -1
  109. package/dist/runtime/game.js +15 -54
  110. package/dist/runtime/mount-game.d.ts.map +1 -1
  111. package/dist/runtime/mount-game.js +12 -1
  112. package/dist/runtime/mount-manifest.d.ts.map +1 -1
  113. package/dist/runtime/mount-manifest.js +4 -5
  114. package/dist/runtime/types.d.ts +18 -0
  115. package/dist/runtime/types.d.ts.map +1 -1
  116. package/dist/setup/setup-renderer.d.ts +3 -4
  117. package/dist/setup/setup-renderer.d.ts.map +1 -1
  118. package/dist/setup/setup-renderer.js +3 -4
  119. package/dist/world3d-react/engine-bridge.d.ts +1 -1
  120. package/dist/world3d-react/engine-bridge.js +1 -1
  121. package/dist/world3d-react/r3f-adapter.d.ts +7 -0
  122. package/dist/world3d-react/r3f-adapter.d.ts.map +1 -1
  123. package/dist/world3d-react/r3f-adapter.js +50 -21
  124. package/dist/world3d-react/r3f-root-factory.d.ts.map +1 -1
  125. package/dist/world3d-react/r3f-root-factory.js +4 -0
  126. package/dist/world3d-react/world-context.d.ts +9 -3
  127. package/dist/world3d-react/world-context.d.ts.map +1 -1
  128. package/dist/world3d-react/world-context.js +5 -18
  129. package/package.json +1 -1
  130. package/schemas/engine-capabilities.json +7 -7
  131. package/src/adapter/adapter-module.ts +407 -50
  132. package/src/adapter/authoring-seam-contract.ts +211 -0
  133. package/src/adapter/authoring.ts +242 -50
  134. package/src/adapter/entrypoint-selection-source.ts +159 -0
  135. package/src/adapter/finders/scenes-from-entrypoint-selection.ts +159 -28
  136. package/src/adapter/first-party-systems.ts +17 -3
  137. package/src/adapter/index.ts +51 -1
  138. package/src/adapter/ingest/contract-system-adapters.ts +26 -30
  139. package/src/adapter/ingest/game-contract-seams.ts +85 -0
  140. package/src/adapter/ingest/game-contract.ts +9 -4
  141. package/src/adapter/ingest/merge-debug-adapters.ts +193 -0
  142. package/src/adapter/ingest/observation-debug-adapter.ts +158 -0
  143. package/src/adapter/ingest/scene-capture.ts +126 -23
  144. package/src/adapter/ingest/visible-capture-window.ts +63 -9
  145. package/src/adapter/native-debug-module.ts +307 -0
  146. package/src/adapter/object3d-authoring-subject.ts +50 -0
  147. package/src/adapter/root-adapter.ts +21 -56
  148. package/src/adapter/root-seam-contract.ts +56 -0
  149. package/src/adapter/seam-evidence.ts +336 -0
  150. package/src/adapter/setup-three-root-adapter.ts +4 -6
  151. package/src/adapter/system-adapter.ts +6 -1
  152. package/src/adapter/system-seam-contract.ts +121 -0
  153. package/src/animation/xstate-animation-binding.ts +151 -4
  154. package/src/canvas-react/engine-bridge.ts +6 -6
  155. package/src/canvas-react/index.ts +2 -1
  156. package/src/canvas-react/pixi-react-adapter.tsx +55 -34
  157. package/src/canvas-react/pixi-react-root-factory.tsx +4 -0
  158. package/src/canvas-react/world-context.ts +26 -101
  159. package/src/ecs/user-data.ts +5 -0
  160. package/src/input/input-manager.ts +122 -0
  161. package/src/pixi/authoring.ts +236 -1
  162. package/src/pixi/ingest.ts +9 -2
  163. package/src/pixi/scene-capture.ts +20 -5
  164. package/src/render/particles-factory.ts +16 -6
  165. package/src/runtime/create-runtime.ts +41 -106
  166. package/src/runtime/game.ts +41 -111
  167. package/src/runtime/mount-game.ts +19 -1
  168. package/src/runtime/mount-manifest.ts +4 -5
  169. package/src/runtime/types.ts +22 -0
  170. package/src/setup/setup-renderer.ts +3 -4
  171. package/src/world3d-react/engine-bridge.ts +1 -1
  172. package/src/world3d-react/r3f-adapter.tsx +64 -24
  173. package/src/world3d-react/r3f-root-factory.tsx +4 -0
  174. package/src/world3d-react/world-context.ts +11 -36
@@ -35,6 +35,13 @@ export interface CreateR3FAdapterOptions {
35
35
  * actions). A missing/invalid map degrades loudly without failing the
36
36
  * mount — see `world-context.ts`. */
37
37
  readonly inputMapPath?: string | null | undefined;
38
+ /**
39
+ * Transitional compatibility for explicit pre-native adapters. The
40
+ * default-export root factory always passes `false`: ordinary R3F trees get
41
+ * no vgai context/provider or private scheduler. Remove this switch with
42
+ * the last explicit legacy adapter migration.
43
+ */
44
+ readonly engineRuntime?: boolean | undefined;
38
45
  /** The colour pipeline this world was AUTHORED for, applied to the host's renderer for the life
39
46
  * of the mount and restored on dispose. Omit it (every world here does) to keep the host's own
40
47
  * defaults; declare it when the world's colours were picked against a different engine's
@@ -1 +1 @@
1
- {"version":3,"file":"r3f-adapter.d.ts","sourceRoot":"","sources":["../../src/world3d-react/r3f-adapter.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAUH,OAAO,EAA2B,KAAK,SAAS,EAAsB,MAAM,OAAO,CAAC;AACpF,OAAO,KAAK,EAAoB,WAAW,EAAoB,MAAM,YAAY,CAAC;AAclF,OAAO,EAA4B,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AA2CvF,sEAAsE;AACtE,MAAM,WAAW,uBAAuB;IACtC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB;;uBAEmB;IACnB,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;IAC5B;;;;0CAIsC;IACtC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClD;;;iDAG6C;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;CACrD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,WAAW,CA6e9E"}
1
+ {"version":3,"file":"r3f-adapter.d.ts","sourceRoot":"","sources":["../../src/world3d-react/r3f-adapter.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAWH,OAAO,EAA2B,KAAK,SAAS,EAAsB,MAAM,OAAO,CAAC;AACpF,OAAO,KAAK,EAAoB,WAAW,EAAoB,MAAM,YAAY,CAAC;AAgBlF,OAAO,EAA4B,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AA2CvF,sEAAsE;AACtE,MAAM,WAAW,uBAAuB;IACtC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB;;uBAEmB;IACnB,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;IAC5B;;;;0CAIsC;IACtC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClD;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7C;;;iDAG6C;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;CACrD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,WAAW,CA2gB9E"}
@@ -18,7 +18,7 @@
18
18
  * project guarantees via its Vite `resolve.dedupe: ['three', 'react',
19
19
  * 'react-dom']` (see `examples/r3f-first-party/vite.config.ts`).
20
20
  */
21
- import { advance, createRoot, extend, events as pointerEvents, useFrame, } from '@react-three/fiber';
21
+ import { advance, createRoot, extend, flushSync, events as pointerEvents, useFrame, } from '@react-three/fiber';
22
22
  import { createElement, Fragment, useEffect, useMemo } from 'react';
23
23
  import { registerRenderVitals } from '../dev/register-render-vitals';
24
24
  import { createRenderDebugAdapter, frameCaptureContextFor, } from '../dev/render-debug-adapter';
@@ -27,6 +27,7 @@ import { RENDER_SUBMIT_PHASE } from '../dev/render-vitals';
27
27
  import { createWebGLFrameCapture } from '../dev/webgl-frame-capture';
28
28
  import { WorldProvider } from '../react/world-state';
29
29
  import { createSoftParticleDepthPass } from '../render/soft-particle-depth';
30
+ import { getDebugRegistry } from '../runtime/debug-registry';
30
31
  import { devLayersEnabled } from '../runtime/dev-layers';
31
32
  import { EngineBridge } from './engine-bridge';
32
33
  import { applyWorldRendererConfig } from './renderer-config';
@@ -68,7 +69,7 @@ function SoftParticleDepth() {
68
69
  * second `requestAnimationFrame` loop (design §1.C).
69
70
  */
70
71
  export function createR3FAdapter(options) {
71
- const { id, content, inputMapPath, renderer: rendererConfig } = options;
72
+ const { id, content, inputMapPath, engineRuntime = true, renderer: rendererConfig } = options;
72
73
  return {
73
74
  id,
74
75
  async mount(host) {
@@ -122,15 +123,14 @@ export function createR3FAdapter(options) {
122
123
  // for one is a compile error rather than anything that can happen at
123
124
  // runtime. `scene`/`camera` are backfilled right after fiber's first
124
125
  // commit resolves them, before any tick can run.
125
- const runtime = createR3FRootContext(host, { id, inputMapPath });
126
- const { systems } = runtime;
126
+ const runtime = engineRuntime ? createR3FRootContext(host, { id, inputMapPath }) : null;
127
127
  // `ctx` rides the bridge so ordinary components can reach engine
128
128
  // services through `useGameContext()`.
129
- const bridge = { ctx: runtime.ctx };
129
+ const bridge = runtime ? { ctx: runtime.ctx } : null;
130
130
  // Actions must exist before any component's init()/first frame reads
131
131
  // them — wait for the (never-rejecting) input-map load before the first
132
132
  // commit.
133
- await runtime.inputMapReady;
133
+ await runtime?.inputMapReady;
134
134
  // The world's own colour pipeline. Applied AFTER `root.configure()` below — see the comment
135
135
  // at its call site for why "before" is the version that silently does nothing.
136
136
  let restoreRendererConfig = () => { };
@@ -147,6 +147,19 @@ export function createR3FAdapter(options) {
147
147
  const statePromise = new Promise((resolve) => {
148
148
  resolveState = resolve;
149
149
  });
150
+ let resolveEffectsReady;
151
+ const effectsReady = new Promise((resolve) => {
152
+ resolveEffectsReady = resolve;
153
+ });
154
+ // Rendered after the game's content. React runs passive effects in tree
155
+ // order, so when this fires every useDebugProvider/useDebugCommand in
156
+ // the initial tree has registered. A Play acknowledgement is a claim
157
+ // that the mounted game's command surface exists, not merely that Fiber
158
+ // allocated a scene.
159
+ function MountEffectsReady() {
160
+ useEffect(resolveEffectsReady, []);
161
+ return null;
162
+ }
150
163
  // `root.configure()` is ASYNC in fiber v9 (`Promise<ReconcilerRoot>`)
151
164
  // — the design doc's §1.C sketch shows it called synchronously; the
152
165
  // real v9 API (verified against
@@ -244,11 +257,13 @@ export function createR3FAdapter(options) {
244
257
  // until this landed. `host.game` is genuinely absent in bare/headless
245
258
  // hosts, so the provider is conditional and those hooks stay the inert
246
259
  // no-ops they already document themselves to be.
247
- const game = runtime.ctx.game;
260
+ const game = runtime?.ctx.game;
248
261
  // `SoftParticleDepth` trails the world's own tree deliberately — see its
249
262
  // docblock for why the subscription must land last.
250
- const world = createElement(Fragment, null, content, createElement(SoftParticleDepth, { key: 'vgai-soft-particle-depth' }));
251
- root.render(createElement(EngineBridge.Provider, { value: bridge }, game ? createElement(WorldProvider, { game }, world) : world));
263
+ const world = createElement(Fragment, null, content, runtime ? createElement(SoftParticleDepth, { key: 'vgai-soft-particle-depth' }) : null, createElement(MountEffectsReady, { key: 'vgai-mount-effects-ready' }));
264
+ root.render(bridge
265
+ ? createElement(EngineBridge.Provider, { value: bridge }, game ? createElement(WorldProvider, { game }, world) : world)
266
+ : world);
252
267
  // A reconcile-time crash (e.g. a missing `extend` catalogue entry)
253
268
  // surfaces as an uncaught window error and `onCreated` never fires —
254
269
  // without this guard, `mount()` would await `statePromise` FOREVER and
@@ -258,7 +273,7 @@ export function createR3FAdapter(options) {
258
273
  const onError = (event) => {
259
274
  cleanup();
260
275
  root.unmount();
261
- runtime.dispose();
276
+ runtime?.dispose();
262
277
  restoreRendererConfig();
263
278
  reject(new Error(`r3f-adapter: fiber crashed before its first commit — ${event.message} ` +
264
279
  '(mount() fails loudly instead of hanging on onCreated)'));
@@ -266,7 +281,7 @@ export function createR3FAdapter(options) {
266
281
  const timer = setTimeout(() => {
267
282
  cleanup();
268
283
  root.unmount();
269
- runtime.dispose();
284
+ runtime?.dispose();
270
285
  restoreRendererConfig();
271
286
  reject(new Error('r3f-adapter: onCreated did not fire within 10s — the R3F tree never reached ' +
272
287
  'its first commit (mount() fails loudly instead of hanging)'));
@@ -276,7 +291,7 @@ export function createR3FAdapter(options) {
276
291
  window.removeEventListener('error', onError);
277
292
  };
278
293
  window.addEventListener('error', onError);
279
- void statePromise.then((s) => {
294
+ void Promise.all([statePromise, effectsReady]).then(([s]) => {
280
295
  cleanup();
281
296
  resolve(s);
282
297
  });
@@ -294,7 +309,7 @@ export function createR3FAdapter(options) {
294
309
  // Backfill the component runtime's ctx with fiber's real scene/camera
295
310
  // (W3 — see the HONESTY NOTE above). This happens before the first
296
311
  // `update(dt)` tick, so no component ever observes them missing.
297
- runtime.setSceneCamera(state.scene, state.camera);
312
+ runtime?.setSceneCamera(state.scene, state.camera);
298
313
  // THE STORE IS THE TRUTH, NOT THE `onCreated` SNAPSHOT.
299
314
  //
300
315
  // `state` is one immutable zustand snapshot: fiber's store is
@@ -319,6 +334,7 @@ export function createR3FAdapter(options) {
319
334
  // `RootState` for precisely this reason, so reading it per frame is the
320
335
  // supported way to see the live root. It is a plain map read.
321
336
  const live = () => state.get();
337
+ const gameDebugRegistry = host.game ? getDebugRegistry(host.game) : null;
322
338
  // --- Live render vitals (issue #1504), for an R3F world -----------------
323
339
  // The SAME door the classic adapter seeds
324
340
  // (`../adapter/setup-three-root-adapter.ts`), seeded here for the same
@@ -357,8 +373,11 @@ export function createR3FAdapter(options) {
357
373
  // sets on play — under the dev gate this is the same cost arriving a
358
374
  // little earlier, not a new one.
359
375
  host.game.profiler.enabled = true;
376
+ if (!gameDebugRegistry) {
377
+ throw new Error('r3f-adapter: mounted Game has no debug registry.');
378
+ }
360
379
  renderVitals = registerRenderVitals({
361
- registry: runtime.debugRegistry,
380
+ registry: gameDebugRegistry,
362
381
  worldId: id,
363
382
  profiler: host.game.profiler,
364
383
  scene: live().scene,
@@ -382,8 +401,12 @@ export function createR3FAdapter(options) {
382
401
  memory: () => collectRenderMemory(live().scene, host.renderer.info),
383
402
  })
384
403
  : null;
404
+ // The session debug registry is host instrumentation, not component
405
+ // context. Publishing the existing adapter here lets coverage and
406
+ // editor panels see the same door without wrapping the R3F tree.
407
+ const systemAdapters = runtime?.systemAdapters ?? (gameDebugRegistry ? { debug: gameDebugRegistry.adapter } : {});
385
408
  if (renderDebugWiring)
386
- runtime.systemAdapters.renderDebug = renderDebugWiring.adapter;
409
+ systemAdapters.renderDebug = renderDebugWiring.adapter;
387
410
  // The engine drives every `useFrame` through the mounted world's
388
411
  // `update(dt)` hook, never off a raw host-loop callback. That is the
389
412
  // whole pause story: `runFrameImpl` (`runtime/game.ts`) calls
@@ -411,7 +434,7 @@ export function createR3FAdapter(options) {
411
434
  // resumed frame as one giant `useFrame` delta — the exact "time passed
412
435
  // while frozen" illusion acceptance gate 2 forbids.
413
436
  let elapsed = 0;
414
- /** The camera `ctx` was last backfilled with — see `update()`. */
437
+ /** The legacy component ctx camera last backfilled by `update()`. */
415
438
  let backfilledCamera = state.camera;
416
439
  return {
417
440
  kind: 'three',
@@ -444,7 +467,7 @@ export function createR3FAdapter(options) {
444
467
  // WebGL2 context exists, `renderDebug` are engine-seeded. Game code
445
468
  // adds its own capabilities via `ctx.registerSystemAdapter` exactly
446
469
  // as in a classic world.
447
- systems: runtime.systemAdapters,
470
+ systems: systemAdapters,
448
471
  update(dt) {
449
472
  elapsed += dt;
450
473
  const current = live();
@@ -453,14 +476,14 @@ export function createR3FAdapter(options) {
453
476
  // component reading one while the frame uses the other is the
454
477
  // silent kind of wrong. Identity-gated, so this is one comparison
455
478
  // per frame in the overwhelmingly common no-swap case.
456
- if (current.camera !== backfilledCamera) {
479
+ if (runtime && current.camera !== backfilledCamera) {
457
480
  backfilledCamera = current.camera;
458
481
  runtime.setSceneCamera(current.scene, current.camera);
459
482
  }
460
483
  // Engine phases FIRST (registered systems mutate transforms),
461
484
  // then fiber's advance (useFrame callbacks + the actual render see
462
485
  // the fresh state). Both are host-gated: neither runs while frozen.
463
- systems.run(dt);
486
+ runtime?.systems.run(dt);
464
487
  renderDebugWiring?.beforeRender();
465
488
  try {
466
489
  const profiler = host.game?.profiler;
@@ -513,7 +536,13 @@ export function createR3FAdapter(options) {
513
536
  // Reject/restore a pending capture while its renderables still
514
537
  // exist, then unmount component effects and strip runtime state.
515
538
  renderDebugWiring?.dispose();
516
- root.unmount();
539
+ // The editor audits and disposes the mounted game's window/document realm immediately
540
+ // after `session.stop()`. Fiber's ordinary `unmount()` submits a concurrent reconciler
541
+ // update, so component effect cleanups could still be pending at that boundary and the
542
+ // audit would truthfully report/reclaim listeners the world was about to remove itself.
543
+ // Force the unmount COMMIT (not fiber's deliberately delayed renderer/context disposal)
544
+ // to finish here: a stopped root owns no live component lifecycle beyond this method.
545
+ flushSync(() => root.unmount());
517
546
  // The vitals fold owns two `profiler.subscribe` registrations, and
518
547
  // `runtime.dispose()`'s `strip(id)` removes the REGISTRATIONS only —
519
548
  // this is the one path that ends the subscriptions, exactly as the
@@ -521,7 +550,7 @@ export function createR3FAdapter(options) {
521
550
  // subscription can never outlive the door it feeds.
522
551
  renderVitals?.dispose();
523
552
  renderVitals = null;
524
- runtime.dispose();
553
+ runtime?.dispose();
525
554
  restoreRendererConfig();
526
555
  },
527
556
  };
@@ -1 +1 @@
1
- {"version":3,"file":"r3f-root-factory.d.ts","sourceRoot":"","sources":["../../src/world3d-react/r3f-root-factory.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAUnE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAU/F;AAED;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,qBAQ5B,CAAC"}
1
+ {"version":3,"file":"r3f-root-factory.d.ts","sourceRoot":"","sources":["../../src/world3d-react/r3f-root-factory.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAUnE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAc/F;AAED;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,qBAQ5B,CAAC"}
@@ -59,6 +59,10 @@ export function resolveR3FEntryAdapter(entryModule, rootId) {
59
59
  return createR3FAdapter({
60
60
  id: rootId,
61
61
  content: createElement(mod.default),
62
+ // A default-exported R3F component is an ordinary R3F app. The host
63
+ // controls Fiber's native `frameloop: 'never'` scheduler; vgai runtime
64
+ // context, systems and state providers never enter its React tree.
65
+ engineRuntime: false,
62
66
  });
63
67
  }
64
68
  return null;
@@ -13,12 +13,15 @@
13
13
  * `ctx.debug = registry.forRoot(id)`, so a component's
14
14
  * effect can `ctx.debug.registerStateProvider(...)` and be visible
15
15
  * to `vgai eval`/the editor's debug panels — previously a silent no-op.
16
- * 2. **`ctx.input` is the game-owned `InputManager`** (`host.game.input`
16
+ * 2. **`ctx.input` is the game-owned `InputManager` when a Game hosts the
17
+ * root** (`host.game.input` —
17
18
  * the same instance the classic adapter uses when hosted), and this
18
19
  * module registers the SAME per-world debug-registry seams the classic
19
20
  * adapter registers (`setVirtualInputTarget`/`setInputActionsSource`/
20
21
  * `setInputTraceSource`), which is what makes `game.input.*` (the bot
21
22
  * input doctrine) work instead of throwing `DEBUG_INPUT_UNAVAILABLE`.
23
+ * A bare/foreign mount receives `null`, visibly in the type, rather than
24
+ * a getter that throws later inside a frame callback.
22
25
  * The project's input map is loaded through `Game.loadInputMap` (the
23
26
  * game-owned, load-once path every root shares) with the SAME default
24
27
  * path the classic adapter uses. A missing/unparseable map degrades
@@ -36,7 +39,7 @@ import type { ThreeHostContext } from '../adapter';
36
39
  import type { SystemAdapters } from '../adapter/system-adapter';
37
40
  import { createSystemRunner } from '../core/system-runner';
38
41
  import { type DebugRegistry } from '../runtime/debug-registry';
39
- import type { GameContext } from '../runtime/types';
42
+ import type { ComponentContextBase, GameContext } from '../runtime/types';
40
43
  /** The classic adapter's default input-map path — the conventional location
41
44
  * every scaffolded project ships (`public/inputmaps/default.inputmap.json`). */
42
45
  export declare const DEFAULT_INPUT_MAP_PATH = "/inputmaps/default.inputmap.json";
@@ -75,7 +78,10 @@ export declare const DEFAULT_INPUT_MAP_PATH = "/inputmaps/default.inputmap.json"
75
78
  * exporting `setup(ctx): Promise<GameCleanup>`, which `SetupThreeRootAdapter`
76
79
  * mounts with the complete {@link GameContext}.
77
80
  */
78
- export type R3FGameContext = Pick<GameContext, 'scene' | 'camera' | 'sceneIndex' | 'input' | 'assets' | 'systems' | 'clock' | 'debug' | 'random' | 'game' | 'roots' | 'playtest' | 'registerSystemAdapter'>;
81
+ export type R3FGameContext = ComponentContextBase & Pick<GameContext, 'scene' | 'sceneIndex' | 'assets' | 'clock'> & {
82
+ /** Fiber may install either a perspective or orthographic camera. */
83
+ camera: THREE.Camera;
84
+ };
79
85
  export interface R3FRootContextOptions {
80
86
  /** World id — provenance for every debug-registry registration (matches the
81
87
  * manifest root id by the same convention the classic adapter follows). */
@@ -1 +1 @@
1
- {"version":3,"file":"world-context.d.ts","sourceRoot":"","sources":["../../src/world3d-react/world-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAgCK;AAEL,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAOhE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAEL,KAAK,aAAa,EAEnB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;iFACiF;AACjF,eAAO,MAAM,sBAAsB,qCAAqC,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,MAAM,cAAc,GAAG,IAAI,CAC/B,WAAW,EACT,OAAO,GACP,QAAQ,GACR,YAAY,GACZ,OAAO,GACP,QAAQ,GACR,SAAS,GACT,OAAO,GACP,OAAO,GACP,QAAQ,GACR,MAAM,GACN,OAAO,GACP,UAAU,GACV,uBAAuB,CAC1B,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC;gFAC4E;IAC5E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACnD;AAED;+EAC+E;AAC/E,MAAM,WAAW,cAAc;IAC7B;6EACyE;IACzE,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;IACxD;;mFAE+E;IAC/E,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC;;;;;;yDAMqD;IACrD,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC;qEACiE;IACjE,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC;;;4DAGwD;IACxD,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;IAC/D;;gFAE4E;IAC5E,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,qBAAqB,GAC7B,cAAc,CAsKhB"}
1
+ {"version":3,"file":"world-context.d.ts","sourceRoot":"","sources":["../../src/world3d-react/world-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAmCK;AAEL,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAOhE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAEL,KAAK,aAAa,EAEnB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE1E;iFACiF;AACjF,eAAO,MAAM,sBAAsB,qCAAqC,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAC/C,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,YAAY,GAAG,QAAQ,GAAG,OAAO,CAAC,GAAG;IAC/D,qEAAqE;IACrE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC;CACtB,CAAC;AAEJ,MAAM,WAAW,qBAAqB;IACpC;gFAC4E;IAC5E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACnD;AAED;+EAC+E;AAC/E,MAAM,WAAW,cAAc;IAC7B;6EACyE;IACzE,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;IACxD;;mFAE+E;IAC/E,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC;;;;;;yDAMqD;IACrD,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC;qEACiE;IACjE,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC;;;4DAGwD;IACxD,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;IAC/D;;gFAE4E;IAC5E,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,qBAAqB,GAC7B,cAAc,CAqJhB"}
@@ -13,12 +13,15 @@
13
13
  * `ctx.debug = registry.forRoot(id)`, so a component's
14
14
  * effect can `ctx.debug.registerStateProvider(...)` and be visible
15
15
  * to `vgai eval`/the editor's debug panels — previously a silent no-op.
16
- * 2. **`ctx.input` is the game-owned `InputManager`** (`host.game.input`
16
+ * 2. **`ctx.input` is the game-owned `InputManager` when a Game hosts the
17
+ * root** (`host.game.input` —
17
18
  * the same instance the classic adapter uses when hosted), and this
18
19
  * module registers the SAME per-world debug-registry seams the classic
19
20
  * adapter registers (`setVirtualInputTarget`/`setInputActionsSource`/
20
21
  * `setInputTraceSource`), which is what makes `game.input.*` (the bot
21
22
  * input doctrine) work instead of throwing `DEBUG_INPUT_UNAVAILABLE`.
23
+ * A bare/foreign mount receives `null`, visibly in the type, rather than
24
+ * a getter that throws later inside a frame callback.
22
25
  * The project's input map is loaded through `Game.loadInputMap` (the
23
26
  * game-owned, load-once path every root shares) with the SAME default
24
27
  * path the classic adapter uses. A missing/unparseable map degrades
@@ -63,6 +66,7 @@ export function createR3FRootContext(host, options) {
63
66
  const ctxRaw = {
64
67
  systems,
65
68
  assets: host.assets,
69
+ input: host.game?.input ?? null,
66
70
  registerSystemAdapter: (kind, adapter) => {
67
71
  // biome-ignore lint/suspicious/noExplicitAny: same per-key record write the classic adapter performs; correct by construction
68
72
  systemAdapters[kind] = adapter;
@@ -106,7 +110,6 @@ export function createR3FRootContext(host, options) {
106
110
  if (host.game) {
107
111
  const game = host.game;
108
112
  const input = game.input;
109
- ctxRaw['input'] = input;
110
113
  debugRegistry.setInputActionsSource(id, () => input.actionNames().map((name) => ({ name, valueType: input.getActionValueType(name) })));
111
114
  debugRegistry.setInputTraceSource(id, () => {
112
115
  const raw = input.getInputTrace();
@@ -144,22 +147,6 @@ export function createR3FRootContext(host, options) {
144
147
  });
145
148
  }
146
149
  }
147
- else {
148
- // `input` IS a field of every R3F world's ctx — it is game-owned, and this
149
- // particular mount has no Game to own it (a bare harness or a foreign
150
- // host). A throwing getter names that at the read, instead of handing back
151
- // `undefined` that breaks deep inside a frame callback. Non-enumerable so
152
- // spreads/serialization of the ctx never trip it.
153
- Object.defineProperty(ctxRaw, 'input', {
154
- configurable: true,
155
- enumerable: false,
156
- get() {
157
- throw new Error(`R3F world "${id}": ctx.input is game-owned and this mount has no Game host ` +
158
- '(host.game is absent — bare harness/foreign host); mount through the vgai host ' +
159
- 'to get the shared InputManager.');
160
- },
161
- });
162
- }
163
150
  return {
164
151
  ctx,
165
152
  systems,
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@vgai/engine",
3
3
  "author": "Volter AI, Inc.",
4
4
  "license": "Apache-2.0",
5
- "version": "0.5.15",
5
+ "version": "0.5.16",
6
6
  "description": "Readable TypeScript game engine and universal host for Three.js, PixiJS, and React games.",
7
7
  "keywords": [
8
8
  "game-engine",
@@ -15,7 +15,7 @@
15
15
  ],
16
16
  "notes": "Grepped packages/engine/src for ccd/CCD/setCcdEnabled: zero matches. The engine constructs no rigid bodies itself — games build their own descs via ctx.RAPIER — and nothing engine-side ever enables ccd. Workaround: keep dynamic-body per-step travel small relative to the thinnest collider, or add intermediate colliders; a game may also call setCcdEnabled on its own bodies directly.",
17
17
  "evidenceHashes": {
18
- "packages/engine/src/adapter/setup-three-root-adapter.ts": "53d1cbcde6d1941273d5212a6fbb24e12c49de75453b28b534e5078174a27a0b"
18
+ "packages/engine/src/adapter/setup-three-root-adapter.ts": "98fb35199a246d99fe752ea467d48a30c71da7f4d4e6f06efa372b0c6abb5456"
19
19
  }
20
20
  },
21
21
  {
@@ -35,15 +35,15 @@
35
35
  "id": "input-value-model",
36
36
  "area": "input",
37
37
  "status": "supported-with-caveats",
38
- "claim": "F1 (spec §12) added a typed action-value model (digital/scalar/vector2/pointerDelta/pointerPosition, default digital) with real deadzone/combine rules and a valueType-mismatch throw. F2 ('Complete Device Backends') un-rejects mouse_move/gamepad_axis_pair, giving both real readers (mouse_move -> pointerDelta/vector2 off the real mouse-delta accumulator; gamepad_axis_pair -> vector2 off a coupled xAxis/yAxis stick, deadzone-rescaled like gamepad_axis), and adds touch_button/touch_stick (digital/vector2) via setTouchButton/setTouchStick. Real window focus (blur/focus) and pointer-lock state are tracked and flush held REAL-device input on loss (#144: virtual/scheduled machine input gates on enabled only and survives blur); gamepad device identity (id/mapping) is exposed via getGamepadInfo/ActionValueSource.gamepadId. F4 adds getLastActiveDevice() + an optional getPrompt device param. Caveat: pointerPosition still has no real-device backend (test_pointer_position injection only); scalar isn't fed by mouse_move (no axis-select field).",
38
+ "claim": "F1 (spec §12) added a typed action-value model (digital/scalar/vector2/pointerDelta/pointerPosition, default digital) with real deadzone/combine rules and a valueType-mismatch throw. F2 ('Complete Device Backends') un-rejects mouse_move/gamepad_axis_pair, giving both real readers (mouse_move -> pointerDelta/vector2 off the real mouse-delta accumulator; gamepad_axis_pair -> vector2 off a coupled xAxis/yAxis stick, deadzone-rescaled like gamepad_axis), and adds touch_button/touch_stick (digital/vector2) via setTouchButton/setTouchStick. Real window focus (blur/focus) and pointer-lock state are tracked (requestPointerLock / exitPointerLock) and flush held REAL-device input on loss (#144: virtual/scheduled machine input gates on enabled only and survives blur); gamepad device identity (id/mapping) is exposed via getGamepadInfo/ActionValueSource.gamepadId. F4 adds getLastActiveDevice() + an optional getPrompt device param. Device-poll getters (isDeviceKeyPressed / isDeviceMouseButtonPressed / getDeviceMouseDelta / getDeviceMousePosition / hasDeviceGamepad / isDeviceGamepadButtonPressed / getDeviceGamepadAxis) read the same keysDown/mouseButtons/gamepads stores without inventing implicit actions, and return inert at READ time when inputActive is false. Caveat: pointerPosition still has no real-device backend (test_pointer_position injection only); scalar isn't fed by mouse_move (no axis-select field).",
39
39
  "evidence": [
40
40
  "packages/engine/src/input/input-types.ts:2-40 (InputBinding union incl. mouse_move/gamepad_axis_pair/touch_button/touch_stick/test_*)",
41
- "packages/engine/src/input/input-manager.ts:609-1905 (digital/gating/typed getters/inject*/setTouch*/getGamepadInfo/F4 getLastActiveDevice+getPrompt)"
41
+ "packages/engine/src/input/input-manager.ts (digital/gating/typed getters/device-poll/inject*/setTouch*/getGamepadInfo/F4 getLastActiveDevice+getPrompt/requestPointerLock/exitPointerLock)"
42
42
  ],
43
43
  "notes": "gamepad disconnect/reconnect needs no special-cased code: poll() re-reads navigator.getGamepads() and every read skips a null slot, so a disconnected gamepad's actions go neutral with a correct isJustReleased edge for free — see input-device-backends-f2.test.ts.",
44
44
  "evidenceHashes": {
45
45
  "packages/engine/src/input/input-types.ts": "62d9005609a72b55a8214c869d1b27825b1a17b9389f3f3f5d0b04725331b382",
46
- "packages/engine/src/input/input-manager.ts": "966fc855157ae431329126258530fdfc6e59ee6884903408fd892ee02a368463"
46
+ "packages/engine/src/input/input-manager.ts": "41b1255eb18c74c022b3cc87f6e050d3fb7ee6883215e3888337ff0d94defa63"
47
47
  }
48
48
  },
49
49
  {
@@ -58,7 +58,7 @@
58
58
  "notes": "This is a compatibility integration, not the canonical animation model: project-owned native controllers may drive AnimationMixer directly. When XState is deliberately used, parallel regions use distinct layer names; the binding filters native clip tracks, uses AnimationUtils.makeClipAdditive, can register with ctx.systems, and publishes format-neutral native runtime inspection until disposal.",
59
59
  "evidenceHashes": {
60
60
  "packages/engine/src/animation/xstate-animation-meta.ts": "80376ac74819803616c4b396244a971cb9a6b2859c33409d032363bfeb3db44f",
61
- "packages/engine/src/animation/xstate-animation-binding.ts": "8551c5f7015abaffa3cd24c07cd483cc619a4090c91bbd29e9602988bebacad4"
61
+ "packages/engine/src/animation/xstate-animation-binding.ts": "8ec400c5abe1550a72adca3dfe30e57c42239fdd1eb986b8e05ec973ea3f3d7b"
62
62
  }
63
63
  },
64
64
  {
@@ -88,7 +88,7 @@
88
88
  "notes": "Scope is native-DOM React ONLY (D-N1): R3F/@pixi/react games are canvas games and ride the three.js/pixi seams instead. Generality limit: ONE proven game (react-rpg — menu-to-dungeon e2e under React 19; both game-2 candidates rejected on evidence, R-N7). Game deps must be host-installed root deps (D-N6). Open R-N6: vendored react games' raw window listeners bypass the play-mode input gate.",
89
89
  "evidenceHashes": {
90
90
  "vendor/games/verify-unaltered.mjs": "257a362030a10f4d72a3280815069fbd83f436e1619391635b6f8df2ddd6c339",
91
- "packages/editor/src/adapter-resolver.ts": "4d766e42c997a08e8bf4bbecff2efd876f2b01f3d5befb6dfbc5c848007984c0"
91
+ "packages/editor/src/adapter-resolver.ts": "433a2c22d274510302cd4be0163e5bd7400387bef7dc97bb7fab5eeac601127b"
92
92
  }
93
93
  },
94
94
  {
@@ -104,7 +104,7 @@
104
104
  "notes": "This file exists for negatives engine-api.json can't express (e.g. 'parses but does nothing'). For the full positive surface, read engine-api.json/.md — regenerated by `npm run generate-engine-manifest`, can't drift.",
105
105
  "evidenceHashes": {
106
106
  "packages/engine/schemas/engine-api.json": "cdfd395fab88575a13c74451051e6c86f8b07980152120f00caa62098ecf7917",
107
- "packages/engine/src/animation/xstate-animation-binding.ts": "8551c5f7015abaffa3cd24c07cd483cc619a4090c91bbd29e9602988bebacad4",
107
+ "packages/engine/src/animation/xstate-animation-binding.ts": "8ec400c5abe1550a72adca3dfe30e57c42239fdd1eb986b8e05ec973ea3f3d7b",
108
108
  "packages/engine/src/animation/blend-node.ts": "9e1d2018acf138bb984fd0d2c7ff1045ef00689e613abf531453af0a1ba06b2e"
109
109
  }
110
110
  }