@forgeax/engine-assets-runtime 0.1.4 → 0.1.7

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 (311) hide show
  1. package/README.md +453 -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 +554 -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 +143 -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 +7233 -114
  134. package/dist/index.mjs.map +1 -1
  135. package/dist/internal.d.ts +1 -1
  136. package/dist/internal.d.ts.map +1 -1
  137. package/dist/loader-registry.d.ts +63 -0
  138. package/dist/loader-registry.d.ts.map +1 -0
  139. package/dist/loaders/inline-pack.d.ts +72 -0
  140. package/dist/loaders/inline-pack.d.ts.map +1 -0
  141. package/dist/loaders/mesh-bin.d.ts +21 -0
  142. package/dist/loaders/mesh-bin.d.ts.map +1 -0
  143. package/dist/loaders/pack-artifact.d.ts +20 -0
  144. package/dist/loaders/pack-artifact.d.ts.map +1 -0
  145. package/dist/material/dependency-graph.d.ts +9 -0
  146. package/dist/material/dependency-graph.d.ts.map +1 -0
  147. package/dist/material/generation-cache.d.ts +24 -0
  148. package/dist/material/generation-cache.d.ts.map +1 -0
  149. package/dist/material/inspection.d.ts +24 -0
  150. package/dist/material/inspection.d.ts.map +1 -0
  151. package/dist/material/loader.d.ts +59 -0
  152. package/dist/material/loader.d.ts.map +1 -0
  153. package/dist/material/runtime-shader.d.ts +14 -0
  154. package/dist/material/runtime-shader.d.ts.map +1 -0
  155. package/dist/mesh-bin.d.ts +3 -0
  156. package/dist/mesh-bin.d.ts.map +1 -0
  157. package/dist/mipmap-generator.d.ts +132 -0
  158. package/dist/mipmap-generator.d.ts.map +1 -0
  159. package/dist/payload-validate.d.ts +60 -0
  160. package/dist/payload-validate.d.ts.map +1 -0
  161. package/dist/plugin.d.ts +16 -0
  162. package/dist/plugin.d.ts.map +1 -0
  163. package/dist/registry/artifact-io.d.ts +16 -0
  164. package/dist/registry/artifact-io.d.ts.map +1 -0
  165. package/dist/registry/asset-evidence.d.ts +13 -0
  166. package/dist/registry/asset-evidence.d.ts.map +1 -0
  167. package/dist/registry/catalog-state.d.ts +47 -0
  168. package/dist/registry/catalog-state.d.ts.map +1 -0
  169. package/dist/registry/catalog.d.ts +48 -0
  170. package/dist/registry/catalog.d.ts.map +1 -0
  171. package/dist/registry/instantiate.d.ts +153 -0
  172. package/dist/registry/instantiate.d.ts.map +1 -0
  173. package/dist/registry/load-by-guid.d.ts +247 -0
  174. package/dist/registry/load-by-guid.d.ts.map +1 -0
  175. package/dist/registry/load-state.d.ts +26 -0
  176. package/dist/registry/load-state.d.ts.map +1 -0
  177. package/dist/registry/runtime-projection.d.ts +16 -0
  178. package/dist/registry/runtime-projection.d.ts.map +1 -0
  179. package/dist/registry/scene-publication-fence.d.ts +38 -0
  180. package/dist/registry/scene-publication-fence.d.ts.map +1 -0
  181. package/dist/registry/validate-material.d.ts +77 -0
  182. package/dist/registry/validate-material.d.ts.map +1 -0
  183. package/dist/resolve-asset-handle.d.ts +43 -0
  184. package/dist/resolve-asset-handle.d.ts.map +1 -0
  185. package/dist/resolve-tileset-runtime.d.ts +23 -0
  186. package/dist/resolve-tileset-runtime.d.ts.map +1 -0
  187. package/dist/scene-handle-fields.d.ts +88 -0
  188. package/dist/scene-handle-fields.d.ts.map +1 -0
  189. package/dist/scene-payload.d.ts +15 -0
  190. package/dist/scene-payload.d.ts.map +1 -0
  191. package/dist/wire-default-loaders.d.ts +29 -0
  192. package/dist/wire-default-loaders.d.ts.map +1 -0
  193. package/package.json +22 -9
  194. package/src/__tests__/aabb.unit.test.ts +97 -0
  195. package/src/__tests__/artifact-integrity.unit.test.ts +99 -0
  196. package/src/__tests__/artifact-io.unit.test.ts +127 -0
  197. package/src/__tests__/asset-evidence-sdk.integration.test.ts +32 -0
  198. package/src/__tests__/asset-evidence.unit.test.ts +32 -0
  199. package/src/__tests__/{asset-kind.test.ts → asset-kind.unit.test.ts} +1 -1
  200. package/src/__tests__/asset-registry-catalog-reconcile.integration.test.ts +74 -0
  201. package/src/__tests__/asset-registry-catalog-reconcile.unit.test.ts +89 -0
  202. package/src/__tests__/asset-registry-catalog.unit.test.ts +170 -0
  203. package/src/__tests__/asset-registry.pack-cache.test.ts +96 -0
  204. package/src/__tests__/asset-registry.unit.test.ts +511 -0
  205. package/src/__tests__/asset-runtime-error-code-owner.test-d.ts +63 -0
  206. package/src/__tests__/catalog-replica.unit.test.ts +196 -0
  207. package/src/__tests__/catalog-source-v2.unit.test.ts +158 -0
  208. package/src/__tests__/catalog-source.unit.test.ts +126 -0
  209. package/src/__tests__/catalog-subscription-order.unit.test.ts +65 -0
  210. package/src/__tests__/catalog-subscription.unit.test.ts +28 -0
  211. package/src/__tests__/decode-image-bytes-pass-through.test.ts +34 -0
  212. package/src/__tests__/decode-image-bytes.browser.test.ts +102 -0
  213. package/src/__tests__/decode-image-bytes.test-d.ts +108 -0
  214. package/src/__tests__/decode-image-bytes.test.ts +90 -0
  215. package/src/__tests__/decode-image-mime-owner.test.ts +2 -5
  216. package/src/__tests__/dynamic-texture-store.unit.test.ts +115 -0
  217. package/src/__tests__/errors-asset.unit.test.ts +92 -0
  218. package/src/__tests__/inline-pack-loaders.unit.test.ts +548 -0
  219. package/src/__tests__/instantiate-context.unit.test.ts +65 -0
  220. package/src/__tests__/instantiate-publication-fence.unit.test.ts +158 -0
  221. package/src/__tests__/instantiate.unit.test.ts +684 -0
  222. package/src/__tests__/loader-input-contract.unit.test.ts +89 -0
  223. package/src/__tests__/loader-registry.unit.test.ts +101 -0
  224. package/src/__tests__/material-dependency-invalidation.unit.test.ts +14 -0
  225. package/src/__tests__/material-generation-cache.unit.test.ts +46 -0
  226. package/src/__tests__/material-load-consumer.test-d.ts +42 -0
  227. package/src/__tests__/material-ready.integration.test.ts +508 -0
  228. package/src/__tests__/material-stale-generation.unit.test.ts +147 -0
  229. package/src/__tests__/mesh-bin-morph-v3.test.ts +22 -0
  230. package/src/__tests__/mesh-bin.unit.test.ts +152 -0
  231. package/src/__tests__/mipmap-generator.unit.test.ts +202 -0
  232. package/src/__tests__/pack-artifact-transcode-model-owner.test.ts +32 -0
  233. package/src/__tests__/pack-basis-load.integration.test.ts +168 -0
  234. package/src/__tests__/payload-validate.unit.test.ts +285 -0
  235. package/src/__tests__/plugin.test.ts +55 -0
  236. package/src/__tests__/registry-concurrency.bench.test.ts +40 -0
  237. package/src/__tests__/registry-failure-retry.integration.test.ts +69 -0
  238. package/src/__tests__/registry-load-graph.integration.test.ts +246 -0
  239. package/src/__tests__/registry-load-state.unit.test.ts +35 -0
  240. package/src/__tests__/registry-package-cache.unit.test.ts +53 -0
  241. package/src/__tests__/registry-public-ready.unit.test.ts +38 -0
  242. package/src/__tests__/resolve-asset-handle.unit.test.ts +238 -0
  243. package/src/__tests__/scene-handle-fields.unit.test.ts +129 -0
  244. package/src/__tests__/scene-payload.unit.test.ts +151 -0
  245. package/src/__tests__/scene-publication-fence.test.ts +129 -0
  246. package/src/__tests__/scriptable-pack-public-surface.test-d.ts +27 -0
  247. package/src/__tests__/static-rig.integration.test.ts +8 -0
  248. package/src/__tests__/validate-material.unit.test.ts +359 -0
  249. package/src/__tests__/video-pack-loader.integration.test.ts +83 -0
  250. package/src/aabb.ts +107 -0
  251. package/src/asset-registry.ts +2045 -0
  252. package/src/builtin-asset-registry.ts +164 -0
  253. package/src/catalog-source.ts +14 -70
  254. package/src/decode-image-bytes.ts +126 -0
  255. package/src/dynamic-texture-store.ts +234 -0
  256. package/src/errors/asset.ts +347 -0
  257. package/src/handles.ts +186 -0
  258. package/src/image-error.ts +45 -0
  259. package/src/index.ts +183 -16
  260. package/src/internal.ts +1 -0
  261. package/src/loader-registry.ts +157 -0
  262. package/src/loaders/inline-pack.ts +837 -0
  263. package/src/loaders/mesh-bin.ts +304 -0
  264. package/src/loaders/pack-artifact.ts +665 -0
  265. package/src/material/dependency-graph.ts +46 -0
  266. package/src/material/generation-cache.ts +177 -0
  267. package/src/material/inspection.ts +46 -0
  268. package/src/material/loader.ts +326 -0
  269. package/src/material/runtime-shader.ts +133 -0
  270. package/src/mesh-bin.ts +5 -0
  271. package/src/mipmap-generator.ts +450 -0
  272. package/src/payload-validate.ts +573 -0
  273. package/src/plugin.ts +48 -0
  274. package/src/registry/artifact-io.ts +259 -0
  275. package/src/registry/asset-evidence.ts +51 -0
  276. package/src/registry/catalog-state.ts +409 -0
  277. package/src/registry/catalog.ts +369 -0
  278. package/src/registry/instantiate.ts +1056 -0
  279. package/src/registry/load-by-guid.ts +1657 -0
  280. package/src/registry/load-state.ts +115 -0
  281. package/src/registry/runtime-projection.ts +63 -0
  282. package/src/registry/scene-publication-fence.ts +256 -0
  283. package/src/registry/validate-material.ts +385 -0
  284. package/src/resolve-asset-handle.ts +147 -0
  285. package/src/resolve-tileset-runtime.ts +72 -0
  286. package/src/scene-handle-fields.ts +221 -0
  287. package/src/scene-payload.ts +207 -0
  288. package/src/wire-default-loaders.ts +79 -0
  289. package/dist/__tests__/asset-graph-red.integration.test.d.ts +0 -2
  290. package/dist/__tests__/asset-graph-red.integration.test.d.ts.map +0 -1
  291. package/dist/__tests__/asset-kind.test.d.ts +0 -2
  292. package/dist/__tests__/asset-kind.test.d.ts.map +0 -1
  293. package/dist/__tests__/asset-registry-core.integration.test.d.ts +0 -2
  294. package/dist/__tests__/asset-registry-core.integration.test.d.ts.map +0 -1
  295. package/dist/__tests__/asset-registry-public-api.test-d.d.ts +0 -2
  296. package/dist/__tests__/asset-registry-public-api.test-d.d.ts.map +0 -1
  297. package/dist/__tests__/asset-runtime-core-lifecycle.integration.test.d.ts +0 -2
  298. package/dist/__tests__/asset-runtime-core-lifecycle.integration.test.d.ts.map +0 -1
  299. package/dist/__tests__/asset-runtime-snapshot.unit.test.d.ts +0 -2
  300. package/dist/__tests__/asset-runtime-snapshot.unit.test.d.ts.map +0 -1
  301. package/dist/__tests__/catalog-session-red.unit.test.d.ts +0 -2
  302. package/dist/__tests__/catalog-session-red.unit.test.d.ts.map +0 -1
  303. package/dist/__tests__/registry-lifecycle-red.integration.test.d.ts +0 -2
  304. package/dist/__tests__/registry-lifecycle-red.integration.test.d.ts.map +0 -1
  305. package/src/__tests__/asset-graph-red.integration.test.ts +0 -113
  306. package/src/__tests__/asset-registry-core.integration.test.ts +0 -161
  307. package/src/__tests__/asset-registry-public-api.test-d.ts +0 -31
  308. package/src/__tests__/asset-runtime-core-lifecycle.integration.test.ts +0 -79
  309. package/src/__tests__/asset-runtime-snapshot.unit.test.ts +0 -23
  310. package/src/__tests__/catalog-session-red.unit.test.ts +0 -276
  311. package/src/__tests__/registry-lifecycle-red.integration.test.ts +0 -80
@@ -0,0 +1,115 @@
1
+ export type LoadStatus = 'unloaded' | 'provisional' | 'ready' | 'failed';
2
+
3
+ export interface LoadRecord {
4
+ readonly status: LoadStatus;
5
+ readonly refs: readonly string[];
6
+ readonly value?: unknown;
7
+ readonly error?: unknown;
8
+ }
9
+
10
+ interface MutableLoadRecord {
11
+ status: LoadStatus;
12
+ refs: readonly string[];
13
+ value?: unknown;
14
+ error?: unknown;
15
+ }
16
+
17
+ export class LoadStateStore {
18
+ private readonly records = new Map<string, MutableLoadRecord>();
19
+
20
+ get(guid: string): LoadRecord | undefined {
21
+ return this.records.get(guid.toLowerCase());
22
+ }
23
+
24
+ getReady<T = unknown>(guid: string): T | undefined {
25
+ const record = this.records.get(guid.toLowerCase());
26
+ return record?.status === 'ready' ? (record.value as T) : undefined;
27
+ }
28
+
29
+ /**
30
+ * Return a provisional value only for the internal SCC back-edge bridge.
31
+ * Public callers must use getReady so promotion remains the visibility gate.
32
+ */
33
+ getProvisional<T = unknown>(guid: string): T | undefined {
34
+ const record = this.records.get(guid.toLowerCase());
35
+ return record?.status === 'provisional' ? (record.value as T) : undefined;
36
+ }
37
+
38
+ begin(guid: string, refs: readonly string[]): LoadRecord {
39
+ const key = guid.toLowerCase();
40
+ const existing = this.records.get(key);
41
+ if (existing?.status === 'provisional' || existing?.status === 'ready') return existing;
42
+ const record: MutableLoadRecord = {
43
+ status: 'provisional',
44
+ refs: refs.map((ref) => ref.toLowerCase()),
45
+ };
46
+ this.records.set(key, record);
47
+ return record;
48
+ }
49
+
50
+ resolveAsset(guid: string, value: unknown): void {
51
+ const key = guid.toLowerCase();
52
+ const record = this.records.get(key);
53
+ if (record === undefined) {
54
+ this.records.set(key, { status: 'provisional', refs: [], value });
55
+ return;
56
+ }
57
+ record.value = value;
58
+ }
59
+
60
+ registerReady(guid: string, value: unknown): void {
61
+ this.begin(guid, []);
62
+ this.resolveAsset(guid, value);
63
+ this.promoteReady(guid);
64
+ }
65
+
66
+ promoteReady(guid: string): void {
67
+ const root = guid.toLowerCase();
68
+ const group = this.provisionalGroup(root);
69
+ if (group.size === 0) return;
70
+ for (const key of group) {
71
+ const record = this.records.get(key);
72
+ if (record?.value === undefined) return;
73
+ for (const ref of record.refs) {
74
+ const dependency = this.records.get(ref);
75
+ if (dependency === undefined || (dependency.status !== 'ready' && !group.has(ref))) return;
76
+ }
77
+ }
78
+ for (const key of group) {
79
+ const record = this.records.get(key);
80
+ if (record !== undefined) record.status = 'ready';
81
+ }
82
+ }
83
+
84
+ fail(guid: string, error: unknown): readonly string[] {
85
+ const doomed = this.provisionalGroup(guid.toLowerCase());
86
+ if (doomed.size === 0) doomed.add(guid.toLowerCase());
87
+ for (const key of doomed) {
88
+ this.records.set(key, { status: 'unloaded', refs: [] });
89
+ }
90
+ const root = this.records.get(guid.toLowerCase());
91
+ if (root !== undefined) root.error = error;
92
+ return [...doomed];
93
+ }
94
+
95
+ remove(guid: string): void {
96
+ this.records.delete(guid.toLowerCase());
97
+ }
98
+
99
+ clear(): void {
100
+ this.records.clear();
101
+ }
102
+
103
+ private provisionalGroup(root: string): Set<string> {
104
+ const group = new Set<string>();
105
+ const visit = (key: string): void => {
106
+ if (group.has(key)) return;
107
+ const record = this.records.get(key);
108
+ if (record?.status !== 'provisional') return;
109
+ group.add(key);
110
+ for (const ref of record.refs) visit(ref);
111
+ };
112
+ visit(root);
113
+ return group;
114
+ }
115
+ }
@@ -0,0 +1,63 @@
1
+ import { err, ok, type Result } from '@forgeax/engine-rhi';
2
+ import { ASSET_ERROR_HINTS, AssetError, type CatalogProjection } from '@forgeax/engine-types';
3
+ import type { CatalogRecord } from './catalog';
4
+
5
+ export interface RuntimeProjection {
6
+ readonly subject: CatalogProjection['subject'];
7
+ readonly execution: CatalogProjection['execution'];
8
+ readonly lifecycle: CatalogProjection['lifecycle'];
9
+ readonly packageUrl: string;
10
+ }
11
+
12
+ function projectionError(guid: string, expected: string): AssetError {
13
+ return new AssetError({
14
+ code: 'asset-not-imported',
15
+ expected,
16
+ hint: `${ASSET_ERROR_HINTS['asset-not-imported']} (GUID ${guid})`,
17
+ });
18
+ }
19
+
20
+ /**
21
+ * Resolve the producer-declared runtime projection. Legacy rows return
22
+ * undefined so inline/dev assets retain their existing registration path;
23
+ * rows that publish any projection axis must publish all three axes.
24
+ */
25
+ export function resolveRuntimeProjection(
26
+ guid: string,
27
+ record: CatalogRecord,
28
+ ): Result<RuntimeProjection | undefined, AssetError> {
29
+ const hasAxes =
30
+ record.subject !== undefined ||
31
+ record.execution !== undefined ||
32
+ record.lifecycle !== undefined ||
33
+ record.projection !== undefined;
34
+ if (!hasAxes) return ok(undefined);
35
+
36
+ const projection = record.projection;
37
+ if (
38
+ projection === undefined ||
39
+ record.subject === undefined ||
40
+ record.execution === undefined ||
41
+ record.lifecycle === undefined
42
+ ) {
43
+ return err(
44
+ projectionError(
45
+ guid,
46
+ 'catalog entry to publish subject, execution, lifecycle, and projection together',
47
+ ),
48
+ );
49
+ }
50
+ if (
51
+ projection.subject !== record.subject ||
52
+ projection.execution !== record.execution ||
53
+ projection.lifecycle !== record.lifecycle
54
+ ) {
55
+ return err(projectionError(guid, 'catalog projection axes to agree with their row fields'));
56
+ }
57
+ return ok({
58
+ subject: projection.subject,
59
+ execution: projection.execution,
60
+ lifecycle: projection.lifecycle,
61
+ packageUrl: record.packageUrl,
62
+ });
63
+ }
@@ -0,0 +1,256 @@
1
+ import { err, ok, type Result } from '@forgeax/engine-rhi';
2
+ import type {
3
+ AssetPublicationEnvelope,
4
+ CatalogEntry,
5
+ ScenePublicationFence as EngineScenePublicationFence,
6
+ } from '@forgeax/engine-types';
7
+
8
+ export const SCENE_PUBLICATION_FENCE_SCHEMA = 'scene-publication-fence/1' as const;
9
+ export const SCENE_PUBLICATION_RECOVERY_ACTIONS = Object.freeze([
10
+ 'continue-last-known-good',
11
+ 'retry-rebuild',
12
+ 'fresh-reopen',
13
+ ] as const);
14
+
15
+ export type ScenePublicationFence = EngineScenePublicationFence;
16
+
17
+ export type ScenePublicationFencePhase = 'publication' | 'catalog' | 'observation' | 'instantiate';
18
+
19
+ export interface ScenePublicationFenceError {
20
+ readonly code: 'asset-generation-fence-mismatch';
21
+ readonly phase: ScenePublicationFencePhase;
22
+ readonly hint: string;
23
+ readonly sourcePath?: string;
24
+ readonly sourceRevision?: string;
25
+ readonly expected?: ScenePublicationFence;
26
+ readonly actual?: ScenePublicationFence;
27
+ readonly currentGeneration?: number;
28
+ readonly lastKnownGood?: ScenePublicationFence;
29
+ readonly retryable: boolean;
30
+ readonly recoveryActions: readonly string[];
31
+ }
32
+
33
+ export interface ScenePublicationObservation {
34
+ readonly fence: ScenePublicationFence;
35
+ readonly outputs: readonly string[];
36
+ }
37
+
38
+ function errorFor(
39
+ phase: ScenePublicationFencePhase,
40
+ hint: string,
41
+ input: {
42
+ readonly publication?: AssetPublicationEnvelope;
43
+ readonly expected?: ScenePublicationFence;
44
+ readonly actual?: ScenePublicationFence;
45
+ readonly lastKnownGood?: ScenePublicationFence;
46
+ readonly retryable?: boolean;
47
+ } = {},
48
+ ): ScenePublicationFenceError {
49
+ return {
50
+ code: 'asset-generation-fence-mismatch',
51
+ phase,
52
+ hint,
53
+ ...(input.publication === undefined
54
+ ? {}
55
+ : {
56
+ sourcePath: input.publication.sourcePath,
57
+ sourceRevision: input.publication.sourceRevision,
58
+ }),
59
+ ...(input.expected === undefined ? {} : { expected: input.expected }),
60
+ ...(input.actual === undefined ? {} : { actual: input.actual }),
61
+ ...(input.actual === undefined
62
+ ? {}
63
+ : { currentGeneration: input.actual.publicationGeneration }),
64
+ ...(input.lastKnownGood === undefined ? {} : { lastKnownGood: input.lastKnownGood }),
65
+ retryable: input.retryable ?? false,
66
+ recoveryActions: SCENE_PUBLICATION_RECOVERY_ACTIONS,
67
+ };
68
+ }
69
+
70
+ function isRecord(value: unknown): value is Record<string, unknown> {
71
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
72
+ }
73
+
74
+ function sameFence(left: ScenePublicationFence, right: ScenePublicationFence): boolean {
75
+ return (
76
+ left.schemaVersion === right.schemaVersion &&
77
+ left.sourcePath === right.sourcePath &&
78
+ left.sourceRevision === right.sourceRevision &&
79
+ left.publicationGeneration === right.publicationGeneration &&
80
+ left.outputDigest === right.outputDigest &&
81
+ left.outputSetDigest === right.outputSetDigest &&
82
+ left.receiptIdentity === right.receiptIdentity
83
+ );
84
+ }
85
+
86
+ /** Convert one complete Engine publication into the serializable Scene fence. */
87
+ export function createScenePublicationFence(
88
+ publication: AssetPublicationEnvelope,
89
+ ): Result<ScenePublicationFence, ScenePublicationFenceError> {
90
+ if (publication.outputs.length === 0) {
91
+ return err(
92
+ errorFor('publication', 'publication must contain at least one output', { publication }),
93
+ );
94
+ }
95
+ if (!Number.isSafeInteger(publication.generation) || publication.generation < 1) {
96
+ return err(
97
+ errorFor('publication', 'publication generation must be a positive integer', { publication }),
98
+ );
99
+ }
100
+ const receipt = publication.receipt;
101
+ if (
102
+ receipt.schemaVersion !== 'asset-publication-receipt/1' ||
103
+ receipt.sourcePath !== publication.sourcePath ||
104
+ receipt.sourceRevision !== publication.sourceRevision ||
105
+ receipt.outputDigest !== publication.digest ||
106
+ receipt.outputSetDigest !== publication.outputSetDigest
107
+ ) {
108
+ return err(
109
+ errorFor('publication', 'publication receipt does not match the complete output tuple', {
110
+ publication,
111
+ }),
112
+ );
113
+ }
114
+ const outputGuids = new Set<string>();
115
+ for (const output of publication.outputs) {
116
+ const key = output.guid.toLowerCase();
117
+ if (outputGuids.has(key)) {
118
+ return err(
119
+ errorFor('publication', 'publication output GUIDs must be unique', { publication }),
120
+ );
121
+ }
122
+ outputGuids.add(key);
123
+ }
124
+ return ok({
125
+ schemaVersion: SCENE_PUBLICATION_FENCE_SCHEMA,
126
+ sourcePath: publication.sourcePath,
127
+ sourceRevision: publication.sourceRevision,
128
+ publicationGeneration: publication.generation,
129
+ outputDigest: publication.digest,
130
+ outputSetDigest: publication.outputSetDigest,
131
+ receiptIdentity: receipt.inputFingerprint,
132
+ });
133
+ }
134
+
135
+ /** Parse the persisted fence without guessing legacy generation or identity. */
136
+ export function parseScenePublicationFence(
137
+ value: unknown,
138
+ ): Result<ScenePublicationFence, ScenePublicationFenceError> {
139
+ if (
140
+ !isRecord(value) ||
141
+ value.schemaVersion !== SCENE_PUBLICATION_FENCE_SCHEMA ||
142
+ typeof value.sourcePath !== 'string' ||
143
+ typeof value.sourceRevision !== 'string' ||
144
+ !Number.isSafeInteger(value.publicationGeneration) ||
145
+ (value.publicationGeneration as number) < 1 ||
146
+ typeof value.outputDigest !== 'string' ||
147
+ typeof value.outputSetDigest !== 'string' ||
148
+ typeof value.receiptIdentity !== 'string'
149
+ ) {
150
+ return err(errorFor('publication', 'publication fence shape is incomplete or invalid'));
151
+ }
152
+ const generation = value.publicationGeneration as number;
153
+ return ok(
154
+ Object.freeze({
155
+ schemaVersion: SCENE_PUBLICATION_FENCE_SCHEMA,
156
+ sourcePath: value.sourcePath,
157
+ sourceRevision: value.sourceRevision,
158
+ publicationGeneration: generation,
159
+ outputDigest: value.outputDigest,
160
+ outputSetDigest: value.outputSetDigest,
161
+ receiptIdentity: value.receiptIdentity,
162
+ }),
163
+ );
164
+ }
165
+
166
+ /** Compare all fence fields; no single generation or GUID may stand in for it. */
167
+ export function compareScenePublicationFences(
168
+ expected: ScenePublicationFence,
169
+ actual: ScenePublicationFence,
170
+ lastKnownGood?: ScenePublicationFence,
171
+ ): Result<ScenePublicationFence, ScenePublicationFenceError> {
172
+ if (sameFence(expected, actual)) return ok(actual);
173
+ const input = {
174
+ expected,
175
+ actual,
176
+ retryable: true,
177
+ ...(lastKnownGood === undefined ? {} : { lastKnownGood }),
178
+ };
179
+ return err(
180
+ errorFor('observation', 'publication fence does not match the expected complete tuple', input),
181
+ );
182
+ }
183
+
184
+ /** Derive a fence only when every Catalog row for the source publication is present and aligned. */
185
+ export function scenePublicationFenceFromCatalog(
186
+ entries: readonly CatalogEntry[],
187
+ outputGuid: string,
188
+ ): Result<ScenePublicationFence, ScenePublicationFenceError> {
189
+ const row = entries.find((entry) => entry.guid.toLowerCase() === outputGuid.toLowerCase());
190
+ const publication = row?.publication;
191
+ if (publication === undefined) {
192
+ return err(errorFor('catalog', 'Catalog row has no complete publication tuple'));
193
+ }
194
+ const expected = new Set(publication.outputs.map((output) => output.guid.toLowerCase()));
195
+ const observed = entries.filter((entry) => expected.has(entry.guid.toLowerCase()));
196
+ if (
197
+ observed.length !== expected.size ||
198
+ observed.some(
199
+ (entry) =>
200
+ entry.publication?.sourcePath !== publication.sourcePath ||
201
+ entry.publication?.generation !== publication.generation ||
202
+ entry.publication?.digest !== publication.digest ||
203
+ entry.publication?.outputSetDigest !== publication.outputSetDigest,
204
+ )
205
+ ) {
206
+ return err(
207
+ errorFor('catalog', 'Catalog does not contain one complete publication tuple', {
208
+ publication,
209
+ }),
210
+ );
211
+ }
212
+ return createScenePublicationFence(publication);
213
+ }
214
+
215
+ export async function observeScenePublication(input: {
216
+ readonly publication: AssetPublicationEnvelope;
217
+ readonly waitForOutput: (guid: string) => Promise<void>;
218
+ readonly timeoutMs?: number;
219
+ readonly lastKnownGood?: ScenePublicationFence;
220
+ }): Promise<Result<ScenePublicationObservation, ScenePublicationFenceError>> {
221
+ const created = createScenePublicationFence(input.publication);
222
+ if (!created.ok) return created;
223
+ const timeoutMs = input.timeoutMs ?? 5_000;
224
+ const wait = Promise.all(
225
+ input.publication.outputs.map((output) => input.waitForOutput(output.guid)),
226
+ );
227
+ let timer: ReturnType<typeof setTimeout> | undefined;
228
+ try {
229
+ await Promise.race([
230
+ wait,
231
+ new Promise<never>((_, reject) => {
232
+ timer = setTimeout(() => reject(new Error('publication-observation-timeout')), timeoutMs);
233
+ }),
234
+ ]);
235
+ } catch {
236
+ const failureInput = {
237
+ publication: input.publication,
238
+ actual: created.value,
239
+ retryable: true,
240
+ ...(input.lastKnownGood === undefined ? {} : { lastKnownGood: input.lastKnownGood }),
241
+ };
242
+ return err(
243
+ errorFor(
244
+ 'observation',
245
+ 'publication output observation did not complete before the fence deadline',
246
+ failureInput,
247
+ ),
248
+ );
249
+ } finally {
250
+ if (timer !== undefined) clearTimeout(timer);
251
+ }
252
+ return ok({
253
+ fence: created.value,
254
+ outputs: input.publication.outputs.map((output) => output.guid),
255
+ });
256
+ }