@carbonenginejs/runtime-resource 0.1.0 → 0.8.0

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 (329) hide show
  1. package/FORMAT-PROVENANCE.md +155 -135
  2. package/LICENSE +21 -21
  3. package/NOTICE +29 -29
  4. package/README.md +548 -241
  5. package/dist/CjsMotherLode.js +1271 -56
  6. package/dist/CjsMotherLode.js.map +1 -1
  7. package/dist/CjsResMan.js +3005 -740
  8. package/dist/CjsResMan.js.map +1 -1
  9. package/dist/CjsResManQueue.js +214 -214
  10. package/dist/CjsResource.js +566 -366
  11. package/dist/CjsResource.js.map +1 -1
  12. package/dist/CjsResourceSource.js +59 -59
  13. package/dist/_virtual/_rollupPluginBabelHelpers.js +153 -153
  14. package/dist/format/CjsBlueReader.js +269 -0
  15. package/dist/format/CjsBlueReader.js.map +1 -0
  16. package/dist/format/CjsFormat.js +192 -178
  17. package/dist/format/CjsFormat.js.map +1 -1
  18. package/dist/format/CjsReader.js +18 -0
  19. package/dist/format/CjsReader.js.map +1 -0
  20. package/dist/format/CjsResourceProbe.js +277 -269
  21. package/dist/format/CjsResourceProbe.js.map +1 -1
  22. package/dist/format/payloadContract.js +173 -173
  23. package/dist/formats/black/CjsBlackFormat.js +278 -272
  24. package/dist/formats/black/CjsBlackFormat.js.map +1 -1
  25. package/dist/formats/black/core/CjsBlackBinaryReader.js +153 -153
  26. package/dist/formats/black/core/CjsBlackPropertyReaders.js +382 -382
  27. package/dist/formats/black/core/CjsBlackReader.js +639 -760
  28. package/dist/formats/black/core/CjsBlackReader.js.map +1 -1
  29. package/dist/formats/black/core/CjsBlackSchemaRegistry.js +433 -433
  30. package/dist/formats/black/core/black-schema-v1-2026-07-11.json.js +4 -4
  31. package/dist/formats/black/core/blackConstants.js +7 -7
  32. package/dist/formats/black/core/blackDefinitions.js +10 -10
  33. package/dist/formats/black/core/blackEnums.js +6 -6
  34. package/dist/formats/black/core/blackSchema.js +3 -3
  35. package/dist/formats/black/core/blackVersion.js +22 -22
  36. package/dist/formats/black/core/helpers.js +182 -182
  37. package/dist/formats/black/core/schema.js +4 -4
  38. package/dist/formats/black/index.js +2 -2
  39. package/dist/formats/bnk/CjsBnkFormat.js +238 -245
  40. package/dist/formats/bnk/CjsBnkFormat.js.map +1 -1
  41. package/dist/formats/bnk/core/graph.js +140 -0
  42. package/dist/formats/bnk/core/graph.js.map +1 -0
  43. package/dist/formats/bnk/core/helpers.js +409 -354
  44. package/dist/formats/bnk/core/helpers.js.map +1 -1
  45. package/dist/formats/bnk/core/musicNodes.js +521 -0
  46. package/dist/formats/bnk/core/musicNodes.js.map +1 -0
  47. package/dist/formats/bnk/core/soundbanksInfo.js +246 -246
  48. package/dist/formats/bnk/index.js +2 -3
  49. package/dist/formats/bnk/index.js.map +1 -1
  50. package/dist/formats/cmf/CjsCmfFormat.js +497 -491
  51. package/dist/formats/cmf/CjsCmfFormat.js.map +1 -1
  52. package/dist/formats/cmf/core/binary.js +118 -118
  53. package/dist/formats/cmf/core/buffers.js +233 -233
  54. package/dist/formats/cmf/core/constants.js +47 -47
  55. package/dist/formats/cmf/core/gr2Anim.js +453 -453
  56. package/dist/formats/cmf/core/helpers.js +287 -287
  57. package/dist/formats/cmf/core/pack.js +276 -276
  58. package/dist/formats/cmf/core/schema.js +364 -364
  59. package/dist/formats/cmf/core/shared.js +268 -268
  60. package/dist/formats/cmf/core/writer.js +517 -517
  61. package/dist/formats/cmf/index.js +2 -2
  62. package/dist/formats/dds/CjsDdsFormat.js +193 -187
  63. package/dist/formats/dds/CjsDdsFormat.js.map +1 -1
  64. package/dist/formats/dds/core/bc6h.js +288 -0
  65. package/dist/formats/dds/core/bc6h.js.map +1 -0
  66. package/dist/formats/dds/core/bc7.js +251 -0
  67. package/dist/formats/dds/core/bc7.js.map +1 -0
  68. package/dist/formats/dds/core/helpers.js +815 -812
  69. package/dist/formats/dds/core/helpers.js.map +1 -1
  70. package/dist/formats/dds/index.js +2 -2
  71. package/dist/formats/fbx/CjsFbxFormat.js +266 -260
  72. package/dist/formats/fbx/CjsFbxFormat.js.map +1 -1
  73. package/dist/formats/fbx/core/helpers.js +3901 -3901
  74. package/dist/formats/fbx/index.js +2 -2
  75. package/dist/formats/flac/CjsFlacFormat.js +87 -81
  76. package/dist/formats/flac/CjsFlacFormat.js.map +1 -1
  77. package/dist/formats/flac/core/helpers.js +295 -295
  78. package/dist/formats/flac/index.js +2 -2
  79. package/dist/formats/gif/CjsGifFormat.js +87 -81
  80. package/dist/formats/gif/CjsGifFormat.js.map +1 -1
  81. package/dist/formats/gif/core/helpers.js +360 -360
  82. package/dist/formats/gif/index.js +2 -2
  83. package/dist/formats/gltf/CjsGltfFormat.js +290 -284
  84. package/dist/formats/gltf/CjsGltfFormat.js.map +1 -1
  85. package/dist/formats/gltf/core/helpers.js +292 -292
  86. package/dist/formats/gltf/core/json.js +79 -79
  87. package/dist/formats/gltf/core/parser.js +666 -666
  88. package/dist/formats/gltf/core/targets.js +163 -163
  89. package/dist/formats/gltf/index.js +2 -2
  90. package/dist/formats/index.js +23 -23
  91. package/dist/formats/jpeg/CjsJpegFormat.js +206 -200
  92. package/dist/formats/jpeg/CjsJpegFormat.js.map +1 -1
  93. package/dist/formats/jpeg/core/helpers.js +350 -350
  94. package/dist/formats/jpeg/core/jpeg.js +377 -377
  95. package/dist/formats/jpeg/index.js +2 -2
  96. package/dist/formats/mp3/CjsMp3Format.js +192 -186
  97. package/dist/formats/mp3/CjsMp3Format.js.map +1 -1
  98. package/dist/formats/mp3/core/helpers.js +338 -338
  99. package/dist/formats/mp3/index.js +2 -2
  100. package/dist/formats/mp4/CjsMp4Format.js +191 -185
  101. package/dist/formats/mp4/CjsMp4Format.js.map +1 -1
  102. package/dist/formats/mp4/core/helpers.js +449 -449
  103. package/dist/formats/mp4/index.js +2 -2
  104. package/dist/formats/obj/CjsObjFormat.js +253 -247
  105. package/dist/formats/obj/CjsObjFormat.js.map +1 -1
  106. package/dist/formats/obj/core/helpers.js +573 -573
  107. package/dist/formats/obj/core/json.js +64 -64
  108. package/dist/formats/obj/core/parser.js +321 -321
  109. package/dist/formats/obj/index.js +2 -2
  110. package/dist/formats/ogg/CjsOggFormat.js +88 -82
  111. package/dist/formats/ogg/CjsOggFormat.js.map +1 -1
  112. package/dist/formats/ogg/core/helpers.js +387 -387
  113. package/dist/formats/ogg/core/imdct.js +178 -178
  114. package/dist/formats/ogg/core/vorbis.js +999 -999
  115. package/dist/formats/ogg/index.js +2 -2
  116. package/dist/formats/png/CjsPngFormat.js +195 -189
  117. package/dist/formats/png/CjsPngFormat.js.map +1 -1
  118. package/dist/formats/png/core/helpers.js +583 -583
  119. package/dist/formats/png/index.js +2 -2
  120. package/dist/formats/red/CjsRedFormat.js +261 -247
  121. package/dist/formats/red/CjsRedFormat.js.map +1 -1
  122. package/dist/formats/red/core/CjsRedReader.js +194 -201
  123. package/dist/formats/red/core/CjsRedReader.js.map +1 -1
  124. package/dist/formats/red/core/blackDefinitions.js +3 -3
  125. package/dist/formats/red/core/helpers.js +139 -136
  126. package/dist/formats/red/core/helpers.js.map +1 -1
  127. package/dist/formats/red/core/redGraph.js +68 -68
  128. package/dist/formats/red/core/schema.js +4 -4
  129. package/dist/formats/red/index.js +2 -2
  130. package/dist/formats/stl/CjsStlFormat.js +365 -304
  131. package/dist/formats/stl/CjsStlFormat.js.map +1 -1
  132. package/dist/formats/stl/core/helpers.js +261 -257
  133. package/dist/formats/stl/core/helpers.js.map +1 -1
  134. package/dist/formats/stl/core/json.js +51 -51
  135. package/dist/formats/stl/core/stl.js +629 -544
  136. package/dist/formats/stl/core/stl.js.map +1 -1
  137. package/dist/formats/stl/core/targets.js +163 -163
  138. package/dist/formats/stl/index.js +2 -2
  139. package/dist/formats/tga/CjsTgaFormat.js +192 -186
  140. package/dist/formats/tga/CjsTgaFormat.js.map +1 -1
  141. package/dist/formats/tga/core/helpers.js +446 -446
  142. package/dist/formats/tga/index.js +2 -2
  143. package/dist/formats/wav/CjsWavFormat.js +192 -186
  144. package/dist/formats/wav/CjsWavFormat.js.map +1 -1
  145. package/dist/formats/wav/core/helpers.js +328 -328
  146. package/dist/formats/wav/index.js +2 -2
  147. package/dist/formats/webm/CjsWebmFormat.js +191 -185
  148. package/dist/formats/webm/CjsWebmFormat.js.map +1 -1
  149. package/dist/formats/webm/core/helpers.js +537 -537
  150. package/dist/formats/webm/index.js +2 -2
  151. package/dist/formats/webp/CjsWebpFormat.js +86 -80
  152. package/dist/formats/webp/CjsWebpFormat.js.map +1 -1
  153. package/dist/formats/webp/core/helpers.js +214 -214
  154. package/dist/formats/webp/index.js +2 -2
  155. package/dist/formats/wem/CjsWemFormat.js +241 -197
  156. package/dist/formats/wem/CjsWemFormat.js.map +1 -1
  157. package/dist/formats/wem/core/bitStream.js +259 -259
  158. package/dist/formats/wem/core/codebookLibrary.js +164 -164
  159. package/dist/formats/wem/core/helpers.js +417 -336
  160. package/dist/formats/wem/core/helpers.js.map +1 -1
  161. package/dist/formats/wem/core/packedCodebooksAotuv603.js +30 -30
  162. package/dist/formats/wem/core/ptadpcm.js +77 -0
  163. package/dist/formats/wem/core/ptadpcm.js.map +1 -0
  164. package/dist/formats/wem/core/resolve.js +121 -0
  165. package/dist/formats/wem/core/resolve.js.map +1 -0
  166. package/dist/formats/wem/core/wemToOgg.js +485 -485
  167. package/dist/formats/wem/index.js +2 -2
  168. package/dist/formats/yaml/CjsYamlFormat.js +83 -77
  169. package/dist/formats/yaml/CjsYamlFormat.js.map +1 -1
  170. package/dist/formats/yaml/core/CjsYamlReader.js +305 -304
  171. package/dist/formats/yaml/core/CjsYamlReader.js.map +1 -1
  172. package/dist/formats/yaml/core/helpers.js +160 -160
  173. package/dist/formats/yaml/index.js +2 -2
  174. package/dist/index.js +48 -52
  175. package/dist/index.js.map +1 -1
  176. package/dist/resourcePath.js +18 -18
  177. package/dist/resourceStates.js +9 -9
  178. package/dist/{generated/resources → resources}/AudioGeometryResData.js +47 -47
  179. package/dist/resources/AudioGeometryResData.js.map +1 -0
  180. package/dist/{generated/resources → resources}/GStateBindingCallbackData.js +31 -31
  181. package/dist/resources/GStateBindingCallbackData.js.map +1 -0
  182. package/dist/{generated/resources → resources}/MeshDecalData.js +37 -37
  183. package/dist/resources/MeshDecalData.js.map +1 -0
  184. package/dist/{generated/resources → resources}/MeshDecalLodData.js +34 -34
  185. package/dist/resources/MeshDecalLodData.js.map +1 -0
  186. package/dist/resources/Tr2EffectRes.js +71 -71
  187. package/dist/resources/Tr2GrannyIntersectionResult.js +60 -0
  188. package/dist/resources/Tr2GrannyIntersectionResult.js.map +1 -0
  189. package/dist/resources/Tr2GrannyStateRes.js +44 -44
  190. package/dist/resources/Tr2ImageRes.js +114 -114
  191. package/dist/resources/Tr2LightProfileRes.js +40 -40
  192. package/dist/{generated/resources → resources}/Tr2MaterialArea.js +34 -34
  193. package/dist/resources/Tr2MaterialArea.js.map +1 -0
  194. package/dist/{generated/resources → resources}/Tr2MaterialMesh.js +31 -31
  195. package/dist/resources/Tr2MaterialMesh.js.map +1 -0
  196. package/dist/{generated/resources → resources}/Tr2MaterialRes.js +34 -34
  197. package/dist/resources/Tr2MaterialRes.js.map +1 -0
  198. package/dist/{generated/resources → resources}/Tr2ShaderPermutation.js +43 -43
  199. package/dist/resources/Tr2ShaderPermutation.js.map +1 -0
  200. package/dist/{generated/resources → resources}/Tr2TextureLodManager.js +100 -60
  201. package/dist/resources/Tr2TextureLodManager.js.map +1 -0
  202. package/dist/{generated/resources → resources}/Tr2TextureLodUpdateRequest.js +37 -37
  203. package/dist/resources/Tr2TextureLodUpdateRequest.js.map +1 -0
  204. package/dist/{generated/resources → resources}/Tr2TexturePackChannel.js +37 -37
  205. package/dist/resources/Tr2TexturePackChannel.js.map +1 -0
  206. package/dist/resources/Tr2TexturePipeline.js +68 -0
  207. package/dist/resources/Tr2TexturePipeline.js.map +1 -0
  208. package/dist/{generated/resources → resources}/Tr2TexturePipelineParams.js +34 -34
  209. package/dist/resources/Tr2TexturePipelineParams.js.map +1 -0
  210. package/dist/{generated/resources → resources}/Tr2TexturePipelineStepCompress.js +40 -40
  211. package/dist/resources/Tr2TexturePipelineStepCompress.js.map +1 -0
  212. package/dist/{generated/resources → resources}/Tr2TexturePipelineStepLimitSize.js +34 -34
  213. package/dist/resources/Tr2TexturePipelineStepLimitSize.js.map +1 -0
  214. package/dist/{generated/resources → resources}/Tr2TexturePipelineStepLoad.js +31 -31
  215. package/dist/resources/Tr2TexturePipelineStepLoad.js.map +1 -0
  216. package/dist/{generated/resources → resources}/Tr2TexturePipelineStepPack.js +43 -43
  217. package/dist/resources/Tr2TexturePipelineStepPack.js.map +1 -0
  218. package/dist/resources/TriGeometryRes.js +239 -216
  219. package/dist/resources/TriGeometryRes.js.map +1 -1
  220. package/dist/{generated/resources → resources}/TriGeometryResAreaData.js +59 -59
  221. package/dist/resources/TriGeometryResAreaData.js.map +1 -0
  222. package/dist/{generated/resources → resources}/TriGeometryResJointData.js +38 -38
  223. package/dist/resources/TriGeometryResJointData.js.map +1 -0
  224. package/dist/{generated/resources → resources}/TriGeometryResLodData.js +88 -88
  225. package/dist/resources/TriGeometryResLodData.js.map +1 -0
  226. package/dist/{generated/resources → resources}/TriGeometryResMeshData.js +63 -63
  227. package/dist/resources/TriGeometryResMeshData.js.map +1 -0
  228. package/dist/{generated/resources → resources}/TriGeometryResSkeletonData.js +34 -34
  229. package/dist/resources/TriGeometryResSkeletonData.js.map +1 -0
  230. package/dist/resources/TriGrannyRes.js +43 -43
  231. package/dist/{generated/resources → resources}/TriJointBinding.js +38 -38
  232. package/dist/resources/TriJointBinding.js.map +1 -0
  233. package/dist/{generated/resources → resources}/TriMorphTargetGeometryConstants.js +46 -46
  234. package/dist/resources/TriMorphTargetGeometryConstants.js.map +1 -0
  235. package/dist/{generated/resources → resources}/TriRtGeometryConstants.js +88 -88
  236. package/dist/resources/TriRtGeometryConstants.js.map +1 -0
  237. package/dist/resources/TriTextureRes.js +304 -304
  238. package/dist/{generated/resources → resources}/enums.js +18 -18
  239. package/dist/resources/enums.js.map +1 -0
  240. package/dist/resources/resourceBoundary.js +47 -47
  241. package/dist/resources/texturePipelineBehavior.js +320 -0
  242. package/dist/resources/texturePipelineBehavior.js.map +1 -0
  243. package/dist/texture/CjsTextureArrayRes.js +406 -406
  244. package/dist/texture/CjsTextureParameterProxy.js +133 -133
  245. package/format-notices/black/LICENSE +21 -21
  246. package/format-notices/black/NOTICE +47 -47
  247. package/format-notices/bnk/LICENSE +21 -21
  248. package/format-notices/bnk/NOTICE +20 -17
  249. package/format-notices/cmf/LICENSE +21 -21
  250. package/format-notices/cmf/NOTICE +36 -36
  251. package/format-notices/dds/LICENSE +21 -21
  252. package/format-notices/dds/NOTICE +14 -14
  253. package/format-notices/fbx/LICENSE +21 -21
  254. package/format-notices/fbx/NOTICE +14 -14
  255. package/format-notices/flac/LICENSE +21 -21
  256. package/format-notices/flac/NOTICE +14 -14
  257. package/format-notices/gif/LICENSE +21 -21
  258. package/format-notices/gif/NOTICE +14 -14
  259. package/format-notices/gltf/LICENSE +21 -21
  260. package/format-notices/gltf/NOTICE +27 -27
  261. package/format-notices/jpeg/LICENSE +21 -21
  262. package/format-notices/jpeg/NOTICE +14 -14
  263. package/format-notices/mp3/LICENSE +21 -21
  264. package/format-notices/mp3/NOTICE +14 -14
  265. package/format-notices/mp4/LICENSE +21 -21
  266. package/format-notices/mp4/NOTICE +14 -14
  267. package/format-notices/obj/LICENSE +21 -21
  268. package/format-notices/obj/NOTICE +26 -26
  269. package/format-notices/ogg/LICENSE +21 -21
  270. package/format-notices/ogg/NOTICE +28 -28
  271. package/format-notices/png/LICENSE +21 -21
  272. package/format-notices/png/NOTICE +14 -14
  273. package/format-notices/red/LICENSE +21 -21
  274. package/format-notices/red/NOTICE +31 -31
  275. package/format-notices/stl/LICENSE +21 -21
  276. package/format-notices/stl/NOTICE +21 -21
  277. package/format-notices/tga/LICENSE +21 -21
  278. package/format-notices/tga/NOTICE +14 -14
  279. package/format-notices/wav/LICENSE +21 -21
  280. package/format-notices/wav/NOTICE +14 -14
  281. package/format-notices/webm/LICENSE +21 -21
  282. package/format-notices/webm/NOTICE +14 -14
  283. package/format-notices/webp/LICENSE +21 -21
  284. package/format-notices/webp/NOTICE +14 -14
  285. package/format-notices/wem/LICENSE +57 -57
  286. package/format-notices/wem/NOTICE +33 -33
  287. package/format-notices/yaml/LICENSE +21 -21
  288. package/format-notices/yaml/NOTICE +44 -44
  289. package/package.json +51 -51
  290. package/resource-lifecycle.md +679 -487
  291. package/dist/generated/resources/AudioGeometryResData.js.map +0 -1
  292. package/dist/generated/resources/CmfVertexReader.js +0 -46
  293. package/dist/generated/resources/CmfVertexReader.js.map +0 -1
  294. package/dist/generated/resources/GStateBindingCallbackData.js.map +0 -1
  295. package/dist/generated/resources/MeshDecalData.js.map +0 -1
  296. package/dist/generated/resources/MeshDecalLodData.js.map +0 -1
  297. package/dist/generated/resources/Tr2AsyncSave.js +0 -46
  298. package/dist/generated/resources/Tr2AsyncSave.js.map +0 -1
  299. package/dist/generated/resources/Tr2CmfContents.js +0 -37
  300. package/dist/generated/resources/Tr2CmfContents.js.map +0 -1
  301. package/dist/generated/resources/Tr2GrannyIntersectionResult.js +0 -58
  302. package/dist/generated/resources/Tr2GrannyIntersectionResult.js.map +0 -1
  303. package/dist/generated/resources/Tr2LoadPrepareFence.js +0 -37
  304. package/dist/generated/resources/Tr2LoadPrepareFence.js.map +0 -1
  305. package/dist/generated/resources/Tr2MaterialArea.js.map +0 -1
  306. package/dist/generated/resources/Tr2MaterialMesh.js.map +0 -1
  307. package/dist/generated/resources/Tr2MaterialRes.js.map +0 -1
  308. package/dist/generated/resources/Tr2ShaderPermutation.js.map +0 -1
  309. package/dist/generated/resources/Tr2TextureLodManager.js.map +0 -1
  310. package/dist/generated/resources/Tr2TextureLodUpdateRequest.js.map +0 -1
  311. package/dist/generated/resources/Tr2TexturePackChannel.js.map +0 -1
  312. package/dist/generated/resources/Tr2TexturePipeline.js +0 -44
  313. package/dist/generated/resources/Tr2TexturePipeline.js.map +0 -1
  314. package/dist/generated/resources/Tr2TexturePipelineParams.js.map +0 -1
  315. package/dist/generated/resources/Tr2TexturePipelineStepCompress.js.map +0 -1
  316. package/dist/generated/resources/Tr2TexturePipelineStepLimitSize.js.map +0 -1
  317. package/dist/generated/resources/Tr2TexturePipelineStepLoad.js.map +0 -1
  318. package/dist/generated/resources/Tr2TexturePipelineStepPack.js.map +0 -1
  319. package/dist/generated/resources/TriGeometryResAreaData.js.map +0 -1
  320. package/dist/generated/resources/TriGeometryResJointData.js.map +0 -1
  321. package/dist/generated/resources/TriGeometryResLodData.js.map +0 -1
  322. package/dist/generated/resources/TriGeometryResMeshData.js.map +0 -1
  323. package/dist/generated/resources/TriGeometryResSkeletonData.js.map +0 -1
  324. package/dist/generated/resources/TriJointBinding.js.map +0 -1
  325. package/dist/generated/resources/TriMorphTargetGeometryConstants.js.map +0 -1
  326. package/dist/generated/resources/TriRtGeometryConstants.js.map +0 -1
  327. package/dist/generated/resources/enums.js.map +0 -1
  328. package/dist/generated/resources/index.js +0 -32
  329. package/dist/generated/resources/index.js.map +0 -1
package/README.md CHANGED
@@ -1,241 +1,548 @@
1
- # @carbonenginejs/runtime-resource
2
-
3
- CarbonEngineJS resource lifecycle, cache, format selection, source, and object
4
- loading contracts.
5
-
6
- This package owns the GPU-free resource layer:
7
-
8
- - `CjsResource` state and Carbon-style resource methods.
9
- - `CjsTextureArrayRes` and `CjsTextureParameterProxy` for material-facing,
10
- frame-coalesced texture-array inputs without changing ordinary texture
11
- parameter behavior.
12
- - `CjsMotherLode` cache lookup/insert/delete/stats.
13
- - `CjsResMan` semantic resource construction, registered-format selection,
14
- concurrency-limited source loading, staged prepare queues, layered
15
- source/read/resource deduplication, object loader dispatch, and prefetch.
16
- - Raw `CjsEventEmitter` from `core-types/model` for manager/runtime events
17
- without requiring `CjsModel` inheritance. External listeners unregister
18
- directly with `OffEvent`; listener scopes, owner-side `ListenTo` helpers,
19
- and a separate resource notification layer are not part of the contract.
20
- - Path normalization and extension helpers.
21
- - Source adapters for memory and `fetch`.
22
- - Plain reader/converter payload objects with focused shared validators.
23
- - Canonical Carbon resource classes that validate and hold CPU payloads
24
- privately:
25
- `TriTextureRes`, `TriGeometryRes`, `Tr2EffectRes`, `Tr2ImageRes`,
26
- `TriGrannyRes`, `Tr2GrannyStateRes`, and `Tr2LightProfileRes`.
27
- - Opaque engine-owned subobject slots for backend adapters.
28
- - Format policy, format class contracts, and load/prepare state mapping stay
29
- inside this package's implementation and public API rather than external
30
- workspace notes.
31
- - Non-shader format implementations are owned as explicit tree-shakeable
32
- subpaths under `@carbonenginejs/runtime-resource/formats/<name>`.
33
-
34
- It intentionally does not own WebGL/WebGPU realization. Engine packages should adapt prepared resources into backend objects.
35
-
36
- Authoring source is decorated JavaScript. Published/consumer output is built ESM in `npm/dist`.
37
-
38
- ## Package relationships
39
-
40
- - `runtime-core` may configure and expose a `CjsResMan`, but does not own its
41
- implementation.
42
- - `runtime-trinity` and `runtime-sof` may request GPU-free objects and resources
43
- without selecting an engine.
44
- - `engine-webgpu` and future WebGL engines consume loaded resources and own all
45
- backend allocations, preparation, replacement, and destruction.
46
-
47
- Concrete formats are not imported or registered by the package root:
48
-
49
- ```js
50
- import { CjsResMan } from "@carbonenginejs/runtime-resource";
51
- import { CjsMp4Format } from "@carbonenginejs/runtime-resource/formats/mp4";
52
-
53
- const resMan = new CjsResMan().Register({
54
- source,
55
- formats: [ CjsMp4Format ]
56
- });
57
-
58
- const resource = resMan.GetResource("res:/video/intro.mp4");
59
- const video = await resource.Ready();
60
- ```
61
-
62
- Formats return plain payload objects. Semantic resource classes apply them
63
- through `SetPayload()`, validate their own required fields, and throw
64
- `CJS_RESOURCE_PAYLOAD_INVALID` before replacing a previously valid payload.
65
- `GetPayload()`, `HasPayload()`, and `ReleasePayload()` manage transient CPU
66
- retention without introducing a parallel DTO class hierarchy.
67
-
68
- ## Queued load and staged prepare
69
-
70
- `GetObject()`, `LoadObject()`, and resource `Ready()` use two manager-owned
71
- queues:
72
-
73
- ```text
74
- BACKGROUND: deduplicated source load, limited by maxConcurrentLoads
75
- MAIN: read -> configured prepare stages -> resource publication
76
- ```
77
-
78
- Each main-queue stage is a separate item. `maxPrepareTime` is a per-pump budget
79
- in seconds, and `maxPrepareItemsPerTick` can add an item-count limit. The
80
- default scheduler keeps promise-based calls working; a `CjsLibrary` or direct
81
- caller can provide its frame scheduler and default build behavior:
82
-
83
- ```js
84
- const resMan = new CjsResMan({
85
- source,
86
- maxConcurrentLoads: 8,
87
- maxPrepareTime: 0.005,
88
- queueScheduler: callback => requestAnimationFrame(callback),
89
- preparePipelines: {
90
- cmf_test: {
91
- default: true,
92
- stages: [
93
- {
94
- name: "convert",
95
- prepare: (payload, context) => convertToCmf(payload, context)
96
- }
97
- ]
98
- }
99
- }
100
- });
101
-
102
- await resMan.FetchResource("res:/model/ship.gr2", {
103
- requirement: "geometry",
104
- preparePipeline: "cmf_test"
105
- });
106
- ```
107
-
108
- The library chooses the named pipeline from registered behavior and detected
109
- capabilities. `CjsResMan` executes that request; it does not inspect WebGL,
110
- WebGPU, texture, geometry, or codec support to select one. A request may
111
- override the default with `preparePipeline` or append direct `prepareStages`.
112
-
113
- Blue-compatible queue controls are exposed directly on `CjsResMan`:
114
- `AddToQueue`, `CancelFromQueue`, `GetNextIdForQueue`,
115
- `PumpMainThreadQueue`, `PauseQueue`, `ResumeQueue`, `GetPendingLoads`, and
116
- `GetPendingPrepares`. `Update()`/`Tick()` pump work, while `Wait()` is the
117
- method-level queue fence; no separate fence object is required.
118
-
119
- Format classes own input extensions. Resource classes are registered by a
120
- semantic requirement, never by file extension:
121
-
122
- ```js
123
- const resMan = new CjsResMan().Register({
124
- source,
125
- formats: [ CjsDdsFormat, CjsPngFormat ],
126
- resourceTypes: [ TriTextureRes, Tr2ImageRes ]
127
- });
128
-
129
- const texture = resMan.GetResource("res:/image/ship.png", {
130
- requirement: "texture",
131
- emit: "image"
132
- });
133
- const image = resMan.GetResource("res:/image/ship.png", {
134
- requirement: "image",
135
- emit: "image"
136
- });
137
- ```
138
-
139
- Those are distinct resource identities but share the normalized source-byte
140
- operation. The manager does not expose an extension-to-resource compatibility
141
- registry.
142
-
143
- Texture-array resources expose one ordinary-looking proxy per ordered layer:
144
-
145
- ```js
146
- const textureArray = new CjsTextureArrayRes({
147
- paths: [
148
- "res:/detail1.dds",
149
- "res:/detail2.dds",
150
- "res:/detail3.dds"
151
- ],
152
- layerNames: [ "Detail1Map", "Detail2Map", "Detail3Map" ],
153
- updateScheduler: resource => frameQueue.add(resource)
154
- });
155
-
156
- const detail2 = textureArray.GetLayerParameter(1);
157
- detail2.SetValue("res:/replacement.dds");
158
-
159
- detail2.textureRes === textureArray; // true
160
- ```
161
-
162
- Proxy setters only update their source path and invalidate the parent. The
163
- parent is scheduled once even if several proxies change in the same frame.
164
- The next-frame consumer calls `Update()` or `ConsumeUpdateRequest()` to obtain
165
- one immutable ordered snapshot. Runtime-resource does not know which shader
166
- metadata caused the aggregate request; shader packages and engine adapters map
167
- public parameter names to layer indices.
168
-
169
- Public effect parameters remain separate from these internal proxies. Their
170
- authored paths and individual 2D source resources are not replaced by the
171
- aggregate. An engine-owned, non-persisted bridge mirrors public changes into
172
- the fixed internal layers.
173
-
174
- Consumed snapshots are explicit in-flight generations. An adapter either
175
- publishes the current candidate atomically, requeues retryable work, or records
176
- failure:
177
-
178
- ```js
179
- const request = textureArray.ConsumeUpdateRequest();
180
-
181
- try {
182
- const candidate = await adapter.PrepareTextureArray(request);
183
- const result = textureArray.CommitPreparedAdapterRevision(
184
- request.revision,
185
- "webgpu",
186
- candidate
187
- );
188
-
189
- // A rejected/stale candidate is destroyed by the commit method by default.
190
- // The adapter owns disposal of a successfully displaced allocation.
191
- result.displaced?.destroy();
192
- } catch (error) {
193
- textureArray.FailUpdateRequest(request.revision, error, { retry: true });
194
- }
195
-
196
- await textureArray.Ready(); // the generation requested at call time
197
- ```
198
-
199
- `SetLayerResource()` attaches a resolved source without rewriting the logical
200
- requested path. `TouchLayer()` invalidates an in-place source revision.
201
- `RetryUpdateRequest()` restores consumed work, and `HandleAdapterLoss()` drops
202
- an unusable adapter allocation and schedules a complete topology rebuild.
203
- Topology-changing snapshots set `topologyChanged: true` and report only valid
204
- current layer indices in `dirtyLayers`.
205
-
206
- ## Development
207
-
208
- Install dependencies and run the non-interactive baseline checks from the
209
- repository root:
210
-
211
- ```sh
212
- npm install
213
- npm run lint
214
- npm run check
215
- npm test
216
- ```
217
-
218
- `npm run check` builds the consumer package and proves that decorator metadata
219
- matches between authoring source and built output. `npm test` additionally runs
220
- the complete GPU-free unit suite; it requires no private assets, credentials,
221
- network access, browser, or GPU after dependencies are installed.
222
-
223
- See [Runtime Resource Lifecycle](resource-lifecycle.md) for state, retention,
224
- and texture-array generation contracts. See
225
- [Format ownership and fork provenance](FORMAT-PROVENANCE.md) for copied-reader
226
- ownership, licenses, exclusions, and the deferred GR2 migration. Both documents
227
- ship with the published package.
228
-
229
- ## Provenance
230
-
231
- CarbonEngine and Fenris Creations (CCP Games) are named for interoperability
232
- and provenance context. This package contains CarbonEngineJS original resource
233
- infrastructure, CarbonEngine-shaped resource ports, and maintained copies of
234
- the non-shader readers identified in `FORMAT-PROVENANCE.md`. It does not copy
235
- Fenris Creations game assets, proprietary documentation, or shader source.
236
- CarbonEngine and historical JavaScript implementations were used as the
237
- behavioral references described in the package notices.
238
-
239
- This project is not affiliated with, endorsed by, or sponsored by CCP Games or
240
- CCP ehf. EVE Online and related marks remain the property of their respective
241
- owners.
1
+ # @carbonenginejs/runtime-resource
2
+
3
+ CarbonEngineJS resource lifecycle, cache, format selection, source, and object
4
+ loading contracts.
5
+
6
+ This package owns the GPU-free resource layer:
7
+
8
+ - `CjsResource` state and Carbon-style resource methods.
9
+ - `CjsTextureArrayRes` and `CjsTextureParameterProxy` for material-facing,
10
+ frame-coalesced texture-array inputs without changing ordinary texture
11
+ parameter behavior.
12
+ - `CjsMotherLode` canonical identity, explicit replacement results, activity
13
+ and lock metadata, deterministic payload/adapter cleanup, and cache stats.
14
+ - `CjsResMan` semantic resource construction, registered-format selection,
15
+ concurrency-limited source loading, staged prepare queues, layered
16
+ source/read/resource deduplication, object loader dispatch, and prefetch.
17
+ - Raw `CjsEventEmitter` from `core-types/model` for manager/runtime events
18
+ without requiring `CjsModel` inheritance. External listeners unregister
19
+ directly with `OffEvent`; listener scopes, owner-side `ListenTo` helpers,
20
+ and a separate resource notification layer are not part of the contract.
21
+ - Path normalization and extension helpers.
22
+ - Source adapters for memory and `fetch`.
23
+ - Plain reader/converter payload objects with focused shared validators.
24
+ - Canonical Carbon resource classes that validate and hold CPU payloads
25
+ privately:
26
+ `TriTextureRes`, `TriGeometryRes`, `Tr2EffectRes`, `Tr2ImageRes`,
27
+ `TriGrannyRes`, `Tr2GrannyStateRes`, and `Tr2LightProfileRes`.
28
+ - `Tr2TexturePipeline` for Carbon-shaped CPU-only load, limit-size, compress
29
+ validation, and channel-pack steps, plus `Tr2TextureLodManager` for explicit
30
+ texture-resource membership without device-memory policy.
31
+ - Opaque engine-owned subobject slots for backend adapters.
32
+ - Format policy, format class contracts, and load/prepare state mapping stay
33
+ inside this package's implementation and public API rather than external
34
+ workspace notes.
35
+ - Non-shader format implementations are owned as explicit tree-shakeable
36
+ subpaths under `@carbonenginejs/runtime-resource/formats/<name>`.
37
+
38
+ It intentionally does not own WebGL/WebGPU realization. Engine packages should adapt prepared resources into backend objects.
39
+
40
+ Authoring source is decorated JavaScript. Published/consumer output is built ESM in `npm/dist`.
41
+
42
+ Completed Carbon data classes live with maintained source under
43
+ `src/resources`; `src/generated` is reserved for unresolved active ports and is
44
+ currently absent. Native shapes that JavaScript replaces or does not use are
45
+ retained only under `src/dropped`, with their disposition documented there,
46
+ and are never exported or bundled.
47
+
48
+ ## Package relationships
49
+
50
+ - `runtime-core` may configure and expose a `CjsResMan`, but does not own its
51
+ implementation.
52
+ - `runtime-trinity` and `runtime-sof` may request GPU-free objects and resources
53
+ without selecting an engine.
54
+ - `engine-webgpu` and future WebGL engines consume loaded resources and own all
55
+ backend allocations, preparation, replacement, and destruction.
56
+
57
+ Concrete formats are not imported or registered by the package root:
58
+
59
+ ```js
60
+ import { CjsResMan } from "@carbonenginejs/runtime-resource";
61
+ import { CjsMp4Format } from "@carbonenginejs/runtime-resource/formats/mp4";
62
+
63
+ const resMan = new CjsResMan().Register({
64
+ source,
65
+ formats: [ CjsMp4Format ]
66
+ });
67
+
68
+ const resource = resMan.GetResource("res:/video/intro.mp4");
69
+ const video = await resource.Ready();
70
+ ```
71
+
72
+ Formats return plain payload objects. Semantic resource classes apply them
73
+ through `SetPayload()`, validate their own required fields, and throw
74
+ `CJS_RESOURCE_PAYLOAD_INVALID` before replacing a previously valid payload.
75
+ `GetPayload()`, `HasPayload()`, and `ReleasePayload()` manage transient CPU
76
+ retention without introducing a parallel DTO class hierarchy.
77
+
78
+ Red payload output reserves configurable type, ID, reference, and sequence
79
+ values markers (`_type`, `_id`, `_reference`, and `_values` by default).
80
+ Repeated or cyclic sequences use an ID-bearing values envelope; unique
81
+ sequences remain arrays. Authored fields may not collide with active markers,
82
+ so remap the marker options when those names are real data. Disabling the
83
+ reference marker preserves actual JavaScript identity; cyclic output in that
84
+ mode is intentionally not JSON-serializable.
85
+
86
+ ## Texture CPU pipeline and LOD membership
87
+
88
+ `Tr2TexturePipeline` is the Carbon texture-specific CPU bitmap pipeline, not a
89
+ general resource prepare stage. `GetResourceDependencies()` returns the sorted
90
+ unique paths required by load and channel-pack steps. `Execute()` resolves
91
+ those inputs from an explicit `inputs` map/object, an async `load(path)`
92
+ callback, or an injected `CjsResMan`, then returns a canonical plain
93
+ `rgba8unorm` payload:
94
+
95
+ ```js
96
+ import {
97
+ Tr2TexturePipeline,
98
+ Tr2TexturePipelineStepLoad,
99
+ Tr2TexturePipelineStepLimitSize
100
+ } from "@carbonenginejs/runtime-resource";
101
+
102
+ const load = new Tr2TexturePipelineStepLoad();
103
+ load.path = "res:/texture/source.png";
104
+ const limit = new Tr2TexturePipelineStepLimitSize();
105
+ limit.maxWidth = 512;
106
+
107
+ const pipeline = new Tr2TexturePipeline();
108
+ pipeline.steps = [ load, limit ];
109
+ const rgba = await pipeline.Execute(0, 0, { resMan });
110
+ ```
111
+
112
+ The maintained runtime path currently accepts canonical `rgba8unorm` inputs.
113
+ Load copies the source bitmap, limit-size repeatedly performs a 2x2 CPU
114
+ downsample, pack builds logical RGBA channels from independent inputs, and
115
+ Carbon's present compress step remains validation-only because the native
116
+ method is itself a no-op. Unsupported step types fail explicitly.
117
+
118
+ Decoded DDS fallback currently has a narrower contract than native DDS texture
119
+ output. `emit: "rgba"` returns one canonical 2D surface decoded from the first
120
+ DDS subresource; it does not preserve stored mip levels, cube faces, array
121
+ layers, or volume slices. Consumers may use it for ordinary 2D fallback when
122
+ the engine owns any required mip generation, but must not infer decoded
123
+ multi-subresource support from a successful RGBA probe. A future richer
124
+ decoded-texture contract must be introduced explicitly rather than overloading
125
+ the current RGBA fields. The software path includes BC1-BC5 and BC7 as RGBA8,
126
+ plus signed and unsigned BC6H as linear `Float32Array` RGBA without clamping HDR
127
+ values. These block decoders are implemented in-project with no codec package.
128
+
129
+ `Tr2TextureLodManager` owns only ordered resource membership through
130
+ `RegisterTexture()`, `UnregisterTexture()`, and `GetManagedTextures()`. Engine
131
+ packages continue to own GPU allocations, upload accounting, device budgets,
132
+ capability limits, and device-loss recovery.
133
+
134
+ ## Wwise soundbanks and media
135
+
136
+ `formats/bnk` and `formats/wem` cover the Wwise audio pipeline end to end.
137
+ `CjsBnkFormat.inspect()` decodes the chunk map, embedded media index, bank
138
+ names, and the HIRC listing with version-stable typed fields (event action
139
+ lists, action type/target, sound and music-track source ids; pinned against
140
+ bank generator version 150). The Wwise-domain toolkit is grouped under the
141
+ `CjsBnkFormat.wwise` static: the SoundbanksInfo catalog helpers, the FNV-1
142
+ id hash, and event → media resolution:
143
+
144
+ ```js
145
+ import { CjsBnkFormat } from "@carbonenginejs/runtime-resource/formats/bnk";
146
+ import { CjsWemFormat } from "@carbonenginejs/runtime-resource/formats/wem";
147
+
148
+ const inspections = bankByteArrays.map(bytes => CjsBnkFormat.inspect(bytes));
149
+ const { eventMedia } = CjsBnkFormat.wwise.eventMediaFromBanks(inspections);
150
+ // eventMedia: Map<eventObjectId, Set<wemId>> - banks may split events from
151
+ // their target sounds, so pass every related bank to one call.
152
+
153
+ const ogg = CjsWemFormat.toOgg(wemBytes); // Wwise Vorbis -> Ogg (lossless)
154
+ const pcm = CjsWemFormat.toPcm(wemBytes); // PTADPCM / 16-bit PCM -> float32
155
+ ```
156
+
157
+ The read/inspect path stays a pure container reader; `wwise.eventMediaFromBanks`
158
+ is graph interpretation offered for consumers with their own engines — the
159
+ resource lifecycle never calls it.
160
+
161
+ ## STL export
162
+
163
+ `CjsStlFormat` writes shared geometry directly to binary or ASCII STL. The
164
+ writer consumes `mesh.vertex.position` and triangular `mesh.indices[].faces`;
165
+ multiple meshes and index groups are flattened in encounter order because STL
166
+ does not carry portable scene, material, skin, or animation structure.
167
+
168
+ ```js
169
+ import { CjsStlFormat } from "@carbonenginejs/runtime-resource/formats/stl";
170
+
171
+ const bytes = CjsStlFormat.write(sharedGeometry, {
172
+ binary: true,
173
+ solidName: "ship_hull",
174
+ scale: 1000,
175
+ requireWatertight: true
176
+ });
177
+ ```
178
+
179
+ Writes do not mutate the shared input. Facet normals are recalculated from
180
+ winding by default; set `recalculateNormals: false` to average valid vertex
181
+ normals. Degenerate triangles are skipped by default. Index values must be safe
182
+ integers within the position channel, and binary output rejects coordinates
183
+ outside float32 range instead of silently emitting infinities. The
184
+ `requireWatertight` option rejects open, non-manifold, inconsistently wound, or
185
+ degenerate output.
186
+
187
+ ## MotherLode ownership
188
+
189
+ `CjsResMan` resolves each normalized path and promised output to one canonical
190
+ MotherLode key. `Insert(key, resource, options)` reports `{ inserted, replaced,
191
+ displaced }`; replacement, deletion, clearing, and shutdown destroy attached
192
+ adapter allocations and release the complete CPU payload by default. Callers
193
+ that deliberately retain ownership may pass `{ cleanup: false }` and keep the
194
+ returned displaced resource. If replacement cleanup fails, insertion throws a
195
+ contextual error and leaves the existing owner registered. These ordinary
196
+ ownership removals preserve the handle's last resource state; `PURGED` is
197
+ reserved for successful policy eviction through inactivity or byte pressure.
198
+
199
+ Canonical resource identity is the normalized source path plus its promised
200
+ output tag. `variant` is the explicit tag; otherwise `emit`, `requirement`, or
201
+ `payload` supplies it. Human-readable identities are written as
202
+ `res:/ship.gr2@cmf`, although MotherLode uses an internal delimiter. Reader,
203
+ constructor, and format-option implementations never enter the key. CjsLibrary
204
+ chooses the promised output and ResMan executes the current setup-time format
205
+ registration for it.
206
+
207
+ Output selection is case-insensitive for identity and matching, but format
208
+ readers receive the canonical declared spelling (for example `cmfJson`). A
209
+ legacy direct object loader exposes only its unforced default; named output
210
+ variants belong on a format class. Unsupported `@output` requests fail before
211
+ cache lookup, so a resident handle cannot bypass the declaration.
212
+
213
+ A released CPU payload retains only the small request needed to reconstruct
214
+ that same path/output from its source and `sourceRevision`. The retained
215
+ promised-output fields and source provenance win over later
216
+ `Ready()`/`GetObject()` overrides, while cache/reload policy remains per-call.
217
+ Payload leases protect active consumers; an engine may release its own backend
218
+ adapter without destroying shared CPU data. Change setup-time registrations
219
+ only with an explicit resource reset (`Delete`/`Clear`) or a new manager. A
220
+ changed output contract must use a new tag such as `@cmf2`.
221
+
222
+ `Startup()` and `Shutdown()` are idempotent. `HasKey`, `Lookup`, `Delete`,
223
+ `GetKeys`, `GetValues`, `GetSize`, `SetCacheSize`, `GetCacheSize`, `GetStats`,
224
+ `TrimCache`, `ReplaceExpected`, `Clear`, and `ClearCached` provide the
225
+ Carbon-shaped cache vocabulary plus the exact-owner compare-and-swap required
226
+ by staged JavaScript reload. The old `Has`, `GetCount`, and `DeleteAll` names
227
+ remain temporary compatibility aliases.
228
+
229
+ Byte budgeting applies only to records explicitly admitted with
230
+ `{ cached: true, bytes }`; JavaScript reachability is never inferred. The byte
231
+ value is a caller-supplied safe-integer eviction weight, not a heuristic walk of
232
+ the resource graph. `TrimCache()` removes positive-byte cached identities in
233
+ oldest-admission order until `cacheBytes <= cacheSize`. Live, locked,
234
+ `cacheable: false`, and zero-byte entries do not create pressure. With default
235
+ cleanup, successful pressure eviction performs the same deterministic
236
+ payload/adapter cleanup as inactivity eviction and marks detached compatible
237
+ handles `PURGED`.
238
+
239
+ `SetCacheSize()` installs and immediately enforces a new budget. `Update()` and
240
+ `Tick()` retry cache housekeeping after pumping queues; `{ cache: false }`
241
+ skips it for one update. Cleanup failure leaves that candidate canonical,
242
+ continues through later candidates, and throws
243
+ `CJS_MOTHERLODE_CACHE_TRIM_FAILED` with a partial result.
244
+
245
+ `CjsResMan` binds resource-facing `KeepAlive`, `KeepPayloadAlive`, `Lock`, and
246
+ `Unlock` operations to the canonical key. Publishing a non-null payload renews
247
+ its independent lease; `GetPayload()`, `HasPayload()`, `IsGood()`, and other
248
+ queries remain pure. `PurgeInactive(options)` performs an explicit deterministic
249
+ sweep using independent identity and payload frame/time limits. Locks skip both
250
+ forms of eviction. Identity expiry cleans adapters and payloads, detaches the
251
+ handle, marks it `PURGED`, and removes it; payload expiry releases only the CPU
252
+ payload. A sweep never fetches, prepares, or reloads a resource.
253
+
254
+ Generic/base reader results participate in the same payload ownership: the
255
+ manager stores the complete result through `SetPayload()` and mirrors it on the
256
+ compatibility `object` property. Payload release clears that alias only while
257
+ it still identifies the released value. Concurrent object/readiness calls
258
+ share only their in-flight operation; settled promises are removed so evicted
259
+ graphs are collectible and failed operations can be explicitly retried. A
260
+ resident payload returns without rereading, while a released payload is rebuilt
261
+ only by an explicit `GetObject()` or `Ready()` call.
262
+
263
+ Source and parsed-format caches use explicit provenance. `sourceRevision` is an
264
+ opaque caller/source-supplied string or finite number identifying source
265
+ content for one source object and normalized path. It scopes read caches only;
266
+ it does not alter MotherLode resource identity, and changing it does not replace a
267
+ resident payload without `reload: true`.
268
+
269
+ `cacheSource` and `cacheFormat` are tri-state per-call policies:
270
+
271
+ - omitted: share in-flight or explicitly retained work, then drop a newly
272
+ completed record;
273
+ - `true`: share and retain success; a joining caller upgrades the record;
274
+ - `false`: bypass sharing and retention.
275
+
276
+ Failures are never retained. Format records are additionally isolated by
277
+ selected source object, frozen registration descriptor, revision, and effective
278
+ format options. Re-registering a format with new defaults therefore cannot
279
+ reuse an old descriptor's parse. Registered defaults are copied into deeply
280
+ frozen plain-object/array snapshots. Material format options that cannot be
281
+ represented safely (for example class instances with hidden mutable state)
282
+ bypass format-cache sharing instead of risking a false match; functions and
283
+ byte views use cache-local identity plus visible byte content where applicable.
284
+
285
+ `reload: true` synchronously detaches every queued/source/format read record for
286
+ the selected source/path before fresh work starts. Existing consumers keep
287
+ their detached promises; reload does not abort them. Fresh success repopulates
288
+ only caches explicitly requested with `cacheSource: true` or
289
+ `cacheFormat: true`. `InvalidateReadCache(path, { source, sourceRevision })`
290
+ provides the same no-abort invalidation explicitly; omitting `sourceRevision`
291
+ removes all revisions for that source/path. `Delete()` remains canonical
292
+ resource-identity-only, while `Clear()` resets all read ledgers.
293
+
294
+ A resource loader retains the effective selected source and `sourceRevision`
295
+ for reconstruction, including the manager default selected at creation, but
296
+ not cache flags or one-shot reload.
297
+
298
+ Reload is candidate-first. When an owner already exists,
299
+ `GetResource(path, { reload: true })` returns a distinct off-registry candidate
300
+ without changing ordinary lookup. `Ready()` on that candidate, `GetObject()` /
301
+ `FetchResource()` with `reload: true`, and the explicit `ReloadObject()` /
302
+ `ReloadResource()` helpers all run the same queued contract:
303
+
304
+ 1. purge-lock the exact former owner and invalidate reusable reads once;
305
+ 2. read, convert through the selected format, and publish payload state only on the detached candidate;
306
+ 3. require the newest per-key reload token and exact former ownership;
307
+ 4. compare-and-swap the fully loaded CPU candidate into MotherLode;
308
+ 5. invalidate and clean the displaced handle after the lookup switch.
309
+
310
+ Source, format, or publication failure therefore leaves the former handle, state,
311
+ payload, and adapters canonical; the failed candidate's attached payload and
312
+ adapters are cleaned and its original error is retained. An otherwise-
313
+ successful candidate that was superseded, deleted, cleared, or replaced rejects
314
+ with `CJS_RESMAN_STALE_RELOAD_CANDIDATE` and cannot resurrect the key. Failed
315
+ freshness attempts still invalidate reusable source/format records when their
316
+ work begins; the already-published canonical payload is not dependent on those
317
+ records. If displaced-owner cleanup fails after the swap,
318
+ the promise rejects with `CJS_MOTHERLODE_REPLACE_CLEANUP_FAILED`, whose result
319
+ explicitly reports `committed: true`; the good candidate remains canonical.
320
+
321
+ This deliberately differs from Carbon's `BlueAsyncRes::Reload`, which reloads
322
+ one stable handle in place and releases its old data before success. Existing
323
+ JavaScript references likewise are never silently retargeted: they keep the
324
+ displaced handle, while fresh lookup sees the committed candidate.
325
+
326
+ Every queued, direct, standalone, and candidate resource preparation captures the exact
327
+ MotherLode, canonical key, resource handle, and manager-local ownership
328
+ generation. Delete, Clear, reload replacement, or handle reinsertion makes old
329
+ work stale before it can enter another state or publish. Otherwise-
330
+ successful obsolete work rejects with `CJS_RESMAN_STALE_RESOURCE_OPERATION`;
331
+ an obsolete source/format failure preserves its original rejection while
332
+ suppressing `SetError()` on the detached handle.
333
+
334
+ Candidate work is a normal `Wait()` root and blocks synchronous MotherLode
335
+ replacement while active. MotherLode replacement otherwise remains synchronous
336
+ configuration and rejects with
337
+ `CJS_RESMAN_ACTIVE_RESOURCE_OPERATIONS` while queued or direct mutations are
338
+ active. `Wait()` drains queued roots; callers must separately await direct load
339
+ or direct prepare promises before retrying replacement. Started source or
340
+ format work is not yet aborted; deterministic cleanup applies to the staged
341
+ candidate resource itself.
342
+
343
+ Automatic scheduling is available only when a caller supplies
344
+ `autoPurgePolicy` to the constructor/`Register()` or calls
345
+ `SetAutoPurgePolicy()`. It is disabled by default and deliberately accepts only
346
+ millisecond limits: MotherLode activity frames count explicit observations and
347
+ are not renderer frames. `Update()`/`Tick()` run a due sweep after queue pumps;
348
+ `{ purge: false }` skips it for one call. The first pump after configuration is
349
+ due immediately, then `intervalMilliseconds` limits cadence. Manager-owned
350
+ queued and direct resource work holds a balanced purge lock until completion.
351
+
352
+ ```js
353
+ const resMan = new CjsResMan({
354
+ source,
355
+ autoPurgePolicy: {
356
+ intervalMilliseconds: 1000,
357
+ maxIdleMilliseconds: 60_000,
358
+ payloadMaxIdleMilliseconds: 10_000
359
+ }
360
+ });
361
+
362
+ resMan.Update();
363
+ ```
364
+
365
+ Cache trimming and automatic inactivity sweeps retain the strict no-reload
366
+ rule. Application retention defaults, automatic resource/payload byte
367
+ estimation, separate CPU/adapter budgets, and purged-resource/device-loss
368
+ recovery policy remain later work.
369
+
370
+ ## Queued CPU load and publication
371
+
372
+ `GetObject()`, `LoadObject()`, and resource `Ready()` use two manager-owned
373
+ queues:
374
+
375
+ ```text
376
+ BACKGROUND: deduplicated source load, limited by maxConcurrentLoads
377
+ MAIN: reader/format conversion -> resource publication
378
+ ```
379
+
380
+ The main reader/format operation and publication are separate queue items.
381
+ `maxPrepareTime` is a per-pump budget in seconds, and
382
+ `maxPrepareItemsPerTick` can add an item-count limit. The default scheduler
383
+ keeps promise-based calls working; a `CjsLibrary` or direct caller can provide
384
+ its frame scheduler:
385
+
386
+ ```js
387
+ const resMan = new CjsResMan({
388
+ source,
389
+ maxConcurrentLoads: 8,
390
+ maxPrepareTime: 0.005,
391
+ queueScheduler: callback => requestAnimationFrame(callback)
392
+ });
393
+
394
+ await resMan.FetchResource("res:/model/ship.gr2", {
395
+ requirement: "geometry",
396
+ emit: "cmf"
397
+ });
398
+ ```
399
+
400
+ The selected format class owns conversion to the promised CPU output.
401
+ `CjsResMan` does not inspect WebGL, WebGPU, texture, geometry, or codec support,
402
+ and it does not run backend realization. An engine consumes the published CPU
403
+ resource afterward through its own explicit operation.
404
+
405
+ Blue-compatible queue controls are exposed directly on `CjsResMan`:
406
+ `AddToQueue`, `CancelFromQueue`, `GetNextIdForQueue`,
407
+ `PumpMainThreadQueue`, `PauseQueue`, `ResumeQueue`, `GetPendingLoads`, and
408
+ `GetPendingPrepares`. `Update()`/`Tick()` pump work. `Wait()` synchronously
409
+ captures queued resource-operation roots and low-level queue tasks that already
410
+ exist when it is called. Captured resource roots include publication work
411
+ enqueued after an asynchronous read; unrelated later roots/tasks do not
412
+ postpone the fence. Failure and queued cancellation count as settlement and remain
413
+ observable through their original operation promises.
414
+
415
+ By default `Wait()` pumps the two queues directly within their ordinary budgets
416
+ and never runs automatic purge housekeeping. It preserves pause state;
417
+ `{ pump: false }` leaves all progress to an external driver. A standalone
418
+ canonical `PrepareResourceObjectQueued()` call is a queued root. Direct
419
+ `LoadResourceObject()`, direct `PrepareResourceObject()`, standalone
420
+ `ReadResource()`, and standalone `ReadFormatOnce()` calls bypass both queues
421
+ and are outside this fence unless they own a captured queue task, although
422
+ direct resource mutations are still tracked for safe MotherLode replacement.
423
+ `WaitUrgent()` remains deferred until the queue has real per-item priority and
424
+ urgent-membership semantics.
425
+
426
+ Format classes own input extensions. Resource classes are registered by a
427
+ semantic requirement, never by file extension:
428
+
429
+ ```js
430
+ const resMan = new CjsResMan().Register({
431
+ source,
432
+ formats: [ CjsDdsFormat, CjsPngFormat ],
433
+ resourceTypes: [ TriTextureRes, Tr2ImageRes ]
434
+ });
435
+
436
+ const texture = resMan.GetResource("res:/image/ship.png", {
437
+ requirement: "texture",
438
+ emit: "image"
439
+ });
440
+ const image = resMan.GetResource("res:/image/ship.png", {
441
+ requirement: "image",
442
+ emit: "image"
443
+ });
444
+ ```
445
+
446
+ Those are distinct resource identities but share the normalized source-byte
447
+ operation. The manager does not expose an extension-to-resource compatibility
448
+ registry.
449
+
450
+ Texture-array resources expose one ordinary-looking proxy per ordered layer:
451
+
452
+ ```js
453
+ const textureArray = new CjsTextureArrayRes({
454
+ paths: [
455
+ "res:/detail1.dds",
456
+ "res:/detail2.dds",
457
+ "res:/detail3.dds"
458
+ ],
459
+ layerNames: [ "Detail1Map", "Detail2Map", "Detail3Map" ],
460
+ updateScheduler: resource => frameQueue.add(resource)
461
+ });
462
+
463
+ const detail2 = textureArray.GetLayerParameter(1);
464
+ detail2.SetValue("res:/replacement.dds");
465
+
466
+ detail2.textureRes === textureArray; // true
467
+ ```
468
+
469
+ Proxy setters only update their source path and invalidate the parent. The
470
+ parent is scheduled once even if several proxies change in the same frame.
471
+ The next-frame consumer calls `Update()` or `ConsumeUpdateRequest()` to obtain
472
+ one immutable ordered snapshot. Runtime-resource does not know which shader
473
+ metadata caused the aggregate request; shader packages and engine adapters map
474
+ public parameter names to layer indices.
475
+
476
+ Public effect parameters remain separate from these internal proxies. Their
477
+ authored paths and individual 2D source resources are not replaced by the
478
+ aggregate. An engine-owned, non-persisted bridge mirrors public changes into
479
+ the fixed internal layers.
480
+
481
+ Consumed snapshots are explicit in-flight generations. An adapter either
482
+ publishes the current candidate atomically, requeues retryable work, or records
483
+ failure:
484
+
485
+ ```js
486
+ const request = textureArray.ConsumeUpdateRequest();
487
+
488
+ try {
489
+ const candidate = await adapter.PrepareTextureArray(request);
490
+ const result = textureArray.CommitPreparedAdapterRevision(
491
+ request.revision,
492
+ "webgpu",
493
+ candidate
494
+ );
495
+
496
+ // A rejected/stale candidate is destroyed by the commit method by default.
497
+ // The adapter owns disposal of a successfully displaced allocation.
498
+ result.displaced?.destroy();
499
+ } catch (error) {
500
+ textureArray.FailUpdateRequest(request.revision, error, { retry: true });
501
+ }
502
+
503
+ await textureArray.Ready(); // the generation requested at call time
504
+ ```
505
+
506
+ `SetLayerResource()` attaches a resolved source without rewriting the logical
507
+ requested path. `TouchLayer()` invalidates an in-place source revision.
508
+ `RetryUpdateRequest()` restores consumed work, and `HandleAdapterLoss()` drops
509
+ an unusable adapter allocation and schedules a complete topology rebuild.
510
+ Topology-changing snapshots set `topologyChanged: true` and report only valid
511
+ current layer indices in `dirtyLayers`.
512
+
513
+ ## Development
514
+
515
+ Install dependencies and run the non-interactive baseline checks from the
516
+ repository root:
517
+
518
+ ```sh
519
+ npm install
520
+ npm run lint
521
+ npm run check
522
+ npm test
523
+ ```
524
+
525
+ `npm run check` builds the consumer package and proves that decorator metadata
526
+ matches between authoring source and built output. `npm test` additionally runs
527
+ the complete GPU-free unit suite; it requires no private assets, credentials,
528
+ network access, browser, or GPU after dependencies are installed.
529
+
530
+ See [Runtime Resource Lifecycle](resource-lifecycle.md) for state, retention,
531
+ and texture-array generation contracts. See
532
+ [Format ownership and fork provenance](FORMAT-PROVENANCE.md) for copied-reader
533
+ ownership, licenses, exclusions, and the deferred GR2 migration. Both documents
534
+ ship with the published package.
535
+
536
+ ## Provenance
537
+
538
+ CarbonEngine and Fenris Creations (CCP Games) are named for interoperability
539
+ and provenance context. This package contains CarbonEngineJS original resource
540
+ infrastructure, CarbonEngine-shaped resource ports, and maintained copies of
541
+ the non-shader readers identified in `FORMAT-PROVENANCE.md`. It does not copy
542
+ Fenris Creations game assets, proprietary documentation, or shader source.
543
+ CarbonEngine and historical JavaScript implementations were used as the
544
+ behavioral references described in the package notices.
545
+
546
+ This project is not affiliated with, endorsed by, or sponsored by CCP Games or
547
+ CCP ehf. EVE Online and related marks remain the property of their respective
548
+ owners.