@forgeax/engine-app 0.1.2
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/LICENSE +202 -0
- package/README.md +418 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__tests__/animation-asset-lookup.unit.test.d.ts +2 -0
- package/dist/__tests__/animation-asset-lookup.unit.test.d.ts.map +1 -0
- package/dist/__tests__/app-rebuild.test.d.ts +2 -0
- package/dist/__tests__/app-rebuild.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-types.test-d.d.ts +2 -0
- package/dist/__tests__/asset-registry-types.test-d.d.ts.map +1 -0
- package/dist/__tests__/assets-runtime-assembly.integration.test.d.ts +2 -0
- package/dist/__tests__/assets-runtime-assembly.integration.test.d.ts.map +1 -0
- package/dist/__tests__/callback-deletion.unit.test.d.ts +2 -0
- package/dist/__tests__/callback-deletion.unit.test.d.ts.map +1 -0
- package/dist/__tests__/callback-inference.test-d.d.ts +2 -0
- package/dist/__tests__/callback-inference.test-d.d.ts.map +1 -0
- package/dist/__tests__/component-introspection.unit.test.d.ts +2 -0
- package/dist/__tests__/component-introspection.unit.test.d.ts.map +1 -0
- package/dist/__tests__/consumer-migration.unit.test.d.ts +2 -0
- package/dist/__tests__/consumer-migration.unit.test.d.ts.map +1 -0
- package/dist/__tests__/create-app-features.test.d.ts +2 -0
- package/dist/__tests__/create-app-features.test.d.ts.map +1 -0
- package/dist/__tests__/create-app-listener-sync.test.d.ts +2 -0
- package/dist/__tests__/create-app-listener-sync.test.d.ts.map +1 -0
- package/dist/__tests__/create-app-lock-provider.test.d.ts +2 -0
- package/dist/__tests__/create-app-lock-provider.test.d.ts.map +1 -0
- package/dist/__tests__/create-app-plugin.test.d.ts +2 -0
- package/dist/__tests__/create-app-plugin.test.d.ts.map +1 -0
- package/dist/__tests__/create-app-stop.test.d.ts +2 -0
- package/dist/__tests__/create-app-stop.test.d.ts.map +1 -0
- package/dist/__tests__/create-app-time.unit.test.d.ts +2 -0
- package/dist/__tests__/create-app-time.unit.test.d.ts.map +1 -0
- package/dist/__tests__/create-app.test-d.d.ts +2 -0
- package/dist/__tests__/create-app.test-d.d.ts.map +1 -0
- package/dist/__tests__/create-app.test.d.ts +2 -0
- package/dist/__tests__/create-app.test.d.ts.map +1 -0
- package/dist/__tests__/create-renderer-error.integration.test.d.ts +2 -0
- package/dist/__tests__/create-renderer-error.integration.test.d.ts.map +1 -0
- package/dist/__tests__/create-renderer-lifecycle.integration.test.d.ts +2 -0
- package/dist/__tests__/create-renderer-lifecycle.integration.test.d.ts.map +1 -0
- package/dist/__tests__/docs-gate-green.unit.test.d.ts +2 -0
- package/dist/__tests__/docs-gate-green.unit.test.d.ts.map +1 -0
- package/dist/__tests__/draw-source-headless-full-chain.test.d.ts +2 -0
- package/dist/__tests__/draw-source-headless-full-chain.test.d.ts.map +1 -0
- package/dist/__tests__/draw-source-injected-world-update-visibility.test.d.ts +2 -0
- package/dist/__tests__/draw-source-injected-world-update-visibility.test.d.ts.map +1 -0
- package/dist/__tests__/draw-source-single-world-regression.test.d.ts +2 -0
- package/dist/__tests__/draw-source-single-world-regression.test.d.ts.map +1 -0
- package/dist/__tests__/ecs-import.unit.test.d.ts +2 -0
- package/dist/__tests__/ecs-import.unit.test.d.ts.map +1 -0
- package/dist/__tests__/engine-worker-bootstrap-channel.unit.test.d.ts +2 -0
- package/dist/__tests__/engine-worker-bootstrap-channel.unit.test.d.ts.map +1 -0
- package/dist/__tests__/engine-worker-world-swap.unit.test.d.ts +2 -0
- package/dist/__tests__/engine-worker-world-swap.unit.test.d.ts.map +1 -0
- package/dist/__tests__/errors-pointer-lock-failed.test.d.ts +2 -0
- package/dist/__tests__/errors-pointer-lock-failed.test.d.ts.map +1 -0
- package/dist/__tests__/execution-bootstrap-isolation.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-bootstrap-isolation.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-error-detail.test-d.d.ts +2 -0
- package/dist/__tests__/execution-error-detail.test-d.d.ts.map +1 -0
- package/dist/__tests__/execution-error-detail.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-error-detail.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-frame-order.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-frame-order.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-frame-protocol.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-frame-protocol.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-input-sample.contract.test.d.ts +2 -0
- package/dist/__tests__/execution-input-sample.contract.test.d.ts.map +1 -0
- package/dist/__tests__/execution-measurement.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-measurement.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-public-api.test-d.d.ts +2 -0
- package/dist/__tests__/execution-public-api.test-d.d.ts.map +1 -0
- package/dist/__tests__/execution-public-api.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-public-api.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-rebuild.contract.test.d.ts +2 -0
- package/dist/__tests__/execution-rebuild.contract.test.d.ts.map +1 -0
- package/dist/__tests__/execution-report.contract.test.d.ts +2 -0
- package/dist/__tests__/execution-report.contract.test.d.ts.map +1 -0
- package/dist/__tests__/execution-report.types.test-d.d.ts +2 -0
- package/dist/__tests__/execution-report.types.test-d.d.ts.map +1 -0
- package/dist/__tests__/execution-schema-freshness.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-schema-freshness.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-selector.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-selector.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-shared-kernel.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-shared-kernel.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-world-health-owner.test-d.d.ts +2 -0
- package/dist/__tests__/execution-world-health-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/fallback-camera.unit.test.d.ts +2 -0
- package/dist/__tests__/fallback-camera.unit.test.d.ts.map +1 -0
- package/dist/__tests__/frame-loop-time-elapsed.test.d.ts +2 -0
- package/dist/__tests__/frame-loop-time-elapsed.test.d.ts.map +1 -0
- package/dist/__tests__/frame-loop-time.unit.test.d.ts +2 -0
- package/dist/__tests__/frame-loop-time.unit.test.d.ts.map +1 -0
- package/dist/__tests__/frame-loop-world-array.test.d.ts +2 -0
- package/dist/__tests__/frame-loop-world-array.test.d.ts.map +1 -0
- package/dist/__tests__/kernel-worker-runtime.unit.test.d.ts +2 -0
- package/dist/__tests__/kernel-worker-runtime.unit.test.d.ts.map +1 -0
- package/dist/__tests__/migration-surface-inventory.unit.test.d.ts +2 -0
- package/dist/__tests__/migration-surface-inventory.unit.test.d.ts.map +1 -0
- package/dist/__tests__/plugin-policy.integration.test.d.ts +2 -0
- package/dist/__tests__/plugin-policy.integration.test.d.ts.map +1 -0
- package/dist/__tests__/plugin-profiles.unit.test.d.ts +2 -0
- package/dist/__tests__/plugin-profiles.unit.test.d.ts.map +1 -0
- package/dist/__tests__/profiler-default-off.test.d.ts +2 -0
- package/dist/__tests__/profiler-default-off.test.d.ts.map +1 -0
- package/dist/__tests__/profiler-frame-token.test.d.ts +2 -0
- package/dist/__tests__/profiler-frame-token.test.d.ts.map +1 -0
- package/dist/__tests__/profiler-phase-catalog.test.d.ts +2 -0
- package/dist/__tests__/profiler-phase-catalog.test.d.ts.map +1 -0
- package/dist/__tests__/profiler-phase-integration.test.d.ts +2 -0
- package/dist/__tests__/profiler-phase-integration.test.d.ts.map +1 -0
- package/dist/__tests__/profiler-remote-bridge.test.d.ts +2 -0
- package/dist/__tests__/profiler-remote-bridge.test.d.ts.map +1 -0
- package/dist/__tests__/remote-integration.test.d.ts +2 -0
- package/dist/__tests__/remote-integration.test.d.ts.map +1 -0
- package/dist/__tests__/remote-serve-flag.test.d.ts +2 -0
- package/dist/__tests__/remote-serve-flag.test.d.ts.map +1 -0
- package/dist/__tests__/rhi-capture-capability.test-d.d.ts +2 -0
- package/dist/__tests__/rhi-capture-capability.test-d.d.ts.map +1 -0
- package/dist/__tests__/rhi-capture-host.integration.test.d.ts +2 -0
- package/dist/__tests__/rhi-capture-host.integration.test.d.ts.map +1 -0
- package/dist/__tests__/rhi-capture.browser.test.d.ts +2 -0
- package/dist/__tests__/rhi-capture.browser.test.d.ts.map +1 -0
- package/dist/__tests__/surface-handoff.test.d.ts +2 -0
- package/dist/__tests__/surface-handoff.test.d.ts.map +1 -0
- package/dist/__tests__/types-m2.test-d.d.ts +2 -0
- package/dist/__tests__/types-m2.test-d.d.ts.map +1 -0
- package/dist/__tests__/visibility-remote.integration.test.d.ts +2 -0
- package/dist/__tests__/visibility-remote.integration.test.d.ts.map +1 -0
- package/dist/__tests__/worker-execution-stop.test.d.ts +2 -0
- package/dist/__tests__/worker-execution-stop.test.d.ts.map +1 -0
- package/dist/animation-asset-lookup.d.ts +8 -0
- package/dist/animation-asset-lookup.d.ts.map +1 -0
- package/dist/assets-runtime-assembly.d.ts +19 -0
- package/dist/assets-runtime-assembly.d.ts.map +1 -0
- package/dist/canvas-policy.d.ts +4 -0
- package/dist/canvas-policy.d.ts.map +1 -0
- package/dist/create-app.d.ts +57 -0
- package/dist/create-app.d.ts.map +1 -0
- package/dist/engine-worker-runtime.mjs +884 -0
- package/dist/engine-worker-runtime.mjs.map +1 -0
- package/dist/errors.d.ts +263 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/execution/attached-world-swap.d.ts +21 -0
- package/dist/execution/attached-world-swap.d.ts.map +1 -0
- package/dist/execution/bootstrap-entry.d.ts +30 -0
- package/dist/execution/bootstrap-entry.d.ts.map +1 -0
- package/dist/execution/capabilities.d.ts +5 -0
- package/dist/execution/capabilities.d.ts.map +1 -0
- package/dist/execution/control.d.ts +14 -0
- package/dist/execution/control.d.ts.map +1 -0
- package/dist/execution/engine-worker-runtime.d.ts +2 -0
- package/dist/execution/engine-worker-runtime.d.ts.map +1 -0
- package/dist/execution/engine-worker.d.ts +23 -0
- package/dist/execution/engine-worker.d.ts.map +1 -0
- package/dist/execution/host-controller.d.ts +16 -0
- package/dist/execution/host-controller.d.ts.map +1 -0
- package/dist/execution/index.d.ts +10 -0
- package/dist/execution/index.d.ts.map +1 -0
- package/dist/execution/kernel-pool.d.ts +14 -0
- package/dist/execution/kernel-pool.d.ts.map +1 -0
- package/dist/execution/kernel-worker-runtime.d.ts +2 -0
- package/dist/execution/kernel-worker-runtime.d.ts.map +1 -0
- package/dist/execution/measurement.d.ts +7 -0
- package/dist/execution/measurement.d.ts.map +1 -0
- package/dist/execution/protocol.d.ts +84 -0
- package/dist/execution/protocol.d.ts.map +1 -0
- package/dist/execution/report.d.ts +5 -0
- package/dist/execution/report.d.ts.map +1 -0
- package/dist/execution/schema.d.ts +4 -0
- package/dist/execution/schema.d.ts.map +1 -0
- package/dist/execution/selector.d.ts +10 -0
- package/dist/execution/selector.d.ts.map +1 -0
- package/dist/execution/types.d.ts +107 -0
- package/dist/execution/types.d.ts.map +1 -0
- package/dist/fallback-camera.d.ts +8 -0
- package/dist/fallback-camera.d.ts.map +1 -0
- package/dist/game-context.d.ts +98 -0
- package/dist/game-context.d.ts.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +3985 -0
- package/dist/index.mjs.map +1 -0
- package/dist/input-map-plugin.d.ts +5 -0
- package/dist/input-map-plugin.d.ts.map +1 -0
- package/dist/input-plugin.d.ts +4 -0
- package/dist/input-plugin.d.ts.map +1 -0
- package/dist/internal/assembled-engine-profile.d.ts +5 -0
- package/dist/internal/assembled-engine-profile.d.ts.map +1 -0
- package/dist/internal/assets-world-plugin.d.ts +21 -0
- package/dist/internal/assets-world-plugin.d.ts.map +1 -0
- package/dist/internal/browser-remote-bridge.d.ts +25 -0
- package/dist/internal/browser-remote-bridge.d.ts.map +1 -0
- package/dist/internal/browser-rhi-debug-runtime.d.ts +18 -0
- package/dist/internal/browser-rhi-debug-runtime.d.ts.map +1 -0
- package/dist/internal/component-introspection.d.ts +24 -0
- package/dist/internal/component-introspection.d.ts.map +1 -0
- package/dist/internal/ecs-import.d.ts +6 -0
- package/dist/internal/ecs-import.d.ts.map +1 -0
- package/dist/internal/engine-profile-common.d.ts +9 -0
- package/dist/internal/engine-profile-common.d.ts.map +1 -0
- package/dist/internal/error-fanout.d.ts +66 -0
- package/dist/internal/error-fanout.d.ts.map +1 -0
- package/dist/internal/frame-loop.d.ts +35 -0
- package/dist/internal/frame-loop.d.ts.map +1 -0
- package/dist/internal/input-attach.d.ts +44 -0
- package/dist/internal/input-attach.d.ts.map +1 -0
- package/dist/internal/main-engine-profile.d.ts +15 -0
- package/dist/internal/main-engine-profile.d.ts.map +1 -0
- package/dist/internal/remote-serve-flag.d.ts +23 -0
- package/dist/internal/remote-serve-flag.d.ts.map +1 -0
- package/dist/internal/remote-server-plugin.d.ts +8 -0
- package/dist/internal/remote-server-plugin.d.ts.map +1 -0
- package/dist/internal/rhi-capture.d.ts +19 -0
- package/dist/internal/rhi-capture.d.ts.map +1 -0
- package/dist/internal/rhi-debug-flag.d.ts +18 -0
- package/dist/internal/rhi-debug-flag.d.ts.map +1 -0
- package/dist/internal/worker-engine-profile.d.ts +13 -0
- package/dist/internal/worker-engine-profile.d.ts.map +1 -0
- package/dist/kernel-worker-runtime.mjs +69 -0
- package/dist/kernel-worker-runtime.mjs.map +1 -0
- package/dist/load-game-errors.d.ts +131 -0
- package/dist/load-game-errors.d.ts.map +1 -0
- package/dist/load-game.d.ts +34 -0
- package/dist/load-game.d.ts.map +1 -0
- package/dist/plugin-factories.d.ts +3 -0
- package/dist/plugin-factories.d.ts.map +1 -0
- package/dist/tool-preview/bootstrap.d.ts +113 -0
- package/dist/tool-preview/bootstrap.d.ts.map +1 -0
- package/dist/tool-preview/evidence.d.ts +20 -0
- package/dist/tool-preview/evidence.d.ts.map +1 -0
- package/dist/tool-preview/framing.d.ts +13 -0
- package/dist/tool-preview/framing.d.ts.map +1 -0
- package/dist/tool-preview/recipe.d.ts +54 -0
- package/dist/tool-preview/recipe.d.ts.map +1 -0
- package/dist/types.d.ts +444 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +86 -0
- package/schema/execution-report.schema.json +168 -0
- package/src/__tests__/animation-asset-lookup.unit.test.ts +20 -0
- package/src/__tests__/app-rebuild.test.ts +31 -0
- package/src/__tests__/asset-registry-types.test-d.ts +16 -0
- package/src/__tests__/assets-runtime-assembly.integration.test.ts +68 -0
- package/src/__tests__/callback-deletion.unit.test.ts +14 -0
- package/src/__tests__/callback-inference.test-d.ts +32 -0
- package/src/__tests__/component-introspection.unit.test.ts +56 -0
- package/src/__tests__/consumer-migration.unit.test.ts +21 -0
- package/src/__tests__/create-app-features.test.ts +40 -0
- package/src/__tests__/create-app-listener-sync.test.ts +377 -0
- package/src/__tests__/create-app-lock-provider.test.ts +212 -0
- package/src/__tests__/create-app-plugin.test.ts +217 -0
- package/src/__tests__/create-app-stop.test.ts +617 -0
- package/src/__tests__/create-app-time.unit.test.ts +26 -0
- package/src/__tests__/create-app.test-d.ts +82 -0
- package/src/__tests__/create-app.test.ts +346 -0
- package/src/__tests__/create-renderer-error.integration.test.ts +24 -0
- package/src/__tests__/create-renderer-lifecycle.integration.test.ts +19 -0
- package/src/__tests__/docs-gate-green.unit.test.ts +61 -0
- package/src/__tests__/draw-source-headless-full-chain.test.ts +226 -0
- package/src/__tests__/draw-source-injected-world-update-visibility.test.ts +182 -0
- package/src/__tests__/draw-source-single-world-regression.test.ts +185 -0
- package/src/__tests__/ecs-import.unit.test.ts +40 -0
- package/src/__tests__/engine-worker-bootstrap-channel.unit.test.ts +74 -0
- package/src/__tests__/engine-worker-world-swap.unit.test.ts +76 -0
- package/src/__tests__/errors-pointer-lock-failed.test.ts +268 -0
- package/src/__tests__/execution-bootstrap-isolation.unit.test.ts +80 -0
- package/src/__tests__/execution-error-detail.test-d.ts +13 -0
- package/src/__tests__/execution-error-detail.unit.test.ts +35 -0
- package/src/__tests__/execution-frame-order.unit.test.ts +38 -0
- package/src/__tests__/execution-frame-protocol.unit.test.ts +61 -0
- package/src/__tests__/execution-input-sample.contract.test.ts +23 -0
- package/src/__tests__/execution-measurement.unit.test.ts +20 -0
- package/src/__tests__/execution-public-api.test-d.ts +10 -0
- package/src/__tests__/execution-public-api.unit.test.ts +9 -0
- package/src/__tests__/execution-rebuild.contract.test.ts +36 -0
- package/src/__tests__/execution-report.contract.test.ts +50 -0
- package/src/__tests__/execution-report.types.test-d.ts +16 -0
- package/src/__tests__/execution-schema-freshness.unit.test.ts +20 -0
- package/src/__tests__/execution-selector.unit.test.ts +72 -0
- package/src/__tests__/execution-shared-kernel.unit.test.ts +180 -0
- package/src/__tests__/execution-world-health-owner.test-d.ts +9 -0
- package/src/__tests__/fallback-camera.unit.test.ts +32 -0
- package/src/__tests__/frame-loop-time-elapsed.test.ts +115 -0
- package/src/__tests__/frame-loop-time.unit.test.ts +61 -0
- package/src/__tests__/frame-loop-world-array.test.ts +441 -0
- package/src/__tests__/kernel-worker-runtime.unit.test.ts +50 -0
- package/src/__tests__/migration-surface-inventory.unit.test.ts +13 -0
- package/src/__tests__/plugin-policy.integration.test.ts +15 -0
- package/src/__tests__/plugin-profiles.unit.test.ts +190 -0
- package/src/__tests__/profiler-default-off.test.ts +53 -0
- package/src/__tests__/profiler-frame-token.test.ts +248 -0
- package/src/__tests__/profiler-phase-catalog.test.ts +30 -0
- package/src/__tests__/profiler-phase-integration.test.ts +129 -0
- package/src/__tests__/profiler-remote-bridge.test.ts +31 -0
- package/src/__tests__/remote-integration.test.ts +224 -0
- package/src/__tests__/remote-serve-flag.test.ts +24 -0
- package/src/__tests__/rhi-capture-capability.test-d.ts +22 -0
- package/src/__tests__/rhi-capture-host.integration.test.ts +157 -0
- package/src/__tests__/rhi-capture.browser.test.ts +51 -0
- package/src/__tests__/surface-handoff.test.ts +92 -0
- package/src/__tests__/types-m2.test-d.ts +81 -0
- package/src/__tests__/visibility-remote.integration.test.ts +119 -0
- package/src/__tests__/worker-execution-stop.test.ts +116 -0
- package/src/animation-asset-lookup.ts +13 -0
- package/src/assets-runtime-assembly.ts +70 -0
- package/src/canvas-policy.ts +16 -0
- package/src/create-app.ts +1351 -0
- package/src/errors.ts +454 -0
- package/src/execution/attached-world-swap.ts +67 -0
- package/src/execution/bootstrap-entry.ts +122 -0
- package/src/execution/capabilities.ts +102 -0
- package/src/execution/control.ts +52 -0
- package/src/execution/engine-worker-runtime.ts +400 -0
- package/src/execution/engine-worker.ts +161 -0
- package/src/execution/host-controller.ts +454 -0
- package/src/execution/index.ts +43 -0
- package/src/execution/kernel-pool.ts +236 -0
- package/src/execution/kernel-worker-runtime.ts +103 -0
- package/src/execution/measurement.ts +39 -0
- package/src/execution/protocol.ts +131 -0
- package/src/execution/report.ts +66 -0
- package/src/execution/schema.ts +168 -0
- package/src/execution/selector.ts +98 -0
- package/src/execution/types.ts +146 -0
- package/src/fallback-camera.ts +20 -0
- package/src/game-context.ts +126 -0
- package/src/index.ts +176 -0
- package/src/input-map-plugin.ts +21 -0
- package/src/input-plugin.ts +26 -0
- package/src/internal/assembled-engine-profile.ts +22 -0
- package/src/internal/assets-world-plugin.ts +79 -0
- package/src/internal/browser-remote-bridge.ts +267 -0
- package/src/internal/browser-rhi-debug-runtime.ts +67 -0
- package/src/internal/component-introspection.ts +116 -0
- package/src/internal/ecs-import.ts +41 -0
- package/src/internal/engine-profile-common.ts +10 -0
- package/src/internal/error-fanout.ts +129 -0
- package/src/internal/frame-loop.ts +525 -0
- package/src/internal/input-attach.ts +104 -0
- package/src/internal/main-engine-profile.ts +80 -0
- package/src/internal/remote-serve-flag.ts +50 -0
- package/src/internal/remote-server-plugin.ts +16 -0
- package/src/internal/rhi-capture.ts +211 -0
- package/src/internal/rhi-debug-flag.ts +40 -0
- package/src/internal/worker-engine-profile.ts +47 -0
- package/src/load-game-errors.ts +211 -0
- package/src/load-game.ts +115 -0
- package/src/plugin-factories.ts +2 -0
- package/src/tool-preview/bootstrap.ts +848 -0
- package/src/tool-preview/evidence.ts +59 -0
- package/src/tool-preview/framing.ts +52 -0
- package/src/tool-preview/recipe.ts +137 -0
- package/src/types.ts +505 -0
- package/src/vite-env.d.ts +12 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EXECUTION_CAPABILITY_NAMES,
|
|
3
|
+
type ExecutionCapabilities,
|
|
4
|
+
type ExecutionCapabilityName,
|
|
5
|
+
} from './types';
|
|
6
|
+
|
|
7
|
+
export function unavailableExecutionCapabilities(reason: string): ExecutionCapabilities {
|
|
8
|
+
return Object.fromEntries(
|
|
9
|
+
EXECUTION_CAPABILITY_NAMES.map((name) => [name, { available: false, reason }]),
|
|
10
|
+
) as unknown as ExecutionCapabilities;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function missingExecutionCapabilities(
|
|
14
|
+
capabilities: ExecutionCapabilities,
|
|
15
|
+
required: readonly ExecutionCapabilityName[],
|
|
16
|
+
): readonly ExecutionCapabilityName[] {
|
|
17
|
+
return required.filter((name) => !capabilities[name].available);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface WorkerProbeResult {
|
|
21
|
+
readonly workerAnimationFrame: boolean;
|
|
22
|
+
readonly workerWebGpu: boolean;
|
|
23
|
+
readonly atomicsWait: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function probeWorker(timeoutMs: number): Promise<WorkerProbeResult> {
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
const source = `postMessage({workerAnimationFrame:typeof requestAnimationFrame==='function',workerWebGpu:typeof navigator==='object'&&navigator.gpu!==undefined,atomicsWait:typeof Atomics==='object'&&typeof Atomics.wait==='function'})`;
|
|
29
|
+
const url = URL.createObjectURL(new Blob([source], { type: 'text/javascript' }));
|
|
30
|
+
const worker = new Worker(url);
|
|
31
|
+
const finish = (result: WorkerProbeResult): void => {
|
|
32
|
+
worker.terminate();
|
|
33
|
+
URL.revokeObjectURL(url);
|
|
34
|
+
resolve(result);
|
|
35
|
+
};
|
|
36
|
+
const timeout = setTimeout(
|
|
37
|
+
() => finish({ workerAnimationFrame: false, workerWebGpu: false, atomicsWait: false }),
|
|
38
|
+
timeoutMs,
|
|
39
|
+
);
|
|
40
|
+
worker.onmessage = (event: MessageEvent<WorkerProbeResult>) => {
|
|
41
|
+
clearTimeout(timeout);
|
|
42
|
+
finish(event.data);
|
|
43
|
+
};
|
|
44
|
+
worker.onerror = () => {
|
|
45
|
+
clearTimeout(timeout);
|
|
46
|
+
finish({ workerAnimationFrame: false, workerWebGpu: false, atomicsWait: false });
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function probeExecutionCapabilities(
|
|
52
|
+
canvas: HTMLCanvasElement,
|
|
53
|
+
timeoutMs = 2_000,
|
|
54
|
+
): Promise<ExecutionCapabilities> {
|
|
55
|
+
const workerAvailable = typeof Worker === 'function';
|
|
56
|
+
const offscreenAvailable = typeof canvas.transferControlToOffscreen === 'function';
|
|
57
|
+
const isolated = globalThis.crossOriginIsolated === true;
|
|
58
|
+
const sabAvailable = typeof SharedArrayBuffer === 'function';
|
|
59
|
+
const worker = workerAvailable
|
|
60
|
+
? await probeWorker(timeoutMs)
|
|
61
|
+
: { workerAnimationFrame: false, workerWebGpu: false, atomicsWait: false };
|
|
62
|
+
return {
|
|
63
|
+
worker: {
|
|
64
|
+
available: workerAvailable,
|
|
65
|
+
reason: workerAvailable ? 'Worker constructor observed' : 'Worker constructor unavailable',
|
|
66
|
+
},
|
|
67
|
+
offscreenCanvas: {
|
|
68
|
+
available: offscreenAvailable,
|
|
69
|
+
reason: offscreenAvailable
|
|
70
|
+
? 'canvas transferControlToOffscreen observed'
|
|
71
|
+
: 'canvas cannot transfer to OffscreenCanvas',
|
|
72
|
+
},
|
|
73
|
+
workerAnimationFrame: {
|
|
74
|
+
available: worker.workerAnimationFrame,
|
|
75
|
+
reason: worker.workerAnimationFrame
|
|
76
|
+
? 'requestAnimationFrame observed in DedicatedWorker'
|
|
77
|
+
: 'requestAnimationFrame unavailable in DedicatedWorker',
|
|
78
|
+
},
|
|
79
|
+
workerWebGpu: {
|
|
80
|
+
available: worker.workerWebGpu,
|
|
81
|
+
reason: worker.workerWebGpu
|
|
82
|
+
? 'navigator.gpu observed in DedicatedWorker'
|
|
83
|
+
: 'navigator.gpu unavailable in DedicatedWorker',
|
|
84
|
+
},
|
|
85
|
+
crossOriginIsolated: {
|
|
86
|
+
available: isolated,
|
|
87
|
+
reason: isolated ? 'crossOriginIsolated is true' : 'crossOriginIsolated is false',
|
|
88
|
+
},
|
|
89
|
+
sharedArrayBuffer: {
|
|
90
|
+
available: sabAvailable,
|
|
91
|
+
reason: sabAvailable
|
|
92
|
+
? 'SharedArrayBuffer constructor observed'
|
|
93
|
+
: 'SharedArrayBuffer constructor unavailable',
|
|
94
|
+
},
|
|
95
|
+
atomicsWait: {
|
|
96
|
+
available: worker.atomicsWait,
|
|
97
|
+
reason: worker.atomicsWait
|
|
98
|
+
? 'Atomics.wait observed in DedicatedWorker'
|
|
99
|
+
: 'Atomics.wait unavailable in DedicatedWorker',
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { AudioState } from '@forgeax/engine-audio';
|
|
2
|
+
import { err } from '@forgeax/engine-types';
|
|
3
|
+
import { APP_ERROR_HINTS, APP_EXPECTED, AppError } from '../errors';
|
|
4
|
+
import { executionAudioReport } from './report';
|
|
5
|
+
import type { ExecutionControl, ExecutionEngineHealth, ExecutionReport } from './types';
|
|
6
|
+
|
|
7
|
+
export function cloneExecutionReport(report: ExecutionReport): ExecutionReport {
|
|
8
|
+
return structuredClone(report);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface LocalExecutionControl extends ExecutionControl {
|
|
12
|
+
setEngineHealth(health: ExecutionEngineHealth): void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface LocalExecutionReportProviders {
|
|
16
|
+
readonly audio?: () => AudioState;
|
|
17
|
+
readonly world?: () => ExecutionReport['world'];
|
|
18
|
+
/** Host-owned poisoned-World replacement transaction. */
|
|
19
|
+
readonly rebuild?: ExecutionControl['rebuild'];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function createLocalExecutionControl(
|
|
23
|
+
initial: ExecutionReport,
|
|
24
|
+
providers: LocalExecutionReportProviders = {},
|
|
25
|
+
): LocalExecutionControl {
|
|
26
|
+
let current = cloneExecutionReport(initial);
|
|
27
|
+
return {
|
|
28
|
+
setEngineHealth: (health) => {
|
|
29
|
+
current = { ...current, engine: { ...current.engine, health } };
|
|
30
|
+
},
|
|
31
|
+
report: () =>
|
|
32
|
+
cloneExecutionReport({
|
|
33
|
+
...current,
|
|
34
|
+
world: providers.world?.() ?? current.world,
|
|
35
|
+
audio: executionAudioReport(providers.audio?.()),
|
|
36
|
+
}),
|
|
37
|
+
rebuild: async () => {
|
|
38
|
+
if (providers.rebuild !== undefined) return providers.rebuild();
|
|
39
|
+
return err(
|
|
40
|
+
new AppError({
|
|
41
|
+
code: 'app-execution-rebuild-failed',
|
|
42
|
+
expected: APP_EXPECTED['app-execution-rebuild-failed'],
|
|
43
|
+
hint: APP_ERROR_HINTS['app-execution-rebuild-failed'],
|
|
44
|
+
detail: {
|
|
45
|
+
worldIdentity: current.world.identity,
|
|
46
|
+
cause: new Error('This local assembly has no bootstrap module.'),
|
|
47
|
+
},
|
|
48
|
+
}),
|
|
49
|
+
);
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
import { createAssetRegistry, createCatalogSource } from '@forgeax/engine-assets-runtime';
|
|
2
|
+
import { type AudioIntent, createAudioIntentBackend } from '@forgeax/engine-audio';
|
|
3
|
+
import { createWorldContext, World } from '@forgeax/engine-ecs';
|
|
4
|
+
import type { SharedKernelExecutor } from '@forgeax/engine-ecs/shared';
|
|
5
|
+
import type { InputBackend, InputBackendSample } from '@forgeax/engine-input';
|
|
6
|
+
import type { Context, Plugin } from '@forgeax/engine-plugin';
|
|
7
|
+
import type { Renderer } from '@forgeax/engine-render';
|
|
8
|
+
import { createRenderer } from '@forgeax/engine-runtime';
|
|
9
|
+
import { createAnimationPayloadLookup } from '../animation-asset-lookup';
|
|
10
|
+
import { type AssetRuntimeAssembly, assembleAssetRuntime } from '../assets-runtime-assembly';
|
|
11
|
+
import { syncCameraAspect } from '../canvas-policy';
|
|
12
|
+
import { workerEngineProfile } from '../internal/worker-engine-profile';
|
|
13
|
+
import { commitAttachedWorld, SerializedRebuildQueue } from './attached-world-swap';
|
|
14
|
+
import {
|
|
15
|
+
executionBootstrapHostPlugin,
|
|
16
|
+
type PreparedExecutionBootstrap,
|
|
17
|
+
prepareBootstrapEntry,
|
|
18
|
+
} from './bootstrap-entry';
|
|
19
|
+
import { createKernelPool, type KernelPool } from './kernel-pool';
|
|
20
|
+
import type {
|
|
21
|
+
EngineToHostMessage,
|
|
22
|
+
ExecutionFrameMessage,
|
|
23
|
+
ExecutionInitMessage,
|
|
24
|
+
ExecutionRebuildMessage,
|
|
25
|
+
HostToEngineMessage,
|
|
26
|
+
} from './protocol';
|
|
27
|
+
|
|
28
|
+
const scope = globalThis as unknown as {
|
|
29
|
+
postMessage(message: EngineToHostMessage): void;
|
|
30
|
+
onmessage: ((event: MessageEvent<HostToEngineMessage>) => void) | null;
|
|
31
|
+
close(): void;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
let renderer: Renderer | undefined;
|
|
35
|
+
let currentSample: InputBackendSample = {
|
|
36
|
+
downKeys: new Set(),
|
|
37
|
+
upKeys: new Set(),
|
|
38
|
+
buttons: [false, false, false],
|
|
39
|
+
movementX: 0,
|
|
40
|
+
movementY: 0,
|
|
41
|
+
wheelDelta: 0,
|
|
42
|
+
focused: true,
|
|
43
|
+
pointerLocked: false,
|
|
44
|
+
};
|
|
45
|
+
let lastFrameId = 0;
|
|
46
|
+
let engineCanvas: OffscreenCanvas | undefined;
|
|
47
|
+
interface WorkerRealm {
|
|
48
|
+
readonly world: World;
|
|
49
|
+
readonly init: ExecutionInitMessage;
|
|
50
|
+
pendingAudioIntents: AudioIntent[];
|
|
51
|
+
kernelPool: KernelPool | undefined;
|
|
52
|
+
pluginContext: Context | undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let realm: WorkerRealm | undefined;
|
|
56
|
+
const rebuildQueue = new SerializedRebuildQueue();
|
|
57
|
+
const inputBackend: InputBackend = {
|
|
58
|
+
sample: () => currentSample,
|
|
59
|
+
detach: () => {},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
function sharedKernelPlugin(target: WorkerRealm): Plugin {
|
|
63
|
+
return {
|
|
64
|
+
name: 'shared-kernel-executor',
|
|
65
|
+
inject: ['world'],
|
|
66
|
+
apply(ctx) {
|
|
67
|
+
const executor: SharedKernelExecutor = {
|
|
68
|
+
warmup(kernel) {
|
|
69
|
+
target.kernelPool ??= createKernelPool();
|
|
70
|
+
target.kernelPool.warmup?.(kernel);
|
|
71
|
+
},
|
|
72
|
+
execute(kernel, spans) {
|
|
73
|
+
target.kernelPool ??= createKernelPool();
|
|
74
|
+
return target.kernelPool.execute(kernel, spans);
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
ctx.effect(() => {
|
|
78
|
+
ctx.world.insertResource('SharedKernelExecutor', executor);
|
|
79
|
+
return () => {
|
|
80
|
+
ctx.world.removeResource('SharedKernelExecutor');
|
|
81
|
+
target.kernelPool?.dispose();
|
|
82
|
+
target.kernelPool = undefined;
|
|
83
|
+
};
|
|
84
|
+
}, 'execution/shared-kernel');
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function serializableCause(cause: unknown): { readonly name: string; readonly message: string } {
|
|
90
|
+
return cause instanceof Error
|
|
91
|
+
? { name: cause.name, message: cause.message }
|
|
92
|
+
: { name: 'Error', message: String(cause) };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function serializableDetail(cause: unknown): unknown {
|
|
96
|
+
if (cause instanceof Error) return serializableCause(cause);
|
|
97
|
+
if (Array.isArray(cause)) return cause.map(serializableDetail);
|
|
98
|
+
if (typeof cause === 'object' && cause !== null) {
|
|
99
|
+
const named = cause as { readonly name?: unknown; readonly message?: unknown };
|
|
100
|
+
if (typeof named.name === 'string' || typeof named.message === 'string') {
|
|
101
|
+
return {
|
|
102
|
+
name: typeof named.name === 'string' ? named.name : 'Error',
|
|
103
|
+
message: typeof named.message === 'string' ? named.message : String(cause),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return Object.fromEntries(
|
|
107
|
+
Object.entries(cause).map(([key, value]) => [key, serializableDetail(value)]),
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
return cause;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function postFault(
|
|
114
|
+
source: 'bootstrap' | 'runtime' | 'world' | 'rebuild',
|
|
115
|
+
code: string,
|
|
116
|
+
expected: string,
|
|
117
|
+
hint: string,
|
|
118
|
+
cause: unknown,
|
|
119
|
+
partialWrite = false,
|
|
120
|
+
): void {
|
|
121
|
+
scope.postMessage({
|
|
122
|
+
kind: 'fault',
|
|
123
|
+
worldIdentity: realm?.world.identity ?? null,
|
|
124
|
+
source,
|
|
125
|
+
code,
|
|
126
|
+
expected,
|
|
127
|
+
hint,
|
|
128
|
+
detail: serializableDetail(cause),
|
|
129
|
+
partialWrite,
|
|
130
|
+
retryable: false,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function disposeRealm(target: WorkerRealm): Promise<void> {
|
|
135
|
+
await target.pluginContext?.fiber.dispose();
|
|
136
|
+
target.pluginContext = undefined;
|
|
137
|
+
target.pendingAudioIntents = [];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function postBootstrapFault(error: {
|
|
141
|
+
readonly code: string;
|
|
142
|
+
readonly expected: string;
|
|
143
|
+
readonly hint: string;
|
|
144
|
+
readonly detail: unknown;
|
|
145
|
+
}): void {
|
|
146
|
+
postFault('bootstrap', error.code, error.expected, error.hint, error.detail);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function createRealm(init: ExecutionInitMessage): Promise<boolean> {
|
|
150
|
+
const preparedResult = await prepareBootstrapEntry(init.bootstrapUrl, init.bootstrapData);
|
|
151
|
+
if (!preparedResult.ok) {
|
|
152
|
+
postBootstrapFault(preparedResult.error);
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
const prepared: PreparedExecutionBootstrap = preparedResult.value;
|
|
156
|
+
const nextWorld = new World({
|
|
157
|
+
...(init.time !== undefined ? { time: init.time } : {}),
|
|
158
|
+
storage: init.tier === 'shared' ? 'shared' : 'local',
|
|
159
|
+
});
|
|
160
|
+
const candidate: WorkerRealm = {
|
|
161
|
+
world: nextWorld,
|
|
162
|
+
init,
|
|
163
|
+
pendingAudioIntents: [],
|
|
164
|
+
kernelPool: undefined,
|
|
165
|
+
pluginContext: undefined,
|
|
166
|
+
};
|
|
167
|
+
const audioBackend = createAudioIntentBackend({
|
|
168
|
+
emit: (intent) => candidate.pendingAudioIntents.push(intent),
|
|
169
|
+
});
|
|
170
|
+
let candidateRenderer: Renderer | undefined;
|
|
171
|
+
let assetAssembly: AssetRuntimeAssembly | undefined;
|
|
172
|
+
let rendererLifecycleTransferred = false;
|
|
173
|
+
const previousRenderer = renderer;
|
|
174
|
+
let previousSurfaceReleased = false;
|
|
175
|
+
try {
|
|
176
|
+
if (previousRenderer !== undefined) {
|
|
177
|
+
const released = previousRenderer.releaseSurface();
|
|
178
|
+
if (!released.ok) throw released.error;
|
|
179
|
+
previousSurfaceReleased = true;
|
|
180
|
+
}
|
|
181
|
+
candidateRenderer = await createRenderer(
|
|
182
|
+
init.canvas,
|
|
183
|
+
prepared.features === undefined ? {} : { features: prepared.features },
|
|
184
|
+
init.shaderManifestUrl === undefined
|
|
185
|
+
? undefined
|
|
186
|
+
: { shaderManifestUrl: init.shaderManifestUrl },
|
|
187
|
+
);
|
|
188
|
+
const ready = await candidateRenderer.ready;
|
|
189
|
+
if (!ready.ok) throw ready.error;
|
|
190
|
+
rendererLifecycleTransferred = true;
|
|
191
|
+
const assetRegistry = createAssetRegistry({
|
|
192
|
+
catalog: createCatalogSource({
|
|
193
|
+
url: new URL('pack-index.json', globalThis.location?.href ?? 'http://localhost/').href,
|
|
194
|
+
}),
|
|
195
|
+
});
|
|
196
|
+
const assembledAssets = assembleAssetRuntime(assetRegistry, []);
|
|
197
|
+
if (!assembledAssets.ok) {
|
|
198
|
+
assetRegistry.dispose();
|
|
199
|
+
throw assembledAssets.error;
|
|
200
|
+
}
|
|
201
|
+
assetAssembly = assembledAssets.value;
|
|
202
|
+
const pluginContext = await createWorldContext(
|
|
203
|
+
nextWorld,
|
|
204
|
+
workerEngineProfile({
|
|
205
|
+
renderer: candidateRenderer,
|
|
206
|
+
assetAssembly,
|
|
207
|
+
input: inputBackend,
|
|
208
|
+
audio: audioBackend,
|
|
209
|
+
animationPayloads: createAnimationPayloadLookup(candidateRenderer.assets),
|
|
210
|
+
extensions: [
|
|
211
|
+
...(init.tier === 'shared' ? [sharedKernelPlugin(candidate)] : []),
|
|
212
|
+
executionBootstrapHostPlugin({
|
|
213
|
+
...(init.bootstrapPort === undefined ? {} : { port: init.bootstrapPort }),
|
|
214
|
+
setPointerLockAllowed(allowed): void {
|
|
215
|
+
scope.postMessage({
|
|
216
|
+
kind: 'host-control',
|
|
217
|
+
command: 'set-pointer-lock-allowed',
|
|
218
|
+
allowed,
|
|
219
|
+
});
|
|
220
|
+
},
|
|
221
|
+
}),
|
|
222
|
+
...(prepared.plugins ?? []),
|
|
223
|
+
],
|
|
224
|
+
}),
|
|
225
|
+
);
|
|
226
|
+
candidate.pluginContext = pluginContext;
|
|
227
|
+
const activeRenderer = candidateRenderer;
|
|
228
|
+
const previousRealm = realm;
|
|
229
|
+
const committed = await commitAttachedWorld(
|
|
230
|
+
candidateRenderer,
|
|
231
|
+
previousRealm?.world,
|
|
232
|
+
nextWorld,
|
|
233
|
+
async () => {
|
|
234
|
+
await candidate.kernelPool?.ready();
|
|
235
|
+
return true;
|
|
236
|
+
},
|
|
237
|
+
);
|
|
238
|
+
if (!committed) {
|
|
239
|
+
await disposeRealm(candidate);
|
|
240
|
+
if (previousSurfaceReleased) previousRenderer?.restoreSurface();
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
realm = candidate;
|
|
244
|
+
renderer = activeRenderer;
|
|
245
|
+
lastFrameId = 0;
|
|
246
|
+
if (previousRealm !== undefined) await disposeRealm(previousRealm);
|
|
247
|
+
return true;
|
|
248
|
+
} catch (cause) {
|
|
249
|
+
await disposeRealm(candidate);
|
|
250
|
+
assetAssembly?.dispose();
|
|
251
|
+
assetAssembly?.registry.dispose();
|
|
252
|
+
if (!rendererLifecycleTransferred) candidateRenderer?.dispose();
|
|
253
|
+
if (previousSurfaceReleased) previousRenderer?.restoreSurface();
|
|
254
|
+
throw cause;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async function initialize(message: ExecutionInitMessage): Promise<void> {
|
|
259
|
+
try {
|
|
260
|
+
engineCanvas = message.canvas;
|
|
261
|
+
if (!(await createRealm(message))) return;
|
|
262
|
+
scope.postMessage({
|
|
263
|
+
kind: 'ready',
|
|
264
|
+
worldIdentity: realm?.world.identity ?? '',
|
|
265
|
+
realm: 'worker',
|
|
266
|
+
workerWebGpu: typeof navigator === 'object' && navigator.gpu !== undefined,
|
|
267
|
+
});
|
|
268
|
+
} catch (cause) {
|
|
269
|
+
postFault(
|
|
270
|
+
'bootstrap',
|
|
271
|
+
'app-execution-bootstrap-failed',
|
|
272
|
+
'Engine Worker creates a realm-local World, Renderer and GPU owner',
|
|
273
|
+
'inspect the worker bootstrap cause and module URL',
|
|
274
|
+
cause,
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function runFrame(message: ExecutionFrameMessage): void {
|
|
280
|
+
const activeRealm = realm;
|
|
281
|
+
if (activeRealm === undefined || renderer === undefined) return;
|
|
282
|
+
const { world } = activeRealm;
|
|
283
|
+
if (message.worldIdentity !== world.identity || message.frameId <= lastFrameId) return;
|
|
284
|
+
currentSample = message.inputSample;
|
|
285
|
+
const canvasWidth =
|
|
286
|
+
Number.isFinite(message.canvasWidth) && message.canvasWidth > 0
|
|
287
|
+
? Math.max(1, Math.floor(message.canvasWidth))
|
|
288
|
+
: undefined;
|
|
289
|
+
const canvasHeight =
|
|
290
|
+
Number.isFinite(message.canvasHeight) && message.canvasHeight > 0
|
|
291
|
+
? Math.max(1, Math.floor(message.canvasHeight))
|
|
292
|
+
: undefined;
|
|
293
|
+
if (canvasWidth !== undefined && canvasHeight !== undefined) {
|
|
294
|
+
if (engineCanvas !== undefined) {
|
|
295
|
+
if (engineCanvas.width !== canvasWidth) engineCanvas.width = canvasWidth;
|
|
296
|
+
if (engineCanvas.height !== canvasHeight) engineCanvas.height = canvasHeight;
|
|
297
|
+
}
|
|
298
|
+
syncCameraAspect(world, canvasWidth, canvasHeight);
|
|
299
|
+
}
|
|
300
|
+
const started = performance.now();
|
|
301
|
+
try {
|
|
302
|
+
const update = world.update(message.deltaSeconds);
|
|
303
|
+
if (!update.ok) throw update.error;
|
|
304
|
+
if (world.execution.health === 'poisoned') {
|
|
305
|
+
const fault = world.execution.fault;
|
|
306
|
+
postFault(
|
|
307
|
+
'world',
|
|
308
|
+
fault?.code ?? 'world-poisoned',
|
|
309
|
+
'World remains healthy through update',
|
|
310
|
+
'rebuild the poisoned World explicitly',
|
|
311
|
+
fault,
|
|
312
|
+
fault?.partialWrite ?? true,
|
|
313
|
+
);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const updateFinished = performance.now();
|
|
317
|
+
const draw = renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 });
|
|
318
|
+
if (draw !== undefined && !draw.ok) throw draw.error;
|
|
319
|
+
lastFrameId = message.frameId;
|
|
320
|
+
const kernelDispatch = activeRealm.kernelPool?.takeLastDispatch() ?? null;
|
|
321
|
+
const audioIntents = activeRealm.pendingAudioIntents;
|
|
322
|
+
activeRealm.pendingAudioIntents = [];
|
|
323
|
+
scope.postMessage({
|
|
324
|
+
kind: 'frame-complete',
|
|
325
|
+
worldIdentity: world.identity,
|
|
326
|
+
frameId: message.frameId,
|
|
327
|
+
engineUpdateMs: updateFinished - started,
|
|
328
|
+
kernelWaitMs: kernelDispatch?.waitMs ?? 0,
|
|
329
|
+
...(audioIntents.length > 0 ? { audioIntents } : {}),
|
|
330
|
+
...(kernelDispatch !== null
|
|
331
|
+
? {
|
|
332
|
+
kernelDispatch: {
|
|
333
|
+
eligible: true,
|
|
334
|
+
usedShared: kernelDispatch.mode === 'shared',
|
|
335
|
+
reason:
|
|
336
|
+
kernelDispatch.mode === 'shared' ? ('shared' as const) : ('forced-inline' as const),
|
|
337
|
+
dispatched: kernelDispatch.dispatched,
|
|
338
|
+
completed: kernelDispatch.completed,
|
|
339
|
+
},
|
|
340
|
+
}
|
|
341
|
+
: {}),
|
|
342
|
+
});
|
|
343
|
+
} catch (cause) {
|
|
344
|
+
const fault = world.execution.fault;
|
|
345
|
+
postFault(
|
|
346
|
+
fault === null ? 'runtime' : 'world',
|
|
347
|
+
fault?.code ?? 'app-system-update-failed',
|
|
348
|
+
'World update completes before Renderer draw',
|
|
349
|
+
fault === null ? 'inspect the runtime cause' : 'rebuild the poisoned World explicitly',
|
|
350
|
+
cause,
|
|
351
|
+
fault?.partialWrite ?? false,
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
async function rebuild(message: ExecutionRebuildMessage): Promise<void> {
|
|
357
|
+
const activeRealm = realm;
|
|
358
|
+
if (
|
|
359
|
+
activeRealm === undefined ||
|
|
360
|
+
renderer === undefined ||
|
|
361
|
+
message.worldIdentity !== activeRealm.world.identity
|
|
362
|
+
)
|
|
363
|
+
return;
|
|
364
|
+
const previousWorldIdentity = activeRealm.world.identity;
|
|
365
|
+
try {
|
|
366
|
+
if (engineCanvas === undefined) return;
|
|
367
|
+
const init: ExecutionInitMessage = { ...activeRealm.init, canvas: engineCanvas };
|
|
368
|
+
if (!(await createRealm(init))) return;
|
|
369
|
+
scope.postMessage({
|
|
370
|
+
kind: 'rebuilt',
|
|
371
|
+
previousWorldIdentity,
|
|
372
|
+
worldIdentity: realm?.world.identity ?? '',
|
|
373
|
+
});
|
|
374
|
+
} catch (cause) {
|
|
375
|
+
postFault(
|
|
376
|
+
'rebuild',
|
|
377
|
+
'app-execution-rebuild-failed',
|
|
378
|
+
'bootstrap creates a fresh World identity',
|
|
379
|
+
'inspect the bootstrap cause or create a new App',
|
|
380
|
+
cause,
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
scope.onmessage = (event): void => {
|
|
386
|
+
const message = event.data;
|
|
387
|
+
if (message.kind === 'init') void initialize(message);
|
|
388
|
+
else if (message.kind === 'frame') runFrame(message);
|
|
389
|
+
else if (message.kind === 'rebuild') {
|
|
390
|
+
void rebuildQueue.enqueue(() => rebuild(message));
|
|
391
|
+
} else if (message.kind === 'dispose') {
|
|
392
|
+
void (async () => {
|
|
393
|
+
if (realm !== undefined) {
|
|
394
|
+
await disposeRealm(realm);
|
|
395
|
+
realm = undefined;
|
|
396
|
+
}
|
|
397
|
+
scope.close();
|
|
398
|
+
})();
|
|
399
|
+
}
|
|
400
|
+
};
|