@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/src/index.ts
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// @forgeax/engine-assets-runtime -- public API barrel.
|
|
2
|
+
//
|
|
3
|
+
// Tier 2.1 package extracted from @forgeax/engine-runtime
|
|
4
|
+
// (feat-20260705-runtime-tier2-decomposition M1). The asset registry, loader
|
|
5
|
+
// registry, GUID resolution, default-loader wiring, builtin handles, and the
|
|
6
|
+
// dynamic-texture / mipmap helpers live here; the runtime package injects the
|
|
7
|
+
// post-spawn hook + extra loaders (audio / video) at the createRenderer
|
|
8
|
+
// assembly point.
|
|
9
|
+
//
|
|
10
|
+
// w14 public surface: exports exactly the asset-cluster symbols that the
|
|
11
|
+
// runtime package (src + __tests__) consumes plus what the pre-w14 runtime
|
|
12
|
+
// barrel re-exported to external consumers. Downstream apps repoint to this
|
|
13
|
+
// package in w15 (the runtime barrel no longer re-exports any asset symbol).
|
|
14
|
+
|
|
15
|
+
export type {
|
|
16
|
+
AnimationClip,
|
|
17
|
+
AnimationGraph,
|
|
18
|
+
AudioClipAsset,
|
|
19
|
+
EquirectAsset,
|
|
20
|
+
FontAsset,
|
|
21
|
+
MaterialAsset,
|
|
22
|
+
ParticleEffectAsset,
|
|
23
|
+
RenderPipelineAsset,
|
|
24
|
+
SamplerAsset,
|
|
25
|
+
SceneAsset,
|
|
26
|
+
SkeletonAsset,
|
|
27
|
+
SkinAsset,
|
|
28
|
+
TextureAsset,
|
|
29
|
+
TilesetAsset,
|
|
30
|
+
VideoAsset,
|
|
31
|
+
} from '@forgeax/engine-types';
|
|
32
|
+
export { defineAssetKind } from './asset-kind.js';
|
|
33
|
+
// ─── AssetRegistry + Asset / MeshAsset type aliases ─────────────────────────
|
|
34
|
+
export type {
|
|
35
|
+
Asset,
|
|
36
|
+
CatalogReconcileError,
|
|
37
|
+
CatalogReconcileResult,
|
|
38
|
+
MeshAsset,
|
|
39
|
+
} from './asset-registry';
|
|
40
|
+
export { AssetRegistry } from './asset-registry';
|
|
41
|
+
// ─── Process-static builtin payload registry + vertex-layout SSOT ───────────
|
|
42
|
+
export {
|
|
43
|
+
BUILTIN_BASE,
|
|
44
|
+
BUILTIN_CUBE,
|
|
45
|
+
BUILTIN_CYLINDER,
|
|
46
|
+
BUILTIN_NINESLICE_QUAD,
|
|
47
|
+
BUILTIN_QUAD,
|
|
48
|
+
BUILTIN_SPHERE,
|
|
49
|
+
BUILTIN_TRIANGLE,
|
|
50
|
+
BuiltinAssetRegistry,
|
|
51
|
+
} from './builtin-asset-registry';
|
|
52
|
+
export { type CatalogListener, type CatalogSource, createCatalogSource } from './catalog-source';
|
|
53
|
+
// ─── Runtime image byte decoder (tweak-20260714 M1) ──────────────────────────
|
|
54
|
+
export { decodeImageBytes } from './decode-image-bytes';
|
|
55
|
+
// ─── Dynamic per-frame texture store ────────────────────────────────────────
|
|
56
|
+
export {
|
|
57
|
+
adaptDynamicTextureDevice,
|
|
58
|
+
type DynamicTextureDevice,
|
|
59
|
+
DynamicTextureStore,
|
|
60
|
+
} from './dynamic-texture-store';
|
|
61
|
+
// ─── Asset cluster error model (closed union + classes) ─────────────────────
|
|
62
|
+
export type {
|
|
63
|
+
AssetRuntimeError,
|
|
64
|
+
AssetRuntimeErrorCode,
|
|
65
|
+
MaterialResolvedEmptyPassesDetail,
|
|
66
|
+
SceneCollectAssetGuidUnresolvedDetail,
|
|
67
|
+
SceneCollectEntityRefOutOfClosureDetail,
|
|
68
|
+
} from './errors/asset';
|
|
69
|
+
export {
|
|
70
|
+
MaterialResolvedEmptyPassesError,
|
|
71
|
+
MeshBinAssetError,
|
|
72
|
+
MeshSsboCapacityExceededError,
|
|
73
|
+
MeshSsboCeilingReachedError,
|
|
74
|
+
SceneCollectAssetGuidUnresolvedError,
|
|
75
|
+
SceneCollectEntityRefOutOfClosureError,
|
|
76
|
+
} from './errors/asset';
|
|
77
|
+
// ─── Builtin mesh handles (re-exported by asset-registry from ./handles) ─────
|
|
78
|
+
export {
|
|
79
|
+
builtinMeshGuid,
|
|
80
|
+
HANDLE_CUBE,
|
|
81
|
+
HANDLE_CYLINDER,
|
|
82
|
+
HANDLE_NINESLICE_QUAD,
|
|
83
|
+
HANDLE_QUAD,
|
|
84
|
+
HANDLE_SPHERE,
|
|
85
|
+
HANDLE_TRIANGLE,
|
|
86
|
+
} from './handles';
|
|
87
|
+
// The five-action runtime registry is distinct from the legacy authoring
|
|
88
|
+
// AssetRegistry class above. Keep its resolver on the public package boundary
|
|
89
|
+
// so render/VFX hosts never reach through `/internal` to inspect loaded assets.
|
|
90
|
+
export {
|
|
91
|
+
type AssetRegistry as RuntimeAssetRegistry,
|
|
92
|
+
type AssetRegistryResolver,
|
|
93
|
+
createAssetRegistry,
|
|
94
|
+
getAssetRegistryResolver,
|
|
95
|
+
} from './internal/load-asset.js';
|
|
96
|
+
// ─── Loader-injection surface ───────────────────────────────────────────────
|
|
97
|
+
export { LoaderRegistry } from './loader-registry';
|
|
98
|
+
// ─── Default loader tables + individual loaders (pre-w14 consumer face) ──────
|
|
99
|
+
export {
|
|
100
|
+
animationClipLoader,
|
|
101
|
+
animationGraphLoader,
|
|
102
|
+
audioLoader,
|
|
103
|
+
INLINE_PACK_LOADERS,
|
|
104
|
+
materialLoader,
|
|
105
|
+
meshLoader,
|
|
106
|
+
particleEffectLoader,
|
|
107
|
+
renderPipelineLoader,
|
|
108
|
+
sceneLoader,
|
|
109
|
+
skeletonLoader,
|
|
110
|
+
skinLoader,
|
|
111
|
+
tilesetLoader,
|
|
112
|
+
} from './loaders/inline-pack';
|
|
113
|
+
// ─── Mesh binary container decode ───────────────────────────────────────────
|
|
114
|
+
export {
|
|
115
|
+
type UnpackedMeshBin,
|
|
116
|
+
unpackMeshBinV4,
|
|
117
|
+
} from './loaders/mesh-bin';
|
|
118
|
+
export {
|
|
119
|
+
equirectLoader,
|
|
120
|
+
fontLoader,
|
|
121
|
+
PACK_ARTIFACT_LOADERS,
|
|
122
|
+
renderPipelineLoader as renderPipelineArtifactLoader,
|
|
123
|
+
textureLoader,
|
|
124
|
+
tilesetLoader as tilesetArtifactLoader,
|
|
125
|
+
} from './loaders/pack-artifact';
|
|
126
|
+
export { MaterialGenerationCache } from './material/generation-cache';
|
|
127
|
+
export { inspectMaterialRuntime, type MaterialRuntimeInfo } from './material/inspection';
|
|
128
|
+
export {
|
|
129
|
+
createMaterialLoader,
|
|
130
|
+
type MaterialLoadError,
|
|
131
|
+
type MaterialLoadErrorCode,
|
|
132
|
+
type MaterialLoadErrorDetail,
|
|
133
|
+
type MaterialLoaderOptions,
|
|
134
|
+
type MaterialLoadRequest,
|
|
135
|
+
type MaterialPublication,
|
|
136
|
+
type MaterialReady,
|
|
137
|
+
} from './material/loader';
|
|
138
|
+
export {
|
|
139
|
+
installMaterialReadyShaders,
|
|
140
|
+
materialParametersToParamSchema,
|
|
141
|
+
runtimeMaterialShaderId,
|
|
142
|
+
} from './material/runtime-shader';
|
|
143
|
+
// ─── Mipmap generation helpers ──────────────────────────────────────────────
|
|
144
|
+
export {
|
|
145
|
+
blitMipmapsSync,
|
|
146
|
+
getOrCreateMipmapPipeline,
|
|
147
|
+
type MipmapBlitDevice,
|
|
148
|
+
type MipmapShaderModuleFactory,
|
|
149
|
+
mipmapCacheSize,
|
|
150
|
+
numMipLevels,
|
|
151
|
+
} from './mipmap-generator';
|
|
152
|
+
// ─── Register-time payload validation ───────────────────────────────────────
|
|
153
|
+
export { type TilesetValidateOptions, validateTilesetPayload } from './payload-validate';
|
|
154
|
+
export { assetLoaderPlugin, assetsPlugin, packLoaderPlugin } from './plugin';
|
|
155
|
+
export {
|
|
156
|
+
createRuntimeAssetEvidenceAdapter,
|
|
157
|
+
type RuntimeEvidenceSource,
|
|
158
|
+
} from './registry/asset-evidence';
|
|
159
|
+
export { CatalogReplica, type CatalogReplicaSnapshot } from './registry/catalog-state';
|
|
160
|
+
export type { PostSpawnHook, SkinJointResolver } from './registry/instantiate';
|
|
161
|
+
// ─── Scene instantiate collaboration contract types (D-1 injected hook) ─────
|
|
162
|
+
export { buildSceneChildContext } from './registry/instantiate';
|
|
163
|
+
export { loadMaterialReadyByGuid } from './registry/load-by-guid';
|
|
164
|
+
export {
|
|
165
|
+
compareScenePublicationFences,
|
|
166
|
+
createScenePublicationFence,
|
|
167
|
+
observeScenePublication,
|
|
168
|
+
parseScenePublicationFence,
|
|
169
|
+
SCENE_PUBLICATION_FENCE_SCHEMA,
|
|
170
|
+
SCENE_PUBLICATION_RECOVERY_ACTIONS,
|
|
171
|
+
type ScenePublicationFence,
|
|
172
|
+
type ScenePublicationFenceError,
|
|
173
|
+
type ScenePublicationFencePhase,
|
|
174
|
+
type ScenePublicationObservation,
|
|
175
|
+
scenePublicationFenceFromCatalog,
|
|
176
|
+
} from './registry/scene-publication-fence';
|
|
177
|
+
// ─── Handle-to-payload resolution ───────────────────────────────────────────
|
|
178
|
+
export { resolveAssetHandle, walkMaterialPassesOverSharedRefs } from './resolve-asset-handle';
|
|
179
|
+
export {
|
|
180
|
+
type ResolvedTilesetRuntime,
|
|
181
|
+
resolveTilesetRuntime,
|
|
182
|
+
type TilesetAtlasLookup,
|
|
183
|
+
type TilesetRuntimeError,
|
|
184
|
+
type TilesetRuntimeErrorCode,
|
|
185
|
+
} from './resolve-tileset-runtime';
|
|
186
|
+
// Public scene-pack boundary: editor/play hosts reuse the engine's canonical
|
|
187
|
+
// refs-index -> SceneAsset reconstruction when refreshing a saved SceneAsset.
|
|
188
|
+
export { parseScenePayload } from './scene-payload';
|
|
189
|
+
export { createDefaultLoaderRegistry, wireDefaultLoaders } from './wire-default-loaders';
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { AssetLoadError, Result } from '@forgeax/engine-types';
|
|
2
|
+
|
|
3
|
+
export interface ArtifactCacheSnapshot {
|
|
4
|
+
readonly entries: number;
|
|
5
|
+
readonly pending: number;
|
|
6
|
+
readonly hits: number;
|
|
7
|
+
readonly misses: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class ArtifactCache {
|
|
11
|
+
private readonly values = new Map<string, Uint8Array>();
|
|
12
|
+
private readonly pending = new Map<string, Promise<Result<Uint8Array, AssetLoadError>>>();
|
|
13
|
+
private hits = 0;
|
|
14
|
+
private misses = 0;
|
|
15
|
+
|
|
16
|
+
read(
|
|
17
|
+
contentAddress: string,
|
|
18
|
+
reader: () => Promise<Result<Uint8Array, AssetLoadError>>,
|
|
19
|
+
): Promise<Result<Uint8Array, AssetLoadError>> {
|
|
20
|
+
const value = this.values.get(contentAddress);
|
|
21
|
+
if (value !== undefined) {
|
|
22
|
+
this.hits += 1;
|
|
23
|
+
return Promise.resolve({ ok: true, value: new Uint8Array(value) } as Result<
|
|
24
|
+
Uint8Array,
|
|
25
|
+
AssetLoadError
|
|
26
|
+
>);
|
|
27
|
+
}
|
|
28
|
+
const pending = this.pending.get(contentAddress);
|
|
29
|
+
if (pending !== undefined) {
|
|
30
|
+
this.hits += 1;
|
|
31
|
+
return pending;
|
|
32
|
+
}
|
|
33
|
+
this.misses += 1;
|
|
34
|
+
const request = Promise.resolve()
|
|
35
|
+
.then(reader)
|
|
36
|
+
.then((result) => {
|
|
37
|
+
if (result.ok) this.values.set(contentAddress, new Uint8Array(result.value));
|
|
38
|
+
return result;
|
|
39
|
+
})
|
|
40
|
+
.finally(() => {
|
|
41
|
+
if (this.pending.get(contentAddress) === request) this.pending.delete(contentAddress);
|
|
42
|
+
});
|
|
43
|
+
this.pending.set(contentAddress, request);
|
|
44
|
+
return request;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
clear(contentAddress?: string): void {
|
|
48
|
+
if (contentAddress === undefined) {
|
|
49
|
+
this.values.clear();
|
|
50
|
+
this.pending.clear();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
this.values.delete(contentAddress);
|
|
54
|
+
this.pending.delete(contentAddress);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
snapshot(): ArtifactCacheSnapshot {
|
|
58
|
+
return Object.freeze({
|
|
59
|
+
entries: this.values.size,
|
|
60
|
+
pending: this.pending.size,
|
|
61
|
+
hits: this.hits,
|
|
62
|
+
misses: this.misses,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import type { AssetLoadError, Result } from '@forgeax/engine-types';
|
|
2
|
+
import { err, ok } from '@forgeax/engine-types';
|
|
3
|
+
import { freezeRuntimePayload } from './immutable-payload.js';
|
|
4
|
+
|
|
5
|
+
export interface AssetGraphValue<P> {
|
|
6
|
+
readonly value: P;
|
|
7
|
+
readonly refs: readonly string[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface AssetGraphOptions<
|
|
11
|
+
P extends { readonly value: unknown; readonly refs: readonly string[] },
|
|
12
|
+
> {
|
|
13
|
+
readonly read: (guid: string, signal: AbortSignal) => Promise<Result<P, AssetLoadError>>;
|
|
14
|
+
readonly maxConcurrentReads?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface AssetGraphCounters {
|
|
18
|
+
readonly loads: number;
|
|
19
|
+
readonly cacheHits: number;
|
|
20
|
+
readonly readErrors: number;
|
|
21
|
+
readonly noChange: number;
|
|
22
|
+
readonly listenerFailures: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface AssetGraphSnapshot {
|
|
26
|
+
readonly epoch: number;
|
|
27
|
+
readonly ready: readonly string[];
|
|
28
|
+
readonly pending: number;
|
|
29
|
+
readonly resources: number;
|
|
30
|
+
readonly sccs: readonly (readonly string[])[];
|
|
31
|
+
readonly counters: AssetGraphCounters;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type GraphResult<P> = Result<P, AssetLoadError>;
|
|
35
|
+
type GraphListener = (snapshot: AssetGraphSnapshot) => void;
|
|
36
|
+
|
|
37
|
+
function cancelled(guid: string): AssetLoadError {
|
|
38
|
+
return {
|
|
39
|
+
code: 'asset-load-cancelled',
|
|
40
|
+
expected: 'the request AbortSignal to remain live until asset closure completes',
|
|
41
|
+
hint: 'retry with a live AbortSignal when the request is still needed',
|
|
42
|
+
detail: { guid },
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function disposed(scopeId = 'asset-runtime'): AssetLoadError {
|
|
47
|
+
return {
|
|
48
|
+
code: 'asset-runtime-disposed',
|
|
49
|
+
expected: 'an active asset runtime scope',
|
|
50
|
+
hint: 'obtain a new Registry from the current realm',
|
|
51
|
+
detail: { scopeId },
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function superseded(guid: string, generation: number): AssetLoadError {
|
|
56
|
+
return {
|
|
57
|
+
code: 'asset-superseded',
|
|
58
|
+
expected: 'the load ticket to remain current until promotion',
|
|
59
|
+
hint: 'load the current publication after the Catalog change',
|
|
60
|
+
detail: { guid, generation },
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function thrown(guid: string): AssetLoadError {
|
|
65
|
+
return {
|
|
66
|
+
code: 'asset-decode-failed',
|
|
67
|
+
expected: 'the graph reader to return a Result',
|
|
68
|
+
hint: 'repair the owner reader and retry the current publication',
|
|
69
|
+
detail: { guid, kind: 'asset-graph-reader' },
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function freezeSnapshot(snapshot: AssetGraphSnapshot): AssetGraphSnapshot {
|
|
74
|
+
return Object.freeze({
|
|
75
|
+
...snapshot,
|
|
76
|
+
ready: Object.freeze([...snapshot.ready]),
|
|
77
|
+
sccs: Object.freeze(snapshot.sccs.map((scc) => Object.freeze([...scc]))),
|
|
78
|
+
counters: Object.freeze({ ...snapshot.counters }),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export class AssetGraph<P extends { readonly value: unknown; readonly refs: readonly string[] }> {
|
|
83
|
+
private readonly read: AssetGraphOptions<P>['read'];
|
|
84
|
+
private readonly limit: number;
|
|
85
|
+
private readonly ready = new Map<string, P>();
|
|
86
|
+
private readonly forward = new Map<string, Set<string>>();
|
|
87
|
+
private readonly reverse = new Map<string, Set<string>>();
|
|
88
|
+
private readonly reads = new Map<string, Promise<GraphResult<P>>>();
|
|
89
|
+
private readonly requests = new Map<string, Promise<GraphResult<P>>>();
|
|
90
|
+
private readonly listeners = new Set<GraphListener>();
|
|
91
|
+
private readonly sccs: (readonly string[])[] = [];
|
|
92
|
+
private activeReads = 0;
|
|
93
|
+
private readWaiters: (() => void)[] = [];
|
|
94
|
+
private disposed = false;
|
|
95
|
+
private epoch = 0;
|
|
96
|
+
private counters: AssetGraphCounters = {
|
|
97
|
+
loads: 0,
|
|
98
|
+
cacheHits: 0,
|
|
99
|
+
readErrors: 0,
|
|
100
|
+
noChange: 0,
|
|
101
|
+
listenerFailures: 0,
|
|
102
|
+
};
|
|
103
|
+
private currentSnapshot: AssetGraphSnapshot;
|
|
104
|
+
|
|
105
|
+
constructor(options: AssetGraphOptions<P>) {
|
|
106
|
+
this.read = options.read;
|
|
107
|
+
this.limit = Math.max(1, Math.floor(options.maxConcurrentReads ?? 8));
|
|
108
|
+
this.currentSnapshot = freezeSnapshot({
|
|
109
|
+
epoch: 0,
|
|
110
|
+
ready: [],
|
|
111
|
+
pending: 0,
|
|
112
|
+
resources: 0,
|
|
113
|
+
sccs: [],
|
|
114
|
+
counters: this.counters,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
load(guid: string, signal = new AbortController().signal): Promise<GraphResult<P>> {
|
|
119
|
+
const canonicalGuid = guid.toLowerCase();
|
|
120
|
+
if (this.disposed) return Promise.resolve(err(disposed()));
|
|
121
|
+
if (signal.aborted) return Promise.resolve(err(cancelled(canonicalGuid)));
|
|
122
|
+
const cached = this.ready.get(canonicalGuid);
|
|
123
|
+
if (cached !== undefined) {
|
|
124
|
+
this.counters = {
|
|
125
|
+
...this.counters,
|
|
126
|
+
cacheHits: this.counters.cacheHits + 1,
|
|
127
|
+
noChange: this.counters.noChange + 1,
|
|
128
|
+
};
|
|
129
|
+
this.publish();
|
|
130
|
+
return Promise.resolve(ok(cached));
|
|
131
|
+
}
|
|
132
|
+
const existing = this.requests.get(canonicalGuid);
|
|
133
|
+
if (existing !== undefined) {
|
|
134
|
+
this.counters = { ...this.counters, cacheHits: this.counters.cacheHits + 1 };
|
|
135
|
+
return existing;
|
|
136
|
+
}
|
|
137
|
+
this.counters = { ...this.counters, loads: this.counters.loads + 1 };
|
|
138
|
+
const ticketEpoch = this.epoch;
|
|
139
|
+
const request = this.loadClosure(canonicalGuid, signal, ticketEpoch).finally(() => {
|
|
140
|
+
if (this.requests.get(canonicalGuid) === request) this.requests.delete(canonicalGuid);
|
|
141
|
+
this.publish();
|
|
142
|
+
});
|
|
143
|
+
this.requests.set(canonicalGuid, request);
|
|
144
|
+
this.publish();
|
|
145
|
+
return request;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
invalidate(guid: string): readonly string[] {
|
|
149
|
+
const affected = this.collectAffected([guid.toLowerCase()]);
|
|
150
|
+
this.drop(affected);
|
|
151
|
+
this.epoch += 1;
|
|
152
|
+
this.publish();
|
|
153
|
+
return [...affected].sort();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
invalidateForCatalogChange(guids?: readonly string[]): void {
|
|
157
|
+
if (this.disposed) return;
|
|
158
|
+
const affected =
|
|
159
|
+
guids === undefined || guids.length === 0
|
|
160
|
+
? new Set([...this.ready.keys(), ...this.requests.keys(), ...this.reads.keys()])
|
|
161
|
+
: this.collectAffected(guids.map((item) => item.toLowerCase()));
|
|
162
|
+
this.drop(affected);
|
|
163
|
+
this.epoch += 1;
|
|
164
|
+
this.publish();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
subscribe(listener: GraphListener): () => void {
|
|
168
|
+
this.listeners.add(listener);
|
|
169
|
+
return () => this.listeners.delete(listener);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
snapshot(): AssetGraphSnapshot {
|
|
173
|
+
return this.currentSnapshot;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
lookup(guid: string): P['value'] | undefined {
|
|
177
|
+
return this.ready.get(guid.toLowerCase())?.value;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
guidOf(value: P['value']): string | undefined {
|
|
181
|
+
for (const [guid, entry] of this.ready) {
|
|
182
|
+
if (entry.value === value) return guid;
|
|
183
|
+
}
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
dispose(): void {
|
|
188
|
+
if (this.disposed) return;
|
|
189
|
+
this.disposed = true;
|
|
190
|
+
this.epoch += 1;
|
|
191
|
+
this.ready.clear();
|
|
192
|
+
this.forward.clear();
|
|
193
|
+
this.reverse.clear();
|
|
194
|
+
this.reads.clear();
|
|
195
|
+
const waiters = this.readWaiters;
|
|
196
|
+
this.readWaiters = [];
|
|
197
|
+
for (const resolve of waiters) resolve();
|
|
198
|
+
this.publish();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private async loadClosure(
|
|
202
|
+
root: string,
|
|
203
|
+
signal: AbortSignal,
|
|
204
|
+
ticketEpoch: number,
|
|
205
|
+
): Promise<GraphResult<P>> {
|
|
206
|
+
const values = new Map<string, P>();
|
|
207
|
+
const visiting = new Set<string>();
|
|
208
|
+
const visited = new Set<string>();
|
|
209
|
+
const group = new Set<string>();
|
|
210
|
+
const result = await this.visit(root, signal, visiting, visited, values, group);
|
|
211
|
+
if (!result.ok) return result;
|
|
212
|
+
if (this.disposed) return err(disposed());
|
|
213
|
+
if (signal.aborted) return err(cancelled(root));
|
|
214
|
+
if (ticketEpoch !== this.epoch) return err(superseded(root, ticketEpoch));
|
|
215
|
+
for (const [guid, value] of values) this.promote(guid, value);
|
|
216
|
+
this.recordScc();
|
|
217
|
+
return ok(values.get(root) as P);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
private async visit(
|
|
221
|
+
guid: string,
|
|
222
|
+
signal: AbortSignal,
|
|
223
|
+
visiting: Set<string>,
|
|
224
|
+
visited: Set<string>,
|
|
225
|
+
values: Map<string, P>,
|
|
226
|
+
group: Set<string>,
|
|
227
|
+
): Promise<GraphResult<P>> {
|
|
228
|
+
if (visiting.has(guid)) {
|
|
229
|
+
group.add(guid);
|
|
230
|
+
return ok({ value: undefined as unknown as P, refs: [] } as unknown as P);
|
|
231
|
+
}
|
|
232
|
+
if (visited.has(guid)) return ok(values.get(guid) as P);
|
|
233
|
+
const cached = this.ready.get(guid);
|
|
234
|
+
if (cached !== undefined) {
|
|
235
|
+
values.set(guid, cached);
|
|
236
|
+
visited.add(guid);
|
|
237
|
+
return ok(cached);
|
|
238
|
+
}
|
|
239
|
+
visiting.add(guid);
|
|
240
|
+
const read = await this.readOnce(guid, signal);
|
|
241
|
+
if (!read.ok) {
|
|
242
|
+
visiting.delete(guid);
|
|
243
|
+
return read;
|
|
244
|
+
}
|
|
245
|
+
values.set(guid, read.value);
|
|
246
|
+
group.add(guid);
|
|
247
|
+
this.link(guid, read.value.refs);
|
|
248
|
+
for (const ref of read.value.refs) {
|
|
249
|
+
const child = await this.visit(ref, signal, visiting, visited, values, group);
|
|
250
|
+
if (!child.ok) {
|
|
251
|
+
visiting.delete(guid);
|
|
252
|
+
return err({
|
|
253
|
+
code: 'asset-dependency-failed',
|
|
254
|
+
expected: 'every referenced asset to load successfully',
|
|
255
|
+
hint: 'repair the dependency publication and retry the root asset',
|
|
256
|
+
detail: { guid, dependencyGuid: ref },
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
visiting.delete(guid);
|
|
261
|
+
visited.add(guid);
|
|
262
|
+
return ok(read.value);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
private readOnce(guid: string, signal: AbortSignal): Promise<GraphResult<P>> {
|
|
266
|
+
const existing = this.reads.get(guid);
|
|
267
|
+
if (existing !== undefined) return existing;
|
|
268
|
+
const request = this.withPermit(async () => {
|
|
269
|
+
if (this.disposed) return err(disposed());
|
|
270
|
+
if (signal.aborted) return err(cancelled(guid));
|
|
271
|
+
try {
|
|
272
|
+
const result = await this.read(guid, signal);
|
|
273
|
+
if (signal.aborted) return err(cancelled(guid));
|
|
274
|
+
if (!result.ok)
|
|
275
|
+
this.counters = { ...this.counters, readErrors: this.counters.readErrors + 1 };
|
|
276
|
+
return result;
|
|
277
|
+
} catch {
|
|
278
|
+
this.counters = { ...this.counters, readErrors: this.counters.readErrors + 1 };
|
|
279
|
+
return err(thrown(guid));
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
this.reads.set(guid, request);
|
|
283
|
+
void request.finally(() => {
|
|
284
|
+
if (this.reads.get(guid) === request) this.reads.delete(guid);
|
|
285
|
+
});
|
|
286
|
+
return request;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
private async withPermit<T>(operation: () => Promise<T>): Promise<T> {
|
|
290
|
+
if (this.activeReads >= this.limit) {
|
|
291
|
+
await new Promise<void>((resolve) => this.readWaiters.push(resolve));
|
|
292
|
+
}
|
|
293
|
+
this.activeReads += 1;
|
|
294
|
+
try {
|
|
295
|
+
return await operation();
|
|
296
|
+
} finally {
|
|
297
|
+
this.activeReads -= 1;
|
|
298
|
+
this.readWaiters.shift()?.();
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
private link(guid: string, refs: readonly string[]): void {
|
|
303
|
+
const previous = this.forward.get(guid) ?? new Set<string>();
|
|
304
|
+
for (const ref of previous) this.reverse.get(ref)?.delete(guid);
|
|
305
|
+
const next = new Set(refs.map((ref) => ref.toLowerCase()));
|
|
306
|
+
this.forward.set(guid, next);
|
|
307
|
+
for (const ref of next) {
|
|
308
|
+
const dependents = this.reverse.get(ref) ?? new Set<string>();
|
|
309
|
+
dependents.add(guid);
|
|
310
|
+
this.reverse.set(ref, dependents);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private collectAffected(guids: readonly string[]): Set<string> {
|
|
315
|
+
const affected = new Set<string>();
|
|
316
|
+
const queue = [...guids];
|
|
317
|
+
while (queue.length > 0) {
|
|
318
|
+
const current = queue.shift();
|
|
319
|
+
if (current === undefined || affected.has(current)) continue;
|
|
320
|
+
affected.add(current);
|
|
321
|
+
for (const dependent of this.reverse.get(current) ?? []) queue.push(dependent);
|
|
322
|
+
}
|
|
323
|
+
return affected;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
private drop(affected: ReadonlySet<string>): void {
|
|
327
|
+
for (const item of affected) {
|
|
328
|
+
for (const ref of this.forward.get(item) ?? []) this.reverse.get(ref)?.delete(item);
|
|
329
|
+
for (const dependent of this.reverse.get(item) ?? []) {
|
|
330
|
+
this.forward.get(dependent)?.delete(item);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
for (const item of affected) {
|
|
334
|
+
this.ready.delete(item);
|
|
335
|
+
this.requests.delete(item);
|
|
336
|
+
this.reads.delete(item);
|
|
337
|
+
this.forward.delete(item);
|
|
338
|
+
this.reverse.delete(item);
|
|
339
|
+
}
|
|
340
|
+
this.sccs.splice(0, this.sccs.length);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
private promote(guid: string, value: P): void {
|
|
344
|
+
if (value === undefined) return;
|
|
345
|
+
this.ready.set(
|
|
346
|
+
guid,
|
|
347
|
+
Object.freeze({
|
|
348
|
+
...value,
|
|
349
|
+
value: freezeRuntimePayload(value.value),
|
|
350
|
+
refs: Object.freeze([...value.refs]),
|
|
351
|
+
}) as P,
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
private recordScc(): void {
|
|
356
|
+
const indexByGuid = new Map<string, number>();
|
|
357
|
+
const lowByGuid = new Map<string, number>();
|
|
358
|
+
const stack: string[] = [];
|
|
359
|
+
const onStack = new Set<string>();
|
|
360
|
+
let nextIndex = 0;
|
|
361
|
+
const components: string[][] = [];
|
|
362
|
+
const visit = (guid: string): void => {
|
|
363
|
+
indexByGuid.set(guid, nextIndex);
|
|
364
|
+
lowByGuid.set(guid, nextIndex);
|
|
365
|
+
nextIndex += 1;
|
|
366
|
+
stack.push(guid);
|
|
367
|
+
onStack.add(guid);
|
|
368
|
+
for (const ref of this.forward.get(guid) ?? []) {
|
|
369
|
+
if (!indexByGuid.has(ref)) {
|
|
370
|
+
visit(ref);
|
|
371
|
+
lowByGuid.set(guid, Math.min(lowByGuid.get(guid) ?? 0, lowByGuid.get(ref) ?? 0));
|
|
372
|
+
} else if (onStack.has(ref)) {
|
|
373
|
+
lowByGuid.set(guid, Math.min(lowByGuid.get(guid) ?? 0, indexByGuid.get(ref) ?? 0));
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
if (lowByGuid.get(guid) !== indexByGuid.get(guid)) return;
|
|
377
|
+
const component: string[] = [];
|
|
378
|
+
let member: string | undefined;
|
|
379
|
+
do {
|
|
380
|
+
member = stack.pop();
|
|
381
|
+
if (member === undefined) break;
|
|
382
|
+
onStack.delete(member);
|
|
383
|
+
component.push(member);
|
|
384
|
+
} while (member !== guid);
|
|
385
|
+
if (component.length > 1 || this.forward.get(guid)?.has(guid) === true)
|
|
386
|
+
components.push(component.sort());
|
|
387
|
+
};
|
|
388
|
+
for (const guid of this.forward.keys()) if (!indexByGuid.has(guid)) visit(guid);
|
|
389
|
+
this.sccs.splice(
|
|
390
|
+
0,
|
|
391
|
+
this.sccs.length,
|
|
392
|
+
...components.map((component) => Object.freeze(component)),
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
private publish(): void {
|
|
397
|
+
this.currentSnapshot = freezeSnapshot({
|
|
398
|
+
epoch: this.epoch,
|
|
399
|
+
ready: [...this.ready.keys()].sort(),
|
|
400
|
+
pending: this.requests.size + this.reads.size,
|
|
401
|
+
resources: this.ready.size,
|
|
402
|
+
sccs: this.sccs,
|
|
403
|
+
counters: this.counters,
|
|
404
|
+
});
|
|
405
|
+
for (const listener of [...this.listeners]) {
|
|
406
|
+
try {
|
|
407
|
+
listener(this.currentSnapshot);
|
|
408
|
+
} catch {
|
|
409
|
+
this.counters = {
|
|
410
|
+
...this.counters,
|
|
411
|
+
listenerFailures: Math.min(1024, this.counters.listenerFailures + 1),
|
|
412
|
+
};
|
|
413
|
+
this.currentSnapshot = freezeSnapshot({
|
|
414
|
+
...this.currentSnapshot,
|
|
415
|
+
counters: this.counters,
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|