@blac/core 2.0.20 → 2.0.21

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 (283) hide show
  1. package/README.md +19 -29
  2. package/dist/{StateContainerRegistry-CSNm2Ws5.cjs → StateContainerRegistry-141uHhtF.cjs} +300 -397
  3. package/dist/StateContainerRegistry-141uHhtF.cjs.map +1 -0
  4. package/dist/{StateContainerRegistry-CvBOhAT2.js → StateContainerRegistry-vWgsD-RP.js} +277 -392
  5. package/dist/StateContainerRegistry-vWgsD-RP.js.map +1 -0
  6. package/dist/config.d.cts +2 -2
  7. package/dist/config.d.ts +2 -2
  8. package/dist/constants.d.cts +11 -20
  9. package/dist/constants.d.ts +11 -20
  10. package/dist/core/Cubit.d.ts +14 -10
  11. package/dist/core/StateContainer.d.ts +55 -4
  12. package/dist/core/StateContainerRegistry.d.ts +94 -18
  13. package/dist/core/meta.d.ts +5 -11
  14. package/dist/core/symbols.d.ts +18 -1
  15. package/dist/decorators/blac.d.ts +28 -26
  16. package/dist/{ensure-B2DYCbbI.cjs → ensure-ORVw-WRE.cjs} +214 -39
  17. package/dist/ensure-ORVw-WRE.cjs.map +1 -0
  18. package/dist/{ensure-BSJHkUNX.js → ensure-k-6q7O25.js} +183 -38
  19. package/dist/ensure-k-6q7O25.js.map +1 -0
  20. package/dist/index.cjs +212 -32
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +4 -6
  23. package/dist/index.d.ts +4 -6
  24. package/dist/index.js +192 -12
  25. package/dist/index.js.map +1 -1
  26. package/dist/plugin/BlacPlugin.d.ts +14 -0
  27. package/dist/plugin/PluginManager.d.ts +12 -7
  28. package/dist/plugins.cjs +400 -3
  29. package/dist/plugins.cjs.map +1 -0
  30. package/dist/plugins.d.cts +6 -2
  31. package/dist/plugins.d.ts +6 -2
  32. package/dist/plugins.js +398 -1
  33. package/dist/plugins.js.map +1 -0
  34. package/dist/registry/acquire.d.ts +2 -2
  35. package/dist/registry/config.d.ts +9 -0
  36. package/dist/registry/management.d.ts +3 -0
  37. package/dist/registry/queries.d.ts +15 -0
  38. package/dist/registry/release.d.ts +3 -3
  39. package/dist/testing.cjs +13 -15
  40. package/dist/testing.cjs.map +1 -1
  41. package/dist/testing.d.cts +1 -1
  42. package/dist/testing.d.ts +1 -1
  43. package/dist/testing.js +2 -4
  44. package/dist/testing.js.map +1 -1
  45. package/dist/types/branded.d.ts +3 -3
  46. package/dist/types/utilities.d.ts +45 -18
  47. package/dist/utils/static-props.d.ts +25 -1
  48. package/dist/utils/structural-key.d.ts +0 -7
  49. package/dist/watch/index.d.ts +1 -1
  50. package/dist/watch/watch.d.ts +22 -3
  51. package/package.json +17 -55
  52. package/dist/StateContainerRegistry-4Ljd9EJx.js +0 -1018
  53. package/dist/StateContainerRegistry-4Ljd9EJx.js.map +0 -1
  54. package/dist/StateContainerRegistry-8zNFMAf7.js +0 -1067
  55. package/dist/StateContainerRegistry-8zNFMAf7.js.map +0 -1
  56. package/dist/StateContainerRegistry-B1GYx9Xo.js +0 -975
  57. package/dist/StateContainerRegistry-B1GYx9Xo.js.map +0 -1
  58. package/dist/StateContainerRegistry-B_ViBLvJ.js +0 -975
  59. package/dist/StateContainerRegistry-B_ViBLvJ.js.map +0 -1
  60. package/dist/StateContainerRegistry-Bx2ly-ba.cjs +0 -1162
  61. package/dist/StateContainerRegistry-Bx2ly-ba.cjs.map +0 -1
  62. package/dist/StateContainerRegistry-C0dCNlTF.js +0 -1005
  63. package/dist/StateContainerRegistry-C0dCNlTF.js.map +0 -1
  64. package/dist/StateContainerRegistry-CEWpc1bj.cjs +0 -1113
  65. package/dist/StateContainerRegistry-CEWpc1bj.cjs.map +0 -1
  66. package/dist/StateContainerRegistry-CS18y4au.cjs +0 -1163
  67. package/dist/StateContainerRegistry-CS18y4au.cjs.map +0 -1
  68. package/dist/StateContainerRegistry-CSNm2Ws5.cjs.map +0 -1
  69. package/dist/StateContainerRegistry-CchRjnjX.cjs +0 -1094
  70. package/dist/StateContainerRegistry-CchRjnjX.cjs.map +0 -1
  71. package/dist/StateContainerRegistry-CvBOhAT2.js.map +0 -1
  72. package/dist/StateContainerRegistry-D8nu7Z0T.js +0 -1068
  73. package/dist/StateContainerRegistry-D8nu7Z0T.js.map +0 -1
  74. package/dist/StateContainerRegistry-D9BIRjwv.cjs +0 -1088
  75. package/dist/StateContainerRegistry-D9BIRjwv.cjs.map +0 -1
  76. package/dist/StateContainerRegistry-DJQqLrli.cjs +0 -1046
  77. package/dist/StateContainerRegistry-DJQqLrli.cjs.map +0 -1
  78. package/dist/StateContainerRegistry-DVIBTxGZ.js +0 -999
  79. package/dist/StateContainerRegistry-DVIBTxGZ.js.map +0 -1
  80. package/dist/StateContainerRegistry-Dj77cok9.js +0 -1005
  81. package/dist/StateContainerRegistry-Dj77cok9.js.map +0 -1
  82. package/dist/StateContainerRegistry-FWKzPfS-.cjs +0 -1046
  83. package/dist/StateContainerRegistry-FWKzPfS-.cjs.map +0 -1
  84. package/dist/StateContainerRegistry-lIwo-0m3.cjs +0 -1094
  85. package/dist/StateContainerRegistry-lIwo-0m3.cjs.map +0 -1
  86. package/dist/acquire-B1LYXSGj.js +0 -35
  87. package/dist/acquire-B1LYXSGj.js.map +0 -1
  88. package/dist/acquire-B1Wuqla6.cjs +0 -46
  89. package/dist/acquire-B1Wuqla6.cjs.map +0 -1
  90. package/dist/acquire-BJ3xUEmh.cjs +0 -46
  91. package/dist/acquire-BJ3xUEmh.cjs.map +0 -1
  92. package/dist/acquire-BP-ZfUH2.js +0 -35
  93. package/dist/acquire-BP-ZfUH2.js.map +0 -1
  94. package/dist/acquire-BRIJVJaA.js +0 -35
  95. package/dist/acquire-BRIJVJaA.js.map +0 -1
  96. package/dist/acquire-BZIs6tpN.cjs +0 -46
  97. package/dist/acquire-BZIs6tpN.cjs.map +0 -1
  98. package/dist/acquire-B_p1fNEh.cjs +0 -46
  99. package/dist/acquire-B_p1fNEh.cjs.map +0 -1
  100. package/dist/acquire-BdsVZBSe.js +0 -35
  101. package/dist/acquire-BdsVZBSe.js.map +0 -1
  102. package/dist/acquire-C3nOb3XR.cjs +0 -46
  103. package/dist/acquire-C3nOb3XR.cjs.map +0 -1
  104. package/dist/acquire-C4WAS13G.cjs +0 -46
  105. package/dist/acquire-C4WAS13G.cjs.map +0 -1
  106. package/dist/acquire-C7beGI55.cjs +0 -46
  107. package/dist/acquire-C7beGI55.cjs.map +0 -1
  108. package/dist/acquire-CbMz7gWs.cjs +0 -46
  109. package/dist/acquire-CbMz7gWs.cjs.map +0 -1
  110. package/dist/acquire-CwB083au.js +0 -35
  111. package/dist/acquire-CwB083au.js.map +0 -1
  112. package/dist/acquire-DHEOEVcj.cjs +0 -46
  113. package/dist/acquire-DHEOEVcj.cjs.map +0 -1
  114. package/dist/acquire-DXEQDZGr.js +0 -35
  115. package/dist/acquire-DXEQDZGr.js.map +0 -1
  116. package/dist/acquire-Dg-uXwjF.js +0 -35
  117. package/dist/acquire-Dg-uXwjF.js.map +0 -1
  118. package/dist/acquire-Don7499S.js +0 -35
  119. package/dist/acquire-Don7499S.js.map +0 -1
  120. package/dist/acquire-G8aB3GAx.js +0 -35
  121. package/dist/acquire-G8aB3GAx.js.map +0 -1
  122. package/dist/branded-BybA0_xw.cjs +0 -18
  123. package/dist/branded-BybA0_xw.cjs.map +0 -1
  124. package/dist/branded-DkbUzncc.js +0 -13
  125. package/dist/branded-DkbUzncc.js.map +0 -1
  126. package/dist/config-8pyOC5gv.js +0 -16
  127. package/dist/config-8pyOC5gv.js.map +0 -1
  128. package/dist/config-B1l8ZGlU.cjs +0 -32
  129. package/dist/config-B1l8ZGlU.cjs.map +0 -1
  130. package/dist/config-C7Ktkjh2.cjs +0 -32
  131. package/dist/config-C7Ktkjh2.cjs.map +0 -1
  132. package/dist/config-COT4Xkbc.js +0 -16
  133. package/dist/config-COT4Xkbc.js.map +0 -1
  134. package/dist/config-CzcoAvBo.js +0 -16
  135. package/dist/config-CzcoAvBo.js.map +0 -1
  136. package/dist/config-D6BoWWPv.js +0 -16
  137. package/dist/config-D6BoWWPv.js.map +0 -1
  138. package/dist/config-DOO82lbZ.js +0 -16
  139. package/dist/config-DOO82lbZ.js.map +0 -1
  140. package/dist/config-DVM_ESQt.js +0 -16
  141. package/dist/config-DVM_ESQt.js.map +0 -1
  142. package/dist/config-DVMnlHLv.cjs +0 -32
  143. package/dist/config-DVMnlHLv.cjs.map +0 -1
  144. package/dist/config-DWUq7zEQ.cjs +0 -32
  145. package/dist/config-DWUq7zEQ.cjs.map +0 -1
  146. package/dist/config-DXI3kzmA.js +0 -16
  147. package/dist/config-DXI3kzmA.js.map +0 -1
  148. package/dist/config-Dk1FxfXj.js +0 -16
  149. package/dist/config-Dk1FxfXj.js.map +0 -1
  150. package/dist/config-b2z51Y_C.cjs +0 -32
  151. package/dist/config-b2z51Y_C.cjs.map +0 -1
  152. package/dist/config-buwpT7l3.js +0 -16
  153. package/dist/config-buwpT7l3.js.map +0 -1
  154. package/dist/config-cwaTn5oy.cjs +0 -32
  155. package/dist/config-cwaTn5oy.cjs.map +0 -1
  156. package/dist/config-d9_U58ef.cjs +0 -32
  157. package/dist/config-d9_U58ef.cjs.map +0 -1
  158. package/dist/config-hWTayX76.cjs +0 -32
  159. package/dist/config-hWTayX76.cjs.map +0 -1
  160. package/dist/config-sCPKPYxZ.cjs +0 -32
  161. package/dist/config-sCPKPYxZ.cjs.map +0 -1
  162. package/dist/debug.cjs +0 -14
  163. package/dist/debug.d.cts +0 -34
  164. package/dist/debug.d.ts +0 -34
  165. package/dist/debug.js +0 -4
  166. package/dist/ensure-B1dEDsZV.cjs +0 -559
  167. package/dist/ensure-B1dEDsZV.cjs.map +0 -1
  168. package/dist/ensure-B2DYCbbI.cjs.map +0 -1
  169. package/dist/ensure-B59ri8oo.js +0 -555
  170. package/dist/ensure-B59ri8oo.js.map +0 -1
  171. package/dist/ensure-B9oOqpOB.cjs +0 -558
  172. package/dist/ensure-B9oOqpOB.cjs.map +0 -1
  173. package/dist/ensure-BKdq5xsY.cjs +0 -578
  174. package/dist/ensure-BKdq5xsY.cjs.map +0 -1
  175. package/dist/ensure-BSJHkUNX.js.map +0 -1
  176. package/dist/ensure-BfYnvAsI.js +0 -709
  177. package/dist/ensure-BfYnvAsI.js.map +0 -1
  178. package/dist/ensure-Bm91o37h.js +0 -536
  179. package/dist/ensure-Bm91o37h.js.map +0 -1
  180. package/dist/ensure-C1a8IFWy.js +0 -535
  181. package/dist/ensure-C1a8IFWy.js.map +0 -1
  182. package/dist/ensure-CLQEx0Nn.cjs +0 -528
  183. package/dist/ensure-CLQEx0Nn.cjs.map +0 -1
  184. package/dist/ensure-CPMiGvov.js +0 -493
  185. package/dist/ensure-CPMiGvov.js.map +0 -1
  186. package/dist/ensure-CQimeD0I.cjs +0 -559
  187. package/dist/ensure-CQimeD0I.cjs.map +0 -1
  188. package/dist/ensure-DEXSmA-N.js +0 -526
  189. package/dist/ensure-DEXSmA-N.js.map +0 -1
  190. package/dist/ensure-DTTTd2h8.js +0 -536
  191. package/dist/ensure-DTTTd2h8.js.map +0 -1
  192. package/dist/ensure-Dx86f80B.cjs +0 -567
  193. package/dist/ensure-Dx86f80B.cjs.map +0 -1
  194. package/dist/ensure-DzqGej43.js +0 -526
  195. package/dist/ensure-DzqGej43.js.map +0 -1
  196. package/dist/ensure-VuiNPccZ.cjs +0 -559
  197. package/dist/ensure-VuiNPccZ.cjs.map +0 -1
  198. package/dist/ensure-_Oh8XPm4.cjs +0 -732
  199. package/dist/ensure-_Oh8XPm4.cjs.map +0 -1
  200. package/dist/ensure-fsQMjaQL.cjs +0 -567
  201. package/dist/ensure-fsQMjaQL.cjs.map +0 -1
  202. package/dist/ensure-vxAGa2v3.js +0 -536
  203. package/dist/ensure-vxAGa2v3.js.map +0 -1
  204. package/dist/management-14vsHr4t.cjs +0 -85
  205. package/dist/management-14vsHr4t.cjs.map +0 -1
  206. package/dist/management-444tdCBG.js +0 -38
  207. package/dist/management-444tdCBG.js.map +0 -1
  208. package/dist/management-7jLjVHJd.js +0 -38
  209. package/dist/management-7jLjVHJd.js.map +0 -1
  210. package/dist/management-AFuqQEwz.js +0 -38
  211. package/dist/management-AFuqQEwz.js.map +0 -1
  212. package/dist/management-BodVLAfJ.cjs +0 -85
  213. package/dist/management-BodVLAfJ.cjs.map +0 -1
  214. package/dist/management-BqGBdL01.js +0 -38
  215. package/dist/management-BqGBdL01.js.map +0 -1
  216. package/dist/management-BvUAjril.js +0 -38
  217. package/dist/management-BvUAjril.js.map +0 -1
  218. package/dist/management-C-WGcwPD.js +0 -38
  219. package/dist/management-C-WGcwPD.js.map +0 -1
  220. package/dist/management-C2wv4vGS.cjs +0 -85
  221. package/dist/management-C2wv4vGS.cjs.map +0 -1
  222. package/dist/management-D2pjr38g.js +0 -38
  223. package/dist/management-D2pjr38g.js.map +0 -1
  224. package/dist/management-D5WsZoTV.js +0 -38
  225. package/dist/management-D5WsZoTV.js.map +0 -1
  226. package/dist/management-DAV9sN-k.cjs +0 -85
  227. package/dist/management-DAV9sN-k.cjs.map +0 -1
  228. package/dist/management-Df7Y2sF1.cjs +0 -85
  229. package/dist/management-Df7Y2sF1.cjs.map +0 -1
  230. package/dist/management-DwO0nBzL.cjs +0 -85
  231. package/dist/management-DwO0nBzL.cjs.map +0 -1
  232. package/dist/management-GcnNJjnm.cjs +0 -85
  233. package/dist/management-GcnNJjnm.cjs.map +0 -1
  234. package/dist/management-P_Vi_cvw.js +0 -38
  235. package/dist/management-P_Vi_cvw.js.map +0 -1
  236. package/dist/management-QWLpbXlW.cjs +0 -85
  237. package/dist/management-QWLpbXlW.cjs.map +0 -1
  238. package/dist/management-iE4x6IQY.cjs +0 -85
  239. package/dist/management-iE4x6IQY.cjs.map +0 -1
  240. package/dist/types.cjs +0 -3
  241. package/dist/types.d.cts +0 -8
  242. package/dist/types.d.ts +0 -8
  243. package/dist/types.js +0 -2
  244. package/dist/watch-B0pUfh-v.cjs +0 -119
  245. package/dist/watch-B0pUfh-v.cjs.map +0 -1
  246. package/dist/watch-B91opmva.cjs +0 -70
  247. package/dist/watch-B91opmva.cjs.map +0 -1
  248. package/dist/watch-BHfCtbn1.cjs +0 -119
  249. package/dist/watch-BHfCtbn1.cjs.map +0 -1
  250. package/dist/watch-BmC-Tyqg.js +0 -59
  251. package/dist/watch-BmC-Tyqg.js.map +0 -1
  252. package/dist/watch-Bx0y-Kcc.js +0 -108
  253. package/dist/watch-Bx0y-Kcc.js.map +0 -1
  254. package/dist/watch-CKWt788i.cjs +0 -70
  255. package/dist/watch-CKWt788i.cjs.map +0 -1
  256. package/dist/watch-CfrSSrRY.js +0 -59
  257. package/dist/watch-CfrSSrRY.js.map +0 -1
  258. package/dist/watch-Cnh3PtQA.cjs +0 -70
  259. package/dist/watch-Cnh3PtQA.cjs.map +0 -1
  260. package/dist/watch-D01MdwKy.js +0 -108
  261. package/dist/watch-D01MdwKy.js.map +0 -1
  262. package/dist/watch-D8hwbFU1.js +0 -59
  263. package/dist/watch-D8hwbFU1.js.map +0 -1
  264. package/dist/watch-DPVUwPUY.js +0 -59
  265. package/dist/watch-DPVUwPUY.js.map +0 -1
  266. package/dist/watch-DVmvZBMD.cjs +0 -70
  267. package/dist/watch-DVmvZBMD.cjs.map +0 -1
  268. package/dist/watch-DkDGmGAx.cjs +0 -119
  269. package/dist/watch-DkDGmGAx.cjs.map +0 -1
  270. package/dist/watch-DttHqefn.js +0 -108
  271. package/dist/watch-DttHqefn.js.map +0 -1
  272. package/dist/watch-DxdIZjpt.cjs +0 -119
  273. package/dist/watch-DxdIZjpt.cjs.map +0 -1
  274. package/dist/watch-DztcJuUy.cjs +0 -119
  275. package/dist/watch-DztcJuUy.cjs.map +0 -1
  276. package/dist/watch-IrR8G28N.js +0 -108
  277. package/dist/watch-IrR8G28N.js.map +0 -1
  278. package/dist/watch-entry.cjs +0 -4
  279. package/dist/watch-entry.d.cts +0 -24
  280. package/dist/watch-entry.d.ts +0 -24
  281. package/dist/watch-entry.js +0 -2
  282. package/dist/watch-hx0r92Zc.js +0 -108
  283. package/dist/watch-hx0r92Zc.js.map +0 -1
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/registry/borrow.ts","../src/registry/release.ts","../src/decorators/blac.ts"],"sourcesContent":["import { getRegistry } from './config';\nimport type {\n ExtractArgs,\n StateContainerConstructor,\n} from '../types/utilities';\n\n/**\n * How to identify the instance to borrow: the same `args` shape `useBloc`\n * accepts, resolved through the class's `static key(args)` so call sites use\n * the *same* shape they pass to `useBloc` instead of hand-deriving the key.\n *\n * `borrowSafe(ChannelBloc, { args: { channelId } })` and\n * `useBloc(ChannelBloc, { args: { channelId } })` therefore always agree.\n */\nexport type BorrowTarget<T extends StateContainerConstructor> = {\n args?: ExtractArgs<T>;\n};\n\nfunction resolveBorrowKey<T extends StateContainerConstructor>(\n BlocClass: T,\n target: BorrowTarget<T> | undefined,\n): string {\n return getRegistry().resolveKey(BlocClass, undefined, target?.args);\n}\n\nexport function borrow<T extends StateContainerConstructor>(\n BlocClass: T,\n target?: BorrowTarget<T>,\n): InstanceType<T> {\n return getRegistry().borrow(BlocClass, resolveBorrowKey(BlocClass, target));\n}\n\nexport function borrowSafe<T extends StateContainerConstructor>(\n BlocClass: T,\n target?: BorrowTarget<T>,\n):\n | { error: Error; instance: null }\n | { error: null; instance: InstanceType<T> } {\n return getRegistry().borrowSafe(\n BlocClass,\n resolveBorrowKey(BlocClass, target),\n );\n}\n","import { getRegistry } from './config';\nimport type {\n ExtractArgs,\n StateContainerConstructor,\n} from '../types/utilities';\n\n/**\n * Release a reference to an instance. Instance identity is derived purely from\n * `args` (must match the `args` it was acquired with).\n *\n * @param opts.args - Construction/identity args; derives the instance key\n * @param opts.refId - The specific ref to drop; drops one arbitrary ref if omitted\n * @param opts.forceDispose - Force immediate disposal regardless of refs\n */\nexport function release<T extends StateContainerConstructor>(\n BlocClass: T,\n opts?: { args?: ExtractArgs<T>; refId?: string; forceDispose?: boolean },\n): void {\n const registry = getRegistry();\n const key = registry.resolveKey(BlocClass, undefined, opts?.args);\n registry.release(BlocClass, key, opts?.forceDispose ?? false, opts?.refId);\n}\n","import { BLAC_STATIC_PROPS } from '../constants';\nimport type { EqualityFn } from '../config';\n\n/**\n * Configuration options for the @blac decorator.\n * Only one option can be specified at a time (union type).\n */\nexport type BlacOptions =\n /** Mark bloc to never be auto-disposed when ref count reaches 0 */\n | { keepAlive: true }\n /** Exclude bloc from DevTools tracking (prevents infinite loops) */\n | { excludeFromDevTools: true }\n /**\n * Override the global equality check for this bloc.\n * Return `true` to skip the emit (states considered equal).\n */\n | { equality: EqualityFn }\n /**\n * Override the default structural-hash identity key.\n * The function receives `args` and returns the instance key string.\n * Distinct return values → distinct instances; identical values → shared instance.\n * Non-identity fields can be excluded so they don't fork new instances.\n */\n | { key: (args: any) => string };\n\n/**\n * Decorator to configure StateContainer classes.\n *\n * @example Decorator syntax (requires experimentalDecorators or TC39 decorators)\n * ```ts\n * @blac({ keepAlive: true })\n * class AuthBloc extends Cubit<AuthState> {}\n *\n * @blac({ excludeFromDevTools: true })\n * class InternalBloc extends Cubit<InternalState> {}\n * ```\n *\n * @example Function syntax (no decorator support needed)\n * ```ts\n * const AuthBloc = blac({ keepAlive: true })(\n * class extends Cubit<AuthState> {}\n * );\n * ```\n */\nexport function blac(options: BlacOptions) {\n return function <T extends new (...args: any[]) => any>(\n target: T,\n _context?: ClassDecoratorContext,\n ): T {\n if ('keepAlive' in options && options.keepAlive) {\n (target as any)[BLAC_STATIC_PROPS.KEEP_ALIVE] = true;\n }\n if ('excludeFromDevTools' in options && options.excludeFromDevTools) {\n (target as any)[BLAC_STATIC_PROPS.EXCLUDE_FROM_DEVTOOLS] = true;\n }\n if ('equality' in options && typeof options.equality === 'function') {\n (target as any)[BLAC_STATIC_PROPS.EQUALITY] = options.equality;\n }\n if ('key' in options && typeof options.key === 'function') {\n (target as any)[BLAC_STATIC_PROPS.KEY] = options.key;\n }\n return target;\n };\n}\n"],"mappings":";;;;;;;;;AAkBA,SAAS,iBACP,WACA,QACQ;CACR,OAAO,YAAY,EAAE,WAAW,WAAW,KAAA,GAAW,QAAQ,IAAI;AACpE;AAEA,SAAgB,OACd,WACA,QACiB;CACjB,OAAO,YAAY,EAAE,OAAO,WAAW,iBAAiB,WAAW,MAAM,CAAC;AAC5E;AAEA,SAAgB,WACd,WACA,QAG6C;CAC7C,OAAO,YAAY,EAAE,WACnB,WACA,iBAAiB,WAAW,MAAM,CACpC;AACF;;;;;;;;;;;AC5BA,SAAgB,QACd,WACA,MACM;CACN,MAAM,WAAW,YAAY;CAC7B,MAAM,MAAM,SAAS,WAAW,WAAW,KAAA,GAAW,MAAM,IAAI;CAChE,SAAS,QAAQ,WAAW,KAAK,MAAM,gBAAgB,OAAO,MAAM,KAAK;AAC3E;;;;;;;;;;;;;;;;;;;;;;ACuBA,SAAgB,KAAK,SAAsB;CACzC,OAAO,SACL,QACA,UACG;EACH,IAAI,eAAe,WAAW,QAAQ,WACpC,OAAgB,kBAAkB,cAAc;EAElD,IAAI,yBAAyB,WAAW,QAAQ,qBAC9C,OAAgB,kBAAkB,yBAAyB;EAE7D,IAAI,cAAc,WAAW,OAAO,QAAQ,aAAa,YACvD,OAAgB,kBAAkB,YAAY,QAAQ;EAExD,IAAI,SAAS,WAAW,OAAO,QAAQ,QAAQ,YAC7C,OAAgB,kBAAkB,OAAO,QAAQ;EAEnD,OAAO;CACT;AACF"}
1
+ {"version":3,"file":"index.js","names":["ALL_PATHS"],"sources":["../src/registry/borrow.ts","../src/registry/release.ts","../src/registry/queries.ts","../src/registry/management.ts","../src/decorators/blac.ts","../src/watch/watch.ts","../src/types/branded.ts"],"sourcesContent":["import { getRegistry } from './config';\nimport type {\n ExtractArgs,\n StateContainerConstructor,\n} from '../types/utilities';\n\n/**\n * How to identify the instance to borrow: the same `args` shape `useBloc`\n * accepts, resolved through the class's `static key(args)` so call sites use\n * the *same* shape they pass to `useBloc` instead of hand-deriving the key.\n *\n * `borrowSafe(ChannelBloc, { args: { channelId } })` and\n * `useBloc(ChannelBloc, { args: { channelId } })` therefore always agree.\n */\nexport type BorrowTarget<T extends StateContainerConstructor> = {\n args?: ExtractArgs<T>;\n};\n\nfunction resolveBorrowKey<T extends StateContainerConstructor>(\n BlocClass: T,\n target: BorrowTarget<T> | undefined,\n): string {\n return getRegistry().resolveKey(BlocClass, undefined, target?.args);\n}\n\nexport function borrow<T extends StateContainerConstructor>(\n BlocClass: T,\n target?: BorrowTarget<T>,\n): InstanceType<T> {\n return getRegistry().borrow(BlocClass, resolveBorrowKey(BlocClass, target));\n}\n\nexport function borrowSafe<T extends StateContainerConstructor>(\n BlocClass: T,\n target?: BorrowTarget<T>,\n):\n | { error: Error; instance: null }\n | { error: null; instance: InstanceType<T> } {\n return getRegistry().borrowSafe(\n BlocClass,\n resolveBorrowKey(BlocClass, target),\n );\n}\n","import { getRegistry } from './config';\nimport type {\n ExtractArgs,\n StateContainerConstructor,\n} from '../types/utilities';\n\n/**\n * Release a reference to an instance. Instance identity is derived purely from\n * `args` (must match the `args` it was acquired with).\n *\n * @param opts - `args` derives the instance key (must match acquire);\n * `refId` picks the ref to drop, otherwise one arbitrary ref goes;\n * `forceDispose` disposes immediately regardless of remaining refs.\n */\nexport function release<T extends StateContainerConstructor>(\n BlocClass: T,\n opts?: { args?: ExtractArgs<T>; refId?: string; forceDispose?: boolean },\n): void {\n const registry = getRegistry();\n const key = registry.resolveKey(BlocClass, undefined, opts?.args);\n registry.release(BlocClass, key, opts?.forceDispose ?? false, opts?.refId);\n}\n","import { getRegistry } from './config';\nimport type {\n ExtractArgs,\n StateContainerConstructor,\n InstanceReadonlyState,\n} from '../types/utilities';\n\n/**\n * @public\n */\nexport function hasInstance<T extends StateContainerConstructor>(\n BlocClass: T,\n opts?: { args?: ExtractArgs<T> },\n): boolean {\n const registry = getRegistry();\n const key = registry.resolveKey(BlocClass, undefined, opts?.args);\n return registry.hasInstance(BlocClass, key);\n}\n\n/**\n * @public\n */\nexport function getRefCount<T extends StateContainerConstructor>(\n BlocClass: T,\n opts?: { args?: ExtractArgs<T> },\n): number {\n const registry = getRegistry();\n const key = registry.resolveKey(BlocClass, undefined, opts?.args);\n return registry.getRefCount(BlocClass, key);\n}\n\n/**\n * @public\n */\nexport function getRefIds<T extends StateContainerConstructor>(\n BlocClass: T,\n opts?: { args?: ExtractArgs<T> },\n): string[] {\n const registry = getRegistry();\n const key = registry.resolveKey(BlocClass, undefined, opts?.args);\n return registry.getRefIds(BlocClass, key);\n}\n\n/**\n * @public\n */\nexport function getAll<T extends StateContainerConstructor>(\n BlocClass: T,\n): InstanceReadonlyState<T>[] {\n return getRegistry().getAll(BlocClass);\n}\n\n/**\n * @public\n */\nexport function forEach<T extends StateContainerConstructor>(\n BlocClass: T,\n callback: (instance: InstanceReadonlyState<T>) => void,\n): void {\n getRegistry().forEach(BlocClass, callback);\n}\n","import { getRegistry } from './config';\nimport type { StateContainerConstructor } from '../types/utilities';\n\nexport function clear<T extends StateContainerConstructor>(BlocClass: T): void {\n getRegistry().clear(BlocClass);\n}\n\nexport function clearAll(): void {\n getRegistry().clearAll();\n}\n\n/**\n * @public\n */\nexport function register<T extends StateContainerConstructor>(\n BlocClass: T,\n): void {\n getRegistry().register(BlocClass);\n}\n","import { BLAC_STATIC_PROPS } from '../constants';\nimport type { EqualityFn } from '../config';\n\n/**\n * Configuration options for the `@blac` decorator. Any combination may be\n * given at once.\n */\nexport interface BlacOptions {\n /** Mark bloc to never be auto-disposed when ref count reaches 0 */\n keepAlive?: true;\n /** Exclude bloc from DevTools tracking (prevents infinite loops) */\n excludeFromDevTools?: true;\n /**\n * Override the global equality check for this bloc.\n * Return `true` to skip the emit (states considered equal).\n */\n equality?: EqualityFn;\n /**\n * Override the default structural-hash identity key.\n * The function receives `args` and returns the instance key string.\n * Distinct return values → distinct instances; identical values → shared instance.\n * Non-identity fields can be excluded so they don't fork new instances.\n */\n key?: (args: any) => string;\n /**\n * Explicit, minification-safe identity for this bloc class. Falls back to\n * `constructor.name` when unset, which collides across classes once a\n * minifier shortens class names.\n */\n name?: string;\n}\n\n/**\n * Decorator to configure StateContainer classes.\n *\n * @example Decorator syntax (requires experimentalDecorators or TC39 decorators)\n * ```ts\n * @blac({ keepAlive: true })\n * class AuthBloc extends Cubit<AuthState> {}\n *\n * @blac({ excludeFromDevTools: true })\n * class InternalBloc extends Cubit<InternalState> {}\n *\n * @blac({ keepAlive: true, key: (args) => args.userId })\n * class UserBloc extends Cubit<UserState> {}\n * ```\n *\n * @example Function syntax (no decorator support needed)\n * ```ts\n * const AuthBloc = blac({ keepAlive: true })(\n * class extends Cubit<AuthState> {}\n * );\n * ```\n */\nexport function blac(options: BlacOptions) {\n return function <T extends new (...args: any[]) => any>(\n target: T,\n _context?: ClassDecoratorContext,\n ): T {\n if ('keepAlive' in options && options.keepAlive) {\n (target as any)[BLAC_STATIC_PROPS.KEEP_ALIVE] = true;\n }\n if ('excludeFromDevTools' in options && options.excludeFromDevTools) {\n (target as any)[BLAC_STATIC_PROPS.EXCLUDE_FROM_DEVTOOLS] = true;\n }\n if ('equality' in options && typeof options.equality === 'function') {\n (target as any)[BLAC_STATIC_PROPS.EQUALITY] = options.equality;\n }\n if ('key' in options && typeof options.key === 'function') {\n (target as any)[BLAC_STATIC_PROPS.KEY] = options.key;\n }\n if ('name' in options && typeof options.name === 'string') {\n (target as any)[BLAC_STATIC_PROPS.BLAC_NAME] = options.name;\n }\n return target;\n };\n}\n","import { ALL_PATHS } from '@dirtytalk/structural';\nimport { ON_DISPOSE } from '../core/symbols';\nimport { getRegistry } from '../registry';\nimport { resolveInstanceKey } from '../registry/acquire';\nimport type {\n ExtractArgs,\n StateContainerConstructor,\n StateContainerInstance,\n} from '../types/utilities';\n\nconst STOP: unique symbol = Symbol('watch.STOP');\ntype StopSymbol = typeof STOP;\n\nconst BLOC_REF_MARKER = Symbol('BlocRef');\n\n/**\n * Reference to a specific bloc instance, identified by class + the resolved\n * key derived from its `args`.\n */\nexport interface BlocRef<T extends StateContainerConstructor> {\n [BLOC_REF_MARKER]: true;\n blocClass: T;\n /** @internal The resolved instance key (derived from `args`). */\n instanceId: string;\n /**\n * @internal The `args` used to derive `instanceId`. Forwarded to\n * `registry.acquire` so a not-yet-created instance is initialized with\n * these args instead of `undefined`.\n */\n args?: unknown;\n}\n\n/**\n * Create a reference to a specific bloc instance, identified by its `args`.\n * The key is derived the same way `useBloc`/`acquire` derive it.\n *\n * @example\n * ```ts\n * watch(instance(UserBloc, { userId: 'user-123' }), (userBloc) => {\n * console.log(userBloc.state.name);\n * });\n * ```\n */\nexport function instance<T extends StateContainerConstructor>(\n BlocClass: T,\n args?: ExtractArgs<T>,\n): BlocRef<T> {\n return {\n [BLOC_REF_MARKER]: true,\n blocClass: BlocClass,\n instanceId: resolveInstanceKey(BlocClass, args),\n args,\n };\n}\n\nfunction isBlocRef(\n input: unknown,\n): input is BlocRef<StateContainerConstructor> {\n return (\n typeof input === 'object' && input !== null && BLOC_REF_MARKER in input\n );\n}\n\ntype BlocInput = StateContainerConstructor | BlocRef<StateContainerConstructor>;\n\n/**\n * Options for `watch`.\n */\nexport interface WatchOptions {\n /**\n * When `false`, `watch` observes passively instead of owning the instance:\n * it does not create a missing instance, and it does not take a real\n * ownership ref on an existing one (so it never keeps an otherwise-\n * unreferenced instance alive).\n *\n * If the instance does not exist yet when `watch` is called, the callback\n * simply never fires for it — `watch` does not poll or wait for a later\n * creation. Call `watch` again once the instance is known to exist (e.g.\n * after some other owner has acquired it).\n *\n * Defaults to `true`.\n */\n create?: boolean;\n}\n\ntype ExtractInstance<T> =\n T extends BlocRef<infer C>\n ? InstanceType<C>\n : T extends StateContainerConstructor\n ? InstanceType<T>\n : never;\n\ntype ExtractInstances<T extends readonly BlocInput[]> = {\n [K in keyof T]: ExtractInstance<T[K]>;\n};\n\n/**\n * Watch function signature for single bloc.\n */\nexport interface WatchSingleFn {\n <T extends StateContainerConstructor>(\n bloc: T | BlocRef<T>,\n callback: (bloc: InstanceType<T>) => void | StopSymbol,\n options?: WatchOptions,\n ): () => void;\n\n STOP: StopSymbol;\n}\n\n/**\n * Watch function signature for multiple blocs.\n */\nexport interface WatchMultipleFn {\n <T extends readonly BlocInput[]>(\n blocs: T,\n callback: (blocs: ExtractInstances<T>) => void | StopSymbol,\n options?: WatchOptions,\n ): () => void;\n\n STOP: StopSymbol;\n}\n\n/**\n * Combined watch function type.\n */\nexport interface WatchFn extends WatchSingleFn {\n <T extends readonly BlocInput[]>(\n blocs: T,\n callback: (blocs: ExtractInstances<T>) => void | StopSymbol,\n options?: WatchOptions,\n ): () => void;\n}\n\n/**\n * Class + resolved key + args needed to (re-)acquire a specific instance.\n * `watch` keeps this around per input so it can re-acquire the same logical\n * instance if the underlying container is disposed elsewhere.\n */\ninterface WatchTarget {\n blocClass: StateContainerConstructor;\n key: string;\n args: unknown;\n}\n\nfunction toWatchTarget(input: BlocInput): WatchTarget {\n const registry = getRegistry();\n if (isBlocRef(input)) {\n return {\n blocClass: input.blocClass,\n key: input.instanceId,\n args: input.args,\n };\n }\n return {\n blocClass: input,\n key: registry.resolveKey(input, undefined, undefined),\n args: undefined,\n };\n}\n\nlet watchRefSeq = 0;\n\n/**\n * Acquire a real ref (countRef: true) for the target, carrying its `args`\n * through so a not-yet-created instance is initialized correctly. The\n * caller is responsible for releasing `refId` in cleanup.\n */\nfunction resolveBloc(\n target: WatchTarget,\n refId: string,\n): StateContainerInstance {\n const registry = getRegistry();\n return registry.acquire(target.blocClass, target.key, {\n countRef: true,\n refId,\n args: target.args,\n });\n}\n\n/**\n * Passive lookup for `{ create: false }`: never creates the instance and\n * never takes a ref. Returns `undefined` when no instance currently exists.\n */\nfunction resolveBlocPassive(\n target: WatchTarget,\n): StateContainerInstance | undefined {\n const registry = getRegistry();\n try {\n return registry.acquire(target.blocClass, target.key, {\n canCreate: false,\n countRef: false,\n args: target.args,\n });\n } catch {\n return undefined;\n }\n}\n\nfunction isArray(input: unknown): input is readonly BlocInput[] {\n return Array.isArray(input);\n}\n\n/**\n * Watch one or more blocs for state changes.\n *\n * Thin wrapper around `container.channel.subscribe(ALL_PATHS, ...)`. The\n * callback fires once immediately, then on every state change of any of the\n * passed blocs. Returning `watch.STOP` from the callback tears down all\n * subscriptions.\n *\n * Note: subscriptions are microtask-deferred (per the DirtyChannel default\n * scheduler), so callbacks land asynchronously after `emit()`.\n *\n * By default `watch` creates the instance if it does not exist and holds a\n * real ownership ref until `unwatch`, matching `acquire`. Pass\n * `{ create: false }` to observe passively instead — see `WatchOptions`.\n *\n * @example Single bloc\n * ```ts\n * const unwatch = watch(UserBloc, (userBloc) => {\n * console.log(userBloc.state.name);\n * });\n * ```\n *\n * @example Multiple blocs\n * ```ts\n * const unwatch = watch(\n * [UserBloc, SettingsBloc] as const,\n * ([userBloc, settingsBloc]) => {\n * console.log(userBloc.state.name, settingsBloc.state.theme);\n * }\n * );\n * ```\n *\n * @example With specific instance\n * ```ts\n * const unwatch = watch(\n * instance(UserBloc, { userId: 'user-123' }),\n * (userBloc) => {\n * console.log(userBloc.state.name);\n * }\n * );\n * ```\n *\n * @example Stop watching from callback\n * ```ts\n * const unwatch = watch(UserBloc, (userBloc) => {\n * if (userBloc.state.done) {\n * return watch.STOP;\n * }\n * });\n * ```\n */\nfunction watchImpl<T extends StateContainerConstructor>(\n bloc: T | BlocRef<T>,\n callback: (bloc: InstanceType<T>) => void | StopSymbol,\n options?: WatchOptions,\n): () => void;\n\nfunction watchImpl<T extends readonly BlocInput[]>(\n blocs: T,\n callback: (blocs: ExtractInstances<T>) => void | StopSymbol,\n options?: WatchOptions,\n): () => void;\n\nfunction watchImpl(\n blocsOrBloc: BlocInput | readonly BlocInput[],\n callback: (blocsOrBloc: any) => void | StopSymbol,\n options?: WatchOptions,\n): () => void {\n const isSingle = !isArray(blocsOrBloc);\n const inputs = isSingle ? [blocsOrBloc] : blocsOrBloc;\n const registry = getRegistry();\n const create = options?.create ?? true;\n\n const targets = inputs.map(toWatchTarget);\n const refIds = targets.map(() => `_watch_${watchRefSeq++}`);\n\n let disposed = false;\n const instances: Array<StateContainerInstance | undefined> = targets.map(\n (target, i) =>\n create ? resolveBloc(target, refIds[i]) : resolveBlocPassive(target),\n );\n const channelUnsubs: Array<(() => void) | undefined> = [];\n const disposedUnsubs: Array<(() => void) | undefined> = [];\n\n const cleanup = () => {\n if (disposed) return;\n disposed = true;\n for (const unsub of channelUnsubs) unsub?.();\n for (const unsub of disposedUnsubs) unsub?.();\n channelUnsubs.length = 0;\n disposedUnsubs.length = 0;\n if (create) {\n for (let i = 0; i < targets.length; i++) {\n registry.release(\n targets[i].blocClass,\n targets[i].key,\n false,\n refIds[i],\n );\n }\n }\n };\n\n const runCallback = () => {\n if (disposed) return;\n if (instances.some((i) => i === undefined)) return;\n const arg = isSingle ? instances[0] : instances;\n const result = callback(arg);\n if (result === STOP) cleanup();\n };\n\n const subscribeAt = (index: number) => {\n channelUnsubs[index] = instances[index]?.channel.subscribe(\n () => ALL_PATHS,\n runCallback,\n );\n };\n\n // On external dispose, tear down the stale subscription and re-acquire +\n // resubscribe (microtask-deferred, so it never runs mid-mutation of the\n // registry that triggered the dispose, e.g. `clearAll()`), then notify.\n const resubscribeAt = (index: number) => {\n if (disposed) return;\n instances[index] = create\n ? resolveBloc(targets[index], refIds[index])\n : resolveBlocPassive(targets[index]);\n subscribeAt(index);\n subscribeDisposeAt(index);\n runCallback();\n };\n\n const subscribeDisposeAt = (index: number) => {\n disposedUnsubs[index] = instances[index]?.[ON_DISPOSE](() => {\n channelUnsubs[index]?.();\n channelUnsubs[index] = undefined;\n queueMicrotask(() => resubscribeAt(index));\n });\n };\n\n for (let i = 0; i < instances.length; i++) {\n subscribeAt(i);\n }\n\n for (let i = 0; i < instances.length; i++) {\n subscribeDisposeAt(i);\n }\n\n // Fire once immediately so the consumer sees the current state.\n runCallback();\n\n return cleanup;\n}\n\nexport const watch: WatchFn = Object.assign(watchImpl, { STOP }) as WatchFn;\n","declare const brand: unique symbol;\n\n/**\n * Utility type for creating branded/nominal types.\n * Prevents accidental type confusion between similar primitive types.\n * @typeParam T - The base type\n * @typeParam B - The brand identifier\n */\nexport type Brand<T, B> = T & { [brand]: B };\n\n/**\n * Branded string type for type-safe IDs.\n * @typeParam B - The brand identifier\n */\nexport type BrandedId<B> = Brand<string, B>;\n\n/**\n * Branded string type for state container instance IDs\n */\nexport type InstanceId = Brand<string, 'InstanceId'>;\n\n/**\n * Create a branded InstanceId from a string\n * @param id - The string ID to brand\n * @returns Branded InstanceId\n */\nexport function instanceId(id: string): InstanceId {\n return id as InstanceId;\n}\n"],"mappings":"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"}
@@ -99,6 +99,20 @@ export interface BlacPlugin {
99
99
  * Fires when a container's hydration status transitions.
100
100
  */
101
101
  onHydrationChange?(ctx: PluginContext, status: HydrationStatus, previousStatus: HydrationStatus): void;
102
+ /**
103
+ * Fires on the same 0↔1 ownership transition that drives the container's
104
+ * own `onActivate`, after that hook has already run. `signal` aborts on the
105
+ * matching `onDeactivate` and on dispose — dispose does NOT fire
106
+ * `onDeactivate` (or this hook), matching `StateContainer`'s single
107
+ * teardown path.
108
+ */
109
+ onActivate?(ctx: PluginContext, signal: AbortSignal): void;
110
+ /**
111
+ * Fires when an active container goes back to unowned while surviving in
112
+ * the registry (i.e. `keepAlive`), after the container's own `onDeactivate`
113
+ * has already run. Not fired on dispose.
114
+ */
115
+ onDeactivate?(ctx: PluginContext): void;
102
116
  /**
103
117
  * @internal Devtools-only — fires when a named ref is acquired.
104
118
  * Not part of the state-change event payload.
@@ -6,7 +6,8 @@ import type { BlacPlugin, PluginConfig } from './BlacPlugin';
6
6
  * The manager hooks into two surfaces:
7
7
  *
8
8
  * 1. **Registry lifecycle events** (`created`, `disposed`, `refAcquired`,
9
- * `refReleased`, `depsChanged`) — synchronous, fired by the registry.
9
+ * `refReleased`, `depsChanged`, `activated`, `deactivated`) — synchronous,
10
+ * fired by the registry.
10
11
  *
11
12
  * 2. **Per-container channel flushes** — microtask-coalesced. For each
12
13
  * container, the manager subscribes once at create-time with
@@ -38,6 +39,12 @@ export declare class PluginManager {
38
39
  * manager hands plugins on each flush.
39
40
  */
40
41
  private containerBridges;
42
+ /**
43
+ * One `PluginContext` per container. The context closes over `registry` and
44
+ * `container` only — both stable for the container's lifetime — so it is
45
+ * safe to reuse instead of rebuilding its method table on every dispatch.
46
+ */
47
+ private contextCache;
41
48
  /**
42
49
  * Create a new PluginManager
43
50
  * @param registry - The StateContainerRegistry to monitor for lifecycle events
@@ -130,14 +137,12 @@ export declare class PluginManager {
130
137
  */
131
138
  private notifyPlugins;
132
139
  /**
133
- * Build a `PluginContext` for a given focal container. The context is
134
- * cheap to build — its methods close over `this.registry` directly —
135
- * so we create one per dispatch rather than caching per container.
136
- * This sidesteps the lifetime question of "when do we evict a cached
137
- * context?" and keeps `paths`-vs-`container` lifetimes cleanly
138
- * separated.
140
+ * Get the `PluginContext` for a focal container, building it once and
141
+ * reusing it thereafter. Eviction is handled by the `WeakMap`: the context
142
+ * dies with the container. `undefined` (install-time) is never cached.
139
143
  */
140
144
  private buildContext;
145
+ private createContext;
141
146
  /**
142
147
  * Check if plugin should be enabled based on environment
143
148
  */
package/dist/plugins.cjs CHANGED
@@ -1,4 +1,401 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_StateContainerRegistry = require("./StateContainerRegistry-CS18y4au.cjs");
3
- exports.PluginManager = require_StateContainerRegistry.PluginManager;
4
- exports.getPluginManager = require_StateContainerRegistry.getPluginManager;
2
+ const require_StateContainerRegistry = require("./StateContainerRegistry-141uHhtF.cjs");
3
+ let _dirtytalk_structural = require("@dirtytalk/structural");
4
+ //#region src/plugin/PluginManager.ts
5
+ /**
6
+ * Manages plugin lifecycle for the BlaC state management system.
7
+ *
8
+ * The manager hooks into two surfaces:
9
+ *
10
+ * 1. **Registry lifecycle events** (`created`, `disposed`, `refAcquired`,
11
+ * `refReleased`, `depsChanged`, `activated`, `deactivated`) — synchronous,
12
+ * fired by the registry.
13
+ *
14
+ * 2. **Per-container channel flushes** — microtask-coalesced. For each
15
+ * container, the manager subscribes once at create-time with
16
+ * `ALL_PATHS` interest and stashes `prevState`. On every flush it
17
+ * captures the new state + the changed `PathSet` and dispatches
18
+ * `onStateChange(ctx, prev, next, paths)` to every enabled plugin.
19
+ *
20
+ * The `ALL_PATHS` subscription cost: plugins counted as a consumer with
21
+ * `ALL_PATHS` interest will defeat the single-consumer-skip optimization
22
+ * in `StructuralContainer`. This is the intended trade-off — devtools /
23
+ * persist plugins genuinely want every change, and stateful plugins
24
+ * (logging) can decode `paths` via `container.interner.lookup(id)` to log
25
+ * only relevant fields. Plugins that want low overhead should remain
26
+ * uninstalled or environment-gated.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * const manager = createPluginManager(registry);
31
+ * manager.install(myPlugin, { environment: 'development' });
32
+ * ```
33
+ */
34
+ var PluginManager = class {
35
+ /**
36
+ * Create a new PluginManager
37
+ * @param registry - The StateContainerRegistry to monitor for lifecycle events
38
+ */
39
+ constructor(registry) {
40
+ this.plugins = /* @__PURE__ */ new Map();
41
+ this.lifecycleUnsubscribers = [];
42
+ this.containerBridges = /* @__PURE__ */ new WeakMap();
43
+ this.contextCache = /* @__PURE__ */ new WeakMap();
44
+ this.registry = registry;
45
+ this.setupLifecycleHooks();
46
+ }
47
+ /**
48
+ * Install a plugin with optional configuration
49
+ * @param plugin - The plugin to install
50
+ * @param config - Optional plugin configuration
51
+ * @throws Error if plugin is already installed
52
+ */
53
+ install(plugin, config = {}) {
54
+ const effectiveConfig = {
55
+ enabled: true,
56
+ environment: "all",
57
+ ...config
58
+ };
59
+ if (!this.shouldEnablePlugin(effectiveConfig)) {
60
+ if (require_StateContainerRegistry.IS_DEV) console.log(`[BlaC] Plugin "${plugin.name}" skipped (environment mismatch)`);
61
+ return;
62
+ }
63
+ if (this.plugins.has(plugin.name)) throw new Error(`Plugin "${plugin.name}" is already installed`);
64
+ const installContext = this.buildContext(void 0);
65
+ this.plugins.set(plugin.name, {
66
+ plugin,
67
+ config: effectiveConfig,
68
+ installContext
69
+ });
70
+ this.backfillPlugin(this.plugins.get(plugin.name));
71
+ if (plugin.onInstall) try {
72
+ plugin.onInstall(installContext);
73
+ } catch (error) {
74
+ console.error(`[BlaC] Error installing plugin "${plugin.name}":`, error);
75
+ this.plugins.delete(plugin.name);
76
+ throw error;
77
+ }
78
+ if (require_StateContainerRegistry.IS_DEV) console.log(`[BlaC] Plugin "${plugin.name}" v${plugin.version} installed`);
79
+ }
80
+ /**
81
+ * Uninstall a plugin by name
82
+ * @param pluginName - The name of the plugin to uninstall
83
+ * @throws Error if plugin is not installed
84
+ */
85
+ uninstall(pluginName) {
86
+ const installed = this.plugins.get(pluginName);
87
+ if (!installed) throw new Error(`Plugin "${pluginName}" is not installed`);
88
+ if (installed.plugin.onUninstall) try {
89
+ installed.plugin.onUninstall();
90
+ } catch (error) {
91
+ console.error(`[BlaC] Error uninstalling plugin "${pluginName}":`, error);
92
+ }
93
+ this.plugins.delete(pluginName);
94
+ if (require_StateContainerRegistry.IS_DEV) console.log(`[BlaC] Plugin "${pluginName}" uninstalled`);
95
+ }
96
+ /**
97
+ * Get an installed plugin by name
98
+ * @param pluginName - The name of the plugin to retrieve
99
+ * @returns The plugin instance or undefined if not found
100
+ */
101
+ getPlugin(pluginName) {
102
+ return this.plugins.get(pluginName)?.plugin;
103
+ }
104
+ /**
105
+ * Get all installed plugins
106
+ * @returns Array of all installed plugins
107
+ */
108
+ getAllPlugins() {
109
+ return Array.from(this.plugins.values()).map((p) => p.plugin);
110
+ }
111
+ /**
112
+ * Check if a plugin is installed
113
+ * @param pluginName - The name of the plugin to check
114
+ * @returns true if the plugin is installed
115
+ */
116
+ hasPlugin(pluginName) {
117
+ return this.plugins.has(pluginName);
118
+ }
119
+ /**
120
+ * Uninstall all plugins
121
+ */
122
+ clear() {
123
+ for (const name of this.plugins.keys()) this.uninstall(name);
124
+ }
125
+ destroy() {
126
+ this.clear();
127
+ for (const unsub of this.lifecycleUnsubscribers) unsub();
128
+ this.lifecycleUnsubscribers = [];
129
+ }
130
+ /**
131
+ * Wire registry lifecycle events into plugin dispatch.
132
+ *
133
+ * `onStateChange` is NOT wired through `registry.on('stateChanged', …)` —
134
+ * that event lacks the `PathSet` payload. Instead, on each `created` we
135
+ * subscribe to the container's channel directly so we get
136
+ * `(paths)` and capture `(prev, next)` via the per-container snapshot.
137
+ */
138
+ setupLifecycleHooks() {
139
+ this.lifecycleUnsubscribers = [
140
+ this.registry.on("created", (instance) => {
141
+ this.attachStateBridge(instance);
142
+ this.notifyPlugins("onCreated", instance);
143
+ }),
144
+ this.registry.on("disposed", (instance) => {
145
+ this.notifyPlugins("onDestroyed", instance);
146
+ this.detachStateBridge(instance);
147
+ }),
148
+ this.registry.on("refAcquired", (instance, refId) => {
149
+ this.notifyPlugins("onRefAcquired", instance, refId);
150
+ }),
151
+ this.registry.on("refReleased", (instance, refId) => {
152
+ this.notifyPlugins("onRefReleased", instance, refId);
153
+ }),
154
+ this.registry.on("depsChanged", (instance, previousDeps, currentDeps) => {
155
+ this.notifyPlugins("onDepsChanged", instance, previousDeps, currentDeps);
156
+ }),
157
+ this.registry.on("hydrationChanged", (instance, status, previousStatus) => {
158
+ this.notifyPlugins("onHydrationChange", instance, status, previousStatus);
159
+ }),
160
+ this.registry.on("activated", (instance, signal) => {
161
+ this.notifyPlugins("onActivate", instance, signal);
162
+ }),
163
+ this.registry.on("deactivated", (instance) => {
164
+ this.notifyPlugins("onDeactivate", instance);
165
+ })
166
+ ];
167
+ }
168
+ /**
169
+ * Subscribe to the container's channel with `ALL_PATHS` interest, so we
170
+ * fire on every flush. Capture `prev` from the snapshot taken on the
171
+ * previous flush (or at create-time for the first flush) and pass the
172
+ * channel's `paths` argument straight through to plugins.
173
+ *
174
+ * Per-container bookkeeping is stored in a `WeakMap` keyed by the
175
+ * container itself, so a disposed/GC'd container drops cleanly.
176
+ */
177
+ attachStateBridge(container) {
178
+ if (this.containerBridges.has(container)) return;
179
+ const bridge = {
180
+ unsub: () => {},
181
+ prevState: container.state
182
+ };
183
+ this.containerBridges.set(container, bridge);
184
+ bridge.unsub = container.channel.subscribe(() => _dirtytalk_structural.ALL_PATHS, (paths) => this.dispatchStateChange(container, paths));
185
+ }
186
+ detachStateBridge(container) {
187
+ const bridge = this.containerBridges.get(container);
188
+ if (!bridge) return;
189
+ bridge.unsub();
190
+ this.containerBridges.delete(container);
191
+ }
192
+ /**
193
+ * Backfill a newly-installed plugin with the app's existing instances.
194
+ *
195
+ * Iterates every registered `Type` and its live instances (`getAll`
196
+ * already skips disposed), attaching the state bridge (idempotent — see
197
+ * `attachStateBridge`) and, if the plugin implements `onCreated`, invoking
198
+ * it directly for each instance. Scoped to this single plugin only — the
199
+ * broadcast `notifyPlugins` would re-notify *every* plugin of *every*
200
+ * instance, which is wrong here.
201
+ */
202
+ backfillPlugin(installed) {
203
+ const { plugin } = installed;
204
+ for (const Type of this.registry.getTypes()) for (const instance of this.registry.getAll(Type)) {
205
+ const container = instance;
206
+ this.attachStateBridge(container);
207
+ if (!plugin.onCreated) continue;
208
+ try {
209
+ plugin.onCreated(this.buildContext(container));
210
+ } catch (error) {
211
+ console.error(`[BlaC] Error in plugin "${plugin.name}" onCreated (backfill):`, error);
212
+ }
213
+ }
214
+ }
215
+ /**
216
+ * Channel-flush callback. Snapshots `next`, hands `(prev, next, paths)`
217
+ * to every enabled plugin's `onStateChange`, then updates `prevState`
218
+ * for the next flush.
219
+ *
220
+ * Note: `prev` is captured once and reused across plugins — every
221
+ * plugin sees the same `prev`/`next`/`paths` regardless of dispatch
222
+ * order, matching the "snapshot prev once per flush" invariant.
223
+ */
224
+ dispatchStateChange(container, paths) {
225
+ const bridge = this.containerBridges.get(container);
226
+ if (!bridge) return;
227
+ const prev = bridge.prevState;
228
+ const next = container.state;
229
+ bridge.prevState = next;
230
+ let ctx;
231
+ for (const { plugin, config } of this.plugins.values()) {
232
+ if (!config.enabled) continue;
233
+ const hook = plugin.onStateChange;
234
+ if (typeof hook !== "function") continue;
235
+ try {
236
+ ctx ??= this.buildContext(container);
237
+ hook.call(plugin, ctx, prev, next, paths);
238
+ } catch (error) {
239
+ console.error(`[BlaC] Error in plugin "${plugin.name}" onStateChange:`, error);
240
+ }
241
+ }
242
+ }
243
+ /**
244
+ * Notify all plugins of a lifecycle event.
245
+ *
246
+ * Builds a fresh `PluginContext` per dispatch (instance becomes
247
+ * `ctx.container`), so plugins can reach the focal bloc through
248
+ * `ctx.container`. The context is built lazily on the first matching
249
+ * hook and reused for every other plugin in this dispatch — zero
250
+ * builds if no enabled plugin implements `hookName`.
251
+ */
252
+ notifyPlugins(hookName, instance, ...extraArgs) {
253
+ let ctx;
254
+ for (const { plugin, config } of this.plugins.values()) {
255
+ if (!config.enabled) continue;
256
+ const hook = plugin[hookName];
257
+ if (typeof hook !== "function") continue;
258
+ try {
259
+ ctx ??= this.buildContext(instance);
260
+ hook.call(plugin, ctx, ...extraArgs);
261
+ } catch (error) {
262
+ console.error(`[BlaC] Error in plugin "${plugin.name}" ${hookName}:`, error);
263
+ }
264
+ }
265
+ }
266
+ /**
267
+ * Get the `PluginContext` for a focal container, building it once and
268
+ * reusing it thereafter. Eviction is handled by the `WeakMap`: the context
269
+ * dies with the container. `undefined` (install-time) is never cached.
270
+ */
271
+ buildContext(container) {
272
+ if (container !== void 0) {
273
+ const cached = this.contextCache.get(container);
274
+ if (cached) return cached;
275
+ }
276
+ const context = this.createContext(container);
277
+ if (container !== void 0) this.contextCache.set(container, context);
278
+ return context;
279
+ }
280
+ createContext(container) {
281
+ const registry = this.registry;
282
+ return {
283
+ container,
284
+ getInstanceMetadata: (instance) => {
285
+ return {
286
+ id: instance.$blac.id,
287
+ className: require_StateContainerRegistry.getBlacName(instance.constructor),
288
+ isDisposed: instance.$blac.disposed,
289
+ name: instance.$blac.name,
290
+ createdAt: instance.$blac.createdAt,
291
+ state: instance.state,
292
+ hydrationStatus: instance.$blac.hydration.status,
293
+ isHydrated: instance.$blac.hydration.isHydrated,
294
+ hydrationError: instance.$blac.hydration.error,
295
+ changedWhileHydrating: instance.$blac.hydration.changedWhileHydrating,
296
+ args: instance.args
297
+ };
298
+ },
299
+ getState: (instance) => {
300
+ return instance.state;
301
+ },
302
+ getHydrationStatus: (instance) => {
303
+ return instance.$blac.hydration.status;
304
+ },
305
+ startHydration: (instance) => {
306
+ instance.$blac.hydration.begin();
307
+ },
308
+ applyHydratedState: (instance, state) => {
309
+ return instance.$blac.hydration.apply(state);
310
+ },
311
+ finishHydration: (instance) => {
312
+ instance.$blac.hydration.finish();
313
+ },
314
+ failHydration: (instance, error) => {
315
+ instance.$blac.hydration.fail(error);
316
+ },
317
+ waitForHydration: (instance) => {
318
+ return instance.$blac.hydration.wait();
319
+ },
320
+ queryInstances: (typeClass) => {
321
+ return registry.getAll(typeClass);
322
+ },
323
+ getAllTypes: () => {
324
+ return registry.getTypes();
325
+ },
326
+ getStats: () => {
327
+ return registry.getStats();
328
+ },
329
+ getRefIds: (instanceId) => {
330
+ return registry.getRefIdsById(instanceId);
331
+ }
332
+ };
333
+ }
334
+ /**
335
+ * Check if plugin should be enabled based on environment
336
+ */
337
+ shouldEnablePlugin(config) {
338
+ if (!config.enabled) return false;
339
+ if (config.environment === "all") return true;
340
+ return this.getCurrentEnvironment() === config.environment;
341
+ }
342
+ /**
343
+ * Get current environment
344
+ */
345
+ getCurrentEnvironment() {
346
+ if (typeof process !== "undefined") {
347
+ if (process.env.NODE_ENV === "test") return "test";
348
+ if (process.env.NODE_ENV === "production") return "production";
349
+ return "development";
350
+ }
351
+ return "development";
352
+ }
353
+ };
354
+ /**
355
+ * Create a plugin manager instance
356
+ * @param registry - The StateContainerRegistry to monitor for lifecycle events
357
+ * @returns A new PluginManager instance
358
+ */
359
+ function createPluginManager(registry) {
360
+ return new PluginManager(registry);
361
+ }
362
+ //#endregion
363
+ //#region src/plugins.ts
364
+ /**
365
+ * Plugins Subpath Export
366
+ *
367
+ * Plugin system utilities for extending BlaC functionality.
368
+ * Import from `@blac/core/plugins`
369
+ *
370
+ * @example
371
+ * ```typescript
372
+ * import { PluginManager, getPluginManager, type BlacPlugin } from '@blac/core/plugins';
373
+ *
374
+ * const myPlugin: BlacPlugin = {
375
+ * name: 'my-plugin',
376
+ * onCreated(context) {
377
+ * console.log('Instance created:', context.container);
378
+ * },
379
+ * };
380
+ *
381
+ * getPluginManager().register(myPlugin);
382
+ * ```
383
+ *
384
+ * @packageDocumentation
385
+ */
386
+ /**
387
+ * Global plugin manager (initialized lazily)
388
+ */
389
+ let _globalPluginManager = null;
390
+ /**
391
+ * Get the global plugin manager
392
+ */
393
+ function getPluginManager() {
394
+ if (!_globalPluginManager) _globalPluginManager = createPluginManager(require_StateContainerRegistry.globalRegistry);
395
+ return _globalPluginManager;
396
+ }
397
+ //#endregion
398
+ exports.PluginManager = PluginManager;
399
+ exports.getPluginManager = getPluginManager;
400
+
401
+ //# sourceMappingURL=plugins.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugins.cjs","names":["IS_DEV","ALL_PATHS","getBlacName","globalRegistry"],"sources":["../src/plugin/PluginManager.ts","../src/plugins.ts"],"sourcesContent":["import { ALL_PATHS, type PathSet } from '@dirtytalk/structural';\nimport type { StateContainer } from '../core/StateContainer';\nimport { IS_DEV } from '../constants';\nimport { getBlacName } from '../utils/static-props';\nimport type { StateContainerConstructor } from '../types/utilities';\nimport type { StateContainerRegistry } from '../core/StateContainerRegistry';\nimport type {\n BlacPlugin,\n PluginContext,\n PluginConfig,\n InstanceMetadata,\n} from './BlacPlugin';\n\n/**\n * Internal structure for tracking installed plugins.\n *\n * `installContext` is the context handed to `onInstall` — it has no\n * `container`. Per-container contexts are built on demand by\n * `buildContext()` so each event carries the right focal container\n * without mutating a shared object.\n * @internal\n */\ninterface InstalledPlugin {\n plugin: BlacPlugin;\n config: PluginConfig;\n installContext: PluginContext;\n}\n\n/**\n * Per-container bookkeeping for the channel-bridge plugin dispatcher.\n *\n * - `unsub` tears down the channel subscription on dispose.\n * - `prevState` is the state snapshot the manager will hand plugins as\n * `prev` on the next flush; it is updated to the post-flush state after\n * each dispatch.\n * @internal\n */\ninterface ContainerBridge {\n unsub: () => void;\n prevState: any;\n}\n\n/**\n * Manages plugin lifecycle for the BlaC state management system.\n *\n * The manager hooks into two surfaces:\n *\n * 1. **Registry lifecycle events** (`created`, `disposed`, `refAcquired`,\n * `refReleased`, `depsChanged`, `activated`, `deactivated`) — synchronous,\n * fired by the registry.\n *\n * 2. **Per-container channel flushes** — microtask-coalesced. For each\n * container, the manager subscribes once at create-time with\n * `ALL_PATHS` interest and stashes `prevState`. On every flush it\n * captures the new state + the changed `PathSet` and dispatches\n * `onStateChange(ctx, prev, next, paths)` to every enabled plugin.\n *\n * The `ALL_PATHS` subscription cost: plugins counted as a consumer with\n * `ALL_PATHS` interest will defeat the single-consumer-skip optimization\n * in `StructuralContainer`. This is the intended trade-off — devtools /\n * persist plugins genuinely want every change, and stateful plugins\n * (logging) can decode `paths` via `container.interner.lookup(id)` to log\n * only relevant fields. Plugins that want low overhead should remain\n * uninstalled or environment-gated.\n *\n * @example\n * ```ts\n * const manager = createPluginManager(registry);\n * manager.install(myPlugin, { environment: 'development' });\n * ```\n */\nexport class PluginManager {\n private plugins = new Map<string, InstalledPlugin>();\n private registry: StateContainerRegistry;\n private lifecycleUnsubscribers: (() => void)[] = [];\n\n /**\n * Per-container channel-bridge bookkeeping. Subscribed at `created`,\n * torn down at `disposed`. Holds the rolling `prevState` snapshot the\n * manager hands plugins on each flush.\n */\n private containerBridges = new WeakMap<\n StateContainer<any, any, any>,\n ContainerBridge\n >();\n\n /**\n * One `PluginContext` per container. The context closes over `registry` and\n * `container` only — both stable for the container's lifetime — so it is\n * safe to reuse instead of rebuilding its method table on every dispatch.\n */\n private contextCache = new WeakMap<\n StateContainer<any, any, any>,\n PluginContext\n >();\n\n /**\n * Create a new PluginManager\n * @param registry - The StateContainerRegistry to monitor for lifecycle events\n */\n constructor(registry: StateContainerRegistry) {\n this.registry = registry;\n this.setupLifecycleHooks();\n }\n\n /**\n * Install a plugin with optional configuration\n * @param plugin - The plugin to install\n * @param config - Optional plugin configuration\n * @throws Error if plugin is already installed\n */\n install(plugin: BlacPlugin, config: PluginConfig = {}): void {\n const effectiveConfig: PluginConfig = {\n enabled: true,\n environment: 'all',\n ...config,\n };\n\n if (!this.shouldEnablePlugin(effectiveConfig)) {\n if (IS_DEV) {\n console.log(\n `[BlaC] Plugin \"${plugin.name}\" skipped (environment mismatch)`,\n );\n }\n return;\n }\n\n if (this.plugins.has(plugin.name)) {\n throw new Error(`Plugin \"${plugin.name}\" is already installed`);\n }\n\n const installContext = this.buildContext(undefined);\n\n this.plugins.set(plugin.name, {\n plugin,\n config: effectiveConfig,\n installContext,\n });\n\n this.backfillPlugin(this.plugins.get(plugin.name)!);\n\n if (plugin.onInstall) {\n try {\n plugin.onInstall(installContext);\n } catch (error) {\n console.error(\n `[BlaC] Error installing plugin \"${plugin.name}\":`,\n error,\n );\n this.plugins.delete(plugin.name);\n throw error;\n }\n }\n\n if (IS_DEV) {\n console.log(\n `[BlaC] Plugin \"${plugin.name}\" v${plugin.version} installed`,\n );\n }\n }\n\n /**\n * Uninstall a plugin by name\n * @param pluginName - The name of the plugin to uninstall\n * @throws Error if plugin is not installed\n */\n uninstall(pluginName: string): void {\n const installed = this.plugins.get(pluginName);\n if (!installed) {\n throw new Error(`Plugin \"${pluginName}\" is not installed`);\n }\n\n if (installed.plugin.onUninstall) {\n try {\n installed.plugin.onUninstall();\n } catch (error) {\n console.error(\n `[BlaC] Error uninstalling plugin \"${pluginName}\":`,\n error,\n );\n }\n }\n\n this.plugins.delete(pluginName);\n if (IS_DEV) {\n console.log(`[BlaC] Plugin \"${pluginName}\" uninstalled`);\n }\n }\n\n /**\n * Get an installed plugin by name\n * @param pluginName - The name of the plugin to retrieve\n * @returns The plugin instance or undefined if not found\n */\n getPlugin(pluginName: string): BlacPlugin | undefined {\n return this.plugins.get(pluginName)?.plugin;\n }\n\n /**\n * Get all installed plugins\n * @returns Array of all installed plugins\n */\n getAllPlugins(): BlacPlugin[] {\n return Array.from(this.plugins.values()).map((p) => p.plugin);\n }\n\n /**\n * Check if a plugin is installed\n * @param pluginName - The name of the plugin to check\n * @returns true if the plugin is installed\n */\n hasPlugin(pluginName: string): boolean {\n return this.plugins.has(pluginName);\n }\n\n /**\n * Uninstall all plugins\n */\n clear(): void {\n for (const name of this.plugins.keys()) {\n this.uninstall(name);\n }\n }\n\n destroy(): void {\n this.clear();\n for (const unsub of this.lifecycleUnsubscribers) {\n unsub();\n }\n this.lifecycleUnsubscribers = [];\n }\n\n /**\n * Wire registry lifecycle events into plugin dispatch.\n *\n * `onStateChange` is NOT wired through `registry.on('stateChanged', …)` —\n * that event lacks the `PathSet` payload. Instead, on each `created` we\n * subscribe to the container's channel directly so we get\n * `(paths)` and capture `(prev, next)` via the per-container snapshot.\n */\n private setupLifecycleHooks(): void {\n this.lifecycleUnsubscribers = [\n this.registry.on('created', (instance) => {\n this.attachStateBridge(instance);\n this.notifyPlugins('onCreated', instance);\n }),\n this.registry.on('disposed', (instance) => {\n this.notifyPlugins('onDestroyed', instance);\n this.detachStateBridge(instance);\n }),\n this.registry.on('refAcquired', (instance, refId) => {\n this.notifyPlugins('onRefAcquired', instance, refId);\n }),\n this.registry.on('refReleased', (instance, refId) => {\n this.notifyPlugins('onRefReleased', instance, refId);\n }),\n this.registry.on('depsChanged', (instance, previousDeps, currentDeps) => {\n this.notifyPlugins(\n 'onDepsChanged',\n instance,\n previousDeps,\n currentDeps,\n );\n }),\n this.registry.on(\n 'hydrationChanged',\n (instance, status, previousStatus) => {\n this.notifyPlugins(\n 'onHydrationChange',\n instance,\n status,\n previousStatus,\n );\n },\n ),\n this.registry.on('activated', (instance, signal) => {\n this.notifyPlugins('onActivate', instance, signal);\n }),\n this.registry.on('deactivated', (instance) => {\n this.notifyPlugins('onDeactivate', instance);\n }),\n ];\n }\n\n /**\n * Subscribe to the container's channel with `ALL_PATHS` interest, so we\n * fire on every flush. Capture `prev` from the snapshot taken on the\n * previous flush (or at create-time for the first flush) and pass the\n * channel's `paths` argument straight through to plugins.\n *\n * Per-container bookkeeping is stored in a `WeakMap` keyed by the\n * container itself, so a disposed/GC'd container drops cleanly.\n */\n private attachStateBridge(container: StateContainer<any, any, any>): void {\n // Defensive: if a container is somehow created twice (it shouldn't be),\n // don't double-subscribe — the existing bridge is canonical.\n if (this.containerBridges.has(container)) return;\n\n const bridge: ContainerBridge = {\n unsub: () => {},\n prevState: container.state,\n };\n this.containerBridges.set(container, bridge);\n\n bridge.unsub = container.channel.subscribe(\n () => ALL_PATHS,\n (paths) => this.dispatchStateChange(container, paths),\n );\n }\n\n private detachStateBridge(container: StateContainer<any, any, any>): void {\n const bridge = this.containerBridges.get(container);\n if (!bridge) return;\n bridge.unsub();\n this.containerBridges.delete(container);\n }\n\n /**\n * Backfill a newly-installed plugin with the app's existing instances.\n *\n * Iterates every registered `Type` and its live instances (`getAll`\n * already skips disposed), attaching the state bridge (idempotent — see\n * `attachStateBridge`) and, if the plugin implements `onCreated`, invoking\n * it directly for each instance. Scoped to this single plugin only — the\n * broadcast `notifyPlugins` would re-notify *every* plugin of *every*\n * instance, which is wrong here.\n */\n private backfillPlugin(installed: InstalledPlugin): void {\n const { plugin } = installed;\n for (const Type of this.registry.getTypes()) {\n for (const instance of this.registry.getAll(Type)) {\n // getAll returns the readonly public view; internal bridging needs the\n // real container (same friction the `as any` in queryInstances handles).\n const container = instance as StateContainer<any, any, any>;\n this.attachStateBridge(container);\n\n if (!plugin.onCreated) continue;\n try {\n plugin.onCreated(this.buildContext(container));\n } catch (error) {\n console.error(\n `[BlaC] Error in plugin \"${plugin.name}\" onCreated (backfill):`,\n error,\n );\n }\n }\n }\n }\n\n /**\n * Channel-flush callback. Snapshots `next`, hands `(prev, next, paths)`\n * to every enabled plugin's `onStateChange`, then updates `prevState`\n * for the next flush.\n *\n * Note: `prev` is captured once and reused across plugins — every\n * plugin sees the same `prev`/`next`/`paths` regardless of dispatch\n * order, matching the \"snapshot prev once per flush\" invariant.\n */\n private dispatchStateChange(\n container: StateContainer<any, any, any>,\n paths: PathSet,\n ): void {\n const bridge = this.containerBridges.get(container);\n if (!bridge) return;\n\n const prev = bridge.prevState;\n const next = container.state;\n bridge.prevState = next;\n\n let ctx: PluginContext | undefined;\n for (const { plugin, config } of this.plugins.values()) {\n if (!config.enabled) continue;\n // eslint-disable-next-line @typescript-eslint/unbound-method -- invoked via .call below\n const hook = plugin.onStateChange;\n if (typeof hook !== 'function') continue;\n\n try {\n ctx ??= this.buildContext(container);\n hook.call(plugin, ctx, prev, next, paths);\n } catch (error) {\n console.error(\n `[BlaC] Error in plugin \"${plugin.name}\" onStateChange:`,\n error,\n );\n }\n }\n }\n\n /**\n * Notify all plugins of a lifecycle event.\n *\n * Builds a fresh `PluginContext` per dispatch (instance becomes\n * `ctx.container`), so plugins can reach the focal bloc through\n * `ctx.container`. The context is built lazily on the first matching\n * hook and reused for every other plugin in this dispatch — zero\n * builds if no enabled plugin implements `hookName`.\n */\n private notifyPlugins(\n hookName: Exclude<keyof BlacPlugin, 'onStateChange'>,\n instance: StateContainer<any, any, any>,\n ...extraArgs: any[]\n ): void {\n let ctx: PluginContext | undefined;\n for (const { plugin, config } of this.plugins.values()) {\n if (!config.enabled) continue;\n\n const hook = plugin[hookName];\n if (typeof hook !== 'function') continue;\n\n try {\n ctx ??= this.buildContext(instance);\n (hook as any).call(plugin, ctx, ...extraArgs);\n } catch (error) {\n console.error(\n `[BlaC] Error in plugin \"${plugin.name}\" ${hookName}:`,\n error,\n );\n }\n }\n }\n\n /**\n * Get the `PluginContext` for a focal container, building it once and\n * reusing it thereafter. Eviction is handled by the `WeakMap`: the context\n * dies with the container. `undefined` (install-time) is never cached.\n */\n private buildContext(\n container: StateContainer<any, any, any> | undefined,\n ): PluginContext {\n if (container !== undefined) {\n const cached = this.contextCache.get(container);\n if (cached) return cached;\n }\n const context = this.createContext(container);\n if (container !== undefined) this.contextCache.set(container, context);\n return context;\n }\n\n private createContext(\n container: StateContainer<any, any, any> | undefined,\n ): PluginContext {\n const registry = this.registry;\n return {\n container,\n\n getInstanceMetadata: (\n instance: StateContainer<any, any, any>,\n ): InstanceMetadata => {\n return {\n id: instance.$blac.id,\n className: getBlacName(\n instance.constructor as StateContainerConstructor,\n ),\n isDisposed: instance.$blac.disposed,\n name: instance.$blac.name,\n createdAt: instance.$blac.createdAt,\n state: instance.state,\n hydrationStatus: instance.$blac.hydration.status,\n isHydrated: instance.$blac.hydration.isHydrated,\n hydrationError: instance.$blac.hydration.error,\n changedWhileHydrating: instance.$blac.hydration.changedWhileHydrating,\n args: instance.args,\n };\n },\n\n getState: <S extends object = any>(instance: StateContainer<S>): S => {\n return instance.state;\n },\n\n getHydrationStatus: (instance: StateContainer<any, any, any>) => {\n return instance.$blac.hydration.status;\n },\n\n startHydration: (instance: StateContainer<any, any, any>) => {\n instance.$blac.hydration.begin();\n },\n\n applyHydratedState: <S extends object = any>(\n instance: StateContainer<S>,\n state: S,\n ): boolean => {\n return instance.$blac.hydration.apply(state);\n },\n\n finishHydration: (instance: StateContainer<any, any, any>) => {\n instance.$blac.hydration.finish();\n },\n\n failHydration: (\n instance: StateContainer<any, any, any>,\n error: Error,\n ) => {\n instance.$blac.hydration.fail(error);\n },\n\n waitForHydration: (instance: StateContainer<any, any, any>) => {\n return instance.$blac.hydration.wait();\n },\n\n queryInstances: <T extends StateContainer<any, any, any>>(\n typeClass: new (...args: any[]) => T,\n ): T[] => {\n return registry.getAll(typeClass as any);\n },\n\n getAllTypes: () => {\n return registry.getTypes();\n },\n\n getStats: () => {\n return registry.getStats();\n },\n\n getRefIds: (instanceId: string): string[] => {\n return registry.getRefIdsById(instanceId);\n },\n };\n }\n\n /**\n * Check if plugin should be enabled based on environment\n */\n private shouldEnablePlugin(config: PluginConfig): boolean {\n if (!config.enabled) return false;\n if (config.environment === 'all') return true;\n\n const currentEnv = this.getCurrentEnvironment();\n return currentEnv === config.environment;\n }\n\n /**\n * Get current environment\n */\n private getCurrentEnvironment(): 'development' | 'production' | 'test' {\n if (typeof process !== 'undefined') {\n if (process.env.NODE_ENV === 'test') return 'test';\n if (process.env.NODE_ENV === 'production') return 'production';\n return 'development';\n }\n return 'development';\n }\n}\n\n/**\n * Create a plugin manager instance\n * @param registry - The StateContainerRegistry to monitor for lifecycle events\n * @returns A new PluginManager instance\n */\nexport function createPluginManager(\n registry: StateContainerRegistry,\n): PluginManager {\n return new PluginManager(registry);\n}\n","/**\n * Plugins Subpath Export\n *\n * Plugin system utilities for extending BlaC functionality.\n * Import from `@blac/core/plugins`\n *\n * @example\n * ```typescript\n * import { PluginManager, getPluginManager, type BlacPlugin } from '@blac/core/plugins';\n *\n * const myPlugin: BlacPlugin = {\n * name: 'my-plugin',\n * onCreated(context) {\n * console.log('Instance created:', context.container);\n * },\n * };\n *\n * getPluginManager().register(myPlugin);\n * ```\n *\n * @packageDocumentation\n */\n\nimport { createPluginManager, PluginManager } from './plugin/PluginManager';\nimport { globalRegistry } from './core/StateContainerRegistry';\n\nexport { PluginManager } from './plugin/PluginManager';\nexport type {\n BlacPlugin,\n BlacPluginWithInit,\n PluginContext,\n PluginConfig,\n InstanceMetadata,\n} from './plugin/BlacPlugin';\n\n/**\n * Global plugin manager (initialized lazily)\n */\nlet _globalPluginManager: PluginManager | null = null;\n\n/**\n * Get the global plugin manager\n */\nexport function getPluginManager(): PluginManager {\n if (!_globalPluginManager) {\n _globalPluginManager = createPluginManager(globalRegistry);\n }\n return _globalPluginManager;\n}\n"],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  * Plugins Subpath Export
3
3
  *
4
4
  * Plugin system utilities for extending BlaC functionality.
5
- * Import from '@blac/core/plugins'
5
+ * Import from `@blac/core/plugins`
6
6
  *
7
7
  * @example
8
8
  * ```typescript
@@ -20,6 +20,10 @@
20
20
  *
21
21
  * @packageDocumentation
22
22
  */
23
+ import { PluginManager } from './plugin/PluginManager';
23
24
  export { PluginManager } from './plugin/PluginManager';
24
25
  export type { BlacPlugin, BlacPluginWithInit, PluginContext, PluginConfig, InstanceMetadata, } from './plugin/BlacPlugin';
25
- export { getPluginManager } from './core/StateContainerRegistry';
26
+ /**
27
+ * Get the global plugin manager
28
+ */
29
+ export declare function getPluginManager(): PluginManager;
package/dist/plugins.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Plugins Subpath Export
3
3
  *
4
4
  * Plugin system utilities for extending BlaC functionality.
5
- * Import from '@blac/core/plugins'
5
+ * Import from `@blac/core/plugins`
6
6
  *
7
7
  * @example
8
8
  * ```typescript
@@ -20,6 +20,10 @@
20
20
  *
21
21
  * @packageDocumentation
22
22
  */
23
+ import { PluginManager } from './plugin/PluginManager';
23
24
  export { PluginManager } from './plugin/PluginManager';
24
25
  export type { BlacPlugin, BlacPluginWithInit, PluginContext, PluginConfig, InstanceMetadata, } from './plugin/BlacPlugin';
25
- export { getPluginManager } from './core/StateContainerRegistry';
26
+ /**
27
+ * Get the global plugin manager
28
+ */
29
+ export declare function getPluginManager(): PluginManager;