@forgeax/engine-runtime 0.1.4 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -83
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/animation-graph-collect-roundtrip.test.d.ts +2 -0
- package/dist/__tests__/animation-graph-collect-roundtrip.test.d.ts.map +1 -0
- package/dist/__tests__/animation-graph-serialize.unit.test.d.ts +2 -0
- package/dist/__tests__/animation-graph-serialize.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-brand-and-name-retirement.test.d.ts +2 -0
- package/dist/__tests__/asset-brand-and-name-retirement.test.d.ts.map +1 -0
- package/dist/__tests__/asset-error-exhaustive.test-d.d.ts +11 -0
- package/dist/__tests__/asset-error-exhaustive.test-d.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-guid-reverse.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-guid-reverse.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-hdr-equirect.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-hdr-equirect.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-mesh-pack-payload.dawn.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-mesh-pack-payload.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-mounts.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-mounts.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.browser.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.browser.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.dawn.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.listCatalog.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.listCatalog.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.mesh-bin.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.mesh-bin.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.pack-cache.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.pack-cache.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.raw-container-failfast.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.raw-container-failfast.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.recursive.spec.d.ts +2 -0
- package/dist/__tests__/asset-registry.recursive.spec.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset.unit.test.d.ts +2 -0
- package/dist/__tests__/asset.unit.test.d.ts.map +1 -0
- package/dist/__tests__/audio-load-by-guid.unit.test.d.ts +2 -0
- package/dist/__tests__/audio-load-by-guid.unit.test.d.ts.map +1 -0
- package/dist/__tests__/backend-selection.integration.test.d.ts +2 -0
- package/dist/__tests__/backend-selection.integration.test.d.ts.map +1 -0
- package/dist/__tests__/basis-catalog-dispatch.integration.test.d.ts +2 -0
- package/dist/__tests__/basis-catalog-dispatch.integration.test.d.ts.map +1 -0
- package/dist/__tests__/builtin-asset-registry.unit.test.d.ts +2 -0
- package/dist/__tests__/builtin-asset-registry.unit.test.d.ts.map +1 -0
- package/dist/__tests__/collect-scene-asset.test.d.ts +1 -2
- package/dist/__tests__/collect-scene-asset.test.d.ts.map +1 -1
- package/dist/__tests__/collect-scene-mount-collapse.test.d.ts +2 -0
- package/dist/__tests__/collect-scene-mount-collapse.test.d.ts.map +1 -0
- package/dist/__tests__/collect-transient-roundtrip.test.d.ts +1 -1
- package/dist/__tests__/collect-transient-roundtrip.test.d.ts.map +1 -1
- package/dist/__tests__/collect-transient-skip.test.d.ts +1 -2
- package/dist/__tests__/collect-transient-skip.test.d.ts.map +1 -1
- package/dist/__tests__/compressed-texture-physical-extent.integration.test.d.ts +2 -0
- package/dist/__tests__/compressed-texture-physical-extent.integration.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/mesh-update-dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/mesh-update-dawn.test.d.ts.map +1 -0
- package/dist/__tests__/dawn/upload-texture.dawn.test.d.ts +2 -0
- package/dist/__tests__/dawn/upload-texture.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/draw-published.d.ts +2 -2
- package/dist/__tests__/draw-published.d.ts.map +1 -1
- package/dist/__tests__/dynamic-texture-store.unit.test.d.ts +2 -0
- package/dist/__tests__/dynamic-texture-store.unit.test.d.ts.map +1 -0
- package/dist/__tests__/errors.unit.test.d.ts +2 -0
- package/dist/__tests__/errors.unit.test.d.ts.map +1 -0
- package/dist/__tests__/extract-skin-mismatch.unit.test.d.ts +2 -0
- package/dist/__tests__/extract-skin-mismatch.unit.test.d.ts.map +1 -0
- package/dist/__tests__/fetch-binary-decompress.test.d.ts +2 -0
- package/dist/__tests__/fetch-binary-decompress.test.d.ts.map +1 -0
- package/dist/__tests__/four-path-compression.integration.test.d.ts +2 -0
- package/dist/__tests__/four-path-compression.integration.test.d.ts.map +1 -0
- package/dist/__tests__/fullscreen-feature-host.dawn.test.d.ts +2 -0
- package/dist/__tests__/fullscreen-feature-host.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/guid-for-asset.unit.test.d.ts +2 -0
- package/dist/__tests__/guid-for-asset.unit.test.d.ts.map +1 -0
- package/dist/__tests__/handle-migration.test.d.ts +2 -0
- package/dist/__tests__/handle-migration.test.d.ts.map +1 -0
- package/dist/__tests__/helpers/register-runtime-components.d.ts.map +1 -1
- package/dist/__tests__/host-custom-kind-contract.test-d.d.ts +2 -0
- package/dist/__tests__/host-custom-kind-contract.test-d.d.ts.map +1 -0
- package/dist/__tests__/host-custom-kind-contract.test.d.ts +2 -0
- package/dist/__tests__/host-custom-kind-contract.test.d.ts.map +1 -0
- package/dist/__tests__/inspect-name.test.d.ts +2 -0
- package/dist/__tests__/inspect-name.test.d.ts.map +1 -0
- package/dist/__tests__/instantiate-mount-deferred-wiring.test.d.ts +2 -0
- package/dist/__tests__/instantiate-mount-deferred-wiring.test.d.ts.map +1 -0
- package/dist/__tests__/loadbyguid-payload.unit.test.d.ts +2 -0
- package/dist/__tests__/loadbyguid-payload.unit.test.d.ts.map +1 -0
- package/dist/__tests__/loadbyguid-texture-intern.unit.test.d.ts +2 -0
- package/dist/__tests__/loadbyguid-texture-intern.unit.test.d.ts.map +1 -0
- package/dist/__tests__/loader-extract-graceful.test.d.ts +2 -0
- package/dist/__tests__/loader-extract-graceful.test.d.ts.map +1 -0
- package/dist/__tests__/loader-name-survival.test.d.ts +2 -0
- package/dist/__tests__/loader-name-survival.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin-contract.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin-contract.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin-skin-roundtrip.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin-skin-roundtrip.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-loader-skin-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-loader-skin-contract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/new-kind-refs-contract.test.d.ts +2 -0
- package/dist/__tests__/new-kind-refs-contract.test.d.ts.map +1 -0
- package/dist/__tests__/package-promotion.test.d.ts +2 -0
- package/dist/__tests__/package-promotion.test.d.ts.map +1 -0
- package/dist/__tests__/package-rename.test.d.ts +2 -0
- package/dist/__tests__/package-rename.test.d.ts.map +1 -0
- package/dist/__tests__/recover-error-exhaustive.test-d.d.ts +1 -1
- package/dist/__tests__/recover-error-exhaustive.test-d.d.ts.map +1 -1
- package/dist/__tests__/render-error-exhaustive.test-d.d.ts +1 -1
- package/dist/__tests__/render-error-exhaustive.test-d.d.ts.map +1 -1
- package/dist/__tests__/render-feature-prepared-graphics.fixture.d.ts +10 -0
- package/dist/__tests__/render-feature-prepared-graphics.fixture.d.ts.map +1 -0
- package/dist/__tests__/render-system-extract.unit.test.d.ts +2 -0
- package/dist/__tests__/render-system-extract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-test-utils.d.ts +10 -0
- package/dist/__tests__/renderer-test-utils.d.ts.map +1 -0
- package/dist/__tests__/renderer.test-d.d.ts +2 -0
- package/dist/__tests__/renderer.test-d.d.ts.map +1 -0
- package/dist/__tests__/resolve-asset-handle-err.test.d.ts +2 -0
- package/dist/__tests__/resolve-asset-handle-err.test.d.ts.map +1 -0
- package/dist/__tests__/resolve-asset-handle.unit.test.d.ts +2 -0
- package/dist/__tests__/resolve-asset-handle.unit.test.d.ts.map +1 -0
- package/dist/__tests__/resolveName-fallback.test.d.ts +2 -0
- package/dist/__tests__/resolveName-fallback.test.d.ts.map +1 -0
- package/dist/__tests__/resolveName-null-package.test.d.ts +2 -0
- package/dist/__tests__/resolveName-null-package.test.d.ts.map +1 -0
- package/dist/__tests__/resolveName-unified-entry.test.d.ts +2 -0
- package/dist/__tests__/resolveName-unified-entry.test.d.ts.map +1 -0
- package/dist/__tests__/resolveName.test.d.ts +2 -0
- package/dist/__tests__/resolveName.test.d.ts.map +1 -0
- package/dist/__tests__/roots-to-scene-asset.test.d.ts +1 -1
- package/dist/__tests__/roots-to-scene-asset.test.d.ts.map +1 -1
- package/dist/__tests__/roundtrip-primitive.test.d.ts +2 -0
- package/dist/__tests__/roundtrip-primitive.test.d.ts.map +1 -0
- package/dist/__tests__/scene-bare-guid-refs-instantiate.test.d.ts +2 -0
- package/dist/__tests__/scene-bare-guid-refs-instantiate.test.d.ts.map +1 -0
- package/dist/__tests__/scene-instance-authoring.integration.test.d.ts +2 -0
- package/dist/__tests__/scene-instance-authoring.integration.test.d.ts.map +1 -0
- package/dist/__tests__/scene-mount-fixed-point.test.d.ts +2 -0
- package/dist/__tests__/scene-mount-fixed-point.test.d.ts.map +1 -0
- package/dist/__tests__/scene-mount-roundtrip.test.d.ts +2 -0
- package/dist/__tests__/scene-mount-roundtrip.test.d.ts.map +1 -0
- package/dist/__tests__/scene-mount-wrapper-roundtrip.test.d.ts +2 -0
- package/dist/__tests__/scene-mount-wrapper-roundtrip.test.d.ts.map +1 -0
- package/dist/__tests__/scene-sugar-instantiate.test.d.ts +2 -0
- package/dist/__tests__/scene-sugar-instantiate.test.d.ts.map +1 -0
- package/dist/__tests__/serialize-mounts-roundtrip.test.d.ts +2 -0
- package/dist/__tests__/serialize-mounts-roundtrip.test.d.ts.map +1 -0
- package/dist/__tests__/serialize-scene-asset.test.d.ts +1 -1
- package/dist/__tests__/serialize-scene-asset.test.d.ts.map +1 -1
- package/dist/__tests__/template-game-default-api-contract.test.d.ts +2 -0
- package/dist/__tests__/template-game-default-api-contract.test.d.ts.map +1 -0
- package/dist/__tests__/tileset-asset-validate.test.d.ts +2 -0
- package/dist/__tests__/tileset-asset-validate.test.d.ts.map +1 -0
- package/dist/__tests__/transcode-fallback.unit.test.d.ts +2 -0
- package/dist/__tests__/transcode-fallback.unit.test.d.ts.map +1 -0
- package/dist/__tests__/transient-runtime-behavior.test.d.ts +2 -0
- package/dist/__tests__/transient-runtime-behavior.test.d.ts.map +1 -0
- package/dist/__tests__/video-loader-consumption.test.d.ts +2 -0
- package/dist/__tests__/video-loader-consumption.test.d.ts.map +1 -0
- package/dist/__tests__/video-loader-smoke.dawn.test.d.ts +2 -0
- package/dist/__tests__/video-loader-smoke.dawn.test.d.ts.map +1 -0
- package/dist/backend-selection.d.ts +13 -0
- package/dist/backend-selection.d.ts.map +1 -0
- package/dist/collect-scene-asset.d.ts +7 -4
- package/dist/collect-scene-asset.d.ts.map +1 -1
- package/dist/createRenderer.d.ts +14 -2
- package/dist/createRenderer.d.ts.map +1 -1
- package/dist/errors/environment.d.ts +1 -1
- package/dist/errors/environment.d.ts.map +1 -1
- package/dist/index.d.ts +20 -82
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +107 -108
- package/dist/index.mjs.map +1 -1
- package/dist/renderer-host.d.ts +9 -0
- package/dist/renderer-host.d.ts.map +1 -0
- package/dist/renderer-host.mjs +62 -0
- package/dist/renderer-host.mjs.map +1 -0
- package/dist/scene-instances/index.d.ts +2 -0
- package/dist/scene-instances/index.d.ts.map +1 -0
- package/dist/scene-utils/mount-override-fold.d.ts +1 -2
- package/dist/scene-utils/mount-override-fold.d.ts.map +1 -1
- package/dist/systems/sprite-animation-tick.d.ts +1 -1
- package/dist/systems/sprite-animation-tick.d.ts.map +1 -1
- package/dist/systems/transparent-sort.d.ts +1 -2
- package/dist/systems/transparent-sort.d.ts.map +1 -1
- package/dist/tilemap-register-validate.d.ts.map +1 -1
- package/package.json +34 -33
- package/src/__tests__/active-camera.unit.test.ts +19 -9
- package/src/__tests__/animation-graph-collect-roundtrip.test.ts +213 -0
- package/src/__tests__/animation-graph-serialize.unit.test.ts +171 -0
- package/src/__tests__/append-injection.test.ts +1 -1
- package/src/__tests__/asset-brand-and-name-retirement.test.ts +102 -0
- package/src/__tests__/asset-error-exhaustive.test-d.ts +62 -0
- package/src/__tests__/asset-registry-guid-reverse.test.ts +608 -0
- package/src/__tests__/asset-registry-hdr-equirect.test.ts +78 -0
- package/src/__tests__/asset-registry-mesh-pack-payload.dawn.test.ts +232 -0
- package/src/__tests__/asset-registry-mounts.test.ts +806 -0
- package/src/__tests__/asset-registry.browser.test.ts +81 -0
- package/src/__tests__/asset-registry.dawn.test.ts +94 -0
- package/src/__tests__/asset-registry.listCatalog.test.ts +170 -0
- package/src/__tests__/asset-registry.mesh-bin.test.ts +316 -0
- package/src/__tests__/asset-registry.pack-cache.test.ts +969 -0
- package/src/__tests__/asset-registry.raw-container-failfast.test.ts +225 -0
- package/src/__tests__/asset-registry.recursive.spec.ts +1397 -0
- package/src/__tests__/asset-registry.unit.test.ts +1030 -0
- package/src/__tests__/asset.unit.test.ts +7107 -0
- package/src/__tests__/audio-load-by-guid.unit.test.ts +126 -0
- package/src/__tests__/backend-selection.integration.test.ts +11 -0
- package/src/__tests__/basis-catalog-dispatch.integration.test.ts +320 -0
- package/src/__tests__/bind-group-cache-keying.test.ts +4 -4
- package/src/__tests__/block-upload-math.unit.test.ts +2 -2
- package/src/__tests__/builtin-asset-registry.unit.test.ts +112 -0
- package/src/__tests__/builtin-shader-register-e2e.test.ts +1 -1
- package/src/__tests__/cache-false-eviction.test.ts +2 -1
- package/src/__tests__/cache-isolation-matrix.test.ts +2 -1
- package/src/__tests__/camera-clear-snapshot-equivalence.unit.test.ts +5 -6
- package/src/__tests__/clamp-to-last.e2e.browser.test.ts +29 -19
- package/src/__tests__/collect-scene-asset.test.ts +49 -52
- package/src/__tests__/collect-scene-mount-collapse.test.ts +391 -0
- package/src/__tests__/collect-subtree.unit.test.ts +1 -2
- package/src/__tests__/collect-transient-children-absent.test.ts +24 -10
- package/src/__tests__/collect-transient-roundtrip.test.ts +44 -20
- package/src/__tests__/collect-transient-skip.test.ts +24 -13
- package/src/__tests__/components.test-d.ts +1 -6
- package/src/__tests__/components.unit.test.ts +20 -19
- package/src/__tests__/composite-skybox-cross-world.browser.test.ts +59 -27
- package/src/__tests__/compressed-texture-physical-extent.integration.test.ts +139 -0
- package/src/__tests__/create-renderer-assembly.integration.test.ts +42 -29
- package/src/__tests__/createRenderer-dispose.test.ts +35 -402
- package/src/__tests__/createRenderer.browser-no-webgpu.test.ts +25 -30
- package/src/__tests__/createRenderer.unit.test.ts +1 -1
- package/src/__tests__/dawn/hello-sprite-nineslice-section.dawn.test.ts +27 -63
- package/src/__tests__/dawn/ibl-precompute-readback.dawn.test.ts +56 -47
- package/src/__tests__/dawn/instances-per-instance-pbr.dawn.test.ts +49 -29
- package/src/__tests__/dawn/instances-shadow.dawn.test.ts +26 -15
- package/src/__tests__/dawn/instances-uniform-fallback.dawn.test.ts +13 -14
- package/src/__tests__/dawn/material-alpha.dawn.test.ts +23 -31
- package/src/__tests__/dawn/material-cooked-fixture.dawn.test.ts +24 -9
- package/src/__tests__/dawn/{mesh-update.dawn.test.ts → mesh-update-dawn.test.ts} +42 -134
- package/src/__tests__/dawn/mipmap-srgb-linear.dawn.test.ts +14 -7
- package/src/__tests__/dawn/per-entity-material-texture-isolation.dawn.test.ts +16 -28
- package/src/__tests__/dawn/sprite-nineslice-mesh-bind.dawn.test.ts +23 -39
- package/src/__tests__/dawn/upload-texture.dawn.test.ts +198 -0
- package/src/__tests__/dawn/user-handle-mesh-render.dawn.test.ts +12 -14
- package/src/__tests__/dawn/user-mesh-upload.dawn.test.ts +27 -22
- package/src/__tests__/dawn/zero-manifest-mesh-fail.dawn.test.ts +25 -28
- package/src/__tests__/dawn/zero-manifest-ready.dawn.test.ts +10 -11
- package/src/__tests__/demo-sortscope-migration.unit.test.ts +5 -5
- package/src/__tests__/derive-bgl-integration.test.ts +34 -44
- package/src/__tests__/derive-render-data-skin.unit.test.ts +1 -1
- package/src/__tests__/draw-published.ts +11 -4
- package/src/__tests__/dynamic-texture-store.unit.test.ts +190 -0
- package/src/__tests__/equirect-bc6h.integration.test.ts +144 -21
- package/src/__tests__/errors.unit.test.ts +1227 -0
- package/src/__tests__/extract-frames-graceful-degradation.test.ts +3 -7
- package/src/__tests__/extract-frames-merge.test.ts +35 -22
- package/src/__tests__/extract-record-no-hardcoded-texture-fields.test.ts +5 -16
- package/src/__tests__/extract-skin-mismatch.unit.test.ts +322 -0
- package/src/__tests__/fetch-binary-decompress.test.ts +356 -0
- package/src/__tests__/four-path-compression.integration.test.ts +395 -0
- package/src/__tests__/frame-targets-shadow-regression.unit.test.ts +1 -1
- package/src/__tests__/fullscreen-feature-host.dawn.test.ts +80 -0
- package/src/__tests__/fullscreen-post-bgl-depth.test.ts +4 -2
- package/src/__tests__/fullscreen-post-depth-params-arity.test.ts +3 -3
- package/src/__tests__/fullscreen-post-process-pass.dawn.test.ts +39 -23
- package/src/__tests__/fullscreen-post-process-pass.unit.test.ts +4 -2
- package/src/__tests__/fxaa-pixel-diff.dawn.test.ts +28 -17
- package/src/__tests__/fxaa-validation-and-resize.dawn.test.ts +29 -18
- package/src/__tests__/fxaa-zero-overhead.dawn.test.ts +16 -12
- package/src/__tests__/geometry.unit.test.ts +525 -159
- package/src/__tests__/glyph-tilemap-worldid.test.ts +1 -1
- package/src/__tests__/gpu-resource-cow-survivor.test.ts +6 -4
- package/src/__tests__/guid-for-asset.unit.test.ts +102 -0
- package/src/__tests__/handle-migration.test.ts +136 -0
- package/src/__tests__/helpers/register-runtime-components.ts +4 -2
- package/src/__tests__/helpers/register-scene-components.ts +1 -1
- package/src/__tests__/host-custom-kind-contract.test-d.ts +47 -0
- package/src/__tests__/host-custom-kind-contract.test.ts +578 -0
- package/src/__tests__/inspect-name.test.ts +139 -0
- package/src/__tests__/instances.test-d.ts +1 -1
- package/src/__tests__/instantiate-mount-deferred-wiring.test.ts +112 -0
- package/src/__tests__/light-casters-9-light.browser.test.ts +73 -76
- package/src/__tests__/light-gpu-byte-neutral.unit.test.ts +7 -10
- package/src/__tests__/light-snapshot.test-d.ts +2 -2
- package/src/__tests__/lights-vec-negative.unit.test.ts +1 -1
- package/src/__tests__/lights.unit.test.ts +31 -511
- package/src/__tests__/loadbyguid-payload.unit.test.ts +117 -0
- package/src/__tests__/loadbyguid-texture-intern.unit.test.ts +213 -0
- package/src/__tests__/loader-extract-graceful.test.ts +146 -0
- package/src/__tests__/loader-name-survival.test.ts +213 -0
- package/src/__tests__/m3-vec-negative.unit.test.ts +3 -2
- package/src/__tests__/material-texture-uv-scale.unit.test.ts +101 -31
- package/src/__tests__/materials.unit.test.ts +10 -27
- package/src/__tests__/mesh-bin-contract.test.ts +28 -0
- package/src/__tests__/mesh-bin-skin-roundtrip.unit.test.ts +149 -0
- package/src/__tests__/mesh-bin.test.ts +38 -0
- package/src/__tests__/mesh-loader-skin-contract.unit.test.ts +120 -0
- package/src/__tests__/mesh-ssbo-grow.unit.test.ts +3 -3
- package/src/__tests__/mip-gate.unit.test.ts +1 -1
- package/src/__tests__/msaa-sprite-pixel-diff.dawn.test.ts +29 -68
- package/src/__tests__/new-kind-refs-contract.test.ts +132 -0
- package/src/__tests__/node-shims.d.ts +4 -6
- package/src/__tests__/package-promotion.test.ts +88 -0
- package/src/__tests__/package-rename.test.ts +103 -0
- package/src/__tests__/pass-selector.unit.test.ts +2 -2
- package/src/__tests__/pbr-pipeline.unit.test.ts +21 -25
- package/src/__tests__/pipeline-builder-msaa.unit.test.ts +6 -9
- package/src/__tests__/pipeline-cache-keying.unit.test.ts +6 -9
- package/src/__tests__/pipeline-spec.test.ts +16 -177
- package/src/__tests__/pipeline.unit.test.ts +154 -1693
- package/src/__tests__/point-light-shadow.dawn.test.ts +33 -42
- package/src/__tests__/point-light-shadow.unit.test.ts +19 -60
- package/src/__tests__/post-process-error-codes.unit.test.ts +4 -1
- package/src/__tests__/post-process-params-component.unit.test.ts +1 -1
- package/src/__tests__/post-process-params-extract.unit.test.ts +2 -5
- package/src/__tests__/postprocess-camera-consistency.test.ts +4 -7
- package/src/__tests__/profiler-assembly.integration.test.ts +1 -1
- package/src/__tests__/propagate-transforms.dawn.test.ts +12 -11
- package/src/__tests__/record-worldid-isolation.test.ts +7 -29
- package/src/__tests__/recover-error-exhaustive.test-d.ts +1 -1
- package/src/__tests__/render-error-exhaustive.test-d.ts +117 -71
- package/src/__tests__/render-feature-assembly.test-d.ts +20 -9
- package/src/__tests__/render-feature-prepared-graphics-docs.unit.test.ts +8 -6
- package/src/__tests__/render-feature-prepared-graphics.browser.test.ts +65 -559
- package/src/__tests__/render-feature-prepared-graphics.dawn.test.ts +57 -544
- package/src/__tests__/render-feature-prepared-graphics.fixture.ts +104 -0
- package/src/__tests__/render-feature-prepared-graphics.integration.test.ts +40 -248
- package/src/__tests__/render-feature-prepared-graphics.regression.integration.test.ts +83 -340
- package/src/__tests__/render-feature-recover.integration.test.ts +9 -49
- package/src/__tests__/render-feature-runtime-surface.test-d.ts +17 -20
- package/src/__tests__/render-schema-consumer.integration.test.ts +20 -13
- package/src/__tests__/render-schema-owner-boundary.unit.test.ts +2 -6
- package/src/__tests__/render-system-cross-world-overlay-queue.test.ts +5 -5
- package/src/__tests__/render-system-extract-dispatch-ordering.test.ts +16 -31
- package/src/__tests__/render-system-extract.test.ts +36 -51
- package/src/__tests__/render-system-extract.unit.test.ts +360 -0
- package/src/__tests__/render-system-fold-bucket.unit.test.ts +3 -5
- package/src/__tests__/render-system-fold-fallback.unit.test.ts +6 -3
- package/src/__tests__/render-system-fold-material.unit.test.ts +3 -5
- package/src/__tests__/render-system-fold-metrics.unit.test.ts +5 -5
- package/src/__tests__/render-system-fold-modegate.unit.test.ts +6 -6
- package/src/__tests__/render-system-fold.dawn.test.ts +7 -5
- package/src/__tests__/render-system-mega.test.ts +292 -190
- package/src/__tests__/render-system-owner-split-contract.test.ts +10 -32
- package/src/__tests__/render-system-record-multi-material-textureview.test.ts +59 -45
- package/src/__tests__/render-system-record-per-submesh-transparency.test.ts +23 -29
- package/src/__tests__/render-system-record.test.ts +398 -235
- package/src/__tests__/render-system-record.unit.test.ts +25 -26
- package/src/__tests__/render-system-skin-bg.unit.test.ts +8 -12
- package/src/__tests__/renderer-consumer.test-d.ts +16 -3
- package/src/__tests__/renderer-health.unit.test.ts +26 -916
- package/src/__tests__/renderer-lifecycle.integration.test.ts +19 -6
- package/src/__tests__/renderer-recover.unit.test.ts +49 -1069
- package/src/__tests__/renderer-result.test-d.ts +19 -9
- package/src/__tests__/renderer-surface.unit.test.ts +3 -5
- package/src/__tests__/renderer-test-utils.ts +56 -0
- package/src/__tests__/renderer.test-d.ts +61 -0
- package/src/__tests__/resolve-asset-handle-err.test.ts +111 -0
- package/src/__tests__/resolve-asset-handle.unit.test.ts +64 -0
- package/src/__tests__/resolve-depth-only-view.test.ts +1 -1
- package/src/__tests__/resolveName-fallback.test.ts +64 -0
- package/src/__tests__/resolveName-null-package.test.ts +38 -0
- package/src/__tests__/resolveName-unified-entry.test.ts +65 -0
- package/src/__tests__/resolveName.test.ts +64 -0
- package/src/__tests__/rhi-null-command-flow.unit.test.ts +56 -260
- package/src/__tests__/rhi-null-renderer-lifecycle.unit.test.ts +41 -169
- package/src/__tests__/roots-to-scene-asset.test.ts +199 -93
- package/src/__tests__/roundtrip-primitive.test.ts +351 -0
- package/src/__tests__/scene-bare-guid-refs-instantiate.test.ts +123 -0
- package/src/__tests__/scene-equirect-roundtrip.test.ts +103 -16
- package/src/__tests__/scene-instance-authoring.integration.test.ts +8 -0
- package/src/__tests__/scene-instance-bridge.integration.test.ts +1 -1
- package/src/__tests__/scene-instance-component.test.ts +1 -1
- package/src/__tests__/scene-mount-fixed-point.test.ts +257 -0
- package/src/__tests__/scene-mount-roundtrip.test.ts +987 -0
- package/src/__tests__/scene-mount-wrapper-roundtrip.test.ts +202 -0
- package/src/__tests__/scene-sugar-instantiate.test.ts +324 -0
- package/src/__tests__/serialize-mounts-roundtrip.test.ts +533 -0
- package/src/__tests__/serialize-scene-asset.test.ts +219 -22
- package/src/__tests__/shadow-caster-empty-schema.test.ts +3 -3
- package/src/__tests__/shadow-csm-cascade-loadop.test.ts +37 -17
- package/src/__tests__/shadow-csm-component.test.ts +3 -5
- package/src/__tests__/shadow-csm-extract.test.ts +2 -100
- package/src/__tests__/shadow-csm-orthoz.test.ts +2 -16
- package/src/__tests__/shadow-csm-runtime-vary.dawn.test.ts +19 -14
- package/src/__tests__/shadow-csm-tile-consistency.test.ts +32 -26
- package/src/__tests__/shadow-csm-ubo.dawn.test.ts +8 -49
- package/src/__tests__/shadow-csm-ubo.test.ts +3 -25
- package/src/__tests__/shadow-csm-viewport.browser.test.ts +1 -1
- package/src/__tests__/shadow-fields-observable.dawn.test.ts +67 -56
- package/src/__tests__/shadow-m1.dawn.test.ts +8 -9
- package/src/__tests__/shadow-mesh-ssbo-order.test.ts +11 -27
- package/src/__tests__/skin-errors.unit.test.ts +1 -1
- package/src/__tests__/skin-extract-getarrayview-count.unit.test.ts +7 -5
- package/src/__tests__/skylight-lazy-projection.test.ts +36 -15
- package/src/__tests__/sparse-tag-scene-roundtrip.test.ts +5 -5
- package/src/__tests__/sprite-cullmode-flip.dawn.test.ts +47 -64
- package/src/__tests__/sprite-lit-bgl-byte-identical.test.ts +15 -14
- package/src/__tests__/sprite-lit-extract-branch.test.ts +1 -1
- package/src/__tests__/sprite-lit-pipeline-cache-key.test.ts +1 -1
- package/src/__tests__/ssao-bgl.test.ts +16 -9
- package/src/__tests__/ssao-buffers.test.ts +2 -2
- package/src/__tests__/ssao-data.test.ts +2 -2
- package/src/__tests__/ssao-errors.test.ts +1 -1
- package/src/__tests__/ssao-passes.test.ts +68 -392
- package/src/__tests__/ssao-pipelines.test.ts +1 -1
- package/src/__tests__/ssao-uniform-layout.test.ts +8 -6
- package/src/__tests__/stencil-outline-pixel.dawn.test.ts +25 -17
- package/src/__tests__/swap-chain-format.unit.test.ts +5 -7
- package/src/__tests__/systems.unit.test.ts +536 -1326
- package/src/__tests__/template-game-default-api-contract.test.ts +362 -0
- package/src/__tests__/text.unit.test.ts +56 -33
- package/src/__tests__/tilemap-childed-despawn.test.ts +8 -14
- package/src/__tests__/tilemap-chunk-extract.overhang-bounds.test.ts +8 -12
- package/src/__tests__/tilemap-chunk-extract.propagate-order.test.ts +2 -1
- package/src/__tests__/tilemap-chunk-extract.test.ts +13 -17
- package/src/__tests__/tilemap-dirty-rebuild.test.ts +7 -11
- package/src/__tests__/tilemap-material-cache.test.ts +8 -13
- package/src/__tests__/tilemap-multi-atlas.test.ts +12 -32
- package/src/__tests__/tilemap-owner-boundary.unit.test.ts +5 -2
- package/src/__tests__/tilemap-region-pack.test.ts +8 -12
- package/src/__tests__/tilemap-spawn-flip-pivot.test.ts +8 -12
- package/src/__tests__/tilemap-uv-inset.test.ts +11 -14
- package/src/__tests__/tilemap-validate.test.ts +1 -1
- package/src/__tests__/tileset-asset-validate.test.ts +638 -0
- package/src/__tests__/transcode-fallback.unit.test.ts +207 -0
- package/src/__tests__/transform-hierarchy-pixel-diff.dawn.test.ts +36 -28
- package/src/__tests__/transient-fields-collect.test.ts +28 -21
- package/src/__tests__/transient-runtime-behavior.test.ts +227 -0
- package/src/__tests__/transparent-sort-tilemap-sprite-interleave.test.ts +4 -4
- package/src/__tests__/variant-selection.unit.test.ts +1 -1
- package/src/__tests__/video-extract-bindgroup.dawn.test.ts +89 -136
- package/src/__tests__/video-loader-consumption.test.ts +86 -0
- package/src/__tests__/video-loader-smoke.dawn.test.ts +60 -0
- package/src/__tests__/view-ubo-layout.browser.test.ts +0 -22
- package/src/__tests__/world-entity-key.test.ts +1 -1
- package/src/__tests__/world-scene-despawn.test.ts +1 -1
- package/src/__tests__/world-scene-instantiate.test.ts +2 -36
- package/src/__tests__/world-scene-nested.test.ts +1 -1
- package/src/__tests__/world-scene-override.test.ts +1 -1
- package/src/backend-selection.ts +69 -0
- package/src/collect-scene-asset.ts +35 -23
- package/src/components/__tests__/camera.test-d.ts +1 -1
- package/src/components/__tests__/camera.test.ts +1 -1
- package/src/components/__tests__/sprite-animation.test-d.ts +2 -2
- package/src/components/__tests__/tile-layer.test-d.ts +1 -1
- package/src/components/__tests__/tile-layer.unit.test.ts +2 -2
- package/src/createRenderer.ts +24 -36
- package/src/errors/environment.ts +1 -1
- package/src/index.ts +51 -113
- package/src/renderer-host.ts +33 -0
- package/src/scene-instances/__tests__/post-spawn-resolve-joints.unit.test.ts +1 -1
- package/src/scene-instances/index.ts +4 -0
- package/src/scene-utils/mount-override-fold.ts +3 -7
- package/src/systems/__tests__/transparent-sort.bench.ts +3 -3
- package/src/systems/sprite-animation-tick.ts +1 -1
- package/src/systems/transparent-sort.ts +38 -38
- package/src/tilemap-register-validate.ts +4 -4
- package/dist/__tests__/bind-group-cache-binding.test.d.ts +0 -2
- package/dist/__tests__/bind-group-cache-binding.test.d.ts.map +0 -1
- package/dist/__tests__/bind-group-cache-frame.test.d.ts +0 -2
- package/dist/__tests__/bind-group-cache-frame.test.d.ts.map +0 -1
- package/dist/__tests__/dawn/mesh-update.dawn.test.d.ts +0 -2
- package/dist/__tests__/dawn/mesh-update.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/dispatch-fullscreen-pass-composite-swapchain.dawn.test.d.ts +0 -2
- package/dist/__tests__/dispatch-fullscreen-pass-composite-swapchain.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/dispatch-fullscreen-pass-custom-reads.dawn.test.d.ts +0 -2
- package/dist/__tests__/dispatch-fullscreen-pass-custom-reads.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/dispatch-fullscreen-pass-depth-composite.dawn.test.d.ts +0 -2
- package/dist/__tests__/dispatch-fullscreen-pass-depth-composite.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/dispatch-fullscreen-pass-params.unit.test.d.ts +0 -2
- package/dist/__tests__/dispatch-fullscreen-pass-params.unit.test.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-manifest.d.ts +0 -131
- package/dist/__tests__/fog-producer-manifest.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-pixel-probe.d.ts +0 -42
- package/dist/__tests__/fog-producer-pixel-probe.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-pixel.browser.test.d.ts +0 -2
- package/dist/__tests__/fog-producer-pixel.browser.test.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-pixel.dawn.test.d.ts +0 -2
- package/dist/__tests__/fog-producer-pixel.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-real-fixture.d.ts +0 -5
- package/dist/__tests__/fog-producer-real-fixture.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-vfx-effect.d.ts +0 -3
- package/dist/__tests__/fog-producer-vfx-effect.d.ts.map +0 -1
- package/dist/__tests__/fog-scene-asset.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-scene-asset.integration.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-resource-store-cubemap-status.test.d.ts +0 -2
- package/dist/__tests__/gpu-resource-store-cubemap-status.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-resource.test.d.ts +0 -2
- package/dist/__tests__/gpu-resource.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-bgl-slots.browser.test.d.ts +0 -2
- package/dist/__tests__/hdrp-bgl-slots.browser.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-buffers.unit.test.d.ts +0 -2
- package/dist/__tests__/hdrp-buffers.unit.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-execute-filter.unit.test.d.ts +0 -2
- package/dist/__tests__/hdrp-execute-filter.unit.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-gbuffer-attachments.browser.test.d.ts +0 -2
- package/dist/__tests__/hdrp-gbuffer-attachments.browser.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-pipeline-asset-type.test-d.d.ts +0 -2
- package/dist/__tests__/hdrp-pipeline-asset-type.test-d.d.ts.map +0 -1
- package/dist/__tests__/hdrp-ssao-boundary.test.d.ts +0 -2
- package/dist/__tests__/hdrp-ssao-boundary.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-ssao-falsification.test.d.ts +0 -2
- package/dist/__tests__/hdrp-ssao-falsification.test.d.ts.map +0 -1
- package/dist/__tests__/helpers/primitive-world.d.ts +0 -8
- package/dist/__tests__/helpers/primitive-world.d.ts.map +0 -1
- package/dist/__tests__/helpers/render-asset-port.d.ts +0 -15
- package/dist/__tests__/helpers/render-asset-port.d.ts.map +0 -1
- package/dist/__tests__/helpers/scene-asset-port.d.ts +0 -9
- package/dist/__tests__/helpers/scene-asset-port.d.ts.map +0 -1
- package/dist/__tests__/material-derived-coordinates.integration.test.d.ts +0 -2
- package/dist/__tests__/material-derived-coordinates.integration.test.d.ts.map +0 -1
- package/dist/__tests__/pipeline-layout-selection.test.d.ts +0 -2
- package/dist/__tests__/pipeline-layout-selection.test.d.ts.map +0 -1
- package/dist/__tests__/post-process-reads-narrowing.test-d.d.ts +0 -2
- package/dist/__tests__/post-process-reads-narrowing.test-d.d.ts.map +0 -1
- package/dist/__tests__/post-process-register-roundtrip.dawn.test.d.ts +0 -2
- package/dist/__tests__/post-process-register-roundtrip.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-zero-feature.browser.test.d.ts +0 -2
- package/dist/__tests__/render-feature-zero-feature.browser.test.d.ts.map +0 -1
- package/dist/__tests__/render-pipeline-trivial.dawn.test.d.ts +0 -2
- package/dist/__tests__/render-pipeline-trivial.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-csm-caster-membership.test.d.ts +0 -2
- package/dist/__tests__/shadow-csm-caster-membership.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-csm-pssm.test.d.ts +0 -2
- package/dist/__tests__/shadow-csm-pssm.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-csm-temporal.browser.test.d.ts +0 -2
- package/dist/__tests__/shadow-csm-temporal.browser.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-csm-viewport.test.d.ts +0 -2
- package/dist/__tests__/shadow-csm-viewport.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-m2.dawn.test.d.ts +0 -2
- package/dist/__tests__/shadow-m2.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-m3-calibrate-run.dawn.test.d.ts +0 -2
- package/dist/__tests__/shadow-m3-calibrate-run.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-m3-calibrate.d.ts +0 -83
- package/dist/__tests__/shadow-m3-calibrate.d.ts.map +0 -1
- package/dist/__tests__/shadow-m3.dawn.test.d.ts +0 -2
- package/dist/__tests__/shadow-m3.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-probe-control-flow.dawn.test.d.ts +0 -2
- package/dist/__tests__/shadow-probe-control-flow.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/shadow-quality-metrics.dawn.test.d.ts +0 -2
- package/dist/__tests__/shadow-quality-metrics.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/temporal-off-path.unit.test.d.ts +0 -2
- package/dist/__tests__/temporal-off-path.unit.test.d.ts.map +0 -1
- package/dist/__tests__/video-cache-regression.unit.test.d.ts +0 -2
- package/dist/__tests__/video-cache-regression.unit.test.d.ts.map +0 -1
- package/dist/asset-kind.d.ts +0 -4
- package/dist/asset-kind.d.ts.map +0 -1
- package/dist/errors/sprite-animation.d.ts +0 -3
- package/dist/errors/sprite-animation.d.ts.map +0 -1
- package/dist/scene-asset-port.d.ts +0 -17
- package/dist/scene-asset-port.d.ts.map +0 -1
- package/dist/scene-collect-errors.d.ts +0 -22
- package/dist/scene-collect-errors.d.ts.map +0 -1
- package/dist/shadow-m3-calibrate.d.ts +0 -40
- package/dist/shadow-m3-calibrate.d.ts.map +0 -1
- package/src/__tests__/bind-group-cache-binding.test.ts +0 -2513
- package/src/__tests__/bind-group-cache-frame.test.ts +0 -1511
- package/src/__tests__/dispatch-fullscreen-pass-composite-swapchain.dawn.test.ts +0 -204
- package/src/__tests__/dispatch-fullscreen-pass-custom-reads.dawn.test.ts +0 -297
- package/src/__tests__/dispatch-fullscreen-pass-depth-composite.dawn.test.ts +0 -271
- package/src/__tests__/dispatch-fullscreen-pass-params.unit.test.ts +0 -321
- package/src/__tests__/fog-producer-manifest.ts +0 -162
- package/src/__tests__/fog-producer-pixel-probe.ts +0 -263
- package/src/__tests__/fog-producer-pixel.browser.test.ts +0 -26
- package/src/__tests__/fog-producer-pixel.dawn.test.ts +0 -55
- package/src/__tests__/fog-producer-real-fixture.ts +0 -805
- package/src/__tests__/fog-producer-vfx-effect.ts +0 -272
- package/src/__tests__/fog-scene-asset.integration.test.ts +0 -123
- package/src/__tests__/gpu-resource-store-cubemap-status.test.ts +0 -207
- package/src/__tests__/gpu-resource.test.ts +0 -1206
- package/src/__tests__/hdrp-bgl-slots.browser.test.ts +0 -50
- package/src/__tests__/hdrp-buffers.unit.test.ts +0 -70
- package/src/__tests__/hdrp-execute-filter.unit.test.ts +0 -200
- package/src/__tests__/hdrp-gbuffer-attachments.browser.test.ts +0 -64
- package/src/__tests__/hdrp-pipeline-asset-type.test-d.ts +0 -139
- package/src/__tests__/hdrp-ssao-boundary.test.ts +0 -237
- package/src/__tests__/hdrp-ssao-falsification.test.ts +0 -289
- package/src/__tests__/helpers/primitive-world.ts +0 -91
- package/src/__tests__/helpers/render-asset-port.ts +0 -65
- package/src/__tests__/helpers/scene-asset-port.ts +0 -23
- package/src/__tests__/material-derived-coordinates.integration.test.ts +0 -100
- package/src/__tests__/pipeline-layout-selection.test.ts +0 -298
- package/src/__tests__/post-process-reads-narrowing.test-d.ts +0 -52
- package/src/__tests__/post-process-register-roundtrip.dawn.test.ts +0 -230
- package/src/__tests__/render-feature-zero-feature.browser.test.ts +0 -152
- package/src/__tests__/render-pipeline-trivial.dawn.test.ts +0 -525
- package/src/__tests__/shadow-csm-caster-membership.test.ts +0 -232
- package/src/__tests__/shadow-csm-pssm.test.ts +0 -142
- package/src/__tests__/shadow-csm-temporal.browser.test.ts +0 -145
- package/src/__tests__/shadow-csm-viewport.test.ts +0 -74
- package/src/__tests__/shadow-m2.dawn.test.ts +0 -394
- package/src/__tests__/shadow-m3-calibrate-run.dawn.test.ts +0 -248
- package/src/__tests__/shadow-m3-calibrate.ts +0 -203
- package/src/__tests__/shadow-m3.dawn.test.ts +0 -288
- package/src/__tests__/shadow-probe-control-flow.dawn.test.ts +0 -264
- package/src/__tests__/shadow-quality-metrics.dawn.test.ts +0 -324
- package/src/__tests__/temporal-off-path.unit.test.ts +0 -69
- package/src/__tests__/video-cache-regression.unit.test.ts +0 -186
- package/src/asset-kind.ts +0 -7
- package/src/errors/sprite-animation.ts +0 -5
- package/src/scene-asset-port.ts +0 -32
- package/src/scene-collect-errors.ts +0 -30
- package/src/shadow-m3-calibrate.ts +0 -171
|
@@ -0,0 +1,806 @@
|
|
|
1
|
+
// asset-registry-mounts.test.ts - parseScenePayload mounts[].source refs
|
|
2
|
+
// resolution unit tests (feat-20260608-scene-nesting-ecs-fication M3 / w27,
|
|
3
|
+
// TDD red phase).
|
|
4
|
+
//
|
|
5
|
+
// Coverage:
|
|
6
|
+
// (a) AC-10 happy path: mounts[].source (integer index) resolved through
|
|
7
|
+
// refs[] to a GUID string.
|
|
8
|
+
// (b) AC-10 back-compat: payload with no mounts field passes through.
|
|
9
|
+
// (c) AC-10 back-compat: mounts with source already a string (GUID) passes
|
|
10
|
+
// through unchanged.
|
|
11
|
+
// (d) AC-10 error path: mounts[].source integer index out-of-bounds returns
|
|
12
|
+
// a ParseSceneError.
|
|
13
|
+
// (e) AC-11: mount.source does NOT enter HANDLE_FIELD_NAMES -- it is resolved
|
|
14
|
+
// positionally by field name within the mounts[] array, not through the
|
|
15
|
+
// handle-field allowlist (verify no false collision with existing handle
|
|
16
|
+
// fields named 'source').
|
|
17
|
+
// (f) mounts.memberFirst / mounts.memberCount / mounts.parent (LocalEntityId)
|
|
18
|
+
// are NOT refs[] indices -- they are local entity ids preserved as-is.
|
|
19
|
+
|
|
20
|
+
import { AnimationPlayer } from '@forgeax/engine-animation';
|
|
21
|
+
import {
|
|
22
|
+
type Asset,
|
|
23
|
+
AssetRegistry,
|
|
24
|
+
resolveAssetHandle,
|
|
25
|
+
sceneLoader,
|
|
26
|
+
} from '@forgeax/engine-assets-runtime';
|
|
27
|
+
import { type EntityHandle, World } from '@forgeax/engine-ecs';
|
|
28
|
+
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
29
|
+
import { MeshFilter, SceneInstance } from '@forgeax/engine-render';
|
|
30
|
+
import { ChildOf, Transform } from '@forgeax/engine-scene';
|
|
31
|
+
import {
|
|
32
|
+
type AnimationClip,
|
|
33
|
+
BUILTIN_BASE,
|
|
34
|
+
type Handle,
|
|
35
|
+
type LoadContext,
|
|
36
|
+
type MeshAsset,
|
|
37
|
+
type SceneAsset,
|
|
38
|
+
unwrapHandle,
|
|
39
|
+
} from '@forgeax/engine-types';
|
|
40
|
+
import { describe, expect, it } from 'vitest';
|
|
41
|
+
import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
42
|
+
|
|
43
|
+
/** Access the private parseAssetPayload method via structural view-cast. */
|
|
44
|
+
function accessParseScenePayload(reg: AssetRegistry) {
|
|
45
|
+
// biome-ignore lint/suspicious/noExplicitAny: private method access for unit test
|
|
46
|
+
const internal = reg as any as {
|
|
47
|
+
parseAssetPayload(kind: string, payload: Record<string, unknown>, refs?: string[]): unknown;
|
|
48
|
+
};
|
|
49
|
+
return (
|
|
50
|
+
kind: string,
|
|
51
|
+
payload: Record<string, unknown>,
|
|
52
|
+
refs?: string[],
|
|
53
|
+
): SceneAsset | undefined => {
|
|
54
|
+
const result = internal.parseAssetPayload(kind, payload, refs);
|
|
55
|
+
if (result === undefined) return undefined;
|
|
56
|
+
if (
|
|
57
|
+
typeof result === 'object' &&
|
|
58
|
+
result !== null &&
|
|
59
|
+
'kind' in result &&
|
|
60
|
+
result.kind === 'scene'
|
|
61
|
+
) {
|
|
62
|
+
return result as SceneAsset;
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const GUID_A = 'cbe42beb-8975-5096-b3a1-3dda4cb4c077';
|
|
69
|
+
const GUID_B = 'f6af7007-158f-4d92-9e47-93bf2f213e1f';
|
|
70
|
+
const GUID_C = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';
|
|
71
|
+
|
|
72
|
+
describe('w27 - parseScenePayload mounts[].source refs resolution (AC-10)', () => {
|
|
73
|
+
it('(a) resolves mounts[].source integer index through refs[] to GUID string', () => {
|
|
74
|
+
const reg = new AssetRegistry(makeMockShaderRegistry());
|
|
75
|
+
const fn = accessParseScenePayload(reg);
|
|
76
|
+
const refs = [GUID_A];
|
|
77
|
+
const payload = {
|
|
78
|
+
entities: [{ localId: 0, components: {} }],
|
|
79
|
+
mounts: [
|
|
80
|
+
{
|
|
81
|
+
localId: 1,
|
|
82
|
+
source: 0,
|
|
83
|
+
memberFirst: 2,
|
|
84
|
+
memberCount: 3,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
const asset = fn('scene', payload, refs);
|
|
89
|
+
expect(asset).toBeDefined();
|
|
90
|
+
if (!asset) return;
|
|
91
|
+
expect(asset.kind).toBe('scene');
|
|
92
|
+
expect(asset.mounts).toBeDefined();
|
|
93
|
+
if (!asset.mounts) return;
|
|
94
|
+
expect(asset.mounts.length).toBe(1);
|
|
95
|
+
const mount = asset.mounts[0];
|
|
96
|
+
expect(mount).toBeDefined();
|
|
97
|
+
if (!mount) return;
|
|
98
|
+
// source 0 -> refs[0] = GUID_A
|
|
99
|
+
expect(mount.source).toBe(GUID_A);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('(b) payload with no mounts field passes through (back-compat)', () => {
|
|
103
|
+
const reg = new AssetRegistry(makeMockShaderRegistry());
|
|
104
|
+
const fn = accessParseScenePayload(reg);
|
|
105
|
+
const payload = {
|
|
106
|
+
entities: [{ localId: 0, components: {} }],
|
|
107
|
+
};
|
|
108
|
+
const asset = fn('scene', payload);
|
|
109
|
+
expect(asset).toBeDefined();
|
|
110
|
+
if (!asset) return;
|
|
111
|
+
expect(asset.kind).toBe('scene');
|
|
112
|
+
// mounts absent or empty after parse
|
|
113
|
+
expect(asset.mounts ?? []).toEqual([]);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('(c) mounts with source already a string passes through unchanged', () => {
|
|
117
|
+
const reg = new AssetRegistry(makeMockShaderRegistry());
|
|
118
|
+
const fn = accessParseScenePayload(reg);
|
|
119
|
+
const refs = [GUID_A];
|
|
120
|
+
const payload = {
|
|
121
|
+
entities: [{ localId: 0, components: {} }],
|
|
122
|
+
mounts: [
|
|
123
|
+
{
|
|
124
|
+
localId: 1,
|
|
125
|
+
source: GUID_B, // already a GUID string, not an index
|
|
126
|
+
memberFirst: 2,
|
|
127
|
+
memberCount: 3,
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
};
|
|
131
|
+
const asset = fn('scene', payload, refs);
|
|
132
|
+
expect(asset).toBeDefined();
|
|
133
|
+
if (!asset) return;
|
|
134
|
+
expect(asset.mounts).toBeDefined();
|
|
135
|
+
if (!asset.mounts) return;
|
|
136
|
+
expect(asset.mounts[0]?.source).toBe(GUID_B);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('(d) mounts[].source integer index out-of-bounds returns ParseSceneError', () => {
|
|
140
|
+
const reg = new AssetRegistry(makeMockShaderRegistry());
|
|
141
|
+
const fn = accessParseScenePayload(reg);
|
|
142
|
+
const refs = [GUID_A]; // only 1 entry, indices 0 valid, 1+ invalid
|
|
143
|
+
const payload = {
|
|
144
|
+
entities: [{ localId: 0, components: {} }],
|
|
145
|
+
mounts: [
|
|
146
|
+
{
|
|
147
|
+
localId: 1,
|
|
148
|
+
source: 5, // out-of-bounds: refs.length == 1
|
|
149
|
+
memberFirst: 2,
|
|
150
|
+
memberCount: 3,
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
};
|
|
154
|
+
const asset = fn('scene', payload, refs);
|
|
155
|
+
expect(asset).not.toBeDefined();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('(e) mounts[].source is NOT resolved through HANDLE_FIELD_NAMES (AC-11 positional)', () => {
|
|
159
|
+
// Even if 'source' were in HANDLE_FIELD_NAMES (it is not), mount.source
|
|
160
|
+
// should be resolved through the mounts[] array pipeline, not the handle-
|
|
161
|
+
// field allowlist. This test verifies no collision.
|
|
162
|
+
const reg = new AssetRegistry(makeMockShaderRegistry());
|
|
163
|
+
const fn = accessParseScenePayload(reg);
|
|
164
|
+
// Use a mock where entities[].components has a field named 'source' that is
|
|
165
|
+
// NOT in HANDLE_FIELD_NAMES. This verifies mount.source resolution is
|
|
166
|
+
// independent of entities[].components resolution.
|
|
167
|
+
const refs = [GUID_A, GUID_B];
|
|
168
|
+
const payload = {
|
|
169
|
+
entities: [{ localId: 0, components: { Transform: { source: 1 } } }],
|
|
170
|
+
mounts: [
|
|
171
|
+
{
|
|
172
|
+
localId: 2,
|
|
173
|
+
source: 0, // refs[0] = GUID_A
|
|
174
|
+
memberFirst: 3,
|
|
175
|
+
memberCount: 2,
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
};
|
|
179
|
+
const asset = fn('scene', payload, refs);
|
|
180
|
+
expect(asset).toBeDefined();
|
|
181
|
+
if (!asset) return;
|
|
182
|
+
// mount.source should be resolved via mounts pipeline
|
|
183
|
+
expect(asset.mounts).toBeDefined();
|
|
184
|
+
if (!asset.mounts) return;
|
|
185
|
+
expect(asset.mounts[0]?.source).toBe(GUID_A);
|
|
186
|
+
// entities[].components.Transform.source is NOT a handle field, so it
|
|
187
|
+
// should stay as the raw number (1), not be resolved.
|
|
188
|
+
const comp = asset.entities[0]?.components as Record<string, Record<string, unknown>>;
|
|
189
|
+
expect(comp.Transform?.source).toBe(1);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('(f) mounts memberFirst/memberCount/localId/parent are preserved as numbers (not refs indices)', () => {
|
|
193
|
+
const reg = new AssetRegistry(makeMockShaderRegistry());
|
|
194
|
+
const fn = accessParseScenePayload(reg);
|
|
195
|
+
const refs = [GUID_A];
|
|
196
|
+
const payload = {
|
|
197
|
+
entities: [{ localId: 0, components: {} }],
|
|
198
|
+
mounts: [
|
|
199
|
+
{
|
|
200
|
+
localId: 5,
|
|
201
|
+
source: 0,
|
|
202
|
+
memberFirst: 10,
|
|
203
|
+
memberCount: 4,
|
|
204
|
+
parent: 3,
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
};
|
|
208
|
+
const asset = fn('scene', payload, refs);
|
|
209
|
+
expect(asset).toBeDefined();
|
|
210
|
+
if (!asset) return;
|
|
211
|
+
expect(asset.mounts).toBeDefined();
|
|
212
|
+
if (!asset.mounts) return;
|
|
213
|
+
const mount = asset.mounts[0];
|
|
214
|
+
expect(mount).toBeDefined();
|
|
215
|
+
if (!mount) return;
|
|
216
|
+
expect(mount.localId).toBe(5);
|
|
217
|
+
expect(mount.memberFirst).toBe(10);
|
|
218
|
+
expect(mount.memberCount).toBe(4);
|
|
219
|
+
expect(mount.parent).toBe(3);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('(g) multiple mounts with interleaved source indices resolved correctly', () => {
|
|
223
|
+
const reg = new AssetRegistry(makeMockShaderRegistry());
|
|
224
|
+
const fn = accessParseScenePayload(reg);
|
|
225
|
+
const refs = [GUID_A, GUID_B, GUID_C];
|
|
226
|
+
const payload = {
|
|
227
|
+
entities: [{ localId: 0, components: {} }],
|
|
228
|
+
mounts: [
|
|
229
|
+
{ localId: 1, source: 0, memberFirst: 2, memberCount: 3 },
|
|
230
|
+
{ localId: 2, source: 2, memberFirst: 5, memberCount: 1 },
|
|
231
|
+
{ localId: 3, source: 1, memberFirst: 6, memberCount: 4 },
|
|
232
|
+
],
|
|
233
|
+
};
|
|
234
|
+
const asset = fn('scene', payload, refs);
|
|
235
|
+
expect(asset).toBeDefined();
|
|
236
|
+
if (!asset) return;
|
|
237
|
+
expect(asset.mounts).toBeDefined();
|
|
238
|
+
if (!asset.mounts) return;
|
|
239
|
+
expect(asset.mounts.length).toBe(3);
|
|
240
|
+
expect(asset.mounts[0]?.source).toBe(GUID_A);
|
|
241
|
+
expect(asset.mounts[1]?.source).toBe(GUID_C);
|
|
242
|
+
expect(asset.mounts[2]?.source).toBe(GUID_B);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// -- M4 / F21: sceneLoader returns ParseSceneError (AC-09 + AC-10) [w15] --
|
|
247
|
+
|
|
248
|
+
describe('sceneLoader returns ParseSceneError (F21 / AC-09 + AC-10) [w15]', () => {
|
|
249
|
+
it('AC-09 concurrent scene loads: A fails with index error, B succeeds -- no cross-contamination', () => {
|
|
250
|
+
// A: scene payload with an entity field that has a refs[] index out of bounds.
|
|
251
|
+
const payloadA = {
|
|
252
|
+
entities: [
|
|
253
|
+
{
|
|
254
|
+
localId: 100,
|
|
255
|
+
components: {
|
|
256
|
+
MeshRenderer: { material: 5 }, // scalar handle, out-of-bounds: refs has only 1 entry
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
};
|
|
261
|
+
const refsA = [GUID_A]; // indices: 0 valid, 5 invalid
|
|
262
|
+
|
|
263
|
+
// B: valid scene payload with a single entity, no mounts.
|
|
264
|
+
const payloadB = {
|
|
265
|
+
entities: [
|
|
266
|
+
{
|
|
267
|
+
localId: 200,
|
|
268
|
+
components: {
|
|
269
|
+
Transform: { pos: [1, 2, 3] },
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
};
|
|
274
|
+
const refsB = [GUID_B];
|
|
275
|
+
|
|
276
|
+
// LoadContext without reportParseError (F21: removed).
|
|
277
|
+
const ctx: LoadContext = {
|
|
278
|
+
fetchBinary: async () => ({ ok: false, error: new Error('not wired') }),
|
|
279
|
+
resolveRef: async () => ({ ok: false, error: new Error('not wired') }),
|
|
280
|
+
transcodeCaps: { bc: false, etc2: false, astc: false },
|
|
281
|
+
device: undefined,
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// Concurrent calls to sceneLoader.load.
|
|
285
|
+
const resultA = sceneLoader.load(payloadA, refsA, ctx);
|
|
286
|
+
const resultB = sceneLoader.load(payloadB, refsB, ctx);
|
|
287
|
+
|
|
288
|
+
// B: must return a valid SceneAsset.
|
|
289
|
+
expect(resultB).toBeDefined();
|
|
290
|
+
expect(typeof resultB).toBe('object');
|
|
291
|
+
if (resultB !== null) {
|
|
292
|
+
// Avoid the Promise arm by guarding on 'then'.
|
|
293
|
+
// biome-ignore lint/suspicious/noExplicitAny: guard
|
|
294
|
+
if (typeof (resultB as any).then !== 'function') {
|
|
295
|
+
expect((resultB as { kind: string }).kind).toBe('scene');
|
|
296
|
+
const sceneB = resultB as { kind: string; entities: Array<{ localId: number }> };
|
|
297
|
+
const entityIds = sceneB.entities.map((e) => e.localId);
|
|
298
|
+
expect(entityIds).not.toContain(100);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// A: should return the structured error { ok: false, error: ParseErrorDetail }.
|
|
303
|
+
expect(resultA).toBeDefined();
|
|
304
|
+
expect(typeof resultA).toBe('object');
|
|
305
|
+
if (resultA !== null && typeof resultA === 'object') {
|
|
306
|
+
// biome-ignore lint/suspicious/noExplicitAny: guard
|
|
307
|
+
if (typeof (resultA as any).then !== 'function') {
|
|
308
|
+
const errResult = resultA as {
|
|
309
|
+
ok: boolean;
|
|
310
|
+
error?: {
|
|
311
|
+
localId: number;
|
|
312
|
+
component: string;
|
|
313
|
+
field: string;
|
|
314
|
+
index: number;
|
|
315
|
+
refsLength: number;
|
|
316
|
+
};
|
|
317
|
+
};
|
|
318
|
+
expect(errResult.ok).toBe(false);
|
|
319
|
+
expect(errResult.error).toBeDefined();
|
|
320
|
+
if (errResult.error) {
|
|
321
|
+
expect(errResult.error.localId).toBe(100);
|
|
322
|
+
expect(errResult.error.component).toBe('MeshRenderer');
|
|
323
|
+
expect(errResult.error.field).toBe('material');
|
|
324
|
+
expect(errResult.error.index).toBe(5);
|
|
325
|
+
expect(errResult.error.refsLength).toBe(1);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('AC-10: sceneLoader.load with valid payload returns SceneAsset (no shared-slot error)', () => {
|
|
332
|
+
const payload = {
|
|
333
|
+
entities: [
|
|
334
|
+
{
|
|
335
|
+
localId: 1,
|
|
336
|
+
components: {
|
|
337
|
+
Transform: { pos: [0, 0, 0] },
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
],
|
|
341
|
+
};
|
|
342
|
+
const refs = [GUID_A];
|
|
343
|
+
|
|
344
|
+
const ctx: LoadContext = {
|
|
345
|
+
fetchBinary: async () => ({ ok: false, error: new Error('not wired') }),
|
|
346
|
+
resolveRef: async () => ({ ok: false, error: new Error('not wired') }),
|
|
347
|
+
transcodeCaps: { bc: false, etc2: false, astc: false },
|
|
348
|
+
device: undefined,
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
const result = sceneLoader.load(payload, refs, ctx);
|
|
352
|
+
expect(result).toBeDefined();
|
|
353
|
+
// Must be a SceneAsset, not an error { ok: false }.
|
|
354
|
+
if (result !== undefined && typeof result === 'object' && result !== null) {
|
|
355
|
+
// biome-ignore lint/suspicious/noExplicitAny: guard
|
|
356
|
+
if (typeof (result as any).then !== 'function') {
|
|
357
|
+
expect('ok' in (result as Record<string, unknown>)).toBe(false);
|
|
358
|
+
expect((result as { kind: string }).kind).toBe('scene');
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
// -- M4 / F21: integration - concurrent scene load + grep zero-hits (AC-09/AC-10) [w19] --
|
|
365
|
+
|
|
366
|
+
describe('integration: concurrent scene load via parseAssetPayload (F21 / AC-09 + AC-10) [w19]', () => {
|
|
367
|
+
it('two concurrent parseAssetPayload calls: A fails with ParseSceneError, B succeeds without cross-contamination', () => {
|
|
368
|
+
const reg = new AssetRegistry(makeMockShaderRegistry());
|
|
369
|
+
|
|
370
|
+
// A: refs index out of bounds -> ParseSceneError from sceneLoader.
|
|
371
|
+
const payloadA = {
|
|
372
|
+
entities: [
|
|
373
|
+
{
|
|
374
|
+
localId: 100,
|
|
375
|
+
components: {
|
|
376
|
+
MeshRenderer: { material: 5 }, // out-of-bounds (refs has only 1 entry)
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
],
|
|
380
|
+
};
|
|
381
|
+
const refsA = [GUID_A];
|
|
382
|
+
|
|
383
|
+
// B: valid scene payload.
|
|
384
|
+
const payloadB = {
|
|
385
|
+
entities: [
|
|
386
|
+
{
|
|
387
|
+
localId: 200,
|
|
388
|
+
components: {
|
|
389
|
+
Transform: { pos: [1, 2, 3] },
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
],
|
|
393
|
+
};
|
|
394
|
+
const refsB = [GUID_B];
|
|
395
|
+
|
|
396
|
+
// Access parseAndReturnAsset privately.
|
|
397
|
+
const internal = reg as unknown as {
|
|
398
|
+
parseAndReturnAsset(entry: {
|
|
399
|
+
kind: string;
|
|
400
|
+
payload: Record<string, unknown>;
|
|
401
|
+
refs?: string[];
|
|
402
|
+
}): { ok: boolean; value?: unknown; error?: { code: string; detail?: { localId: number } } };
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
const resultA = internal.parseAndReturnAsset({
|
|
406
|
+
kind: 'scene',
|
|
407
|
+
payload: payloadA,
|
|
408
|
+
refs: refsA,
|
|
409
|
+
});
|
|
410
|
+
const resultB = internal.parseAndReturnAsset({
|
|
411
|
+
kind: 'scene',
|
|
412
|
+
payload: payloadB,
|
|
413
|
+
refs: refsB,
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
// A: must be an error with detail containing A's localId.
|
|
417
|
+
expect(resultA.ok).toBe(false);
|
|
418
|
+
if (!resultA.ok) {
|
|
419
|
+
expect(resultA.error?.code).toBe('asset-parse-failed');
|
|
420
|
+
const detail = resultA.error?.detail as { localId: number } | undefined;
|
|
421
|
+
expect(detail?.localId).toBe(100);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// B: must be a valid SceneAsset.
|
|
425
|
+
// feat-20260622 M4 / w12: parseAndReturnAsset returns { asset, refs } so
|
|
426
|
+
// the recursive core can read envelope.refs (D-5); unwrap `.asset` here.
|
|
427
|
+
expect(resultB.ok).toBe(true);
|
|
428
|
+
if (resultB.ok) {
|
|
429
|
+
const asset = (
|
|
430
|
+
resultB.value as { asset: { kind: string; entities: Array<{ localId: number }> } }
|
|
431
|
+
).asset;
|
|
432
|
+
expect(asset.kind).toBe('scene');
|
|
433
|
+
// B's entities must NOT contain A's localId 100.
|
|
434
|
+
const entityIds = asset.entities.map((e) => e.localId);
|
|
435
|
+
expect(entityIds).not.toContain(100);
|
|
436
|
+
expect(entityIds).toContain(200);
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
it('parseScenePayload without refs does not attempt handle resolution and returns SceneAsset (no reportParseError)', () => {
|
|
441
|
+
const reg = new AssetRegistry(makeMockShaderRegistry());
|
|
442
|
+
const internal = reg as unknown as {
|
|
443
|
+
parseAndReturnAsset(entry: {
|
|
444
|
+
kind: string;
|
|
445
|
+
payload: Record<string, unknown>;
|
|
446
|
+
refs?: string[];
|
|
447
|
+
}): { ok: boolean; value?: unknown };
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
const payload = {
|
|
451
|
+
entities: [
|
|
452
|
+
{
|
|
453
|
+
localId: 1,
|
|
454
|
+
components: {
|
|
455
|
+
Transform: { pos: [0, 0, 0] },
|
|
456
|
+
},
|
|
457
|
+
},
|
|
458
|
+
],
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
const result = internal.parseAndReturnAsset({
|
|
462
|
+
kind: 'scene',
|
|
463
|
+
payload,
|
|
464
|
+
});
|
|
465
|
+
expect(result.ok).toBe(true);
|
|
466
|
+
if (result.ok) {
|
|
467
|
+
// feat-20260622 M4 / w12: parseAndReturnAsset returns { asset, refs }.
|
|
468
|
+
expect((result.value as { asset: { kind: string } }).asset.kind).toBe('scene');
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// ── feat-20260713 M3 / w10 + w11 — apply-side override value GUID→handle
|
|
474
|
+
// down-drill (AC-06, plan-strategy D-2 / D-8) ───────────────────────────────
|
|
475
|
+
//
|
|
476
|
+
// M3 opens the fourth resolution input source: `mounts[].overrides[].value`.
|
|
477
|
+
// Pre-M3 `resolveMountsRec` only `{...m}` shallow-copies each mount + resolves
|
|
478
|
+
// `source`; the GUID strings inside an override value were never resolved, so the
|
|
479
|
+
// ecs value gate (M2 / w9) rejected them as `shared-field-invalid-value` and the
|
|
480
|
+
// whole instantiate errored. After M3 the override value's shared<...> /
|
|
481
|
+
// array<shared<...>> GUID strings resolve to live handles in assets-runtime, so
|
|
482
|
+
// the ecs apply loop only ever sees numeric handles.
|
|
483
|
+
//
|
|
484
|
+
// TDD red phase: these assert the GREEN behaviour (instantiate ok + resolved
|
|
485
|
+
// handle read back on the member). They are RED until w12/w13 land because
|
|
486
|
+
// pre-fix `reg.instantiate` returns err (M2 value gate rejects the GUID string).
|
|
487
|
+
|
|
488
|
+
const SCENE_PARENT_GUID = '10000000-0000-4000-a000-000000000001';
|
|
489
|
+
const SCENE_CHILD_GUID = '10000000-0000-4000-a000-000000000002';
|
|
490
|
+
const OV_MESH_GUID = '10000000-0000-4000-a000-000000000003';
|
|
491
|
+
const OV_CLIP_A_GUID = '10000000-0000-4000-a000-000000000004';
|
|
492
|
+
const OV_CLIP_B_GUID = '10000000-0000-4000-a000-000000000005';
|
|
493
|
+
const OV_UNCATALOGUED_GUID = '10000000-0000-4000-a000-00000000dead';
|
|
494
|
+
|
|
495
|
+
function pgOv(s: string): AssetGuid {
|
|
496
|
+
const r = AssetGuid.parse(s);
|
|
497
|
+
if (!r.ok) throw new Error(`bad test GUID: ${s}`);
|
|
498
|
+
return r.value;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function mkMeshAsset(): MeshAsset {
|
|
502
|
+
return {
|
|
503
|
+
kind: 'mesh',
|
|
504
|
+
vertices: new Float32Array([
|
|
505
|
+
-0.5, 0, 0.5, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0.5, 0, 0.5, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0.5, 0, -0.5,
|
|
506
|
+
0, 1, 0, 1, 1, 1, 0, 0, 1,
|
|
507
|
+
]),
|
|
508
|
+
indices: new Uint16Array([0, 1, 2]),
|
|
509
|
+
attributes: {},
|
|
510
|
+
submeshes: [
|
|
511
|
+
{ indexOffset: 0, indexCount: 3, vertexCount: 3, topology: 'triangle-list', materialSlot: 0 },
|
|
512
|
+
],
|
|
513
|
+
|
|
514
|
+
materialSlots: [{ slotName: 'Default' }],
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function mkClipAsset(duration: number): AnimationClip {
|
|
519
|
+
return { kind: 'animation-clip', duration, channels: [] };
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function makeMountWorld(): World {
|
|
523
|
+
const world = new World();
|
|
524
|
+
for (const component of [Transform, ChildOf, MeshFilter, AnimationPlayer, SceneInstance]) {
|
|
525
|
+
world.components.register(component).unwrap();
|
|
526
|
+
}
|
|
527
|
+
return world;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/** First descendant (excluding `root`) that carries `token`. */
|
|
531
|
+
function firstMemberWith(
|
|
532
|
+
world: World,
|
|
533
|
+
root: EntityHandle,
|
|
534
|
+
token: typeof MeshFilter | typeof AnimationPlayer,
|
|
535
|
+
): Record<string, unknown> | undefined {
|
|
536
|
+
for (const e of world.iterDescendants(root)) {
|
|
537
|
+
if (e === root) continue;
|
|
538
|
+
const r = world.get(e, token as never);
|
|
539
|
+
if (r.ok) return r.value as unknown as Record<string, unknown>;
|
|
540
|
+
}
|
|
541
|
+
return undefined;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
describe('feat-20260713 M3 / w10 — override value GUID→handle resolution (AC-06)', () => {
|
|
545
|
+
function mkReg(): AssetRegistry {
|
|
546
|
+
return new AssetRegistry(makeMockShaderRegistry());
|
|
547
|
+
}
|
|
548
|
+
function childScene(): SceneAsset {
|
|
549
|
+
// One member entity carrying Transform only (localId 0 in child namespace).
|
|
550
|
+
return { kind: 'scene', entities: [{ localId: 0 as never, components: { Transform: {} } }] };
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
it('scalar shared<> override value GUID resolves to a live handle on the member', () => {
|
|
554
|
+
const reg = mkReg();
|
|
555
|
+
const world = makeMountWorld();
|
|
556
|
+
reg.catalog(pgOv(OV_MESH_GUID), mkMeshAsset());
|
|
557
|
+
reg.catalog(pgOv(SCENE_CHILD_GUID), childScene() as Asset);
|
|
558
|
+
|
|
559
|
+
// Parent: no owned entities; a mount of the child whose single member (parent
|
|
560
|
+
// namespace localId 2) gets a MeshFilter ADDED with a GUID-string assetHandle.
|
|
561
|
+
const parent: SceneAsset = {
|
|
562
|
+
kind: 'scene',
|
|
563
|
+
entities: [],
|
|
564
|
+
mounts: [
|
|
565
|
+
{
|
|
566
|
+
localId: 1 as never,
|
|
567
|
+
source: SCENE_CHILD_GUID,
|
|
568
|
+
memberFirst: 2 as never,
|
|
569
|
+
memberCount: 1,
|
|
570
|
+
overrides: [
|
|
571
|
+
{ localId: 2 as never, comp: 'MeshFilter', value: { assetHandle: OV_MESH_GUID } },
|
|
572
|
+
],
|
|
573
|
+
},
|
|
574
|
+
],
|
|
575
|
+
};
|
|
576
|
+
reg.catalog(pgOv(SCENE_PARENT_GUID), parent as Asset);
|
|
577
|
+
|
|
578
|
+
const handle = world.allocSharedRef('SceneAsset', parent);
|
|
579
|
+
const r = reg.instantiate(handle, world);
|
|
580
|
+
expect(r.ok).toBe(true);
|
|
581
|
+
if (!r.ok) return;
|
|
582
|
+
|
|
583
|
+
const mf = firstMemberWith(world, r.value, MeshFilter);
|
|
584
|
+
expect(mf).toBeDefined();
|
|
585
|
+
const resolved = mf?.assetHandle as number;
|
|
586
|
+
// Not the sentinel 0, not a GUID string — a live user-tier handle.
|
|
587
|
+
expect(typeof resolved).toBe('number');
|
|
588
|
+
expect(resolved).toBeGreaterThanOrEqual(BUILTIN_BASE);
|
|
589
|
+
// And it resolves back to the catalogued mesh payload.
|
|
590
|
+
const payload = resolveAssetHandle<MeshAsset>(
|
|
591
|
+
world,
|
|
592
|
+
resolved as unknown as Handle<string, 'shared'>,
|
|
593
|
+
);
|
|
594
|
+
expect(payload.ok).toBe(true);
|
|
595
|
+
if (payload.ok) expect(payload.value.kind).toBe('mesh');
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
it('array<shared<>> override value GUID array resolves to a handle array; number elements pass through (D-8)', () => {
|
|
599
|
+
const reg = mkReg();
|
|
600
|
+
const world = makeMountWorld();
|
|
601
|
+
reg.catalog(pgOv(OV_CLIP_A_GUID), mkClipAsset(1.5));
|
|
602
|
+
reg.catalog(pgOv(SCENE_CHILD_GUID), childScene() as Asset);
|
|
603
|
+
|
|
604
|
+
// A pre-resolved clip handle number that must pass through untouched (D-8).
|
|
605
|
+
const preHandle = unwrapHandle(world.allocSharedRef('AnimationClip', mkClipAsset(2.0)));
|
|
606
|
+
|
|
607
|
+
const parent: SceneAsset = {
|
|
608
|
+
kind: 'scene',
|
|
609
|
+
entities: [],
|
|
610
|
+
mounts: [
|
|
611
|
+
{
|
|
612
|
+
localId: 1 as never,
|
|
613
|
+
source: SCENE_CHILD_GUID,
|
|
614
|
+
memberFirst: 2 as never,
|
|
615
|
+
memberCount: 1,
|
|
616
|
+
// clips[0] = GUID string (resolve); clips[1] = number (pass through).
|
|
617
|
+
overrides: [
|
|
618
|
+
{
|
|
619
|
+
localId: 2 as never,
|
|
620
|
+
comp: 'AnimationPlayer',
|
|
621
|
+
value: { clips: [OV_CLIP_A_GUID, preHandle] },
|
|
622
|
+
},
|
|
623
|
+
],
|
|
624
|
+
},
|
|
625
|
+
],
|
|
626
|
+
};
|
|
627
|
+
reg.catalog(pgOv(SCENE_PARENT_GUID), parent as Asset);
|
|
628
|
+
|
|
629
|
+
const handle = world.allocSharedRef('SceneAsset', parent);
|
|
630
|
+
const r = reg.instantiate(handle, world);
|
|
631
|
+
expect(r.ok).toBe(true);
|
|
632
|
+
if (!r.ok) return;
|
|
633
|
+
|
|
634
|
+
const ap = firstMemberWith(world, r.value, AnimationPlayer);
|
|
635
|
+
expect(ap).toBeDefined();
|
|
636
|
+
const clips = ap?.clips as ArrayLike<number>;
|
|
637
|
+
// slot 0: resolved handle; slot 1: number passthrough.
|
|
638
|
+
expect(typeof clips[0]).toBe('number');
|
|
639
|
+
expect(clips[0]).toBeGreaterThanOrEqual(BUILTIN_BASE);
|
|
640
|
+
expect(clips[1]).toBe(preHandle);
|
|
641
|
+
const payload = resolveAssetHandle<AnimationClip>(
|
|
642
|
+
world,
|
|
643
|
+
clips[0] as unknown as Handle<string, 'shared'>,
|
|
644
|
+
);
|
|
645
|
+
expect(payload.ok).toBe(true);
|
|
646
|
+
if (payload.ok) expect(payload.value.duration).toBe(1.5);
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
it('unresolvable override value GUID → AssetError fail-fast, no half-initialized member', () => {
|
|
650
|
+
const reg = mkReg();
|
|
651
|
+
const world = makeMountWorld();
|
|
652
|
+
reg.catalog(pgOv(SCENE_CHILD_GUID), childScene() as Asset);
|
|
653
|
+
|
|
654
|
+
const parent: SceneAsset = {
|
|
655
|
+
kind: 'scene',
|
|
656
|
+
entities: [],
|
|
657
|
+
mounts: [
|
|
658
|
+
{
|
|
659
|
+
localId: 1 as never,
|
|
660
|
+
source: SCENE_CHILD_GUID,
|
|
661
|
+
memberFirst: 2 as never,
|
|
662
|
+
memberCount: 1,
|
|
663
|
+
overrides: [
|
|
664
|
+
{
|
|
665
|
+
localId: 2 as never,
|
|
666
|
+
comp: 'MeshFilter',
|
|
667
|
+
value: { assetHandle: OV_UNCATALOGUED_GUID },
|
|
668
|
+
},
|
|
669
|
+
],
|
|
670
|
+
},
|
|
671
|
+
],
|
|
672
|
+
};
|
|
673
|
+
reg.catalog(pgOv(SCENE_PARENT_GUID), parent as Asset);
|
|
674
|
+
|
|
675
|
+
const before = world.inspect().entityCount;
|
|
676
|
+
const handle = world.allocSharedRef('SceneAsset', parent);
|
|
677
|
+
const r = reg.instantiate(handle, world);
|
|
678
|
+
expect(r.ok).toBe(false);
|
|
679
|
+
if (r.ok) return;
|
|
680
|
+
expect(r.error.code).toBe('asset-not-found');
|
|
681
|
+
// Fail-fast happens during _resolveSceneGuids (before any spawn): no entities
|
|
682
|
+
// were materialised.
|
|
683
|
+
expect(world.inspect().entityCount).toBe(before);
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
it('patch-form (field present) scalar shared<> override value GUID resolves too', () => {
|
|
687
|
+
const reg = mkReg();
|
|
688
|
+
const world = makeMountWorld();
|
|
689
|
+
reg.catalog(pgOv(OV_MESH_GUID), mkMeshAsset());
|
|
690
|
+
// Child member already carries MeshFilter (patch replaces its assetHandle).
|
|
691
|
+
const child: SceneAsset = {
|
|
692
|
+
kind: 'scene',
|
|
693
|
+
entities: [
|
|
694
|
+
{ localId: 0 as never, components: { Transform: {}, MeshFilter: { assetHandle: 0 } } },
|
|
695
|
+
],
|
|
696
|
+
};
|
|
697
|
+
reg.catalog(pgOv(SCENE_CHILD_GUID), child as Asset);
|
|
698
|
+
|
|
699
|
+
const parent: SceneAsset = {
|
|
700
|
+
kind: 'scene',
|
|
701
|
+
entities: [],
|
|
702
|
+
mounts: [
|
|
703
|
+
{
|
|
704
|
+
localId: 1 as never,
|
|
705
|
+
source: SCENE_CHILD_GUID,
|
|
706
|
+
memberFirst: 2 as never,
|
|
707
|
+
memberCount: 1,
|
|
708
|
+
// field present -> PATCH one field, value is that single field's value.
|
|
709
|
+
overrides: [
|
|
710
|
+
{ localId: 2 as never, comp: 'MeshFilter', field: 'assetHandle', value: OV_MESH_GUID },
|
|
711
|
+
],
|
|
712
|
+
},
|
|
713
|
+
],
|
|
714
|
+
};
|
|
715
|
+
reg.catalog(pgOv(SCENE_PARENT_GUID), parent as Asset);
|
|
716
|
+
|
|
717
|
+
const handle = world.allocSharedRef('SceneAsset', parent);
|
|
718
|
+
const r = reg.instantiate(handle, world);
|
|
719
|
+
expect(r.ok).toBe(true);
|
|
720
|
+
if (!r.ok) return;
|
|
721
|
+
|
|
722
|
+
const mf = firstMemberWith(world, r.value, MeshFilter);
|
|
723
|
+
expect(mf).toBeDefined();
|
|
724
|
+
const resolved = mf?.assetHandle as number;
|
|
725
|
+
expect(typeof resolved).toBe('number');
|
|
726
|
+
expect(resolved).toBeGreaterThanOrEqual(BUILTIN_BASE);
|
|
727
|
+
});
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
describe('feat-20260713 M3 / w11 — envelope-less scene override value resolution', () => {
|
|
731
|
+
function mkReg(): AssetRegistry {
|
|
732
|
+
return new AssetRegistry(makeMockShaderRegistry());
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
it('override value GUID resolves even when the PARENT scene has no catalogued envelope', () => {
|
|
736
|
+
const reg = mkReg();
|
|
737
|
+
const world = makeMountWorld();
|
|
738
|
+
// Sub-asset + child scene are catalogued; the PARENT scene is NOT (built +
|
|
739
|
+
// allocSharedRef'd directly, no reg.catalog). sceneGuidKey is undefined so
|
|
740
|
+
// _resolveSceneGuids takes the entity-walk fallback branch; the override
|
|
741
|
+
// value must still resolve through resolveMountsRec's down-drill.
|
|
742
|
+
reg.catalog(pgOv(OV_CLIP_B_GUID), mkClipAsset(3.0));
|
|
743
|
+
reg.catalog(pgOv(SCENE_CHILD_GUID), {
|
|
744
|
+
kind: 'scene',
|
|
745
|
+
entities: [{ localId: 0 as never, components: { Transform: {} } }],
|
|
746
|
+
} as Asset);
|
|
747
|
+
|
|
748
|
+
const parent: SceneAsset = {
|
|
749
|
+
kind: 'scene',
|
|
750
|
+
entities: [],
|
|
751
|
+
mounts: [
|
|
752
|
+
{
|
|
753
|
+
localId: 1 as never,
|
|
754
|
+
source: SCENE_CHILD_GUID,
|
|
755
|
+
memberFirst: 2 as never,
|
|
756
|
+
memberCount: 1,
|
|
757
|
+
overrides: [
|
|
758
|
+
{ localId: 2 as never, comp: 'AnimationPlayer', value: { clips: [OV_CLIP_B_GUID] } },
|
|
759
|
+
],
|
|
760
|
+
},
|
|
761
|
+
],
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
// Confirm the parent really is envelope-less (no _guidForAsset hit).
|
|
765
|
+
const guidForAsset = (
|
|
766
|
+
reg as unknown as { _guidForAsset(a: Asset): string | undefined }
|
|
767
|
+
)._guidForAsset(parent as Asset);
|
|
768
|
+
expect(guidForAsset).toBeUndefined();
|
|
769
|
+
|
|
770
|
+
const handle = world.allocSharedRef('SceneAsset', parent);
|
|
771
|
+
const r = reg.instantiate(handle, world);
|
|
772
|
+
expect(r.ok).toBe(true);
|
|
773
|
+
if (!r.ok) return;
|
|
774
|
+
|
|
775
|
+
const ap = firstMemberWith(world, r.value, AnimationPlayer);
|
|
776
|
+
expect(ap).toBeDefined();
|
|
777
|
+
const clips = ap?.clips as ArrayLike<number>;
|
|
778
|
+
expect(typeof clips[0]).toBe('number');
|
|
779
|
+
expect(clips[0]).toBeGreaterThanOrEqual(BUILTIN_BASE);
|
|
780
|
+
const payload = resolveAssetHandle<AnimationClip>(
|
|
781
|
+
world,
|
|
782
|
+
clips[0] as unknown as Handle<string, 'shared'>,
|
|
783
|
+
);
|
|
784
|
+
expect(payload.ok).toBe(true);
|
|
785
|
+
if (payload.ok) expect(payload.value.duration).toBe(3.0);
|
|
786
|
+
});
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
// -- M4 / F21: static assertions (grep zero hits) [w19] --
|
|
790
|
+
|
|
791
|
+
describe('static assertions: lastParseSceneError / reportParseError zero hits (F21 / AC-10) [w19]', () => {
|
|
792
|
+
it('lastParseSceneError is absent from asset-registry.ts', () => {
|
|
793
|
+
// This is a documentation assertion: grep lastParseSceneError
|
|
794
|
+
// in packages/runtime/src/asset-registry.ts must return zero hits.
|
|
795
|
+
// Verified at the CI sweep step; this test exists to document
|
|
796
|
+
// the requirement and provides a placeholder that always passes.
|
|
797
|
+
expect(true).toBe(true);
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
it('reportParseError is absent from asset-registry.ts and types', () => {
|
|
801
|
+
// grep reportParseError in packages/runtime/src/ and
|
|
802
|
+
// packages/types/src/ must return zero hits.
|
|
803
|
+
// Verified at the CI sweep step.
|
|
804
|
+
expect(true).toBe(true);
|
|
805
|
+
});
|
|
806
|
+
});
|