@forgeax/engine-runtime 0.1.23 → 0.1.24
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 +15 -6
- package/dist/__tests__/advance-animation-player.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/advance-animation-player.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-aabb.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-aabb.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-bootstrap.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-bootstrap.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-builtin-nineslice.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-builtin-nineslice.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-d9-tile-sampler-soft-warn.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-d9-tile-sampler-soft-warn.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-guid.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-guid.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-material-validate.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-material-validate.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-mesh-fail-fast.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-mesh-fail-fast.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-scene.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-scene.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-sprite-slices-validate.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-sprite-slices-validate.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-renderer-assembly.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-renderer-assembly.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/asset.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/bloom-gating.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/bloom-gating.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/builtin-guid-ssot.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/builtin-guid-ssot.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/builtin-pack.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/builtin-pack.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/camera-antialias.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/camera-antialias.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/camera-bloom.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/camera-bloom.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/camera-clear-schema.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/camera-clear-schema.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/camera-ortho.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/camera-ortho.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/cluster-binner.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/cluster-binner.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/create-renderer-fallback-shader-manifest.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/create-renderer-fallback-shader-manifest.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/create-renderer-uniform-fallback.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/create-renderer-uniform-fallback.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/cubemap-upload.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/cubemap-upload.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/dev-import-transport.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/dev-import-transport.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/engine-metrics.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/engine-metrics.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/equirect-projection-error.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/equirect-projection-error.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/font-asset-load.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/font-asset-load.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/fxaa-intermediate-texture.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/fxaa-intermediate-texture.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/fxaa-pipeline.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/fxaa-pipeline.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-residency.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/gpu-residency.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-resource-release.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/gpu-resource-release.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/handle-quad.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/handle-quad.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/hdrp-bgl-slots.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/hdrp-bgl-slots.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/hdrp-grid-invalid.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/hdrp-grid-invalid.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/hdrp-index-list-overflow-once.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/hdrp-index-list-overflow-once.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/hdrp-light-budget.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/hdrp-light-budget.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/hdrp-pipeline-asset-config.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/hdrp-pipeline-asset-config.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/helpers/renderer-device-loss-dawn.d.ts +25 -0
- package/dist/__tests__/helpers/renderer-device-loss-dawn.d.ts.map +1 -0
- package/dist/__tests__/helpers/standard-material-manifest.d.ts +0 -1
- package/dist/__tests__/helpers/standard-material-manifest.d.ts.map +1 -1
- package/dist/__tests__/ibl-pipeline-cache.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/ibl-pipeline-cache.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/lazy-catalog.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/lazy-catalog.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/load-by-guid-hdr.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/load-by-guid-hdr.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/load-by-guid-prod-material-parent.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/load-by-guid-prod-material-parent.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/load-by-guid-prod.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/load-by-guid-prod.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/loader-registry.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/loader-registry.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/m7-hdrp-demo-shape.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/m7-hdrp-demo-shape.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mipmap-formula.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/mipmap-formula.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mipmap-pipeline-cache.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/mipmap-pipeline-cache.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/parse-asset-payload-material.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/parse-asset-payload-material.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/parse-asset-payload-texture.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/parse-asset-payload-texture.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/parse-scene-payload-refs.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/parse-scene-payload-refs.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pass-selector.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/pass-selector.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline-builder.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/pipeline-builder.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline-cache-key-topology.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/pipeline-cache-key-topology.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline-vertex-stride-branch.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/pipeline-vertex-stride-branch.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/pipeline.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/post-process-register.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/post-process-register.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/propagate-transforms.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/propagate-transforms.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/record-all-topology.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/record-all-topology.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/record-draw-branch.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/record-draw-branch.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/record-strip-index-format.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/record-strip-index-format.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/register-with-guid-rgba16float.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/register-with-guid-rgba16float.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-data.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/render-data.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-error-exhaustive.test-d.d.ts.map +1 -1
- package/dist/__tests__/render-query-regression.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/render-query-regression.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-record-submesh.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/render-system-record-submesh.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-system-record-warn-once.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/render-system-record-warn-once.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-device-loss-multi-world.integration.test.d.ts +2 -0
- package/dist/__tests__/renderer-device-loss-multi-world.integration.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-device-loss-provider-feasibility.dawn.test.d.ts +2 -0
- package/dist/__tests__/renderer-device-loss-provider-feasibility.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-device-loss-recovery.dawn.test.d.ts +2 -0
- package/dist/__tests__/renderer-device-loss-recovery.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-draw-world.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/renderer-draw-world.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/renderer-recovery-consumer.integration.test.d.ts +2 -0
- package/dist/__tests__/renderer-recovery-consumer.integration.test.d.ts.map +1 -0
- package/dist/__tests__/renderstate-pipeline-cache.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/renderstate-pipeline-cache.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/resolve-scene-guids.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/resolve-scene-guids.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/shadow-skip-non-triangle.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/shadow-skip-non-triangle.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-errors-kebab-case.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-errors-kebab-case.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-instances-coexist.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-instances-coexist.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-m2-extract-happy.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-m2-extract-happy.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-m2-extract-joint-error.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-m2-extract-joint-error.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-m2-extract-null-assets.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-m2-extract-null-assets.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-m2-extract-skeleton-error.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-m2-extract-skeleton-error.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-m3-record-cache.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-m3-record-cache.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-palette-extract.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-palette-extract.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin-pipeline-routing.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin-pipeline-routing.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skin.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skin.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skybox-extract.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skybox-extract.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skybox-shader-compile.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skybox-shader-compile.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skylight-bind-group.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skylight-bind-group.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skylight-component.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skylight-component.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skylight-fallback-path.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skylight-fallback-path.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skylight-pipeline-layout.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skylight-pipeline-layout.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/skylight-solid-color-extract.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/skylight-solid-color-extract.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sprite-animation-tick-boundary.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/sprite-animation-tick-boundary.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sprite-animation-tick-clamp.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/sprite-animation-tick-clamp.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sprite-animation-tick-frame-duration-negative.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/sprite-animation-tick-frame-duration-negative.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sprite-animation-tick-frame-duration-zero.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/sprite-animation-tick-frame-duration-zero.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sprite-animation-tick-loop.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/sprite-animation-tick-loop.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sprite-animation-tick-override-probe.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/sprite-animation-tick-override-probe.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sprite-animation-tick-regions-mismatch.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/sprite-animation-tick-regions-mismatch.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/storage-buffer-caps.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/storage-buffer-caps.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/systems.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/systems.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/tonemap-hdr-target.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/tonemap-hdr-target.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/tonemap-pipeline-split.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/tonemap-pipeline-split.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/tonemap.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/tonemap.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/transparent-sort-config-get.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/transparent-sort-config-get.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/transparent-sort-config-set.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/transparent-sort-config-set.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/transparent-sort.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/transparent-sort.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/upload-texture-consistency.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/upload-texture-consistency.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/verify-revisions.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/verify-revisions.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/wire-default-loaders.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/wire-default-loaders.cases.unit.test.d.ts.map +1 -0
- package/dist/__tests__/zero-camera-clear-fallback.cases.unit.test.d.ts +2 -0
- package/dist/__tests__/zero-camera-clear-fallback.cases.unit.test.d.ts.map +1 -0
- package/dist/dev-import-transport.d.ts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +30 -30
- package/src/__tests__/advance-animation-player.cases.unit.test.ts +1666 -0
- package/src/__tests__/asset-registry-aabb.cases.unit.test.ts +676 -0
- package/src/__tests__/asset-registry-bootstrap.cases.unit.test.ts +472 -0
- package/src/__tests__/asset-registry-builtin-nineslice.cases.unit.test.ts +481 -0
- package/src/__tests__/asset-registry-d9-tile-sampler-soft-warn.cases.unit.test.ts +568 -0
- package/src/__tests__/asset-registry-guid.cases.unit.test.ts +500 -0
- package/src/__tests__/asset-registry-material-validate.cases.unit.test.ts +756 -0
- package/src/__tests__/asset-registry-mesh-fail-fast.cases.unit.test.ts +591 -0
- package/src/__tests__/asset-registry-scene.cases.unit.test.ts +501 -0
- package/src/__tests__/asset-registry-sprite-slices-validate.cases.unit.test.ts +536 -0
- package/src/__tests__/asset-renderer-assembly.cases.unit.test.ts +496 -0
- package/src/__tests__/asset.cases.unit.test.ts +439 -0
- package/src/__tests__/bind-group-cache-keying.test.ts +6 -2
- package/src/__tests__/bloom-gating.cases.unit.test.ts +428 -0
- package/src/__tests__/builtin-guid-ssot.cases.unit.test.ts +472 -0
- package/src/__tests__/builtin-pack.cases.unit.test.ts +639 -0
- package/src/__tests__/builtin-shader-register-e2e.test.ts +1 -1
- package/src/__tests__/camera-antialias.cases.unit.test.ts +372 -0
- package/src/__tests__/camera-bloom.cases.unit.test.ts +396 -0
- package/src/__tests__/camera-clear-schema.cases.unit.test.ts +391 -0
- package/src/__tests__/camera-ortho.cases.unit.test.ts +484 -0
- package/src/__tests__/cluster-binner.cases.unit.test.ts +817 -0
- package/src/__tests__/collect-scene-mount-collapse.test.ts +5 -1
- package/src/__tests__/create-renderer-fallback-shader-manifest.cases.unit.test.ts +345 -0
- package/src/__tests__/create-renderer-uniform-fallback.cases.unit.test.ts +445 -0
- package/src/__tests__/cubemap-upload.cases.unit.test.ts +593 -0
- package/src/__tests__/dawn/ibl-precompute-readback.dawn.test.ts +184 -17
- package/src/__tests__/dawn/material-alpha.dawn.test.ts +17 -4
- package/src/__tests__/derive-bgl-integration.test.ts +8 -8
- package/src/__tests__/dev-import-transport.cases.unit.test.ts +503 -0
- package/src/__tests__/engine-metrics.cases.unit.test.ts +371 -0
- package/src/__tests__/equirect-projection-error.cases.unit.test.ts +416 -0
- package/src/__tests__/font-asset-load.cases.unit.test.ts +510 -0
- package/src/__tests__/fxaa-intermediate-texture.cases.unit.test.ts +690 -0
- package/src/__tests__/fxaa-pipeline.cases.unit.test.ts +546 -0
- package/src/__tests__/gpu-residency.cases.unit.test.ts +479 -0
- package/src/__tests__/gpu-resource-release.cases.unit.test.ts +929 -0
- package/src/__tests__/handle-quad.cases.unit.test.ts +491 -0
- package/src/__tests__/hdrp-bgl-slots.cases.unit.test.ts +451 -0
- package/src/__tests__/hdrp-grid-invalid.cases.unit.test.ts +408 -0
- package/src/__tests__/hdrp-index-list-overflow-once.cases.unit.test.ts +405 -0
- package/src/__tests__/hdrp-light-budget.cases.unit.test.ts +483 -0
- package/src/__tests__/hdrp-pipeline-asset-config.cases.unit.test.ts +441 -0
- package/src/__tests__/helpers/renderer-device-loss-dawn.ts +69 -0
- package/src/__tests__/helpers/standard-material-manifest.ts +11 -15
- package/src/__tests__/ibl-pipeline-cache.cases.unit.test.ts +415 -0
- package/src/__tests__/lazy-catalog.cases.unit.test.ts +848 -0
- package/src/__tests__/light-gpu-byte-neutral.unit.test.ts +6 -5
- package/src/__tests__/load-by-guid-hdr.cases.unit.test.ts +582 -0
- package/src/__tests__/load-by-guid-prod-material-parent.cases.unit.test.ts +1514 -0
- package/src/__tests__/load-by-guid-prod.cases.unit.test.ts +557 -0
- package/src/__tests__/loader-registry.cases.unit.test.ts +767 -0
- package/src/__tests__/m7-hdrp-demo-shape.cases.unit.test.ts +361 -0
- package/src/__tests__/material-texture-uv-scale.unit.test.ts +2 -2
- package/src/__tests__/materials.unit.test.ts +29 -10
- package/src/__tests__/mipmap-formula.cases.unit.test.ts +434 -0
- package/src/__tests__/mipmap-pipeline-cache.cases.unit.test.ts +490 -0
- package/src/__tests__/parse-asset-payload-material.cases.unit.test.ts +565 -0
- package/src/__tests__/parse-asset-payload-texture.cases.unit.test.ts +720 -0
- package/src/__tests__/parse-scene-payload-refs.cases.unit.test.ts +767 -0
- package/src/__tests__/pass-selector.cases.unit.test.ts +376 -0
- package/src/__tests__/pbr-pipeline.unit.test.ts +23 -5
- package/src/__tests__/pipeline-builder.cases.unit.test.ts +624 -0
- package/src/__tests__/pipeline-cache-key-topology.cases.unit.test.ts +807 -0
- package/src/__tests__/pipeline-vertex-stride-branch.cases.unit.test.ts +379 -0
- package/src/__tests__/pipeline.cases.unit.test.ts +370 -0
- package/src/__tests__/post-process-register.cases.unit.test.ts +405 -0
- package/src/__tests__/propagate-transforms.cases.unit.test.ts +502 -0
- package/src/__tests__/propagate-transforms.dawn.test.ts +19 -2
- package/src/__tests__/record-all-topology.cases.unit.test.ts +608 -0
- package/src/__tests__/record-draw-branch.cases.unit.test.ts +621 -0
- package/src/__tests__/record-strip-index-format.cases.unit.test.ts +419 -0
- package/src/__tests__/register-with-guid-rgba16float.cases.unit.test.ts +469 -0
- package/src/__tests__/render-data.cases.unit.test.ts +553 -0
- package/src/__tests__/render-error-exhaustive.test-d.ts +1 -0
- package/src/__tests__/render-query-regression.cases.unit.test.ts +531 -0
- package/src/__tests__/render-system-record-submesh.cases.unit.test.ts +740 -0
- package/src/__tests__/render-system-record-warn-once.cases.unit.test.ts +418 -0
- package/src/__tests__/render-system-record.test.ts +15 -33
- package/src/__tests__/renderer-device-loss-multi-world.integration.test.ts +103 -0
- package/src/__tests__/renderer-device-loss-provider-feasibility.dawn.test.ts +124 -0
- package/src/__tests__/renderer-device-loss-recovery.dawn.test.ts +50 -0
- package/src/__tests__/renderer-draw-world.cases.unit.test.ts +514 -0
- package/src/__tests__/renderer-recovery-consumer.integration.test.ts +96 -0
- package/src/__tests__/renderstate-pipeline-cache.cases.unit.test.ts +631 -0
- package/src/__tests__/resolve-scene-guids.cases.unit.test.ts +1123 -0
- package/src/__tests__/shadow-caster-empty-schema.test.ts +1 -1
- package/src/__tests__/shadow-fields-observable.dawn.test.ts +2 -2
- package/src/__tests__/shadow-m1.dawn.test.ts +1 -1
- package/src/__tests__/shadow-skip-non-triangle.cases.unit.test.ts +681 -0
- package/src/__tests__/skin-errors-kebab-case.cases.unit.test.ts +475 -0
- package/src/__tests__/skin-extract-getarrayview-count.unit.test.ts +5 -4
- package/src/__tests__/skin-instances-coexist.cases.unit.test.ts +375 -0
- package/src/__tests__/skin-m2-extract-happy.cases.unit.test.ts +592 -0
- package/src/__tests__/skin-m2-extract-joint-error.cases.unit.test.ts +619 -0
- package/src/__tests__/skin-m2-extract-null-assets.cases.unit.test.ts +569 -0
- package/src/__tests__/skin-m2-extract-skeleton-error.cases.unit.test.ts +585 -0
- package/src/__tests__/skin-m3-record-cache.cases.unit.test.ts +457 -0
- package/src/__tests__/skin-palette-extract.cases.unit.test.ts +635 -0
- package/src/__tests__/skin-pipeline-routing.cases.unit.test.ts +411 -0
- package/src/__tests__/skin.cases.unit.test.ts +421 -0
- package/src/__tests__/skybox-extract.cases.unit.test.ts +511 -0
- package/src/__tests__/skybox-shader-compile.cases.unit.test.ts +437 -0
- package/src/__tests__/skylight-bind-group.cases.unit.test.ts +642 -0
- package/src/__tests__/skylight-component.cases.unit.test.ts +386 -0
- package/src/__tests__/skylight-fallback-path.cases.unit.test.ts +566 -0
- package/src/__tests__/skylight-lazy-projection.test.ts +35 -20
- package/src/__tests__/skylight-pipeline-layout.cases.unit.test.ts +506 -0
- package/src/__tests__/skylight-solid-color-extract.cases.unit.test.ts +399 -0
- package/src/__tests__/sprite-animation-tick-boundary.cases.unit.test.ts +521 -0
- package/src/__tests__/sprite-animation-tick-clamp.cases.unit.test.ts +453 -0
- package/src/__tests__/sprite-animation-tick-frame-duration-negative.cases.unit.test.ts +447 -0
- package/src/__tests__/sprite-animation-tick-frame-duration-zero.cases.unit.test.ts +395 -0
- package/src/__tests__/sprite-animation-tick-loop.cases.unit.test.ts +494 -0
- package/src/__tests__/sprite-animation-tick-override-probe.cases.unit.test.ts +439 -0
- package/src/__tests__/sprite-animation-tick-regions-mismatch.cases.unit.test.ts +466 -0
- package/src/__tests__/sprite-lit-bgl-byte-identical.test.ts +5 -5
- package/src/__tests__/sprite-lit-pipeline-cache-key.test.ts +1 -1
- package/src/__tests__/ssao-pipelines.test.ts +1 -1
- package/src/__tests__/storage-buffer-caps.cases.unit.test.ts +373 -0
- package/src/__tests__/systems.cases.unit.test.ts +425 -0
- package/src/__tests__/tilemap-chunk-extract.overhang-bounds.test.ts +1 -1
- package/src/__tests__/tonemap-hdr-target.cases.unit.test.ts +670 -0
- package/src/__tests__/tonemap-pipeline-split.cases.unit.test.ts +867 -0
- package/src/__tests__/tonemap.cases.unit.test.ts +446 -0
- package/src/__tests__/transparent-sort-config-get.cases.unit.test.ts +395 -0
- package/src/__tests__/transparent-sort-config-set.cases.unit.test.ts +424 -0
- package/src/__tests__/transparent-sort.cases.unit.test.ts +684 -0
- package/src/__tests__/upload-texture-consistency.cases.unit.test.ts +478 -0
- package/src/__tests__/verify-revisions.cases.unit.test.ts +619 -0
- package/src/__tests__/wire-default-loaders.cases.unit.test.ts +461 -0
- package/src/__tests__/zero-camera-clear-fallback.cases.unit.test.ts +374 -0
- package/src/dev-import-transport.ts +2 -1
- package/dist/__tests__/asset.unit.test.d.ts +0 -2
- package/dist/__tests__/asset.unit.test.d.ts.map +0 -1
- package/dist/__tests__/pipeline.unit.test.d.ts +0 -2
- package/dist/__tests__/pipeline.unit.test.d.ts.map +0 -1
- package/dist/__tests__/systems.unit.test.d.ts +0 -2
- package/dist/__tests__/systems.unit.test.d.ts.map +0 -1
- package/src/__tests__/asset.unit.test.ts +0 -7113
- package/src/__tests__/pipeline.unit.test.ts +0 -5279
- package/src/__tests__/systems.unit.test.ts +0 -9061
|
@@ -0,0 +1,848 @@
|
|
|
1
|
+
// @ts-nocheck — merged file: cross-source type narrowing failures from blocks originally outside src/ rootDir
|
|
2
|
+
// Consolidated by feat-20260609-test-pool-startup-reduction-merge-tiny-test-files
|
|
3
|
+
// biome-ignore-all lint/complexity/noUselessLoneBlockStatements: block-scope isolation between merged source files (consolidation paradigm)
|
|
4
|
+
//
|
|
5
|
+
// Source files (N=33):
|
|
6
|
+
// - packages/runtime/__tests__/asset-registry.test.ts
|
|
7
|
+
// - packages/runtime/__tests__/loader-registry.test.ts
|
|
8
|
+
// - packages/runtime/__tests__/wire-default-loaders.test.ts
|
|
9
|
+
// - packages/runtime/src/__tests__/asset-registry-aabb.test.ts
|
|
10
|
+
// - packages/runtime/src/__tests__/asset-registry-builtin-nineslice.test.ts
|
|
11
|
+
// - packages/runtime/src/__tests__/asset-registry-d9-tile-sampler-soft-warn.test.ts
|
|
12
|
+
// - packages/runtime/src/__tests__/asset-registry-guid.test.ts
|
|
13
|
+
// - packages/runtime/src/__tests__/asset-registry-material-validate.test.ts
|
|
14
|
+
// - packages/runtime/src/__tests__/asset-registry-mesh-fail-fast.test.ts
|
|
15
|
+
// - packages/runtime/src/__tests__/asset-registry-scene.test.ts
|
|
16
|
+
// - packages/runtime/src/__tests__/asset-registry-sprite-slices-validate.test.ts
|
|
17
|
+
// - packages/runtime/src/__tests__/auto-select.test.ts
|
|
18
|
+
// - packages/runtime/src/__tests__/bindgroup-resize-invalidation.test.ts
|
|
19
|
+
// - packages/runtime/src/__tests__/builtin-guid-ssot.test.ts
|
|
20
|
+
// - packages/runtime/src/__tests__/builtin-pack.test.ts
|
|
21
|
+
// - packages/runtime/src/__tests__/cube-texture-narrowing.test.ts
|
|
22
|
+
// - packages/runtime/src/__tests__/cubemap-upload.test.ts
|
|
23
|
+
// - packages/runtime/src/__tests__/dev-import-transport.test.ts
|
|
24
|
+
// - packages/runtime/src/__tests__/font-asset-load.test.ts
|
|
25
|
+
// - packages/runtime/src/__tests__/handle-quad.test.ts
|
|
26
|
+
// - packages/runtime/src/__tests__/lazy-catalog.test.ts
|
|
27
|
+
// - packages/runtime/src/__tests__/load-by-guid-hdr.test.ts
|
|
28
|
+
// - packages/runtime/src/__tests__/load-by-guid-prod-material-parent.test.ts
|
|
29
|
+
// - packages/runtime/src/__tests__/load-by-guid-prod.test.ts
|
|
30
|
+
// - packages/runtime/src/__tests__/mipmap-formula.test.ts
|
|
31
|
+
// - packages/runtime/src/__tests__/mipmap-pipeline-cache.test.ts
|
|
32
|
+
// - packages/runtime/src/__tests__/parse-asset-payload-material.test.ts
|
|
33
|
+
// - packages/runtime/src/__tests__/parse-asset-payload-texture.test.ts
|
|
34
|
+
// - packages/runtime/src/__tests__/parse-scene-payload-refs.test.ts
|
|
35
|
+
// - packages/runtime/src/__tests__/register-with-guid-rgba16float.test.ts
|
|
36
|
+
// - packages/runtime/src/__tests__/resolve-scene-guids.test.ts
|
|
37
|
+
// - packages/runtime/src/__tests__/upload-texture-consistency.test.ts
|
|
38
|
+
// - packages/runtime/src/__tests__/verify-revisions.test.ts
|
|
39
|
+
//
|
|
40
|
+
// Paradigm: each block-scope wraps a source file. ancestorTitles[0] is the
|
|
41
|
+
// source-preserved inner describe (NOT the source filename for these 3 files
|
|
42
|
+
// — recovery path: vitest report ancestorTitles -> grep this file -> upstream
|
|
43
|
+
// `// ─── from <name>.test.ts ───` block separator -> source filename).
|
|
44
|
+
// Top-level imports merged + deduped.
|
|
45
|
+
|
|
46
|
+
import { readFileSync } from 'node:fs';
|
|
47
|
+
import { fileURLToPath } from 'node:url';
|
|
48
|
+
import * as assetRegistryModule from '@forgeax/engine-assets-runtime';
|
|
49
|
+
import {
|
|
50
|
+
AssetRegistry,
|
|
51
|
+
animationClipLoader,
|
|
52
|
+
buildSceneChildContext,
|
|
53
|
+
fontLoader,
|
|
54
|
+
getOrCreateMipmapPipeline,
|
|
55
|
+
HANDLE_CUBE,
|
|
56
|
+
HANDLE_CYLINDER,
|
|
57
|
+
HANDLE_QUAD,
|
|
58
|
+
HANDLE_SPHERE,
|
|
59
|
+
HANDLE_TRIANGLE,
|
|
60
|
+
INLINE_PACK_LOADERS,
|
|
61
|
+
LoaderRegistry,
|
|
62
|
+
materialLoader,
|
|
63
|
+
meshLoader,
|
|
64
|
+
mipmapCacheSize,
|
|
65
|
+
numMipLevels,
|
|
66
|
+
PACK_ARTIFACT_LOADERS,
|
|
67
|
+
resolveAssetHandle,
|
|
68
|
+
sceneLoader,
|
|
69
|
+
skeletonLoader,
|
|
70
|
+
skinLoader,
|
|
71
|
+
textureLoader,
|
|
72
|
+
walkMaterialPassesOverSharedRefs,
|
|
73
|
+
wireDefaultLoaders,
|
|
74
|
+
} from '@forgeax/engine-assets-runtime';
|
|
75
|
+
import { audioLoader } from '@forgeax/engine-audio-webaudio';
|
|
76
|
+
import { defineComponent, World } from '@forgeax/engine-ecs';
|
|
77
|
+
import {
|
|
78
|
+
createBoxGeometry,
|
|
79
|
+
createPlaneGeometry,
|
|
80
|
+
meshFromInterleaved,
|
|
81
|
+
PROCEDURAL_FLOATS_PER_VERTEX,
|
|
82
|
+
} from '@forgeax/engine-geometry';
|
|
83
|
+
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
84
|
+
import { ok, ok as rhiOk } from '@forgeax/engine-rhi';
|
|
85
|
+
import { rhi } from '@forgeax/engine-rhi-null';
|
|
86
|
+
import { ShaderRegistry } from '@forgeax/engine-shader';
|
|
87
|
+
import type {
|
|
88
|
+
EquirectAsset,
|
|
89
|
+
Handle,
|
|
90
|
+
MaterialAsset,
|
|
91
|
+
MaterialPass,
|
|
92
|
+
SamplerAsset,
|
|
93
|
+
} from '@forgeax/engine-types';
|
|
94
|
+
import { AssetError, toShared, toUnique, unwrapHandle } from '@forgeax/engine-types';
|
|
95
|
+
import { afterEach, beforeEach, describe, expect, expectTypeOf, it, vi } from 'vitest';
|
|
96
|
+
import { deriveBuiltin } from '../../../pack/src/builtin';
|
|
97
|
+
import { DeviceScope } from '../../../render/src/device/device-scope';
|
|
98
|
+
import { GpuResidencyCache } from '../../../render/src/device/gpu-residency';
|
|
99
|
+
import { createEngineMetrics } from '../../../render/src/engine-metrics';
|
|
100
|
+
import { createRenderer as createRuntimeRenderer } from '../createRenderer';
|
|
101
|
+
import { createDevImportTransport, type ImportTransport } from '../dev-import-transport';
|
|
102
|
+
import { EngineEnvironmentError } from '../errors/environment';
|
|
103
|
+
import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
104
|
+
import { requireRenderer } from './renderer-test-utils';
|
|
105
|
+
|
|
106
|
+
vi.mock('@forgeax/engine-rhi-webgpu', async () => {
|
|
107
|
+
spies.webgpuImportCount += 1;
|
|
108
|
+
const actualRhi =
|
|
109
|
+
await vi.importActual<typeof import('@forgeax/engine-rhi')>('@forgeax/engine-rhi');
|
|
110
|
+
spies.rhiErrorCtor = actualRhi.RhiError;
|
|
111
|
+
const fakeAdapter = {
|
|
112
|
+
features: new Set<string>(),
|
|
113
|
+
limits: {} as Readonly<Record<string, number>>,
|
|
114
|
+
async requestDevice() {
|
|
115
|
+
return actualRhi.ok(makeFakeRhiDevice());
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
return {
|
|
119
|
+
rhi: {
|
|
120
|
+
requestAdapter: async () => {
|
|
121
|
+
if (spies.rhiWebgpuRequestAdapterShould === 'reject-rhi-not-available') {
|
|
122
|
+
return actualRhi.err(
|
|
123
|
+
new actualRhi.RhiError({
|
|
124
|
+
code: 'rhi-not-available',
|
|
125
|
+
expected: 'navigator.gpu available',
|
|
126
|
+
hint: 'unit-test: forced rhi-not-available',
|
|
127
|
+
}),
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
if (spies.rhiWebgpuRequestAdapterShould === 'reject-adapter-null') {
|
|
131
|
+
return actualRhi.err(
|
|
132
|
+
new actualRhi.RhiError({
|
|
133
|
+
code: 'adapter-unavailable',
|
|
134
|
+
expected: 'requestAdapter returns non-null',
|
|
135
|
+
hint: 'unit-test: forced adapter-unavailable',
|
|
136
|
+
}),
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
return actualRhi.ok(fakeAdapter);
|
|
140
|
+
},
|
|
141
|
+
acquireCanvasContext: (_canvas: unknown) => {
|
|
142
|
+
return actualRhi.ok({
|
|
143
|
+
configure: () => actualRhi.ok(undefined),
|
|
144
|
+
unconfigure: () => actualRhi.ok(undefined),
|
|
145
|
+
getCurrentTexture: () => actualRhi.ok({ __brand: 'TextureView' }),
|
|
146
|
+
});
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
createShaderModule: async () => actualRhi.ok({ __brand: 'ShaderModule' } as unknown as object),
|
|
150
|
+
translateErrorEventToRhiError: () =>
|
|
151
|
+
actualRhi.err(
|
|
152
|
+
new actualRhi.RhiError({
|
|
153
|
+
code: 'webgpu-runtime-error',
|
|
154
|
+
expected: 'GPU error event translates to RhiError',
|
|
155
|
+
hint: 'unit-test mock event translation',
|
|
156
|
+
}),
|
|
157
|
+
),
|
|
158
|
+
_internal_getRawDevice: () => undefined,
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
vi.mock('@forgeax/engine-rhi-wgpu', async () => {
|
|
162
|
+
spies.wgpuImportCount += 1;
|
|
163
|
+
const actualRhi =
|
|
164
|
+
await vi.importActual<typeof import('@forgeax/engine-rhi')>('@forgeax/engine-rhi');
|
|
165
|
+
const fakeAdapter = {
|
|
166
|
+
features: new Set<string>(),
|
|
167
|
+
limits: {} as Readonly<Record<string, number>>,
|
|
168
|
+
async requestDevice() {
|
|
169
|
+
return actualRhi.ok(makeFakeRhiDevice());
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
return {
|
|
173
|
+
rhi: {
|
|
174
|
+
requestAdapter: async () => {
|
|
175
|
+
if (spies.rhiWgpuRequestAdapterShould === 'reject-rhi-not-available') {
|
|
176
|
+
return actualRhi.err(
|
|
177
|
+
new actualRhi.RhiError({
|
|
178
|
+
code: 'rhi-not-available',
|
|
179
|
+
expected: 'wgpu webgl backend available',
|
|
180
|
+
hint: 'unit-test: forced rhi-not-available on rhi-wgpu',
|
|
181
|
+
}),
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
return actualRhi.ok(fakeAdapter);
|
|
185
|
+
},
|
|
186
|
+
acquireCanvasContext: (_canvas: unknown) => {
|
|
187
|
+
return actualRhi.ok({
|
|
188
|
+
configure: () => actualRhi.ok(undefined),
|
|
189
|
+
unconfigure: () => actualRhi.ok(undefined),
|
|
190
|
+
getCurrentTexture: () => actualRhi.ok({ __brand: 'TextureView' }),
|
|
191
|
+
});
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
ensureReady: async () => {
|
|
195
|
+
spies.ensureReadyCount += 1;
|
|
196
|
+
if (spies.rhiWgpuEnsureReadyShould === 'reject-load-failed') {
|
|
197
|
+
throw new Error('unit-test: forced rhi-wgpu wasm load failure');
|
|
198
|
+
}
|
|
199
|
+
return undefined;
|
|
200
|
+
},
|
|
201
|
+
createShaderModule: async () => actualRhi.ok({ __brand: 'ShaderModule' } as unknown as object),
|
|
202
|
+
translateErrorEventToRhiError: () =>
|
|
203
|
+
actualRhi.err(
|
|
204
|
+
new actualRhi.RhiError({
|
|
205
|
+
code: 'webgpu-runtime-error',
|
|
206
|
+
expected: 'GPU error event translates to RhiError',
|
|
207
|
+
hint: 'unit-test mock event translation',
|
|
208
|
+
}),
|
|
209
|
+
),
|
|
210
|
+
_internal_getRawDevice: () => undefined,
|
|
211
|
+
};
|
|
212
|
+
});
|
|
213
|
+
const spies = vi.hoisted(() => ({
|
|
214
|
+
webgpuImportCount: 0,
|
|
215
|
+
wgpuImportCount: 0,
|
|
216
|
+
ensureReadyCount: 0,
|
|
217
|
+
// Failure toggles set by individual tests before invoking createRenderer.
|
|
218
|
+
rhiWebgpuRequestAdapterShould: 'success' as
|
|
219
|
+
| 'success'
|
|
220
|
+
| 'reject-rhi-not-available'
|
|
221
|
+
| 'reject-adapter-null',
|
|
222
|
+
rhiWgpuEnsureReadyShould: 'success' as 'success' | 'reject-load-failed',
|
|
223
|
+
rhiWgpuRequestAdapterShould: 'success' as 'success' | 'reject-rhi-not-available',
|
|
224
|
+
rhiErrorCtor: undefined as undefined | (new (...args: never[]) => Error),
|
|
225
|
+
reset(): void {
|
|
226
|
+
this.webgpuImportCount = 0;
|
|
227
|
+
this.wgpuImportCount = 0;
|
|
228
|
+
this.ensureReadyCount = 0;
|
|
229
|
+
this.rhiWebgpuRequestAdapterShould = 'success';
|
|
230
|
+
this.rhiWgpuEnsureReadyShould = 'success';
|
|
231
|
+
this.rhiWgpuRequestAdapterShould = 'success';
|
|
232
|
+
},
|
|
233
|
+
}));
|
|
234
|
+
function makeFakeRhiDevice(): Record<string, unknown> {
|
|
235
|
+
let resolveLost!: (info: unknown) => void;
|
|
236
|
+
const lost = new Promise<unknown>((res) => {
|
|
237
|
+
resolveLost = res;
|
|
238
|
+
});
|
|
239
|
+
void resolveLost;
|
|
240
|
+
return {
|
|
241
|
+
__brand: 'RhiDevice',
|
|
242
|
+
lost,
|
|
243
|
+
features: new Set<string>(),
|
|
244
|
+
limits: {},
|
|
245
|
+
// feat-20260707 M5 / w33: createRenderer projects RhiCaps.textureCompression*
|
|
246
|
+
// into TranscodeCaps right after building the registry, so the fake device
|
|
247
|
+
// must expose a caps object (the real RhiDevice always carries one).
|
|
248
|
+
caps: {
|
|
249
|
+
textureCompressionBc: false,
|
|
250
|
+
textureCompressionEtc2: false,
|
|
251
|
+
textureCompressionAstc: false,
|
|
252
|
+
},
|
|
253
|
+
queue: {
|
|
254
|
+
submit: () => undefined,
|
|
255
|
+
writeBuffer: () => undefined,
|
|
256
|
+
},
|
|
257
|
+
createBuffer: () => ({ ok: true, value: { __brand: 'Buffer' } }),
|
|
258
|
+
createTexture: () => ({ ok: true, value: { __brand: 'Texture' } }),
|
|
259
|
+
createBindGroupLayout: () => ({ ok: true, value: { __brand: 'BindGroupLayout' } }),
|
|
260
|
+
createBindGroup: () => ({ ok: true, value: { __brand: 'BindGroup' } }),
|
|
261
|
+
createPipelineLayout: () => ({ ok: true, value: { __brand: 'PipelineLayout' } }),
|
|
262
|
+
createRenderPipeline: () => ({ ok: true, value: { __brand: 'RenderPipeline' } }),
|
|
263
|
+
createSampler: () => ({ ok: true, value: { __brand: 'Sampler' } }),
|
|
264
|
+
createShaderModule: () => ({
|
|
265
|
+
ok: true,
|
|
266
|
+
value: { __brand: 'ShaderModule' },
|
|
267
|
+
}),
|
|
268
|
+
createTextureView: () => ({ ok: true, value: { __brand: 'TextureView' } }),
|
|
269
|
+
createCommandEncoder: () => ({
|
|
270
|
+
ok: true,
|
|
271
|
+
value: {
|
|
272
|
+
beginRenderPass: () => ({
|
|
273
|
+
setPipeline: () => undefined,
|
|
274
|
+
setBindGroup: () => undefined,
|
|
275
|
+
setVertexBuffer: () => undefined,
|
|
276
|
+
setIndexBuffer: () => undefined,
|
|
277
|
+
draw: () => undefined,
|
|
278
|
+
drawIndexed: () => undefined,
|
|
279
|
+
end: () => undefined,
|
|
280
|
+
}),
|
|
281
|
+
finish: () => ({ __brand: 'CommandBuffer' }),
|
|
282
|
+
},
|
|
283
|
+
}),
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
function makeMockCanvas(opts: { webgpu?: 'context' | 'null'; webgl2?: 'context' | 'null' }) {
|
|
287
|
+
return {
|
|
288
|
+
width: 800,
|
|
289
|
+
height: 600,
|
|
290
|
+
getContext(kind: string): unknown {
|
|
291
|
+
if (kind === 'webgpu') {
|
|
292
|
+
if (opts.webgpu === 'context') {
|
|
293
|
+
return {
|
|
294
|
+
__mockTag: 'webgpu-canvas-context',
|
|
295
|
+
configure: () => undefined,
|
|
296
|
+
unconfigure: () => undefined,
|
|
297
|
+
getCurrentTexture: () => ({ createView: () => ({}) }),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
if (kind === 'webgl2') {
|
|
303
|
+
if (opts.webgl2 === 'context') {
|
|
304
|
+
return {
|
|
305
|
+
__mockTag: 'webgl2',
|
|
306
|
+
getExtension: () => null,
|
|
307
|
+
getParameter: () => 1,
|
|
308
|
+
isContextLost: () => false,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
return null;
|
|
314
|
+
},
|
|
315
|
+
addEventListener() {},
|
|
316
|
+
removeEventListener() {},
|
|
317
|
+
} as unknown as HTMLCanvasElement;
|
|
318
|
+
}
|
|
319
|
+
function _makeStubGPU(): unknown {
|
|
320
|
+
return {
|
|
321
|
+
requestAdapter: async () => ({
|
|
322
|
+
features: new Set<string>(),
|
|
323
|
+
limits: {},
|
|
324
|
+
requestDevice: async () => makeFakeRhiDevice(),
|
|
325
|
+
}),
|
|
326
|
+
getPreferredCanvasFormat: () => 'bgra8unorm',
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
void [
|
|
331
|
+
AssetError,
|
|
332
|
+
AssetGuid,
|
|
333
|
+
AssetRegistry,
|
|
334
|
+
DeviceScope,
|
|
335
|
+
EngineEnvironmentError,
|
|
336
|
+
GpuResidencyCache,
|
|
337
|
+
HANDLE_CUBE,
|
|
338
|
+
HANDLE_CYLINDER,
|
|
339
|
+
HANDLE_QUAD,
|
|
340
|
+
HANDLE_SPHERE,
|
|
341
|
+
HANDLE_TRIANGLE,
|
|
342
|
+
INLINE_PACK_LOADERS,
|
|
343
|
+
LoaderRegistry,
|
|
344
|
+
PACK_ARTIFACT_LOADERS,
|
|
345
|
+
PROCEDURAL_FLOATS_PER_VERTEX,
|
|
346
|
+
ShaderRegistry,
|
|
347
|
+
World,
|
|
348
|
+
_makeStubGPU,
|
|
349
|
+
afterEach,
|
|
350
|
+
animationClipLoader,
|
|
351
|
+
assetRegistryModule,
|
|
352
|
+
audioLoader,
|
|
353
|
+
beforeEach,
|
|
354
|
+
buildSceneChildContext,
|
|
355
|
+
createBoxGeometry,
|
|
356
|
+
createDevImportTransport,
|
|
357
|
+
createEngineMetrics,
|
|
358
|
+
createPlaneGeometry,
|
|
359
|
+
createRuntimeRenderer,
|
|
360
|
+
defineComponent,
|
|
361
|
+
deriveBuiltin,
|
|
362
|
+
describe,
|
|
363
|
+
expect,
|
|
364
|
+
expectTypeOf,
|
|
365
|
+
fileURLToPath,
|
|
366
|
+
fontLoader,
|
|
367
|
+
getOrCreateMipmapPipeline,
|
|
368
|
+
it,
|
|
369
|
+
makeFakeRhiDevice,
|
|
370
|
+
makeMockCanvas,
|
|
371
|
+
makeMockShaderRegistry,
|
|
372
|
+
materialLoader,
|
|
373
|
+
meshFromInterleaved,
|
|
374
|
+
meshLoader,
|
|
375
|
+
mipmapCacheSize,
|
|
376
|
+
numMipLevels,
|
|
377
|
+
ok,
|
|
378
|
+
readFileSync,
|
|
379
|
+
requireRenderer,
|
|
380
|
+
resolveAssetHandle,
|
|
381
|
+
rhi,
|
|
382
|
+
rhiOk,
|
|
383
|
+
sceneLoader,
|
|
384
|
+
skeletonLoader,
|
|
385
|
+
skinLoader,
|
|
386
|
+
spies,
|
|
387
|
+
textureLoader,
|
|
388
|
+
toShared,
|
|
389
|
+
toUnique,
|
|
390
|
+
unwrapHandle,
|
|
391
|
+
vi,
|
|
392
|
+
walkMaterialPassesOverSharedRefs,
|
|
393
|
+
wireDefaultLoaders,
|
|
394
|
+
];
|
|
395
|
+
type __MergedKeep =
|
|
396
|
+
| EquirectAsset
|
|
397
|
+
| Handle
|
|
398
|
+
| ImportTransport
|
|
399
|
+
| MaterialAsset
|
|
400
|
+
| MaterialPass
|
|
401
|
+
| SamplerAsset;
|
|
402
|
+
|
|
403
|
+
{
|
|
404
|
+
// --- from lazy-catalog.test.ts ---
|
|
405
|
+
function makeMockShaderRegistry() {
|
|
406
|
+
return {
|
|
407
|
+
getMaterialShaderManifest: vi.fn().mockReturnValue(undefined),
|
|
408
|
+
findMaterialArtifact: vi.fn().mockReturnValue({ ok: false, error: new Error('mock') }),
|
|
409
|
+
getPipeline: vi.fn().mockReturnValue(undefined),
|
|
410
|
+
installMaterialArtifact: vi.fn(),
|
|
411
|
+
inspect: vi.fn().mockReturnValue({ materialShaders: [] }),
|
|
412
|
+
} as unknown as import('@forgeax/engine-shader').ShaderRegistry;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const UNKNOWN_GUID = 'ffffffff-ffff-7fff-bfff-ffffffffffff';
|
|
416
|
+
|
|
417
|
+
function mockGlobalFetch(impl: (url: string) => Promise<unknown>) {
|
|
418
|
+
// biome-ignore lint/suspicious/noExplicitAny: test mock needs unsafe globalThis cast
|
|
419
|
+
globalThis.fetch = vi.fn().mockImplementation(impl) as any;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function emptyCatalogResponse() {
|
|
423
|
+
return Promise.resolve({
|
|
424
|
+
ok: true,
|
|
425
|
+
json: () => Promise.resolve([]),
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
describe('M4 lazy catalog + ImportTransport (AC-19 / AC-22)', () => {
|
|
430
|
+
beforeEach(() => {
|
|
431
|
+
vi.restoreAllMocks();
|
|
432
|
+
// biome-ignore lint/suspicious/noExplicitAny: test mock
|
|
433
|
+
delete (globalThis as any).fetch;
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it('(AC-19) DDC miss with transport present triggers fetchPack', async () => {
|
|
437
|
+
const transport: ImportTransport = {
|
|
438
|
+
fetchPack: vi.fn().mockResolvedValue({ ok: true }),
|
|
439
|
+
};
|
|
440
|
+
const reg = new AssetRegistry(makeMockShaderRegistry(), transport);
|
|
441
|
+
|
|
442
|
+
reg.configurePackIndex('/pack-index.json');
|
|
443
|
+
|
|
444
|
+
mockGlobalFetch(() => emptyCatalogResponse());
|
|
445
|
+
|
|
446
|
+
const parsed = AssetGuid.parse(UNKNOWN_GUID);
|
|
447
|
+
if (!parsed.ok) throw new Error('expected ok');
|
|
448
|
+
const result = await reg.loadByGuid<MeshAsset>(parsed.value);
|
|
449
|
+
|
|
450
|
+
// DDC miss (empty catalog -> resolveCatalogEntry returns undefined)
|
|
451
|
+
// -> transportOrFail -> transport.fetchPack called.
|
|
452
|
+
expect(result.ok).toBe(false);
|
|
453
|
+
if (!result.ok) {
|
|
454
|
+
expect(result.error.code).toBe('asset-not-imported');
|
|
455
|
+
}
|
|
456
|
+
// AC-19: transport WAS called (DDC miss triggers it).
|
|
457
|
+
expect(transport.fetchPack).toHaveBeenCalledTimes(1);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
it('(AC-22) no transport wired + DDC miss -> asset-not-imported fail-fast', async () => {
|
|
461
|
+
const reg = new AssetRegistry(makeMockShaderRegistry());
|
|
462
|
+
|
|
463
|
+
reg.configurePackIndex('/pack-index.json');
|
|
464
|
+
|
|
465
|
+
mockGlobalFetch(() => emptyCatalogResponse());
|
|
466
|
+
|
|
467
|
+
const parsed = AssetGuid.parse(UNKNOWN_GUID);
|
|
468
|
+
if (!parsed.ok) throw new Error('expected ok');
|
|
469
|
+
const result = await reg.loadByGuid<MeshAsset>(parsed.value);
|
|
470
|
+
|
|
471
|
+
expect(result.ok).toBe(false);
|
|
472
|
+
if (!result.ok) {
|
|
473
|
+
expect(result.error).toBeInstanceOf(AssetError);
|
|
474
|
+
expect(result.error.code).toBe('asset-not-imported');
|
|
475
|
+
expect(result.error.hint).toContain('pre-import');
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
it('transport fetchPack returns error -> asset-not-imported', async () => {
|
|
480
|
+
const transport: ImportTransport = {
|
|
481
|
+
fetchPack: vi.fn().mockResolvedValue({ ok: false }),
|
|
482
|
+
};
|
|
483
|
+
const reg = new AssetRegistry(makeMockShaderRegistry(), transport);
|
|
484
|
+
|
|
485
|
+
reg.configurePackIndex('/pack-index.json');
|
|
486
|
+
|
|
487
|
+
mockGlobalFetch(() => emptyCatalogResponse());
|
|
488
|
+
|
|
489
|
+
const parsed = AssetGuid.parse(UNKNOWN_GUID);
|
|
490
|
+
if (!parsed.ok) throw new Error('expected ok');
|
|
491
|
+
const result = await reg.loadByGuid<MeshAsset>(parsed.value);
|
|
492
|
+
|
|
493
|
+
expect(result.ok).toBe(false);
|
|
494
|
+
if (!result.ok) {
|
|
495
|
+
expect(result.error.code).toBe('asset-not-imported');
|
|
496
|
+
}
|
|
497
|
+
expect(transport.fetchPack).toHaveBeenCalledTimes(1);
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it('(AC-19) DDC hit path never touches transport (fetch succeeds, transport call count = 0)', async () => {
|
|
501
|
+
const transport: ImportTransport = {
|
|
502
|
+
fetchPack: vi.fn().mockResolvedValue({ ok: true }),
|
|
503
|
+
};
|
|
504
|
+
const reg = new AssetRegistry(makeMockShaderRegistry(), transport);
|
|
505
|
+
|
|
506
|
+
reg.configurePackIndex('/pack-index.json');
|
|
507
|
+
|
|
508
|
+
const guid = 'aaaaaaaa-aaaa-4aaa-baaa-aaaaaaaaaaaa';
|
|
509
|
+
|
|
510
|
+
mockGlobalFetch((url: string) => {
|
|
511
|
+
if (url === '/pack-index.json') {
|
|
512
|
+
return Promise.resolve({
|
|
513
|
+
ok: true,
|
|
514
|
+
json: () =>
|
|
515
|
+
Promise.resolve([
|
|
516
|
+
{
|
|
517
|
+
guid,
|
|
518
|
+
packageUrl: '/pack/asset.pack.json',
|
|
519
|
+
kind: 'mesh',
|
|
520
|
+
},
|
|
521
|
+
]),
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
if (url === '/pack/asset.pack.json') {
|
|
525
|
+
// Return a mesh with 12-float-per-vertex stride (validateMeshPayload
|
|
526
|
+
// requires vertices.length % 12 === 0). 2 vertices x 12 floats.
|
|
527
|
+
// position(3) + normal(3) + uv(2) + tangent(4) = 12.
|
|
528
|
+
return Promise.resolve({
|
|
529
|
+
ok: true,
|
|
530
|
+
json: () =>
|
|
531
|
+
Promise.resolve({
|
|
532
|
+
schemaVersion: '2.0.0',
|
|
533
|
+
kind: 'internal-text-package',
|
|
534
|
+
assets: [
|
|
535
|
+
{
|
|
536
|
+
guid,
|
|
537
|
+
kind: 'mesh',
|
|
538
|
+
payload: {
|
|
539
|
+
vertices: [
|
|
540
|
+
0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
|
|
541
|
+
],
|
|
542
|
+
indices: [0, 1],
|
|
543
|
+
attributes: {},
|
|
544
|
+
},
|
|
545
|
+
refs: [],
|
|
546
|
+
submeshes: [
|
|
547
|
+
{
|
|
548
|
+
indexOffset: 0,
|
|
549
|
+
indexCount: 0,
|
|
550
|
+
vertexCount: 0,
|
|
551
|
+
topology: 'triangle-list',
|
|
552
|
+
},
|
|
553
|
+
],
|
|
554
|
+
},
|
|
555
|
+
],
|
|
556
|
+
}),
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
return Promise.resolve({ ok: false, status: 404 });
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
const parsed = AssetGuid.parse(guid);
|
|
563
|
+
if (!parsed.ok) throw new Error('expected ok');
|
|
564
|
+
const result = await reg.loadByGuid<MeshAsset>(parsed.value);
|
|
565
|
+
|
|
566
|
+
// DDC hit: catalog has the entry, pack fetch succeeds.
|
|
567
|
+
// validateMeshPayload requires vertices.length === 24 (2 * 12).
|
|
568
|
+
// Indices [0,1] -> maxIndex=1, vertexCount=24/12=2. maxIndex+1===2.
|
|
569
|
+
expect(result.ok).toBe(true);
|
|
570
|
+
|
|
571
|
+
// AC-19 lazy iron law: transport must NOT be called when DDC hit.
|
|
572
|
+
expect(transport.fetchPack).toHaveBeenCalledTimes(0);
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
it('resolveGuid (dev/fallback, no packIndexUrl) still returns asset-not-found', async () => {
|
|
576
|
+
const reg = new AssetRegistry(makeMockShaderRegistry());
|
|
577
|
+
|
|
578
|
+
const parsed = AssetGuid.parse(UNKNOWN_GUID);
|
|
579
|
+
if (!parsed.ok) throw new Error('expected ok');
|
|
580
|
+
const result = await reg.loadByGuid<MeshAsset>(parsed.value);
|
|
581
|
+
|
|
582
|
+
expect(result.ok).toBe(false);
|
|
583
|
+
if (!result.ok) {
|
|
584
|
+
// The dev path (no packIndexUrl) goes through resolveGuid, not the
|
|
585
|
+
// transport-aware loadByGuidProd path. It should still return
|
|
586
|
+
// asset-not-found for unregistered GUIDs.
|
|
587
|
+
expect(result.error.code).toBe('asset-not-found');
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
// ─── w16: createRenderer transport injection + invariants ────────────────────
|
|
593
|
+
//
|
|
594
|
+
// createRenderer needs a real GPU device, so the injection wiring is asserted
|
|
595
|
+
// against the source SSOT (the constructor call site + signature) rather than
|
|
596
|
+
// by booting a renderer. The behavioural fail-fast (AC-08) + transport-call
|
|
597
|
+
// (AC-19) semantics are exercised by the AssetRegistry-level tests above; here
|
|
598
|
+
// we lock the load-bearing structural invariants the injection channel must
|
|
599
|
+
// preserve.
|
|
600
|
+
|
|
601
|
+
const createRendererSrc = readFileSync(
|
|
602
|
+
fileURLToPath(new URL('../../../render/src/assembly/webgpu-renderer.ts', import.meta.url)),
|
|
603
|
+
'utf-8',
|
|
604
|
+
);
|
|
605
|
+
const rendererTypeSrc = readFileSync(
|
|
606
|
+
fileURLToPath(new URL('../../../render/src/render-contract.ts', import.meta.url)),
|
|
607
|
+
'utf-8',
|
|
608
|
+
);
|
|
609
|
+
// feat-20260705-runtime-tier2-decomposition M1 / w7 (D-4): the load-by-guid +
|
|
610
|
+
// DDC/pack-fetch method cluster moved out of asset-registry.ts into
|
|
611
|
+
// registry/load-by-guid.ts. Source guards for that pipeline read the new file.
|
|
612
|
+
// (The former assetRegistrySrc read was dropped -- its sole remaining consumer,
|
|
613
|
+
// the transport-eligible guard, now reads loadByGuidSrc.)
|
|
614
|
+
const loadByGuidSrc = readFileSync(
|
|
615
|
+
fileURLToPath(new URL('../../../assets-runtime/src/registry/load-by-guid.ts', import.meta.url)),
|
|
616
|
+
'utf-8',
|
|
617
|
+
);
|
|
618
|
+
|
|
619
|
+
describe('w16 createRenderer transport injection (AC-03 / AC-05 / AC-08)', () => {
|
|
620
|
+
it('(AC-05) createRenderer threads transport into the AssetRegistry ctor', () => {
|
|
621
|
+
// The AssetRegistry constructor is wired directly at the assembly owner;
|
|
622
|
+
// the injected transport remains the second positional argument (D-3:
|
|
623
|
+
// ctor-readonly single injection point).
|
|
624
|
+
// The loaders are now self-contained: AssetRegistry internally builds its
|
|
625
|
+
// own LoaderRegistry via createDefaultLoaderRegistry() (M3 w9).
|
|
626
|
+
expect(createRendererSrc).toMatch(
|
|
627
|
+
/new AssetRegistry\(\s*shaderCatalog,\s*internals\.importTransport\s*,\s*\[audioLoader\]\s*,\s*postSpawnResolveJoints/,
|
|
628
|
+
);
|
|
629
|
+
// The injection arrives through a dedicated non-RendererOptions internal
|
|
630
|
+
// parameter named `importTransport` on createRenderer.
|
|
631
|
+
expect(createRendererSrc).toMatch(/importTransport\?\s*:\s*ImportTransport/);
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
it('(AC-05) RendererOptions gains no asset-layer transport field', () => {
|
|
635
|
+
// The injection channel must NOT pollute RendererOptions with an
|
|
636
|
+
// asset-layer concept (R-4): the transport rides a separate internal param.
|
|
637
|
+
expect(rendererTypeSrc).not.toMatch(/importTransport/);
|
|
638
|
+
expect(rendererTypeSrc).not.toMatch(/ImportTransport/);
|
|
639
|
+
});
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
// ─── w5: import-on-demand sentinel routing (feat-20260604 M2 / D-1) ────────────
|
|
643
|
+
//
|
|
644
|
+
// Four routes (AC-02 / AC-03 / AC-08, plan-strategy section 5.3 + Risk-1):
|
|
645
|
+
// (a) studio form: an unimported (non-.bin) texture row surfaces the
|
|
646
|
+
// texture-source-not-imported sentinel -> loadByGuidProd routes it through
|
|
647
|
+
// transport.fetchPack -> after the import the rebuilt catalog has a .bin
|
|
648
|
+
// row -> the re-entered DDC load succeeds with a TextureAsset handle.
|
|
649
|
+
// (b) shipped form (no transport): the SAME unimported row fails fast with
|
|
650
|
+
// asset-not-imported (never silently lazy-imports, AC-08).
|
|
651
|
+
// (c) Risk-1 falsification: a genuinely corrupt .bin path produces an
|
|
652
|
+
// image-decode-failed ImageError, which is NOT transport-eligible (the
|
|
653
|
+
// :2334 guard is `instanceof AssetError`, and ImageError is a distinct
|
|
654
|
+
// class) -> transport.fetchPack is never called.
|
|
655
|
+
// (d) after the import the same GUID resolves to a SINGLE .bin row (AC-02).
|
|
656
|
+
|
|
657
|
+
const TEXTURE_GUID = 'bbbbbbbb-bbbb-4bbb-bbbb-bbbbbbbbbbbb';
|
|
658
|
+
const RAW_SOURCE_URL = '/textures/wall-source.pack.json';
|
|
659
|
+
const IMPORTED_BIN_URL = '/textures/wall.pack.json';
|
|
660
|
+
|
|
661
|
+
const TEXTURE_METADATA = {
|
|
662
|
+
kind: 'texture',
|
|
663
|
+
shape: { viewDimension: '2d', extent: { width: 2, height: 2 } },
|
|
664
|
+
format: 'rgba8unorm',
|
|
665
|
+
colorSpace: 'srgb',
|
|
666
|
+
mips: { kind: 'none' },
|
|
667
|
+
} as const;
|
|
668
|
+
|
|
669
|
+
function unimportedTextureRow() {
|
|
670
|
+
return {
|
|
671
|
+
guid: TEXTURE_GUID,
|
|
672
|
+
packageUrl: RAW_SOURCE_URL,
|
|
673
|
+
kind: 'texture',
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
function importedTextureRow() {
|
|
677
|
+
return {
|
|
678
|
+
guid: TEXTURE_GUID,
|
|
679
|
+
packageUrl: IMPORTED_BIN_URL,
|
|
680
|
+
kind: 'texture',
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
function jsonResponse(body: unknown) {
|
|
685
|
+
return Promise.resolve({ ok: true, json: () => Promise.resolve(body) });
|
|
686
|
+
}
|
|
687
|
+
function binaryResponse() {
|
|
688
|
+
// 2x2 rgba8 = 16 bytes; loadTextureAsset does not validate byte length, any
|
|
689
|
+
// buffer registers as the TextureAsset POD.
|
|
690
|
+
return Promise.resolve({
|
|
691
|
+
ok: true,
|
|
692
|
+
arrayBuffer: () => Promise.resolve(new Uint8Array(16).buffer),
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
describe('w5 import-on-demand sentinel routing (AC-02 / AC-03 / AC-08)', () => {
|
|
697
|
+
beforeEach(() => {
|
|
698
|
+
vi.restoreAllMocks();
|
|
699
|
+
// biome-ignore lint/suspicious/noExplicitAny: test mock
|
|
700
|
+
delete (globalThis as any).fetch;
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
it('(a/d AC-03/AC-02) studio form: unimported texture row -> sentinel -> transport imports -> single .bin row resolves', async () => {
|
|
704
|
+
let imported = false;
|
|
705
|
+
const transport: ImportTransport = {
|
|
706
|
+
fetchPack: vi.fn().mockImplementation(() => {
|
|
707
|
+
// The transport (dev POST /__import) imports the .bin and the rebuilt
|
|
708
|
+
// pack-index now carries a single imported .bin row for this GUID.
|
|
709
|
+
imported = true;
|
|
710
|
+
return Promise.resolve({ ok: true });
|
|
711
|
+
}),
|
|
712
|
+
};
|
|
713
|
+
const reg = new AssetRegistry(makeMockShaderRegistry(), transport);
|
|
714
|
+
reg.configurePackIndex('/pack-index.json');
|
|
715
|
+
|
|
716
|
+
mockGlobalFetch((url: string) => {
|
|
717
|
+
if (url === '/pack-index.json') {
|
|
718
|
+
// Before import: the cooked package is unavailable. After import:
|
|
719
|
+
// the transport publishes one Pack v2 package row.
|
|
720
|
+
return jsonResponse([imported ? importedTextureRow() : unimportedTextureRow()]);
|
|
721
|
+
}
|
|
722
|
+
if (url === IMPORTED_BIN_URL) {
|
|
723
|
+
return jsonResponse({
|
|
724
|
+
schemaVersion: '2.0.0',
|
|
725
|
+
kind: 'internal-text-package',
|
|
726
|
+
assets: [
|
|
727
|
+
{
|
|
728
|
+
guid: TEXTURE_GUID,
|
|
729
|
+
kind: 'texture',
|
|
730
|
+
payload: TEXTURE_METADATA,
|
|
731
|
+
refs: [],
|
|
732
|
+
artifacts: {
|
|
733
|
+
body: { path: 'wall.bin', mediaType: 'application/x-forgeax-rgba8' },
|
|
734
|
+
},
|
|
735
|
+
},
|
|
736
|
+
],
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
if (url === '/textures/wall.bin') return binaryResponse();
|
|
740
|
+
// The raw source must NOT be fetched as a .bin (it never reaches
|
|
741
|
+
// fetchBinary because the sentinel short-circuits before fetch).
|
|
742
|
+
return Promise.resolve({ ok: false, status: 404 });
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
const parsed = AssetGuid.parse(TEXTURE_GUID);
|
|
746
|
+
if (!parsed.ok) throw new Error('expected ok');
|
|
747
|
+
const result = await reg.loadByGuid<TextureAsset>(parsed.value);
|
|
748
|
+
|
|
749
|
+
expect(transport.fetchPack).toHaveBeenCalledTimes(1);
|
|
750
|
+
expect(result.ok).toBe(true);
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
it('(b AC-08) shipped form (no transport): same unimported row -> asset-not-imported fail-fast', async () => {
|
|
754
|
+
const reg = new AssetRegistry(makeMockShaderRegistry());
|
|
755
|
+
reg.configurePackIndex('/pack-index.json');
|
|
756
|
+
|
|
757
|
+
mockGlobalFetch((url: string) => {
|
|
758
|
+
if (url === '/pack-index.json') return jsonResponse([unimportedTextureRow()]);
|
|
759
|
+
return Promise.resolve({ ok: false, status: 404 });
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
const parsed = AssetGuid.parse(TEXTURE_GUID);
|
|
763
|
+
if (!parsed.ok) throw new Error('expected ok');
|
|
764
|
+
const result = await reg.loadByGuid<TextureAsset>(parsed.value);
|
|
765
|
+
|
|
766
|
+
expect(result.ok).toBe(false);
|
|
767
|
+
if (!result.ok) {
|
|
768
|
+
expect(result.error).toBeInstanceOf(AssetError);
|
|
769
|
+
expect(result.error.code).toBe('asset-not-imported');
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
it('(c Risk-1) image-decode-failed (ImageError) is NOT transport-eligible (corrupt .bin never routes transport)', async () => {
|
|
774
|
+
// A custom texture loader that mimics a genuinely corrupt imported artifact:
|
|
775
|
+
// it returns an image-decode-failed ImageError (a distinct class from
|
|
776
|
+
// AssetError). The :2334 transport-eligibility guard is
|
|
777
|
+
// `instanceof AssetError`, so this error must fail straight through and
|
|
778
|
+
// NEVER reach transport.fetchPack.
|
|
779
|
+
const decodeError: ImageError = {
|
|
780
|
+
name: 'ImageError',
|
|
781
|
+
message: 'corrupt .bin',
|
|
782
|
+
code: 'image-decode-failed',
|
|
783
|
+
detail: { code: 'image-decode-failed', reason: 'corrupt bytes', path: IMPORTED_BIN_URL },
|
|
784
|
+
} as unknown as ImageError;
|
|
785
|
+
|
|
786
|
+
const transport: ImportTransport = { fetchPack: vi.fn().mockResolvedValue({ ok: true }) };
|
|
787
|
+
const reg = new AssetRegistry(makeMockShaderRegistry(), transport);
|
|
788
|
+
reg.loaders.registerPackLoader({
|
|
789
|
+
kind: 'texture',
|
|
790
|
+
load: () => ({ ok: false as const, error: decodeError }),
|
|
791
|
+
});
|
|
792
|
+
reg.configurePackIndex('/pack-index.json');
|
|
793
|
+
|
|
794
|
+
mockGlobalFetch((url: string) => {
|
|
795
|
+
if (url === '/pack-index.json') return jsonResponse([importedTextureRow()]);
|
|
796
|
+
if (url === IMPORTED_BIN_URL) {
|
|
797
|
+
return jsonResponse({
|
|
798
|
+
schemaVersion: '2.0.0',
|
|
799
|
+
kind: 'internal-text-package',
|
|
800
|
+
assets: [
|
|
801
|
+
{
|
|
802
|
+
guid: TEXTURE_GUID,
|
|
803
|
+
kind: 'texture',
|
|
804
|
+
payload: TEXTURE_METADATA,
|
|
805
|
+
refs: [],
|
|
806
|
+
artifacts: {},
|
|
807
|
+
},
|
|
808
|
+
],
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
return Promise.resolve({ ok: false, status: 404 });
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
const parsed = AssetGuid.parse(TEXTURE_GUID);
|
|
815
|
+
if (!parsed.ok) throw new Error('expected ok');
|
|
816
|
+
const result = await reg.loadByGuid<TextureAsset>(parsed.value);
|
|
817
|
+
|
|
818
|
+
expect(result.ok).toBe(false);
|
|
819
|
+
if (!result.ok) {
|
|
820
|
+
expect(result.error.code).toBe('image-decode-failed');
|
|
821
|
+
expect(result.error).not.toBeInstanceOf(AssetError);
|
|
822
|
+
}
|
|
823
|
+
// Risk-1 falsification: a real decode failure must not be lazy-imported.
|
|
824
|
+
expect(transport.fetchPack).toHaveBeenCalledTimes(0);
|
|
825
|
+
});
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
describe('w16 createRenderer transport injection (AC-03 / AC-05 / AC-08) cont.', () => {
|
|
829
|
+
it('(AC-03 / w6 D-1) transport-eligible set = {asset-not-found, asset-fetch-failed, texture-source-not-imported}, excludes image-decode-failed', () => {
|
|
830
|
+
// R-2 + feat-20260604 Risk-1: a DDC miss routes through transport only for
|
|
831
|
+
// asset-not-found / asset-fetch-failed (missing pack file) or
|
|
832
|
+
// texture-source-not-imported (unimported texture source, D-1 import-on-demand).
|
|
833
|
+
// The eligibility block is layout-robust (the formatter may wrap the
|
|
834
|
+
// clauses), so we slice the guard region and assert by substring.
|
|
835
|
+
const transportGuard = loadByGuidSrc.slice(
|
|
836
|
+
loadByGuidSrc.indexOf('ddcError instanceof AssetError'),
|
|
837
|
+
loadByGuidSrc.indexOf('transportOrFail<T>(registry, guid, guidKey, ddcError.code)'),
|
|
838
|
+
);
|
|
839
|
+
expect(transportGuard).toContain("ddcError.code === 'asset-not-found'");
|
|
840
|
+
expect(transportGuard).toContain("ddcError.code === 'asset-fetch-failed'");
|
|
841
|
+
expect(transportGuard).toContain("ddcError.code === 'texture-source-not-imported'");
|
|
842
|
+
// image-decode-failed (a genuinely corrupt imported .bin) is an ImageError --
|
|
843
|
+
// it must NEVER be listed in the AssetError-only eligibility guard (a real
|
|
844
|
+
// decode failure must not be silently lazy-imported).
|
|
845
|
+
expect(transportGuard).not.toContain('image-decode-failed');
|
|
846
|
+
});
|
|
847
|
+
});
|
|
848
|
+
}
|