@forgeax/engine-assets-runtime 0.1.3 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (313) hide show
  1. package/README.md +442 -175
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/__tests__/aabb.unit.test.d.ts +2 -0
  4. package/dist/__tests__/aabb.unit.test.d.ts.map +1 -0
  5. package/dist/__tests__/artifact-integrity.unit.test.d.ts +2 -0
  6. package/dist/__tests__/artifact-integrity.unit.test.d.ts.map +1 -0
  7. package/dist/__tests__/artifact-io.unit.test.d.ts +2 -0
  8. package/dist/__tests__/artifact-io.unit.test.d.ts.map +1 -0
  9. package/dist/__tests__/asset-evidence-sdk.integration.test.d.ts +2 -0
  10. package/dist/__tests__/asset-evidence-sdk.integration.test.d.ts.map +1 -0
  11. package/dist/__tests__/asset-evidence.unit.test.d.ts +2 -0
  12. package/dist/__tests__/asset-evidence.unit.test.d.ts.map +1 -0
  13. package/dist/__tests__/asset-kind.unit.test.d.ts +2 -0
  14. package/dist/__tests__/asset-kind.unit.test.d.ts.map +1 -0
  15. package/dist/__tests__/asset-registry-catalog-reconcile.integration.test.d.ts +2 -0
  16. package/dist/__tests__/asset-registry-catalog-reconcile.integration.test.d.ts.map +1 -0
  17. package/dist/__tests__/asset-registry-catalog-reconcile.unit.test.d.ts +2 -0
  18. package/dist/__tests__/asset-registry-catalog-reconcile.unit.test.d.ts.map +1 -0
  19. package/dist/__tests__/asset-registry-catalog.unit.test.d.ts +2 -0
  20. package/dist/__tests__/asset-registry-catalog.unit.test.d.ts.map +1 -0
  21. package/dist/__tests__/asset-registry.pack-cache.test.d.ts +2 -0
  22. package/dist/__tests__/asset-registry.pack-cache.test.d.ts.map +1 -0
  23. package/dist/__tests__/asset-registry.unit.test.d.ts +2 -0
  24. package/dist/__tests__/asset-registry.unit.test.d.ts.map +1 -0
  25. package/dist/__tests__/asset-runtime-error-code-owner.test-d.d.ts +2 -0
  26. package/dist/__tests__/asset-runtime-error-code-owner.test-d.d.ts.map +1 -0
  27. package/dist/__tests__/catalog-replica.unit.test.d.ts +2 -0
  28. package/dist/__tests__/catalog-replica.unit.test.d.ts.map +1 -0
  29. package/dist/__tests__/catalog-source-v2.unit.test.d.ts +2 -0
  30. package/dist/__tests__/catalog-source-v2.unit.test.d.ts.map +1 -0
  31. package/dist/__tests__/catalog-source.unit.test.d.ts +2 -0
  32. package/dist/__tests__/catalog-source.unit.test.d.ts.map +1 -0
  33. package/dist/__tests__/catalog-subscription-order.unit.test.d.ts +2 -0
  34. package/dist/__tests__/catalog-subscription-order.unit.test.d.ts.map +1 -0
  35. package/dist/__tests__/catalog-subscription.unit.test.d.ts +2 -0
  36. package/dist/__tests__/catalog-subscription.unit.test.d.ts.map +1 -0
  37. package/dist/__tests__/decode-image-bytes-pass-through.test.d.ts +2 -0
  38. package/dist/__tests__/decode-image-bytes-pass-through.test.d.ts.map +1 -0
  39. package/dist/__tests__/decode-image-bytes.browser.test.d.ts +2 -0
  40. package/dist/__tests__/decode-image-bytes.browser.test.d.ts.map +1 -0
  41. package/dist/__tests__/decode-image-bytes.test-d.d.ts +2 -0
  42. package/dist/__tests__/decode-image-bytes.test-d.d.ts.map +1 -0
  43. package/dist/__tests__/decode-image-bytes.test.d.ts +2 -0
  44. package/dist/__tests__/decode-image-bytes.test.d.ts.map +1 -0
  45. package/dist/__tests__/dynamic-texture-store.unit.test.d.ts +2 -0
  46. package/dist/__tests__/dynamic-texture-store.unit.test.d.ts.map +1 -0
  47. package/dist/__tests__/errors-asset.unit.test.d.ts +2 -0
  48. package/dist/__tests__/errors-asset.unit.test.d.ts.map +1 -0
  49. package/dist/__tests__/inline-pack-loaders.unit.test.d.ts +2 -0
  50. package/dist/__tests__/inline-pack-loaders.unit.test.d.ts.map +1 -0
  51. package/dist/__tests__/instantiate-context.unit.test.d.ts +2 -0
  52. package/dist/__tests__/instantiate-context.unit.test.d.ts.map +1 -0
  53. package/dist/__tests__/instantiate-publication-fence.unit.test.d.ts +2 -0
  54. package/dist/__tests__/instantiate-publication-fence.unit.test.d.ts.map +1 -0
  55. package/dist/__tests__/instantiate.unit.test.d.ts +2 -0
  56. package/dist/__tests__/instantiate.unit.test.d.ts.map +1 -0
  57. package/dist/__tests__/loader-input-contract.unit.test.d.ts +2 -0
  58. package/dist/__tests__/loader-input-contract.unit.test.d.ts.map +1 -0
  59. package/dist/__tests__/loader-registry.unit.test.d.ts +2 -0
  60. package/dist/__tests__/loader-registry.unit.test.d.ts.map +1 -0
  61. package/dist/__tests__/material-dependency-invalidation.unit.test.d.ts +2 -0
  62. package/dist/__tests__/material-dependency-invalidation.unit.test.d.ts.map +1 -0
  63. package/dist/__tests__/material-generation-cache.unit.test.d.ts +2 -0
  64. package/dist/__tests__/material-generation-cache.unit.test.d.ts.map +1 -0
  65. package/dist/__tests__/material-load-consumer.test-d.d.ts +2 -0
  66. package/dist/__tests__/material-load-consumer.test-d.d.ts.map +1 -0
  67. package/dist/__tests__/material-ready.integration.test.d.ts +2 -0
  68. package/dist/__tests__/material-ready.integration.test.d.ts.map +1 -0
  69. package/dist/__tests__/material-stale-generation.unit.test.d.ts +2 -0
  70. package/dist/__tests__/material-stale-generation.unit.test.d.ts.map +1 -0
  71. package/dist/__tests__/mesh-bin-morph-v3.test.d.ts +2 -0
  72. package/dist/__tests__/mesh-bin-morph-v3.test.d.ts.map +1 -0
  73. package/dist/__tests__/mesh-bin.unit.test.d.ts +2 -0
  74. package/dist/__tests__/mesh-bin.unit.test.d.ts.map +1 -0
  75. package/dist/__tests__/mipmap-generator.unit.test.d.ts +2 -0
  76. package/dist/__tests__/mipmap-generator.unit.test.d.ts.map +1 -0
  77. package/dist/__tests__/pack-artifact-transcode-model-owner.test.d.ts +2 -0
  78. package/dist/__tests__/pack-artifact-transcode-model-owner.test.d.ts.map +1 -0
  79. package/dist/__tests__/pack-basis-load.integration.test.d.ts +2 -0
  80. package/dist/__tests__/pack-basis-load.integration.test.d.ts.map +1 -0
  81. package/dist/__tests__/payload-validate.unit.test.d.ts +2 -0
  82. package/dist/__tests__/payload-validate.unit.test.d.ts.map +1 -0
  83. package/dist/__tests__/plugin.test.d.ts +2 -0
  84. package/dist/__tests__/plugin.test.d.ts.map +1 -0
  85. package/dist/__tests__/registry-concurrency.bench.test.d.ts +2 -0
  86. package/dist/__tests__/registry-concurrency.bench.test.d.ts.map +1 -0
  87. package/dist/__tests__/registry-failure-retry.integration.test.d.ts +2 -0
  88. package/dist/__tests__/registry-failure-retry.integration.test.d.ts.map +1 -0
  89. package/dist/__tests__/registry-load-graph.integration.test.d.ts +2 -0
  90. package/dist/__tests__/registry-load-graph.integration.test.d.ts.map +1 -0
  91. package/dist/__tests__/registry-load-state.unit.test.d.ts +2 -0
  92. package/dist/__tests__/registry-load-state.unit.test.d.ts.map +1 -0
  93. package/dist/__tests__/registry-package-cache.unit.test.d.ts +2 -0
  94. package/dist/__tests__/registry-package-cache.unit.test.d.ts.map +1 -0
  95. package/dist/__tests__/registry-public-ready.unit.test.d.ts +2 -0
  96. package/dist/__tests__/registry-public-ready.unit.test.d.ts.map +1 -0
  97. package/dist/__tests__/resolve-asset-handle.unit.test.d.ts +2 -0
  98. package/dist/__tests__/resolve-asset-handle.unit.test.d.ts.map +1 -0
  99. package/dist/__tests__/scene-handle-fields.unit.test.d.ts +2 -0
  100. package/dist/__tests__/scene-handle-fields.unit.test.d.ts.map +1 -0
  101. package/dist/__tests__/scene-payload.unit.test.d.ts +2 -0
  102. package/dist/__tests__/scene-payload.unit.test.d.ts.map +1 -0
  103. package/dist/__tests__/scene-publication-fence.test.d.ts +2 -0
  104. package/dist/__tests__/scene-publication-fence.test.d.ts.map +1 -0
  105. package/dist/__tests__/scriptable-pack-public-surface.test-d.d.ts +2 -0
  106. package/dist/__tests__/scriptable-pack-public-surface.test-d.d.ts.map +1 -0
  107. package/dist/__tests__/static-rig.integration.test.d.ts +2 -0
  108. package/dist/__tests__/static-rig.integration.test.d.ts.map +1 -0
  109. package/dist/__tests__/validate-material.unit.test.d.ts +2 -0
  110. package/dist/__tests__/validate-material.unit.test.d.ts.map +1 -0
  111. package/dist/__tests__/video-pack-loader.integration.test.d.ts +2 -0
  112. package/dist/__tests__/video-pack-loader.integration.test.d.ts.map +1 -0
  113. package/dist/aabb.d.ts +3 -0
  114. package/dist/aabb.d.ts.map +1 -0
  115. package/dist/asset-registry.d.ts +552 -0
  116. package/dist/asset-registry.d.ts.map +1 -0
  117. package/dist/builtin-asset-registry.d.ts +22 -0
  118. package/dist/builtin-asset-registry.d.ts.map +1 -0
  119. package/dist/catalog-source.d.ts +2 -1
  120. package/dist/catalog-source.d.ts.map +1 -1
  121. package/dist/decode-image-bytes.d.ts +57 -0
  122. package/dist/decode-image-bytes.d.ts.map +1 -0
  123. package/dist/dynamic-texture-store.d.ts +89 -0
  124. package/dist/dynamic-texture-store.d.ts.map +1 -0
  125. package/dist/errors/asset.d.ts +201 -0
  126. package/dist/errors/asset.d.ts.map +1 -0
  127. package/dist/handles.d.ts +141 -0
  128. package/dist/handles.d.ts.map +1 -0
  129. package/dist/image-error.d.ts +6 -0
  130. package/dist/image-error.d.ts.map +1 -0
  131. package/dist/index.d.ts +32 -3
  132. package/dist/index.d.ts.map +1 -1
  133. package/dist/index.mjs +7218 -114
  134. package/dist/index.mjs.map +1 -1
  135. package/dist/internal.d.ts +2 -1
  136. package/dist/internal.d.ts.map +1 -1
  137. package/dist/internal.mjs +317 -3
  138. package/dist/internal.mjs.map +1 -1
  139. package/dist/loader-registry.d.ts +63 -0
  140. package/dist/loader-registry.d.ts.map +1 -0
  141. package/dist/loaders/inline-pack.d.ts +72 -0
  142. package/dist/loaders/inline-pack.d.ts.map +1 -0
  143. package/dist/loaders/mesh-bin.d.ts +21 -0
  144. package/dist/loaders/mesh-bin.d.ts.map +1 -0
  145. package/dist/loaders/pack-artifact.d.ts +20 -0
  146. package/dist/loaders/pack-artifact.d.ts.map +1 -0
  147. package/dist/material/dependency-graph.d.ts +9 -0
  148. package/dist/material/dependency-graph.d.ts.map +1 -0
  149. package/dist/material/generation-cache.d.ts +24 -0
  150. package/dist/material/generation-cache.d.ts.map +1 -0
  151. package/dist/material/inspection.d.ts +24 -0
  152. package/dist/material/inspection.d.ts.map +1 -0
  153. package/dist/material/loader.d.ts +59 -0
  154. package/dist/material/loader.d.ts.map +1 -0
  155. package/dist/material/runtime-shader.d.ts +14 -0
  156. package/dist/material/runtime-shader.d.ts.map +1 -0
  157. package/dist/mesh-bin.d.ts +3 -0
  158. package/dist/mesh-bin.d.ts.map +1 -0
  159. package/dist/mipmap-generator.d.ts +132 -0
  160. package/dist/mipmap-generator.d.ts.map +1 -0
  161. package/dist/payload-validate.d.ts +60 -0
  162. package/dist/payload-validate.d.ts.map +1 -0
  163. package/dist/plugin.d.ts +16 -0
  164. package/dist/plugin.d.ts.map +1 -0
  165. package/dist/registry/artifact-io.d.ts +16 -0
  166. package/dist/registry/artifact-io.d.ts.map +1 -0
  167. package/dist/registry/asset-evidence.d.ts +13 -0
  168. package/dist/registry/asset-evidence.d.ts.map +1 -0
  169. package/dist/registry/catalog-state.d.ts +47 -0
  170. package/dist/registry/catalog-state.d.ts.map +1 -0
  171. package/dist/registry/catalog.d.ts +48 -0
  172. package/dist/registry/catalog.d.ts.map +1 -0
  173. package/dist/registry/instantiate.d.ts +153 -0
  174. package/dist/registry/instantiate.d.ts.map +1 -0
  175. package/dist/registry/load-by-guid.d.ts +247 -0
  176. package/dist/registry/load-by-guid.d.ts.map +1 -0
  177. package/dist/registry/load-state.d.ts +26 -0
  178. package/dist/registry/load-state.d.ts.map +1 -0
  179. package/dist/registry/runtime-projection.d.ts +16 -0
  180. package/dist/registry/runtime-projection.d.ts.map +1 -0
  181. package/dist/registry/scene-publication-fence.d.ts +38 -0
  182. package/dist/registry/scene-publication-fence.d.ts.map +1 -0
  183. package/dist/registry/validate-material.d.ts +77 -0
  184. package/dist/registry/validate-material.d.ts.map +1 -0
  185. package/dist/resolve-asset-handle.d.ts +43 -0
  186. package/dist/resolve-asset-handle.d.ts.map +1 -0
  187. package/dist/resolve-tileset-runtime.d.ts +23 -0
  188. package/dist/resolve-tileset-runtime.d.ts.map +1 -0
  189. package/dist/scene-handle-fields.d.ts +88 -0
  190. package/dist/scene-handle-fields.d.ts.map +1 -0
  191. package/dist/scene-payload.d.ts +15 -0
  192. package/dist/scene-payload.d.ts.map +1 -0
  193. package/dist/wire-default-loaders.d.ts +29 -0
  194. package/dist/wire-default-loaders.d.ts.map +1 -0
  195. package/package.json +22 -9
  196. package/src/__tests__/aabb.unit.test.ts +97 -0
  197. package/src/__tests__/artifact-integrity.unit.test.ts +99 -0
  198. package/src/__tests__/artifact-io.unit.test.ts +127 -0
  199. package/src/__tests__/asset-evidence-sdk.integration.test.ts +32 -0
  200. package/src/__tests__/asset-evidence.unit.test.ts +32 -0
  201. package/src/__tests__/{asset-kind.test.ts → asset-kind.unit.test.ts} +1 -1
  202. package/src/__tests__/asset-registry-catalog-reconcile.integration.test.ts +74 -0
  203. package/src/__tests__/asset-registry-catalog-reconcile.unit.test.ts +89 -0
  204. package/src/__tests__/asset-registry-catalog.unit.test.ts +170 -0
  205. package/src/__tests__/asset-registry.pack-cache.test.ts +96 -0
  206. package/src/__tests__/asset-registry.unit.test.ts +511 -0
  207. package/src/__tests__/asset-runtime-error-code-owner.test-d.ts +63 -0
  208. package/src/__tests__/catalog-replica.unit.test.ts +196 -0
  209. package/src/__tests__/catalog-source-v2.unit.test.ts +158 -0
  210. package/src/__tests__/catalog-source.unit.test.ts +126 -0
  211. package/src/__tests__/catalog-subscription-order.unit.test.ts +65 -0
  212. package/src/__tests__/catalog-subscription.unit.test.ts +28 -0
  213. package/src/__tests__/decode-image-bytes-pass-through.test.ts +34 -0
  214. package/src/__tests__/decode-image-bytes.browser.test.ts +102 -0
  215. package/src/__tests__/decode-image-bytes.test-d.ts +108 -0
  216. package/src/__tests__/decode-image-bytes.test.ts +90 -0
  217. package/src/__tests__/decode-image-mime-owner.test.ts +2 -5
  218. package/src/__tests__/dynamic-texture-store.unit.test.ts +115 -0
  219. package/src/__tests__/errors-asset.unit.test.ts +92 -0
  220. package/src/__tests__/inline-pack-loaders.unit.test.ts +548 -0
  221. package/src/__tests__/instantiate-context.unit.test.ts +65 -0
  222. package/src/__tests__/instantiate-publication-fence.unit.test.ts +158 -0
  223. package/src/__tests__/instantiate.unit.test.ts +684 -0
  224. package/src/__tests__/loader-input-contract.unit.test.ts +89 -0
  225. package/src/__tests__/loader-registry.unit.test.ts +101 -0
  226. package/src/__tests__/material-dependency-invalidation.unit.test.ts +14 -0
  227. package/src/__tests__/material-generation-cache.unit.test.ts +46 -0
  228. package/src/__tests__/material-load-consumer.test-d.ts +42 -0
  229. package/src/__tests__/material-ready.integration.test.ts +492 -0
  230. package/src/__tests__/material-stale-generation.unit.test.ts +152 -0
  231. package/src/__tests__/mesh-bin-morph-v3.test.ts +22 -0
  232. package/src/__tests__/mesh-bin.unit.test.ts +152 -0
  233. package/src/__tests__/mipmap-generator.unit.test.ts +202 -0
  234. package/src/__tests__/pack-artifact-transcode-model-owner.test.ts +32 -0
  235. package/src/__tests__/pack-basis-load.integration.test.ts +168 -0
  236. package/src/__tests__/payload-validate.unit.test.ts +285 -0
  237. package/src/__tests__/plugin.test.ts +55 -0
  238. package/src/__tests__/registry-concurrency.bench.test.ts +40 -0
  239. package/src/__tests__/registry-failure-retry.integration.test.ts +69 -0
  240. package/src/__tests__/registry-load-graph.integration.test.ts +246 -0
  241. package/src/__tests__/registry-load-state.unit.test.ts +35 -0
  242. package/src/__tests__/registry-package-cache.unit.test.ts +53 -0
  243. package/src/__tests__/registry-public-ready.unit.test.ts +38 -0
  244. package/src/__tests__/resolve-asset-handle.unit.test.ts +238 -0
  245. package/src/__tests__/scene-handle-fields.unit.test.ts +129 -0
  246. package/src/__tests__/scene-payload.unit.test.ts +151 -0
  247. package/src/__tests__/scene-publication-fence.test.ts +129 -0
  248. package/src/__tests__/scriptable-pack-public-surface.test-d.ts +16 -0
  249. package/src/__tests__/static-rig.integration.test.ts +8 -0
  250. package/src/__tests__/validate-material.unit.test.ts +359 -0
  251. package/src/__tests__/video-pack-loader.integration.test.ts +83 -0
  252. package/src/aabb.ts +107 -0
  253. package/src/asset-registry.ts +2040 -0
  254. package/src/builtin-asset-registry.ts +164 -0
  255. package/src/catalog-source.ts +14 -70
  256. package/src/decode-image-bytes.ts +126 -0
  257. package/src/dynamic-texture-store.ts +234 -0
  258. package/src/errors/asset.ts +347 -0
  259. package/src/handles.ts +180 -0
  260. package/src/image-error.ts +45 -0
  261. package/src/index.ts +177 -19
  262. package/src/internal.ts +2 -0
  263. package/src/loader-registry.ts +157 -0
  264. package/src/loaders/inline-pack.ts +837 -0
  265. package/src/loaders/mesh-bin.ts +304 -0
  266. package/src/loaders/pack-artifact.ts +665 -0
  267. package/src/material/dependency-graph.ts +46 -0
  268. package/src/material/generation-cache.ts +177 -0
  269. package/src/material/inspection.ts +46 -0
  270. package/src/material/loader.ts +326 -0
  271. package/src/material/runtime-shader.ts +133 -0
  272. package/src/mesh-bin.ts +5 -0
  273. package/src/mipmap-generator.ts +450 -0
  274. package/src/payload-validate.ts +573 -0
  275. package/src/plugin.ts +48 -0
  276. package/src/registry/artifact-io.ts +259 -0
  277. package/src/registry/asset-evidence.ts +51 -0
  278. package/src/registry/catalog-state.ts +409 -0
  279. package/src/registry/catalog.ts +369 -0
  280. package/src/registry/instantiate.ts +1056 -0
  281. package/src/registry/load-by-guid.ts +1657 -0
  282. package/src/registry/load-state.ts +115 -0
  283. package/src/registry/runtime-projection.ts +63 -0
  284. package/src/registry/scene-publication-fence.ts +256 -0
  285. package/src/registry/validate-material.ts +386 -0
  286. package/src/resolve-asset-handle.ts +147 -0
  287. package/src/resolve-tileset-runtime.ts +72 -0
  288. package/src/scene-handle-fields.ts +221 -0
  289. package/src/scene-payload.ts +207 -0
  290. package/src/wire-default-loaders.ts +79 -0
  291. package/dist/__tests__/asset-graph-red.integration.test.d.ts +0 -2
  292. package/dist/__tests__/asset-graph-red.integration.test.d.ts.map +0 -1
  293. package/dist/__tests__/asset-kind.test.d.ts +0 -2
  294. package/dist/__tests__/asset-kind.test.d.ts.map +0 -1
  295. package/dist/__tests__/asset-registry-core.integration.test.d.ts +0 -2
  296. package/dist/__tests__/asset-registry-core.integration.test.d.ts.map +0 -1
  297. package/dist/__tests__/asset-registry-public-api.test-d.d.ts +0 -2
  298. package/dist/__tests__/asset-registry-public-api.test-d.d.ts.map +0 -1
  299. package/dist/__tests__/asset-runtime-core-lifecycle.integration.test.d.ts +0 -2
  300. package/dist/__tests__/asset-runtime-core-lifecycle.integration.test.d.ts.map +0 -1
  301. package/dist/__tests__/asset-runtime-snapshot.unit.test.d.ts +0 -2
  302. package/dist/__tests__/asset-runtime-snapshot.unit.test.d.ts.map +0 -1
  303. package/dist/__tests__/catalog-session-red.unit.test.d.ts +0 -2
  304. package/dist/__tests__/catalog-session-red.unit.test.d.ts.map +0 -1
  305. package/dist/__tests__/registry-lifecycle-red.integration.test.d.ts +0 -2
  306. package/dist/__tests__/registry-lifecycle-red.integration.test.d.ts.map +0 -1
  307. package/src/__tests__/asset-graph-red.integration.test.ts +0 -113
  308. package/src/__tests__/asset-registry-core.integration.test.ts +0 -161
  309. package/src/__tests__/asset-registry-public-api.test-d.ts +0 -31
  310. package/src/__tests__/asset-runtime-core-lifecycle.integration.test.ts +0 -79
  311. package/src/__tests__/asset-runtime-snapshot.unit.test.ts +0 -23
  312. package/src/__tests__/catalog-session-red.unit.test.ts +0 -276
  313. package/src/__tests__/registry-lifecycle-red.integration.test.ts +0 -80
@@ -0,0 +1,74 @@
1
+ import { err, ok } from '@forgeax/engine-rhi';
2
+ import { AssetError, type CatalogDelta, type CatalogEntry } from '@forgeax/engine-types';
3
+ import { describe, expect, it, vi } from 'vitest';
4
+ import { AssetRegistry, type CatalogReconcileResult } from '../index.js';
5
+
6
+ const baselineEntry = {
7
+ guid: '22222222-2222-4222-8222-222222222222',
8
+ kind: 'mesh',
9
+ sourcePath: 'failed.glb',
10
+ packageUrl: '/preview/packages/failed',
11
+ revision: { digest: 'sha256:baseline', observedAt: 3, rootId: 'root-failed' },
12
+ } as const satisfies CatalogEntry;
13
+
14
+ function registry(): AssetRegistry {
15
+ return new AssetRegistry({} as never);
16
+ }
17
+
18
+ describe('AssetRegistry.reconcileCatalog recovery integration', () => {
19
+ it('preserves stale gap diagnostics and the source error without silent refresh', async () => {
20
+ let listener: ((delta: CatalogDelta) => void) | undefined;
21
+ const failure = new AssetError({
22
+ code: 'asset-fetch-failed',
23
+ expected: 'an authoritative catalog enumeration',
24
+ hint: 'restore the catalog source and reconcile again',
25
+ });
26
+ const enumerate = vi
27
+ .fn()
28
+ .mockResolvedValueOnce(ok([baselineEntry]))
29
+ .mockResolvedValueOnce(err(failure));
30
+ const writeMeta = vi.fn();
31
+ const writeSourceBytes = vi.fn();
32
+ const assets = registry();
33
+ const source = {
34
+ enumerate,
35
+ subscribe(next: (delta: CatalogDelta) => void) {
36
+ listener = next;
37
+ return () => {
38
+ listener = undefined;
39
+ };
40
+ },
41
+ writeMeta,
42
+ writeSourceBytes,
43
+ };
44
+ assets.setCatalogSource(source);
45
+ await assets.enumerateCatalog();
46
+ listener?.({
47
+ added: [],
48
+ changed: [],
49
+ removed: [],
50
+ authority: 'degraded',
51
+ diagnostics: [
52
+ {
53
+ code: 'catalog-gap',
54
+ severity: 'blocking',
55
+ expected: 'contiguous producer revisions',
56
+ hint: 'reconcile',
57
+ },
58
+ ],
59
+ });
60
+ const refreshCatalog = vi.spyOn(assets, 'refreshCatalog');
61
+
62
+ const result: CatalogReconcileResult = await assets.reconcileCatalog();
63
+
64
+ expect(result).toEqual({ ok: false, error: failure });
65
+ expect(assets.catalogSnapshot()).toMatchObject({
66
+ stale: true,
67
+ diagnostics: [{ code: 'catalog-gap', severity: 'blocking' }],
68
+ entries: [{ guid: baselineEntry.guid, revision: baselineEntry.revision }],
69
+ });
70
+ expect(refreshCatalog).not.toHaveBeenCalled();
71
+ expect(writeMeta).not.toHaveBeenCalled();
72
+ expect(writeSourceBytes).not.toHaveBeenCalled();
73
+ });
74
+ });
@@ -0,0 +1,89 @@
1
+ import { ok } from '@forgeax/engine-rhi';
2
+ import { ASSET_ERROR_HINTS, type CatalogDelta, type CatalogEntry } from '@forgeax/engine-types';
3
+ import { describe, expect, it, vi } from 'vitest';
4
+ import {
5
+ AssetRegistry,
6
+ type CatalogReconcileResult,
7
+ type CatalogReplicaSnapshot,
8
+ } from '../index.js';
9
+
10
+ const currentEntry = {
11
+ guid: '11111111-1111-4111-8111-111111111111',
12
+ kind: 'mesh',
13
+ sourcePath: 'model.glb',
14
+ packageUrl: '/preview/packages/model',
15
+ revision: { digest: 'sha256:current', observedAt: 7, rootId: 'root-model' },
16
+ diagnostics: [{ code: 'catalog-current', severity: 'info', hint: 'current revision' }],
17
+ } as const satisfies CatalogEntry;
18
+
19
+ function registry(): AssetRegistry {
20
+ return new AssetRegistry({} as never);
21
+ }
22
+
23
+ describe('AssetRegistry.reconcileCatalog public contract', () => {
24
+ it('returns the structured unconfigured Result through the package surface', async () => {
25
+ const assets = registry();
26
+
27
+ const result: CatalogReconcileResult = await assets.reconcileCatalog();
28
+
29
+ expect(result.ok).toBe(false);
30
+ if (result.ok) return;
31
+ expect(result.error).toMatchObject({
32
+ code: 'catalog-source-unconfigured',
33
+ expected: 'a configured catalog source',
34
+ });
35
+ expect(result.error.hint).toBe(ASSET_ERROR_HINTS['catalog-source-unconfigured']);
36
+ });
37
+
38
+ it('returns the same immutable revision snapshot without authoring writes', async () => {
39
+ let listener: ((delta: CatalogDelta) => void) | undefined;
40
+ const writeMeta = vi.fn();
41
+ const writeSourceBytes = vi.fn();
42
+ const sourceBytes = new Uint8Array([1, 2, 3, 4]);
43
+ const originalSourceBytes = sourceBytes.slice();
44
+ const source = {
45
+ enumerate: vi.fn(async () => ok([currentEntry])),
46
+ subscribe(next: (delta: CatalogDelta) => void) {
47
+ listener = next;
48
+ return () => {
49
+ listener = undefined;
50
+ };
51
+ },
52
+ writeMeta,
53
+ writeSourceBytes,
54
+ };
55
+ const assets = registry();
56
+ assets.setCatalogSource(source);
57
+ await assets.enumerateCatalog();
58
+ listener?.({
59
+ added: [],
60
+ changed: [],
61
+ removed: [],
62
+ authority: 'degraded',
63
+ diagnostics: [{ code: 'catalog-gap', severity: 'blocking', hint: 'reconcile' }],
64
+ });
65
+
66
+ const result: CatalogReconcileResult = await assets.reconcileCatalog();
67
+
68
+ expect(result.ok).toBe(true);
69
+ if (!result.ok) return;
70
+ const snapshot: CatalogReplicaSnapshot = result.value;
71
+ expect(snapshot).toBe(assets.catalogSnapshot());
72
+ expect(snapshot).toMatchObject({
73
+ stale: false,
74
+ diagnostics: [],
75
+ entries: [
76
+ {
77
+ guid: currentEntry.guid,
78
+ revision: currentEntry.revision,
79
+ diagnostics: currentEntry.diagnostics,
80
+ },
81
+ ],
82
+ });
83
+ expect(Object.isFrozen(snapshot)).toBe(true);
84
+ expect(Object.isFrozen(snapshot.entries)).toBe(true);
85
+ expect(writeMeta).not.toHaveBeenCalled();
86
+ expect(writeSourceBytes).not.toHaveBeenCalled();
87
+ expect(sourceBytes).toEqual(originalSourceBytes);
88
+ });
89
+ });
@@ -0,0 +1,170 @@
1
+ import { ok } from '@forgeax/engine-rhi';
2
+ import type { CatalogDelta } from '@forgeax/engine-types';
3
+ import { describe, expect, it, vi } from 'vitest';
4
+ import { AssetRegistry } from '../asset-registry';
5
+ import { createCatalogSource } from '../catalog-source';
6
+ import { parseCatalog } from '../registry/catalog';
7
+
8
+ function registry(): AssetRegistry {
9
+ return new AssetRegistry({} as never);
10
+ }
11
+
12
+ const packageRow = {
13
+ guid: '11111111-1111-4111-8111-111111111111',
14
+ kind: 'mesh',
15
+ sourcePath: 'model.glb',
16
+ packageUrl: '/preview/packages/model',
17
+ };
18
+
19
+ describe('AssetRegistry.enumerateCatalog', () => {
20
+ it('exposes packageUrl navigation from the configured catalog source', async () => {
21
+ const assets = registry();
22
+ assets.setCatalogSource({
23
+ enumerate: async () => ok([packageRow]),
24
+ subscribe: () => () => {},
25
+ });
26
+
27
+ const result = await assets.enumerateCatalog();
28
+
29
+ expect(result).toEqual({ ok: true, value: [packageRow] });
30
+ });
31
+
32
+ it('shares concurrent reads and retries after a failed source read', async () => {
33
+ const enumerate = vi
34
+ .fn()
35
+ .mockResolvedValueOnce({ ok: false, error: { code: 'asset-fetch-failed' } })
36
+ .mockResolvedValueOnce(ok([]));
37
+ const source = { enumerate, subscribe: () => () => {} };
38
+ const assets = registry();
39
+ assets.setCatalogSource(source);
40
+
41
+ const [first, second] = await Promise.all([
42
+ assets.enumerateCatalog(),
43
+ assets.enumerateCatalog(),
44
+ ]);
45
+ expect(first.ok).toBe(false);
46
+ expect(second.ok).toBe(false);
47
+ expect(enumerate).toHaveBeenCalledTimes(1);
48
+ expect((await assets.enumerateCatalog()).ok).toBe(true);
49
+ expect(enumerate).toHaveBeenCalledTimes(2);
50
+ });
51
+
52
+ it('returns the complete neutral entry from a catalog source', async () => {
53
+ const entry = {
54
+ guid: '11111111-1111-4111-8111-111111111111',
55
+ kind: 'host/blob',
56
+ packageUrl: '/assets/blob.bin',
57
+ sourcePath: 'assets/blob.source',
58
+ packageId: 'pkg/registry',
59
+ provenance: { provider: 'registry-fixture', version: '1.0.0' },
60
+ revision: { digest: 'sha256:registry', observedAt: 5, rootId: 'root-registry' },
61
+ sourceKey: 'blob/main',
62
+ sourceIndex: 0,
63
+ relations: [],
64
+ diagnostics: [{ code: 'registry-note', severity: 'info', hint: 'no action' }],
65
+ } as const;
66
+ const assets = registry();
67
+ assets.setCatalogSource(createCatalogSource({ entries: [entry] }));
68
+
69
+ const result = await assets.enumerateCatalog();
70
+
71
+ expect(result).toEqual({ ok: true, value: [entry] });
72
+ });
73
+
74
+ it('clears a catalog source and unsubscribes its producer transport', async () => {
75
+ const listeners = new Set<(delta: CatalogDelta) => void>();
76
+ const unsubscribe = vi.fn(() => listeners.clear());
77
+ const source = {
78
+ enumerate: async () => ok([packageRow]),
79
+ subscribe: (listener: (delta: CatalogDelta) => void) => {
80
+ listeners.add(listener);
81
+ return unsubscribe;
82
+ },
83
+ };
84
+ const assets = registry();
85
+ const received = vi.fn();
86
+ assets.setCatalogSource(source);
87
+ assets.subscribeCatalog(received);
88
+ await assets.enumerateCatalog();
89
+
90
+ assets.clearCatalogSource();
91
+ assets.clearCatalogSource();
92
+
93
+ expect(unsubscribe).toHaveBeenCalledTimes(1);
94
+ expect((await assets.enumerateCatalog()).ok).toBe(false);
95
+ for (const listener of listeners) listener({ added: [], changed: [], removed: [] });
96
+ expect(received).not.toHaveBeenCalled();
97
+ });
98
+
99
+ it('preserves the explicit runtime projection instead of inferring it from locators', () => {
100
+ const guid = '33333333-3333-4333-8333-333333333333';
101
+ const raw = [
102
+ {
103
+ guid,
104
+ packageUrl: '/opaque/runtime/asset.ddc',
105
+ kind: 'particle-effect',
106
+ sourcePath: 'authoring/boss.pack.json',
107
+ subject: 'internal-asset',
108
+ execution: 'cooked',
109
+ lifecycle: 'current',
110
+ projection: {
111
+ subject: 'internal-asset',
112
+ execution: 'cooked',
113
+ lifecycle: 'current',
114
+ operations: {
115
+ preview: { operation: 'preview', enabled: true },
116
+ save: { operation: 'save', enabled: false, reason: 'cooked projection' },
117
+ rebuild: { operation: 'rebuild', enabled: true },
118
+ sourceOverride: { operation: 'sourceOverride', enabled: false },
119
+ instanceOverride: { operation: 'instanceOverride', enabled: false },
120
+ promote: { operation: 'promote', enabled: false },
121
+ },
122
+ },
123
+ },
124
+ ];
125
+
126
+ const result = parseCatalog(raw);
127
+
128
+ expect(result.ok).toBe(true);
129
+ if (!result.ok) return;
130
+ expect(result.value.get(guid)?.subject).toBe('internal-asset');
131
+ expect(result.value.get(guid)?.execution).toBe('cooked');
132
+ expect(result.value.get(guid)?.lifecycle).toBe('current');
133
+ expect(result.value.get(guid)?.projection).toEqual(raw[0]?.projection);
134
+ });
135
+
136
+ it('keeps non-current cooked states explicit even when the locator looks runnable', () => {
137
+ const guid = '44444444-4444-4444-8444-444444444444';
138
+ const result = parseCatalog([
139
+ {
140
+ guid,
141
+ packageUrl: '/opaque/runtime/asset.pack.json',
142
+ kind: 'scene',
143
+ sourcePath: 'authoring/scene.pack.json',
144
+ subject: 'internal-asset',
145
+ execution: 'cooked',
146
+ lifecycle: 'stale',
147
+ projection: {
148
+ subject: 'internal-asset',
149
+ execution: 'cooked',
150
+ lifecycle: 'stale',
151
+ operations: {
152
+ preview: { operation: 'preview', enabled: true },
153
+ save: { operation: 'save', enabled: false },
154
+ rebuild: { operation: 'rebuild', enabled: true },
155
+ sourceOverride: { operation: 'sourceOverride', enabled: false },
156
+ instanceOverride: { operation: 'instanceOverride', enabled: false },
157
+ promote: { operation: 'promote', enabled: false },
158
+ },
159
+ lastKnownGood: { packageUrl: '/opaque/runtime/old.pack.json' },
160
+ },
161
+ },
162
+ ]);
163
+
164
+ expect(result.ok).toBe(true);
165
+ if (!result.ok) return;
166
+ const row = result.value.get(guid);
167
+ expect(row?.lifecycle).toBe('stale');
168
+ expect(row?.projection?.lastKnownGood?.packageUrl).toBe('/opaque/runtime/old.pack.json');
169
+ });
170
+ });
@@ -0,0 +1,96 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { AssetRegistry } from '../asset-registry';
3
+
4
+ const GUID = 'cccccccc-cccc-4ccc-8ccc-cccccccccccc';
5
+
6
+ describe('asset registry cache cleanup', () => {
7
+ it('clears failed package and in-flight state so a retry can succeed', async () => {
8
+ const registry = new AssetRegistry({} as never);
9
+ registry.loaders.register({
10
+ kind: 'host-blob',
11
+ load: (payload) => ({ kind: 'host-blob', ...payload }),
12
+ });
13
+ registry.configurePackIndex('/pack-index.json');
14
+ let attempts = 0;
15
+ vi.stubGlobal(
16
+ 'fetch',
17
+ vi.fn(async (url: string) => {
18
+ if (url.endsWith('pack-index.json')) {
19
+ return new Response(
20
+ JSON.stringify([
21
+ {
22
+ guid: GUID,
23
+ packageUrl: '/asset.pack.json',
24
+ kind: 'host-blob',
25
+ sourcePath: 'asset',
26
+ },
27
+ ]),
28
+ );
29
+ }
30
+ attempts += 1;
31
+ if (attempts === 1) return new Response('missing', { status: 404 });
32
+ return new Response(
33
+ JSON.stringify({
34
+ schemaVersion: '2.0.0',
35
+ kind: 'internal-text-package',
36
+ assets: [
37
+ { guid: GUID, kind: 'host-blob', payload: { value: 1 }, refs: [], artifacts: {} },
38
+ ],
39
+ }),
40
+ );
41
+ }),
42
+ );
43
+
44
+ const first = await registry.loadByGuid(registry.parseGuid(GUID));
45
+ expect(first.ok).toBe(false);
46
+ const second = await registry.loadByGuid(registry.parseGuid(GUID));
47
+ expect(second.ok).toBe(true);
48
+ expect(attempts).toBe(2);
49
+ expect(registry.lookup(GUID)).toBeDefined();
50
+ });
51
+
52
+ it('does not expose an unresolved package as a public fast-path hit', async () => {
53
+ const registry = new AssetRegistry({} as never);
54
+ registry.loaders.register({
55
+ kind: 'host-blob',
56
+ load: (payload) => ({ kind: 'host-blob', ...payload }),
57
+ });
58
+ registry.configurePackIndex('/pack-index.json');
59
+ let release: (() => void) | undefined;
60
+ const pending = new Promise<void>((resolve) => {
61
+ release = resolve;
62
+ });
63
+ vi.stubGlobal(
64
+ 'fetch',
65
+ vi.fn(async (url: string) => {
66
+ if (url.endsWith('pack-index.json')) {
67
+ return new Response(
68
+ JSON.stringify([
69
+ {
70
+ guid: GUID,
71
+ packageUrl: '/asset.pack.json',
72
+ kind: 'host-blob',
73
+ sourcePath: 'asset',
74
+ },
75
+ ]),
76
+ );
77
+ }
78
+ await pending;
79
+ return new Response(
80
+ JSON.stringify({
81
+ schemaVersion: '2.0.0',
82
+ kind: 'internal-text-package',
83
+ assets: [
84
+ { guid: GUID, kind: 'host-blob', payload: { value: 1 }, refs: [], artifacts: {} },
85
+ ],
86
+ }),
87
+ );
88
+ }),
89
+ );
90
+
91
+ const loading = registry.loadByGuid(registry.parseGuid(GUID));
92
+ expect(registry.lookup(GUID)).toBeUndefined();
93
+ release?.();
94
+ await loading;
95
+ });
96
+ });