@carbonenginejs/runtime-resource 0.12.2 → 0.13.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 (541) hide show
  1. package/LICENSE +21 -21
  2. package/NOTICE +32 -32
  3. package/README.md +129 -108
  4. package/dist/CjsMotherLode.js +1271 -1271
  5. package/dist/CjsResMan.js +3858 -3494
  6. package/dist/CjsResMan.js.map +1 -1
  7. package/dist/CjsResManFetchProvider.js +94 -94
  8. package/dist/CjsResManWorkQueue.js +301 -301
  9. package/dist/_virtual/_rollupPluginBabelHelpers.js +150 -150
  10. package/dist/format/CjsBlueReader.js +358 -358
  11. package/dist/format/CjsByteReader.js +310 -310
  12. package/dist/format/CjsByteWriter.js +242 -242
  13. package/dist/format/CjsFormat.js +223 -223
  14. package/dist/format/CjsFormatError.js +41 -41
  15. package/dist/format/CjsReader.js +22 -22
  16. package/dist/format/CjsResourceProbe.js +279 -279
  17. package/dist/format/CjsStringTable.js +268 -268
  18. package/dist/format/carbonEffect/CjsCarbonEffectReader.js +408 -361
  19. package/dist/format/carbonEffect/CjsCarbonEffectReader.js.map +1 -1
  20. package/dist/format/carbonEffect/CjsCarbonEffectWriter.js +373 -373
  21. package/dist/format/carbonEffect/buildCarbonEffectContainer.js +182 -0
  22. package/dist/format/carbonEffect/buildCarbonEffectContainer.js.map +1 -0
  23. package/dist/format/carbonEffect/carbonEffectBackendBlock.js +321 -427
  24. package/dist/format/carbonEffect/carbonEffectBackendBlock.js.map +1 -1
  25. package/dist/format/carbonEffect/carbonEffectRecords.js +1147 -955
  26. package/dist/format/carbonEffect/carbonEffectRecords.js.map +1 -1
  27. package/dist/format/carbonEffect/carbonEffectResourceTransform.js +197 -0
  28. package/dist/format/carbonEffect/carbonEffectResourceTransform.js.map +1 -0
  29. package/dist/format/compareUtf8.js +36 -36
  30. package/dist/format/effect/effectBodyInventory.js +146 -0
  31. package/dist/format/effect/effectBodyInventory.js.map +1 -0
  32. package/dist/format/effect/effectPermutationGraph.js +257 -257
  33. package/dist/format/effect/effectPermutationGraph.js.map +1 -1
  34. package/dist/format/effect/sha256.js +114 -114
  35. package/dist/format/index.js +11 -11
  36. package/dist/format/payloadContract.js +193 -193
  37. package/dist/formats/black/CjsBlackFormat.js +310 -292
  38. package/dist/formats/black/CjsBlackFormat.js.map +1 -1
  39. package/dist/formats/black/core/CjsBlackBinaryReader.js +260 -260
  40. package/dist/formats/black/core/CjsBlackPropertyReaders.js +448 -448
  41. package/dist/formats/black/core/CjsBlackReader.js +764 -764
  42. package/dist/formats/black/core/CjsBlackSchemaRegistry.js +540 -540
  43. package/dist/formats/black/core/black-schema-v1-2026-07-23.json.js +4 -4
  44. package/dist/formats/black/core/blackConstants.js +7 -7
  45. package/dist/formats/black/core/blackDefinitions.js +10 -10
  46. package/dist/formats/black/core/blackEnums.js +6 -6
  47. package/dist/formats/black/core/blackSchema.js +3 -3
  48. package/dist/formats/black/core/blackVersion.js +22 -22
  49. package/dist/formats/black/core/helpers.js +199 -199
  50. package/dist/formats/black/core/schema.js +4 -4
  51. package/dist/formats/black/index.js +2 -2
  52. package/dist/formats/bnk/CjsBnkFormat.js +175 -156
  53. package/dist/formats/bnk/CjsBnkFormat.js.map +1 -1
  54. package/dist/formats/bnk/core/busNodes.js +252 -0
  55. package/dist/formats/bnk/core/busNodes.js.map +1 -0
  56. package/dist/formats/bnk/core/effectNodes.js +147 -0
  57. package/dist/formats/bnk/core/effectNodes.js.map +1 -0
  58. package/dist/formats/bnk/core/eventAction.js +416 -305
  59. package/dist/formats/bnk/core/eventAction.js.map +1 -1
  60. package/dist/formats/bnk/core/globalSettings.js +215 -0
  61. package/dist/formats/bnk/core/globalSettings.js.map +1 -0
  62. package/dist/formats/bnk/core/graph.js +137 -137
  63. package/dist/formats/bnk/core/helpers.js +512 -480
  64. package/dist/formats/bnk/core/helpers.js.map +1 -1
  65. package/dist/formats/bnk/core/musicNodes.js +533 -509
  66. package/dist/formats/bnk/core/musicNodes.js.map +1 -1
  67. package/dist/formats/bnk/core/nodeBase.js +553 -532
  68. package/dist/formats/bnk/core/nodeBase.js.map +1 -1
  69. package/dist/formats/bnk/core/sfxNodes.js +632 -632
  70. package/dist/formats/bnk/core/soundbanksInfo.js +209 -209
  71. package/dist/formats/bnk/index.js +2 -2
  72. package/dist/formats/cmf/CjsCmfFormat.js +497 -497
  73. package/dist/formats/cmf/core/binary.js +194 -194
  74. package/dist/formats/cmf/core/buffers.js +237 -237
  75. package/dist/formats/cmf/core/constants.js +47 -47
  76. package/dist/formats/cmf/core/gr2Anim.js +453 -453
  77. package/dist/formats/cmf/core/helpers.js +318 -318
  78. package/dist/formats/cmf/core/pack.js +276 -276
  79. package/dist/formats/cmf/core/schema.js +374 -374
  80. package/dist/formats/cmf/core/shared.js +277 -277
  81. package/dist/formats/cmf/core/writer.js +571 -571
  82. package/dist/formats/cmf/index.js +2 -2
  83. package/dist/formats/dds/CjsDdsFormat.js +200 -200
  84. package/dist/formats/dds/core/bc6h.js +298 -298
  85. package/dist/formats/dds/core/bc7.js +272 -272
  86. package/dist/formats/dds/core/helpers.js +862 -862
  87. package/dist/formats/dds/index.js +2 -2
  88. package/dist/formats/dxbc/CjsDxbcFormat.js +142 -142
  89. package/dist/formats/dxbc/core/DxbcReader.js +266 -266
  90. package/dist/formats/dxbc/core/container.js +169 -169
  91. package/dist/formats/dxbc/core/decoder.js +789 -789
  92. package/dist/formats/dxbc/core/errors.js +19 -19
  93. package/dist/formats/dxbc/core/helpers.js +220 -220
  94. package/dist/formats/dxbc/core/opcodes.js +45 -45
  95. package/dist/formats/dxbc/core/program.js +91 -91
  96. package/dist/formats/dxbc/core/signature.js +172 -172
  97. package/dist/formats/dxbc/index.js +2 -2
  98. package/dist/formats/fbx/CjsFbxFormat.js +266 -266
  99. package/dist/formats/fbx/core/helpers.js +3932 -3932
  100. package/dist/formats/fbx/index.js +2 -2
  101. package/dist/formats/flac/CjsFlacFormat.js +142 -142
  102. package/dist/formats/flac/core/helpers.js +315 -315
  103. package/dist/formats/flac/index.js +2 -2
  104. package/dist/formats/gif/CjsGifFormat.js +141 -141
  105. package/dist/formats/gif/core/helpers.js +380 -380
  106. package/dist/formats/gif/index.js +2 -2
  107. package/dist/formats/gltf/CjsGltfFormat.js +290 -290
  108. package/dist/formats/gltf/core/helpers.js +307 -307
  109. package/dist/formats/gltf/core/json.js +79 -79
  110. package/dist/formats/gltf/core/parser.js +679 -679
  111. package/dist/formats/gltf/core/targets.js +173 -173
  112. package/dist/formats/gltf/index.js +2 -2
  113. package/dist/formats/gr2/CjsGr2Format.js +289 -289
  114. package/dist/formats/gr2/core/bitknit2.js +282 -282
  115. package/dist/formats/gr2/core/curves.js +1047 -1047
  116. package/dist/formats/gr2/core/gsf.js +72 -72
  117. package/dist/formats/gr2/core/helpers.js +352 -352
  118. package/dist/formats/gr2/core/json.js +622 -622
  119. package/dist/formats/gr2/core/oodle1.js +388 -388
  120. package/dist/formats/gr2/core/tangents.js +48 -48
  121. package/dist/formats/gr2/core/targets.js +361 -361
  122. package/dist/formats/gr2/index.js +2 -2
  123. package/dist/formats/hlsl/CjsHlslFormat.js +254 -254
  124. package/dist/formats/hlsl/core/HlslBinaryUtils.js +15 -15
  125. package/dist/formats/hlsl/core/HlslEffectReadError.js +19 -19
  126. package/dist/formats/hlsl/core/HlslEffectStateManager.js +130 -130
  127. package/dist/formats/hlsl/core/HlslRenderStateSetup.js +37 -37
  128. package/dist/formats/hlsl/core/HlslResourceSetDescription.js +94 -94
  129. package/dist/formats/hlsl/core/HlslShaderBytecode.js +44 -44
  130. package/dist/formats/hlsl/core/analysis.js +51 -51
  131. package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js +850 -781
  132. package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js.map +1 -1
  133. package/dist/formats/hlsl/core/detailMapFamily.js +128 -0
  134. package/dist/formats/hlsl/core/detailMapFamily.js.map +1 -0
  135. package/dist/formats/hlsl/core/helpers.js +270 -270
  136. package/dist/formats/hlsl/core/json.js +284 -284
  137. package/dist/formats/hlsl/core/localLightFamily.js +133 -0
  138. package/dist/formats/hlsl/core/localLightFamily.js.map +1 -0
  139. package/dist/formats/hlsl/core/metadata.js +327 -327
  140. package/dist/formats/hlsl/core/render-states.js +280 -280
  141. package/dist/formats/hlsl/core/tr2/HlslRenderContextEnum.js +43 -43
  142. package/dist/formats/hlsl/core/tr2/resources/HlslEffectRes.js +320 -314
  143. package/dist/formats/hlsl/core/tr2/resources/HlslEffectRes.js.map +1 -1
  144. package/dist/formats/hlsl/core/tr2/resources/HlslShaderPermutation.js +33 -33
  145. package/dist/formats/hlsl/core/tr2/shader/HlslEffectBindingManifest.js +416 -416
  146. package/dist/formats/hlsl/core/tr2/shader/HlslEffectConstant.js +40 -40
  147. package/dist/formats/hlsl/core/tr2/shader/HlslEffectDescription.js +62 -742
  148. package/dist/formats/hlsl/core/tr2/shader/HlslEffectDescription.js.map +1 -1
  149. package/dist/formats/hlsl/core/tr2/shader/HlslEffectLibrary.js +52 -52
  150. package/dist/formats/hlsl/core/tr2/shader/HlslEffectParameterAnnotation.js +38 -38
  151. package/dist/formats/hlsl/core/tr2/shader/HlslEffectResource.js +50 -50
  152. package/dist/formats/hlsl/core/tr2/shader/HlslEffectStageInput.js +86 -86
  153. package/dist/formats/hlsl/core/tr2/shader/HlslEffectTechnique.js +31 -31
  154. package/dist/formats/hlsl/core/tr2/shader/HlslPass.js +42 -42
  155. package/dist/formats/hlsl/core/tr2/shader/HlslSamplerDescription.js +55 -55
  156. package/dist/formats/hlsl/core/tr2/shader/HlslSamplerSetup.js +29 -29
  157. package/dist/formats/hlsl/core/tr2/shader/HlslShader.js +220 -220
  158. package/dist/formats/hlsl/core/tr2/shader/HlslShaderOption.js +30 -30
  159. package/dist/formats/hlsl/index.js +3 -3
  160. package/dist/formats/index.js +33 -31
  161. package/dist/formats/index.js.map +1 -1
  162. package/dist/formats/jpeg/CjsJpegFormat.js +212 -212
  163. package/dist/formats/jpeg/core/helpers.js +402 -402
  164. package/dist/formats/jpeg/core/jpeg.js +480 -480
  165. package/dist/formats/jpeg/index.js +2 -2
  166. package/dist/formats/mp3/CjsMp3Format.js +197 -197
  167. package/dist/formats/mp3/core/helpers.js +375 -375
  168. package/dist/formats/mp3/index.js +2 -2
  169. package/dist/formats/mp4/CjsMp4Format.js +197 -197
  170. package/dist/formats/mp4/core/helpers.js +484 -484
  171. package/dist/formats/mp4/index.js +2 -2
  172. package/dist/formats/obj/CjsObjFormat.js +253 -253
  173. package/dist/formats/obj/core/helpers.js +573 -573
  174. package/dist/formats/obj/core/json.js +64 -64
  175. package/dist/formats/obj/core/parser.js +321 -321
  176. package/dist/formats/obj/index.js +2 -2
  177. package/dist/formats/ogg/CjsOggFormat.js +143 -143
  178. package/dist/formats/ogg/core/helpers.js +410 -410
  179. package/dist/formats/ogg/core/imdct.js +178 -178
  180. package/dist/formats/ogg/core/vorbis.js +1017 -1017
  181. package/dist/formats/ogg/index.js +2 -2
  182. package/dist/formats/pickle/CjsPickleFormat.js +214 -0
  183. package/dist/formats/pickle/CjsPickleFormat.js.map +1 -0
  184. package/dist/formats/pickle/core/CjsPickleProtocol0Reader.js +551 -0
  185. package/dist/formats/pickle/core/CjsPickleProtocol0Reader.js.map +1 -0
  186. package/dist/formats/pickle/index.js +2 -0
  187. package/dist/formats/pickle/index.js.map +1 -0
  188. package/dist/formats/png/CjsPngFormat.js +201 -201
  189. package/dist/formats/png/core/helpers.js +635 -635
  190. package/dist/formats/png/index.js +2 -2
  191. package/dist/formats/red/CjsRedFormat.js +263 -263
  192. package/dist/formats/red/core/CjsRedReader.js +246 -246
  193. package/dist/formats/red/core/blackDefinitions.js +3 -3
  194. package/dist/formats/red/core/helpers.js +158 -158
  195. package/dist/formats/red/core/redGraph.js +71 -68
  196. package/dist/formats/red/core/redGraph.js.map +1 -1
  197. package/dist/formats/red/core/schema.js +4 -4
  198. package/dist/formats/red/index.js +2 -2
  199. package/dist/formats/stl/CjsStlFormat.js +365 -365
  200. package/dist/formats/stl/core/helpers.js +261 -261
  201. package/dist/formats/stl/core/json.js +51 -51
  202. package/dist/formats/stl/core/stl.js +642 -642
  203. package/dist/formats/stl/core/targets.js +173 -173
  204. package/dist/formats/stl/index.js +2 -2
  205. package/dist/formats/tga/CjsTgaFormat.js +197 -197
  206. package/dist/formats/tga/core/helpers.js +493 -493
  207. package/dist/formats/tga/index.js +2 -2
  208. package/dist/formats/wav/CjsWavFormat.js +198 -198
  209. package/dist/formats/wav/core/helpers.js +365 -365
  210. package/dist/formats/wav/index.js +2 -2
  211. package/dist/formats/webgl/CjsWebglFormat.js +199 -221
  212. package/dist/formats/webgl/CjsWebglFormat.js.map +1 -1
  213. package/dist/formats/webgl/core/buildGlslEffectContainer.js +70 -0
  214. package/dist/formats/webgl/core/buildGlslEffectContainer.js.map +1 -0
  215. package/dist/formats/webgl/core/effectPackage.js +900 -792
  216. package/dist/formats/webgl/core/effectPackage.js.map +1 -1
  217. package/dist/formats/webgl/core/errors.js +27 -27
  218. package/dist/formats/webgl/core/errors.js.map +1 -1
  219. package/dist/formats/webgl/core/glsl/DxbcGlslEmitter.js +2820 -2678
  220. package/dist/formats/webgl/core/glsl/DxbcGlslEmitter.js.map +1 -1
  221. package/dist/formats/webgl/core/glsl/DxbcGlslHelpers.js +89 -89
  222. package/dist/formats/webgl/core/glsl/DxbcGlslOperandFormatter.js +486 -428
  223. package/dist/formats/webgl/core/glsl/DxbcGlslOperandFormatter.js.map +1 -1
  224. package/dist/formats/webgl/core/glsl/packedLightFixups.js +98 -0
  225. package/dist/formats/webgl/core/glsl/packedLightFixups.js.map +1 -0
  226. package/dist/formats/webgl/core/glslBackendBlock.js +552 -0
  227. package/dist/formats/webgl/core/glslBackendBlock.js.map +1 -0
  228. package/dist/formats/webgl/core/glslBackendBodySet.js +243 -0
  229. package/dist/formats/webgl/core/glslBackendBodySet.js.map +1 -0
  230. package/dist/formats/webgl/core/{cewgCompleteness.js → glslEffectCompleteness.js} +85 -81
  231. package/dist/formats/webgl/core/glslEffectCompleteness.js.map +1 -0
  232. package/dist/formats/webgl/core/helpers.js +167 -306
  233. package/dist/formats/webgl/core/helpers.js.map +1 -1
  234. package/dist/formats/webgl/core/inspectGlslEffectContainer.js +122 -0
  235. package/dist/formats/webgl/core/inspectGlslEffectContainer.js.map +1 -0
  236. package/dist/formats/webgl/core/readGlslEffectContainer.js +303 -0
  237. package/dist/formats/webgl/core/readGlslEffectContainer.js.map +1 -0
  238. package/dist/formats/webgl/index.js +2 -2
  239. package/dist/formats/webgpu/CjsWebgpuFormat.js +357 -357
  240. package/dist/formats/webgpu/CjsWebgpuFormat.js.map +1 -1
  241. package/dist/formats/webgpu/core/buildCarbonEffectContainer.js +89 -197
  242. package/dist/formats/webgpu/core/buildCarbonEffectContainer.js.map +1 -1
  243. package/dist/formats/webgpu/core/{cewgpu/CewgpuContainer.js → carbonWebgpu/CarbonWebgpuContainer.js} +354 -368
  244. package/dist/formats/webgpu/core/carbonWebgpu/CarbonWebgpuContainer.js.map +1 -0
  245. package/dist/formats/webgpu/core/{cewgpu → carbonWebgpu}/containerViews.js +355 -355
  246. package/dist/formats/webgpu/core/carbonWebgpu/containerViews.js.map +1 -0
  247. package/dist/formats/webgpu/core/{cewgpu → carbonWebgpu}/validateContainer.js +90 -90
  248. package/dist/formats/webgpu/core/carbonWebgpu/validateContainer.js.map +1 -0
  249. package/dist/formats/webgpu/core/effectAnalysis.js +82 -82
  250. package/dist/formats/webgpu/core/effectBackendBodySet.js +289 -289
  251. package/dist/formats/webgpu/core/effectBackendBodySet.js.map +1 -1
  252. package/dist/formats/webgpu/core/errors.js +20 -20
  253. package/dist/formats/webgpu/core/errors.js.map +1 -1
  254. package/dist/formats/webgpu/core/helpers.js +443 -443
  255. package/dist/formats/webgpu/core/helpers.js.map +1 -1
  256. package/dist/formats/webgpu/core/ir/analyzeRegisterValues.js +212 -212
  257. package/dist/formats/webgpu/core/ir/buildControlFlow.js +220 -220
  258. package/dist/formats/webgpu/core/ir/indexableTemps.js +137 -137
  259. package/dist/formats/webgpu/core/ir/inferValueTypes.js +449 -449
  260. package/dist/formats/webgpu/core/ir/lowerDxbcToIr.js +494 -494
  261. package/dist/formats/webgpu/core/ir/resolveRegisterFlow.js +177 -177
  262. package/dist/formats/webgpu/core/ir/sourceLanes.js +61 -61
  263. package/dist/formats/webgpu/core/packageEffect.js +381 -385
  264. package/dist/formats/webgpu/core/packageEffect.js.map +1 -1
  265. package/dist/formats/webgpu/core/packageEffectSelection.js +164 -164
  266. package/dist/formats/webgpu/core/packageEffectSelection.js.map +1 -1
  267. package/dist/formats/webgpu/core/packageMetadata.js +17 -17
  268. package/dist/formats/webgpu/core/schema.js +4 -4
  269. package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js +263 -263
  270. package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js.map +1 -1
  271. package/dist/formats/webgpu/core/wgsl/buildWgslBindingPlan.js +172 -172
  272. package/dist/formats/webgpu/core/wgsl/buildWgslSet.js +320 -320
  273. package/dist/formats/webgpu/core/wgsl/buildWgslSet.js.map +1 -1
  274. package/dist/formats/webgpu/core/wgsl/emitWgsl.js +356 -356
  275. package/dist/formats/webgpu/core/wgsl/hoistEscapingValues.js +77 -77
  276. package/dist/formats/webgpu/core/wgsl/lowerBindingLayout.js +451 -451
  277. package/dist/formats/webgpu/core/wgsl/lowerComputeProgram.js +735 -735
  278. package/dist/formats/webgpu/core/wgsl/lowerCreateHistogramsComputeProgram.js +457 -457
  279. package/dist/formats/webgpu/core/wgsl/lowerFragmentProgram.js +1572 -1572
  280. package/dist/formats/webgpu/core/wgsl/lowerMergeHistogramsComputeProgram.js +659 -659
  281. package/dist/formats/webgpu/core/wgsl/lowerParticleClearComputePrograms.js +734 -734
  282. package/dist/formats/webgpu/core/wgsl/lowerParticleEmitComputeProgram.js +583 -583
  283. package/dist/formats/webgpu/core/wgsl/lowerSkinVerticesComputeProgram.js +621 -621
  284. package/dist/formats/webgpu/core/wgsl/lowerSortComputeProgram.js +824 -824
  285. package/dist/formats/webgpu/core/wgsl/lowerSortInnerComputeProgram.js +697 -697
  286. package/dist/formats/webgpu/core/wgsl/lowerSortStepComputeProgram.js +559 -559
  287. package/dist/formats/webgpu/core/wgsl/lowerVertexProgram.js +1328 -1328
  288. package/dist/formats/webgpu/core/wgsl/particleEmitSemanticDigest.js +110 -110
  289. package/dist/formats/webgpu/core/wgsl/precisionControls.js +55 -55
  290. package/dist/formats/webgpu/core/wgsl/selectionPlans.js +717 -717
  291. package/dist/formats/webgpu/core/wgsl/uniformity.js +78 -78
  292. package/dist/formats/webgpu/core/wgsl/validateExactComputeIr.js +196 -196
  293. package/dist/formats/webgpu/core/wgsl/validateHandleOperand.js +37 -37
  294. package/dist/formats/webgpu/index.js +2 -2
  295. package/dist/formats/webm/CjsWebmFormat.js +197 -197
  296. package/dist/formats/webm/core/helpers.js +572 -572
  297. package/dist/formats/webm/index.js +2 -2
  298. package/dist/formats/webp/CjsWebpFormat.js +140 -140
  299. package/dist/formats/webp/core/helpers.js +237 -237
  300. package/dist/formats/webp/index.js +2 -2
  301. package/dist/formats/wem/CjsWemFormat.js +250 -250
  302. package/dist/formats/wem/core/bitStream.js +261 -261
  303. package/dist/formats/wem/core/codebookLibrary.js +164 -164
  304. package/dist/formats/wem/core/helpers.js +437 -437
  305. package/dist/formats/wem/core/packedCodebooksAotuv603.js +30 -30
  306. package/dist/formats/wem/core/ptadpcm.js +77 -77
  307. package/dist/formats/wem/core/resolve.js +121 -121
  308. package/dist/formats/wem/core/wemToOgg.js +485 -485
  309. package/dist/formats/wem/index.js +2 -2
  310. package/dist/formats/yaml/CjsYamlFormat.js +134 -134
  311. package/dist/formats/yaml/CjsYamlFormat.js.map +1 -1
  312. package/dist/formats/yaml/core/CjsYamlReader.js +400 -402
  313. package/dist/formats/yaml/core/CjsYamlReader.js.map +1 -1
  314. package/dist/formats/yaml/core/helpers.js +196 -170
  315. package/dist/formats/yaml/core/helpers.js.map +1 -1
  316. package/dist/formats/yaml/index.js +2 -2
  317. package/dist/index.js +64 -62
  318. package/dist/index.js.map +1 -1
  319. package/dist/resource/CjsLoadingObject.js +19 -0
  320. package/dist/resource/CjsLoadingObject.js.map +1 -0
  321. package/dist/resource/CjsResource.js +801 -797
  322. package/dist/resource/CjsResource.js.map +1 -1
  323. package/dist/resource/ResourceHandlerMode.js +14 -0
  324. package/dist/resource/ResourceHandlerMode.js.map +1 -0
  325. package/dist/resource/Tr2LightProfileRes.js +32 -32
  326. package/dist/resource/audio/AudioGeometryResData.js +47 -47
  327. package/dist/resource/audio/CjsAudioBufferRes.js +86 -86
  328. package/dist/resource/audio/CjsAudioRes.js +213 -213
  329. package/dist/resource/audio/index.js +4 -4
  330. package/dist/resource/geometry/MeshDecalData.js +37 -37
  331. package/dist/resource/geometry/MeshDecalLodData.js +34 -34
  332. package/dist/resource/geometry/TriGeometryRes.js +675 -675
  333. package/dist/resource/geometry/TriGeometryResAreaData.js +59 -59
  334. package/dist/resource/geometry/TriGeometryResJointData.js +38 -38
  335. package/dist/resource/geometry/TriGeometryResLodData.js +88 -88
  336. package/dist/resource/geometry/TriGeometryResMeshData.js +63 -63
  337. package/dist/resource/geometry/TriGeometryResSkeletonData.js +34 -34
  338. package/dist/resource/geometry/TriJointBinding.js +38 -38
  339. package/dist/resource/geometry/TriMorphTargetGeometryConstants.js +46 -46
  340. package/dist/resource/geometry/TriRtGeometryConstants.js +88 -88
  341. package/dist/resource/geometry/granny/GStateBindingCallbackData.js +31 -31
  342. package/dist/resource/geometry/granny/Tr2GrannyIntersectionResult.js +60 -60
  343. package/dist/resource/geometry/granny/Tr2GrannyStateRes.js +36 -36
  344. package/dist/resource/geometry/granny/TriGrannyRes.js +35 -35
  345. package/dist/resource/geometry/granny/enums.js +10 -10
  346. package/dist/resource/geometry/granny/index.js +6 -6
  347. package/dist/resource/geometry/index.js +17 -17
  348. package/dist/resource/index.js +54 -52
  349. package/dist/resource/index.js.map +1 -1
  350. package/dist/resource/resourceBoundary.js +64 -64
  351. package/dist/resource/shader/Tr2EffectRes.js +336 -431
  352. package/dist/resource/shader/Tr2EffectRes.js.map +1 -1
  353. package/dist/resource/shader/Tr2MaterialArea.js +31 -31
  354. package/dist/resource/shader/Tr2MaterialMesh.js +27 -27
  355. package/dist/resource/shader/Tr2MaterialRes.js +31 -31
  356. package/dist/resource/shader/Tr2Shader.js +283 -261
  357. package/dist/resource/shader/Tr2Shader.js.map +1 -1
  358. package/dist/resource/shader/Tr2ShaderPermutation.js +43 -43
  359. package/dist/resource/shader/index.js +17 -17
  360. package/dist/resource/shader/reflection/Tr2EffectConstant.js +143 -135
  361. package/dist/resource/shader/reflection/Tr2EffectConstant.js.map +1 -1
  362. package/dist/resource/shader/reflection/Tr2EffectDefine.js +30 -30
  363. package/dist/resource/shader/reflection/Tr2EffectDescription.js +114 -109
  364. package/dist/resource/shader/reflection/Tr2EffectDescription.js.map +1 -1
  365. package/dist/resource/shader/reflection/Tr2EffectLibrary.js +168 -147
  366. package/dist/resource/shader/reflection/Tr2EffectLibrary.js.map +1 -1
  367. package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js +125 -105
  368. package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js.map +1 -1
  369. package/dist/resource/shader/reflection/Tr2EffectResource.js +120 -86
  370. package/dist/resource/shader/reflection/Tr2EffectResource.js.map +1 -1
  371. package/dist/resource/shader/reflection/Tr2EffectStageInput.js +372 -241
  372. package/dist/resource/shader/reflection/Tr2EffectStageInput.js.map +1 -1
  373. package/dist/resource/shader/reflection/Tr2EffectTechnique.js +78 -68
  374. package/dist/resource/shader/reflection/Tr2EffectTechnique.js.map +1 -1
  375. package/dist/resource/shader/reflection/Tr2Pass.js +168 -111
  376. package/dist/resource/shader/reflection/Tr2Pass.js.map +1 -1
  377. package/dist/resource/shader/reflection/carbonRecordFields.js +159 -0
  378. package/dist/resource/shader/reflection/carbonRecordFields.js.map +1 -0
  379. package/dist/resource/shader/reflection/shaderStage.js +22 -0
  380. package/dist/resource/shader/reflection/shaderStage.js.map +1 -0
  381. package/dist/resource/shader/sampler/Tr2SamplerSetup.js +135 -77
  382. package/dist/resource/shader/sampler/Tr2SamplerSetup.js.map +1 -1
  383. package/dist/resource/texture/CjsTextureArrayRes.js +472 -472
  384. package/dist/resource/texture/CjsTextureArrayResParameterProxy.js +179 -179
  385. package/dist/resource/texture/Tr2ImageRes.js +120 -120
  386. package/dist/resource/texture/Tr2TextureLodManager.js +82 -82
  387. package/dist/resource/texture/Tr2TextureLodUpdateRequest.js +37 -37
  388. package/dist/resource/texture/Tr2TexturePackChannel.js +37 -37
  389. package/dist/resource/texture/Tr2TexturePipeline.js +54 -54
  390. package/dist/resource/texture/Tr2TexturePipelineParams.js +34 -34
  391. package/dist/resource/texture/Tr2TexturePipelineStepCompress.js +40 -40
  392. package/dist/resource/texture/Tr2TexturePipelineStepGenerateMips.js +22 -22
  393. package/dist/resource/texture/Tr2TexturePipelineStepLimitSize.js +34 -34
  394. package/dist/resource/texture/Tr2TexturePipelineStepLoad.js +31 -31
  395. package/dist/resource/texture/Tr2TexturePipelineStepPack.js +43 -43
  396. package/dist/resource/texture/TriTextureRes.js +359 -359
  397. package/dist/resource/texture/index.js +15 -15
  398. package/dist/resource/texture/texturePipelineBehavior.js +308 -308
  399. package/dist/worker/CjsResManMainThreadLoader.js +89 -87
  400. package/dist/worker/CjsResManMainThreadLoader.js.map +1 -1
  401. package/dist/worker/CjsResManWorker.js +218 -217
  402. package/dist/worker/CjsResManWorker.js.map +1 -1
  403. package/dist/worker/CjsResManWorkerLoader.js +437 -434
  404. package/dist/worker/CjsResManWorkerLoader.js.map +1 -1
  405. package/dist/worker/protocol.js +12 -12
  406. package/docs/README.md +98 -98
  407. package/docs/architecture.md +118 -109
  408. package/docs/concepts/resource-lifecycle.md +226 -225
  409. package/docs/concepts/shader-resource-model.md +111 -114
  410. package/docs/concepts/writing-an-engine-adapter.md +115 -115
  411. package/docs/formats/README.md +138 -136
  412. package/docs/formats/carbon-effect-container.md +553 -452
  413. package/docs/formats/dxbc/README.md +68 -68
  414. package/docs/formats/dxbc/architecture.md +80 -80
  415. package/docs/formats/dxbc/reference/api.md +77 -77
  416. package/docs/formats/dxbc/reference/classes/README.md +9 -9
  417. package/docs/formats/dxbc/reference/decoded-output.md +122 -122
  418. package/docs/formats/gr2.md +160 -160
  419. package/docs/formats/hlsl/README.md +54 -54
  420. package/docs/formats/hlsl/architecture.md +65 -67
  421. package/docs/formats/hlsl/guides/hydrating-json-output.md +60 -62
  422. package/docs/formats/hlsl/guides/reading-effects.md +64 -64
  423. package/docs/formats/hlsl/reference/advanced-analysis.md +61 -66
  424. package/docs/formats/hlsl/reference/api.md +92 -98
  425. package/docs/formats/hlsl/reference/classes/README.md +11 -11
  426. package/docs/formats/hlsl/reference/json-graph.md +97 -100
  427. package/docs/formats/pickle.md +82 -0
  428. package/docs/formats/provenance.md +196 -196
  429. package/docs/formats/stl.md +37 -37
  430. package/docs/formats/webgl/README.md +115 -57
  431. package/docs/formats/webgl/architecture.md +69 -70
  432. package/docs/formats/webgl/carbon-constant-layouts.md +326 -326
  433. package/docs/formats/webgl/decl-io.md +1234 -1234
  434. package/docs/formats/webgl/memory-structured.md +890 -871
  435. package/docs/formats/webgl/reference/classes/README.md +9 -9
  436. package/docs/formats/webgl/texture-sample.md +964 -964
  437. package/docs/formats/webgpu/README.md +84 -84
  438. package/docs/formats/webgpu/architecture.md +95 -96
  439. package/docs/formats/webgpu/formats/{cewgpu.md → carbon-webgpu.md} +215 -216
  440. package/docs/formats/webgpu/guides/effect-packaging.md +189 -191
  441. package/docs/formats/webgpu/reference/api.md +196 -197
  442. package/docs/formats/webgpu/reference/classes/README.md +9 -9
  443. package/docs/formats/webgpu/reference/wgsl-compatibility.md +1546 -1543
  444. package/docs/formats/wwise.md +146 -85
  445. package/docs/reference/classes/README.md +35 -35
  446. package/docs/reference/classes/audio.md +30 -30
  447. package/docs/reference/classes/core.md +216 -206
  448. package/docs/reference/classes/dropped.md +46 -46
  449. package/docs/reference/classes/formats.md +944 -952
  450. package/docs/reference/classes/resources.md +456 -456
  451. package/docs/reference/classes/texture.md +26 -26
  452. package/docs/reference/events.md +117 -117
  453. package/docs/reference/motherlode-cache.md +275 -275
  454. package/docs/reference/queues.md +194 -110
  455. package/docs/reference/reload.md +107 -107
  456. package/docs/reference/texture-arrays.md +113 -113
  457. package/docs/reference/texture-pipeline.md +53 -53
  458. package/docs/reference/workers.md +142 -135
  459. package/docs/roadmap.md +150 -150
  460. package/format-notices/black/LICENSE +21 -21
  461. package/format-notices/black/NOTICE +47 -47
  462. package/format-notices/bnk/LICENSE +21 -21
  463. package/format-notices/bnk/NOTICE +21 -21
  464. package/format-notices/cmf/LICENSE +21 -21
  465. package/format-notices/cmf/NOTICE +36 -36
  466. package/format-notices/dds/LICENSE +21 -21
  467. package/format-notices/dds/NOTICE +14 -14
  468. package/format-notices/dxbc/LICENSE +21 -21
  469. package/format-notices/dxbc/NOTICE +20 -20
  470. package/format-notices/fbx/LICENSE +21 -21
  471. package/format-notices/fbx/NOTICE +14 -14
  472. package/format-notices/flac/LICENSE +21 -21
  473. package/format-notices/flac/NOTICE +14 -14
  474. package/format-notices/gif/LICENSE +21 -21
  475. package/format-notices/gif/NOTICE +14 -14
  476. package/format-notices/gltf/LICENSE +21 -21
  477. package/format-notices/gltf/NOTICE +27 -27
  478. package/format-notices/gr2/LICENSE +21 -21
  479. package/format-notices/gr2/NOTICE +60 -60
  480. package/format-notices/gr2/THIRD-PARTY-NOTICES.md +93 -93
  481. package/format-notices/hlsl/LICENSE +21 -21
  482. package/format-notices/hlsl/NOTICE +25 -25
  483. package/format-notices/jpeg/LICENSE +21 -21
  484. package/format-notices/jpeg/NOTICE +14 -14
  485. package/format-notices/mp3/LICENSE +21 -21
  486. package/format-notices/mp3/NOTICE +14 -14
  487. package/format-notices/mp4/LICENSE +21 -21
  488. package/format-notices/mp4/NOTICE +14 -14
  489. package/format-notices/obj/LICENSE +21 -21
  490. package/format-notices/obj/NOTICE +26 -26
  491. package/format-notices/ogg/LICENSE +21 -21
  492. package/format-notices/ogg/NOTICE +28 -28
  493. package/format-notices/png/LICENSE +21 -21
  494. package/format-notices/png/NOTICE +14 -14
  495. package/format-notices/red/LICENSE +21 -21
  496. package/format-notices/red/NOTICE +31 -31
  497. package/format-notices/stl/LICENSE +21 -21
  498. package/format-notices/stl/NOTICE +21 -21
  499. package/format-notices/tga/LICENSE +21 -21
  500. package/format-notices/tga/NOTICE +14 -14
  501. package/format-notices/wav/LICENSE +21 -21
  502. package/format-notices/wav/NOTICE +14 -14
  503. package/format-notices/webgl/LICENSE +21 -21
  504. package/format-notices/webgl/NOTICE +35 -35
  505. package/format-notices/webgpu/LICENSE +21 -21
  506. package/format-notices/webgpu/NOTICE +31 -31
  507. package/format-notices/webm/LICENSE +21 -21
  508. package/format-notices/webm/NOTICE +14 -14
  509. package/format-notices/webp/LICENSE +21 -21
  510. package/format-notices/webp/NOTICE +14 -14
  511. package/format-notices/wem/LICENSE +57 -57
  512. package/format-notices/wem/NOTICE +33 -33
  513. package/format-notices/yaml/LICENSE +21 -21
  514. package/format-notices/yaml/NOTICE +44 -44
  515. package/package.json +63 -63
  516. package/dist/format/carbonEffect/carbonDescriptionFromPortable.js +0 -372
  517. package/dist/format/carbonEffect/carbonDescriptionFromPortable.js.map +0 -1
  518. package/dist/format/effect/effectReflectionPackage.js +0 -636
  519. package/dist/format/effect/effectReflectionPackage.js.map +0 -1
  520. package/dist/formats/hlsl/core/HlslReader.js +0 -16
  521. package/dist/formats/hlsl/core/HlslReader.js.map +0 -1
  522. package/dist/formats/hlsl/core/portableReflection.js +0 -848
  523. package/dist/formats/hlsl/core/portableReflection.js.map +0 -1
  524. package/dist/formats/hlsl/portable.js +0 -2
  525. package/dist/formats/hlsl/portable.js.map +0 -1
  526. package/dist/formats/webgl/core/cewg/CewgPackage.js +0 -340
  527. package/dist/formats/webgl/core/cewg/CewgPackage.js.map +0 -1
  528. package/dist/formats/webgl/core/cewg/CewgPackageBuilder.js +0 -104
  529. package/dist/formats/webgl/core/cewg/CewgPackageBuilder.js.map +0 -1
  530. package/dist/formats/webgl/core/cewg/binary.js +0 -19
  531. package/dist/formats/webgl/core/cewg/binary.js.map +0 -1
  532. package/dist/formats/webgl/core/cewgCompleteness.js.map +0 -1
  533. package/dist/formats/webgl/core/effectPackageValidation.js +0 -331
  534. package/dist/formats/webgl/core/effectPackageValidation.js.map +0 -1
  535. package/dist/formats/webgpu/core/cewgpu/CewgpuContainer.js.map +0 -1
  536. package/dist/formats/webgpu/core/cewgpu/containerViews.js.map +0 -1
  537. package/dist/formats/webgpu/core/cewgpu/validateContainer.js.map +0 -1
  538. package/dist/resource/shader/portable.js +0 -33
  539. package/dist/resource/shader/portable.js.map +0 -1
  540. package/docs/formats/hlsl/reference/portable-reflection.md +0 -141
  541. package/docs/formats/webgl/effect-reflection.md +0 -127
@@ -1,1271 +1,1271 @@
1
- import { normalizeResourcePath } from '@carbonenginejs/runtime-utils/path';
2
-
3
- // Source: blue/include/IMotherLode.h
4
- // Source: blue/src/MotherLode.h
5
- // Source: blue/src/MotherLode.cpp
6
- const DEFAULT_CACHE_SIZE = 32 * 1024 * 1024;
7
- const MAX_SAFE_INTEGER_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
8
-
9
- /**
10
- * Construction options for {@link CjsMotherLode}.
11
- *
12
- * @typedef {object} CjsMotherLodeOptions
13
- * @property {() => number} [now] Returns a non-negative timestamp for activity diagnostics.
14
- * @property {number} [cacheSize] Initial recorded-byte budget for explicitly cached entries.
15
- */
16
-
17
- /**
18
- * Cleanup policy accepted by ownership-removal operations.
19
- *
20
- * @typedef {object} CjsMotherLodeMutationOptions
21
- * @property {false|Function} [cleanup] `false` retains ownership; a function replaces default cleanup.
22
- * @property {boolean} [destroyAdapters=true] Whether default cleanup destroys adapter allocations.
23
- * @property {boolean} [releasePayload=true] Whether default cleanup releases the complete CPU payload.
24
- */
25
-
26
- /**
27
- * Canonical insertion and record-metadata options.
28
- *
29
- * @typedef {CjsMotherLodeMutationOptions & object} CjsMotherLodeInsertOptions
30
- * @property {boolean} [replace=true] Whether an existing different resource may be displaced.
31
- * @property {string} [variant] Variant used only by the legacy resource-first insertion form.
32
- * @property {number} [bytes=0] Caller-supplied non-negative safe-integer eviction weight.
33
- * @property {boolean} [cacheable=true] Whether policy may explicitly admit the entry to the byte cache.
34
- * @property {boolean} [cached=false] Admit the entry to oldest-first trimming and `ClearCached()`.
35
- * @property {number} [frame] Explicit non-negative activity frame; otherwise the counter advances.
36
- * @property {number} [time] Explicit non-negative activity timestamp; otherwise `now()` is called.
37
- */
38
-
39
- /**
40
- * Exact-owner conditional replacement options.
41
- *
42
- * @typedef {CjsMotherLodeInsertOptions & object} CjsMotherLodeConditionalReplaceOptions
43
- * @property {() => boolean} [commitGuard] Final side-effect-free caller authority check performed immediately before the exact-record comparison.
44
- */
45
-
46
- /**
47
- * Explicit inactivity-sweep policy for {@link CjsMotherLode#PurgeInactive}.
48
- * At least one identity or payload limit must be supplied for work to occur.
49
- * When both frame and time limits exist, reaching either limit is sufficient.
50
- *
51
- * @typedef {CjsMotherLodeMutationOptions & object} CjsMotherLodePurgeOptions
52
- * @property {number} [frame] Current non-negative activity frame; otherwise the registry advances once.
53
- * @property {number} [time] Current non-negative timestamp in milliseconds; otherwise `now()` is called.
54
- * @property {number} [maxIdleFrames] Frames after which an unlocked identity is removed and cleaned.
55
- * @property {number} [maxIdleMilliseconds] Milliseconds after which an unlocked identity is removed and cleaned.
56
- * @property {number} [payloadMaxIdleFrames] Frames after which an unlocked CPU payload is released.
57
- * @property {number} [payloadMaxIdleMilliseconds] Milliseconds after which an unlocked CPU payload is released.
58
- */
59
-
60
- /**
61
- * Immutable result from one explicit inactivity sweep.
62
- *
63
- * @typedef {object} CjsMotherLodePurgeResult
64
- * @property {number} frame Sweep activity frame.
65
- * @property {number} time Sweep timestamp in milliseconds.
66
- * @property {number} purged Number of canonical identities removed and cleaned.
67
- * @property {number} payloadsReleased Number of payloads released while retaining their identities.
68
- * @property {number} locked Number of locked identities skipped by the sweep.
69
- * @property {readonly string[]} purgedKeys Canonical identities removed by the sweep.
70
- * @property {readonly string[]} payloadKeys Canonical identities whose payload lease expired.
71
- */
72
-
73
- /**
74
- * Immutable result from deterministic recorded-byte cache housekeeping.
75
- *
76
- * Only explicitly cached, cacheable, unlocked records contribute to the
77
- * budget. Zero-byte records do not create pressure and are retained because
78
- * removing them cannot reduce the recorded byte total.
79
- *
80
- * @typedef {object} CjsMotherLodeCacheTrimResult
81
- * @property {number} cacheSize Configured recorded-byte budget.
82
- * @property {number} beforeBytes Explicit cached bytes before housekeeping.
83
- * @property {number} afterBytes Explicit cached bytes after housekeeping.
84
- * @property {number} evictedBytes Recorded bytes removed successfully.
85
- * @property {boolean} overBudget Whether retained cached records still exceed the budget.
86
- * @property {number} evicted Number of complete canonical identities removed.
87
- * @property {number} failed Number of candidate identities that reported cleanup or purge-state failure.
88
- * @property {readonly string[]} evictedKeys Canonical identities removed oldest-first.
89
- * @property {readonly string[]} failedKeys Canonical identities that reported a failure.
90
- */
91
-
92
- /**
93
- * Activity-update options accepted by {@link CjsMotherLode#KeepAlive}.
94
- *
95
- * @typedef {object} CjsMotherLodeActivityOptions
96
- * @property {number} [frame] Explicit non-negative activity frame; otherwise the counter advances.
97
- * @property {number} [time] Explicit non-negative activity timestamp; otherwise `now()` is called.
98
- */
99
-
100
- /**
101
- * Immutable result returned by {@link CjsMotherLode#Insert}.
102
- *
103
- * @typedef {object} CjsMotherLodeInsertResult
104
- * @property {string} key Canonical resolved identity key.
105
- * @property {object|Function} resource Resource that owns the canonical identity after the call.
106
- * @property {boolean} inserted Whether the supplied resource was newly registered.
107
- * @property {boolean} replaced Whether a different registered resource was displaced.
108
- * @property {object|Function|null} displaced Previous resource returned after successful cleanup or retention.
109
- */
110
-
111
- /**
112
- * Immutable result from an exact-owner conditional replacement.
113
- *
114
- * @typedef {object} CjsMotherLodeConditionalReplaceResult
115
- * @property {string} key Canonical resolved identity key.
116
- * @property {boolean} committed Whether the expected owner was replaced.
117
- * @property {object|Function|null} resource Canonical resource after the compare-and-swap attempt.
118
- * @property {object|Function|null} displaced Former exact owner when the replacement committed.
119
- */
120
-
121
- /**
122
- * Immutable diagnostic snapshot returned by {@link CjsMotherLode#GetStats}.
123
- *
124
- * @typedef {object} CjsMotherLodeStats
125
- * @property {number} count Compatibility identity count.
126
- * @property {number} size Canonical identity count.
127
- * @property {number} live Number of entries not explicitly classified as cached.
128
- * @property {number} cached Number of explicitly cached entries.
129
- * @property {number} locked Number of entries with at least one eviction lock.
130
- * @property {number} payloads Number of entries currently retaining a CPU payload.
131
- * @property {number} bytes Total caller-supplied eviction weight.
132
- * @property {number} cacheBytes Byte estimate for explicitly cached entries.
133
- * @property {number} cacheSize Configured recorded-byte cache budget.
134
- * @property {number} activityFrame Highest activity frame observed by this registry.
135
- * @property {Readonly<Record<string, number>>} states Resource count grouped by state name.
136
- * @property {readonly string[]} paths Unique resource paths in encounter order.
137
- */
138
-
139
- /**
140
- * Strong, deterministic JavaScript resource registry.
141
- *
142
- * Carbon can move resources between weak live registration and a strong LRU
143
- * cache. JavaScript ownership cannot reproduce that transition reliably, so
144
- * entries remain strongly owned until explicit ownership removal or an
145
- * explicit inactivity/recorded-byte policy evicts them.
146
- */
147
- class CjsMotherLode {
148
- #active = false;
149
- #activityFrame = 0;
150
- #cacheSize = DEFAULT_CACHE_SIZE;
151
- #entries = new Map();
152
- #nextCacheSequence = 1;
153
- #now = defaultNow;
154
-
155
- /**
156
- * Create an active deterministic resource registry.
157
- *
158
- * @param {CjsMotherLodeOptions} [options={}] Clock and recorded-byte cache configuration.
159
- * @throws {TypeError} If options, the clock, or the cache byte budget are invalid.
160
- */
161
- constructor(options = {}) {
162
- if (!options || typeof options !== "object" || Array.isArray(options)) {
163
- throw new TypeError("CjsMotherLode options must be an object.");
164
- }
165
- if (options.now !== undefined) {
166
- if (typeof options.now !== "function") {
167
- throw new TypeError("CjsMotherLode now must be a function.");
168
- }
169
- this.#now = options.now;
170
- }
171
- if (options.cacheSize !== undefined) {
172
- this.SetCacheSize(options.cacheSize);
173
- }
174
- this.Startup();
175
- }
176
-
177
- /**
178
- * Activate resource registration without changing existing entries.
179
- * Repeated calls are idempotent.
180
- *
181
- * @returns {CjsMotherLode} This registry.
182
- */
183
- Startup() {
184
- this.#active = true;
185
- return this;
186
- }
187
-
188
- /**
189
- * Stop registration and deterministically remove every owned entry.
190
- * Default cleanup destroys attached adapter allocations and releases CPU
191
- * payloads. Repeated calls are idempotent after a successful cleanup.
192
- *
193
- * @param {CjsMotherLodeMutationOptions} [options={}] Cleanup policy applied to every entry.
194
- * @returns {CjsMotherLode} This inactive registry.
195
- * @throws {AggregateError} If cleanup fails for one or more entries; registration is still stopped.
196
- */
197
- Shutdown(options = {}) {
198
- try {
199
- this.Clear(options);
200
- } finally {
201
- this.#active = false;
202
- }
203
- return this;
204
- }
205
-
206
- /**
207
- * Return whether new resources may currently be inserted.
208
- *
209
- * @returns {boolean} `true` between `Startup()` and `Shutdown()`.
210
- */
211
- IsStarted() {
212
- return this.#active;
213
- }
214
-
215
- /**
216
- * Insert a canonical key and report whether an existing owner was displaced.
217
- *
218
- * The legacy `Insert(resource, path, variant)` form remains accepted while
219
- * ResMan and consumers migrate to `Insert(key, resource, options)`.
220
- * Cleanup completes before canonical ownership changes, so a cleanup error
221
- * leaves the previous resource registered and the supplied resource owned by
222
- * its caller.
223
- *
224
- * @param {string|object|Function} keyOrResource Canonical key, or the legacy resource object.
225
- * @param {object|Function|string} resourceOrPath Resource object, or the legacy source path.
226
- * @param {CjsMotherLodeInsertOptions|string} [optionsOrVariant={}] Insertion options, or legacy variant.
227
- * @returns {CjsMotherLodeInsertResult} Immutable canonical ownership result.
228
- * @throws {TypeError} If the key, resource, metadata, or activity values are invalid.
229
- * @throws {Error} If the registry is inactive or displaced-resource cleanup fails.
230
- */
231
- Insert(keyOrResource, resourceOrPath, optionsOrVariant = {}) {
232
- if (!this.#active) {
233
- throw motherLodeInactiveError();
234
- }
235
- const {
236
- key,
237
- resource,
238
- options
239
- } = normalizeInsertArguments(keyOrResource, resourceOrPath, optionsOrVariant);
240
- assertResource(resource);
241
- const existing = this.#entries.get(key) || null;
242
- if (existing && existing.resource === resource) {
243
- const updated = {
244
- ...existing
245
- };
246
- this.#UpdateRecord(updated, options);
247
- this.#AssertRecordedBytesTotal(updated, existing);
248
- this.#TouchRecord(updated, options);
249
- Object.assign(existing, updated);
250
- return freezeInsertResult(key, resource, false, false, null);
251
- }
252
- if (existing && options.replace === false) {
253
- return freezeInsertResult(key, existing.resource, false, false, null);
254
- }
255
- const record = this.#CreateRecord(key, resource, options, existing);
256
- if (existing) {
257
- this.#CleanupRecord(existing, options, "replace", true);
258
- }
259
- this.#entries.set(key, record);
260
- return freezeInsertResult(key, resource, true, Boolean(existing), existing?.resource || null);
261
- }
262
-
263
- /**
264
- * Replace one exact canonical owner without running user cleanup between the
265
- * final ownership comparison and registry publication.
266
- *
267
- * The prepared replacement becomes canonical first; displaced-owner cleanup
268
- * then runs exactly once. A cleanup failure therefore cannot roll lookup
269
- * ownership back to a partially cleaned former resource. Such a failure is
270
- * reported as `CJS_MOTHERLODE_REPLACE_CLEANUP_FAILED` with a committed
271
- * result attached. When the expected owner is no longer current, no mutation
272
- * or cleanup occurs and `committed` is `false`.
273
- *
274
- * Recorded bytes and cacheability inherit from the expected record unless
275
- * explicitly supplied. The replacement is live by default because a
276
- * successful staged reload is an activity observation.
277
- *
278
- * @param {string} key Canonical resolved identity key.
279
- * @param {object|Function} expected Exact resource that must still own `key`.
280
- * @param {object|Function} resource Fully prepared replacement resource.
281
- * @param {CjsMotherLodeConditionalReplaceOptions} [options={}] Replacement metadata, authority guard, and displaced-owner cleanup policy.
282
- * @returns {CjsMotherLodeConditionalReplaceResult} Immutable compare-and-swap outcome.
283
- * @throws {TypeError} If the key, resources, metadata, or activity values are invalid.
284
- * @throws {Error} If the registry is inactive or the prepared resource aliases the expected owner.
285
- * @throws {AggregateError} After a committed swap when displaced-owner cleanup fails.
286
- */
287
- ReplaceExpected(key, expected, resource, options = {}) {
288
- if (!this.#active) {
289
- throw motherLodeInactiveError();
290
- }
291
- const resolvedKey = normalizeResolvedKey(key);
292
- const policy = normalizeOptions(options, "conditional replace");
293
- if (policy.commitGuard !== undefined && typeof policy.commitGuard !== "function") {
294
- throw new TypeError("CjsMotherLode conditional replace commitGuard must be a function.");
295
- }
296
- assertResource(expected);
297
- assertResource(resource);
298
- if (resource === expected) {
299
- const error = new Error("CjsMotherLode conditional replacement requires a distinct resource.");
300
- error.code = "CJS_MOTHERLODE_REPLACE_ALIAS";
301
- throw error;
302
- }
303
- const existing = this.#entries.get(resolvedKey) || null;
304
- if (!existing || existing.resource !== expected) {
305
- return freezeConditionalReplaceResult(resolvedKey, false, existing?.resource || null, null);
306
- }
307
- const recordOptions = {
308
- ...policy,
309
- bytes: policy.bytes === undefined ? existing.bytes : policy.bytes,
310
- cacheable: policy.cacheable === undefined ? existing.cacheable : policy.cacheable,
311
- cached: policy.cached === undefined ? false : policy.cached
312
- };
313
- const record = this.#CreateRecord(resolvedKey, resource, recordOptions, existing);
314
-
315
- // Re-check after clock/metadata validation, then publish with no user code
316
- // between the exact-record comparison and Map mutation.
317
- if (policy.commitGuard && !policy.commitGuard() || this.#entries.get(resolvedKey) !== existing) {
318
- return freezeConditionalReplaceResult(resolvedKey, false, this.#entries.get(resolvedKey)?.resource || null, null);
319
- }
320
- this.#entries.set(resolvedKey, record);
321
- const result = freezeConditionalReplaceResult(resolvedKey, true, resource, expected);
322
- try {
323
- this.#CleanupRecord(existing, policy, "conditional replace");
324
- } catch (cause) {
325
- const error = new AggregateError([cause], `CjsMotherLode conditional replacement committed but cleanup failed for ${resolvedKey}.`);
326
- error.code = "CJS_MOTHERLODE_REPLACE_CLEANUP_FAILED";
327
- error.result = result;
328
- throw error;
329
- }
330
- return result;
331
- }
332
-
333
- /**
334
- * Test a canonical identity without loading or renewing its activity.
335
- * Supplying `variant` retains compatibility with the former path-first API.
336
- *
337
- * @param {string} key Canonical resolved key, or normalized source path with `variant`.
338
- * @param {string} [variant] Optional promised-output variant.
339
- * @returns {boolean} Whether the resolved identity is registered.
340
- * @throws {TypeError} If the identity cannot be normalized.
341
- */
342
- HasKey(key, variant = undefined) {
343
- return this.#entries.has(normalizeLookupKey(key, variant));
344
- }
345
-
346
- /**
347
- * Return a canonical resource without loading or renewing its activity.
348
- * Supplying `variant` retains compatibility with the former path-first API.
349
- *
350
- * @param {string} key Canonical resolved key, or normalized source path with `variant`.
351
- * @param {string} [variant] Optional promised-output variant.
352
- * @returns {object|Function|null} Registered resource, or `null` when absent.
353
- * @throws {TypeError} If the identity cannot be normalized.
354
- */
355
- Lookup(key, variant = undefined) {
356
- return this.#entries.get(normalizeLookupKey(key, variant))?.resource || null;
357
- }
358
-
359
- /**
360
- * Remove one canonical identity and clean its manager-owned payloads.
361
- *
362
- * `Delete(path, variant)` remains accepted for compatibility.
363
- *
364
- * @param {string} key Canonical resolved key, or normalized source path with a variant.
365
- * @param {string|CjsMotherLodeMutationOptions} [variantOrOptions] Compatibility variant or cleanup policy.
366
- * @param {CjsMotherLodeMutationOptions} [maybeOptions] Cleanup policy when a variant is supplied.
367
- * @returns {boolean} Whether an entry was removed.
368
- * @throws {TypeError} If identity or cleanup options are invalid.
369
- * @throws {Error} If resource cleanup fails after the identity is removed.
370
- */
371
- Delete(key, variantOrOptions = undefined, maybeOptions = undefined) {
372
- const {
373
- resolvedKey,
374
- options
375
- } = normalizeDeleteArguments(key, variantOrOptions, maybeOptions);
376
- const record = this.#entries.get(resolvedKey);
377
- if (!record) return false;
378
- this.#entries.delete(resolvedKey);
379
- this.#CleanupRecord(record, options, "delete");
380
- return true;
381
- }
382
-
383
- /**
384
- * Remove every promised-output variant for one normalized source path.
385
- * All matching identities are forgotten before cleanup begins.
386
- *
387
- * @param {string} path Source resource path whose canonical variants are removed.
388
- * @param {CjsMotherLodeMutationOptions} [options={}] Cleanup policy for removed resources.
389
- * @returns {boolean} Whether at least one variant was removed.
390
- * @throws {TypeError} If the path is invalid.
391
- * @throws {AggregateError} If cleanup fails for one or more removed resources.
392
- */
393
- DeleteAllVariants(path, options = {}) {
394
- const normalizedPath = normalizePath(path);
395
- const prefix = `${normalizedPath}\u0000`;
396
- const records = [];
397
- for (const [key, record] of this.#entries) {
398
- if (key === normalizedPath || key.startsWith(prefix)) {
399
- records.push(record);
400
- this.#entries.delete(key);
401
- }
402
- }
403
- this.#CleanupRecords(records, options, "delete variants");
404
- return records.length > 0;
405
- }
406
-
407
- /**
408
- * Deterministically remove and clean every owned identity while preserving
409
- * the registry's active/inactive lifecycle state.
410
- *
411
- * @param {CjsMotherLodeMutationOptions} [options={}] Cleanup policy for removed resources.
412
- * @returns {CjsMotherLode} This empty registry.
413
- * @throws {AggregateError} If cleanup fails for one or more removed resources.
414
- */
415
- Clear(options = {}) {
416
- const records = [...this.#entries.values()];
417
- this.#entries.clear();
418
- this.#CleanupRecords(records, options, "clear");
419
- return this;
420
- }
421
-
422
- /**
423
- * Remove entries explicitly classified as cached and not locked.
424
- *
425
- * JavaScript cannot infer external ownership, so only callers that inserted
426
- * an entry with `{ cached: true }` classify it for this operation.
427
- *
428
- * @param {CjsMotherLodeMutationOptions} [options={}] Cleanup policy for removed cache entries.
429
- * @returns {number} Number of entries removed before cleanup.
430
- * @throws {AggregateError} If cleanup fails for one or more removed resources.
431
- */
432
- ClearCached(options = {}) {
433
- const records = [];
434
- for (const [key, record] of this.#entries) {
435
- if (record.cached && record.lockCount === 0) {
436
- records.push(record);
437
- this.#entries.delete(key);
438
- }
439
- }
440
- this.#CleanupRecords(records, options, "clear cached");
441
- return records.length;
442
- }
443
-
444
- /**
445
- * Record explicit use and promote an explicitly cached entry to live.
446
- * The overload `KeepAlive(path, variant, options)` remains available while
447
- * callers migrate to canonical keys.
448
- *
449
- * @param {string} key Canonical resolved key, or normalized source path with a variant.
450
- * @param {string|CjsMotherLodeActivityOptions} [variantOrOptions] Compatibility variant or activity values.
451
- * @param {CjsMotherLodeActivityOptions} [maybeOptions] Activity values when a variant is supplied.
452
- * @returns {object|Function|null} Renewed resource, or `null` when absent.
453
- * @throws {TypeError} If identity or activity values are invalid.
454
- */
455
- KeepAlive(key, variantOrOptions = undefined, maybeOptions = undefined) {
456
- const {
457
- resolvedKey,
458
- options
459
- } = normalizeActivityArguments(key, variantOrOptions, maybeOptions);
460
- const record = this.#entries.get(resolvedKey);
461
- if (!record) return null;
462
- record.cached = false;
463
- record.cacheSequence = 0;
464
- this.#TouchRecord(record, options);
465
- return record.resource;
466
- }
467
-
468
- /**
469
- * Explicitly renew both canonical identity activity and the attached CPU
470
- * payload lease. The resource-facing `KeepPayloadAlive()` method delegates
471
- * here when CjsResMan owns the handle. No payload is read or created.
472
- *
473
- * @param {string} key Canonical resolved key, or normalized source path with a variant.
474
- * @param {string|CjsMotherLodeActivityOptions} [variantOrOptions] Compatibility variant or activity values.
475
- * @param {CjsMotherLodeActivityOptions} [maybeOptions] Activity values when a variant is supplied.
476
- * @returns {object|Function|null} Renewed resource, or `null` when absent.
477
- * @throws {TypeError} If identity or activity values are invalid.
478
- */
479
- KeepPayloadAlive(key, variantOrOptions = undefined, maybeOptions = undefined) {
480
- const {
481
- resolvedKey,
482
- options
483
- } = normalizeActivityArguments(key, variantOrOptions, maybeOptions);
484
- const record = this.#entries.get(resolvedKey);
485
- if (!record) return null;
486
- record.cached = false;
487
- record.cacheSequence = 0;
488
- this.#TouchRecord(record, options);
489
- record.payloadLastUsedFrame = record.lastUsedFrame;
490
- record.payloadLastUsedTime = record.lastUsedTime;
491
- return record.resource;
492
- }
493
-
494
- /**
495
- * Add one eviction lock and promote the identity from cached to live.
496
- * Locking an absent identity is a no-op.
497
- *
498
- * @param {string} key Canonical resolved key, or normalized source path with `variant`.
499
- * @param {string} [variant] Optional promised-output variant.
500
- * @returns {number} New lock count, or `0` when the identity is absent.
501
- * @throws {TypeError} If the identity cannot be normalized.
502
- */
503
- Lock(key, variant = undefined) {
504
- const record = this.#entries.get(normalizeLookupKey(key, variant));
505
- if (!record) return 0;
506
- record.lockCount += 1;
507
- record.cached = false;
508
- record.cacheSequence = 0;
509
- this.#TouchRecord(record, {});
510
- return record.lockCount;
511
- }
512
-
513
- /**
514
- * Release one eviction lock without allowing the count to underflow.
515
- * Unlocking does not itself classify an entry as cached or evict it.
516
- *
517
- * @param {string} key Canonical resolved key, or normalized source path with `variant`.
518
- * @param {string} [variant] Optional promised-output variant.
519
- * @returns {number} Remaining lock count, or `0` when absent or already unlocked.
520
- * @throws {TypeError} If the identity cannot be normalized.
521
- */
522
- Unlock(key, variant = undefined) {
523
- const record = this.#entries.get(normalizeLookupKey(key, variant));
524
- if (!record) return 0;
525
- if (record.lockCount > 0) record.lockCount -= 1;
526
- return record.lockCount;
527
- }
528
-
529
- /**
530
- * Run one explicit deterministic inactivity sweep.
531
- *
532
- * Identity limits remove unlocked canonical entries, destroy their adapter
533
- * allocations, release payloads, detach resource-facing lifecycle callbacks,
534
- * and mark CjsResource-compatible handles purged. Payload limits release only
535
- * the CPU payload while retaining identity and adapters. The sweep never
536
- * fetches, reloads, prepares, or infers external JavaScript ownership.
537
- *
538
- * Cleanup is transactional per identity: a failed cleanup leaves that record
539
- * canonical and reports a contextual error after all candidates are visited.
540
- * Successful candidates are still purged when another candidate fails.
541
- *
542
- * @param {CjsMotherLodePurgeOptions} [options={}] Explicit sweep point, inactivity limits, and cleanup policy.
543
- * @returns {CjsMotherLodePurgeResult} Immutable counts and affected canonical keys.
544
- * @throws {TypeError} If the sweep point, limits, or cleanup policy are invalid.
545
- * @throws {AggregateError} If one or more cleanup, payload-release, or purge-state operations fail.
546
- */
547
- PurgeInactive(options = {}) {
548
- const policy = normalizePurgeOptions(options, this.#activityFrame, this.#now);
549
- this.#activityFrame = Math.max(this.#activityFrame, policy.frame);
550
- const purgedKeys = [];
551
- const payloadKeys = [];
552
- const errors = [];
553
- let locked = 0;
554
- for (const [key, record] of [...this.#entries]) {
555
- if (record.lockCount > 0) {
556
- locked += 1;
557
- continue;
558
- }
559
- if (isInactive(record.lastUsedFrame, record.lastUsedTime, policy.frame, policy.time, policy.maxIdleFrames, policy.maxIdleMilliseconds)) {
560
- try {
561
- this.#CleanupRecord(record, policy, "purge inactive", true);
562
- } catch (error) {
563
- errors.push(error);
564
- continue;
565
- }
566
- this.#entries.delete(key);
567
- purgedKeys.push(key);
568
- try {
569
- record.resource?.MarkPurged?.();
570
- } catch (cause) {
571
- errors.push(motherLodeCleanupError("mark purged", key, record.resource, cause));
572
- }
573
- continue;
574
- }
575
- if (policy.releasePayload !== false && hasOwnedPayload(record.resource) && isInactive(record.payloadLastUsedFrame, record.payloadLastUsedTime, policy.frame, policy.time, policy.payloadMaxIdleFrames, policy.payloadMaxIdleMilliseconds)) {
576
- try {
577
- record.resource.ReleasePayload();
578
- payloadKeys.push(key);
579
- } catch (cause) {
580
- errors.push(motherLodeCleanupError("release inactive payload", key, record.resource, cause));
581
- }
582
- }
583
- }
584
- const result = freezePurgeResult(policy.frame, policy.time, purgedKeys, payloadKeys, locked);
585
- if (errors.length) {
586
- const error = new AggregateError(errors, "CjsMotherLode inactivity purge failed.");
587
- error.code = "CJS_MOTHERLODE_PURGE_FAILED";
588
- error.result = result;
589
- throw error;
590
- }
591
- return result;
592
- }
593
-
594
- /**
595
- * Remove complete explicitly cached identities until their recorded byte
596
- * total fits the configured budget.
597
- *
598
- * Candidates are ordered by explicit cache admission, oldest first. Lookup
599
- * is intentionally pure; callers re-admit a live identity by inserting the
600
- * same handle with `{ cached: true }`. Locks and `KeepAlive()` promote an
601
- * entry to live and therefore remove it from byte-budget consideration.
602
- *
603
- * Cleanup is transactional per candidate. A failure leaves that candidate
604
- * canonical, later candidates are still attempted, and the final aggregate
605
- * error carries the partial immutable result. Successful pressure eviction
606
- * destroys adapters, releases payloads, detaches lifecycle callbacks, marks
607
- * compatible handles `PURGED` by default, and never fetches or reconstructs
608
- * data. Explicit cleanup overrides retain their documented ownership.
609
- *
610
- * @param {CjsMotherLodeMutationOptions} [options={}] Cleanup policy for pressure-evicted identities.
611
- * @returns {CjsMotherLodeCacheTrimResult} Immutable byte totals and affected canonical keys.
612
- * @throws {TypeError} If cleanup options are invalid.
613
- * @throws {AggregateError} If cleanup or purge-state publication fails for one or more candidates.
614
- */
615
- TrimCache(options = {}) {
616
- const policy = normalizeOptions(options, "cache trim");
617
- const beforeBytes = this.#GetCacheBytes();
618
- const evictedKeys = [];
619
- const failedKeys = [];
620
- const errors = [];
621
- let evictedBytes = 0;
622
- if (beforeBytes > this.#cacheSize) {
623
- const candidates = [...this.#entries.values()].filter(record => record.cacheable && record.cached && record.lockCount === 0 && record.bytes > 0).sort((a, b) => a.cacheSequence - b.cacheSequence);
624
- for (const record of candidates) {
625
- if (this.#GetCacheBytes() <= this.#cacheSize) break;
626
- if (this.#entries.get(record.key) !== record || !record.cacheable || !record.cached || record.lockCount > 0 || record.bytes <= 0) {
627
- continue;
628
- }
629
- try {
630
- this.#CleanupRecord(record, policy, "trim cache", true);
631
- } catch (error) {
632
- errors.push(error);
633
- failedKeys.push(record.key);
634
- continue;
635
- }
636
- if (this.#entries.get(record.key) === record) {
637
- this.#entries.delete(record.key);
638
- }
639
- evictedKeys.push(record.key);
640
- evictedBytes += record.bytes;
641
- try {
642
- record.resource?.MarkPurged?.();
643
- } catch (cause) {
644
- errors.push(motherLodeCleanupError("mark cache eviction purged", record.key, record.resource, cause));
645
- failedKeys.push(record.key);
646
- }
647
- }
648
- }
649
- const afterBytes = this.#GetCacheBytes();
650
- const result = freezeCacheTrimResult(this.#cacheSize, beforeBytes, afterBytes, evictedBytes, evictedKeys, failedKeys);
651
- if (errors.length) {
652
- const error = new AggregateError(errors, "CjsMotherLode cache trim failed.");
653
- error.code = "CJS_MOTHERLODE_CACHE_TRIM_FAILED";
654
- error.result = result;
655
- throw error;
656
- }
657
- return result;
658
- }
659
-
660
- /**
661
- * Configure and immediately enforce the recorded-byte budget for explicit
662
- * cached entries. The new budget remains installed if partial cleanup fails,
663
- * matching Carbon's policy-first `SetCacheSize` behavior.
664
- *
665
- * @param {number} bytes Non-negative safe-integer byte budget.
666
- * @param {CjsMotherLodeMutationOptions} [options={}] Cleanup policy for entries displaced by the smaller budget.
667
- * @returns {CjsMotherLode} This registry.
668
- * @throws {TypeError} If `bytes` or cleanup options are invalid.
669
- * @throws {AggregateError} If enforcing the new budget cannot clean one or more cached entries.
670
- */
671
- SetCacheSize(bytes, options = {}) {
672
- assertNonNegativeSafeInteger(bytes, "CjsMotherLode cache size");
673
- const policy = normalizeOptions(options, "set cache size");
674
- this.#cacheSize = bytes;
675
- this.TrimCache(policy);
676
- return this;
677
- }
678
-
679
- /**
680
- * Return the configured recorded-byte cache budget.
681
- *
682
- * @returns {number} Non-negative safe-integer byte budget.
683
- */
684
- GetCacheSize() {
685
- return this.#cacheSize;
686
- }
687
-
688
- /**
689
- * Return an immutable snapshot of canonical identity keys.
690
- *
691
- * @returns {readonly string[]} Canonical keys in insertion order.
692
- */
693
- GetKeys() {
694
- return Object.freeze([...this.#entries.keys()]);
695
- }
696
-
697
- /**
698
- * Return an immutable snapshot of canonical resources.
699
- *
700
- * @returns {readonly (object|Function)[]} Resources in insertion order.
701
- */
702
- GetValues() {
703
- return Object.freeze([...this.#entries.values()].map(record => record.resource));
704
- }
705
-
706
- /**
707
- * Return the number of canonical identities currently registered.
708
- *
709
- * @returns {number} Registry entry count.
710
- */
711
- GetSize() {
712
- return this.#entries.size;
713
- }
714
-
715
- /**
716
- * Return an immutable diagnostic snapshot without renewing activity.
717
- * Byte totals include only caller-supplied estimates and do not measure
718
- * JavaScript reachability or trigger cache policy.
719
- *
720
- * @returns {CjsMotherLodeStats} Current identity, activity, state, and byte totals.
721
- */
722
- GetStats() {
723
- let bytes = 0;
724
- let cacheBytes = 0;
725
- let cached = 0;
726
- let locked = 0;
727
- let payloads = 0;
728
- const paths = new Set();
729
- const states = {};
730
- for (const record of this.#entries.values()) {
731
- bytes += record.bytes;
732
- if (record.cached) {
733
- cached += 1;
734
- cacheBytes += record.bytes;
735
- }
736
- if (record.lockCount > 0) locked += 1;
737
- if (hasOwnedPayload(record.resource)) payloads += 1;
738
- const path = record.resource?.GetPath?.() || record.resource?.path || getKeyPath(record.key);
739
- if (path) paths.add(path);
740
- const state = typeof record.resource?.state === "string" ? record.resource.state : "unknown";
741
- states[state] = (states[state] || 0) + 1;
742
- }
743
- return Object.freeze({
744
- count: this.#entries.size,
745
- size: this.#entries.size,
746
- live: this.#entries.size - cached,
747
- cached,
748
- locked,
749
- payloads,
750
- bytes,
751
- cacheBytes,
752
- cacheSize: this.#cacheSize,
753
- activityFrame: this.#activityFrame,
754
- states: Object.freeze(states),
755
- paths: Object.freeze([...paths])
756
- });
757
- }
758
-
759
- /**
760
- * Return a snapshot iterator of canonical key/resource pairs.
761
- * Later registry mutations do not change the captured key list.
762
- *
763
- * @returns {IterableIterator<[string, object|Function]>} Insertion-ordered entry iterator.
764
- */
765
- Entries() {
766
- return this.GetKeys().map(key => [key, this.#entries.get(key).resource])[Symbol.iterator]();
767
- }
768
-
769
- /**
770
- * Test a path and optional variant through the temporary legacy API.
771
- *
772
- * @deprecated Use `HasKey(getMotherLodeKey(path, variant))`.
773
- * @param {string} path Source resource path.
774
- * @param {string} [variant] Optional promised-output variant.
775
- * @returns {boolean} Whether the resolved identity is registered.
776
- */
777
- Has(path, variant = undefined) {
778
- return this.HasKey(path, variant);
779
- }
780
-
781
- /**
782
- * Return the registry count through the temporary legacy API.
783
- *
784
- * @deprecated Use `GetSize()`.
785
- * @returns {number} Registry entry count.
786
- */
787
- GetCount() {
788
- return this.GetSize();
789
- }
790
-
791
- /**
792
- * Remove all variants through the temporary legacy API.
793
- *
794
- * @deprecated Use `DeleteAllVariants(path, options)`.
795
- * @param {string} path Source resource path.
796
- * @param {CjsMotherLodeMutationOptions} [options={}] Cleanup policy for removed resources.
797
- * @returns {boolean} Whether at least one variant was removed.
798
- */
799
- DeleteAll(path, options = {}) {
800
- return this.DeleteAllVariants(path, options);
801
- }
802
-
803
- /**
804
- * Create and validate an internal ownership record before it is registered.
805
- *
806
- * @param {string} key Canonical resolved identity key.
807
- * @param {object|Function} resource Caller-supplied resource owner.
808
- * @param {CjsMotherLodeInsertOptions} options Validated insertion options.
809
- * @param {object|null} [displaced=null] Existing record excluded from aggregate byte validation.
810
- * @returns {object} Mutable internal ownership record.
811
- */
812
- #CreateRecord(key, resource, options, displaced = null) {
813
- const record = {
814
- key,
815
- resource,
816
- bytes: 0,
817
- cacheable: true,
818
- cached: false,
819
- cacheSequence: 0,
820
- lockCount: 0,
821
- lastUsedFrame: 0,
822
- lastUsedTime: 0,
823
- payloadLastUsedFrame: 0,
824
- payloadLastUsedTime: 0
825
- };
826
- this.#UpdateRecord(record, options);
827
- this.#AssertRecordedBytesTotal(record, displaced);
828
- this.#TouchRecord(record, options);
829
- record.payloadLastUsedFrame = record.lastUsedFrame;
830
- record.payloadLastUsedTime = record.lastUsedTime;
831
- return record;
832
- }
833
-
834
- /**
835
- * Apply explicit size/cache metadata while preserving lock invariants.
836
- *
837
- * @param {object} record Mutable internal ownership record.
838
- * @param {CjsMotherLodeInsertOptions} options Metadata updates.
839
- * @returns {object} The updated record.
840
- */
841
- #UpdateRecord(record, options) {
842
- if (options.bytes !== undefined) {
843
- assertNonNegativeSafeInteger(options.bytes, "CjsMotherLode entry bytes");
844
- record.bytes = options.bytes;
845
- }
846
- if (options.cacheable !== undefined) record.cacheable = Boolean(options.cacheable);
847
- if (options.cached !== undefined) {
848
- record.cached = record.cacheable && record.lockCount === 0 && Boolean(options.cached);
849
- record.cacheSequence = record.cached ? this.#nextCacheSequence++ : 0;
850
- }
851
- if (!record.cacheable || record.lockCount > 0) {
852
- record.cached = false;
853
- record.cacheSequence = 0;
854
- }
855
- return record;
856
- }
857
-
858
- /**
859
- * Reject metadata that would make aggregate recorded-byte arithmetic lose
860
- * integer precision. This keeps trim results and diagnostics exact without
861
- * attempting to measure JavaScript object graphs heuristically.
862
- *
863
- * @param {object} candidate New or updated record.
864
- * @param {object|null} [excluded=null] Existing record replaced by the candidate.
865
- * @returns {void}
866
- * @throws {RangeError} If aggregate recorded bytes exceed the safe-integer range.
867
- */
868
- #AssertRecordedBytesTotal(candidate, excluded = null) {
869
- let total = BigInt(candidate.bytes);
870
- for (const record of this.#entries.values()) {
871
- if (record === excluded) continue;
872
- total += BigInt(record.bytes);
873
- if (total > MAX_SAFE_INTEGER_BIGINT) {
874
- throw new RangeError("CjsMotherLode aggregate recorded bytes exceed Number.MAX_SAFE_INTEGER.");
875
- }
876
- }
877
- }
878
-
879
- /**
880
- * Sum exact byte weights for explicit cached entries.
881
- * Aggregate safety is maintained at insertion/update time.
882
- *
883
- * @returns {number} Exact explicit-cache byte total.
884
- */
885
- #GetCacheBytes() {
886
- let bytes = 0;
887
- for (const record of this.#entries.values()) {
888
- if (record.cacheable && record.cached) bytes += record.bytes;
889
- }
890
- return bytes;
891
- }
892
-
893
- /**
894
- * Record one explicit activity observation for an internal entry.
895
- *
896
- * @param {object} record Mutable internal ownership record.
897
- * @param {CjsMotherLodeActivityOptions} options Optional frame/time override.
898
- * @returns {object} The updated record.
899
- * @throws {TypeError} If frame or time is invalid.
900
- */
901
- #TouchRecord(record, options) {
902
- const frame = options.frame === undefined ? this.#activityFrame + 1 : options.frame;
903
- assertNonNegativeSafeInteger(frame, "CjsMotherLode activity frame");
904
- this.#activityFrame = Math.max(this.#activityFrame, frame);
905
- record.lastUsedFrame = frame;
906
- const time = options.time === undefined ? this.#now() : options.time;
907
- if (typeof time !== "number" || !Number.isFinite(time) || time < 0) {
908
- throw new TypeError("CjsMotherLode activity time must be a non-negative finite number.");
909
- }
910
- record.lastUsedTime = time;
911
- return record;
912
- }
913
-
914
- /**
915
- * Clean one displaced record and attach canonical ownership context to errors.
916
- *
917
- * @param {object} record Internal ownership record being removed.
918
- * @param {CjsMotherLodeMutationOptions} options Cleanup policy.
919
- * @param {string} operation Human-readable ownership operation.
920
- * @param {boolean} [preserveOnFailure=false] Keep lifecycle binding when the record remains canonical.
921
- * @returns {void}
922
- * @throws {Error} Contextual `CJS_MOTHERLODE_CLEANUP_FAILED` error on failure.
923
- */
924
- #CleanupRecord(record, options, operation, preserveOnFailure = false) {
925
- let cleanupComplete = false;
926
- try {
927
- cleanupOwnedResource(record.resource, options);
928
- cleanupComplete = true;
929
- detachResourceLifecycle(record.resource);
930
- } catch (cause) {
931
- if (!preserveOnFailure && !cleanupComplete) {
932
- try {
933
- detachResourceLifecycle(record.resource);
934
- } catch (detachCause) {
935
- cause = new AggregateError([cause, detachCause], "Resource cleanup and detach failed.");
936
- }
937
- }
938
- throw motherLodeCleanupError(operation, record.key, record.resource, cause);
939
- }
940
- }
941
-
942
- /**
943
- * Clean every removed record and aggregate failures without skipping entries.
944
- *
945
- * @param {object[]} records Internal ownership records already removed from the registry.
946
- * @param {CjsMotherLodeMutationOptions} options Shared cleanup policy.
947
- * @param {string} operation Human-readable ownership operation.
948
- * @returns {void}
949
- * @throws {AggregateError} Contextual errors for every failed cleanup.
950
- */
951
- #CleanupRecords(records, options, operation) {
952
- const errors = [];
953
- for (const record of records) {
954
- try {
955
- cleanupOwnedResource(record.resource, options);
956
- } catch (cause) {
957
- errors.push(motherLodeCleanupError(operation, record.key, record.resource, cause));
958
- }
959
- try {
960
- detachResourceLifecycle(record.resource);
961
- } catch (cause) {
962
- errors.push(motherLodeCleanupError(`${operation} detach`, record.key, record.resource, cause));
963
- }
964
- }
965
- if (errors.length) {
966
- throw new AggregateError(errors, `CjsMotherLode ${operation} cleanup failed.`);
967
- }
968
- }
969
- }
970
-
971
- /**
972
- * Build the canonical normalized identity shared by CjsResMan and MotherLode.
973
- * The source path and promised-output variant are separated with an internal null byte;
974
- * variants therefore may not contain that delimiter.
975
- *
976
- * @param {string} path Source resource path normalized with Carbon path rules.
977
- * @param {*} [variant=""] Stable build/outcome variant; empty values use the path alone.
978
- * @returns {string} Canonical resolved MotherLode identity.
979
- * @throws {TypeError} If the path is empty/invalid or the variant contains a null byte.
980
- */
981
- function getMotherLodeKey(path, variant = "") {
982
- const normalizedPath = normalizePath(path);
983
- if (variant === null || variant === undefined || variant === "") return normalizedPath;
984
- const normalizedVariant = String(variant);
985
- if (normalizedVariant.includes("\u0000")) {
986
- throw new TypeError("CjsMotherLode variant may not contain a null character.");
987
- }
988
- return `${normalizedPath}\u0000${normalizedVariant}`;
989
- }
990
- function normalizeInsertArguments(keyOrResource, resourceOrPath, optionsOrVariant) {
991
- if (typeof keyOrResource === "string") {
992
- return {
993
- key: normalizeResolvedKey(keyOrResource),
994
- resource: resourceOrPath,
995
- options: normalizeOptions(optionsOrVariant, "insert")
996
- };
997
- }
998
- const resource = keyOrResource;
999
- const path = resourceOrPath ?? resource?.GetPath?.() ?? resource?.path;
1000
- const isOptions = optionsOrVariant && typeof optionsOrVariant === "object" && !Array.isArray(optionsOrVariant);
1001
- const options = isOptions ? normalizeOptions(optionsOrVariant, "insert") : {};
1002
- const variant = isOptions ? options.variant || "" : optionsOrVariant;
1003
- return {
1004
- key: getMotherLodeKey(path, variant),
1005
- resource,
1006
- options
1007
- };
1008
- }
1009
- function normalizeDeleteArguments(key, variantOrOptions, maybeOptions) {
1010
- if (variantOrOptions && typeof variantOrOptions === "object" && !Array.isArray(variantOrOptions)) {
1011
- return {
1012
- resolvedKey: normalizeResolvedKey(key),
1013
- options: normalizeOptions(variantOrOptions, "delete")
1014
- };
1015
- }
1016
- return {
1017
- resolvedKey: normalizeLookupKey(key, variantOrOptions),
1018
- options: normalizeOptions(maybeOptions || {}, "delete")
1019
- };
1020
- }
1021
- function normalizeActivityArguments(key, variantOrOptions, maybeOptions) {
1022
- if (variantOrOptions && typeof variantOrOptions === "object" && !Array.isArray(variantOrOptions)) {
1023
- return {
1024
- resolvedKey: normalizeResolvedKey(key),
1025
- options: normalizeOptions(variantOrOptions, "activity")
1026
- };
1027
- }
1028
- return {
1029
- resolvedKey: normalizeLookupKey(key, variantOrOptions),
1030
- options: normalizeOptions(maybeOptions || {}, "activity")
1031
- };
1032
- }
1033
- function normalizeLookupKey(key, variant) {
1034
- return variant === undefined ? normalizeResolvedKey(key) : getMotherLodeKey(key, variant);
1035
- }
1036
- function normalizeResolvedKey(key) {
1037
- if (typeof key !== "string" || !key) {
1038
- throw new TypeError("CjsMotherLode requires a canonical string key.");
1039
- }
1040
- const separator = key.indexOf("\u0000");
1041
- return separator === -1 ? getMotherLodeKey(key) : getMotherLodeKey(key.slice(0, separator), key.slice(separator + 1));
1042
- }
1043
- function normalizePath(path) {
1044
- const normalizedPath = normalizeResourcePath(path);
1045
- if (!normalizedPath) throw new TypeError("CjsMotherLode requires a resource path.");
1046
- return normalizedPath;
1047
- }
1048
- function normalizeOptions(options, operation) {
1049
- if (options === null || options === undefined) return {};
1050
- if (!options || typeof options !== "object" || Array.isArray(options)) {
1051
- throw new TypeError(`CjsMotherLode ${operation} options must be an object.`);
1052
- }
1053
- return options;
1054
- }
1055
-
1056
- /**
1057
- * Normalize one explicit inactivity sweep without mutating registry entries.
1058
- *
1059
- * @param {CjsMotherLodePurgeOptions} options Caller policy.
1060
- * @param {number} activityFrame Current registry frame.
1061
- * @param {() => number} now Registry clock.
1062
- * @returns {CjsMotherLodePurgeOptions & {frame: number, time: number}} Validated sweep policy.
1063
- */
1064
- function normalizePurgeOptions(options, activityFrame, now) {
1065
- const policy = normalizeOptions(options, "purge");
1066
- const frame = policy.frame === undefined ? activityFrame + 1 : policy.frame;
1067
- assertNonNegativeSafeInteger(frame, "CjsMotherLode purge frame");
1068
- const time = policy.time === undefined ? now() : policy.time;
1069
- assertNonNegativeFiniteNumber(time, "CjsMotherLode purge time");
1070
- for (const name of ["maxIdleFrames", "payloadMaxIdleFrames"]) {
1071
- if (policy[name] !== undefined) {
1072
- assertNonNegativeSafeInteger(policy[name], `CjsMotherLode ${name}`);
1073
- }
1074
- }
1075
- for (const name of ["maxIdleMilliseconds", "payloadMaxIdleMilliseconds"]) {
1076
- if (policy[name] !== undefined) {
1077
- assertNonNegativeFiniteNumber(policy[name], `CjsMotherLode ${name}`);
1078
- }
1079
- }
1080
- return {
1081
- ...policy,
1082
- frame,
1083
- time
1084
- };
1085
- }
1086
-
1087
- /**
1088
- * Test whether either configured frame/time limit has elapsed.
1089
- *
1090
- * @param {number} lastFrame Last explicit lease frame.
1091
- * @param {number} lastTime Last explicit lease timestamp.
1092
- * @param {number} frame Current sweep frame.
1093
- * @param {number} time Current sweep timestamp.
1094
- * @param {number|undefined} maxFrames Optional frame limit.
1095
- * @param {number|undefined} maxMilliseconds Optional millisecond limit.
1096
- * @returns {boolean} Whether at least one configured limit has elapsed.
1097
- */
1098
- function isInactive(lastFrame, lastTime, frame, time, maxFrames, maxMilliseconds) {
1099
- const frameExpired = maxFrames !== undefined && frame >= lastFrame && frame - lastFrame >= maxFrames;
1100
- const timeExpired = maxMilliseconds !== undefined && time >= lastTime && time - lastTime >= maxMilliseconds;
1101
- return frameExpired || timeExpired;
1102
- }
1103
-
1104
- /**
1105
- * Return whether a resource currently exposes a releasable CPU payload.
1106
- *
1107
- * @param {object|Function} resource Candidate resource.
1108
- * @returns {boolean} Whether `ReleasePayload()` can remove an attached payload.
1109
- */
1110
- function hasOwnedPayload(resource) {
1111
- return typeof resource?.HasPayload === "function" && typeof resource?.ReleasePayload === "function" && Boolean(resource.HasPayload());
1112
- }
1113
-
1114
- /**
1115
- * Detach resource-facing lifecycle callbacks after canonical ownership ends.
1116
- *
1117
- * @param {object|Function} resource Removed resource.
1118
- * @returns {void}
1119
- */
1120
- function detachResourceLifecycle(resource) {
1121
- resource?.SetLifecycleController?.(null);
1122
- }
1123
- function cleanupOwnedResource(resource, options) {
1124
- if (options.cleanup === false) return;
1125
- if (typeof options.cleanup === "function") {
1126
- options.cleanup(resource);
1127
- return;
1128
- }
1129
- const errors = [];
1130
- if (options.destroyAdapters !== false && typeof resource?.DestroyAdapterResources === "function") {
1131
- try {
1132
- resource.DestroyAdapterResources({
1133
- destroy: true
1134
- });
1135
- } catch (error) {
1136
- errors.push(error);
1137
- }
1138
- }
1139
- if (options.releasePayload !== false && typeof resource?.ReleasePayload === "function") {
1140
- try {
1141
- resource.ReleasePayload();
1142
- } catch (error) {
1143
- errors.push(error);
1144
- }
1145
- }
1146
- if (errors.length) {
1147
- throw errors.length === 1 ? errors[0] : new AggregateError(errors, "Resource cleanup failed.");
1148
- }
1149
- }
1150
- function assertResource(resource) {
1151
- if (typeof resource !== "object" && typeof resource !== "function" || resource === null) {
1152
- throw new TypeError("CjsMotherLode requires a resource object.");
1153
- }
1154
- }
1155
- function assertNonNegativeSafeInteger(value, label) {
1156
- if (!Number.isSafeInteger(value) || value < 0) {
1157
- throw new TypeError(`${label} must be a non-negative safe integer.`);
1158
- }
1159
- }
1160
-
1161
- /**
1162
- * Validate a non-negative finite number with a contextual error label.
1163
- *
1164
- * @param {*} value Candidate numeric value.
1165
- * @param {string} label Error-message field name.
1166
- * @returns {void}
1167
- */
1168
- function assertNonNegativeFiniteNumber(value, label) {
1169
- if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
1170
- throw new TypeError(`${label} must be a non-negative finite number.`);
1171
- }
1172
- }
1173
- function freezeInsertResult(key, resource, inserted, replaced, displaced) {
1174
- return Object.freeze({
1175
- key,
1176
- resource,
1177
- inserted,
1178
- replaced,
1179
- displaced
1180
- });
1181
- }
1182
-
1183
- /**
1184
- * Freeze one exact-owner compare-and-swap result.
1185
- *
1186
- * @param {string} key Canonical resolved identity key.
1187
- * @param {boolean} committed Whether publication replaced the expected owner.
1188
- * @param {object|Function|null} resource Canonical owner after the attempt.
1189
- * @param {object|Function|null} displaced Former owner when committed.
1190
- * @returns {CjsMotherLodeConditionalReplaceResult} Immutable replacement result.
1191
- */
1192
- function freezeConditionalReplaceResult(key, committed, resource, displaced) {
1193
- return Object.freeze({
1194
- key,
1195
- committed,
1196
- resource,
1197
- displaced
1198
- });
1199
- }
1200
-
1201
- /**
1202
- * Freeze one inactivity-sweep result and its affected-key snapshots.
1203
- *
1204
- * @param {number} frame Sweep frame.
1205
- * @param {number} time Sweep timestamp.
1206
- * @param {string[]} purgedKeys Removed canonical keys.
1207
- * @param {string[]} payloadKeys Keys whose payloads were released.
1208
- * @param {number} locked Number of locked entries skipped.
1209
- * @returns {CjsMotherLodePurgeResult} Immutable sweep result.
1210
- */
1211
- function freezePurgeResult(frame, time, purgedKeys, payloadKeys, locked) {
1212
- return Object.freeze({
1213
- frame,
1214
- time,
1215
- purged: purgedKeys.length,
1216
- payloadsReleased: payloadKeys.length,
1217
- locked,
1218
- purgedKeys: Object.freeze(purgedKeys),
1219
- payloadKeys: Object.freeze(payloadKeys)
1220
- });
1221
- }
1222
-
1223
- /**
1224
- * Freeze one byte-budget housekeeping result and its affected-key snapshots.
1225
- *
1226
- * @param {number} cacheSize Configured cache budget.
1227
- * @param {number} beforeBytes Cached bytes before housekeeping.
1228
- * @param {number} afterBytes Cached bytes after housekeeping.
1229
- * @param {number} evictedBytes Successfully removed cached bytes.
1230
- * @param {string[]} evictedKeys Successfully removed canonical identities.
1231
- * @param {string[]} failedKeys Candidate identities that reported failure.
1232
- * @returns {CjsMotherLodeCacheTrimResult} Immutable cache-housekeeping result.
1233
- */
1234
- function freezeCacheTrimResult(cacheSize, beforeBytes, afterBytes, evictedBytes, evictedKeys, failedKeys) {
1235
- return Object.freeze({
1236
- cacheSize,
1237
- beforeBytes,
1238
- afterBytes,
1239
- evictedBytes,
1240
- overBudget: afterBytes > cacheSize,
1241
- evicted: evictedKeys.length,
1242
- failed: failedKeys.length,
1243
- evictedKeys: Object.freeze(evictedKeys),
1244
- failedKeys: Object.freeze(failedKeys)
1245
- });
1246
- }
1247
- function motherLodeInactiveError() {
1248
- const error = new Error("CjsMotherLode is shut down. Call Startup() before inserting resources.");
1249
- error.code = "CJS_MOTHERLODE_INACTIVE";
1250
- return error;
1251
- }
1252
- function motherLodeCleanupError(operation, key, resource, cause) {
1253
- const error = new Error(`CjsMotherLode ${operation} cleanup failed for ${key}.`, {
1254
- cause
1255
- });
1256
- error.code = "CJS_MOTHERLODE_CLEANUP_FAILED";
1257
- error.operation = operation;
1258
- error.key = key;
1259
- error.resource = resource;
1260
- return error;
1261
- }
1262
- function getKeyPath(key) {
1263
- const separator = key.indexOf("\u0000");
1264
- return separator === -1 ? key : key.slice(0, separator);
1265
- }
1266
- function defaultNow() {
1267
- return Date.now();
1268
- }
1269
-
1270
- export { CjsMotherLode, getMotherLodeKey };
1271
- //# sourceMappingURL=CjsMotherLode.js.map
1
+ import { normalizeResourcePath } from '@carbonenginejs/runtime-utils/path';
2
+
3
+ // Source: blue/include/IMotherLode.h
4
+ // Source: blue/src/MotherLode.h
5
+ // Source: blue/src/MotherLode.cpp
6
+ const DEFAULT_CACHE_SIZE = 32 * 1024 * 1024;
7
+ const MAX_SAFE_INTEGER_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
8
+
9
+ /**
10
+ * Construction options for {@link CjsMotherLode}.
11
+ *
12
+ * @typedef {object} CjsMotherLodeOptions
13
+ * @property {() => number} [now] Returns a non-negative timestamp for activity diagnostics.
14
+ * @property {number} [cacheSize] Initial recorded-byte budget for explicitly cached entries.
15
+ */
16
+
17
+ /**
18
+ * Cleanup policy accepted by ownership-removal operations.
19
+ *
20
+ * @typedef {object} CjsMotherLodeMutationOptions
21
+ * @property {false|Function} [cleanup] `false` retains ownership; a function replaces default cleanup.
22
+ * @property {boolean} [destroyAdapters=true] Whether default cleanup destroys adapter allocations.
23
+ * @property {boolean} [releasePayload=true] Whether default cleanup releases the complete CPU payload.
24
+ */
25
+
26
+ /**
27
+ * Canonical insertion and record-metadata options.
28
+ *
29
+ * @typedef {CjsMotherLodeMutationOptions & object} CjsMotherLodeInsertOptions
30
+ * @property {boolean} [replace=true] Whether an existing different resource may be displaced.
31
+ * @property {string} [variant] Variant used only by the legacy resource-first insertion form.
32
+ * @property {number} [bytes=0] Caller-supplied non-negative safe-integer eviction weight.
33
+ * @property {boolean} [cacheable=true] Whether policy may explicitly admit the entry to the byte cache.
34
+ * @property {boolean} [cached=false] Admit the entry to oldest-first trimming and `ClearCached()`.
35
+ * @property {number} [frame] Explicit non-negative activity frame; otherwise the counter advances.
36
+ * @property {number} [time] Explicit non-negative activity timestamp; otherwise `now()` is called.
37
+ */
38
+
39
+ /**
40
+ * Exact-owner conditional replacement options.
41
+ *
42
+ * @typedef {CjsMotherLodeInsertOptions & object} CjsMotherLodeConditionalReplaceOptions
43
+ * @property {() => boolean} [commitGuard] Final side-effect-free caller authority check performed immediately before the exact-record comparison.
44
+ */
45
+
46
+ /**
47
+ * Explicit inactivity-sweep policy for {@link CjsMotherLode#PurgeInactive}.
48
+ * At least one identity or payload limit must be supplied for work to occur.
49
+ * When both frame and time limits exist, reaching either limit is sufficient.
50
+ *
51
+ * @typedef {CjsMotherLodeMutationOptions & object} CjsMotherLodePurgeOptions
52
+ * @property {number} [frame] Current non-negative activity frame; otherwise the registry advances once.
53
+ * @property {number} [time] Current non-negative timestamp in milliseconds; otherwise `now()` is called.
54
+ * @property {number} [maxIdleFrames] Frames after which an unlocked identity is removed and cleaned.
55
+ * @property {number} [maxIdleMilliseconds] Milliseconds after which an unlocked identity is removed and cleaned.
56
+ * @property {number} [payloadMaxIdleFrames] Frames after which an unlocked CPU payload is released.
57
+ * @property {number} [payloadMaxIdleMilliseconds] Milliseconds after which an unlocked CPU payload is released.
58
+ */
59
+
60
+ /**
61
+ * Immutable result from one explicit inactivity sweep.
62
+ *
63
+ * @typedef {object} CjsMotherLodePurgeResult
64
+ * @property {number} frame Sweep activity frame.
65
+ * @property {number} time Sweep timestamp in milliseconds.
66
+ * @property {number} purged Number of canonical identities removed and cleaned.
67
+ * @property {number} payloadsReleased Number of payloads released while retaining their identities.
68
+ * @property {number} locked Number of locked identities skipped by the sweep.
69
+ * @property {readonly string[]} purgedKeys Canonical identities removed by the sweep.
70
+ * @property {readonly string[]} payloadKeys Canonical identities whose payload lease expired.
71
+ */
72
+
73
+ /**
74
+ * Immutable result from deterministic recorded-byte cache housekeeping.
75
+ *
76
+ * Only explicitly cached, cacheable, unlocked records contribute to the
77
+ * budget. Zero-byte records do not create pressure and are retained because
78
+ * removing them cannot reduce the recorded byte total.
79
+ *
80
+ * @typedef {object} CjsMotherLodeCacheTrimResult
81
+ * @property {number} cacheSize Configured recorded-byte budget.
82
+ * @property {number} beforeBytes Explicit cached bytes before housekeeping.
83
+ * @property {number} afterBytes Explicit cached bytes after housekeeping.
84
+ * @property {number} evictedBytes Recorded bytes removed successfully.
85
+ * @property {boolean} overBudget Whether retained cached records still exceed the budget.
86
+ * @property {number} evicted Number of complete canonical identities removed.
87
+ * @property {number} failed Number of candidate identities that reported cleanup or purge-state failure.
88
+ * @property {readonly string[]} evictedKeys Canonical identities removed oldest-first.
89
+ * @property {readonly string[]} failedKeys Canonical identities that reported a failure.
90
+ */
91
+
92
+ /**
93
+ * Activity-update options accepted by {@link CjsMotherLode#KeepAlive}.
94
+ *
95
+ * @typedef {object} CjsMotherLodeActivityOptions
96
+ * @property {number} [frame] Explicit non-negative activity frame; otherwise the counter advances.
97
+ * @property {number} [time] Explicit non-negative activity timestamp; otherwise `now()` is called.
98
+ */
99
+
100
+ /**
101
+ * Immutable result returned by {@link CjsMotherLode#Insert}.
102
+ *
103
+ * @typedef {object} CjsMotherLodeInsertResult
104
+ * @property {string} key Canonical resolved identity key.
105
+ * @property {object|Function} resource Resource that owns the canonical identity after the call.
106
+ * @property {boolean} inserted Whether the supplied resource was newly registered.
107
+ * @property {boolean} replaced Whether a different registered resource was displaced.
108
+ * @property {object|Function|null} displaced Previous resource returned after successful cleanup or retention.
109
+ */
110
+
111
+ /**
112
+ * Immutable result from an exact-owner conditional replacement.
113
+ *
114
+ * @typedef {object} CjsMotherLodeConditionalReplaceResult
115
+ * @property {string} key Canonical resolved identity key.
116
+ * @property {boolean} committed Whether the expected owner was replaced.
117
+ * @property {object|Function|null} resource Canonical resource after the compare-and-swap attempt.
118
+ * @property {object|Function|null} displaced Former exact owner when the replacement committed.
119
+ */
120
+
121
+ /**
122
+ * Immutable diagnostic snapshot returned by {@link CjsMotherLode#GetStats}.
123
+ *
124
+ * @typedef {object} CjsMotherLodeStats
125
+ * @property {number} count Compatibility identity count.
126
+ * @property {number} size Canonical identity count.
127
+ * @property {number} live Number of entries not explicitly classified as cached.
128
+ * @property {number} cached Number of explicitly cached entries.
129
+ * @property {number} locked Number of entries with at least one eviction lock.
130
+ * @property {number} payloads Number of entries currently retaining a CPU payload.
131
+ * @property {number} bytes Total caller-supplied eviction weight.
132
+ * @property {number} cacheBytes Byte estimate for explicitly cached entries.
133
+ * @property {number} cacheSize Configured recorded-byte cache budget.
134
+ * @property {number} activityFrame Highest activity frame observed by this registry.
135
+ * @property {Readonly<Record<string, number>>} states Resource count grouped by state name.
136
+ * @property {readonly string[]} paths Unique resource paths in encounter order.
137
+ */
138
+
139
+ /**
140
+ * Strong, deterministic JavaScript resource registry.
141
+ *
142
+ * Carbon can move resources between weak live registration and a strong LRU
143
+ * cache. JavaScript ownership cannot reproduce that transition reliably, so
144
+ * entries remain strongly owned until explicit ownership removal or an
145
+ * explicit inactivity/recorded-byte policy evicts them.
146
+ */
147
+ class CjsMotherLode {
148
+ #active = false;
149
+ #activityFrame = 0;
150
+ #cacheSize = DEFAULT_CACHE_SIZE;
151
+ #entries = new Map();
152
+ #nextCacheSequence = 1;
153
+ #now = defaultNow;
154
+
155
+ /**
156
+ * Create an active deterministic resource registry.
157
+ *
158
+ * @param {CjsMotherLodeOptions} [options={}] Clock and recorded-byte cache configuration.
159
+ * @throws {TypeError} If options, the clock, or the cache byte budget are invalid.
160
+ */
161
+ constructor(options = {}) {
162
+ if (!options || typeof options !== "object" || Array.isArray(options)) {
163
+ throw new TypeError("CjsMotherLode options must be an object.");
164
+ }
165
+ if (options.now !== undefined) {
166
+ if (typeof options.now !== "function") {
167
+ throw new TypeError("CjsMotherLode now must be a function.");
168
+ }
169
+ this.#now = options.now;
170
+ }
171
+ if (options.cacheSize !== undefined) {
172
+ this.SetCacheSize(options.cacheSize);
173
+ }
174
+ this.Startup();
175
+ }
176
+
177
+ /**
178
+ * Activate resource registration without changing existing entries.
179
+ * Repeated calls are idempotent.
180
+ *
181
+ * @returns {CjsMotherLode} This registry.
182
+ */
183
+ Startup() {
184
+ this.#active = true;
185
+ return this;
186
+ }
187
+
188
+ /**
189
+ * Stop registration and deterministically remove every owned entry.
190
+ * Default cleanup destroys attached adapter allocations and releases CPU
191
+ * payloads. Repeated calls are idempotent after a successful cleanup.
192
+ *
193
+ * @param {CjsMotherLodeMutationOptions} [options={}] Cleanup policy applied to every entry.
194
+ * @returns {CjsMotherLode} This inactive registry.
195
+ * @throws {AggregateError} If cleanup fails for one or more entries; registration is still stopped.
196
+ */
197
+ Shutdown(options = {}) {
198
+ try {
199
+ this.Clear(options);
200
+ } finally {
201
+ this.#active = false;
202
+ }
203
+ return this;
204
+ }
205
+
206
+ /**
207
+ * Return whether new resources may currently be inserted.
208
+ *
209
+ * @returns {boolean} `true` between `Startup()` and `Shutdown()`.
210
+ */
211
+ IsStarted() {
212
+ return this.#active;
213
+ }
214
+
215
+ /**
216
+ * Insert a canonical key and report whether an existing owner was displaced.
217
+ *
218
+ * The legacy `Insert(resource, path, variant)` form remains accepted while
219
+ * ResMan and consumers migrate to `Insert(key, resource, options)`.
220
+ * Cleanup completes before canonical ownership changes, so a cleanup error
221
+ * leaves the previous resource registered and the supplied resource owned by
222
+ * its caller.
223
+ *
224
+ * @param {string|object|Function} keyOrResource Canonical key, or the legacy resource object.
225
+ * @param {object|Function|string} resourceOrPath Resource object, or the legacy source path.
226
+ * @param {CjsMotherLodeInsertOptions|string} [optionsOrVariant={}] Insertion options, or legacy variant.
227
+ * @returns {CjsMotherLodeInsertResult} Immutable canonical ownership result.
228
+ * @throws {TypeError} If the key, resource, metadata, or activity values are invalid.
229
+ * @throws {Error} If the registry is inactive or displaced-resource cleanup fails.
230
+ */
231
+ Insert(keyOrResource, resourceOrPath, optionsOrVariant = {}) {
232
+ if (!this.#active) {
233
+ throw motherLodeInactiveError();
234
+ }
235
+ const {
236
+ key,
237
+ resource,
238
+ options
239
+ } = normalizeInsertArguments(keyOrResource, resourceOrPath, optionsOrVariant);
240
+ assertResource(resource);
241
+ const existing = this.#entries.get(key) || null;
242
+ if (existing && existing.resource === resource) {
243
+ const updated = {
244
+ ...existing
245
+ };
246
+ this.#UpdateRecord(updated, options);
247
+ this.#AssertRecordedBytesTotal(updated, existing);
248
+ this.#TouchRecord(updated, options);
249
+ Object.assign(existing, updated);
250
+ return freezeInsertResult(key, resource, false, false, null);
251
+ }
252
+ if (existing && options.replace === false) {
253
+ return freezeInsertResult(key, existing.resource, false, false, null);
254
+ }
255
+ const record = this.#CreateRecord(key, resource, options, existing);
256
+ if (existing) {
257
+ this.#CleanupRecord(existing, options, "replace", true);
258
+ }
259
+ this.#entries.set(key, record);
260
+ return freezeInsertResult(key, resource, true, Boolean(existing), existing?.resource || null);
261
+ }
262
+
263
+ /**
264
+ * Replace one exact canonical owner without running user cleanup between the
265
+ * final ownership comparison and registry publication.
266
+ *
267
+ * The prepared replacement becomes canonical first; displaced-owner cleanup
268
+ * then runs exactly once. A cleanup failure therefore cannot roll lookup
269
+ * ownership back to a partially cleaned former resource. Such a failure is
270
+ * reported as `CJS_MOTHERLODE_REPLACE_CLEANUP_FAILED` with a committed
271
+ * result attached. When the expected owner is no longer current, no mutation
272
+ * or cleanup occurs and `committed` is `false`.
273
+ *
274
+ * Recorded bytes and cacheability inherit from the expected record unless
275
+ * explicitly supplied. The replacement is live by default because a
276
+ * successful staged reload is an activity observation.
277
+ *
278
+ * @param {string} key Canonical resolved identity key.
279
+ * @param {object|Function} expected Exact resource that must still own `key`.
280
+ * @param {object|Function} resource Fully prepared replacement resource.
281
+ * @param {CjsMotherLodeConditionalReplaceOptions} [options={}] Replacement metadata, authority guard, and displaced-owner cleanup policy.
282
+ * @returns {CjsMotherLodeConditionalReplaceResult} Immutable compare-and-swap outcome.
283
+ * @throws {TypeError} If the key, resources, metadata, or activity values are invalid.
284
+ * @throws {Error} If the registry is inactive or the prepared resource aliases the expected owner.
285
+ * @throws {AggregateError} After a committed swap when displaced-owner cleanup fails.
286
+ */
287
+ ReplaceExpected(key, expected, resource, options = {}) {
288
+ if (!this.#active) {
289
+ throw motherLodeInactiveError();
290
+ }
291
+ const resolvedKey = normalizeResolvedKey(key);
292
+ const policy = normalizeOptions(options, "conditional replace");
293
+ if (policy.commitGuard !== undefined && typeof policy.commitGuard !== "function") {
294
+ throw new TypeError("CjsMotherLode conditional replace commitGuard must be a function.");
295
+ }
296
+ assertResource(expected);
297
+ assertResource(resource);
298
+ if (resource === expected) {
299
+ const error = new Error("CjsMotherLode conditional replacement requires a distinct resource.");
300
+ error.code = "CJS_MOTHERLODE_REPLACE_ALIAS";
301
+ throw error;
302
+ }
303
+ const existing = this.#entries.get(resolvedKey) || null;
304
+ if (!existing || existing.resource !== expected) {
305
+ return freezeConditionalReplaceResult(resolvedKey, false, existing?.resource || null, null);
306
+ }
307
+ const recordOptions = {
308
+ ...policy,
309
+ bytes: policy.bytes === undefined ? existing.bytes : policy.bytes,
310
+ cacheable: policy.cacheable === undefined ? existing.cacheable : policy.cacheable,
311
+ cached: policy.cached === undefined ? false : policy.cached
312
+ };
313
+ const record = this.#CreateRecord(resolvedKey, resource, recordOptions, existing);
314
+
315
+ // Re-check after clock/metadata validation, then publish with no user code
316
+ // between the exact-record comparison and Map mutation.
317
+ if (policy.commitGuard && !policy.commitGuard() || this.#entries.get(resolvedKey) !== existing) {
318
+ return freezeConditionalReplaceResult(resolvedKey, false, this.#entries.get(resolvedKey)?.resource || null, null);
319
+ }
320
+ this.#entries.set(resolvedKey, record);
321
+ const result = freezeConditionalReplaceResult(resolvedKey, true, resource, expected);
322
+ try {
323
+ this.#CleanupRecord(existing, policy, "conditional replace");
324
+ } catch (cause) {
325
+ const error = new AggregateError([cause], `CjsMotherLode conditional replacement committed but cleanup failed for ${resolvedKey}.`);
326
+ error.code = "CJS_MOTHERLODE_REPLACE_CLEANUP_FAILED";
327
+ error.result = result;
328
+ throw error;
329
+ }
330
+ return result;
331
+ }
332
+
333
+ /**
334
+ * Test a canonical identity without loading or renewing its activity.
335
+ * Supplying `variant` retains compatibility with the former path-first API.
336
+ *
337
+ * @param {string} key Canonical resolved key, or normalized source path with `variant`.
338
+ * @param {string} [variant] Optional promised-output variant.
339
+ * @returns {boolean} Whether the resolved identity is registered.
340
+ * @throws {TypeError} If the identity cannot be normalized.
341
+ */
342
+ HasKey(key, variant = undefined) {
343
+ return this.#entries.has(normalizeLookupKey(key, variant));
344
+ }
345
+
346
+ /**
347
+ * Return a canonical resource without loading or renewing its activity.
348
+ * Supplying `variant` retains compatibility with the former path-first API.
349
+ *
350
+ * @param {string} key Canonical resolved key, or normalized source path with `variant`.
351
+ * @param {string} [variant] Optional promised-output variant.
352
+ * @returns {object|Function|null} Registered resource, or `null` when absent.
353
+ * @throws {TypeError} If the identity cannot be normalized.
354
+ */
355
+ Lookup(key, variant = undefined) {
356
+ return this.#entries.get(normalizeLookupKey(key, variant))?.resource || null;
357
+ }
358
+
359
+ /**
360
+ * Remove one canonical identity and clean its manager-owned payloads.
361
+ *
362
+ * `Delete(path, variant)` remains accepted for compatibility.
363
+ *
364
+ * @param {string} key Canonical resolved key, or normalized source path with a variant.
365
+ * @param {string|CjsMotherLodeMutationOptions} [variantOrOptions] Compatibility variant or cleanup policy.
366
+ * @param {CjsMotherLodeMutationOptions} [maybeOptions] Cleanup policy when a variant is supplied.
367
+ * @returns {boolean} Whether an entry was removed.
368
+ * @throws {TypeError} If identity or cleanup options are invalid.
369
+ * @throws {Error} If resource cleanup fails after the identity is removed.
370
+ */
371
+ Delete(key, variantOrOptions = undefined, maybeOptions = undefined) {
372
+ const {
373
+ resolvedKey,
374
+ options
375
+ } = normalizeDeleteArguments(key, variantOrOptions, maybeOptions);
376
+ const record = this.#entries.get(resolvedKey);
377
+ if (!record) return false;
378
+ this.#entries.delete(resolvedKey);
379
+ this.#CleanupRecord(record, options, "delete");
380
+ return true;
381
+ }
382
+
383
+ /**
384
+ * Remove every promised-output variant for one normalized source path.
385
+ * All matching identities are forgotten before cleanup begins.
386
+ *
387
+ * @param {string} path Source resource path whose canonical variants are removed.
388
+ * @param {CjsMotherLodeMutationOptions} [options={}] Cleanup policy for removed resources.
389
+ * @returns {boolean} Whether at least one variant was removed.
390
+ * @throws {TypeError} If the path is invalid.
391
+ * @throws {AggregateError} If cleanup fails for one or more removed resources.
392
+ */
393
+ DeleteAllVariants(path, options = {}) {
394
+ const normalizedPath = normalizePath(path);
395
+ const prefix = `${normalizedPath}\u0000`;
396
+ const records = [];
397
+ for (const [key, record] of this.#entries) {
398
+ if (key === normalizedPath || key.startsWith(prefix)) {
399
+ records.push(record);
400
+ this.#entries.delete(key);
401
+ }
402
+ }
403
+ this.#CleanupRecords(records, options, "delete variants");
404
+ return records.length > 0;
405
+ }
406
+
407
+ /**
408
+ * Deterministically remove and clean every owned identity while preserving
409
+ * the registry's active/inactive lifecycle state.
410
+ *
411
+ * @param {CjsMotherLodeMutationOptions} [options={}] Cleanup policy for removed resources.
412
+ * @returns {CjsMotherLode} This empty registry.
413
+ * @throws {AggregateError} If cleanup fails for one or more removed resources.
414
+ */
415
+ Clear(options = {}) {
416
+ const records = [...this.#entries.values()];
417
+ this.#entries.clear();
418
+ this.#CleanupRecords(records, options, "clear");
419
+ return this;
420
+ }
421
+
422
+ /**
423
+ * Remove entries explicitly classified as cached and not locked.
424
+ *
425
+ * JavaScript cannot infer external ownership, so only callers that inserted
426
+ * an entry with `{ cached: true }` classify it for this operation.
427
+ *
428
+ * @param {CjsMotherLodeMutationOptions} [options={}] Cleanup policy for removed cache entries.
429
+ * @returns {number} Number of entries removed before cleanup.
430
+ * @throws {AggregateError} If cleanup fails for one or more removed resources.
431
+ */
432
+ ClearCached(options = {}) {
433
+ const records = [];
434
+ for (const [key, record] of this.#entries) {
435
+ if (record.cached && record.lockCount === 0) {
436
+ records.push(record);
437
+ this.#entries.delete(key);
438
+ }
439
+ }
440
+ this.#CleanupRecords(records, options, "clear cached");
441
+ return records.length;
442
+ }
443
+
444
+ /**
445
+ * Record explicit use and promote an explicitly cached entry to live.
446
+ * The overload `KeepAlive(path, variant, options)` remains available while
447
+ * callers migrate to canonical keys.
448
+ *
449
+ * @param {string} key Canonical resolved key, or normalized source path with a variant.
450
+ * @param {string|CjsMotherLodeActivityOptions} [variantOrOptions] Compatibility variant or activity values.
451
+ * @param {CjsMotherLodeActivityOptions} [maybeOptions] Activity values when a variant is supplied.
452
+ * @returns {object|Function|null} Renewed resource, or `null` when absent.
453
+ * @throws {TypeError} If identity or activity values are invalid.
454
+ */
455
+ KeepAlive(key, variantOrOptions = undefined, maybeOptions = undefined) {
456
+ const {
457
+ resolvedKey,
458
+ options
459
+ } = normalizeActivityArguments(key, variantOrOptions, maybeOptions);
460
+ const record = this.#entries.get(resolvedKey);
461
+ if (!record) return null;
462
+ record.cached = false;
463
+ record.cacheSequence = 0;
464
+ this.#TouchRecord(record, options);
465
+ return record.resource;
466
+ }
467
+
468
+ /**
469
+ * Explicitly renew both canonical identity activity and the attached CPU
470
+ * payload lease. The resource-facing `KeepPayloadAlive()` method delegates
471
+ * here when CjsResMan owns the handle. No payload is read or created.
472
+ *
473
+ * @param {string} key Canonical resolved key, or normalized source path with a variant.
474
+ * @param {string|CjsMotherLodeActivityOptions} [variantOrOptions] Compatibility variant or activity values.
475
+ * @param {CjsMotherLodeActivityOptions} [maybeOptions] Activity values when a variant is supplied.
476
+ * @returns {object|Function|null} Renewed resource, or `null` when absent.
477
+ * @throws {TypeError} If identity or activity values are invalid.
478
+ */
479
+ KeepPayloadAlive(key, variantOrOptions = undefined, maybeOptions = undefined) {
480
+ const {
481
+ resolvedKey,
482
+ options
483
+ } = normalizeActivityArguments(key, variantOrOptions, maybeOptions);
484
+ const record = this.#entries.get(resolvedKey);
485
+ if (!record) return null;
486
+ record.cached = false;
487
+ record.cacheSequence = 0;
488
+ this.#TouchRecord(record, options);
489
+ record.payloadLastUsedFrame = record.lastUsedFrame;
490
+ record.payloadLastUsedTime = record.lastUsedTime;
491
+ return record.resource;
492
+ }
493
+
494
+ /**
495
+ * Add one eviction lock and promote the identity from cached to live.
496
+ * Locking an absent identity is a no-op.
497
+ *
498
+ * @param {string} key Canonical resolved key, or normalized source path with `variant`.
499
+ * @param {string} [variant] Optional promised-output variant.
500
+ * @returns {number} New lock count, or `0` when the identity is absent.
501
+ * @throws {TypeError} If the identity cannot be normalized.
502
+ */
503
+ Lock(key, variant = undefined) {
504
+ const record = this.#entries.get(normalizeLookupKey(key, variant));
505
+ if (!record) return 0;
506
+ record.lockCount += 1;
507
+ record.cached = false;
508
+ record.cacheSequence = 0;
509
+ this.#TouchRecord(record, {});
510
+ return record.lockCount;
511
+ }
512
+
513
+ /**
514
+ * Release one eviction lock without allowing the count to underflow.
515
+ * Unlocking does not itself classify an entry as cached or evict it.
516
+ *
517
+ * @param {string} key Canonical resolved key, or normalized source path with `variant`.
518
+ * @param {string} [variant] Optional promised-output variant.
519
+ * @returns {number} Remaining lock count, or `0` when absent or already unlocked.
520
+ * @throws {TypeError} If the identity cannot be normalized.
521
+ */
522
+ Unlock(key, variant = undefined) {
523
+ const record = this.#entries.get(normalizeLookupKey(key, variant));
524
+ if (!record) return 0;
525
+ if (record.lockCount > 0) record.lockCount -= 1;
526
+ return record.lockCount;
527
+ }
528
+
529
+ /**
530
+ * Run one explicit deterministic inactivity sweep.
531
+ *
532
+ * Identity limits remove unlocked canonical entries, destroy their adapter
533
+ * allocations, release payloads, detach resource-facing lifecycle callbacks,
534
+ * and mark CjsResource-compatible handles purged. Payload limits release only
535
+ * the CPU payload while retaining identity and adapters. The sweep never
536
+ * fetches, reloads, prepares, or infers external JavaScript ownership.
537
+ *
538
+ * Cleanup is transactional per identity: a failed cleanup leaves that record
539
+ * canonical and reports a contextual error after all candidates are visited.
540
+ * Successful candidates are still purged when another candidate fails.
541
+ *
542
+ * @param {CjsMotherLodePurgeOptions} [options={}] Explicit sweep point, inactivity limits, and cleanup policy.
543
+ * @returns {CjsMotherLodePurgeResult} Immutable counts and affected canonical keys.
544
+ * @throws {TypeError} If the sweep point, limits, or cleanup policy are invalid.
545
+ * @throws {AggregateError} If one or more cleanup, payload-release, or purge-state operations fail.
546
+ */
547
+ PurgeInactive(options = {}) {
548
+ const policy = normalizePurgeOptions(options, this.#activityFrame, this.#now);
549
+ this.#activityFrame = Math.max(this.#activityFrame, policy.frame);
550
+ const purgedKeys = [];
551
+ const payloadKeys = [];
552
+ const errors = [];
553
+ let locked = 0;
554
+ for (const [key, record] of [...this.#entries]) {
555
+ if (record.lockCount > 0) {
556
+ locked += 1;
557
+ continue;
558
+ }
559
+ if (isInactive(record.lastUsedFrame, record.lastUsedTime, policy.frame, policy.time, policy.maxIdleFrames, policy.maxIdleMilliseconds)) {
560
+ try {
561
+ this.#CleanupRecord(record, policy, "purge inactive", true);
562
+ } catch (error) {
563
+ errors.push(error);
564
+ continue;
565
+ }
566
+ this.#entries.delete(key);
567
+ purgedKeys.push(key);
568
+ try {
569
+ record.resource?.MarkPurged?.();
570
+ } catch (cause) {
571
+ errors.push(motherLodeCleanupError("mark purged", key, record.resource, cause));
572
+ }
573
+ continue;
574
+ }
575
+ if (policy.releasePayload !== false && hasOwnedPayload(record.resource) && isInactive(record.payloadLastUsedFrame, record.payloadLastUsedTime, policy.frame, policy.time, policy.payloadMaxIdleFrames, policy.payloadMaxIdleMilliseconds)) {
576
+ try {
577
+ record.resource.ReleasePayload();
578
+ payloadKeys.push(key);
579
+ } catch (cause) {
580
+ errors.push(motherLodeCleanupError("release inactive payload", key, record.resource, cause));
581
+ }
582
+ }
583
+ }
584
+ const result = freezePurgeResult(policy.frame, policy.time, purgedKeys, payloadKeys, locked);
585
+ if (errors.length) {
586
+ const error = new AggregateError(errors, "CjsMotherLode inactivity purge failed.");
587
+ error.code = "CJS_MOTHERLODE_PURGE_FAILED";
588
+ error.result = result;
589
+ throw error;
590
+ }
591
+ return result;
592
+ }
593
+
594
+ /**
595
+ * Remove complete explicitly cached identities until their recorded byte
596
+ * total fits the configured budget.
597
+ *
598
+ * Candidates are ordered by explicit cache admission, oldest first. Lookup
599
+ * is intentionally pure; callers re-admit a live identity by inserting the
600
+ * same handle with `{ cached: true }`. Locks and `KeepAlive()` promote an
601
+ * entry to live and therefore remove it from byte-budget consideration.
602
+ *
603
+ * Cleanup is transactional per candidate. A failure leaves that candidate
604
+ * canonical, later candidates are still attempted, and the final aggregate
605
+ * error carries the partial immutable result. Successful pressure eviction
606
+ * destroys adapters, releases payloads, detaches lifecycle callbacks, marks
607
+ * compatible handles `PURGED` by default, and never fetches or reconstructs
608
+ * data. Explicit cleanup overrides retain their documented ownership.
609
+ *
610
+ * @param {CjsMotherLodeMutationOptions} [options={}] Cleanup policy for pressure-evicted identities.
611
+ * @returns {CjsMotherLodeCacheTrimResult} Immutable byte totals and affected canonical keys.
612
+ * @throws {TypeError} If cleanup options are invalid.
613
+ * @throws {AggregateError} If cleanup or purge-state publication fails for one or more candidates.
614
+ */
615
+ TrimCache(options = {}) {
616
+ const policy = normalizeOptions(options, "cache trim");
617
+ const beforeBytes = this.#GetCacheBytes();
618
+ const evictedKeys = [];
619
+ const failedKeys = [];
620
+ const errors = [];
621
+ let evictedBytes = 0;
622
+ if (beforeBytes > this.#cacheSize) {
623
+ const candidates = [...this.#entries.values()].filter(record => record.cacheable && record.cached && record.lockCount === 0 && record.bytes > 0).sort((a, b) => a.cacheSequence - b.cacheSequence);
624
+ for (const record of candidates) {
625
+ if (this.#GetCacheBytes() <= this.#cacheSize) break;
626
+ if (this.#entries.get(record.key) !== record || !record.cacheable || !record.cached || record.lockCount > 0 || record.bytes <= 0) {
627
+ continue;
628
+ }
629
+ try {
630
+ this.#CleanupRecord(record, policy, "trim cache", true);
631
+ } catch (error) {
632
+ errors.push(error);
633
+ failedKeys.push(record.key);
634
+ continue;
635
+ }
636
+ if (this.#entries.get(record.key) === record) {
637
+ this.#entries.delete(record.key);
638
+ }
639
+ evictedKeys.push(record.key);
640
+ evictedBytes += record.bytes;
641
+ try {
642
+ record.resource?.MarkPurged?.();
643
+ } catch (cause) {
644
+ errors.push(motherLodeCleanupError("mark cache eviction purged", record.key, record.resource, cause));
645
+ failedKeys.push(record.key);
646
+ }
647
+ }
648
+ }
649
+ const afterBytes = this.#GetCacheBytes();
650
+ const result = freezeCacheTrimResult(this.#cacheSize, beforeBytes, afterBytes, evictedBytes, evictedKeys, failedKeys);
651
+ if (errors.length) {
652
+ const error = new AggregateError(errors, "CjsMotherLode cache trim failed.");
653
+ error.code = "CJS_MOTHERLODE_CACHE_TRIM_FAILED";
654
+ error.result = result;
655
+ throw error;
656
+ }
657
+ return result;
658
+ }
659
+
660
+ /**
661
+ * Configure and immediately enforce the recorded-byte budget for explicit
662
+ * cached entries. The new budget remains installed if partial cleanup fails,
663
+ * matching Carbon's policy-first `SetCacheSize` behavior.
664
+ *
665
+ * @param {number} bytes Non-negative safe-integer byte budget.
666
+ * @param {CjsMotherLodeMutationOptions} [options={}] Cleanup policy for entries displaced by the smaller budget.
667
+ * @returns {CjsMotherLode} This registry.
668
+ * @throws {TypeError} If `bytes` or cleanup options are invalid.
669
+ * @throws {AggregateError} If enforcing the new budget cannot clean one or more cached entries.
670
+ */
671
+ SetCacheSize(bytes, options = {}) {
672
+ assertNonNegativeSafeInteger(bytes, "CjsMotherLode cache size");
673
+ const policy = normalizeOptions(options, "set cache size");
674
+ this.#cacheSize = bytes;
675
+ this.TrimCache(policy);
676
+ return this;
677
+ }
678
+
679
+ /**
680
+ * Return the configured recorded-byte cache budget.
681
+ *
682
+ * @returns {number} Non-negative safe-integer byte budget.
683
+ */
684
+ GetCacheSize() {
685
+ return this.#cacheSize;
686
+ }
687
+
688
+ /**
689
+ * Return an immutable snapshot of canonical identity keys.
690
+ *
691
+ * @returns {readonly string[]} Canonical keys in insertion order.
692
+ */
693
+ GetKeys() {
694
+ return Object.freeze([...this.#entries.keys()]);
695
+ }
696
+
697
+ /**
698
+ * Return an immutable snapshot of canonical resources.
699
+ *
700
+ * @returns {readonly (object|Function)[]} Resources in insertion order.
701
+ */
702
+ GetValues() {
703
+ return Object.freeze([...this.#entries.values()].map(record => record.resource));
704
+ }
705
+
706
+ /**
707
+ * Return the number of canonical identities currently registered.
708
+ *
709
+ * @returns {number} Registry entry count.
710
+ */
711
+ GetSize() {
712
+ return this.#entries.size;
713
+ }
714
+
715
+ /**
716
+ * Return an immutable diagnostic snapshot without renewing activity.
717
+ * Byte totals include only caller-supplied estimates and do not measure
718
+ * JavaScript reachability or trigger cache policy.
719
+ *
720
+ * @returns {CjsMotherLodeStats} Current identity, activity, state, and byte totals.
721
+ */
722
+ GetStats() {
723
+ let bytes = 0;
724
+ let cacheBytes = 0;
725
+ let cached = 0;
726
+ let locked = 0;
727
+ let payloads = 0;
728
+ const paths = new Set();
729
+ const states = {};
730
+ for (const record of this.#entries.values()) {
731
+ bytes += record.bytes;
732
+ if (record.cached) {
733
+ cached += 1;
734
+ cacheBytes += record.bytes;
735
+ }
736
+ if (record.lockCount > 0) locked += 1;
737
+ if (hasOwnedPayload(record.resource)) payloads += 1;
738
+ const path = record.resource?.GetPath?.() || record.resource?.path || getKeyPath(record.key);
739
+ if (path) paths.add(path);
740
+ const state = typeof record.resource?.state === "string" ? record.resource.state : "unknown";
741
+ states[state] = (states[state] || 0) + 1;
742
+ }
743
+ return Object.freeze({
744
+ count: this.#entries.size,
745
+ size: this.#entries.size,
746
+ live: this.#entries.size - cached,
747
+ cached,
748
+ locked,
749
+ payloads,
750
+ bytes,
751
+ cacheBytes,
752
+ cacheSize: this.#cacheSize,
753
+ activityFrame: this.#activityFrame,
754
+ states: Object.freeze(states),
755
+ paths: Object.freeze([...paths])
756
+ });
757
+ }
758
+
759
+ /**
760
+ * Return a snapshot iterator of canonical key/resource pairs.
761
+ * Later registry mutations do not change the captured key list.
762
+ *
763
+ * @returns {IterableIterator<[string, object|Function]>} Insertion-ordered entry iterator.
764
+ */
765
+ Entries() {
766
+ return this.GetKeys().map(key => [key, this.#entries.get(key).resource])[Symbol.iterator]();
767
+ }
768
+
769
+ /**
770
+ * Test a path and optional variant through the temporary legacy API.
771
+ *
772
+ * @deprecated Use `HasKey(getMotherLodeKey(path, variant))`.
773
+ * @param {string} path Source resource path.
774
+ * @param {string} [variant] Optional promised-output variant.
775
+ * @returns {boolean} Whether the resolved identity is registered.
776
+ */
777
+ Has(path, variant = undefined) {
778
+ return this.HasKey(path, variant);
779
+ }
780
+
781
+ /**
782
+ * Return the registry count through the temporary legacy API.
783
+ *
784
+ * @deprecated Use `GetSize()`.
785
+ * @returns {number} Registry entry count.
786
+ */
787
+ GetCount() {
788
+ return this.GetSize();
789
+ }
790
+
791
+ /**
792
+ * Remove all variants through the temporary legacy API.
793
+ *
794
+ * @deprecated Use `DeleteAllVariants(path, options)`.
795
+ * @param {string} path Source resource path.
796
+ * @param {CjsMotherLodeMutationOptions} [options={}] Cleanup policy for removed resources.
797
+ * @returns {boolean} Whether at least one variant was removed.
798
+ */
799
+ DeleteAll(path, options = {}) {
800
+ return this.DeleteAllVariants(path, options);
801
+ }
802
+
803
+ /**
804
+ * Create and validate an internal ownership record before it is registered.
805
+ *
806
+ * @param {string} key Canonical resolved identity key.
807
+ * @param {object|Function} resource Caller-supplied resource owner.
808
+ * @param {CjsMotherLodeInsertOptions} options Validated insertion options.
809
+ * @param {object|null} [displaced=null] Existing record excluded from aggregate byte validation.
810
+ * @returns {object} Mutable internal ownership record.
811
+ */
812
+ #CreateRecord(key, resource, options, displaced = null) {
813
+ const record = {
814
+ key,
815
+ resource,
816
+ bytes: 0,
817
+ cacheable: true,
818
+ cached: false,
819
+ cacheSequence: 0,
820
+ lockCount: 0,
821
+ lastUsedFrame: 0,
822
+ lastUsedTime: 0,
823
+ payloadLastUsedFrame: 0,
824
+ payloadLastUsedTime: 0
825
+ };
826
+ this.#UpdateRecord(record, options);
827
+ this.#AssertRecordedBytesTotal(record, displaced);
828
+ this.#TouchRecord(record, options);
829
+ record.payloadLastUsedFrame = record.lastUsedFrame;
830
+ record.payloadLastUsedTime = record.lastUsedTime;
831
+ return record;
832
+ }
833
+
834
+ /**
835
+ * Apply explicit size/cache metadata while preserving lock invariants.
836
+ *
837
+ * @param {object} record Mutable internal ownership record.
838
+ * @param {CjsMotherLodeInsertOptions} options Metadata updates.
839
+ * @returns {object} The updated record.
840
+ */
841
+ #UpdateRecord(record, options) {
842
+ if (options.bytes !== undefined) {
843
+ assertNonNegativeSafeInteger(options.bytes, "CjsMotherLode entry bytes");
844
+ record.bytes = options.bytes;
845
+ }
846
+ if (options.cacheable !== undefined) record.cacheable = Boolean(options.cacheable);
847
+ if (options.cached !== undefined) {
848
+ record.cached = record.cacheable && record.lockCount === 0 && Boolean(options.cached);
849
+ record.cacheSequence = record.cached ? this.#nextCacheSequence++ : 0;
850
+ }
851
+ if (!record.cacheable || record.lockCount > 0) {
852
+ record.cached = false;
853
+ record.cacheSequence = 0;
854
+ }
855
+ return record;
856
+ }
857
+
858
+ /**
859
+ * Reject metadata that would make aggregate recorded-byte arithmetic lose
860
+ * integer precision. This keeps trim results and diagnostics exact without
861
+ * attempting to measure JavaScript object graphs heuristically.
862
+ *
863
+ * @param {object} candidate New or updated record.
864
+ * @param {object|null} [excluded=null] Existing record replaced by the candidate.
865
+ * @returns {void}
866
+ * @throws {RangeError} If aggregate recorded bytes exceed the safe-integer range.
867
+ */
868
+ #AssertRecordedBytesTotal(candidate, excluded = null) {
869
+ let total = BigInt(candidate.bytes);
870
+ for (const record of this.#entries.values()) {
871
+ if (record === excluded) continue;
872
+ total += BigInt(record.bytes);
873
+ if (total > MAX_SAFE_INTEGER_BIGINT) {
874
+ throw new RangeError("CjsMotherLode aggregate recorded bytes exceed Number.MAX_SAFE_INTEGER.");
875
+ }
876
+ }
877
+ }
878
+
879
+ /**
880
+ * Sum exact byte weights for explicit cached entries.
881
+ * Aggregate safety is maintained at insertion/update time.
882
+ *
883
+ * @returns {number} Exact explicit-cache byte total.
884
+ */
885
+ #GetCacheBytes() {
886
+ let bytes = 0;
887
+ for (const record of this.#entries.values()) {
888
+ if (record.cacheable && record.cached) bytes += record.bytes;
889
+ }
890
+ return bytes;
891
+ }
892
+
893
+ /**
894
+ * Record one explicit activity observation for an internal entry.
895
+ *
896
+ * @param {object} record Mutable internal ownership record.
897
+ * @param {CjsMotherLodeActivityOptions} options Optional frame/time override.
898
+ * @returns {object} The updated record.
899
+ * @throws {TypeError} If frame or time is invalid.
900
+ */
901
+ #TouchRecord(record, options) {
902
+ const frame = options.frame === undefined ? this.#activityFrame + 1 : options.frame;
903
+ assertNonNegativeSafeInteger(frame, "CjsMotherLode activity frame");
904
+ this.#activityFrame = Math.max(this.#activityFrame, frame);
905
+ record.lastUsedFrame = frame;
906
+ const time = options.time === undefined ? this.#now() : options.time;
907
+ if (typeof time !== "number" || !Number.isFinite(time) || time < 0) {
908
+ throw new TypeError("CjsMotherLode activity time must be a non-negative finite number.");
909
+ }
910
+ record.lastUsedTime = time;
911
+ return record;
912
+ }
913
+
914
+ /**
915
+ * Clean one displaced record and attach canonical ownership context to errors.
916
+ *
917
+ * @param {object} record Internal ownership record being removed.
918
+ * @param {CjsMotherLodeMutationOptions} options Cleanup policy.
919
+ * @param {string} operation Human-readable ownership operation.
920
+ * @param {boolean} [preserveOnFailure=false] Keep lifecycle binding when the record remains canonical.
921
+ * @returns {void}
922
+ * @throws {Error} Contextual `CJS_MOTHERLODE_CLEANUP_FAILED` error on failure.
923
+ */
924
+ #CleanupRecord(record, options, operation, preserveOnFailure = false) {
925
+ let cleanupComplete = false;
926
+ try {
927
+ cleanupOwnedResource(record.resource, options);
928
+ cleanupComplete = true;
929
+ detachResourceLifecycle(record.resource);
930
+ } catch (cause) {
931
+ if (!preserveOnFailure && !cleanupComplete) {
932
+ try {
933
+ detachResourceLifecycle(record.resource);
934
+ } catch (detachCause) {
935
+ cause = new AggregateError([cause, detachCause], "Resource cleanup and detach failed.");
936
+ }
937
+ }
938
+ throw motherLodeCleanupError(operation, record.key, record.resource, cause);
939
+ }
940
+ }
941
+
942
+ /**
943
+ * Clean every removed record and aggregate failures without skipping entries.
944
+ *
945
+ * @param {object[]} records Internal ownership records already removed from the registry.
946
+ * @param {CjsMotherLodeMutationOptions} options Shared cleanup policy.
947
+ * @param {string} operation Human-readable ownership operation.
948
+ * @returns {void}
949
+ * @throws {AggregateError} Contextual errors for every failed cleanup.
950
+ */
951
+ #CleanupRecords(records, options, operation) {
952
+ const errors = [];
953
+ for (const record of records) {
954
+ try {
955
+ cleanupOwnedResource(record.resource, options);
956
+ } catch (cause) {
957
+ errors.push(motherLodeCleanupError(operation, record.key, record.resource, cause));
958
+ }
959
+ try {
960
+ detachResourceLifecycle(record.resource);
961
+ } catch (cause) {
962
+ errors.push(motherLodeCleanupError(`${operation} detach`, record.key, record.resource, cause));
963
+ }
964
+ }
965
+ if (errors.length) {
966
+ throw new AggregateError(errors, `CjsMotherLode ${operation} cleanup failed.`);
967
+ }
968
+ }
969
+ }
970
+
971
+ /**
972
+ * Build the canonical normalized identity shared by CjsResMan and MotherLode.
973
+ * The source path and promised-output variant are separated with an internal null byte;
974
+ * variants therefore may not contain that delimiter.
975
+ *
976
+ * @param {string} path Source resource path normalized with Carbon path rules.
977
+ * @param {*} [variant=""] Stable build/outcome variant; empty values use the path alone.
978
+ * @returns {string} Canonical resolved MotherLode identity.
979
+ * @throws {TypeError} If the path is empty/invalid or the variant contains a null byte.
980
+ */
981
+ function getMotherLodeKey(path, variant = "") {
982
+ const normalizedPath = normalizePath(path);
983
+ if (variant === null || variant === undefined || variant === "") return normalizedPath;
984
+ const normalizedVariant = String(variant);
985
+ if (normalizedVariant.includes("\u0000")) {
986
+ throw new TypeError("CjsMotherLode variant may not contain a null character.");
987
+ }
988
+ return `${normalizedPath}\u0000${normalizedVariant}`;
989
+ }
990
+ function normalizeInsertArguments(keyOrResource, resourceOrPath, optionsOrVariant) {
991
+ if (typeof keyOrResource === "string") {
992
+ return {
993
+ key: normalizeResolvedKey(keyOrResource),
994
+ resource: resourceOrPath,
995
+ options: normalizeOptions(optionsOrVariant, "insert")
996
+ };
997
+ }
998
+ const resource = keyOrResource;
999
+ const path = resourceOrPath ?? resource?.GetPath?.() ?? resource?.path;
1000
+ const isOptions = optionsOrVariant && typeof optionsOrVariant === "object" && !Array.isArray(optionsOrVariant);
1001
+ const options = isOptions ? normalizeOptions(optionsOrVariant, "insert") : {};
1002
+ const variant = isOptions ? options.variant || "" : optionsOrVariant;
1003
+ return {
1004
+ key: getMotherLodeKey(path, variant),
1005
+ resource,
1006
+ options
1007
+ };
1008
+ }
1009
+ function normalizeDeleteArguments(key, variantOrOptions, maybeOptions) {
1010
+ if (variantOrOptions && typeof variantOrOptions === "object" && !Array.isArray(variantOrOptions)) {
1011
+ return {
1012
+ resolvedKey: normalizeResolvedKey(key),
1013
+ options: normalizeOptions(variantOrOptions, "delete")
1014
+ };
1015
+ }
1016
+ return {
1017
+ resolvedKey: normalizeLookupKey(key, variantOrOptions),
1018
+ options: normalizeOptions(maybeOptions || {}, "delete")
1019
+ };
1020
+ }
1021
+ function normalizeActivityArguments(key, variantOrOptions, maybeOptions) {
1022
+ if (variantOrOptions && typeof variantOrOptions === "object" && !Array.isArray(variantOrOptions)) {
1023
+ return {
1024
+ resolvedKey: normalizeResolvedKey(key),
1025
+ options: normalizeOptions(variantOrOptions, "activity")
1026
+ };
1027
+ }
1028
+ return {
1029
+ resolvedKey: normalizeLookupKey(key, variantOrOptions),
1030
+ options: normalizeOptions(maybeOptions || {}, "activity")
1031
+ };
1032
+ }
1033
+ function normalizeLookupKey(key, variant) {
1034
+ return variant === undefined ? normalizeResolvedKey(key) : getMotherLodeKey(key, variant);
1035
+ }
1036
+ function normalizeResolvedKey(key) {
1037
+ if (typeof key !== "string" || !key) {
1038
+ throw new TypeError("CjsMotherLode requires a canonical string key.");
1039
+ }
1040
+ const separator = key.indexOf("\u0000");
1041
+ return separator === -1 ? getMotherLodeKey(key) : getMotherLodeKey(key.slice(0, separator), key.slice(separator + 1));
1042
+ }
1043
+ function normalizePath(path) {
1044
+ const normalizedPath = normalizeResourcePath(path);
1045
+ if (!normalizedPath) throw new TypeError("CjsMotherLode requires a resource path.");
1046
+ return normalizedPath;
1047
+ }
1048
+ function normalizeOptions(options, operation) {
1049
+ if (options === null || options === undefined) return {};
1050
+ if (!options || typeof options !== "object" || Array.isArray(options)) {
1051
+ throw new TypeError(`CjsMotherLode ${operation} options must be an object.`);
1052
+ }
1053
+ return options;
1054
+ }
1055
+
1056
+ /**
1057
+ * Normalize one explicit inactivity sweep without mutating registry entries.
1058
+ *
1059
+ * @param {CjsMotherLodePurgeOptions} options Caller policy.
1060
+ * @param {number} activityFrame Current registry frame.
1061
+ * @param {() => number} now Registry clock.
1062
+ * @returns {CjsMotherLodePurgeOptions & {frame: number, time: number}} Validated sweep policy.
1063
+ */
1064
+ function normalizePurgeOptions(options, activityFrame, now) {
1065
+ const policy = normalizeOptions(options, "purge");
1066
+ const frame = policy.frame === undefined ? activityFrame + 1 : policy.frame;
1067
+ assertNonNegativeSafeInteger(frame, "CjsMotherLode purge frame");
1068
+ const time = policy.time === undefined ? now() : policy.time;
1069
+ assertNonNegativeFiniteNumber(time, "CjsMotherLode purge time");
1070
+ for (const name of ["maxIdleFrames", "payloadMaxIdleFrames"]) {
1071
+ if (policy[name] !== undefined) {
1072
+ assertNonNegativeSafeInteger(policy[name], `CjsMotherLode ${name}`);
1073
+ }
1074
+ }
1075
+ for (const name of ["maxIdleMilliseconds", "payloadMaxIdleMilliseconds"]) {
1076
+ if (policy[name] !== undefined) {
1077
+ assertNonNegativeFiniteNumber(policy[name], `CjsMotherLode ${name}`);
1078
+ }
1079
+ }
1080
+ return {
1081
+ ...policy,
1082
+ frame,
1083
+ time
1084
+ };
1085
+ }
1086
+
1087
+ /**
1088
+ * Test whether either configured frame/time limit has elapsed.
1089
+ *
1090
+ * @param {number} lastFrame Last explicit lease frame.
1091
+ * @param {number} lastTime Last explicit lease timestamp.
1092
+ * @param {number} frame Current sweep frame.
1093
+ * @param {number} time Current sweep timestamp.
1094
+ * @param {number|undefined} maxFrames Optional frame limit.
1095
+ * @param {number|undefined} maxMilliseconds Optional millisecond limit.
1096
+ * @returns {boolean} Whether at least one configured limit has elapsed.
1097
+ */
1098
+ function isInactive(lastFrame, lastTime, frame, time, maxFrames, maxMilliseconds) {
1099
+ const frameExpired = maxFrames !== undefined && frame >= lastFrame && frame - lastFrame >= maxFrames;
1100
+ const timeExpired = maxMilliseconds !== undefined && time >= lastTime && time - lastTime >= maxMilliseconds;
1101
+ return frameExpired || timeExpired;
1102
+ }
1103
+
1104
+ /**
1105
+ * Return whether a resource currently exposes a releasable CPU payload.
1106
+ *
1107
+ * @param {object|Function} resource Candidate resource.
1108
+ * @returns {boolean} Whether `ReleasePayload()` can remove an attached payload.
1109
+ */
1110
+ function hasOwnedPayload(resource) {
1111
+ return typeof resource?.HasPayload === "function" && typeof resource?.ReleasePayload === "function" && Boolean(resource.HasPayload());
1112
+ }
1113
+
1114
+ /**
1115
+ * Detach resource-facing lifecycle callbacks after canonical ownership ends.
1116
+ *
1117
+ * @param {object|Function} resource Removed resource.
1118
+ * @returns {void}
1119
+ */
1120
+ function detachResourceLifecycle(resource) {
1121
+ resource?.SetLifecycleController?.(null);
1122
+ }
1123
+ function cleanupOwnedResource(resource, options) {
1124
+ if (options.cleanup === false) return;
1125
+ if (typeof options.cleanup === "function") {
1126
+ options.cleanup(resource);
1127
+ return;
1128
+ }
1129
+ const errors = [];
1130
+ if (options.destroyAdapters !== false && typeof resource?.DestroyAdapterResources === "function") {
1131
+ try {
1132
+ resource.DestroyAdapterResources({
1133
+ destroy: true
1134
+ });
1135
+ } catch (error) {
1136
+ errors.push(error);
1137
+ }
1138
+ }
1139
+ if (options.releasePayload !== false && typeof resource?.ReleasePayload === "function") {
1140
+ try {
1141
+ resource.ReleasePayload();
1142
+ } catch (error) {
1143
+ errors.push(error);
1144
+ }
1145
+ }
1146
+ if (errors.length) {
1147
+ throw errors.length === 1 ? errors[0] : new AggregateError(errors, "Resource cleanup failed.");
1148
+ }
1149
+ }
1150
+ function assertResource(resource) {
1151
+ if (typeof resource !== "object" && typeof resource !== "function" || resource === null) {
1152
+ throw new TypeError("CjsMotherLode requires a resource object.");
1153
+ }
1154
+ }
1155
+ function assertNonNegativeSafeInteger(value, label) {
1156
+ if (!Number.isSafeInteger(value) || value < 0) {
1157
+ throw new TypeError(`${label} must be a non-negative safe integer.`);
1158
+ }
1159
+ }
1160
+
1161
+ /**
1162
+ * Validate a non-negative finite number with a contextual error label.
1163
+ *
1164
+ * @param {*} value Candidate numeric value.
1165
+ * @param {string} label Error-message field name.
1166
+ * @returns {void}
1167
+ */
1168
+ function assertNonNegativeFiniteNumber(value, label) {
1169
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
1170
+ throw new TypeError(`${label} must be a non-negative finite number.`);
1171
+ }
1172
+ }
1173
+ function freezeInsertResult(key, resource, inserted, replaced, displaced) {
1174
+ return Object.freeze({
1175
+ key,
1176
+ resource,
1177
+ inserted,
1178
+ replaced,
1179
+ displaced
1180
+ });
1181
+ }
1182
+
1183
+ /**
1184
+ * Freeze one exact-owner compare-and-swap result.
1185
+ *
1186
+ * @param {string} key Canonical resolved identity key.
1187
+ * @param {boolean} committed Whether publication replaced the expected owner.
1188
+ * @param {object|Function|null} resource Canonical owner after the attempt.
1189
+ * @param {object|Function|null} displaced Former owner when committed.
1190
+ * @returns {CjsMotherLodeConditionalReplaceResult} Immutable replacement result.
1191
+ */
1192
+ function freezeConditionalReplaceResult(key, committed, resource, displaced) {
1193
+ return Object.freeze({
1194
+ key,
1195
+ committed,
1196
+ resource,
1197
+ displaced
1198
+ });
1199
+ }
1200
+
1201
+ /**
1202
+ * Freeze one inactivity-sweep result and its affected-key snapshots.
1203
+ *
1204
+ * @param {number} frame Sweep frame.
1205
+ * @param {number} time Sweep timestamp.
1206
+ * @param {string[]} purgedKeys Removed canonical keys.
1207
+ * @param {string[]} payloadKeys Keys whose payloads were released.
1208
+ * @param {number} locked Number of locked entries skipped.
1209
+ * @returns {CjsMotherLodePurgeResult} Immutable sweep result.
1210
+ */
1211
+ function freezePurgeResult(frame, time, purgedKeys, payloadKeys, locked) {
1212
+ return Object.freeze({
1213
+ frame,
1214
+ time,
1215
+ purged: purgedKeys.length,
1216
+ payloadsReleased: payloadKeys.length,
1217
+ locked,
1218
+ purgedKeys: Object.freeze(purgedKeys),
1219
+ payloadKeys: Object.freeze(payloadKeys)
1220
+ });
1221
+ }
1222
+
1223
+ /**
1224
+ * Freeze one byte-budget housekeeping result and its affected-key snapshots.
1225
+ *
1226
+ * @param {number} cacheSize Configured cache budget.
1227
+ * @param {number} beforeBytes Cached bytes before housekeeping.
1228
+ * @param {number} afterBytes Cached bytes after housekeeping.
1229
+ * @param {number} evictedBytes Successfully removed cached bytes.
1230
+ * @param {string[]} evictedKeys Successfully removed canonical identities.
1231
+ * @param {string[]} failedKeys Candidate identities that reported failure.
1232
+ * @returns {CjsMotherLodeCacheTrimResult} Immutable cache-housekeeping result.
1233
+ */
1234
+ function freezeCacheTrimResult(cacheSize, beforeBytes, afterBytes, evictedBytes, evictedKeys, failedKeys) {
1235
+ return Object.freeze({
1236
+ cacheSize,
1237
+ beforeBytes,
1238
+ afterBytes,
1239
+ evictedBytes,
1240
+ overBudget: afterBytes > cacheSize,
1241
+ evicted: evictedKeys.length,
1242
+ failed: failedKeys.length,
1243
+ evictedKeys: Object.freeze(evictedKeys),
1244
+ failedKeys: Object.freeze(failedKeys)
1245
+ });
1246
+ }
1247
+ function motherLodeInactiveError() {
1248
+ const error = new Error("CjsMotherLode is shut down. Call Startup() before inserting resources.");
1249
+ error.code = "CJS_MOTHERLODE_INACTIVE";
1250
+ return error;
1251
+ }
1252
+ function motherLodeCleanupError(operation, key, resource, cause) {
1253
+ const error = new Error(`CjsMotherLode ${operation} cleanup failed for ${key}.`, {
1254
+ cause
1255
+ });
1256
+ error.code = "CJS_MOTHERLODE_CLEANUP_FAILED";
1257
+ error.operation = operation;
1258
+ error.key = key;
1259
+ error.resource = resource;
1260
+ return error;
1261
+ }
1262
+ function getKeyPath(key) {
1263
+ const separator = key.indexOf("\u0000");
1264
+ return separator === -1 ? key : key.slice(0, separator);
1265
+ }
1266
+ function defaultNow() {
1267
+ return Date.now();
1268
+ }
1269
+
1270
+ export { CjsMotherLode, getMotherLodeKey };
1271
+ //# sourceMappingURL=CjsMotherLode.js.map