@forgeax/engine-runtime 0.1.3 → 0.1.6
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 +49 -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 +3 -3
- 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 +19 -81
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +105 -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 +575 -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 +0 -18
- 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 +1203 -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 +16 -500
- 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 +14 -21
- 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 +81 -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 +60 -558
- 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 +59 -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 +43 -261
- 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 +12 -11
- 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 +64 -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 +28 -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 +47 -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,1030 @@
|
|
|
1
|
+
// M6 w31 (round 5 D-15): column-mint + two-tier resolve integration TDD.
|
|
2
|
+
//
|
|
3
|
+
// D-15 corrected model (human-inputs implement-architecture-correction
|
|
4
|
+
// 2026-06-16T12:35:07Z): AssetRegistry has ZERO handle concept. There is no
|
|
5
|
+
// shared slot space shared with the World. A `handle` is purely an ECS column
|
|
6
|
+
// value -- a numeric indirect reference resolved two-tier purely by slot range:
|
|
7
|
+
// slot < BUILTIN_BASE -> BuiltinAssetRegistry.resolve(handle) (process-static)
|
|
8
|
+
// slot >= BUILTIN_BASE -> world.sharedRefs.resolve(handle) (user-tier RC)
|
|
9
|
+
// resolution is entirely on the ECS/render/extract side, never via AssetRegistry.
|
|
10
|
+
//
|
|
11
|
+
// Column-mint API (Implementer decision): user-tier payloads mint via
|
|
12
|
+
// `world.allocSharedRef(tag, payload)` (already on World). AssetRegistry no
|
|
13
|
+
// longer produces a column-usable handle; its residual job is GUID->payload
|
|
14
|
+
// cataloguing + `loadByGuid`.
|
|
15
|
+
//
|
|
16
|
+
// These cases are RED before w49 (AssetRegistry still carries `register` /
|
|
17
|
+
// by-handle `get`); w49 removes those entries and turns this file green.
|
|
18
|
+
import { readFileSync } from 'node:fs';
|
|
19
|
+
import { fileURLToPath } from 'node:url';
|
|
20
|
+
import {
|
|
21
|
+
AssetRegistry,
|
|
22
|
+
BUILTIN_CUBE,
|
|
23
|
+
BuiltinAssetRegistry,
|
|
24
|
+
HANDLE_CUBE,
|
|
25
|
+
} from '@forgeax/engine-assets-runtime';
|
|
26
|
+
import { World } from '@forgeax/engine-ecs';
|
|
27
|
+
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
28
|
+
import type { MeshAsset } from '@forgeax/engine-types';
|
|
29
|
+
import { BUILTIN_BASE } from '@forgeax/engine-types';
|
|
30
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
31
|
+
import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
|
|
32
|
+
|
|
33
|
+
const ASSET_REGISTRY_SRC = readFileSync(
|
|
34
|
+
fileURLToPath(new URL('../../../assets-runtime/src/asset-registry.ts', import.meta.url)),
|
|
35
|
+
'utf8',
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
function mesh(): MeshAsset {
|
|
39
|
+
return {
|
|
40
|
+
kind: 'mesh',
|
|
41
|
+
vertices: new Float32Array(12 * 3),
|
|
42
|
+
indices: new Uint16Array([0, 1, 2]),
|
|
43
|
+
} as unknown as MeshAsset;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
describe('D-15 column-mint via world.allocSharedRef (AC-24)', () => {
|
|
47
|
+
it('mints a user-tier slot >= BUILTIN_BASE and resolves to the same payload', () => {
|
|
48
|
+
const world = new World();
|
|
49
|
+
const payload = mesh();
|
|
50
|
+
const handle = world.allocSharedRef<'MeshAsset', MeshAsset>('MeshAsset', payload);
|
|
51
|
+
expect(handle).toBeGreaterThanOrEqual(BUILTIN_BASE);
|
|
52
|
+
const res = world.sharedRefs.resolve<'MeshAsset', MeshAsset>(handle);
|
|
53
|
+
expect(res.ok).toBe(true);
|
|
54
|
+
if (res.ok) expect(res.value).toBe(payload);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('two distinct mints get distinct user-tier slots', () => {
|
|
58
|
+
const world = new World();
|
|
59
|
+
const a = world.allocSharedRef<'MeshAsset', MeshAsset>('MeshAsset', mesh());
|
|
60
|
+
const b = world.allocSharedRef<'MeshAsset', MeshAsset>('MeshAsset', mesh());
|
|
61
|
+
expect(a).not.toBe(b);
|
|
62
|
+
expect(a).toBeGreaterThanOrEqual(BUILTIN_BASE);
|
|
63
|
+
expect(b).toBeGreaterThanOrEqual(BUILTIN_BASE);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe('D-15 builtin handle resolves via BuiltinAssetRegistry (AC-24)', () => {
|
|
68
|
+
it('HANDLE_CUBE (slot < BUILTIN_BASE) resolves through BuiltinAssetRegistry, not the World', () => {
|
|
69
|
+
expect(BuiltinAssetRegistry.resolve(HANDLE_CUBE)).toBe(BUILTIN_CUBE);
|
|
70
|
+
// builtin slot does not live in any World's SharedRefStore
|
|
71
|
+
const world = new World();
|
|
72
|
+
const res = world.sharedRefs.resolve(HANDLE_CUBE);
|
|
73
|
+
expect(res.ok).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('D-15 AssetRegistry has no handle concept (AC-25, static source assertion)', () => {
|
|
78
|
+
it('exposes no anonymous register(asset) -> Handle column-mint entry', () => {
|
|
79
|
+
// The old `register<T extends Asset>(asset): Result<Handle...>` mint moved
|
|
80
|
+
// to the ECS side (world.allocSharedRef). AssetRegistry must not declare it.
|
|
81
|
+
expect(ASSET_REGISTRY_SRC).not.toMatch(/\n {2}register<T extends Asset>\(asset: T\)/);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('exposes no by-handle get(handle) resolution entry', () => {
|
|
85
|
+
// Payload resolution is two-tier by slot range on the ECS/render side;
|
|
86
|
+
// AssetRegistry no longer offers a by-handle `.get(handle)` lookup.
|
|
87
|
+
expect(ASSET_REGISTRY_SRC).not.toMatch(/\n {2}get<T extends Asset>\(handle:/);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('holds no handle->payload Map (private assets: Map<number, Asset>)', () => {
|
|
91
|
+
expect(ASSET_REGISTRY_SRC).not.toMatch(/private readonly assets: Map<number, Asset>/);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('holds no handle->guid reverse map (handleToGuid)', () => {
|
|
95
|
+
expect(ASSET_REGISTRY_SRC).not.toMatch(/handleToGuid/);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// ── M3: invalidate / invalidateAll API + generation TDD helpers ───────────
|
|
100
|
+
|
|
101
|
+
function makeMeshFixture(): MeshAsset {
|
|
102
|
+
return {
|
|
103
|
+
kind: 'mesh',
|
|
104
|
+
vertices: new Float32Array(36),
|
|
105
|
+
indices: new Uint16Array([0, 1, 2]),
|
|
106
|
+
attributes: {},
|
|
107
|
+
materialSlots: [{ slotName: 'Default' }],
|
|
108
|
+
submeshes: [
|
|
109
|
+
{
|
|
110
|
+
indexOffset: 0,
|
|
111
|
+
indexCount: 3,
|
|
112
|
+
vertexCount: 3,
|
|
113
|
+
materialSlot: 0,
|
|
114
|
+
topology: 'triangle-list' as const,
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
} as unknown as MeshAsset;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function makeReg(): AssetRegistry {
|
|
121
|
+
return new AssetRegistry(makeMockShaderRegistry());
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function parseGuid(s: string): AssetGuid {
|
|
125
|
+
const r = AssetGuid.parse(s);
|
|
126
|
+
if (!r.ok) throw new Error(`invalid test GUID: ${s}`);
|
|
127
|
+
return r.value;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
describe('M3 invalidate(guid) (AC-03) [w6]', () => {
|
|
131
|
+
it('removes the catalogued asset after catalog then invalidate', () => {
|
|
132
|
+
const reg = makeReg();
|
|
133
|
+
const guid = 'a0000000-0000-4000-a000-000000000001';
|
|
134
|
+
const mesh = makeMeshFixture();
|
|
135
|
+
reg.catalog(guid, mesh);
|
|
136
|
+
expect(reg.lookup(guid)).toBeDefined();
|
|
137
|
+
|
|
138
|
+
(reg as unknown as { invalidate: (guid: string) => void }).invalidate(guid);
|
|
139
|
+
|
|
140
|
+
expect(reg.lookup(guid)).toBeUndefined();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('is a no-op for a non-existent GUID', () => {
|
|
144
|
+
const reg = makeReg();
|
|
145
|
+
(reg as unknown as { invalidate: (guid: string) => void }).invalidate(
|
|
146
|
+
'a0000000-0000-4000-a000-000000000002',
|
|
147
|
+
);
|
|
148
|
+
expect(reg.lookup('a0000000-0000-4000-a000-000000000002')).toBeUndefined();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('clears the targeted body+index entry but leaves bystander entries intact (AC-03)', async () => {
|
|
152
|
+
const originalFetch = globalThis.fetch;
|
|
153
|
+
const reg = makeReg();
|
|
154
|
+
reg.configurePackIndex('/pack-index.json');
|
|
155
|
+
|
|
156
|
+
const targetGuid = 'a0000000-0000-4000-a000-000000000003';
|
|
157
|
+
const bystanderGuid = 'a0000000-0000-4000-a000-000000000004';
|
|
158
|
+
const targetUrl = '/packs/mesh-target.pack.json';
|
|
159
|
+
const bystanderUrl = '/packs/mesh-bystander.pack.json';
|
|
160
|
+
const packIndex = [
|
|
161
|
+
{ guid: targetGuid, packageUrl: targetUrl, kind: 'mesh' },
|
|
162
|
+
{ guid: bystanderGuid, packageUrl: bystanderUrl, kind: 'mesh' },
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
function makePackFile(g: string): unknown {
|
|
166
|
+
return {
|
|
167
|
+
schemaVersion: '2.0.0',
|
|
168
|
+
kind: 'internal-text-package',
|
|
169
|
+
assets: [
|
|
170
|
+
{
|
|
171
|
+
guid: g,
|
|
172
|
+
kind: 'mesh',
|
|
173
|
+
payload: {
|
|
174
|
+
vertices: [
|
|
175
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
|
|
176
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
177
|
+
],
|
|
178
|
+
indices: [0, 1, 2],
|
|
179
|
+
attributes: {},
|
|
180
|
+
submeshes: [
|
|
181
|
+
{ indexOffset: 0, indexCount: 3, vertexCount: 3, topology: 'triangle-list' },
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const fetchMock = vi.fn().mockImplementation((url: string) => {
|
|
190
|
+
if (url === '/pack-index.json') {
|
|
191
|
+
return Promise.resolve({ ok: true, json: () => Promise.resolve(packIndex) });
|
|
192
|
+
}
|
|
193
|
+
if (url === targetUrl) {
|
|
194
|
+
return Promise.resolve({ ok: true, json: () => Promise.resolve(makePackFile(targetGuid)) });
|
|
195
|
+
}
|
|
196
|
+
if (url === bystanderUrl) {
|
|
197
|
+
return Promise.resolve({
|
|
198
|
+
ok: true,
|
|
199
|
+
json: () => Promise.resolve(makePackFile(bystanderGuid)),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
return Promise.resolve({ ok: false, status: 404 });
|
|
203
|
+
});
|
|
204
|
+
globalThis.fetch = fetchMock as typeof globalThis.fetch;
|
|
205
|
+
|
|
206
|
+
const internal = reg as unknown as {
|
|
207
|
+
packFileCache: Map<string, unknown>;
|
|
208
|
+
packIndexCache: Map<string, unknown> | undefined;
|
|
209
|
+
invalidate: (guid: string) => void;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
// Prod-load both GUIDs so packIndexCache holds both entries and
|
|
214
|
+
// packFileCache holds both bodies (keyed by packageUrl).
|
|
215
|
+
const rt = await reg.loadByGuid<MeshAsset>(parseGuid(targetGuid));
|
|
216
|
+
const rb = await reg.loadByGuid<MeshAsset>(parseGuid(bystanderGuid));
|
|
217
|
+
expect(rt.ok).toBe(true);
|
|
218
|
+
expect(rb.ok).toBe(true);
|
|
219
|
+
expect(internal.packFileCache.has(targetUrl)).toBe(true);
|
|
220
|
+
expect(internal.packFileCache.has(bystanderUrl)).toBe(true);
|
|
221
|
+
expect(internal.packIndexCache?.get(targetGuid)).toBeDefined();
|
|
222
|
+
expect(internal.packIndexCache?.get(bystanderGuid)).toBeDefined();
|
|
223
|
+
|
|
224
|
+
internal.invalidate(targetGuid);
|
|
225
|
+
|
|
226
|
+
// Targeted clear: target body + index entry gone.
|
|
227
|
+
expect(internal.packFileCache.has(targetUrl)).toBe(false);
|
|
228
|
+
expect(internal.packIndexCache?.get(targetGuid)).toBeUndefined();
|
|
229
|
+
|
|
230
|
+
// Bystander survives: single-GUID invalidate must not evict unrelated
|
|
231
|
+
// cached bodies/index entries, and keeps the index Map alive (only
|
|
232
|
+
// invalidateAll nukes it to undefined).
|
|
233
|
+
expect(internal.packFileCache.has(bystanderUrl)).toBe(true);
|
|
234
|
+
expect(internal.packIndexCache?.get(bystanderGuid)).toBeDefined();
|
|
235
|
+
expect(internal.packIndexCache).not.toBeUndefined();
|
|
236
|
+
|
|
237
|
+
// Catalog entry removed + generation bumped (kept from round-1).
|
|
238
|
+
expect(reg.lookup(targetGuid)).toBeUndefined();
|
|
239
|
+
} finally {
|
|
240
|
+
if (originalFetch !== undefined) {
|
|
241
|
+
globalThis.fetch = originalFetch;
|
|
242
|
+
} else {
|
|
243
|
+
// biome-ignore lint/suspicious/noExplicitAny: test teardown
|
|
244
|
+
delete (globalThis as any).fetch;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
describe('M3 invalidateAll() (AC-05 + AC-06) [w7]', () => {
|
|
251
|
+
it('clears all catalogued assets and returns clearedCount', () => {
|
|
252
|
+
const reg = makeReg();
|
|
253
|
+
const countBefore = reg as unknown as { assetCatalog: Map<string, unknown> };
|
|
254
|
+
const initialSize = countBefore.assetCatalog.size;
|
|
255
|
+
reg.catalog('a0000000-0000-4000-a000-000000000001', makeMeshFixture());
|
|
256
|
+
reg.catalog('a0000000-0000-4000-a000-000000000002', makeMeshFixture());
|
|
257
|
+
reg.catalog('a0000000-0000-4000-a000-000000000003', makeMeshFixture());
|
|
258
|
+
|
|
259
|
+
const result = (
|
|
260
|
+
reg as unknown as { invalidateAll: () => { clearedCount: number } }
|
|
261
|
+
).invalidateAll();
|
|
262
|
+
|
|
263
|
+
// clearedCount includes builtins (constructor pre-populates 5 GUIDs).
|
|
264
|
+
expect(result.clearedCount).toBe(initialSize + 3);
|
|
265
|
+
expect(reg.lookup('a0000000-0000-4000-a000-000000000001')).toBeUndefined();
|
|
266
|
+
expect(reg.lookup('a0000000-0000-4000-a000-000000000002')).toBeUndefined();
|
|
267
|
+
expect(reg.lookup('a0000000-0000-4000-a000-000000000003')).toBeUndefined();
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('is idempotent: second call returns clearedCount 0', () => {
|
|
271
|
+
const reg = makeReg();
|
|
272
|
+
reg.catalog('a0000000-0000-4000-a000-000000000001', makeMeshFixture());
|
|
273
|
+
|
|
274
|
+
const internal = reg as unknown as {
|
|
275
|
+
invalidateAll: () => { clearedCount: number };
|
|
276
|
+
packIndexCache: Map<string, unknown> | undefined;
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const first = internal.invalidateAll();
|
|
280
|
+
expect(first.clearedCount).toBeGreaterThan(0);
|
|
281
|
+
|
|
282
|
+
const second = internal.invalidateAll();
|
|
283
|
+
expect(second.clearedCount).toBe(0);
|
|
284
|
+
// No-op clear of an already-undefined index cache must not re-create a Map.
|
|
285
|
+
expect(internal.packIndexCache).toBeUndefined();
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('clears packFileCache and resets packIndexCache to undefined (AC-05, R2-1)', async () => {
|
|
289
|
+
const originalFetch = globalThis.fetch;
|
|
290
|
+
const reg = makeReg();
|
|
291
|
+
reg.configurePackIndex('/pack-index.json');
|
|
292
|
+
|
|
293
|
+
const guid = 'a0000000-0000-4000-a000-000000000040';
|
|
294
|
+
const packIndex = [{ guid, packageUrl: '/packs/mesh-all.pack.json', kind: 'mesh' }];
|
|
295
|
+
const packFile = {
|
|
296
|
+
schemaVersion: '2.0.0',
|
|
297
|
+
kind: 'internal-text-package',
|
|
298
|
+
assets: [
|
|
299
|
+
{
|
|
300
|
+
guid,
|
|
301
|
+
kind: 'mesh',
|
|
302
|
+
payload: {
|
|
303
|
+
vertices: [
|
|
304
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
|
305
|
+
0, 0, 0, 0, 0, 0, 0,
|
|
306
|
+
],
|
|
307
|
+
indices: [0, 1, 2],
|
|
308
|
+
attributes: {},
|
|
309
|
+
submeshes: [
|
|
310
|
+
{ indexOffset: 0, indexCount: 3, vertexCount: 3, topology: 'triangle-list' },
|
|
311
|
+
],
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
],
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const fetchMock = vi.fn().mockImplementation((url: string) => {
|
|
318
|
+
if (url === '/pack-index.json') {
|
|
319
|
+
return Promise.resolve({ ok: true, json: () => Promise.resolve(packIndex) });
|
|
320
|
+
}
|
|
321
|
+
if (url === '/packs/mesh-all.pack.json') {
|
|
322
|
+
return Promise.resolve({ ok: true, json: () => Promise.resolve(packFile) });
|
|
323
|
+
}
|
|
324
|
+
return Promise.resolve({ ok: false, status: 404 });
|
|
325
|
+
});
|
|
326
|
+
globalThis.fetch = fetchMock as typeof globalThis.fetch;
|
|
327
|
+
|
|
328
|
+
const internal = reg as unknown as {
|
|
329
|
+
packFileCache: Map<string, unknown>;
|
|
330
|
+
packIndexCache: Map<string, unknown> | undefined;
|
|
331
|
+
invalidateAll: () => { clearedCount: number };
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
// Prod load populates both shared caches.
|
|
336
|
+
const r = await reg.loadByGuid<MeshAsset>(parseGuid(guid));
|
|
337
|
+
expect(r.ok).toBe(true);
|
|
338
|
+
expect(internal.packFileCache.size).toBeGreaterThan(0);
|
|
339
|
+
expect(internal.packIndexCache).not.toBeUndefined();
|
|
340
|
+
|
|
341
|
+
internal.invalidateAll();
|
|
342
|
+
|
|
343
|
+
// Body cache fully cleared.
|
|
344
|
+
expect(internal.packFileCache.size).toBe(0);
|
|
345
|
+
// Index cache reset to undefined -- NOT an empty Map. An empty Map would
|
|
346
|
+
// short-circuit resolveCatalogEntry's `=== undefined` re-fetch guard
|
|
347
|
+
// (asset-registry.ts:2878) and reproduce the F17b pollution.
|
|
348
|
+
expect(internal.packIndexCache).toBeUndefined();
|
|
349
|
+
} finally {
|
|
350
|
+
if (originalFetch !== undefined) {
|
|
351
|
+
globalThis.fetch = originalFetch;
|
|
352
|
+
} else {
|
|
353
|
+
// biome-ignore lint/suspicious/noExplicitAny: test teardown
|
|
354
|
+
delete (globalThis as any).fetch;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
describe('M3 generation drop: invalidate(guid) drops in-flight (AC-11) [w8]', () => {
|
|
361
|
+
let originalFetch: typeof globalThis.fetch | undefined;
|
|
362
|
+
|
|
363
|
+
beforeEach(() => {
|
|
364
|
+
originalFetch = globalThis.fetch;
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
afterEach(() => {
|
|
368
|
+
if (originalFetch !== undefined) {
|
|
369
|
+
globalThis.fetch = originalFetch;
|
|
370
|
+
} else {
|
|
371
|
+
// biome-ignore lint/suspicious/noExplicitAny: test teardown
|
|
372
|
+
delete (globalThis as any).fetch;
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it('returns asset-invalidated when invalidate is called during loadByGuid', async () => {
|
|
377
|
+
const reg = makeReg();
|
|
378
|
+
reg.configurePackIndex('/pack-index.json');
|
|
379
|
+
|
|
380
|
+
const guid = 'a0000000-0000-4000-a000-000000000010';
|
|
381
|
+
const packIndex = [{ guid, packageUrl: '/packs/test-mesh.pack.json', kind: 'mesh' }];
|
|
382
|
+
const packFile = {
|
|
383
|
+
schemaVersion: '2.0.0',
|
|
384
|
+
kind: 'internal-text-package',
|
|
385
|
+
assets: [
|
|
386
|
+
{
|
|
387
|
+
guid,
|
|
388
|
+
kind: 'mesh',
|
|
389
|
+
payload: {
|
|
390
|
+
vertices: [
|
|
391
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
|
392
|
+
0, 0, 0, 0, 0, 0, 0,
|
|
393
|
+
],
|
|
394
|
+
indices: [0, 1, 2],
|
|
395
|
+
attributes: {},
|
|
396
|
+
submeshes: [
|
|
397
|
+
{ indexOffset: 0, indexCount: 3, vertexCount: 3, topology: 'triangle-list' },
|
|
398
|
+
],
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
],
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
// Block the pack-file fetch so we can invalidate before it resolves.
|
|
405
|
+
let resolvePackFile!: (value: unknown) => void;
|
|
406
|
+
const packFilePromise = new Promise<unknown>((resolve) => {
|
|
407
|
+
resolvePackFile = resolve;
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
const fetchMock = vi.fn().mockImplementation((url: string) => {
|
|
411
|
+
if (url === '/pack-index.json') {
|
|
412
|
+
return Promise.resolve({ ok: true, json: () => Promise.resolve(packIndex) });
|
|
413
|
+
}
|
|
414
|
+
if (url === '/packs/test-mesh.pack.json') {
|
|
415
|
+
return packFilePromise.then((v: unknown) => ({ ok: true, json: () => Promise.resolve(v) }));
|
|
416
|
+
}
|
|
417
|
+
return Promise.resolve({ ok: false, status: 404 });
|
|
418
|
+
});
|
|
419
|
+
globalThis.fetch = fetchMock as typeof globalThis.fetch;
|
|
420
|
+
|
|
421
|
+
try {
|
|
422
|
+
const loadPromise = reg.loadByGuid<MeshAsset>(parseGuid(guid));
|
|
423
|
+
|
|
424
|
+
// Invalidate while pack-file fetch is still blocked.
|
|
425
|
+
(reg as unknown as { invalidate: (g: string) => void }).invalidate(guid);
|
|
426
|
+
|
|
427
|
+
// Now release the pack-file fetch.
|
|
428
|
+
resolvePackFile(packFile);
|
|
429
|
+
|
|
430
|
+
const result = await loadPromise;
|
|
431
|
+
expect(result.ok).toBe(false);
|
|
432
|
+
if (!result.ok) {
|
|
433
|
+
expect(result.error.code).toBe('asset-invalidated');
|
|
434
|
+
}
|
|
435
|
+
// Catalog must NOT contain the asset (result was not written).
|
|
436
|
+
expect(reg.lookup(guid)).toBeUndefined();
|
|
437
|
+
} finally {
|
|
438
|
+
// biome-ignore lint/suspicious/noExplicitAny: test teardown
|
|
439
|
+
delete (globalThis as any).fetch;
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
describe('M3 generation drop: invalidateAll drops all in-flight (AC-12) [w9]', () => {
|
|
445
|
+
let originalFetch: typeof globalThis.fetch | undefined;
|
|
446
|
+
|
|
447
|
+
beforeEach(() => {
|
|
448
|
+
originalFetch = globalThis.fetch;
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
afterEach(() => {
|
|
452
|
+
if (originalFetch !== undefined) {
|
|
453
|
+
globalThis.fetch = originalFetch;
|
|
454
|
+
} else {
|
|
455
|
+
// biome-ignore lint/suspicious/noExplicitAny: test teardown
|
|
456
|
+
delete (globalThis as any).fetch;
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
it('returns asset-invalidated for all in-flight loads after invalidateAll', async () => {
|
|
461
|
+
const reg = makeReg();
|
|
462
|
+
reg.configurePackIndex('/pack-index.json');
|
|
463
|
+
|
|
464
|
+
const guidA = 'a0000000-0000-4000-a000-000000000010';
|
|
465
|
+
const guidB = 'a0000000-0000-4000-a000-000000000011';
|
|
466
|
+
const guidC = 'a0000000-0000-4000-a000-000000000012';
|
|
467
|
+
|
|
468
|
+
const packIndex = [
|
|
469
|
+
{ guid: guidA, packageUrl: '/packs/mesh-a.pack.json', kind: 'mesh' },
|
|
470
|
+
{ guid: guidB, packageUrl: '/packs/mesh-b.pack.json', kind: 'mesh' },
|
|
471
|
+
{ guid: guidC, packageUrl: '/packs/mesh-c.pack.json', kind: 'mesh' },
|
|
472
|
+
];
|
|
473
|
+
|
|
474
|
+
function makePackFile(g: string): unknown {
|
|
475
|
+
return {
|
|
476
|
+
schemaVersion: '2.0.0',
|
|
477
|
+
kind: 'internal-text-package',
|
|
478
|
+
assets: [
|
|
479
|
+
{
|
|
480
|
+
guid: g,
|
|
481
|
+
kind: 'mesh',
|
|
482
|
+
payload: {
|
|
483
|
+
vertices: [
|
|
484
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
|
|
485
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
486
|
+
],
|
|
487
|
+
indices: [0, 1, 2],
|
|
488
|
+
attributes: {},
|
|
489
|
+
submeshes: [
|
|
490
|
+
{ indexOffset: 0, indexCount: 3, vertexCount: 3, topology: 'triangle-list' },
|
|
491
|
+
],
|
|
492
|
+
},
|
|
493
|
+
},
|
|
494
|
+
],
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const resolvers: Array<(value: unknown) => void> = [];
|
|
499
|
+
const packFilePromises = [
|
|
500
|
+
'/packs/mesh-a.pack.json',
|
|
501
|
+
'/packs/mesh-b.pack.json',
|
|
502
|
+
'/packs/mesh-c.pack.json',
|
|
503
|
+
].map(() => {
|
|
504
|
+
return new Promise<unknown>((resolve) => {
|
|
505
|
+
resolvers.push(resolve);
|
|
506
|
+
});
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
let resolverIdx = 0;
|
|
510
|
+
const fetchMock = vi.fn().mockImplementation((url: string) => {
|
|
511
|
+
if (url === '/pack-index.json') {
|
|
512
|
+
return Promise.resolve({ ok: true, json: () => Promise.resolve(packIndex) });
|
|
513
|
+
}
|
|
514
|
+
if (url.startsWith('/packs/mesh-')) {
|
|
515
|
+
const p = packFilePromises[resolverIdx++];
|
|
516
|
+
if (p === undefined) throw new Error(`unexpected pack URL: ${url}`);
|
|
517
|
+
return p.then((v: unknown) => ({ ok: true, json: () => Promise.resolve(v) }));
|
|
518
|
+
}
|
|
519
|
+
return Promise.resolve({ ok: false, status: 404 });
|
|
520
|
+
});
|
|
521
|
+
globalThis.fetch = fetchMock as typeof globalThis.fetch;
|
|
522
|
+
|
|
523
|
+
try {
|
|
524
|
+
const loadA = reg.loadByGuid<MeshAsset>(parseGuid(guidA));
|
|
525
|
+
const loadB = reg.loadByGuid<MeshAsset>(parseGuid(guidB));
|
|
526
|
+
const loadC = reg.loadByGuid<MeshAsset>(parseGuid(guidC));
|
|
527
|
+
|
|
528
|
+
// All three are in-flight now. Invalidate all.
|
|
529
|
+
(reg as unknown as { invalidateAll: () => { clearedCount: number } }).invalidateAll();
|
|
530
|
+
|
|
531
|
+
// Now release all pack-file fetches.
|
|
532
|
+
resolvers[0]?.(makePackFile(guidA));
|
|
533
|
+
resolvers[1]?.(makePackFile(guidB));
|
|
534
|
+
resolvers[2]?.(makePackFile(guidC));
|
|
535
|
+
|
|
536
|
+
const results = await Promise.all([loadA, loadB, loadC]);
|
|
537
|
+
for (const r of results) {
|
|
538
|
+
expect(r.ok).toBe(false);
|
|
539
|
+
if (!r.ok) {
|
|
540
|
+
expect(r.error.code).toBe('asset-invalidated');
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Catalog must be empty.
|
|
545
|
+
expect(reg.lookup(guidA)).toBeUndefined();
|
|
546
|
+
expect(reg.lookup(guidB)).toBeUndefined();
|
|
547
|
+
expect(reg.lookup(guidC)).toBeUndefined();
|
|
548
|
+
} finally {
|
|
549
|
+
// biome-ignore lint/suspicious/noExplicitAny: test teardown
|
|
550
|
+
delete (globalThis as any).fetch;
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
describe('M3 integration: invalidate reload + generation match (AC-04 + AC-13) [w12]', () => {
|
|
556
|
+
let originalFetch: typeof globalThis.fetch | undefined;
|
|
557
|
+
|
|
558
|
+
beforeEach(() => {
|
|
559
|
+
originalFetch = globalThis.fetch;
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
afterEach(() => {
|
|
563
|
+
if (originalFetch !== undefined) {
|
|
564
|
+
globalThis.fetch = originalFetch;
|
|
565
|
+
} else {
|
|
566
|
+
// biome-ignore lint/suspicious/noExplicitAny: test teardown
|
|
567
|
+
delete (globalThis as any).fetch;
|
|
568
|
+
}
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
it('AC-04: invalidate(guid) then loadByGuid re-fetches fresh bytes (not stale cache)', async () => {
|
|
572
|
+
const reg = makeReg();
|
|
573
|
+
reg.configurePackIndex('/pack-index.json');
|
|
574
|
+
|
|
575
|
+
const guid = 'a0000000-0000-4000-a000-000000000020';
|
|
576
|
+
const packUrl = '/packs/mesh-reload.pack.json';
|
|
577
|
+
const packIndex = [{ guid, packageUrl: packUrl, kind: 'mesh' }];
|
|
578
|
+
|
|
579
|
+
// The service changes its response body between the two loads: the first
|
|
580
|
+
// pack-file request serves body-v1 (vertices[0] === 0), every subsequent
|
|
581
|
+
// request serves body-v2 (vertices[0] === 7). A genuine re-fetch after
|
|
582
|
+
// invalidate must observe body-v2; a stale-cache hit would still see v1.
|
|
583
|
+
function makePackBody(firstX: number): unknown {
|
|
584
|
+
return {
|
|
585
|
+
schemaVersion: '2.0.0',
|
|
586
|
+
kind: 'internal-text-package',
|
|
587
|
+
assets: [
|
|
588
|
+
{
|
|
589
|
+
guid,
|
|
590
|
+
kind: 'mesh',
|
|
591
|
+
payload: {
|
|
592
|
+
vertices: [
|
|
593
|
+
firstX,
|
|
594
|
+
0,
|
|
595
|
+
0,
|
|
596
|
+
0,
|
|
597
|
+
0,
|
|
598
|
+
0,
|
|
599
|
+
0,
|
|
600
|
+
0,
|
|
601
|
+
0,
|
|
602
|
+
0,
|
|
603
|
+
0,
|
|
604
|
+
0,
|
|
605
|
+
1,
|
|
606
|
+
0,
|
|
607
|
+
0,
|
|
608
|
+
0,
|
|
609
|
+
0,
|
|
610
|
+
0,
|
|
611
|
+
0,
|
|
612
|
+
0,
|
|
613
|
+
0,
|
|
614
|
+
0,
|
|
615
|
+
0,
|
|
616
|
+
0,
|
|
617
|
+
0,
|
|
618
|
+
1,
|
|
619
|
+
0,
|
|
620
|
+
0,
|
|
621
|
+
0,
|
|
622
|
+
0,
|
|
623
|
+
0,
|
|
624
|
+
0,
|
|
625
|
+
0,
|
|
626
|
+
0,
|
|
627
|
+
0,
|
|
628
|
+
0,
|
|
629
|
+
],
|
|
630
|
+
indices: [0, 1, 2],
|
|
631
|
+
attributes: {},
|
|
632
|
+
submeshes: [
|
|
633
|
+
{ indexOffset: 0, indexCount: 3, vertexCount: 3, topology: 'triangle-list' },
|
|
634
|
+
],
|
|
635
|
+
},
|
|
636
|
+
},
|
|
637
|
+
],
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
const BODY_V1_X = 0;
|
|
641
|
+
const BODY_V2_X = 7;
|
|
642
|
+
|
|
643
|
+
let packFetches = 0;
|
|
644
|
+
const fetchMock = vi.fn().mockImplementation((url: string) => {
|
|
645
|
+
if (url === '/pack-index.json') {
|
|
646
|
+
return Promise.resolve({ ok: true, json: () => Promise.resolve(packIndex) });
|
|
647
|
+
}
|
|
648
|
+
if (url === packUrl) {
|
|
649
|
+
packFetches += 1;
|
|
650
|
+
const body = packFetches === 1 ? makePackBody(BODY_V1_X) : makePackBody(BODY_V2_X);
|
|
651
|
+
return Promise.resolve({ ok: true, json: () => Promise.resolve(body) });
|
|
652
|
+
}
|
|
653
|
+
return Promise.resolve({ ok: false, status: 404 });
|
|
654
|
+
});
|
|
655
|
+
globalThis.fetch = fetchMock as typeof globalThis.fetch;
|
|
656
|
+
|
|
657
|
+
try {
|
|
658
|
+
// First load: fetches body-v1, succeeds, writes to catalog.
|
|
659
|
+
const r1 = await reg.loadByGuid<MeshAsset>(parseGuid(guid));
|
|
660
|
+
expect(r1.ok).toBe(true);
|
|
661
|
+
expect(reg.lookup(guid)).toBeDefined();
|
|
662
|
+
expect(packFetches).toBe(1);
|
|
663
|
+
|
|
664
|
+
// Invalidate the catalogued asset.
|
|
665
|
+
(reg as unknown as { invalidate: (g: string) => void }).invalidate(guid);
|
|
666
|
+
expect(reg.lookup(guid)).toBeUndefined();
|
|
667
|
+
|
|
668
|
+
// Second load: must re-fetch the pack-file body (not serve packFileCache).
|
|
669
|
+
const r2 = await reg.loadByGuid<MeshAsset>(parseGuid(guid));
|
|
670
|
+
expect(r2.ok).toBe(true);
|
|
671
|
+
expect(reg.lookup(guid)).toBeDefined();
|
|
672
|
+
|
|
673
|
+
// (a) the reload re-fetched the body -> packFetches incremented to 2.
|
|
674
|
+
expect(packFetches).toBe(2);
|
|
675
|
+
// (b) the reloaded payload is the FRESH body-v2 (vertices[0] === 7),
|
|
676
|
+
// proving the stale cached body-v1 was NOT served.
|
|
677
|
+
if (r2.ok) {
|
|
678
|
+
expect(r2.value.vertices[0]).toBe(BODY_V2_X);
|
|
679
|
+
}
|
|
680
|
+
} finally {
|
|
681
|
+
// biome-ignore lint/suspicious/noExplicitAny: test teardown
|
|
682
|
+
delete (globalThis as any).fetch;
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
it('AC-13: invalidate then immediate reload -- generation matches, payload catalogued', async () => {
|
|
687
|
+
const reg = makeReg();
|
|
688
|
+
reg.configurePackIndex('/pack-index.json');
|
|
689
|
+
|
|
690
|
+
const guid = 'a0000000-0000-4000-a000-000000000021';
|
|
691
|
+
const packIndex = [{ guid, packageUrl: '/packs/mesh-genmatch.pack.json', kind: 'mesh' }];
|
|
692
|
+
const packFile = {
|
|
693
|
+
schemaVersion: '2.0.0',
|
|
694
|
+
kind: 'internal-text-package',
|
|
695
|
+
assets: [
|
|
696
|
+
{
|
|
697
|
+
guid,
|
|
698
|
+
kind: 'mesh',
|
|
699
|
+
payload: {
|
|
700
|
+
vertices: [
|
|
701
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
|
702
|
+
0, 0, 0, 0, 0, 0, 0,
|
|
703
|
+
],
|
|
704
|
+
indices: [0, 1, 2],
|
|
705
|
+
attributes: {},
|
|
706
|
+
submeshes: [
|
|
707
|
+
{ indexOffset: 0, indexCount: 3, vertexCount: 3, topology: 'triangle-list' },
|
|
708
|
+
],
|
|
709
|
+
},
|
|
710
|
+
},
|
|
711
|
+
],
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
const fetchMock = vi.fn().mockImplementation((url: string) => {
|
|
715
|
+
if (url === '/pack-index.json') {
|
|
716
|
+
return Promise.resolve({ ok: true, json: () => Promise.resolve(packIndex) });
|
|
717
|
+
}
|
|
718
|
+
if (url === '/packs/mesh-genmatch.pack.json') {
|
|
719
|
+
return Promise.resolve({ ok: true, json: () => Promise.resolve(packFile) });
|
|
720
|
+
}
|
|
721
|
+
return Promise.resolve({ ok: false, status: 404 });
|
|
722
|
+
});
|
|
723
|
+
globalThis.fetch = fetchMock as typeof globalThis.fetch;
|
|
724
|
+
|
|
725
|
+
try {
|
|
726
|
+
// Load, invalidate, reload immediately -- the new load picks up the
|
|
727
|
+
// updated generation and completes normally.
|
|
728
|
+
const r1 = await reg.loadByGuid<MeshAsset>(parseGuid(guid));
|
|
729
|
+
expect(r1.ok).toBe(true);
|
|
730
|
+
|
|
731
|
+
(reg as unknown as { invalidate: (g: string) => void }).invalidate(guid);
|
|
732
|
+
|
|
733
|
+
// Immediate reload after invalidate: new generation, catalog re-populated.
|
|
734
|
+
const r2 = await reg.loadByGuid<MeshAsset>(parseGuid(guid));
|
|
735
|
+
expect(r2.ok).toBe(true);
|
|
736
|
+
expect(reg.lookup(guid)).toBeDefined();
|
|
737
|
+
} finally {
|
|
738
|
+
// biome-ignore lint/suspicious/noExplicitAny: test teardown
|
|
739
|
+
delete (globalThis as any).fetch;
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
describe('M3 integration: generation discard error shape (AC-11 + AC-12) [w13]', () => {
|
|
745
|
+
let originalFetch: typeof globalThis.fetch | undefined;
|
|
746
|
+
|
|
747
|
+
beforeEach(() => {
|
|
748
|
+
originalFetch = globalThis.fetch;
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
afterEach(() => {
|
|
752
|
+
if (originalFetch !== undefined) {
|
|
753
|
+
globalThis.fetch = originalFetch;
|
|
754
|
+
} else {
|
|
755
|
+
// biome-ignore lint/suspicious/noExplicitAny: test teardown
|
|
756
|
+
delete (globalThis as any).fetch;
|
|
757
|
+
}
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
it('invalidate(guid) during load: error has code, hint, and expected with GUID', async () => {
|
|
761
|
+
const reg = makeReg();
|
|
762
|
+
reg.configurePackIndex('/pack-index.json');
|
|
763
|
+
|
|
764
|
+
const guid = 'a0000000-0000-4000-a000-000000000030';
|
|
765
|
+
const packIndex = [{ guid, packageUrl: '/packs/err-shape.pack.json', kind: 'mesh' }];
|
|
766
|
+
|
|
767
|
+
let resolvePackFile!: (value: unknown) => void;
|
|
768
|
+
const packFilePromise = new Promise<unknown>((resolve) => {
|
|
769
|
+
resolvePackFile = resolve;
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
const fetchMock = vi.fn().mockImplementation((url: string) => {
|
|
773
|
+
if (url === '/pack-index.json') {
|
|
774
|
+
return Promise.resolve({ ok: true, json: () => Promise.resolve(packIndex) });
|
|
775
|
+
}
|
|
776
|
+
if (url === '/packs/err-shape.pack.json') {
|
|
777
|
+
return packFilePromise.then((v: unknown) => ({ ok: true, json: () => Promise.resolve(v) }));
|
|
778
|
+
}
|
|
779
|
+
return Promise.resolve({ ok: false, status: 404 });
|
|
780
|
+
});
|
|
781
|
+
globalThis.fetch = fetchMock as typeof globalThis.fetch;
|
|
782
|
+
|
|
783
|
+
try {
|
|
784
|
+
const loadPromise = reg.loadByGuid<MeshAsset>(parseGuid(guid));
|
|
785
|
+
(reg as unknown as { invalidate: (g: string) => void }).invalidate(guid);
|
|
786
|
+
|
|
787
|
+
resolvePackFile({
|
|
788
|
+
schemaVersion: '2.0.0',
|
|
789
|
+
kind: 'internal-text-package',
|
|
790
|
+
assets: [
|
|
791
|
+
{
|
|
792
|
+
guid,
|
|
793
|
+
kind: 'mesh',
|
|
794
|
+
payload: {
|
|
795
|
+
vertices: [
|
|
796
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
|
|
797
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
798
|
+
],
|
|
799
|
+
indices: [0, 1, 2],
|
|
800
|
+
attributes: {},
|
|
801
|
+
submeshes: [
|
|
802
|
+
{ indexOffset: 0, indexCount: 3, vertexCount: 3, topology: 'triangle-list' },
|
|
803
|
+
],
|
|
804
|
+
},
|
|
805
|
+
},
|
|
806
|
+
],
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
const result = await loadPromise;
|
|
810
|
+
expect(result.ok).toBe(false);
|
|
811
|
+
if (!result.ok) {
|
|
812
|
+
expect(result.error.code).toBe('asset-invalidated');
|
|
813
|
+
expect(result.error.hint).toBeTruthy();
|
|
814
|
+
expect(result.error.hint).toContain('invalidate');
|
|
815
|
+
expect(result.error.expected).toContain(guid.toLowerCase());
|
|
816
|
+
}
|
|
817
|
+
} finally {
|
|
818
|
+
// biome-ignore lint/suspicious/noExplicitAny: test teardown
|
|
819
|
+
delete (globalThis as any).fetch;
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
it('invalidateAll during load: error has code, hint, and expected', async () => {
|
|
824
|
+
const reg = makeReg();
|
|
825
|
+
reg.configurePackIndex('/pack-index.json');
|
|
826
|
+
|
|
827
|
+
const guid = 'a0000000-0000-4000-a000-000000000031';
|
|
828
|
+
const packIndex = [{ guid, packageUrl: '/packs/err-all.pack.json', kind: 'mesh' }];
|
|
829
|
+
|
|
830
|
+
let resolvePackFile!: (value: unknown) => void;
|
|
831
|
+
const packFilePromise = new Promise<unknown>((resolve) => {
|
|
832
|
+
resolvePackFile = resolve;
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
const fetchMock = vi.fn().mockImplementation((url: string) => {
|
|
836
|
+
if (url === '/pack-index.json') {
|
|
837
|
+
return Promise.resolve({ ok: true, json: () => Promise.resolve(packIndex) });
|
|
838
|
+
}
|
|
839
|
+
if (url === '/packs/err-all.pack.json') {
|
|
840
|
+
return packFilePromise.then((v: unknown) => ({ ok: true, json: () => Promise.resolve(v) }));
|
|
841
|
+
}
|
|
842
|
+
return Promise.resolve({ ok: false, status: 404 });
|
|
843
|
+
});
|
|
844
|
+
globalThis.fetch = fetchMock as typeof globalThis.fetch;
|
|
845
|
+
|
|
846
|
+
try {
|
|
847
|
+
const loadPromise = reg.loadByGuid<MeshAsset>(parseGuid(guid));
|
|
848
|
+
(reg as unknown as { invalidateAll: () => { clearedCount: number } }).invalidateAll();
|
|
849
|
+
|
|
850
|
+
resolvePackFile({
|
|
851
|
+
schemaVersion: '2.0.0',
|
|
852
|
+
kind: 'internal-text-package',
|
|
853
|
+
assets: [
|
|
854
|
+
{
|
|
855
|
+
guid,
|
|
856
|
+
kind: 'mesh',
|
|
857
|
+
payload: {
|
|
858
|
+
vertices: [
|
|
859
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
|
|
860
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
861
|
+
],
|
|
862
|
+
indices: [0, 1, 2],
|
|
863
|
+
attributes: {},
|
|
864
|
+
submeshes: [
|
|
865
|
+
{ indexOffset: 0, indexCount: 3, vertexCount: 3, topology: 'triangle-list' },
|
|
866
|
+
],
|
|
867
|
+
},
|
|
868
|
+
},
|
|
869
|
+
],
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
const result = await loadPromise;
|
|
873
|
+
expect(result.ok).toBe(false);
|
|
874
|
+
if (!result.ok) {
|
|
875
|
+
expect(result.error.code).toBe('asset-invalidated');
|
|
876
|
+
expect(result.error.hint).toBeTruthy();
|
|
877
|
+
expect(result.error.hint).toContain('invalidate');
|
|
878
|
+
expect(result.error.expected).toBeTruthy();
|
|
879
|
+
}
|
|
880
|
+
} finally {
|
|
881
|
+
// biome-ignore lint/suspicious/noExplicitAny: test teardown
|
|
882
|
+
delete (globalThis as any).fetch;
|
|
883
|
+
}
|
|
884
|
+
});
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
describe('AC-05 InspectEntry.kind (brand removed)', () => {
|
|
888
|
+
it('inspect() returns entries with .kind (not .brand)', () => {
|
|
889
|
+
const reg = new AssetRegistry(makeMockShaderRegistry());
|
|
890
|
+
const guid = AssetGuid.format(AssetGuid.random());
|
|
891
|
+
reg.catalog(guid, {
|
|
892
|
+
kind: 'sampler' as const,
|
|
893
|
+
magFilter: 'linear' as const,
|
|
894
|
+
minFilter: 'linear' as const,
|
|
895
|
+
});
|
|
896
|
+
const snap = reg.inspect();
|
|
897
|
+
const entry = snap.assets.find((e) => e.guid === guid.toLowerCase());
|
|
898
|
+
expect(entry).toBeDefined();
|
|
899
|
+
expect(entry?.kind).toBe('sampler');
|
|
900
|
+
expect(entry?.name).toBe('');
|
|
901
|
+
expect(entry).toHaveProperty('kind');
|
|
902
|
+
expect(entry).not.toHaveProperty('brand');
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
it('inspect().assets[].kind mirrors payload.kind', () => {
|
|
906
|
+
const reg = new AssetRegistry(makeMockShaderRegistry());
|
|
907
|
+
const payload = {
|
|
908
|
+
kind: 'sampler' as const,
|
|
909
|
+
magFilter: 'linear' as const,
|
|
910
|
+
minFilter: 'linear' as const,
|
|
911
|
+
};
|
|
912
|
+
const guid = AssetGuid.format(AssetGuid.random());
|
|
913
|
+
reg.catalog(guid, payload);
|
|
914
|
+
const snap = reg.inspect();
|
|
915
|
+
const entry = snap.assets.find((e) => e.guid === guid.toLowerCase());
|
|
916
|
+
expect(entry, 'inspect entry for sampler').toBeDefined();
|
|
917
|
+
expect(entry?.kind).toBe('sampler');
|
|
918
|
+
});
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
describe('AC-06 parseAssetPayload passthrough + dev/prod consistency', () => {
|
|
922
|
+
let originalFetch: typeof globalThis.fetch | undefined;
|
|
923
|
+
|
|
924
|
+
beforeEach(() => {
|
|
925
|
+
originalFetch = globalThis.fetch;
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
afterEach(() => {
|
|
929
|
+
if (originalFetch !== undefined) {
|
|
930
|
+
globalThis.fetch = originalFetch;
|
|
931
|
+
} else {
|
|
932
|
+
// biome-ignore lint/suspicious/noExplicitAny: test teardown
|
|
933
|
+
delete (globalThis as any).fetch;
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
it('engine-known kind (mesh) parses correctly via prod path', async () => {
|
|
938
|
+
const reg = makeReg();
|
|
939
|
+
reg.configurePackIndex('/pack-index.json');
|
|
940
|
+
|
|
941
|
+
const guid = 'b0000000-0000-4000-a000-000000000010';
|
|
942
|
+
const packIndex = [{ guid, packageUrl: '/packs/ac06-mesh.pack.json', kind: 'mesh' }];
|
|
943
|
+
|
|
944
|
+
const fetchMock = vi.fn().mockImplementation((url: string) => {
|
|
945
|
+
if (url === '/pack-index.json') {
|
|
946
|
+
return Promise.resolve({ ok: true, json: () => Promise.resolve(packIndex) });
|
|
947
|
+
}
|
|
948
|
+
if (url === '/packs/ac06-mesh.pack.json') {
|
|
949
|
+
return Promise.resolve({
|
|
950
|
+
ok: true,
|
|
951
|
+
json: () =>
|
|
952
|
+
Promise.resolve({
|
|
953
|
+
schemaVersion: '2.0.0',
|
|
954
|
+
kind: 'internal-text-package',
|
|
955
|
+
assets: [
|
|
956
|
+
{
|
|
957
|
+
guid,
|
|
958
|
+
kind: 'mesh',
|
|
959
|
+
payload: {
|
|
960
|
+
vertices: [
|
|
961
|
+
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
962
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
963
|
+
],
|
|
964
|
+
indices: [0, 1, 2],
|
|
965
|
+
attributes: {},
|
|
966
|
+
submeshes: [
|
|
967
|
+
{ indexOffset: 0, indexCount: 3, vertexCount: 3, topology: 'triangle-list' },
|
|
968
|
+
],
|
|
969
|
+
},
|
|
970
|
+
},
|
|
971
|
+
],
|
|
972
|
+
}),
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
return Promise.resolve({ ok: false, status: 404 });
|
|
976
|
+
});
|
|
977
|
+
globalThis.fetch = fetchMock as typeof globalThis.fetch;
|
|
978
|
+
|
|
979
|
+
const result = await reg.loadByGuid<MeshAsset>(parseGuid(guid));
|
|
980
|
+
expect(result.ok).toBe(true);
|
|
981
|
+
if (!result.ok) return;
|
|
982
|
+
expect(result.value.kind).toBe('mesh');
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
it('unmatched kind payload survives passthrough via prod loadByGuid', async () => {
|
|
986
|
+
const reg = makeReg();
|
|
987
|
+
reg.configurePackIndex('/pack-index.json');
|
|
988
|
+
|
|
989
|
+
const guid = 'b0000000-0000-4000-a000-000000000011';
|
|
990
|
+
const customPayload = { fieldA: 42, fieldB: 'hello' };
|
|
991
|
+
const packIndex = [{ guid, packageUrl: '/packs/ac06-unknown.pack.json', kind: 'host-custom' }];
|
|
992
|
+
|
|
993
|
+
const fetchMock = vi.fn().mockImplementation((url: string) => {
|
|
994
|
+
if (url === '/pack-index.json') {
|
|
995
|
+
return Promise.resolve({ ok: true, json: () => Promise.resolve(packIndex) });
|
|
996
|
+
}
|
|
997
|
+
if (url === '/packs/ac06-unknown.pack.json') {
|
|
998
|
+
return Promise.resolve({
|
|
999
|
+
ok: true,
|
|
1000
|
+
json: () =>
|
|
1001
|
+
Promise.resolve({
|
|
1002
|
+
schemaVersion: '2.0.0',
|
|
1003
|
+
kind: 'internal-text-package',
|
|
1004
|
+
assets: [{ guid, kind: 'host-custom', payload: customPayload }],
|
|
1005
|
+
}),
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
return Promise.resolve({ ok: false, status: 404 });
|
|
1009
|
+
});
|
|
1010
|
+
globalThis.fetch = fetchMock as typeof globalThis.fetch;
|
|
1011
|
+
|
|
1012
|
+
const result = await reg.loadByGuid(parseGuid(guid));
|
|
1013
|
+
expect(result.ok).toBe(false);
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
it('dev-path catalog stores payload with kind faithfully', () => {
|
|
1017
|
+
const reg = makeReg();
|
|
1018
|
+
const guid = AssetGuid.format(AssetGuid.random());
|
|
1019
|
+
const payload = {
|
|
1020
|
+
kind: 'sampler' as const,
|
|
1021
|
+
magFilter: 'linear' as const,
|
|
1022
|
+
minFilter: 'linear' as const,
|
|
1023
|
+
};
|
|
1024
|
+
reg.catalog(guid, payload);
|
|
1025
|
+
|
|
1026
|
+
const stored = reg.lookup(guid);
|
|
1027
|
+
expect(stored).toBeDefined();
|
|
1028
|
+
expect(stored?.kind).toBe('sampler');
|
|
1029
|
+
});
|
|
1030
|
+
});
|