@forgeax/engine-assets-runtime 0.0.0-dev.8d955ade1c79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +491 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__tests__/aabb.unit.test.d.ts +2 -0
- package/dist/__tests__/aabb.unit.test.d.ts.map +1 -0
- package/dist/__tests__/artifact-integrity.unit.test.d.ts +2 -0
- package/dist/__tests__/artifact-integrity.unit.test.d.ts.map +1 -0
- package/dist/__tests__/artifact-io.unit.test.d.ts +2 -0
- package/dist/__tests__/artifact-io.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-evidence-sdk.integration.test.d.ts +2 -0
- package/dist/__tests__/asset-evidence-sdk.integration.test.d.ts.map +1 -0
- package/dist/__tests__/asset-evidence.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-evidence.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-kind.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-kind.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-catalog-reconcile.integration.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-catalog-reconcile.integration.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-catalog-reconcile.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-catalog-reconcile.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-catalog.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-catalog.unit.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.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-runtime-error-code-owner.test-d.d.ts +2 -0
- package/dist/__tests__/asset-runtime-error-code-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/catalog-replica.unit.test.d.ts +2 -0
- package/dist/__tests__/catalog-replica.unit.test.d.ts.map +1 -0
- package/dist/__tests__/catalog-source-v2.unit.test.d.ts +2 -0
- package/dist/__tests__/catalog-source-v2.unit.test.d.ts.map +1 -0
- package/dist/__tests__/catalog-source.unit.test.d.ts +2 -0
- package/dist/__tests__/catalog-source.unit.test.d.ts.map +1 -0
- package/dist/__tests__/catalog-subscription-order.unit.test.d.ts +2 -0
- package/dist/__tests__/catalog-subscription-order.unit.test.d.ts.map +1 -0
- package/dist/__tests__/catalog-subscription.unit.test.d.ts +2 -0
- package/dist/__tests__/catalog-subscription.unit.test.d.ts.map +1 -0
- package/dist/__tests__/decode-image-bytes-pass-through.test.d.ts +2 -0
- package/dist/__tests__/decode-image-bytes-pass-through.test.d.ts.map +1 -0
- package/dist/__tests__/decode-image-bytes.browser.test.d.ts +2 -0
- package/dist/__tests__/decode-image-bytes.browser.test.d.ts.map +1 -0
- package/dist/__tests__/decode-image-bytes.test-d.d.ts +2 -0
- package/dist/__tests__/decode-image-bytes.test-d.d.ts.map +1 -0
- package/dist/__tests__/decode-image-bytes.test.d.ts +2 -0
- package/dist/__tests__/decode-image-bytes.test.d.ts.map +1 -0
- package/dist/__tests__/decode-image-mime-owner.test.d.ts +2 -0
- package/dist/__tests__/decode-image-mime-owner.test.d.ts.map +1 -0
- 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-asset.unit.test.d.ts +2 -0
- package/dist/__tests__/errors-asset.unit.test.d.ts.map +1 -0
- package/dist/__tests__/inline-pack-loaders.unit.test.d.ts +2 -0
- package/dist/__tests__/inline-pack-loaders.unit.test.d.ts.map +1 -0
- package/dist/__tests__/instantiate-context.unit.test.d.ts +2 -0
- package/dist/__tests__/instantiate-context.unit.test.d.ts.map +1 -0
- package/dist/__tests__/instantiate-publication-fence.unit.test.d.ts +2 -0
- package/dist/__tests__/instantiate-publication-fence.unit.test.d.ts.map +1 -0
- package/dist/__tests__/instantiate.unit.test.d.ts +2 -0
- package/dist/__tests__/instantiate.unit.test.d.ts.map +1 -0
- package/dist/__tests__/loader-input-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/loader-input-contract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/loader-registry.unit.test.d.ts +2 -0
- package/dist/__tests__/loader-registry.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-dependency-invalidation.unit.test.d.ts +2 -0
- package/dist/__tests__/material-dependency-invalidation.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-generation-cache.unit.test.d.ts +2 -0
- package/dist/__tests__/material-generation-cache.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-load-consumer.test-d.d.ts +2 -0
- package/dist/__tests__/material-load-consumer.test-d.d.ts.map +1 -0
- package/dist/__tests__/material-ready.integration.test.d.ts +2 -0
- package/dist/__tests__/material-ready.integration.test.d.ts.map +1 -0
- package/dist/__tests__/material-stale-generation.unit.test.d.ts +2 -0
- package/dist/__tests__/material-stale-generation.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin-morph-v3.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin-morph-v3.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mipmap-generator.unit.test.d.ts +2 -0
- package/dist/__tests__/mipmap-generator.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pack-artifact-transcode-model-owner.test.d.ts +2 -0
- package/dist/__tests__/pack-artifact-transcode-model-owner.test.d.ts.map +1 -0
- package/dist/__tests__/pack-basis-load.integration.test.d.ts +2 -0
- package/dist/__tests__/pack-basis-load.integration.test.d.ts.map +1 -0
- package/dist/__tests__/payload-validate.unit.test.d.ts +2 -0
- package/dist/__tests__/payload-validate.unit.test.d.ts.map +1 -0
- package/dist/__tests__/plugin.test.d.ts +2 -0
- package/dist/__tests__/plugin.test.d.ts.map +1 -0
- package/dist/__tests__/registry-concurrency.bench.test.d.ts +2 -0
- package/dist/__tests__/registry-concurrency.bench.test.d.ts.map +1 -0
- package/dist/__tests__/registry-failure-retry.integration.test.d.ts +2 -0
- package/dist/__tests__/registry-failure-retry.integration.test.d.ts.map +1 -0
- package/dist/__tests__/registry-load-graph.integration.test.d.ts +2 -0
- package/dist/__tests__/registry-load-graph.integration.test.d.ts.map +1 -0
- package/dist/__tests__/registry-load-state.unit.test.d.ts +2 -0
- package/dist/__tests__/registry-load-state.unit.test.d.ts.map +1 -0
- package/dist/__tests__/registry-package-cache.unit.test.d.ts +2 -0
- package/dist/__tests__/registry-package-cache.unit.test.d.ts.map +1 -0
- package/dist/__tests__/registry-public-ready.unit.test.d.ts +2 -0
- package/dist/__tests__/registry-public-ready.unit.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__/scene-handle-fields.unit.test.d.ts +2 -0
- package/dist/__tests__/scene-handle-fields.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scene-payload.unit.test.d.ts +2 -0
- package/dist/__tests__/scene-payload.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scene-publication-fence.test.d.ts +2 -0
- package/dist/__tests__/scene-publication-fence.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-public-surface.test-d.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-public-surface.test-d.d.ts.map +1 -0
- package/dist/__tests__/static-rig.integration.test.d.ts +2 -0
- package/dist/__tests__/static-rig.integration.test.d.ts.map +1 -0
- package/dist/__tests__/validate-material.unit.test.d.ts +2 -0
- package/dist/__tests__/validate-material.unit.test.d.ts.map +1 -0
- package/dist/__tests__/video-pack-loader.integration.test.d.ts +2 -0
- package/dist/__tests__/video-pack-loader.integration.test.d.ts.map +1 -0
- package/dist/aabb.d.ts +3 -0
- package/dist/aabb.d.ts.map +1 -0
- package/dist/asset-kind.d.ts +4 -0
- package/dist/asset-kind.d.ts.map +1 -0
- package/dist/asset-registry.d.ts +554 -0
- package/dist/asset-registry.d.ts.map +1 -0
- package/dist/builtin-asset-registry.d.ts +22 -0
- package/dist/builtin-asset-registry.d.ts.map +1 -0
- package/dist/catalog-source.d.ts +24 -0
- package/dist/catalog-source.d.ts.map +1 -0
- package/dist/decode-image-bytes.d.ts +57 -0
- package/dist/decode-image-bytes.d.ts.map +1 -0
- package/dist/dynamic-texture-store.d.ts +89 -0
- package/dist/dynamic-texture-store.d.ts.map +1 -0
- package/dist/errors/asset.d.ts +201 -0
- package/dist/errors/asset.d.ts.map +1 -0
- package/dist/handles.d.ts +143 -0
- package/dist/handles.d.ts.map +1 -0
- package/dist/image-error.d.ts +6 -0
- package/dist/image-error.d.ts.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +8359 -0
- package/dist/index.mjs.map +1 -0
- package/dist/internal/artifact-cache.d.ts +17 -0
- package/dist/internal/artifact-cache.d.ts.map +1 -0
- package/dist/internal/asset-graph.d.ts +70 -0
- package/dist/internal/asset-graph.d.ts.map +1 -0
- package/dist/internal/catalog-session.d.ts +60 -0
- package/dist/internal/catalog-session.d.ts.map +1 -0
- package/dist/internal/decoder-registry.d.ts +19 -0
- package/dist/internal/decoder-registry.d.ts.map +1 -0
- package/dist/internal/immutable-payload.d.ts +9 -0
- package/dist/internal/immutable-payload.d.ts.map +1 -0
- package/dist/internal/load-asset.d.ts +31 -0
- package/dist/internal/load-asset.d.ts.map +1 -0
- package/dist/internal/pack-reader.d.ts +18 -0
- package/dist/internal/pack-reader.d.ts.map +1 -0
- package/dist/internal/validate-runtime-row.d.ts +7 -0
- package/dist/internal/validate-runtime-row.d.ts.map +1 -0
- package/dist/internal.d.ts +3 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.mjs +329 -0
- package/dist/internal.mjs.map +1 -0
- package/dist/loader-registry.d.ts +63 -0
- package/dist/loader-registry.d.ts.map +1 -0
- package/dist/loaders/inline-pack.d.ts +72 -0
- package/dist/loaders/inline-pack.d.ts.map +1 -0
- package/dist/loaders/mesh-bin.d.ts +21 -0
- package/dist/loaders/mesh-bin.d.ts.map +1 -0
- package/dist/loaders/pack-artifact.d.ts +20 -0
- package/dist/loaders/pack-artifact.d.ts.map +1 -0
- package/dist/material/dependency-graph.d.ts +9 -0
- package/dist/material/dependency-graph.d.ts.map +1 -0
- package/dist/material/generation-cache.d.ts +24 -0
- package/dist/material/generation-cache.d.ts.map +1 -0
- package/dist/material/inspection.d.ts +24 -0
- package/dist/material/inspection.d.ts.map +1 -0
- package/dist/material/loader.d.ts +59 -0
- package/dist/material/loader.d.ts.map +1 -0
- package/dist/material/runtime-shader.d.ts +14 -0
- package/dist/material/runtime-shader.d.ts.map +1 -0
- package/dist/mesh-bin.d.ts +3 -0
- package/dist/mesh-bin.d.ts.map +1 -0
- package/dist/mipmap-generator.d.ts +132 -0
- package/dist/mipmap-generator.d.ts.map +1 -0
- package/dist/payload-validate.d.ts +60 -0
- package/dist/payload-validate.d.ts.map +1 -0
- package/dist/plugin.d.ts +16 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/registry/artifact-io.d.ts +16 -0
- package/dist/registry/artifact-io.d.ts.map +1 -0
- package/dist/registry/asset-evidence.d.ts +13 -0
- package/dist/registry/asset-evidence.d.ts.map +1 -0
- package/dist/registry/catalog-state.d.ts +47 -0
- package/dist/registry/catalog-state.d.ts.map +1 -0
- package/dist/registry/catalog.d.ts +48 -0
- package/dist/registry/catalog.d.ts.map +1 -0
- package/dist/registry/instantiate.d.ts +153 -0
- package/dist/registry/instantiate.d.ts.map +1 -0
- package/dist/registry/load-by-guid.d.ts +247 -0
- package/dist/registry/load-by-guid.d.ts.map +1 -0
- package/dist/registry/load-state.d.ts +26 -0
- package/dist/registry/load-state.d.ts.map +1 -0
- package/dist/registry/runtime-projection.d.ts +16 -0
- package/dist/registry/runtime-projection.d.ts.map +1 -0
- package/dist/registry/scene-publication-fence.d.ts +38 -0
- package/dist/registry/scene-publication-fence.d.ts.map +1 -0
- package/dist/registry/validate-material.d.ts +77 -0
- package/dist/registry/validate-material.d.ts.map +1 -0
- package/dist/resolve-asset-handle.d.ts +43 -0
- package/dist/resolve-asset-handle.d.ts.map +1 -0
- package/dist/resolve-tileset-runtime.d.ts +23 -0
- package/dist/resolve-tileset-runtime.d.ts.map +1 -0
- package/dist/scene-handle-fields.d.ts +88 -0
- package/dist/scene-handle-fields.d.ts.map +1 -0
- package/dist/scene-payload.d.ts +15 -0
- package/dist/scene-payload.d.ts.map +1 -0
- package/dist/wire-default-loaders.d.ts +29 -0
- package/dist/wire-default-loaders.d.ts.map +1 -0
- package/package.json +76 -0
- package/src/__tests__/aabb.unit.test.ts +97 -0
- package/src/__tests__/artifact-integrity.unit.test.ts +99 -0
- package/src/__tests__/artifact-io.unit.test.ts +127 -0
- package/src/__tests__/asset-evidence-sdk.integration.test.ts +32 -0
- package/src/__tests__/asset-evidence.unit.test.ts +32 -0
- package/src/__tests__/asset-kind.unit.test.ts +14 -0
- package/src/__tests__/asset-registry-catalog-reconcile.integration.test.ts +74 -0
- package/src/__tests__/asset-registry-catalog-reconcile.unit.test.ts +89 -0
- package/src/__tests__/asset-registry-catalog.unit.test.ts +170 -0
- package/src/__tests__/asset-registry.pack-cache.test.ts +96 -0
- package/src/__tests__/asset-registry.unit.test.ts +511 -0
- package/src/__tests__/asset-runtime-error-code-owner.test-d.ts +63 -0
- package/src/__tests__/catalog-replica.unit.test.ts +196 -0
- package/src/__tests__/catalog-source-v2.unit.test.ts +158 -0
- package/src/__tests__/catalog-source.unit.test.ts +126 -0
- package/src/__tests__/catalog-subscription-order.unit.test.ts +65 -0
- package/src/__tests__/catalog-subscription.unit.test.ts +28 -0
- package/src/__tests__/decode-image-bytes-pass-through.test.ts +34 -0
- package/src/__tests__/decode-image-bytes.browser.test.ts +102 -0
- package/src/__tests__/decode-image-bytes.test-d.ts +108 -0
- package/src/__tests__/decode-image-bytes.test.ts +90 -0
- package/src/__tests__/decode-image-mime-owner.test.ts +38 -0
- package/src/__tests__/dynamic-texture-store.unit.test.ts +115 -0
- package/src/__tests__/errors-asset.unit.test.ts +92 -0
- package/src/__tests__/inline-pack-loaders.unit.test.ts +548 -0
- package/src/__tests__/instantiate-context.unit.test.ts +65 -0
- package/src/__tests__/instantiate-publication-fence.unit.test.ts +158 -0
- package/src/__tests__/instantiate.unit.test.ts +684 -0
- package/src/__tests__/loader-input-contract.unit.test.ts +89 -0
- package/src/__tests__/loader-registry.unit.test.ts +101 -0
- package/src/__tests__/material-dependency-invalidation.unit.test.ts +14 -0
- package/src/__tests__/material-generation-cache.unit.test.ts +46 -0
- package/src/__tests__/material-load-consumer.test-d.ts +42 -0
- package/src/__tests__/material-ready.integration.test.ts +508 -0
- package/src/__tests__/material-stale-generation.unit.test.ts +147 -0
- package/src/__tests__/mesh-bin-morph-v3.test.ts +22 -0
- package/src/__tests__/mesh-bin.unit.test.ts +152 -0
- package/src/__tests__/mipmap-generator.unit.test.ts +202 -0
- package/src/__tests__/pack-artifact-transcode-model-owner.test.ts +32 -0
- package/src/__tests__/pack-basis-load.integration.test.ts +168 -0
- package/src/__tests__/payload-validate.unit.test.ts +285 -0
- package/src/__tests__/plugin.test.ts +55 -0
- package/src/__tests__/registry-concurrency.bench.test.ts +40 -0
- package/src/__tests__/registry-failure-retry.integration.test.ts +69 -0
- package/src/__tests__/registry-load-graph.integration.test.ts +246 -0
- package/src/__tests__/registry-load-state.unit.test.ts +35 -0
- package/src/__tests__/registry-package-cache.unit.test.ts +53 -0
- package/src/__tests__/registry-public-ready.unit.test.ts +38 -0
- package/src/__tests__/resolve-asset-handle.unit.test.ts +238 -0
- package/src/__tests__/scene-handle-fields.unit.test.ts +129 -0
- package/src/__tests__/scene-payload.unit.test.ts +151 -0
- package/src/__tests__/scene-publication-fence.test.ts +129 -0
- package/src/__tests__/scriptable-pack-public-surface.test-d.ts +27 -0
- package/src/__tests__/static-rig.integration.test.ts +8 -0
- package/src/__tests__/validate-material.unit.test.ts +359 -0
- package/src/__tests__/video-pack-loader.integration.test.ts +83 -0
- package/src/aabb.ts +107 -0
- package/src/asset-kind.ts +6 -0
- package/src/asset-registry.ts +2045 -0
- package/src/builtin-asset-registry.ts +164 -0
- package/src/catalog-source.ts +89 -0
- package/src/decode-image-bytes.ts +126 -0
- package/src/dynamic-texture-store.ts +234 -0
- package/src/errors/asset.ts +347 -0
- package/src/handles.ts +186 -0
- package/src/image-error.ts +45 -0
- package/src/index.ts +189 -0
- package/src/internal/artifact-cache.ts +65 -0
- package/src/internal/asset-graph.ts +420 -0
- package/src/internal/catalog-session.ts +345 -0
- package/src/internal/decoder-registry.ts +175 -0
- package/src/internal/immutable-payload.ts +20 -0
- package/src/internal/load-asset.ts +254 -0
- package/src/internal/pack-reader.ts +183 -0
- package/src/internal/validate-runtime-row.ts +51 -0
- package/src/internal.ts +6 -0
- package/src/loader-registry.ts +157 -0
- package/src/loaders/inline-pack.ts +837 -0
- package/src/loaders/mesh-bin.ts +304 -0
- package/src/loaders/pack-artifact.ts +665 -0
- package/src/material/dependency-graph.ts +46 -0
- package/src/material/generation-cache.ts +177 -0
- package/src/material/inspection.ts +46 -0
- package/src/material/loader.ts +326 -0
- package/src/material/runtime-shader.ts +133 -0
- package/src/mesh-bin.ts +5 -0
- package/src/mipmap-generator.ts +450 -0
- package/src/payload-validate.ts +573 -0
- package/src/plugin.ts +48 -0
- package/src/registry/artifact-io.ts +259 -0
- package/src/registry/asset-evidence.ts +51 -0
- package/src/registry/catalog-state.ts +409 -0
- package/src/registry/catalog.ts +369 -0
- package/src/registry/instantiate.ts +1056 -0
- package/src/registry/load-by-guid.ts +1657 -0
- package/src/registry/load-state.ts +115 -0
- package/src/registry/runtime-projection.ts +63 -0
- package/src/registry/scene-publication-fence.ts +256 -0
- package/src/registry/validate-material.ts +385 -0
- package/src/resolve-asset-handle.ts +147 -0
- package/src/resolve-tileset-runtime.ts +72 -0
- package/src/scene-handle-fields.ts +221 -0
- package/src/scene-payload.ts +207 -0
- package/src/wire-default-loaders.ts +79 -0
package/README.md
ADDED
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
# @forgeax/engine-assets-runtime
|
|
2
|
+
|
|
3
|
+
## MaterialAsset 唯一成功路径
|
|
4
|
+
|
|
5
|
+
Runtime 负责 `paramSchema -> derive -> compile/reflect -> cook/load -> extract/record`
|
|
6
|
+
中的 load 与 extract/record 消费:按 GUID 读取 producer 已 cook 的 artifact、
|
|
7
|
+
receipt、`coordinateSet` 与 `physicalUvScale`,验证 `layoutIdentity` 后交给
|
|
8
|
+
render owner。缺失或 stale 时 inspect、修复 producer 并 recook;不会在 runtime
|
|
9
|
+
编译 WGSL 或补写 app 侧坐标。
|
|
10
|
+
|
|
11
|
+
> [!CAUTION]
|
|
12
|
+
> 只按结构化 `code`、`detail`、`hint` 恢复;不要复制 source-owned error union,
|
|
13
|
+
> 也不要从 URL 或数组顺序重建 asset identity。
|
|
14
|
+
|
|
15
|
+
## Authoring and recovery index
|
|
16
|
+
|
|
17
|
+
## Material contract index
|
|
18
|
+
|
|
19
|
+
The runtime loader reads the Pack publication by GUID and exposes a read-only
|
|
20
|
+
projection to render. Runtime values and module slots are data, while compiler
|
|
21
|
+
context is a closed input; no material macro, feature define, cook, or DDC
|
|
22
|
+
write belongs here. Preserve the layered identities
|
|
23
|
+
`materialContractDigest`, `sourceClosureDigest`, `layoutIdentity`,
|
|
24
|
+
`programIdentity`, `cookIdentity`, and `materialPublicationIdentity`, and
|
|
25
|
+
compare `current` with `generation` before sending the first producer
|
|
26
|
+
divergence back through cold-cook and receipt/artifact verification.
|
|
27
|
+
|
|
28
|
+
The shortest static consumer path is `configurePackIndex(url)` followed by
|
|
29
|
+
`loadByGuid<ConcreteAsset>(guid)`. The public barrel exposes the concrete
|
|
30
|
+
16-kind types and returns durable payloads, dependency `refs`, and local
|
|
31
|
+
`artifacts`; it does not mint a generic GUID-to-handle materializer. Animation,
|
|
32
|
+
tileset, render, audio, and VFX owners perform their own World or Host
|
|
33
|
+
projection after load.
|
|
34
|
+
|
|
35
|
+
Catalog source is selected once per host mode: `configureRuntimeBinding(binding)`
|
|
36
|
+
owns the scope/generation-bound catalog used by Vite development servers, while
|
|
37
|
+
`configurePackIndex(url)` owns the static catalog emitted by a production build.
|
|
38
|
+
They are alternatives, not a sequence. App demos can use the shared
|
|
39
|
+
`configureRuntimeAssetCatalog` helper from `@forgeax/apps-shared` to keep this
|
|
40
|
+
selection as one SSOT and avoid overwriting a scoped development URL with
|
|
41
|
+
`/pack-index.json`.
|
|
42
|
+
|
|
43
|
+
Read structured errors by `code` and use `hint`/`detail` to inspect, rebuild or
|
|
44
|
+
cold-cook, refresh LKG, or attach a capability. Host capability loss affects
|
|
45
|
+
install/play/execute only; descriptor loading stays available.
|
|
46
|
+
|
|
47
|
+
The runtime matrix is the same 16 durable kinds as `SCRIPTABLE_PACK_ASSET_KINDS`:
|
|
48
|
+
`mesh`, `material`, `scene`, `texture`, `equirect`, `sampler`, `font`,
|
|
49
|
+
`render-pipeline`, `tileset`, `video`, `skeleton`, `skin`, `animation-clip`,
|
|
50
|
+
`animation-graph`, `audio`, and `particle-effect`.
|
|
51
|
+
|
|
52
|
+
The authority map is [`asset-authority.schema.json`](../../asset-authority.schema.json). Runtime reads Catalog decisions and validated Pack/DDC projections by GUID; it never writes Pack, Meta, DDC, or authoring state.
|
|
53
|
+
|
|
54
|
+
## Material publication inspection
|
|
55
|
+
|
|
56
|
+
`inspectMaterialRuntime(ready)` is the read-only, GUID-addressed inspection
|
|
57
|
+
projection after the `MaterialReady` gate. Its first-level identity is
|
|
58
|
+
`materialGuid`, `publicationGeneration`, `specializationKey`,
|
|
59
|
+
`artifactDigest`, and `readiness`. Expand `sourceClosure`,
|
|
60
|
+
`parameterContract`, `refs`, and `receipt` only when owner debugging requires
|
|
61
|
+
them. The returned `status: 'Ready'` is retained for compatibility with the
|
|
62
|
+
runtime result; `readiness: 'ready'` is the machine-readable state field.
|
|
63
|
+
|
|
64
|
+
Material failure consumers branch on stable kebab-case codes:
|
|
65
|
+
`shader-module-not-found`, `material-reflection-binding-mismatch`,
|
|
66
|
+
`material-specialization-not-cooked`, `asset-artifact-missing`,
|
|
67
|
+
`asset-artifact-integrity-mismatch`, and `material-cook-record-invalid`.
|
|
68
|
+
Read `expected`, `actual`, `hint`, `retryable`, and `recoveryActions` when
|
|
69
|
+
present. Do not infer readiness from a transport URL, a shader manifest, a
|
|
70
|
+
natural-language message, or a fallback material.
|
|
71
|
+
|
|
72
|
+
## Plugin-owned loader registrations
|
|
73
|
+
|
|
74
|
+
`assetsPlugin(registry)` provides the renderer-owned registry to a Cordis
|
|
75
|
+
realm. `assetLoaderPlugin(loader)` and `packLoaderPlugin(loader)` register one
|
|
76
|
+
loader as a reversible Fiber effect, so removing a feature cannot leave a stale
|
|
77
|
+
kind handler behind. GUID, Catalog, Pack, and payload identity remain owned by
|
|
78
|
+
`AssetRegistry`; the plugin controls only the registration lifetime.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
const feature = await app.pluginContext.plugin(assetLoaderPlugin(dialogueLoader));
|
|
82
|
+
const dialogue = await app.renderer.assets.loadByGuid<Dialogue>(guid);
|
|
83
|
+
await feature.dispose();
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Use `registry.loaders.register(...)` directly only in a lower-level host that
|
|
87
|
+
owns the matching disposer itself. App/game capability composition should use
|
|
88
|
+
the plugin helpers so registration and teardown share one Cordis lifecycle.
|
|
89
|
+
|
|
90
|
+
| Lifecycle | Runtime action | Recovery |
|
|
91
|
+
|:--|:--|:--|
|
|
92
|
+
| `current` | Load the validated projection | Inspect evidence if a dependency fails |
|
|
93
|
+
| `missing` / `cooking` | Return structured not-ready failure | Rebuild in the studio/build host |
|
|
94
|
+
| `stale` / `failed` | Do not use raw source | Cold cook, or explicitly preview last-known-good |
|
|
95
|
+
|
|
96
|
+
Use [`check-asset-authority-audit.mjs`](../../scripts/forgeax/check-asset-authority-audit.mjs) to inspect the owner and runtime-source conclusion before changing a producer.
|
|
97
|
+
|
|
98
|
+
> [!IMPORTANT]
|
|
99
|
+
> Runtime material consumption is `configurePackIndex` -> `loadByGuid<MaterialAsset>` -> cooked readiness -> `world.internSharedRef`. The registry returns the loaded payload and follows its dependency graph; it does not create an app-owned shader artifact or a parallel material authoring surface. Interning preserves one handle per catalogued payload identity inside a World; explicit `world.allocSharedRef` calls remain independent resources.
|
|
100
|
+
|
|
101
|
+
## MaterialAsset runtime recovery
|
|
102
|
+
|
|
103
|
+
Load a root and any inherited child through the same GUID catalog. A ready material must have its effective `passes`, `values`, `parent` chain, per-slot `coordinates`, references, artifact, and receipt. For a failure, switch on the structured code, read `detail` and `hint`, repair the package or cook output, and retry the same GUID. A missing cook is not a valid fallback to runtime compilation.
|
|
104
|
+
|
|
105
|
+
The runtime asset layer: catalogue an asset by GUID, load its payload + all
|
|
106
|
+
transitively-referenced sub-assets, resolve a `Handle` back to its payload, and
|
|
107
|
+
wire the default loader set. Tier 2.1 package extracted from
|
|
108
|
+
`@forgeax/engine-runtime` (feat-20260705-runtime-tier2-decomposition M1) so an AI
|
|
109
|
+
user loads only the asset-cluster concept surface — not the whole renderer — when
|
|
110
|
+
the task is "get an asset into the World".
|
|
111
|
+
|
|
112
|
+
## AssetEvidence SDK boundary
|
|
113
|
+
|
|
114
|
+
`AssetRegistry.inspect(guid)` and `verifyByGuid(guid)` expose the injected SDK side of the same `AssetEvidence` chain: source declaration, catalog `packageUrl`/`cookReceiptUrl`, producer `CookReceipt`, Pack v2 artifact verification, and optional runtime state. The registry does not import the Node CLI, Vite, or filesystem policy. Call `configureAssetEvidence(source)` in a host that can supply those facts; without it the result is explicit `unknown`/capability-missing evidence.
|
|
115
|
+
|
|
116
|
+
Cook states are not interchangeable: `notCooked`, `ready/current`, `ready/stale`, and `unknown` each describe a different recovery action. Package/artifact verification separately reports `notChecked`, `passed`, or `failed`. Follow `.code` and `.hint`, repair the producer or package, and rerun the probe; never convert a missing capability into passed evidence.
|
|
117
|
+
|
|
118
|
+
## 30-second self-introduction
|
|
119
|
+
|
|
120
|
+
- **`AssetRegistry`** — instance-per-engine GUID -> payload catalogue. `catalog` /
|
|
121
|
+
`loadByGuid` / `lookup` / `parseGuid` / `inspect` / `resolveName` / `packageOf` /
|
|
122
|
+
`rename` / `invalidate` / `invalidateAll` / `instantiate`. Post-D-17 it stores
|
|
123
|
+
the PAYLOAD and mints no handles (scene GUID resolution interns column handles
|
|
124
|
+
on the World via `world.internSharedRef('Kind', payload)`). `Renderer.assets` is an `AssetRegistry`
|
|
125
|
+
assembled by `createRenderer` (which injects the post-spawn hook and concrete
|
|
126
|
+
Web Audio loader; video is a default loader — see D-1 / D-2).
|
|
127
|
+
- **`HANDLE_CUBE` / `HANDLE_TRIANGLE` / `HANDLE_QUAD` / `HANDLE_SPHERE` /
|
|
128
|
+
`HANDLE_CYLINDER` / `HANDLE_NINESLICE_QUAD`** — process-static builtin mesh
|
|
129
|
+
handles (reserved ids 1-6, `< BUILTIN_BASE`), resolved through
|
|
130
|
+
`BuiltinAssetRegistry` (never reference-counted). Pair with `MeshFilter`.
|
|
131
|
+
- **`resolveAssetHandle(world, handle)`** — two-tier (builtin / user-tier
|
|
132
|
+
`world.sharedRefs`) handle -> payload resolution; returns a closed-union error
|
|
133
|
+
(`shared-ref-stale` / `shared-ref-released` / `asset-not-found`) so callers
|
|
134
|
+
distinguish "re-acquire handle" from "re-load asset" from "check GUID".
|
|
135
|
+
- **`LoaderRegistry` + `wireDefaultLoaders(registry, extraLoaders?)` +
|
|
136
|
+
`createDefaultLoaderRegistry(extraLoaders?)`** — the default set covers the
|
|
137
|
+
complete 16-kind durable `Asset` union, including both inline and artifact
|
|
138
|
+
forms. `createRenderer` may replace the durable audio descriptor loader with
|
|
139
|
+
its concrete Web Audio catalog-entry loader; the registry still has one
|
|
140
|
+
owner per kind.
|
|
141
|
+
|
|
142
|
+
### 30s hands-on example
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { AssetRegistry, HANDLE_CUBE } from '@forgeax/engine-assets-runtime';
|
|
146
|
+
import { MeshFilter, MeshRenderer } from '@forgeax/engine-render';
|
|
147
|
+
import { Transform } from '@forgeax/engine-scene';
|
|
148
|
+
|
|
149
|
+
// A builtin mesh handle needs no registration ceremony:
|
|
150
|
+
world.spawn(
|
|
151
|
+
{ component: Transform, data: { pos: [0, 0, 0], quat: [0, 0, 0, 1], scale: [1, 1, 1] } },
|
|
152
|
+
{ component: MeshFilter, data: { assetHandle: HANDLE_CUBE } },
|
|
153
|
+
{ component: MeshRenderer, data: { materials: [matHandle] } },
|
|
154
|
+
).unwrap();
|
|
155
|
+
|
|
156
|
+
// Imported meshes normally use an empty override vector. AssetRegistry
|
|
157
|
+
// recursively loads MeshAsset.materialSlots defaults; Render inherits them.
|
|
158
|
+
world.spawn(
|
|
159
|
+
{ component: Transform, data: {} },
|
|
160
|
+
{ component: MeshFilter, data: { assetHandle: importedMeshHandle } },
|
|
161
|
+
{ component: MeshRenderer, data: { materials: [] } },
|
|
162
|
+
).unwrap();
|
|
163
|
+
|
|
164
|
+
// Catalogue + load a GUID-addressed asset (dev / inline path):
|
|
165
|
+
const guid = assets.parseGuid('cbe42beb-8975-5096-b3a1-3dda4cb4c077');
|
|
166
|
+
const res = await assets.loadByGuid(guid); // -> Result<payload> (D-17: payload, not handle)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## API surface
|
|
170
|
+
|
|
171
|
+
| Symbol | Kind | One-line |
|
|
172
|
+
|:--|:--|:--|
|
|
173
|
+
| `AssetRegistry` | class | GUID -> payload catalogue + loader dispatch + scene instantiate |
|
|
174
|
+
| `HANDLE_CUBE` / `HANDLE_TRIANGLE` / `HANDLE_QUAD` / `HANDLE_SPHERE` / `HANDLE_CYLINDER` / `HANDLE_NINESLICE_QUAD` | const | builtin mesh handles (ids 1-6) |
|
|
175
|
+
| `BuiltinAssetRegistry` / `BUILTIN_*` / `BUILTIN_BASE` | const | process-static builtin payloads and reserved slot boundary; the shared vertex-layout SSOT is `PROCEDURAL_FLOATS_PER_VERTEX` from `@forgeax/engine-geometry` |
|
|
176
|
+
| `resolveAssetHandle` / `walkMaterialPassesOverSharedRefs` | fn | two-tier handle -> payload resolution |
|
|
177
|
+
| `LoaderRegistry` | class | kind -> loader dispatch table |
|
|
178
|
+
| `wireDefaultLoaders` / `createDefaultLoaderRegistry` | fn | wire all 16 durable Asset loaders + caller `extraLoaders` |
|
|
179
|
+
| `assetsPlugin` / `assetLoaderPlugin` / `packLoaderPlugin` | fn | Provide a registry and bind loader registrations to a Cordis Fiber lifetime |
|
|
180
|
+
| `DynamicTextureStore` / `DynamicTextureDevice` | class/type | per-frame dynamic texture upload store; replacement devices invalidate stale transient textures before the next upload |
|
|
181
|
+
| `unpackMeshBin` / `UnpackedMeshBin` | fn/type | strict mesh-binary v4 sidecar decode with geometry projection verification |
|
|
182
|
+
| `validateTilesetPayload` / `TilesetValidateOptions` | fn/type | register-time tileset payload gate |
|
|
183
|
+
| `PostSpawnHook` / `SkinJointResolver` | type | post-spawn hook contract (D-1; runtime injects `postSpawnResolveJoints`) |
|
|
184
|
+
| `Asset` / `MeshAsset` | type | re-exported asset union shapes (SSOT `@forgeax/engine-types`) |
|
|
185
|
+
|
|
186
|
+
Full `AssetRegistry` surface + signatures: source SSOT
|
|
187
|
+
`packages/assets-runtime/src/asset-registry.ts`. The load + DDC / pack-fetch
|
|
188
|
+
pipeline lives in `packages/assets-runtime/src/registry/load-by-guid.ts`; the
|
|
189
|
+
instantiate cluster + hook types in `registry/instantiate.ts`; material
|
|
190
|
+
validation in `registry/validate-material.ts`.
|
|
191
|
+
|
|
192
|
+
## Mesh binary v4 loading
|
|
193
|
+
|
|
194
|
+
[`loaders/mesh-bin.ts`](src/loaders/mesh-bin.ts) is the sole runtime decoder.
|
|
195
|
+
It rebuilds the geometry projection from the wire mask, compares schema version,
|
|
196
|
+
stride, and digest, validates every byte/cardinality bound, then publishes the
|
|
197
|
+
mesh and metadata as one result. Inline-pack loading uses the same loader.
|
|
198
|
+
|
|
199
|
+
| Failure | Runtime behavior | Next action |
|
|
200
|
+
|:--|:--|:--|
|
|
201
|
+
| v2/v3 or malformed bytes | closed `MeshBinAssetError` with sourceKey, expected/actual, and recovery hint | Re-cook through the owning importer |
|
|
202
|
+
| invalid refs or metadata | no MeshAsset publication | Repair producer refs/Meta and retry |
|
|
203
|
+
| failed replacement after a prior load | retain the catalogued last-known-good payload | Inspect evidence, cold-cook, then publish atomically |
|
|
204
|
+
|
|
205
|
+
> [!WARNING]
|
|
206
|
+
> Runtime has no legacy decoder and no re-cook fallback. A white/default mesh
|
|
207
|
+
> is not a valid recovery for a malformed artifact.
|
|
208
|
+
|
|
209
|
+
### Scene instantiation failure atomicity
|
|
210
|
+
|
|
211
|
+
> [!IMPORTANT]
|
|
212
|
+
> `assets.instantiate` and `assets.instantiateFlat` run the renderer-injected
|
|
213
|
+
> post-spawn hook as part of one transaction. If joint wiring returns the
|
|
214
|
+
> existing `skin-joint-path-unresolved` or `skin-asset-unresolved` result, the
|
|
215
|
+
> method returns that exact structured error and removes only the entities,
|
|
216
|
+
> hierarchy, mount state, joint publication, and shared-reference grants
|
|
217
|
+
> created by that call. Repair the catalogued asset or joint path, then retry
|
|
218
|
+
> with the same registry and World; no process or World reconstruction is
|
|
219
|
+
> required.
|
|
220
|
+
|
|
221
|
+
## Catalog source: enumerate first-class asset rows, then observe row changes
|
|
222
|
+
|
|
223
|
+
> [!IMPORTANT]
|
|
224
|
+
> A catalog is an enumerable complete row set plus subscribable row changes.
|
|
225
|
+
> Wire the source, **subscribe before enumerating**, then keep the local view
|
|
226
|
+
> keyed by GUID. `CatalogDelta` reports facts only; it never chooses a page
|
|
227
|
+
> reload or an editor update policy.
|
|
228
|
+
|
|
229
|
+
`CatalogSource` is the runtime boundary between `AssetRegistry` and a concrete
|
|
230
|
+
catalog transport. Its two operations are `enumerate()` and `subscribe()`;
|
|
231
|
+
the public row and delta shapes are `CatalogEntry` and `CatalogDelta` from
|
|
232
|
+
`@forgeax/engine-types`. Read those exported types for their complete fields
|
|
233
|
+
instead of copying a second schema into a consumer.
|
|
234
|
+
|
|
235
|
+
### Producer fact parity
|
|
236
|
+
|
|
237
|
+
> [!IMPORTANT]
|
|
238
|
+
> Static entries and URL/fetch entries have the same neutral `CatalogEntry`
|
|
239
|
+
> shape. `parseCatalog()` preserves `packageId`, `provenance`, `revision`,
|
|
240
|
+
> `sourceKey`, `sourceIndex`, `relations`, and `diagnostics`; it never derives
|
|
241
|
+
> them from `relativeUrl`, `sourcePath`, or array position.
|
|
242
|
+
|
|
243
|
+
| Runtime input | What is checked | What is not done |
|
|
244
|
+
|:--|:--|:--|
|
|
245
|
+
| Static `entries` | Optional expected revision continuity | No payload decode or re-import |
|
|
246
|
+
| URL `fetch` | JSON shape, required locator, and optional expected revision | No identity reconstruction from the URL |
|
|
247
|
+
| Malformed row | Structured `asset-parse-failed` with `expected` and `hint` | Never converted into an empty successful catalog |
|
|
248
|
+
| Revision mismatch | Structured failure with expected and actual revisions | Never applied over the last verified snapshot |
|
|
249
|
+
|
|
250
|
+
The source is a neutral facts boundary, not an asset decoder. Decode, upload,
|
|
251
|
+
and GPU work remain in their existing loader paths; the catalog parser only
|
|
252
|
+
enumerates facts and locators.
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
const unsubscribe = assets.subscribeCatalog((delta) => {
|
|
256
|
+
// Delete first, then replace added/changed rows by their stable GUID.
|
|
257
|
+
for (const guid of delta.removed) rowsByGuid.delete(guid.toLowerCase());
|
|
258
|
+
for (const row of [...delta.added, ...delta.changed]) {
|
|
259
|
+
rowsByGuid.set(row.guid.toLowerCase(), row);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
const snapshot = await assets.enumerateCatalog();
|
|
264
|
+
if (!snapshot.ok) {
|
|
265
|
+
console.error(snapshot.error.code, snapshot.error.hint);
|
|
266
|
+
// Fix the source configuration or catalog endpoint, then call enumerateCatalog() again.
|
|
267
|
+
} else {
|
|
268
|
+
for (const row of snapshot.value) rowsByGuid.set(row.guid.toLowerCase(), row);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Safe to call more than once.
|
|
272
|
+
unsubscribe();
|
|
273
|
+
unsubscribe();
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Delta and refresh ownership
|
|
277
|
+
|
|
278
|
+
| Delta set | Carries | Consumer action |
|
|
279
|
+
|:--|:--|:--|
|
|
280
|
+
| `added` | complete new `CatalogEntry` rows | insert/replace by lowercase GUID |
|
|
281
|
+
| `changed` | complete replacement `CatalogEntry` rows | replace by lowercase GUID |
|
|
282
|
+
| `removed` | stable GUID strings | remove by lowercase GUID |
|
|
283
|
+
|
|
284
|
+
The producer emits no delta when its final catalog projection is unchanged.
|
|
285
|
+
Source-only byte changes likewise are not forged into a row change. A browser
|
|
286
|
+
host that needs those bytes to refresh explicitly selects its own policy (for
|
|
287
|
+
the Vite adapter, `reloadAssetHost()`); an editor can instead merge the delta
|
|
288
|
+
without recreating its realm. The registry does not import Vite or infer either
|
|
289
|
+
policy.
|
|
290
|
+
|
|
291
|
+
### Recovery and static sources
|
|
292
|
+
|
|
293
|
+
Call `setCatalogSource(source)` before enumeration. Replacing a source disposes the
|
|
294
|
+
previous producer subscription; call `clearCatalogSource()` during host teardown
|
|
295
|
+
to remove the active transport and replica listeners without clearing payload caches.
|
|
296
|
+
Without a source,
|
|
297
|
+
`enumerateCatalog()` returns the structured `catalog-source-unconfigured`
|
|
298
|
+
error; endpoint and parse failures remain structured results as well. Inspect
|
|
299
|
+
`.code` and `.hint`, repair the external condition, then enumerate again — a
|
|
300
|
+
failed enumeration is not a permanent result.
|
|
301
|
+
|
|
302
|
+
When a source returns `asset-parse-failed`, branch on `.expected`, `.hint`, and
|
|
303
|
+
`.detail` rather than `.message`. For a stale revision, keep the last verified
|
|
304
|
+
catalog, repair or re-fetch the producer revision, and enumerate again.
|
|
305
|
+
|
|
306
|
+
Subscribe before the first enumeration to avoid the consumer-side missed-event
|
|
307
|
+
window. If a consumer subscribed late or needs to resynchronise after a
|
|
308
|
+
transport interruption, enumerate again and merge the returned complete rows
|
|
309
|
+
by GUID. A static source may safely return an idempotent no-op unsubscribe: it
|
|
310
|
+
has no continuing change transport and must not manufacture deltas.
|
|
311
|
+
|
|
312
|
+
## Error model
|
|
313
|
+
|
|
314
|
+
`AssetRuntimeErrorCode` is the package's closed error-code SSOT (exhaustive
|
|
315
|
+
`switch (err.code)` without `default`; TS guards completeness). Read the source,
|
|
316
|
+
don't duplicate the member list — `packages/assets-runtime/src/errors/asset.ts`
|
|
317
|
+
(grep `export type AssetRuntimeErrorCode`). Each error object carries
|
|
318
|
+
`.code` / `.expected` / `.hint` / `.detail`. `RendererError` (in
|
|
319
|
+
`@forgeax/engine-runtime`) composes `AssetRuntimeError` into its onError fan-out
|
|
320
|
+
union, so a dropped arm is a compile error.
|
|
321
|
+
|
|
322
|
+
## Dependencies
|
|
323
|
+
|
|
324
|
+
`@forgeax/engine-{codec, ecs, geometry, graphics-extras, image, pack, rhi, shader, types}`.
|
|
325
|
+
Never imports `@forgeax/engine-runtime` or an audio backend: runtime injects the
|
|
326
|
+
post-spawn hook and concrete audio catalog-entry loader at `createRenderer`.
|
|
327
|
+
|
|
328
|
+
## Runtime image bytes decoder (`decodeImageBytes`)
|
|
329
|
+
|
|
330
|
+
`decodeImageBytes(bytes, mime, opts?)` is the runtime SDK entry for AI users
|
|
331
|
+
who already hold image bytes in memory (fetched from a URL, embedded as
|
|
332
|
+
base64, produced by an out-of-tree decoder, etc.) and want to feed them into
|
|
333
|
+
`world.allocSharedRef('TextureAsset', pod)` + `GpuResourceStore.ensureResident`
|
|
334
|
+
without the disk-side importer / pack build pipeline in the loop. It is the
|
|
335
|
+
runtime counterpart to the build-time `.bin` / `.ktx2` texture loaders --
|
|
336
|
+
those stay authoritative for shipped assets; `decodeImageBytes` covers the
|
|
337
|
+
"bytes only exist at runtime" case that static loaders cannot serve
|
|
338
|
+
(tweak-20260714).
|
|
339
|
+
|
|
340
|
+
### Signature
|
|
341
|
+
|
|
342
|
+
```ts
|
|
343
|
+
export async function decodeImageBytes(
|
|
344
|
+
bytes: Uint8Array | ArrayBuffer,
|
|
345
|
+
mime: string,
|
|
346
|
+
opts?: { colorSpace?: 'srgb' | 'linear'; mipmap?: boolean },
|
|
347
|
+
): Promise<Result<TextureAsset, ImageError>>;
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
- `bytes` -- encoded image byte stream (PNG or JPEG). Both `Uint8Array` and
|
|
351
|
+
`ArrayBuffer` accepted; the function does not take ownership.
|
|
352
|
+
- `mime` -- byte-stream mime type. v1 whitelist: `'image/png' | 'image/jpeg'`
|
|
353
|
+
(see boundaries below).
|
|
354
|
+
- `opts.colorSpace` -- `'srgb'` (default) or `'linear'`. Derives POD `format`:
|
|
355
|
+
`srgb -> 'rgba8unorm-srgb'`, `linear -> 'rgba8unorm'` (mirrors the
|
|
356
|
+
build-time `packages/image/src/image-importer.ts` `colorSpaceToFormat`
|
|
357
|
+
rule -- one SSOT, no drift).
|
|
358
|
+
- `opts.mipmap` -- `true` (default) or `false`. When `true`, `mipLevelCount`
|
|
359
|
+
is computed by the existing `numMipLevels({ width, height })`; when
|
|
360
|
+
`false`, `mipLevelCount === 1`.
|
|
361
|
+
|
|
362
|
+
### v1 boundaries (explicit non-goals)
|
|
363
|
+
|
|
364
|
+
The function is intentionally a thin bridge from bytes to a `TextureAsset`
|
|
365
|
+
POD. What it does NOT do:
|
|
366
|
+
|
|
367
|
+
- **No network I/O.** `decodeImageBytes` never `fetch`es; the caller supplies
|
|
368
|
+
bytes.
|
|
369
|
+
- **No GPU upload.** The POD is fed into the existing
|
|
370
|
+
`world.allocSharedRef('TextureAsset', pod)` +
|
|
371
|
+
`GpuResourceStore.ensureResident` path -- the upload primitives are not
|
|
372
|
+
duplicated or replaced.
|
|
373
|
+
- **v1 supports PNG / JPEG only.** GIF / WebP / SVG / AVIF / KTX2 / HDR
|
|
374
|
+
(`.hdr`) fall to `image-format-unsupported`; convert offline (or
|
|
375
|
+
reach for the build-time importer, which handles a wider set) rather
|
|
376
|
+
than expanding this API's mime table.
|
|
377
|
+
- **Not a replacement for the static texture loader.** Shipped `.bin` /
|
|
378
|
+
`.ktx2` continue to flow through the pack pipeline (`loadByGuid`); this
|
|
379
|
+
API only covers the runtime-only-bytes case (progressive disclosure --
|
|
380
|
+
AI user sees the smaller, more focused surface).
|
|
381
|
+
- **Not a Node / server-side decoder.** Requires an environment with
|
|
382
|
+
`createImageBitmap` + `OffscreenCanvas` (browser main thread or Worker).
|
|
383
|
+
Missing capability surfaces as a structured `image-decode-failed` error
|
|
384
|
+
(never a silent broken POD).
|
|
385
|
+
|
|
386
|
+
### Error codes (closed union subset)
|
|
387
|
+
|
|
388
|
+
`decodeImageBytes` only ever produces the four base `ImageErrorCode` members
|
|
389
|
+
listed here; the other atlas / HDR members of the union are not reachable
|
|
390
|
+
from this API. Every error object carries `.code` / `.expected` /
|
|
391
|
+
`.hint` / `.detail`; `.detail` narrows per `.code` (discriminated union).
|
|
392
|
+
Read the source, do not duplicate the member list --
|
|
393
|
+
`packages/types/src/index.ts` (grep `export type ImageErrorCode`).
|
|
394
|
+
|
|
395
|
+
| code | trigger | `.detail` narrows to |
|
|
396
|
+
|:--|:--|:--|
|
|
397
|
+
| `image-format-unsupported` | mime not in `['image/png', 'image/jpeg']` | `{ actualMime, path?, formatColorSpaceConflict? }` |
|
|
398
|
+
| `image-decode-failed` | decoder rejected bytes, or env lacks `createImageBitmap` | `{ reason, path? }` |
|
|
399
|
+
| `image-dimension-out-of-bounds` | reserved; transparent pass-through if the underlying decoder ever surfaces it | `{ requested: {width,height}, limit }` |
|
|
400
|
+
| `image-meta-missing` | reserved; not raised by this API in v1 (kept in the union for a single grep-discoverable SSOT) | `{ sourcePath, expectedSidecarPath }` |
|
|
401
|
+
|
|
402
|
+
### Error self-recovery paradigm
|
|
403
|
+
|
|
404
|
+
Structured errors with copy-pastable hints -- AI users consume via property
|
|
405
|
+
access, never by parsing `.message` (charter P3 explicit failure + P4
|
|
406
|
+
consistent abstraction; AGENTS.md Error model). Exhaustive `switch
|
|
407
|
+
(err.code)` needs no `default` -- TypeScript guards union completeness at
|
|
408
|
+
compile time, so future minor adds to `ImageErrorCode` surface as a
|
|
409
|
+
localised type error rather than a silent miss.
|
|
410
|
+
|
|
411
|
+
```ts
|
|
412
|
+
import { decodeImageBytes } from '@forgeax/engine-assets-runtime';
|
|
413
|
+
|
|
414
|
+
const bytes = new Uint8Array(await (await fetch(url)).arrayBuffer());
|
|
415
|
+
const result = await decodeImageBytes(bytes, 'image/png');
|
|
416
|
+
if (!result.ok) {
|
|
417
|
+
const err = result.error;
|
|
418
|
+
// .hint carries an executable recovery instruction (see IMAGE_ERROR_HINTS
|
|
419
|
+
// SSOT in packages/types/src/index.ts); no string parsing needed.
|
|
420
|
+
console.error(err.code, err.hint);
|
|
421
|
+
// ImageError correlates the envelope code with its detail shape, so one
|
|
422
|
+
// discriminant drives both recovery routing and IDE narrowing.
|
|
423
|
+
switch (err.code) {
|
|
424
|
+
case 'image-format-unsupported':
|
|
425
|
+
// err.detail.actualMime -- rejected mime; convert offline
|
|
426
|
+
console.error('bad mime:', err.detail.actualMime);
|
|
427
|
+
break;
|
|
428
|
+
case 'image-decode-failed':
|
|
429
|
+
// err.detail.reason -- underlying decoder message (or "env lacks
|
|
430
|
+
// createImageBitmap" when the platform capability is missing)
|
|
431
|
+
console.error('decode reason:', err.detail.reason);
|
|
432
|
+
break;
|
|
433
|
+
case 'image-dimension-out-of-bounds':
|
|
434
|
+
console.error('too big:', err.detail.requested, err.detail.limit);
|
|
435
|
+
break;
|
|
436
|
+
case 'image-meta-missing':
|
|
437
|
+
console.error('missing sidecar:', err.detail.expectedSidecarPath);
|
|
438
|
+
break;
|
|
439
|
+
}
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Bytes in, POD out -- charter P4 one abstraction, same POD shape as the
|
|
444
|
+
// build-time texture loader emits, so downstream does not care about the
|
|
445
|
+
// byte source (progressive disclosure: allocSharedRef + ensureResident is
|
|
446
|
+
// the same call site as static assets).
|
|
447
|
+
const handle = world.allocSharedRef('TextureAsset', result.value);
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
### Isolation gate boundary
|
|
451
|
+
|
|
452
|
+
`decode-image-bytes.ts` is the SINGLE file in `@forgeax/engine-assets-runtime`
|
|
453
|
+
allowed to statically import `@forgeax/engine-image`. The
|
|
454
|
+
`scripts/check-image-pipeline-isolation.mjs` (a.2-anti) rule pins this
|
|
455
|
+
exact path as its whitelist; the wider runtime and the rest of
|
|
456
|
+
assets-runtime remain gated so a future accidental static import falls
|
|
457
|
+
loud, not silent.
|
|
458
|
+
|
|
459
|
+
## Route map
|
|
460
|
+
|
|
461
|
+
- Import images / glTF / fonts, wire `loadByGuid`, author sidecars: skill
|
|
462
|
+
`forgeax-engine-assets`.
|
|
463
|
+
- Full asset-chain narrative (sidecar -> import -> pack-index -> loadByGuid):
|
|
464
|
+
`packages/pack/README.md` + `forgeax-engine-assets/README.md`.
|
|
465
|
+
- Runtime image bytes decoding (this package, runtime-only-bytes case):
|
|
466
|
+
see the `decodeImageBytes` section above.
|
|
467
|
+
# Static asset evidence
|
|
468
|
+
|
|
469
|
+
> [!IMPORTANT]
|
|
470
|
+
> Static assets load from Pack v2 through `packageUrl`. Runtime-only bytes are the separate exception.
|
|
471
|
+
|
|
472
|
+
`AssetEvidence` joins `packageUrl`, cook freshness, artifact verification, and optional runtime state. Use `lookup/verify --guid --project --catalog --json` or the SDK `inspect(guid)` / `verifyByGuid(guid)` surface. `notCooked`, `stale`, and `unknown` are distinct recovery states; `unknown` is not verification success.
|
|
473
|
+
|
|
474
|
+
## Indexed consumer recovery
|
|
475
|
+
|
|
476
|
+
The runtime consumer follows a read-only handoff:
|
|
477
|
+
|
|
478
|
+
1. **Inspect** with `assets.inspect(guid)` or `assets.verifyByGuid(guid)` and
|
|
479
|
+
branch on the structured result's `code`, `detail`, and evidence states.
|
|
480
|
+
2. **Repair and rebuild or cold-cook** in the producer host. The runtime never
|
|
481
|
+
writes source Meta, Pack, DDC, receipts, or Catalog authority.
|
|
482
|
+
3. **Verify** source freshness, receipt, package integrity, and artifact
|
|
483
|
+
status through the injected evidence source or Pack CLI.
|
|
484
|
+
4. **Retry** the same GUID after a verified Catalog projection is available.
|
|
485
|
+
Do not parse log strings, compile raw source in the player, or substitute a
|
|
486
|
+
custom mesh/material for a missing engine product.
|
|
487
|
+
|
|
488
|
+
`AssetRegistry` consumes a producer projection and exposes structured not-ready
|
|
489
|
+
errors. It does not register importers, run `runImport`, own DDC lifecycle, or
|
|
490
|
+
expand the runtime transport surface. `unknown` means that required evidence
|
|
491
|
+
was unavailable; it is never a successful verification result.
|