@blac/core 2.0.20 → 2.0.22
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.
- package/README.md +19 -29
- package/dist/{StateContainerRegistry-CSNm2Ws5.cjs → StateContainerRegistry-141uHhtF.cjs} +300 -397
- package/dist/StateContainerRegistry-141uHhtF.cjs.map +1 -0
- package/dist/{StateContainerRegistry-DVIBTxGZ.js → StateContainerRegistry-BH7aOMWV.js} +299 -406
- package/dist/StateContainerRegistry-BH7aOMWV.js.map +1 -0
- package/dist/{StateContainerRegistry-Dj77cok9.js → StateContainerRegistry-BsH_s0pJ.js} +303 -413
- package/dist/StateContainerRegistry-BsH_s0pJ.js.map +1 -0
- package/dist/{StateContainerRegistry-lIwo-0m3.cjs → StateContainerRegistry-CBLeHObO.cjs} +326 -418
- package/dist/StateContainerRegistry-CBLeHObO.cjs.map +1 -0
- package/dist/{StateContainerRegistry-D9BIRjwv.cjs → StateContainerRegistry-MkT0W4C2.cjs} +322 -411
- package/dist/StateContainerRegistry-MkT0W4C2.cjs.map +1 -0
- package/dist/{StateContainerRegistry-CvBOhAT2.js → StateContainerRegistry-vWgsD-RP.js} +277 -392
- package/dist/StateContainerRegistry-vWgsD-RP.js.map +1 -0
- package/dist/config.d.cts +2 -2
- package/dist/config.d.ts +2 -2
- package/dist/constants.d.cts +4 -20
- package/dist/constants.d.ts +4 -20
- package/dist/core/Cubit.d.ts +14 -10
- package/dist/core/StateContainer.d.ts +68 -8
- package/dist/core/StateContainerRegistry.d.ts +100 -19
- package/dist/core/meta.d.ts +34 -47
- package/dist/core/symbols.d.ts +18 -1
- package/dist/decorators/blac.d.ts +28 -26
- package/dist/{ensure-BKdq5xsY.cjs → ensure-BjhbDW8Z.cjs} +223 -40
- package/dist/ensure-BjhbDW8Z.cjs.map +1 -0
- package/dist/ensure-BnVyd6s4.js +730 -0
- package/dist/ensure-BnVyd6s4.js.map +1 -0
- package/dist/ensure-CneMkpbI.js +731 -0
- package/dist/ensure-CneMkpbI.js.map +1 -0
- package/dist/{ensure-CQimeD0I.cjs → ensure-D15vGnk5.cjs} +348 -124
- package/dist/ensure-D15vGnk5.cjs.map +1 -0
- package/dist/{ensure-B1dEDsZV.cjs → ensure-D3w6YGp9.cjs} +349 -124
- package/dist/ensure-D3w6YGp9.cjs.map +1 -0
- package/dist/{ensure-B2DYCbbI.cjs → ensure-ORVw-WRE.cjs} +214 -39
- package/dist/ensure-ORVw-WRE.cjs.map +1 -0
- package/dist/{ensure-B59ri8oo.js → ensure-dsqWhIuO.js} +192 -39
- package/dist/ensure-dsqWhIuO.js.map +1 -0
- package/dist/{ensure-BSJHkUNX.js → ensure-k-6q7O25.js} +183 -38
- package/dist/ensure-k-6q7O25.js.map +1 -0
- package/dist/index.cjs +212 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -6
- package/dist/index.d.ts +4 -6
- package/dist/index.js +192 -12
- package/dist/index.js.map +1 -1
- package/dist/plugin/BlacPlugin.d.ts +14 -0
- package/dist/plugin/PluginManager.d.ts +21 -11
- package/dist/plugins.cjs +403 -3
- package/dist/plugins.cjs.map +1 -0
- package/dist/plugins.d.cts +6 -2
- package/dist/plugins.d.ts +6 -2
- package/dist/plugins.js +401 -1
- package/dist/plugins.js.map +1 -0
- package/dist/registry/acquire.d.ts +2 -2
- package/dist/registry/config.d.ts +9 -0
- package/dist/registry/management.d.ts +3 -0
- package/dist/registry/queries.d.ts +15 -0
- package/dist/registry/release.d.ts +3 -3
- package/dist/testing.cjs +13 -15
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +2 -4
- package/dist/testing.js.map +1 -1
- package/dist/types/branded.d.ts +3 -3
- package/dist/types/utilities.d.ts +45 -18
- package/dist/utils/static-props.d.ts +25 -1
- package/dist/utils/structural-key.d.ts +0 -7
- package/dist/watch/index.d.ts +1 -1
- package/dist/watch/watch.d.ts +22 -3
- package/package.json +16 -55
- package/dist/StateContainerRegistry-4Ljd9EJx.js +0 -1018
- package/dist/StateContainerRegistry-4Ljd9EJx.js.map +0 -1
- package/dist/StateContainerRegistry-8zNFMAf7.js +0 -1067
- package/dist/StateContainerRegistry-8zNFMAf7.js.map +0 -1
- package/dist/StateContainerRegistry-B1GYx9Xo.js +0 -975
- package/dist/StateContainerRegistry-B1GYx9Xo.js.map +0 -1
- package/dist/StateContainerRegistry-B_ViBLvJ.js +0 -975
- package/dist/StateContainerRegistry-B_ViBLvJ.js.map +0 -1
- package/dist/StateContainerRegistry-Bx2ly-ba.cjs +0 -1162
- package/dist/StateContainerRegistry-Bx2ly-ba.cjs.map +0 -1
- package/dist/StateContainerRegistry-C0dCNlTF.js +0 -1005
- package/dist/StateContainerRegistry-C0dCNlTF.js.map +0 -1
- package/dist/StateContainerRegistry-CEWpc1bj.cjs +0 -1113
- package/dist/StateContainerRegistry-CEWpc1bj.cjs.map +0 -1
- package/dist/StateContainerRegistry-CS18y4au.cjs +0 -1163
- package/dist/StateContainerRegistry-CS18y4au.cjs.map +0 -1
- package/dist/StateContainerRegistry-CSNm2Ws5.cjs.map +0 -1
- package/dist/StateContainerRegistry-CchRjnjX.cjs +0 -1094
- package/dist/StateContainerRegistry-CchRjnjX.cjs.map +0 -1
- package/dist/StateContainerRegistry-CvBOhAT2.js.map +0 -1
- package/dist/StateContainerRegistry-D8nu7Z0T.js +0 -1068
- package/dist/StateContainerRegistry-D8nu7Z0T.js.map +0 -1
- package/dist/StateContainerRegistry-D9BIRjwv.cjs.map +0 -1
- package/dist/StateContainerRegistry-DJQqLrli.cjs +0 -1046
- package/dist/StateContainerRegistry-DJQqLrli.cjs.map +0 -1
- package/dist/StateContainerRegistry-DVIBTxGZ.js.map +0 -1
- package/dist/StateContainerRegistry-Dj77cok9.js.map +0 -1
- package/dist/StateContainerRegistry-FWKzPfS-.cjs +0 -1046
- package/dist/StateContainerRegistry-FWKzPfS-.cjs.map +0 -1
- package/dist/StateContainerRegistry-lIwo-0m3.cjs.map +0 -1
- package/dist/acquire-B1LYXSGj.js +0 -35
- package/dist/acquire-B1LYXSGj.js.map +0 -1
- package/dist/acquire-B1Wuqla6.cjs +0 -46
- package/dist/acquire-B1Wuqla6.cjs.map +0 -1
- package/dist/acquire-BJ3xUEmh.cjs +0 -46
- package/dist/acquire-BJ3xUEmh.cjs.map +0 -1
- package/dist/acquire-BP-ZfUH2.js +0 -35
- package/dist/acquire-BP-ZfUH2.js.map +0 -1
- package/dist/acquire-BRIJVJaA.js +0 -35
- package/dist/acquire-BRIJVJaA.js.map +0 -1
- package/dist/acquire-BZIs6tpN.cjs +0 -46
- package/dist/acquire-BZIs6tpN.cjs.map +0 -1
- package/dist/acquire-B_p1fNEh.cjs +0 -46
- package/dist/acquire-B_p1fNEh.cjs.map +0 -1
- package/dist/acquire-BdsVZBSe.js +0 -35
- package/dist/acquire-BdsVZBSe.js.map +0 -1
- package/dist/acquire-C3nOb3XR.cjs +0 -46
- package/dist/acquire-C3nOb3XR.cjs.map +0 -1
- package/dist/acquire-C4WAS13G.cjs +0 -46
- package/dist/acquire-C4WAS13G.cjs.map +0 -1
- package/dist/acquire-C7beGI55.cjs +0 -46
- package/dist/acquire-C7beGI55.cjs.map +0 -1
- package/dist/acquire-CbMz7gWs.cjs +0 -46
- package/dist/acquire-CbMz7gWs.cjs.map +0 -1
- package/dist/acquire-CwB083au.js +0 -35
- package/dist/acquire-CwB083au.js.map +0 -1
- package/dist/acquire-DHEOEVcj.cjs +0 -46
- package/dist/acquire-DHEOEVcj.cjs.map +0 -1
- package/dist/acquire-DXEQDZGr.js +0 -35
- package/dist/acquire-DXEQDZGr.js.map +0 -1
- package/dist/acquire-Dg-uXwjF.js +0 -35
- package/dist/acquire-Dg-uXwjF.js.map +0 -1
- package/dist/acquire-Don7499S.js +0 -35
- package/dist/acquire-Don7499S.js.map +0 -1
- package/dist/acquire-G8aB3GAx.js +0 -35
- package/dist/acquire-G8aB3GAx.js.map +0 -1
- package/dist/branded-BybA0_xw.cjs +0 -18
- package/dist/branded-BybA0_xw.cjs.map +0 -1
- package/dist/branded-DkbUzncc.js +0 -13
- package/dist/branded-DkbUzncc.js.map +0 -1
- package/dist/config-8pyOC5gv.js +0 -16
- package/dist/config-8pyOC5gv.js.map +0 -1
- package/dist/config-B1l8ZGlU.cjs +0 -32
- package/dist/config-B1l8ZGlU.cjs.map +0 -1
- package/dist/config-C7Ktkjh2.cjs +0 -32
- package/dist/config-C7Ktkjh2.cjs.map +0 -1
- package/dist/config-COT4Xkbc.js +0 -16
- package/dist/config-COT4Xkbc.js.map +0 -1
- package/dist/config-CzcoAvBo.js +0 -16
- package/dist/config-CzcoAvBo.js.map +0 -1
- package/dist/config-D6BoWWPv.js +0 -16
- package/dist/config-D6BoWWPv.js.map +0 -1
- package/dist/config-DOO82lbZ.js +0 -16
- package/dist/config-DOO82lbZ.js.map +0 -1
- package/dist/config-DVM_ESQt.js +0 -16
- package/dist/config-DVM_ESQt.js.map +0 -1
- package/dist/config-DVMnlHLv.cjs +0 -32
- package/dist/config-DVMnlHLv.cjs.map +0 -1
- package/dist/config-DWUq7zEQ.cjs +0 -32
- package/dist/config-DWUq7zEQ.cjs.map +0 -1
- package/dist/config-DXI3kzmA.js +0 -16
- package/dist/config-DXI3kzmA.js.map +0 -1
- package/dist/config-Dk1FxfXj.js +0 -16
- package/dist/config-Dk1FxfXj.js.map +0 -1
- package/dist/config-b2z51Y_C.cjs +0 -32
- package/dist/config-b2z51Y_C.cjs.map +0 -1
- package/dist/config-buwpT7l3.js +0 -16
- package/dist/config-buwpT7l3.js.map +0 -1
- package/dist/config-cwaTn5oy.cjs +0 -32
- package/dist/config-cwaTn5oy.cjs.map +0 -1
- package/dist/config-d9_U58ef.cjs +0 -32
- package/dist/config-d9_U58ef.cjs.map +0 -1
- package/dist/config-hWTayX76.cjs +0 -32
- package/dist/config-hWTayX76.cjs.map +0 -1
- package/dist/config-sCPKPYxZ.cjs +0 -32
- package/dist/config-sCPKPYxZ.cjs.map +0 -1
- package/dist/debug.cjs +0 -14
- package/dist/debug.d.cts +0 -34
- package/dist/debug.d.ts +0 -34
- package/dist/debug.js +0 -4
- package/dist/ensure-B1dEDsZV.cjs.map +0 -1
- package/dist/ensure-B2DYCbbI.cjs.map +0 -1
- package/dist/ensure-B59ri8oo.js.map +0 -1
- package/dist/ensure-B9oOqpOB.cjs +0 -558
- package/dist/ensure-B9oOqpOB.cjs.map +0 -1
- package/dist/ensure-BKdq5xsY.cjs.map +0 -1
- package/dist/ensure-BSJHkUNX.js.map +0 -1
- package/dist/ensure-BfYnvAsI.js +0 -709
- package/dist/ensure-BfYnvAsI.js.map +0 -1
- package/dist/ensure-Bm91o37h.js +0 -536
- package/dist/ensure-Bm91o37h.js.map +0 -1
- package/dist/ensure-C1a8IFWy.js +0 -535
- package/dist/ensure-C1a8IFWy.js.map +0 -1
- package/dist/ensure-CLQEx0Nn.cjs +0 -528
- package/dist/ensure-CLQEx0Nn.cjs.map +0 -1
- package/dist/ensure-CPMiGvov.js +0 -493
- package/dist/ensure-CPMiGvov.js.map +0 -1
- package/dist/ensure-CQimeD0I.cjs.map +0 -1
- package/dist/ensure-DEXSmA-N.js +0 -526
- package/dist/ensure-DEXSmA-N.js.map +0 -1
- package/dist/ensure-DTTTd2h8.js +0 -536
- package/dist/ensure-DTTTd2h8.js.map +0 -1
- package/dist/ensure-Dx86f80B.cjs +0 -567
- package/dist/ensure-Dx86f80B.cjs.map +0 -1
- package/dist/ensure-DzqGej43.js +0 -526
- package/dist/ensure-DzqGej43.js.map +0 -1
- package/dist/ensure-VuiNPccZ.cjs +0 -559
- package/dist/ensure-VuiNPccZ.cjs.map +0 -1
- package/dist/ensure-_Oh8XPm4.cjs +0 -732
- package/dist/ensure-_Oh8XPm4.cjs.map +0 -1
- package/dist/ensure-fsQMjaQL.cjs +0 -567
- package/dist/ensure-fsQMjaQL.cjs.map +0 -1
- package/dist/ensure-vxAGa2v3.js +0 -536
- package/dist/ensure-vxAGa2v3.js.map +0 -1
- package/dist/management-14vsHr4t.cjs +0 -85
- package/dist/management-14vsHr4t.cjs.map +0 -1
- package/dist/management-444tdCBG.js +0 -38
- package/dist/management-444tdCBG.js.map +0 -1
- package/dist/management-7jLjVHJd.js +0 -38
- package/dist/management-7jLjVHJd.js.map +0 -1
- package/dist/management-AFuqQEwz.js +0 -38
- package/dist/management-AFuqQEwz.js.map +0 -1
- package/dist/management-BodVLAfJ.cjs +0 -85
- package/dist/management-BodVLAfJ.cjs.map +0 -1
- package/dist/management-BqGBdL01.js +0 -38
- package/dist/management-BqGBdL01.js.map +0 -1
- package/dist/management-BvUAjril.js +0 -38
- package/dist/management-BvUAjril.js.map +0 -1
- package/dist/management-C-WGcwPD.js +0 -38
- package/dist/management-C-WGcwPD.js.map +0 -1
- package/dist/management-C2wv4vGS.cjs +0 -85
- package/dist/management-C2wv4vGS.cjs.map +0 -1
- package/dist/management-D2pjr38g.js +0 -38
- package/dist/management-D2pjr38g.js.map +0 -1
- package/dist/management-D5WsZoTV.js +0 -38
- package/dist/management-D5WsZoTV.js.map +0 -1
- package/dist/management-DAV9sN-k.cjs +0 -85
- package/dist/management-DAV9sN-k.cjs.map +0 -1
- package/dist/management-Df7Y2sF1.cjs +0 -85
- package/dist/management-Df7Y2sF1.cjs.map +0 -1
- package/dist/management-DwO0nBzL.cjs +0 -85
- package/dist/management-DwO0nBzL.cjs.map +0 -1
- package/dist/management-GcnNJjnm.cjs +0 -85
- package/dist/management-GcnNJjnm.cjs.map +0 -1
- package/dist/management-P_Vi_cvw.js +0 -38
- package/dist/management-P_Vi_cvw.js.map +0 -1
- package/dist/management-QWLpbXlW.cjs +0 -85
- package/dist/management-QWLpbXlW.cjs.map +0 -1
- package/dist/management-iE4x6IQY.cjs +0 -85
- package/dist/management-iE4x6IQY.cjs.map +0 -1
- package/dist/types.cjs +0 -3
- package/dist/types.d.cts +0 -8
- package/dist/types.d.ts +0 -8
- package/dist/types.js +0 -2
- package/dist/watch-B0pUfh-v.cjs +0 -119
- package/dist/watch-B0pUfh-v.cjs.map +0 -1
- package/dist/watch-B91opmva.cjs +0 -70
- package/dist/watch-B91opmva.cjs.map +0 -1
- package/dist/watch-BHfCtbn1.cjs +0 -119
- package/dist/watch-BHfCtbn1.cjs.map +0 -1
- package/dist/watch-BmC-Tyqg.js +0 -59
- package/dist/watch-BmC-Tyqg.js.map +0 -1
- package/dist/watch-Bx0y-Kcc.js +0 -108
- package/dist/watch-Bx0y-Kcc.js.map +0 -1
- package/dist/watch-CKWt788i.cjs +0 -70
- package/dist/watch-CKWt788i.cjs.map +0 -1
- package/dist/watch-CfrSSrRY.js +0 -59
- package/dist/watch-CfrSSrRY.js.map +0 -1
- package/dist/watch-Cnh3PtQA.cjs +0 -70
- package/dist/watch-Cnh3PtQA.cjs.map +0 -1
- package/dist/watch-D01MdwKy.js +0 -108
- package/dist/watch-D01MdwKy.js.map +0 -1
- package/dist/watch-D8hwbFU1.js +0 -59
- package/dist/watch-D8hwbFU1.js.map +0 -1
- package/dist/watch-DPVUwPUY.js +0 -59
- package/dist/watch-DPVUwPUY.js.map +0 -1
- package/dist/watch-DVmvZBMD.cjs +0 -70
- package/dist/watch-DVmvZBMD.cjs.map +0 -1
- package/dist/watch-DkDGmGAx.cjs +0 -119
- package/dist/watch-DkDGmGAx.cjs.map +0 -1
- package/dist/watch-DttHqefn.js +0 -108
- package/dist/watch-DttHqefn.js.map +0 -1
- package/dist/watch-DxdIZjpt.cjs +0 -119
- package/dist/watch-DxdIZjpt.cjs.map +0 -1
- package/dist/watch-DztcJuUy.cjs +0 -119
- package/dist/watch-DztcJuUy.cjs.map +0 -1
- package/dist/watch-IrR8G28N.js +0 -108
- package/dist/watch-IrR8G28N.js.map +0 -1
- package/dist/watch-entry.cjs +0 -4
- package/dist/watch-entry.d.cts +0 -24
- package/dist/watch-entry.d.ts +0 -24
- package/dist/watch-entry.js +0 -2
- package/dist/watch-hx0r92Zc.js +0 -108
- package/dist/watch-hx0r92Zc.js.map +0 -1
package/dist/plugins.js
CHANGED
|
@@ -1,2 +1,402 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { l as getBlacName, m as IS_DEV, n as globalRegistry } from "./StateContainerRegistry-BsH_s0pJ.js";
|
|
2
|
+
import { ALL_PATHS } from "@dirtytalk/structural";
|
|
3
|
+
//#region src/plugin/PluginManager.ts
|
|
4
|
+
/**
|
|
5
|
+
* Manages plugin lifecycle for the BlaC state management system.
|
|
6
|
+
*
|
|
7
|
+
* The manager hooks into two surfaces:
|
|
8
|
+
*
|
|
9
|
+
* 1. **Registry lifecycle events** (`created`, `disposed`, `refAcquired`,
|
|
10
|
+
* `refReleased`, `depsChanged`, `activated`, `deactivated`) — synchronous,
|
|
11
|
+
* fired by the registry.
|
|
12
|
+
*
|
|
13
|
+
* 2. **Per-container channel flushes** — microtask-coalesced. For each
|
|
14
|
+
* container, the manager subscribes once at create-time with
|
|
15
|
+
* `ALL_PATHS` interest and stashes `prevState`. On every flush it
|
|
16
|
+
* captures the new state + the changed `PathSet` and dispatches
|
|
17
|
+
* `onStateChange(ctx, prev, next, paths)` to every enabled plugin.
|
|
18
|
+
*
|
|
19
|
+
* The `ALL_PATHS` subscription cost: plugins counted as a consumer with
|
|
20
|
+
* `ALL_PATHS` interest will defeat the single-consumer-skip optimization
|
|
21
|
+
* in `StructuralContainer`. This is the intended trade-off — devtools /
|
|
22
|
+
* persist plugins genuinely want every change, and stateful plugins
|
|
23
|
+
* (logging) can decode `paths` via `container.interner.lookup(id)` to log
|
|
24
|
+
* only relevant fields. Plugins that want low overhead should remain
|
|
25
|
+
* uninstalled or environment-gated.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const manager = createPluginManager(registry);
|
|
30
|
+
* manager.install(myPlugin, { environment: 'development' });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
var PluginManager = class {
|
|
34
|
+
/**
|
|
35
|
+
* Create a new PluginManager
|
|
36
|
+
* @param registry - The StateContainerRegistry to monitor for lifecycle events
|
|
37
|
+
*/
|
|
38
|
+
constructor(registry) {
|
|
39
|
+
this.plugins = /* @__PURE__ */ new Map();
|
|
40
|
+
this.lifecycleUnsubscribers = [];
|
|
41
|
+
this.containerBridges = /* @__PURE__ */ new Map();
|
|
42
|
+
this.contextCache = /* @__PURE__ */ new WeakMap();
|
|
43
|
+
this.registry = registry;
|
|
44
|
+
this.setupLifecycleHooks();
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Install a plugin with optional configuration
|
|
48
|
+
* @param plugin - The plugin to install
|
|
49
|
+
* @param config - Optional plugin configuration
|
|
50
|
+
* @throws Error if plugin is already installed
|
|
51
|
+
*/
|
|
52
|
+
install(plugin, config = {}) {
|
|
53
|
+
const effectiveConfig = {
|
|
54
|
+
enabled: true,
|
|
55
|
+
environment: "all",
|
|
56
|
+
...config
|
|
57
|
+
};
|
|
58
|
+
if (!this.shouldEnablePlugin(effectiveConfig)) {
|
|
59
|
+
if (IS_DEV) console.log(`[BlaC] Plugin "${plugin.name}" skipped (environment mismatch)`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (this.plugins.has(plugin.name)) throw new Error(`Plugin "${plugin.name}" is already installed`);
|
|
63
|
+
const installContext = this.buildContext(void 0);
|
|
64
|
+
this.plugins.set(plugin.name, {
|
|
65
|
+
plugin,
|
|
66
|
+
config: effectiveConfig,
|
|
67
|
+
installContext
|
|
68
|
+
});
|
|
69
|
+
this.backfillPlugin(this.plugins.get(plugin.name));
|
|
70
|
+
if (plugin.onInstall) try {
|
|
71
|
+
plugin.onInstall(installContext);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error(`[BlaC] Error installing plugin "${plugin.name}":`, error);
|
|
74
|
+
this.plugins.delete(plugin.name);
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
if (IS_DEV) console.log(`[BlaC] Plugin "${plugin.name}" v${plugin.version} installed`);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Uninstall a plugin by name
|
|
81
|
+
* @param pluginName - The name of the plugin to uninstall
|
|
82
|
+
* @throws Error if plugin is not installed
|
|
83
|
+
*/
|
|
84
|
+
uninstall(pluginName) {
|
|
85
|
+
const installed = this.plugins.get(pluginName);
|
|
86
|
+
if (!installed) throw new Error(`Plugin "${pluginName}" is not installed`);
|
|
87
|
+
if (installed.plugin.onUninstall) try {
|
|
88
|
+
installed.plugin.onUninstall();
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error(`[BlaC] Error uninstalling plugin "${pluginName}":`, error);
|
|
91
|
+
}
|
|
92
|
+
this.plugins.delete(pluginName);
|
|
93
|
+
if (IS_DEV) console.log(`[BlaC] Plugin "${pluginName}" uninstalled`);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Get an installed plugin by name
|
|
97
|
+
* @param pluginName - The name of the plugin to retrieve
|
|
98
|
+
* @returns The plugin instance or undefined if not found
|
|
99
|
+
*/
|
|
100
|
+
getPlugin(pluginName) {
|
|
101
|
+
return this.plugins.get(pluginName)?.plugin;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get all installed plugins
|
|
105
|
+
* @returns Array of all installed plugins
|
|
106
|
+
*/
|
|
107
|
+
getAllPlugins() {
|
|
108
|
+
return Array.from(this.plugins.values()).map((p) => p.plugin);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Check if a plugin is installed
|
|
112
|
+
* @param pluginName - The name of the plugin to check
|
|
113
|
+
* @returns true if the plugin is installed
|
|
114
|
+
*/
|
|
115
|
+
hasPlugin(pluginName) {
|
|
116
|
+
return this.plugins.has(pluginName);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Uninstall all plugins
|
|
120
|
+
*/
|
|
121
|
+
clear() {
|
|
122
|
+
for (const name of this.plugins.keys()) this.uninstall(name);
|
|
123
|
+
}
|
|
124
|
+
destroy() {
|
|
125
|
+
this.clear();
|
|
126
|
+
for (const unsub of this.lifecycleUnsubscribers) unsub();
|
|
127
|
+
this.lifecycleUnsubscribers = [];
|
|
128
|
+
for (const bridge of this.containerBridges.values()) bridge.unsub();
|
|
129
|
+
this.containerBridges.clear();
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Wire registry lifecycle events into plugin dispatch.
|
|
133
|
+
*
|
|
134
|
+
* `onStateChange` is NOT wired through `registry.on('stateChanged', …)` —
|
|
135
|
+
* that event lacks the `PathSet` payload. Instead, on each `created` we
|
|
136
|
+
* subscribe to the container's channel directly so we get
|
|
137
|
+
* `(paths)` and capture `(prev, next)` via the per-container snapshot.
|
|
138
|
+
*/
|
|
139
|
+
setupLifecycleHooks() {
|
|
140
|
+
this.lifecycleUnsubscribers = [
|
|
141
|
+
this.registry.on("created", (instance) => {
|
|
142
|
+
this.attachStateBridge(instance);
|
|
143
|
+
this.notifyPlugins("onCreated", instance);
|
|
144
|
+
}),
|
|
145
|
+
this.registry.on("disposed", (instance) => {
|
|
146
|
+
this.notifyPlugins("onDestroyed", instance);
|
|
147
|
+
this.detachStateBridge(instance);
|
|
148
|
+
}),
|
|
149
|
+
this.registry.on("refAcquired", (instance, refId) => {
|
|
150
|
+
this.notifyPlugins("onRefAcquired", instance, refId);
|
|
151
|
+
}),
|
|
152
|
+
this.registry.on("refReleased", (instance, refId) => {
|
|
153
|
+
this.notifyPlugins("onRefReleased", instance, refId);
|
|
154
|
+
}),
|
|
155
|
+
this.registry.on("depsChanged", (instance, previousDeps, currentDeps) => {
|
|
156
|
+
this.notifyPlugins("onDepsChanged", instance, previousDeps, currentDeps);
|
|
157
|
+
}),
|
|
158
|
+
this.registry.on("hydrationChanged", (instance, status, previousStatus) => {
|
|
159
|
+
this.notifyPlugins("onHydrationChange", instance, status, previousStatus);
|
|
160
|
+
}),
|
|
161
|
+
this.registry.on("activated", (instance, signal) => {
|
|
162
|
+
this.notifyPlugins("onActivate", instance, signal);
|
|
163
|
+
}),
|
|
164
|
+
this.registry.on("deactivated", (instance) => {
|
|
165
|
+
this.notifyPlugins("onDeactivate", instance);
|
|
166
|
+
})
|
|
167
|
+
];
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Subscribe to the container's channel with `ALL_PATHS` interest, so we
|
|
171
|
+
* fire on every flush. Capture `prev` from the snapshot taken on the
|
|
172
|
+
* previous flush (or at create-time for the first flush) and pass the
|
|
173
|
+
* channel's `paths` argument straight through to plugins.
|
|
174
|
+
*
|
|
175
|
+
* Per-container bookkeeping is stored in `containerBridges`, a strong `Map`
|
|
176
|
+
* keyed by the container. Entries are removed explicitly — on the
|
|
177
|
+
* container's `disposed` event, or for all of them in `destroy()`.
|
|
178
|
+
*/
|
|
179
|
+
attachStateBridge(container) {
|
|
180
|
+
if (this.containerBridges.has(container)) return;
|
|
181
|
+
const bridge = {
|
|
182
|
+
unsub: () => {},
|
|
183
|
+
prevState: container.state
|
|
184
|
+
};
|
|
185
|
+
this.containerBridges.set(container, bridge);
|
|
186
|
+
bridge.unsub = container.channel.subscribe(() => ALL_PATHS, (paths) => this.dispatchStateChange(container, paths));
|
|
187
|
+
}
|
|
188
|
+
detachStateBridge(container) {
|
|
189
|
+
const bridge = this.containerBridges.get(container);
|
|
190
|
+
if (!bridge) return;
|
|
191
|
+
bridge.unsub();
|
|
192
|
+
this.containerBridges.delete(container);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Backfill a newly-installed plugin with the app's existing instances.
|
|
196
|
+
*
|
|
197
|
+
* Iterates every registered `Type` and its live instances (`getAll`
|
|
198
|
+
* already skips disposed), attaching the state bridge (idempotent — see
|
|
199
|
+
* `attachStateBridge`) and, if the plugin implements `onCreated`, invoking
|
|
200
|
+
* it directly for each instance. Scoped to this single plugin only — the
|
|
201
|
+
* broadcast `notifyPlugins` would re-notify *every* plugin of *every*
|
|
202
|
+
* instance, which is wrong here.
|
|
203
|
+
*/
|
|
204
|
+
backfillPlugin(installed) {
|
|
205
|
+
const { plugin } = installed;
|
|
206
|
+
for (const Type of this.registry.getTypes()) for (const instance of this.registry.getAll(Type)) {
|
|
207
|
+
const container = instance;
|
|
208
|
+
this.attachStateBridge(container);
|
|
209
|
+
if (!plugin.onCreated) continue;
|
|
210
|
+
try {
|
|
211
|
+
plugin.onCreated(this.buildContext(container));
|
|
212
|
+
} catch (error) {
|
|
213
|
+
console.error(`[BlaC] Error in plugin "${plugin.name}" onCreated (backfill):`, error);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Channel-flush callback. Snapshots `next`, hands `(prev, next, paths)`
|
|
219
|
+
* to every enabled plugin's `onStateChange`, then updates `prevState`
|
|
220
|
+
* for the next flush.
|
|
221
|
+
*
|
|
222
|
+
* Note: `prev` is captured once and reused across plugins — every
|
|
223
|
+
* plugin sees the same `prev`/`next`/`paths` regardless of dispatch
|
|
224
|
+
* order, matching the "snapshot prev once per flush" invariant.
|
|
225
|
+
*/
|
|
226
|
+
dispatchStateChange(container, paths) {
|
|
227
|
+
const bridge = this.containerBridges.get(container);
|
|
228
|
+
if (!bridge) return;
|
|
229
|
+
const prev = bridge.prevState;
|
|
230
|
+
const next = container.state;
|
|
231
|
+
bridge.prevState = next;
|
|
232
|
+
let ctx;
|
|
233
|
+
for (const { plugin, config } of this.plugins.values()) {
|
|
234
|
+
if (!config.enabled) continue;
|
|
235
|
+
const hook = plugin.onStateChange;
|
|
236
|
+
if (typeof hook !== "function") continue;
|
|
237
|
+
try {
|
|
238
|
+
ctx ??= this.buildContext(container);
|
|
239
|
+
hook.call(plugin, ctx, prev, next, paths);
|
|
240
|
+
} catch (error) {
|
|
241
|
+
console.error(`[BlaC] Error in plugin "${plugin.name}" onStateChange:`, error);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Notify all plugins of a lifecycle event.
|
|
247
|
+
*
|
|
248
|
+
* Builds a fresh `PluginContext` per dispatch (instance becomes
|
|
249
|
+
* `ctx.container`), so plugins can reach the focal bloc through
|
|
250
|
+
* `ctx.container`. The context is built lazily on the first matching
|
|
251
|
+
* hook and reused for every other plugin in this dispatch — zero
|
|
252
|
+
* builds if no enabled plugin implements `hookName`.
|
|
253
|
+
*/
|
|
254
|
+
notifyPlugins(hookName, instance, ...extraArgs) {
|
|
255
|
+
let ctx;
|
|
256
|
+
for (const { plugin, config } of this.plugins.values()) {
|
|
257
|
+
if (!config.enabled) continue;
|
|
258
|
+
const hook = plugin[hookName];
|
|
259
|
+
if (typeof hook !== "function") continue;
|
|
260
|
+
try {
|
|
261
|
+
ctx ??= this.buildContext(instance);
|
|
262
|
+
hook.call(plugin, ctx, ...extraArgs);
|
|
263
|
+
} catch (error) {
|
|
264
|
+
console.error(`[BlaC] Error in plugin "${plugin.name}" ${hookName}:`, error);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Get the `PluginContext` for a focal container, building it once and
|
|
270
|
+
* reusing it thereafter. Eviction is handled by the `WeakMap`: the context
|
|
271
|
+
* dies with the container. `undefined` (install-time) is never cached.
|
|
272
|
+
*/
|
|
273
|
+
buildContext(container) {
|
|
274
|
+
if (container !== void 0) {
|
|
275
|
+
const cached = this.contextCache.get(container);
|
|
276
|
+
if (cached) return cached;
|
|
277
|
+
}
|
|
278
|
+
const context = this.createContext(container);
|
|
279
|
+
if (container !== void 0) this.contextCache.set(container, context);
|
|
280
|
+
return context;
|
|
281
|
+
}
|
|
282
|
+
createContext(container) {
|
|
283
|
+
const registry = this.registry;
|
|
284
|
+
return {
|
|
285
|
+
container,
|
|
286
|
+
getInstanceMetadata: (instance) => {
|
|
287
|
+
return {
|
|
288
|
+
id: instance.$blac.id,
|
|
289
|
+
className: getBlacName(instance.constructor),
|
|
290
|
+
isDisposed: instance.$blac.disposed,
|
|
291
|
+
name: instance.$blac.name,
|
|
292
|
+
createdAt: instance.$blac.createdAt,
|
|
293
|
+
state: instance.state,
|
|
294
|
+
hydrationStatus: instance.$blac.hydration.status,
|
|
295
|
+
isHydrated: instance.$blac.hydration.isHydrated,
|
|
296
|
+
hydrationError: instance.$blac.hydration.error,
|
|
297
|
+
changedWhileHydrating: instance.$blac.hydration.changedWhileHydrating,
|
|
298
|
+
args: instance.args
|
|
299
|
+
};
|
|
300
|
+
},
|
|
301
|
+
getState: (instance) => {
|
|
302
|
+
return instance.state;
|
|
303
|
+
},
|
|
304
|
+
getHydrationStatus: (instance) => {
|
|
305
|
+
return instance.$blac.hydration.status;
|
|
306
|
+
},
|
|
307
|
+
startHydration: (instance) => {
|
|
308
|
+
instance.$blac.hydration.begin();
|
|
309
|
+
},
|
|
310
|
+
applyHydratedState: (instance, state) => {
|
|
311
|
+
return instance.$blac.hydration.apply(state);
|
|
312
|
+
},
|
|
313
|
+
finishHydration: (instance) => {
|
|
314
|
+
instance.$blac.hydration.finish();
|
|
315
|
+
},
|
|
316
|
+
failHydration: (instance, error) => {
|
|
317
|
+
instance.$blac.hydration.fail(error);
|
|
318
|
+
},
|
|
319
|
+
waitForHydration: (instance) => {
|
|
320
|
+
return instance.$blac.hydration.wait();
|
|
321
|
+
},
|
|
322
|
+
queryInstances: (typeClass) => {
|
|
323
|
+
return registry.getAll(typeClass);
|
|
324
|
+
},
|
|
325
|
+
getAllTypes: () => {
|
|
326
|
+
return registry.getTypes();
|
|
327
|
+
},
|
|
328
|
+
getStats: () => {
|
|
329
|
+
return registry.getStats();
|
|
330
|
+
},
|
|
331
|
+
getRefIds: (instanceId) => {
|
|
332
|
+
return registry.getRefIdsById(instanceId);
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Check if plugin should be enabled based on environment
|
|
338
|
+
*/
|
|
339
|
+
shouldEnablePlugin(config) {
|
|
340
|
+
if (!config.enabled) return false;
|
|
341
|
+
if (config.environment === "all") return true;
|
|
342
|
+
return this.getCurrentEnvironment() === config.environment;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Get current environment
|
|
346
|
+
*/
|
|
347
|
+
getCurrentEnvironment() {
|
|
348
|
+
if (typeof process !== "undefined") {
|
|
349
|
+
if (process.env.NODE_ENV === "test") return "test";
|
|
350
|
+
if (process.env.NODE_ENV === "production") return "production";
|
|
351
|
+
return "development";
|
|
352
|
+
}
|
|
353
|
+
return "development";
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
/**
|
|
357
|
+
* Create a plugin manager instance
|
|
358
|
+
* @param registry - The StateContainerRegistry to monitor for lifecycle events
|
|
359
|
+
* @returns A new PluginManager instance
|
|
360
|
+
*/
|
|
361
|
+
function createPluginManager(registry) {
|
|
362
|
+
return new PluginManager(registry);
|
|
363
|
+
}
|
|
364
|
+
//#endregion
|
|
365
|
+
//#region src/plugins.ts
|
|
366
|
+
/**
|
|
367
|
+
* Plugins Subpath Export
|
|
368
|
+
*
|
|
369
|
+
* Plugin system utilities for extending BlaC functionality.
|
|
370
|
+
* Import from `@blac/core/plugins`
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
* ```typescript
|
|
374
|
+
* import { PluginManager, getPluginManager, type BlacPlugin } from '@blac/core/plugins';
|
|
375
|
+
*
|
|
376
|
+
* const myPlugin: BlacPlugin = {
|
|
377
|
+
* name: 'my-plugin',
|
|
378
|
+
* onCreated(context) {
|
|
379
|
+
* console.log('Instance created:', context.container);
|
|
380
|
+
* },
|
|
381
|
+
* };
|
|
382
|
+
*
|
|
383
|
+
* getPluginManager().register(myPlugin);
|
|
384
|
+
* ```
|
|
385
|
+
*
|
|
386
|
+
* @packageDocumentation
|
|
387
|
+
*/
|
|
388
|
+
/**
|
|
389
|
+
* Global plugin manager (initialized lazily)
|
|
390
|
+
*/
|
|
391
|
+
let _globalPluginManager = null;
|
|
392
|
+
/**
|
|
393
|
+
* Get the global plugin manager
|
|
394
|
+
*/
|
|
395
|
+
function getPluginManager() {
|
|
396
|
+
if (!_globalPluginManager) _globalPluginManager = createPluginManager(globalRegistry);
|
|
397
|
+
return _globalPluginManager;
|
|
398
|
+
}
|
|
399
|
+
//#endregion
|
|
2
400
|
export { PluginManager, getPluginManager };
|
|
401
|
+
|
|
402
|
+
//# sourceMappingURL=plugins.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugins.js","names":[],"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` and at `destroy()`. Holds the rolling `prevState`\n * snapshot the manager hands plugins on each flush.\n *\n * Strong, not a WeakMap, because `destroy()` has to enumerate the live\n * bridges to unsubscribe them. Safe: entries are removed on `disposed`, and\n * the owning registry holds its own strong reference over that same window.\n */\n private containerBridges = new Map<\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 // Without this each container keeps its ALL_PATHS bridge subscriber — and\n // the single-consumer-skip penalty that comes with it — forever.\n for (const bridge of this.containerBridges.values()) {\n bridge.unsub();\n }\n this.containerBridges.clear();\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 `containerBridges`, a strong `Map`\n * keyed by the container. Entries are removed explicitly — on the\n * container's `disposed` event, or for all of them in `destroy()`.\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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"}
|
|
@@ -5,8 +5,8 @@ import type { ExtractArgs, StateContainerConstructor } from '../types/utilities'
|
|
|
5
5
|
* a structural hash of `args`, or the default sentinel when there are none).
|
|
6
6
|
*
|
|
7
7
|
* @param BlocClass - The StateContainer class constructor
|
|
8
|
-
* @param opts
|
|
9
|
-
*
|
|
8
|
+
* @param opts - `args` derives the instance key; `refId` names the reference
|
|
9
|
+
* for debugging and is auto-generated when omitted.
|
|
10
10
|
*/
|
|
11
11
|
export declare function acquire<T extends StateContainerConstructor>(BlocClass: T, opts?: {
|
|
12
12
|
args?: ExtractArgs<T>;
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { StateContainerRegistry } from '../core/StateContainerRegistry';
|
|
2
|
+
/**
|
|
3
|
+
* @public
|
|
4
|
+
*/
|
|
2
5
|
export declare function getRegistry(): StateContainerRegistry;
|
|
6
|
+
/**
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
3
9
|
export declare function setRegistry(registry: StateContainerRegistry): void;
|
|
10
|
+
/**
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
4
13
|
export declare function getStats(): {
|
|
5
14
|
registeredTypes: number;
|
|
6
15
|
totalInstances: number;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { StateContainerConstructor } from '../types/utilities';
|
|
2
2
|
export declare function clear<T extends StateContainerConstructor>(BlocClass: T): void;
|
|
3
3
|
export declare function clearAll(): void;
|
|
4
|
+
/**
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
4
7
|
export declare function register<T extends StateContainerConstructor>(BlocClass: T): void;
|
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
import type { ExtractArgs, StateContainerConstructor, InstanceReadonlyState } from '../types/utilities';
|
|
2
|
+
/**
|
|
3
|
+
* @public
|
|
4
|
+
*/
|
|
2
5
|
export declare function hasInstance<T extends StateContainerConstructor>(BlocClass: T, opts?: {
|
|
3
6
|
args?: ExtractArgs<T>;
|
|
4
7
|
}): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
5
11
|
export declare function getRefCount<T extends StateContainerConstructor>(BlocClass: T, opts?: {
|
|
6
12
|
args?: ExtractArgs<T>;
|
|
7
13
|
}): number;
|
|
14
|
+
/**
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
8
17
|
export declare function getRefIds<T extends StateContainerConstructor>(BlocClass: T, opts?: {
|
|
9
18
|
args?: ExtractArgs<T>;
|
|
10
19
|
}): string[];
|
|
20
|
+
/**
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
11
23
|
export declare function getAll<T extends StateContainerConstructor>(BlocClass: T): InstanceReadonlyState<T>[];
|
|
24
|
+
/**
|
|
25
|
+
* @public
|
|
26
|
+
*/
|
|
12
27
|
export declare function forEach<T extends StateContainerConstructor>(BlocClass: T, callback: (instance: InstanceReadonlyState<T>) => void): void;
|
|
@@ -3,9 +3,9 @@ import type { ExtractArgs, StateContainerConstructor } from '../types/utilities'
|
|
|
3
3
|
* Release a reference to an instance. Instance identity is derived purely from
|
|
4
4
|
* `args` (must match the `args` it was acquired with).
|
|
5
5
|
*
|
|
6
|
-
* @param opts
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* @param opts - `args` derives the instance key (must match acquire);
|
|
7
|
+
* `refId` picks the ref to drop, otherwise one arbitrary ref goes;
|
|
8
|
+
* `forceDispose` disposes immediately regardless of remaining refs.
|
|
9
9
|
*/
|
|
10
10
|
export declare function release<T extends StateContainerConstructor>(BlocClass: T, opts?: {
|
|
11
11
|
args?: ExtractArgs<T>;
|
package/dist/testing.cjs
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_StateContainerRegistry = require("./StateContainerRegistry-
|
|
3
|
-
const require_ensure = require("./ensure-
|
|
4
|
-
const require_config = require("./config-DWUq7zEQ.cjs");
|
|
5
|
-
const require_acquire = require("./acquire-BJ3xUEmh.cjs");
|
|
2
|
+
const require_StateContainerRegistry = require("./StateContainerRegistry-CBLeHObO.cjs");
|
|
3
|
+
const require_ensure = require("./ensure-D3w6YGp9.cjs");
|
|
6
4
|
//#region src/testing.ts
|
|
7
5
|
/** Synthetic owner id used by test helpers for pre-wired deps. */
|
|
8
6
|
const TESTING_DEPS_OWNER = "testing-deps";
|
|
@@ -10,38 +8,38 @@ function createTestRegistry() {
|
|
|
10
8
|
return new require_StateContainerRegistry.StateContainerRegistry();
|
|
11
9
|
}
|
|
12
10
|
function withTestRegistry(fn) {
|
|
13
|
-
const previous =
|
|
11
|
+
const previous = require_ensure.getRegistry();
|
|
14
12
|
const testRegistry = createTestRegistry();
|
|
15
|
-
|
|
13
|
+
require_ensure.setRegistry(testRegistry);
|
|
16
14
|
try {
|
|
17
15
|
const result = fn(testRegistry);
|
|
18
16
|
if (result instanceof Promise) return result.then((value) => {
|
|
19
|
-
|
|
17
|
+
require_ensure.setRegistry(previous);
|
|
20
18
|
return value;
|
|
21
19
|
}, (error) => {
|
|
22
|
-
|
|
20
|
+
require_ensure.setRegistry(previous);
|
|
23
21
|
throw error;
|
|
24
22
|
});
|
|
25
|
-
|
|
23
|
+
require_ensure.setRegistry(previous);
|
|
26
24
|
return result;
|
|
27
25
|
} catch (error) {
|
|
28
|
-
|
|
26
|
+
require_ensure.setRegistry(previous);
|
|
29
27
|
throw error;
|
|
30
28
|
}
|
|
31
29
|
}
|
|
32
30
|
function blacTestSetup() {
|
|
33
31
|
let savedRegistry;
|
|
34
32
|
beforeEach(() => {
|
|
35
|
-
savedRegistry =
|
|
36
|
-
|
|
33
|
+
savedRegistry = require_ensure.getRegistry();
|
|
34
|
+
require_ensure.setRegistry(new require_StateContainerRegistry.StateContainerRegistry());
|
|
37
35
|
});
|
|
38
36
|
afterEach(() => {
|
|
39
|
-
|
|
37
|
+
require_ensure.setRegistry(savedRegistry);
|
|
40
38
|
});
|
|
41
39
|
}
|
|
42
40
|
function registerOverride(BlocClass, instance, args) {
|
|
43
|
-
const registry =
|
|
44
|
-
const key =
|
|
41
|
+
const registry = require_ensure.getRegistry();
|
|
42
|
+
const key = require_ensure.resolveInstanceKey(BlocClass, args);
|
|
45
43
|
registry.insertInstance(BlocClass, key, instance, new Map([["testing-override", 1]]));
|
|
46
44
|
}
|
|
47
45
|
function overrideEnsure(BlocClass, instance, fn, args) {
|
package/dist/testing.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.cjs","names":["StateContainerRegistry","getRegistry","resolveInstanceKey","INIT_CONFIG","Cubit","APPLY_DEPS","ensure"],"sources":["../src/testing.ts"],"sourcesContent":["import { Cubit } from './core/Cubit';\nimport { StateContainerRegistry } from './core/StateContainerRegistry';\nimport { APPLY_DEPS, INIT_CONFIG } from './core/symbols';\nimport { ensure, getRegistry, setRegistry } from './registry';\nimport { resolveInstanceKey } from './registry/acquire';\nimport type {\n ExtractArgs,\n ExtractDeps,\n ExtractState,\n StateContainerConstructor,\n} from './types/utilities';\n\n/** Synthetic owner id used by test helpers for pre-wired deps. */\nconst TESTING_DEPS_OWNER = 'testing-deps';\n\ndeclare const beforeEach: (fn: () => void) => void;\ndeclare const afterEach: (fn: () => void) => void;\n\n// --- createTestRegistry + withTestRegistry ---\n\nexport function createTestRegistry(): StateContainerRegistry {\n return new StateContainerRegistry();\n}\n\nexport function withTestRegistry<T>(\n fn: (registry: StateContainerRegistry) => T,\n): T {\n const previous = getRegistry();\n const testRegistry = createTestRegistry();\n setRegistry(testRegistry);\n try {\n const result = fn(testRegistry);\n if (result instanceof Promise) {\n return result.then(\n (value) => {\n setRegistry(previous);\n return value;\n },\n (error) => {\n setRegistry(previous);\n throw error;\n },\n ) as T;\n }\n setRegistry(previous);\n return result;\n } catch (error) {\n setRegistry(previous);\n throw error;\n }\n}\n\n// --- blacTestSetup ---\n\nexport function blacTestSetup(): void {\n let savedRegistry: StateContainerRegistry;\n beforeEach(() => {\n savedRegistry = getRegistry();\n setRegistry(new StateContainerRegistry());\n });\n afterEach(() => {\n setRegistry(savedRegistry);\n });\n}\n\n// --- registerOverride + overrideEnsure ---\n\nexport function registerOverride<T extends StateContainerConstructor>(\n BlocClass: T,\n instance: InstanceType<T>,\n args?: ExtractArgs<T>,\n): void {\n const registry = getRegistry();\n const key = resolveInstanceKey(BlocClass, args);\n registry.insertInstance(\n BlocClass,\n key,\n instance,\n new Map([['testing-override', 1]]),\n );\n}\n\nexport function overrideEnsure<T extends StateContainerConstructor, R>(\n BlocClass: T,\n instance: InstanceType<T>,\n fn: () => R,\n args?: ExtractArgs<T>,\n): R {\n return withTestRegistry(() => {\n registerOverride(BlocClass, instance, args);\n return fn();\n });\n}\n\n// --- createCubitStub ---\n\ntype MethodKeys<T> = {\n [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never;\n}[keyof T];\n\nexport interface CubitStubOptions<T extends StateContainerConstructor> {\n state?: ExtractState<T> extends Record<string, any>\n ? Partial<ExtractState<T>>\n : ExtractState<T>;\n methods?: Partial<\n Record<MethodKeys<InstanceType<T>>, (...args: any[]) => any>\n >;\n /**\n * Args to pass to init(). If the bloc's Args type is not void, supplying\n * args here causes initConfig({ args }) to be called so init() runs.\n */\n args?: ExtractArgs<T> extends void ? never : ExtractArgs<T>;\n /**\n * Deps slice to pre-wire via the core [APPLY_DEPS] path (synthetic owner\n * \"testing-deps\"), so onDepsChanged fires during tests.\n */\n deps?: Partial<ExtractDeps<T>>;\n}\n\nexport function createCubitStub<T extends StateContainerConstructor>(\n BlocClass: T,\n options?: CubitStubOptions<T>,\n): InstanceType<T> {\n const instance = new BlocClass() as InstanceType<T>;\n\n // Run init() if args are supplied — goes through the same initConfig path\n // that the registry uses, so lifecycle hooks fire correctly.\n if (options?.args != null) {\n instance[INIT_CONFIG]({ args: options.args });\n }\n\n if (options?.state != null) {\n if (instance instanceof Cubit) {\n const currentState = instance.state;\n if (\n typeof currentState === 'object' &&\n currentState !== null &&\n typeof options.state === 'object' &&\n options.state !== null\n ) {\n instance.patch(options.state as any);\n } else {\n instance.emit(options.state as any);\n }\n }\n }\n if (options?.methods) {\n for (const [key, impl] of Object.entries(options.methods)) {\n if (typeof impl === 'function') {\n (instance as any)[key] = impl;\n }\n }\n }\n\n // Pre-wire deps via the core merge path so onDepsChanged fires in tests.\n if (options?.deps != null) {\n (instance as any)[APPLY_DEPS](TESTING_DEPS_OWNER, options.deps);\n }\n\n return instance;\n}\n\n// --- withBlocState ---\n\nexport function withBlocState<T extends StateContainerConstructor>(\n BlocClass: T,\n state: ExtractState<T> extends Record<string, any>\n ? Partial<ExtractState<T>>\n : ExtractState<T>,\n args?: ExtractArgs<T>,\n): InstanceType<T> {\n const instance = ensure(BlocClass, { args });\n if (instance instanceof Cubit) {\n const currentState = instance.state;\n if (\n typeof currentState === 'object' &&\n currentState !== null &&\n typeof state === 'object' &&\n state !== null\n ) {\n instance.patch(state as any);\n } else {\n instance.emit(state as any);\n }\n }\n return instance;\n}\n\n// --- withBlocMethod ---\n\nexport function withBlocMethod<T extends StateContainerConstructor>(\n BlocClass: T,\n methodName: keyof InstanceType<T>,\n impl: (...args: any[]) => any,\n args?: ExtractArgs<T>,\n): InstanceType<T> {\n const instance = ensure(BlocClass, { args });\n (instance as any)[methodName] = impl;\n return instance;\n}\n\n// --- flush ---\n\n/**\n * Drain pending microtasks so any channel-flushed effects (channel\n * subscribers, `onSystemEvent('stateChanged')` handlers, plugin hooks) run\n * before the next assertion.\n *\n * The default `MicrotaskScheduler` coalesces emits within a tick; tests\n * that emit and then assert on subscriber side-effects need `await flush()`\n * between the two.\n */\nexport async function flush(): Promise<void> {\n // queueMicrotask wins the race against the channel's own queued flush —\n // we resolve after the channel has drained its current pending flush.\n await Promise.resolve();\n await Promise.resolve();\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"testing.cjs","names":["StateContainerRegistry","getRegistry","resolveInstanceKey","INIT_CONFIG","Cubit","APPLY_DEPS","ensure"],"sources":["../src/testing.ts"],"sourcesContent":["import { Cubit } from './core/Cubit';\nimport { StateContainerRegistry } from './core/StateContainerRegistry';\nimport { APPLY_DEPS, INIT_CONFIG } from './core/symbols';\nimport { ensure, getRegistry, setRegistry } from './registry';\nimport { resolveInstanceKey } from './registry/acquire';\nimport type {\n ExtractArgs,\n ExtractDeps,\n ExtractState,\n StateContainerConstructor,\n} from './types/utilities';\n\n/** Synthetic owner id used by test helpers for pre-wired deps. */\nconst TESTING_DEPS_OWNER = 'testing-deps';\n\ndeclare const beforeEach: (fn: () => void) => void;\ndeclare const afterEach: (fn: () => void) => void;\n\n// --- createTestRegistry + withTestRegistry ---\n\nexport function createTestRegistry(): StateContainerRegistry {\n return new StateContainerRegistry();\n}\n\nexport function withTestRegistry<T>(\n fn: (registry: StateContainerRegistry) => T,\n): T {\n const previous = getRegistry();\n const testRegistry = createTestRegistry();\n setRegistry(testRegistry);\n try {\n const result = fn(testRegistry);\n if (result instanceof Promise) {\n return result.then(\n (value) => {\n setRegistry(previous);\n return value;\n },\n (error) => {\n setRegistry(previous);\n throw error;\n },\n ) as T;\n }\n setRegistry(previous);\n return result;\n } catch (error) {\n setRegistry(previous);\n throw error;\n }\n}\n\n// --- blacTestSetup ---\n\nexport function blacTestSetup(): void {\n let savedRegistry: StateContainerRegistry;\n beforeEach(() => {\n savedRegistry = getRegistry();\n setRegistry(new StateContainerRegistry());\n });\n afterEach(() => {\n setRegistry(savedRegistry);\n });\n}\n\n// --- registerOverride + overrideEnsure ---\n\nexport function registerOverride<T extends StateContainerConstructor>(\n BlocClass: T,\n instance: InstanceType<T>,\n args?: ExtractArgs<T>,\n): void {\n const registry = getRegistry();\n const key = resolveInstanceKey(BlocClass, args);\n registry.insertInstance(\n BlocClass,\n key,\n instance,\n new Map([['testing-override', 1]]),\n );\n}\n\nexport function overrideEnsure<T extends StateContainerConstructor, R>(\n BlocClass: T,\n instance: InstanceType<T>,\n fn: () => R,\n args?: ExtractArgs<T>,\n): R {\n return withTestRegistry(() => {\n registerOverride(BlocClass, instance, args);\n return fn();\n });\n}\n\n// --- createCubitStub ---\n\ntype MethodKeys<T> = {\n [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never;\n}[keyof T];\n\nexport interface CubitStubOptions<T extends StateContainerConstructor> {\n state?: ExtractState<T> extends Record<string, any>\n ? Partial<ExtractState<T>>\n : ExtractState<T>;\n methods?: Partial<\n Record<MethodKeys<InstanceType<T>>, (...args: any[]) => any>\n >;\n /**\n * Args to pass to init(). If the bloc's Args type is not void, supplying\n * args here causes `initConfig({ args })` to be called so `init()` runs.\n */\n args?: ExtractArgs<T> extends void ? never : ExtractArgs<T>;\n /**\n * Deps slice to pre-wire via the core [APPLY_DEPS] path (synthetic owner\n * \"testing-deps\"), so onDepsChanged fires during tests.\n */\n deps?: Partial<ExtractDeps<T>>;\n}\n\nexport function createCubitStub<T extends StateContainerConstructor>(\n BlocClass: T,\n options?: CubitStubOptions<T>,\n): InstanceType<T> {\n const instance = new BlocClass() as InstanceType<T>;\n\n // Run init() if args are supplied — goes through the same initConfig path\n // that the registry uses, so lifecycle hooks fire correctly.\n if (options?.args != null) {\n instance[INIT_CONFIG]({ args: options.args });\n }\n\n if (options?.state != null) {\n if (instance instanceof Cubit) {\n const currentState = instance.state;\n if (\n typeof currentState === 'object' &&\n currentState !== null &&\n typeof options.state === 'object' &&\n options.state !== null\n ) {\n instance.patch(options.state as any);\n } else {\n instance.emit(options.state as any);\n }\n }\n }\n if (options?.methods) {\n for (const [key, impl] of Object.entries(options.methods)) {\n if (typeof impl === 'function') {\n (instance as any)[key] = impl;\n }\n }\n }\n\n // Pre-wire deps via the core merge path so onDepsChanged fires in tests.\n if (options?.deps != null) {\n (instance as any)[APPLY_DEPS](TESTING_DEPS_OWNER, options.deps);\n }\n\n return instance;\n}\n\n// --- withBlocState ---\n\nexport function withBlocState<T extends StateContainerConstructor>(\n BlocClass: T,\n state: ExtractState<T> extends Record<string, any>\n ? Partial<ExtractState<T>>\n : ExtractState<T>,\n args?: ExtractArgs<T>,\n): InstanceType<T> {\n const instance = ensure(BlocClass, { args });\n if (instance instanceof Cubit) {\n const currentState = instance.state;\n if (\n typeof currentState === 'object' &&\n currentState !== null &&\n typeof state === 'object' &&\n state !== null\n ) {\n instance.patch(state as any);\n } else {\n instance.emit(state as any);\n }\n }\n return instance;\n}\n\n// --- withBlocMethod ---\n\nexport function withBlocMethod<T extends StateContainerConstructor>(\n BlocClass: T,\n methodName: keyof InstanceType<T>,\n impl: (...args: any[]) => any,\n args?: ExtractArgs<T>,\n): InstanceType<T> {\n const instance = ensure(BlocClass, { args });\n (instance as any)[methodName] = impl;\n return instance;\n}\n\n// --- flush ---\n\n/**\n * Drain pending microtasks so any channel-flushed effects (channel\n * subscribers, `onSystemEvent('stateChanged')` handlers, plugin hooks) run\n * before the next assertion.\n *\n * The default `MicrotaskScheduler` coalesces emits within a tick; tests\n * that emit and then assert on subscriber side-effects need `await flush()`\n * between the two.\n */\nexport async function flush(): Promise<void> {\n // queueMicrotask wins the race against the channel's own queued flush —\n // we resolve after the channel has drained its current pending flush.\n await Promise.resolve();\n await Promise.resolve();\n}\n"],"mappings":";;;;;AAaA,MAAM,qBAAqB;AAO3B,SAAgB,qBAA6C;CAC3D,OAAO,IAAIA,+BAAAA,uBAAuB;AACpC;AAEA,SAAgB,iBACd,IACG;CACH,MAAM,WAAWC,eAAAA,YAAY;CAC7B,MAAM,eAAe,mBAAmB;CACxC,eAAA,YAAY,YAAY;CACxB,IAAI;EACF,MAAM,SAAS,GAAG,YAAY;EAC9B,IAAI,kBAAkB,SACpB,OAAO,OAAO,MACX,UAAU;GACT,eAAA,YAAY,QAAQ;GACpB,OAAO;EACT,IACC,UAAU;GACT,eAAA,YAAY,QAAQ;GACpB,MAAM;EACR,CACF;EAEF,eAAA,YAAY,QAAQ;EACpB,OAAO;CACT,SAAS,OAAO;EACd,eAAA,YAAY,QAAQ;EACpB,MAAM;CACR;AACF;AAIA,SAAgB,gBAAsB;CACpC,IAAI;CACJ,iBAAiB;EACf,gBAAgBA,eAAAA,YAAY;EAC5B,eAAA,YAAY,IAAID,+BAAAA,uBAAuB,CAAC;CAC1C,CAAC;CACD,gBAAgB;EACd,eAAA,YAAY,aAAa;CAC3B,CAAC;AACH;AAIA,SAAgB,iBACd,WACA,UACA,MACM;CACN,MAAM,WAAWC,eAAAA,YAAY;CAC7B,MAAM,MAAMC,eAAAA,mBAAmB,WAAW,IAAI;CAC9C,SAAS,eACP,WACA,KACA,UACA,IAAI,IAAI,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,CACnC;AACF;AAEA,SAAgB,eACd,WACA,UACA,IACA,MACG;CACH,OAAO,uBAAuB;EAC5B,iBAAiB,WAAW,UAAU,IAAI;EAC1C,OAAO,GAAG;CACZ,CAAC;AACH;AA2BA,SAAgB,gBACd,WACA,SACiB;CACjB,MAAM,WAAW,IAAI,UAAU;CAI/B,IAAI,SAAS,QAAQ,MACnB,SAASC,+BAAAA,aAAa,EAAE,MAAM,QAAQ,KAAK,CAAC;CAG9C,IAAI,SAAS,SAAS;MAChB,oBAAoBC,eAAAA,OAAO;GAC7B,MAAM,eAAe,SAAS;GAC9B,IACE,OAAO,iBAAiB,YACxB,iBAAiB,QACjB,OAAO,QAAQ,UAAU,YACzB,QAAQ,UAAU,MAElB,SAAS,MAAM,QAAQ,KAAY;QAEnC,SAAS,KAAK,QAAQ,KAAY;EAEtC;;CAEF,IAAI,SAAS;OACN,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,QAAQ,OAAO,GACtD,IAAI,OAAO,SAAS,YAClB,SAAkB,OAAO;CAAA;CAM/B,IAAI,SAAS,QAAQ,MACnB,SAAkBC,+BAAAA,YAAY,oBAAoB,QAAQ,IAAI;CAGhE,OAAO;AACT;AAIA,SAAgB,cACd,WACA,OAGA,MACiB;CACjB,MAAM,WAAWC,eAAAA,OAAO,WAAW,EAAE,KAAK,CAAC;CAC3C,IAAI,oBAAoBF,eAAAA,OAAO;EAC7B,MAAM,eAAe,SAAS;EAC9B,IACE,OAAO,iBAAiB,YACxB,iBAAiB,QACjB,OAAO,UAAU,YACjB,UAAU,MAEV,SAAS,MAAM,KAAY;OAE3B,SAAS,KAAK,KAAY;CAE9B;CACA,OAAO;AACT;AAIA,SAAgB,eACd,WACA,YACA,MACA,MACiB;CACjB,MAAM,WAAWE,eAAAA,OAAO,WAAW,EAAE,KAAK,CAAC;CAC3C,SAAkB,cAAc;CAChC,OAAO;AACT;;;;;;;;;;AAaA,eAAsB,QAAuB;CAG3C,MAAM,QAAQ,QAAQ;CACtB,MAAM,QAAQ,QAAQ;AACxB"}
|
package/dist/testing.d.cts
CHANGED
|
@@ -13,7 +13,7 @@ export interface CubitStubOptions<T extends StateContainerConstructor> {
|
|
|
13
13
|
methods?: Partial<Record<MethodKeys<InstanceType<T>>, (...args: any[]) => any>>;
|
|
14
14
|
/**
|
|
15
15
|
* Args to pass to init(). If the bloc's Args type is not void, supplying
|
|
16
|
-
* args here causes initConfig({ args }) to be called so init() runs.
|
|
16
|
+
* args here causes `initConfig({ args })` to be called so `init()` runs.
|
|
17
17
|
*/
|
|
18
18
|
args?: ExtractArgs<T> extends void ? never : ExtractArgs<T>;
|
|
19
19
|
/**
|
package/dist/testing.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export interface CubitStubOptions<T extends StateContainerConstructor> {
|
|
|
13
13
|
methods?: Partial<Record<MethodKeys<InstanceType<T>>, (...args: any[]) => any>>;
|
|
14
14
|
/**
|
|
15
15
|
* Args to pass to init(). If the bloc's Args type is not void, supplying
|
|
16
|
-
* args here causes initConfig({ args }) to be called so init() runs.
|
|
16
|
+
* args here causes `initConfig({ args })` to be called so `init()` runs.
|
|
17
17
|
*/
|
|
18
18
|
args?: ExtractArgs<T> extends void ? never : ExtractArgs<T>;
|
|
19
19
|
/**
|