@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,168 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EXECUTION_CAPABILITY_NAMES,
|
|
3
|
+
EXECUTION_REQUESTED_TIERS,
|
|
4
|
+
EXECUTION_TIERS,
|
|
5
|
+
type ExecutionReport,
|
|
6
|
+
} from './types';
|
|
7
|
+
|
|
8
|
+
export const EXECUTION_REPORT_SCHEMA_VERSION = 1 as const;
|
|
9
|
+
|
|
10
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
11
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function hasExactKeys(value: Record<string, unknown>, keys: readonly string[]): boolean {
|
|
15
|
+
const actual = Object.keys(value);
|
|
16
|
+
return actual.length === keys.length && keys.every((key) => key in value);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isMeasurement(value: unknown): boolean {
|
|
20
|
+
if (!isRecord(value) || !hasExactKeys(value, ['samples', 'p50', 'p95', 'p99', 'jitter'])) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return (
|
|
24
|
+
Number.isInteger(value.samples) &&
|
|
25
|
+
(value.samples as number) > 0 &&
|
|
26
|
+
['p50', 'p95', 'p99', 'jitter'].every(
|
|
27
|
+
(key) => typeof value[key] === 'number' && Number.isFinite(value[key]) && value[key] >= 0,
|
|
28
|
+
)
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isExecutionReport(value: unknown): value is ExecutionReport {
|
|
33
|
+
if (!isRecord(value)) return false;
|
|
34
|
+
if (
|
|
35
|
+
!hasExactKeys(value, [
|
|
36
|
+
'schemaVersion',
|
|
37
|
+
'requestedTier',
|
|
38
|
+
'actualTier',
|
|
39
|
+
'selectionReason',
|
|
40
|
+
'sharedEvidencePassed',
|
|
41
|
+
'capabilities',
|
|
42
|
+
'engine',
|
|
43
|
+
'world',
|
|
44
|
+
'kernelDispatch',
|
|
45
|
+
'performance',
|
|
46
|
+
'audio',
|
|
47
|
+
'fault',
|
|
48
|
+
])
|
|
49
|
+
) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const report = value as unknown as Partial<ExecutionReport>;
|
|
53
|
+
if (report.schemaVersion !== EXECUTION_REPORT_SCHEMA_VERSION) return false;
|
|
54
|
+
if (!EXECUTION_REQUESTED_TIERS.includes(report.requestedTier as never)) return false;
|
|
55
|
+
if (report.actualTier !== null && !EXECUTION_TIERS.includes(report.actualTier as never)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
if (typeof report.sharedEvidencePassed !== 'boolean' || report.capabilities === undefined) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
if (
|
|
62
|
+
!EXECUTION_CAPABILITY_NAMES.every((name) => {
|
|
63
|
+
const fact = report.capabilities?.[name];
|
|
64
|
+
return typeof fact?.available === 'boolean' && typeof fact.reason === 'string';
|
|
65
|
+
})
|
|
66
|
+
)
|
|
67
|
+
return false;
|
|
68
|
+
const engine = report.engine;
|
|
69
|
+
const world = report.world;
|
|
70
|
+
const dispatch = report.kernelDispatch;
|
|
71
|
+
const performance = report.performance;
|
|
72
|
+
const audio = report.audio;
|
|
73
|
+
if (!isRecord(engine) || !hasExactKeys(engine, ['realm', 'health'])) return false;
|
|
74
|
+
if (!['host', 'worker'].includes(engine.realm as string)) return false;
|
|
75
|
+
if (!['idle', 'starting', 'running', 'stopped', 'faulted'].includes(engine.health as string)) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
if (
|
|
79
|
+
!isRecord(world) ||
|
|
80
|
+
!hasExactKeys(world, ['identity', 'health', 'partialWrite', 'retryable'])
|
|
81
|
+
) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
if (world.identity !== null && typeof world.identity !== 'string') return false;
|
|
85
|
+
if (!['healthy', 'poisoned'].includes(world.health as string)) return false;
|
|
86
|
+
if (typeof world.partialWrite !== 'boolean' || typeof world.retryable !== 'boolean') return false;
|
|
87
|
+
if (
|
|
88
|
+
!isRecord(dispatch) ||
|
|
89
|
+
!hasExactKeys(dispatch, ['eligible', 'usedShared', 'reason', 'dispatched', 'completed'])
|
|
90
|
+
) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
if (typeof dispatch.eligible !== 'boolean' || typeof dispatch.usedShared !== 'boolean')
|
|
94
|
+
return false;
|
|
95
|
+
if (
|
|
96
|
+
![
|
|
97
|
+
'no-eligible-kernel',
|
|
98
|
+
'zero-work',
|
|
99
|
+
'small-span',
|
|
100
|
+
'forced-inline',
|
|
101
|
+
'shared',
|
|
102
|
+
'poisoned',
|
|
103
|
+
].includes(dispatch.reason as string)
|
|
104
|
+
)
|
|
105
|
+
return false;
|
|
106
|
+
if (!Number.isInteger(dispatch.dispatched) || !Number.isInteger(dispatch.completed)) return false;
|
|
107
|
+
if (
|
|
108
|
+
!isRecord(performance) ||
|
|
109
|
+
!hasExactKeys(performance, ['hostFrameMs', 'engineUpdateMs', 'kernelWaitMs', 'hostAudioMs'])
|
|
110
|
+
)
|
|
111
|
+
return false;
|
|
112
|
+
for (const key of ['hostFrameMs', 'engineUpdateMs', 'kernelWaitMs', 'hostAudioMs'] as const) {
|
|
113
|
+
const measurement = performance[key];
|
|
114
|
+
if (measurement !== null && !isMeasurement(measurement)) return false;
|
|
115
|
+
}
|
|
116
|
+
if (
|
|
117
|
+
!isRecord(audio) ||
|
|
118
|
+
!hasExactKeys(audio, ['owner', 'contextState', 'activeSourceCount', 'lastError'])
|
|
119
|
+
)
|
|
120
|
+
return false;
|
|
121
|
+
if (audio.owner !== 'host') return false;
|
|
122
|
+
if (!['running', 'suspended', 'closed'].includes(audio.contextState as string)) return false;
|
|
123
|
+
if (!Number.isInteger(audio.activeSourceCount) || (audio.activeSourceCount as number) < 0)
|
|
124
|
+
return false;
|
|
125
|
+
if (audio.lastError !== null) {
|
|
126
|
+
if (!isRecord(audio.lastError)) return false;
|
|
127
|
+
if (!hasExactKeys(audio.lastError, ['code', 'expected', 'hint', 'detail'])) return false;
|
|
128
|
+
if (
|
|
129
|
+
typeof audio.lastError.code !== 'string' ||
|
|
130
|
+
typeof audio.lastError.expected !== 'string' ||
|
|
131
|
+
typeof audio.lastError.hint !== 'string'
|
|
132
|
+
)
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
if (report.fault !== null) {
|
|
136
|
+
if (!isRecord(report.fault)) return false;
|
|
137
|
+
if (
|
|
138
|
+
!hasExactKeys(report.fault, [
|
|
139
|
+
'source',
|
|
140
|
+
'code',
|
|
141
|
+
'expected',
|
|
142
|
+
'hint',
|
|
143
|
+
'detail',
|
|
144
|
+
'partialWrite',
|
|
145
|
+
'retryable',
|
|
146
|
+
])
|
|
147
|
+
)
|
|
148
|
+
return false;
|
|
149
|
+
if (
|
|
150
|
+
!['bootstrap', 'handshake', 'runtime', 'kernel', 'world', 'rebuild'].includes(
|
|
151
|
+
report.fault.source as string,
|
|
152
|
+
)
|
|
153
|
+
)
|
|
154
|
+
return false;
|
|
155
|
+
if (
|
|
156
|
+
typeof report.fault.code !== 'string' ||
|
|
157
|
+
typeof report.fault.expected !== 'string' ||
|
|
158
|
+
typeof report.fault.hint !== 'string'
|
|
159
|
+
)
|
|
160
|
+
return false;
|
|
161
|
+
if (
|
|
162
|
+
typeof report.fault.partialWrite !== 'boolean' ||
|
|
163
|
+
typeof report.fault.retryable !== 'boolean'
|
|
164
|
+
)
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { err, ok, type Result } from '@forgeax/engine-types';
|
|
2
|
+
import { APP_ERROR_HINTS, APP_EXPECTED, AppError, type AppError as AppErrorType } from '../errors';
|
|
3
|
+
import { missingExecutionCapabilities } from './capabilities';
|
|
4
|
+
import type {
|
|
5
|
+
ExecutionCapabilities,
|
|
6
|
+
ExecutionCapabilityName,
|
|
7
|
+
ExecutionRequestedTier,
|
|
8
|
+
ExecutionSelection,
|
|
9
|
+
ExecutionTier,
|
|
10
|
+
} from './types';
|
|
11
|
+
|
|
12
|
+
const ENGINE_WORKER_CAPABILITIES = [
|
|
13
|
+
'worker',
|
|
14
|
+
'offscreenCanvas',
|
|
15
|
+
'workerWebGpu',
|
|
16
|
+
] as const satisfies readonly ExecutionCapabilityName[];
|
|
17
|
+
const SHARED_CAPABILITIES = [
|
|
18
|
+
...ENGINE_WORKER_CAPABILITIES,
|
|
19
|
+
'crossOriginIsolated',
|
|
20
|
+
'sharedArrayBuffer',
|
|
21
|
+
'atomicsWait',
|
|
22
|
+
] as const satisfies readonly ExecutionCapabilityName[];
|
|
23
|
+
|
|
24
|
+
export interface ExecutionSelectionInput {
|
|
25
|
+
readonly requestedTier: ExecutionRequestedTier;
|
|
26
|
+
readonly capabilities: ExecutionCapabilities;
|
|
27
|
+
readonly sharedEvidencePassed: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function requiredCapabilities(tier: ExecutionTier): readonly ExecutionCapabilityName[] {
|
|
31
|
+
if (tier === 'main-serial') return [];
|
|
32
|
+
if (tier === 'engine-worker') return ENGINE_WORKER_CAPABILITIES;
|
|
33
|
+
return SHARED_CAPABILITIES;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function missingFor(
|
|
37
|
+
tier: ExecutionTier,
|
|
38
|
+
capabilities: ExecutionCapabilities,
|
|
39
|
+
): readonly ExecutionCapabilityName[] {
|
|
40
|
+
return missingExecutionCapabilities(capabilities, requiredCapabilities(tier));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function selectExecutionTier(
|
|
44
|
+
input: ExecutionSelectionInput,
|
|
45
|
+
): Result<ExecutionSelection, AppErrorType> {
|
|
46
|
+
if (input.requestedTier !== 'auto') {
|
|
47
|
+
const missing = missingFor(input.requestedTier, input.capabilities);
|
|
48
|
+
if (missing.length > 0 || (input.requestedTier === 'shared' && !input.sharedEvidencePassed)) {
|
|
49
|
+
return err(
|
|
50
|
+
new AppError({
|
|
51
|
+
code: 'app-execution-tier-unavailable',
|
|
52
|
+
expected: APP_EXPECTED['app-execution-tier-unavailable'],
|
|
53
|
+
hint: APP_ERROR_HINTS['app-execution-tier-unavailable'],
|
|
54
|
+
detail: {
|
|
55
|
+
requestedTier: input.requestedTier,
|
|
56
|
+
missingCapabilities: missing,
|
|
57
|
+
sharedEvidencePassed: input.sharedEvidencePassed,
|
|
58
|
+
},
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return ok({
|
|
63
|
+
requestedTier: input.requestedTier,
|
|
64
|
+
actualTier: input.requestedTier,
|
|
65
|
+
selectionReason: 'explicit-request',
|
|
66
|
+
missingCapabilities: [],
|
|
67
|
+
sharedEvidencePassed: input.sharedEvidencePassed,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const sharedMissing = missingFor('shared', input.capabilities);
|
|
72
|
+
if (sharedMissing.length === 0 && input.sharedEvidencePassed) {
|
|
73
|
+
return ok({
|
|
74
|
+
requestedTier: 'auto',
|
|
75
|
+
actualTier: 'shared',
|
|
76
|
+
selectionReason: 'auto-shared',
|
|
77
|
+
missingCapabilities: [],
|
|
78
|
+
sharedEvidencePassed: true,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const workerMissing = missingFor('engine-worker', input.capabilities);
|
|
82
|
+
if (workerMissing.length === 0) {
|
|
83
|
+
return ok({
|
|
84
|
+
requestedTier: 'auto',
|
|
85
|
+
actualTier: 'engine-worker',
|
|
86
|
+
selectionReason: 'auto-engine-worker',
|
|
87
|
+
missingCapabilities: sharedMissing,
|
|
88
|
+
sharedEvidencePassed: input.sharedEvidencePassed,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return ok({
|
|
92
|
+
requestedTier: 'auto',
|
|
93
|
+
actualTier: 'main-serial',
|
|
94
|
+
selectionReason: 'auto-main-serial',
|
|
95
|
+
missingCapabilities: workerMissing,
|
|
96
|
+
sharedEvidencePassed: input.sharedEvidencePassed,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type { WorldExecutionHealth } from '@forgeax/engine-ecs/shared';
|
|
2
|
+
import type { Result } from '@forgeax/engine-types';
|
|
3
|
+
import type { AppError } from '../errors';
|
|
4
|
+
|
|
5
|
+
export const EXECUTION_TIERS = ['main-serial', 'engine-worker', 'shared'] as const;
|
|
6
|
+
export type ExecutionTier = (typeof EXECUTION_TIERS)[number];
|
|
7
|
+
|
|
8
|
+
export const EXECUTION_REQUESTED_TIERS = ['auto', ...EXECUTION_TIERS] as const;
|
|
9
|
+
export type ExecutionRequestedTier = (typeof EXECUTION_REQUESTED_TIERS)[number];
|
|
10
|
+
|
|
11
|
+
export const EXECUTION_CAPABILITY_NAMES = [
|
|
12
|
+
'worker',
|
|
13
|
+
'offscreenCanvas',
|
|
14
|
+
'workerAnimationFrame',
|
|
15
|
+
'workerWebGpu',
|
|
16
|
+
'crossOriginIsolated',
|
|
17
|
+
'sharedArrayBuffer',
|
|
18
|
+
'atomicsWait',
|
|
19
|
+
] as const;
|
|
20
|
+
export type ExecutionCapabilityName = (typeof EXECUTION_CAPABILITY_NAMES)[number];
|
|
21
|
+
|
|
22
|
+
export interface ExecutionCapabilityFact {
|
|
23
|
+
readonly available: boolean;
|
|
24
|
+
readonly reason: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type ExecutionCapabilities = Readonly<
|
|
28
|
+
Record<ExecutionCapabilityName, ExecutionCapabilityFact>
|
|
29
|
+
>;
|
|
30
|
+
|
|
31
|
+
export type ExecutionSelectionReason =
|
|
32
|
+
| 'explicit-request'
|
|
33
|
+
| 'auto-shared'
|
|
34
|
+
| 'auto-engine-worker'
|
|
35
|
+
| 'auto-main-serial';
|
|
36
|
+
|
|
37
|
+
export interface ExecutionSelection {
|
|
38
|
+
readonly requestedTier: ExecutionRequestedTier;
|
|
39
|
+
readonly actualTier: ExecutionTier;
|
|
40
|
+
readonly selectionReason: ExecutionSelectionReason;
|
|
41
|
+
readonly missingCapabilities: readonly ExecutionCapabilityName[];
|
|
42
|
+
readonly sharedEvidencePassed: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type ExecutionEngineHealth = 'idle' | 'starting' | 'running' | 'stopped' | 'faulted';
|
|
46
|
+
export type ExecutionWorldHealth = WorldExecutionHealth;
|
|
47
|
+
|
|
48
|
+
export type KernelDispatchReason =
|
|
49
|
+
| 'no-eligible-kernel'
|
|
50
|
+
| 'zero-work'
|
|
51
|
+
| 'small-span'
|
|
52
|
+
| 'forced-inline'
|
|
53
|
+
| 'shared'
|
|
54
|
+
| 'poisoned';
|
|
55
|
+
|
|
56
|
+
export interface ExecutionMeasurement {
|
|
57
|
+
readonly samples: number;
|
|
58
|
+
readonly p50: number;
|
|
59
|
+
readonly p95: number;
|
|
60
|
+
readonly p99: number;
|
|
61
|
+
readonly jitter: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface ExecutionFault {
|
|
65
|
+
readonly source: 'bootstrap' | 'handshake' | 'runtime' | 'kernel' | 'world' | 'rebuild';
|
|
66
|
+
readonly code: string;
|
|
67
|
+
readonly expected: string;
|
|
68
|
+
readonly hint: string;
|
|
69
|
+
readonly detail: unknown;
|
|
70
|
+
readonly partialWrite: boolean;
|
|
71
|
+
readonly retryable: boolean;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface ExecutionAudioReport {
|
|
75
|
+
readonly owner: 'host';
|
|
76
|
+
readonly contextState: 'running' | 'suspended' | 'closed';
|
|
77
|
+
readonly activeSourceCount: number;
|
|
78
|
+
readonly lastError: {
|
|
79
|
+
readonly code: string;
|
|
80
|
+
readonly expected: string;
|
|
81
|
+
readonly hint: string;
|
|
82
|
+
readonly detail: unknown;
|
|
83
|
+
} | null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface ExecutionReport {
|
|
87
|
+
readonly schemaVersion: 1;
|
|
88
|
+
readonly requestedTier: ExecutionRequestedTier;
|
|
89
|
+
readonly actualTier: ExecutionTier | null;
|
|
90
|
+
readonly selectionReason: ExecutionSelectionReason | null;
|
|
91
|
+
readonly sharedEvidencePassed: boolean;
|
|
92
|
+
readonly capabilities: ExecutionCapabilities;
|
|
93
|
+
readonly engine: {
|
|
94
|
+
readonly realm: 'host' | 'worker';
|
|
95
|
+
readonly health: ExecutionEngineHealth;
|
|
96
|
+
};
|
|
97
|
+
readonly world: {
|
|
98
|
+
readonly identity: string | null;
|
|
99
|
+
readonly health: ExecutionWorldHealth;
|
|
100
|
+
readonly partialWrite: boolean;
|
|
101
|
+
readonly retryable: boolean;
|
|
102
|
+
};
|
|
103
|
+
readonly kernelDispatch: {
|
|
104
|
+
readonly eligible: boolean;
|
|
105
|
+
readonly usedShared: boolean;
|
|
106
|
+
readonly reason: KernelDispatchReason;
|
|
107
|
+
readonly dispatched: number;
|
|
108
|
+
readonly completed: number;
|
|
109
|
+
};
|
|
110
|
+
readonly performance: {
|
|
111
|
+
readonly hostFrameMs: ExecutionMeasurement | null;
|
|
112
|
+
readonly engineUpdateMs: ExecutionMeasurement | null;
|
|
113
|
+
readonly kernelWaitMs: ExecutionMeasurement | null;
|
|
114
|
+
readonly hostAudioMs: ExecutionMeasurement | null;
|
|
115
|
+
};
|
|
116
|
+
readonly audio: ExecutionAudioReport;
|
|
117
|
+
readonly fault: ExecutionFault | null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface ExecutionOptions {
|
|
121
|
+
readonly tier?: ExecutionRequestedTier;
|
|
122
|
+
/** Absolute or import.meta.url-relative URL of an ExecutionBootstrapEntry module. */
|
|
123
|
+
readonly bootstrap: string | URL;
|
|
124
|
+
/** Structured-cloneable input supplied identically to main and Worker realms. */
|
|
125
|
+
readonly bootstrapData?: ExecutionBootstrapValue;
|
|
126
|
+
/**
|
|
127
|
+
* Realm side of an optional typed host/game channel. The engine transfers it
|
|
128
|
+
* to a Worker when needed and otherwise preserves the same port contract.
|
|
129
|
+
*/
|
|
130
|
+
readonly bootstrapPort?: MessagePort;
|
|
131
|
+
readonly startupTimeoutMs?: number;
|
|
132
|
+
readonly frameTimeoutMs?: number;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export type ExecutionBootstrapValue =
|
|
136
|
+
| null
|
|
137
|
+
| boolean
|
|
138
|
+
| number
|
|
139
|
+
| string
|
|
140
|
+
| readonly ExecutionBootstrapValue[]
|
|
141
|
+
| { readonly [key: string]: ExecutionBootstrapValue };
|
|
142
|
+
|
|
143
|
+
export interface ExecutionControl {
|
|
144
|
+
report(): ExecutionReport;
|
|
145
|
+
rebuild(): Promise<Result<ExecutionReport, AppError>>;
|
|
146
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { World } from '@forgeax/engine-ecs';
|
|
2
|
+
import { Camera, perspective } from '@forgeax/engine-render';
|
|
3
|
+
import { Transform } from '@forgeax/engine-scene';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Ensure the neutral camera used only by hosts that have no game bootstrap.
|
|
7
|
+
* An authored scene camera wins; a resolved game owns its camera policy and
|
|
8
|
+
* must not call this helper.
|
|
9
|
+
*/
|
|
10
|
+
export function ensureFallbackCamera(
|
|
11
|
+
world: World,
|
|
12
|
+
aspect = 1,
|
|
13
|
+
): ReturnType<World['spawn']> | undefined {
|
|
14
|
+
const cameras = world.query({ with: [Camera] }).unwrap();
|
|
15
|
+
if (!cameras[Symbol.iterator]().next().done) return undefined;
|
|
16
|
+
return world.spawn(
|
|
17
|
+
{ component: Transform, data: { pos: [0, 0.6, 5] } },
|
|
18
|
+
{ component: Camera, data: perspective({ fov: Math.PI / 3, aspect, far: 1000 }) },
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// @forgeax/engine-app -- Host service for asset-resident game plugins.
|
|
2
|
+
//
|
|
3
|
+
// App, Preview, and Devkit provide one realm-local GameHost service. Project
|
|
4
|
+
// code default-exports a native Cordis plugin that injects this service and
|
|
5
|
+
// owns all non-ECS teardown through Fiber effects.
|
|
6
|
+
|
|
7
|
+
import type { AssetRegistry } from '@forgeax/engine-assets-runtime';
|
|
8
|
+
import type { EntityHandle } from '@forgeax/engine-ecs';
|
|
9
|
+
import type { Plugin } from '@forgeax/engine-plugin';
|
|
10
|
+
import type { Renderer } from '@forgeax/engine-render';
|
|
11
|
+
import type { SceneAsset } from '@forgeax/engine-types';
|
|
12
|
+
|
|
13
|
+
import type { App } from './types';
|
|
14
|
+
|
|
15
|
+
/** JSON-shaped data a game may intentionally project through a host bridge. */
|
|
16
|
+
export type GameProjectionValue =
|
|
17
|
+
| null
|
|
18
|
+
| boolean
|
|
19
|
+
| number
|
|
20
|
+
| string
|
|
21
|
+
| GameProjectionValue[]
|
|
22
|
+
| { [key: string]: GameProjectionValue };
|
|
23
|
+
|
|
24
|
+
/** Lightweight, host-agnostic argument schema for a game-owned action. */
|
|
25
|
+
export interface GameActionArgsSchema {
|
|
26
|
+
readonly type?: 'string' | 'number' | 'boolean' | 'object' | 'array';
|
|
27
|
+
readonly properties?: Record<string, GameActionArgsSchema>;
|
|
28
|
+
readonly required?: string[];
|
|
29
|
+
readonly enum?: GameProjectionValue[];
|
|
30
|
+
readonly items?: GameActionArgsSchema;
|
|
31
|
+
readonly nullable?: boolean;
|
|
32
|
+
readonly description?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A Play-only capability owned by game code. The host may discover and invoke it,
|
|
37
|
+
* but never supplies its gameplay semantics or reaches into the game World.
|
|
38
|
+
*/
|
|
39
|
+
export interface GameActionDef {
|
|
40
|
+
readonly id: string;
|
|
41
|
+
readonly title: string;
|
|
42
|
+
readonly description?: string;
|
|
43
|
+
readonly argsSchema?: GameActionArgsSchema;
|
|
44
|
+
/** Return an optional JSON-shaped result for inspection clients. */
|
|
45
|
+
readonly run: (
|
|
46
|
+
args: GameProjectionValue,
|
|
47
|
+
) => GameProjectionValue | void | Promise<GameProjectionValue | void>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** A named, serializable Play-only read projection owned by game code. */
|
|
51
|
+
export interface GameReadDef {
|
|
52
|
+
readonly id: string;
|
|
53
|
+
readonly title: string;
|
|
54
|
+
readonly description?: string;
|
|
55
|
+
readonly read: () => GameProjectionValue | Promise<GameProjectionValue>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Host-provided registration sink for one Play run. Games receive this only while
|
|
60
|
+
* bootstrapping the fresh transient world; registrations are discarded when the run is disposed.
|
|
61
|
+
*/
|
|
62
|
+
export interface GameProjectionRegistrar {
|
|
63
|
+
registerAction(def: GameActionDef): () => void;
|
|
64
|
+
registerRead(def: GameReadDef): () => void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Realm-local Host capability injected into an asset-resident game plugin. */
|
|
68
|
+
export interface GameHost {
|
|
69
|
+
/** Presentation canvas owned by this Host realm. */
|
|
70
|
+
readonly canvas: HTMLCanvasElement;
|
|
71
|
+
/** The WebGPU Renderer (optional — some hosts may not expose it). */
|
|
72
|
+
readonly renderer?: Renderer;
|
|
73
|
+
/** The AssetRegistry owned by the App realm. */
|
|
74
|
+
readonly assets: AssetRegistry;
|
|
75
|
+
/** The App handle for lifecycle introspection. */
|
|
76
|
+
readonly app: App;
|
|
77
|
+
/** Synthetic root entity of the host-instantiated defaultScene. Carries the
|
|
78
|
+
* SceneInstance component. Absent when the game has no defaultScene. */
|
|
79
|
+
readonly defaultSceneRoot?: EntityHandle;
|
|
80
|
+
/** The loaded SceneAsset payload for the defaultScene. Contains the
|
|
81
|
+
* author-side entity list with Name components. Absent when the game has
|
|
82
|
+
* no defaultScene. */
|
|
83
|
+
readonly defaultScene?: SceneAsset;
|
|
84
|
+
/**
|
|
85
|
+
* Controlled UI container for this run. Games must mount their DOM UI here
|
|
86
|
+
* (`(ctx.uiRoot ?? document.body).appendChild(el)`) instead of appending
|
|
87
|
+
* directly to `document.body`. In the embedded editor viewport the host
|
|
88
|
+
* removes this whole container when the run is disposed. Absent means game
|
|
89
|
+
* code falls back to `document.body`.
|
|
90
|
+
*/
|
|
91
|
+
readonly uiRoot?: HTMLElement;
|
|
92
|
+
/**
|
|
93
|
+
* M2 D-3: command-set pointer-lock gate. The game template calls this
|
|
94
|
+
* when the view mode changes (e.g. `setPointerLockAllowed(mode === 'fps')`
|
|
95
|
+
* to allow lock in FPS, disallow in top-down). The host wires this to
|
|
96
|
+
* App.input.setPointerLockAllowed?.() which delegates to the input backend.
|
|
97
|
+
* Absent → the host does not support pointer-lock gating (e.g. a host
|
|
98
|
+
* that only runs top-down games).
|
|
99
|
+
*/
|
|
100
|
+
readonly setPointerLockAllowed?: (allowed: boolean) => void;
|
|
101
|
+
/**
|
|
102
|
+
* Optional Play-only projection seam. A game registers its own action and read
|
|
103
|
+
* capabilities here; the host may only discover/invoke/read those closures. It
|
|
104
|
+
* is deliberately absent outside a host that can clear registrations on disposal.
|
|
105
|
+
*/
|
|
106
|
+
readonly gameProjection?: GameProjectionRegistrar;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Provide the Host boundary to a native Cordis game plugin.
|
|
111
|
+
*/
|
|
112
|
+
export function gameHostPlugin(host: GameHost): Plugin {
|
|
113
|
+
return {
|
|
114
|
+
name: 'game-host',
|
|
115
|
+
provide: 'gameHost',
|
|
116
|
+
apply(ctx) {
|
|
117
|
+
ctx.provide('gameHost', host);
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
declare module '@forgeax/engine-plugin' {
|
|
123
|
+
interface EngineContextServices {
|
|
124
|
+
gameHost?: GameHost;
|
|
125
|
+
}
|
|
126
|
+
}
|