@blac/core 2.0.22 → 2.1.1

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 (76) hide show
  1. package/dist/StateContainer-BUyuxKb3.cjs +667 -0
  2. package/dist/StateContainer-BUyuxKb3.cjs.map +1 -0
  3. package/dist/StateContainer-B_7UHc3I.js +632 -0
  4. package/dist/StateContainer-B_7UHc3I.js.map +1 -0
  5. package/dist/StateContainer-Bh2ipNYj.js +632 -0
  6. package/dist/StateContainer-Bh2ipNYj.js.map +1 -0
  7. package/dist/StateContainer-ClzwKnQZ.cjs +667 -0
  8. package/dist/StateContainer-ClzwKnQZ.cjs.map +1 -0
  9. package/dist/StateContainer-DiQATvZh.js +632 -0
  10. package/dist/StateContainer-DiQATvZh.js.map +1 -0
  11. package/dist/StateContainer-j16zciex.cjs +667 -0
  12. package/dist/StateContainer-j16zciex.cjs.map +1 -0
  13. package/dist/StateContainerRegistry-BX-DlHXO.js +747 -0
  14. package/dist/StateContainerRegistry-BX-DlHXO.js.map +1 -0
  15. package/dist/StateContainerRegistry-Bf5VuVjm.js +747 -0
  16. package/dist/StateContainerRegistry-Bf5VuVjm.js.map +1 -0
  17. package/dist/StateContainerRegistry-BvSRfhKv.cjs +866 -0
  18. package/dist/StateContainerRegistry-BvSRfhKv.cjs.map +1 -0
  19. package/dist/StateContainerRegistry-C8jxrcqk.cjs +866 -0
  20. package/dist/StateContainerRegistry-C8jxrcqk.cjs.map +1 -0
  21. package/dist/StateContainerRegistry-DjQQtlCW.cjs +870 -0
  22. package/dist/StateContainerRegistry-DjQQtlCW.cjs.map +1 -0
  23. package/dist/StateContainerRegistry-gQbEutkN.js +751 -0
  24. package/dist/StateContainerRegistry-gQbEutkN.js.map +1 -0
  25. package/dist/config.d.cts +10 -6
  26. package/dist/config.d.ts +10 -6
  27. package/dist/constants.d.cts +12 -17
  28. package/dist/constants.d.ts +12 -17
  29. package/dist/core/StateContainer.d.ts +16 -34
  30. package/dist/core/StateContainerRegistry.d.ts +57 -174
  31. package/dist/core/symbols.d.ts +7 -4
  32. package/dist/decorators/blac.d.ts +5 -2
  33. package/dist/ensure-Bi_yXk6f.js +46 -0
  34. package/dist/ensure-Bi_yXk6f.js.map +1 -0
  35. package/dist/ensure-Bx41d-il.cjs +63 -0
  36. package/dist/ensure-Bx41d-il.cjs.map +1 -0
  37. package/dist/ensure-C7UQ_mKM.js +46 -0
  38. package/dist/ensure-C7UQ_mKM.js.map +1 -0
  39. package/dist/ensure-D1D4WjYf.cjs +63 -0
  40. package/dist/ensure-D1D4WjYf.cjs.map +1 -0
  41. package/dist/ensure-DBnuRJ0R.cjs +63 -0
  42. package/dist/ensure-DBnuRJ0R.cjs.map +1 -0
  43. package/dist/ensure-NTB9nnnj.js +46 -0
  44. package/dist/ensure-NTB9nnnj.js.map +1 -0
  45. package/dist/index.cjs +70 -49
  46. package/dist/index.cjs.map +1 -1
  47. package/dist/index.d.cts +2 -7
  48. package/dist/index.d.ts +2 -7
  49. package/dist/index.js +53 -26
  50. package/dist/index.js.map +1 -1
  51. package/dist/internal.cjs +10 -0
  52. package/dist/internal.d.cts +8 -0
  53. package/dist/internal.d.ts +8 -0
  54. package/dist/internal.js +3 -0
  55. package/dist/plugin/BlacPlugin.d.ts +8 -24
  56. package/dist/plugin/PluginManager.d.ts +41 -64
  57. package/dist/plugins.cjs +67 -87
  58. package/dist/plugins.cjs.map +1 -1
  59. package/dist/plugins.d.cts +7 -6
  60. package/dist/plugins.d.ts +7 -6
  61. package/dist/plugins.js +67 -87
  62. package/dist/plugins.js.map +1 -1
  63. package/dist/registry/config.d.ts +3 -0
  64. package/dist/testing.cjs +37 -35
  65. package/dist/testing.cjs.map +1 -1
  66. package/dist/testing.d.cts +5 -11
  67. package/dist/testing.d.ts +5 -11
  68. package/dist/testing.js +28 -26
  69. package/dist/testing.js.map +1 -1
  70. package/dist/types/branded.d.ts +3 -10
  71. package/dist/types/utilities.d.ts +0 -18
  72. package/dist/utils/idGenerator.d.ts +6 -13
  73. package/dist/utils/static-props.d.ts +9 -35
  74. package/dist/utils/structural-key.d.ts +0 -5
  75. package/dist/watch/watch.d.ts +4 -9
  76. package/package.json +15 -2
package/dist/testing.js CHANGED
@@ -1,5 +1,6 @@
1
- import { i as INIT_CONFIG, r as APPLY_DEPS, t as StateContainerRegistry } from "./StateContainerRegistry-BsH_s0pJ.js";
2
- import { i as Cubit, l as setRegistry, r as resolveInstanceKey, s as getRegistry, t as ensure } from "./ensure-CneMkpbI.js";
1
+ import { a as INSERT_INSTANCE, i as INIT_CONFIG, r as APPLY_DEPS, t as StateContainerRegistry } from "./StateContainerRegistry-gQbEutkN.js";
2
+ import { i as getRegistry, o as setRegistry, r as getStateChangeCount } from "./StateContainer-Bh2ipNYj.js";
3
+ import { r as resolveInstanceKey, t as ensure } from "./ensure-Bi_yXk6f.js";
3
4
  //#region src/testing.ts
4
5
  /** Synthetic owner id used by test helpers for pre-wired deps. */
5
6
  const TESTING_DEPS_OWNER = "testing-deps";
@@ -28,18 +29,21 @@ function withTestRegistry(fn) {
28
29
  }
29
30
  function blacTestSetup() {
30
31
  let savedRegistry;
32
+ let testRegistry;
31
33
  beforeEach(() => {
32
34
  savedRegistry = getRegistry();
33
- setRegistry(new StateContainerRegistry());
35
+ testRegistry = createTestRegistry();
36
+ setRegistry(testRegistry);
34
37
  });
35
38
  afterEach(() => {
39
+ testRegistry.clearAll();
36
40
  setRegistry(savedRegistry);
37
41
  });
38
42
  }
39
43
  function registerOverride(BlocClass, instance, args) {
40
44
  const registry = getRegistry();
41
45
  const key = resolveInstanceKey(BlocClass, args);
42
- registry.insertInstance(BlocClass, key, instance, new Map([["testing-override", 1]]));
46
+ registry[INSERT_INSTANCE](BlocClass, key, instance, new Map([["testing-override", 1]]));
43
47
  }
44
48
  function overrideEnsure(BlocClass, instance, fn, args) {
45
49
  return withTestRegistry(() => {
@@ -49,14 +53,8 @@ function overrideEnsure(BlocClass, instance, fn, args) {
49
53
  }
50
54
  function createCubitStub(BlocClass, options) {
51
55
  const instance = new BlocClass();
52
- if (options?.args != null) instance[INIT_CONFIG]({ args: options.args });
53
- if (options?.state != null) {
54
- if (instance instanceof Cubit) {
55
- const currentState = instance.state;
56
- if (typeof currentState === "object" && currentState !== null && typeof options.state === "object" && options.state !== null) instance.patch(options.state);
57
- else instance.emit(options.state);
58
- }
59
- }
56
+ instance[INIT_CONFIG]({ args: options?.args });
57
+ if (options?.state != null) applyState(instance, options.state);
60
58
  if (options?.methods) {
61
59
  for (const [key, impl] of Object.entries(options.methods)) if (typeof impl === "function") instance[key] = impl;
62
60
  }
@@ -65,31 +63,35 @@ function createCubitStub(BlocClass, options) {
65
63
  }
66
64
  function withBlocState(BlocClass, state, args) {
67
65
  const instance = ensure(BlocClass, { args });
68
- if (instance instanceof Cubit) {
69
- const currentState = instance.state;
70
- if (typeof currentState === "object" && currentState !== null && typeof state === "object" && state !== null) instance.patch(state);
71
- else instance.emit(state);
72
- }
66
+ applyState(instance, state);
73
67
  return instance;
74
68
  }
69
+ function applyState(instance, state) {
70
+ const target = instance;
71
+ if (typeof target.state === "object" && target.state !== null && typeof state === "object" && state !== null) target.patch(state);
72
+ else target.emit(state);
73
+ }
75
74
  function withBlocMethod(BlocClass, methodName, impl, args) {
76
75
  const instance = ensure(BlocClass, { args });
77
76
  instance[methodName] = impl;
78
77
  return instance;
79
78
  }
80
79
  /**
81
- * Drain pending microtasks so any channel-flushed effects (channel
82
- * subscribers, `onSystemEvent('stateChanged')` handlers, plugin hooks) run
83
- * before the next assertion.
84
- *
85
- * The default `MicrotaskScheduler` coalesces emits within a tick; tests
86
- * that emit and then assert on subscriber side-effects need `await flush()`
87
- * between the two.
80
+ * Drain pending microtasks so channel-flushed effects (subscribers,
81
+ * `onSystemEvent('stateChanged')` handlers, plugin hooks) run before the
82
+ * next assertion. A subscriber can emit again, so this keeps draining until
83
+ * a round passes with no new state change.
88
84
  */
89
85
  async function flush() {
90
- await Promise.resolve();
91
- await Promise.resolve();
86
+ for (let i = 0; i < MAX_FLUSH_ROUNDS; i++) {
87
+ const before = getStateChangeCount();
88
+ await Promise.resolve();
89
+ await Promise.resolve();
90
+ if (getStateChangeCount() === before) return;
91
+ }
92
+ throw new Error(`[blac] flush(): state was still changing after ${MAX_FLUSH_ROUNDS} rounds`);
92
93
  }
94
+ const MAX_FLUSH_ROUNDS = 100;
93
95
  //#endregion
94
96
  export { blacTestSetup, createCubitStub, createTestRegistry, flush, overrideEnsure, registerOverride, withBlocMethod, withBlocState, withTestRegistry };
95
97
 
@@ -1 +1 @@
1
- {"version":3,"file":"testing.js","names":[],"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,IAAI,uBAAuB;AACpC;AAEA,SAAgB,iBACd,IACG;CACH,MAAM,WAAW,YAAY;CAC7B,MAAM,eAAe,mBAAmB;CACxC,YAAY,YAAY;CACxB,IAAI;EACF,MAAM,SAAS,GAAG,YAAY;EAC9B,IAAI,kBAAkB,SACpB,OAAO,OAAO,MACX,UAAU;GACT,YAAY,QAAQ;GACpB,OAAO;EACT,IACC,UAAU;GACT,YAAY,QAAQ;GACpB,MAAM;EACR,CACF;EAEF,YAAY,QAAQ;EACpB,OAAO;CACT,SAAS,OAAO;EACd,YAAY,QAAQ;EACpB,MAAM;CACR;AACF;AAIA,SAAgB,gBAAsB;CACpC,IAAI;CACJ,iBAAiB;EACf,gBAAgB,YAAY;EAC5B,YAAY,IAAI,uBAAuB,CAAC;CAC1C,CAAC;CACD,gBAAgB;EACd,YAAY,aAAa;CAC3B,CAAC;AACH;AAIA,SAAgB,iBACd,WACA,UACA,MACM;CACN,MAAM,WAAW,YAAY;CAC7B,MAAM,MAAM,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,SAAS,aAAa,EAAE,MAAM,QAAQ,KAAK,CAAC;CAG9C,IAAI,SAAS,SAAS;MAChB,oBAAoB,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,SAAkB,YAAY,oBAAoB,QAAQ,IAAI;CAGhE,OAAO;AACT;AAIA,SAAgB,cACd,WACA,OAGA,MACiB;CACjB,MAAM,WAAW,OAAO,WAAW,EAAE,KAAK,CAAC;CAC3C,IAAI,oBAAoB,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,WAAW,OAAO,WAAW,EAAE,KAAK,CAAC;CAC3C,SAAkB,cAAc;CAChC,OAAO;AACT;;;;;;;;;;AAaA,eAAsB,QAAuB;CAG3C,MAAM,QAAQ,QAAQ;CACtB,MAAM,QAAQ,QAAQ;AACxB"}
1
+ {"version":3,"file":"testing.js","names":[],"sources":["../src/testing.ts"],"sourcesContent":["import type { Cubit } from './core/Cubit';\nimport { getStateChangeCount } from './core/StateContainer';\nimport { StateContainerRegistry } from './core/StateContainerRegistry';\nimport { APPLY_DEPS, INIT_CONFIG, INSERT_INSTANCE } 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\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\nexport function blacTestSetup(): void {\n let savedRegistry: StateContainerRegistry;\n let testRegistry: StateContainerRegistry;\n beforeEach(() => {\n savedRegistry = getRegistry();\n testRegistry = createTestRegistry();\n setRegistry(testRegistry);\n });\n afterEach(() => {\n testRegistry.clearAll();\n setRegistry(savedRegistry);\n });\n}\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[INSERT_INSTANCE](\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\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 /** Args passed to init(). */\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 instance[INIT_CONFIG]({ args: options?.args });\n\n if (options?.state != null) {\n applyState(instance, options.state);\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\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 applyState(instance, state);\n return instance;\n}\n\n// emit/patch are protected on StateContainer; Cubit only makes them public.\nfunction applyState(instance: object, state: unknown): void {\n const target = instance as Cubit<any>;\n if (\n typeof target.state === 'object' &&\n target.state !== null &&\n typeof state === 'object' &&\n state !== null\n ) {\n target.patch(state);\n } else {\n target.emit(state as any);\n }\n}\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/**\n * Drain pending microtasks so channel-flushed effects (subscribers,\n * `onSystemEvent('stateChanged')` handlers, plugin hooks) run before the\n * next assertion. A subscriber can emit again, so this keeps draining until\n * a round passes with no new state change.\n */\nexport async function flush(): Promise<void> {\n for (let i = 0; i < MAX_FLUSH_ROUNDS; i++) {\n const before = getStateChangeCount();\n // The second tick lets microtasks queued by the drain itself (registry\n // `stateChanged` delivery) run too.\n await Promise.resolve();\n await Promise.resolve();\n if (getStateChangeCount() === before) return;\n }\n throw new Error(\n `[blac] flush(): state was still changing after ${MAX_FLUSH_ROUNDS} rounds`,\n );\n}\n\nconst MAX_FLUSH_ROUNDS = 100;\n"],"mappings":";;;;;AAcA,MAAM,qBAAqB;AAK3B,SAAgB,qBAA6C;CAC3D,OAAO,IAAI,uBAAuB;AACpC;AAEA,SAAgB,iBACd,IACG;CACH,MAAM,WAAW,YAAY;CAC7B,MAAM,eAAe,mBAAmB;CACxC,YAAY,YAAY;CACxB,IAAI;EACF,MAAM,SAAS,GAAG,YAAY;EAC9B,IAAI,kBAAkB,SACpB,OAAO,OAAO,MACX,UAAU;GACT,YAAY,QAAQ;GACpB,OAAO;EACT,IACC,UAAU;GACT,YAAY,QAAQ;GACpB,MAAM;EACR,CACF;EAEF,YAAY,QAAQ;EACpB,OAAO;CACT,SAAS,OAAO;EACd,YAAY,QAAQ;EACpB,MAAM;CACR;AACF;AAEA,SAAgB,gBAAsB;CACpC,IAAI;CACJ,IAAI;CACJ,iBAAiB;EACf,gBAAgB,YAAY;EAC5B,eAAe,mBAAmB;EAClC,YAAY,YAAY;CAC1B,CAAC;CACD,gBAAgB;EACd,aAAa,SAAS;EACtB,YAAY,aAAa;CAC3B,CAAC;AACH;AAEA,SAAgB,iBACd,WACA,UACA,MACM;CACN,MAAM,WAAW,YAAY;CAC7B,MAAM,MAAM,mBAAmB,WAAW,IAAI;CAC9C,SAAS,iBACP,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;AAsBA,SAAgB,gBACd,WACA,SACiB;CACjB,MAAM,WAAW,IAAI,UAAU;CAC/B,SAAS,aAAa,EAAE,MAAM,SAAS,KAAK,CAAC;CAE7C,IAAI,SAAS,SAAS,MACpB,WAAW,UAAU,QAAQ,KAAK;CAEpC,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,SAAkB,YAAY,oBAAoB,QAAQ,IAAI;CAGhE,OAAO;AACT;AAEA,SAAgB,cACd,WACA,OAGA,MACiB;CACjB,MAAM,WAAW,OAAO,WAAW,EAAE,KAAK,CAAC;CAC3C,WAAW,UAAU,KAAK;CAC1B,OAAO;AACT;AAGA,SAAS,WAAW,UAAkB,OAAsB;CAC1D,MAAM,SAAS;CACf,IACE,OAAO,OAAO,UAAU,YACxB,OAAO,UAAU,QACjB,OAAO,UAAU,YACjB,UAAU,MAEV,OAAO,MAAM,KAAK;MAElB,OAAO,KAAK,KAAY;AAE5B;AAEA,SAAgB,eACd,WACA,YACA,MACA,MACiB;CACjB,MAAM,WAAW,OAAO,WAAW,EAAE,KAAK,CAAC;CAC3C,SAAkB,cAAc;CAChC,OAAO;AACT;;;;;;;AAQA,eAAsB,QAAuB;CAC3C,KAAK,IAAI,IAAI,GAAG,IAAI,kBAAkB,KAAK;EACzC,MAAM,SAAS,oBAAoB;EAGnC,MAAM,QAAQ,QAAQ;EACtB,MAAM,QAAQ,QAAQ;EACtB,IAAI,oBAAoB,MAAM,QAAQ;CACxC;CACA,MAAM,IAAI,MACR,kDAAkD,iBAAiB,QACrE;AACF;AAEA,MAAM,mBAAmB"}
@@ -1,7 +1,7 @@
1
1
  declare const brand: unique symbol;
2
2
  /**
3
- * Utility type for creating branded/nominal types.
4
- * Prevents accidental type confusion between similar primitive types.
3
+ * Nominal type helper, preventing accidental confusion between similar
4
+ * primitive types.
5
5
  * @typeParam T - The base type
6
6
  * @typeParam B - The brand identifier
7
7
  */
@@ -13,14 +13,7 @@ export type Brand<T, B> = T & {
13
13
  * @typeParam B - The brand identifier
14
14
  */
15
15
  export type BrandedId<B> = Brand<string, B>;
16
- /**
17
- * Branded string type for state container instance IDs
18
- */
16
+ /** Branded string type for state container instance IDs. */
19
17
  export type InstanceId = Brand<string, 'InstanceId'>;
20
- /**
21
- * Create a branded InstanceId from a string
22
- * @param id - The string ID to brand
23
- * @returns Branded InstanceId
24
- */
25
18
  export declare function instanceId(id: string): InstanceId;
26
19
  export {};
@@ -58,21 +58,3 @@ export type WithState<I, S> = I & {
58
58
  export type InstanceReadonlyState<T extends StateContainerConstructor = StateContainerConstructor> = WithState<InstanceType<T>, ExtractState<T>>;
59
59
  export type InstanceState<T extends StateContainerConstructor = StateContainerConstructor> = WithState<InstanceType<T>, ExtractStateMutable<T>>;
60
60
  export type StateContainerInstance<S extends object = any> = WithState<StateContainer<S, any, any>, Readonly<S>>;
61
- /**
62
- * Extract constructor argument types from a class
63
- * @typeParam T - The class type
64
- */
65
- export type ExtractConstructorArgs<T> = T extends new (...args: infer P) => any ? P : never[];
66
- /**
67
- * Extract instance type from an abstract class constructor
68
- * @typeParam T - The abstract class constructor type
69
- */
70
- export type BlocInstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;
71
- /**
72
- * Constructor type for StateContainer classes.
73
- * Used for type-safe hook parameters.
74
- * @typeParam TBloc - The StateContainer instance type
75
- */
76
- export type BlocConstructor<S extends object = any, T extends new (...args: any[]) => StateContainer<S, any, any> = new (...args: any[]) => StateContainer<S, any, any>> = (new (...args: any[]) => InstanceType<T>) & {
77
- keepAlive?: boolean;
78
- };
@@ -1,21 +1,14 @@
1
1
  /**
2
- * Centralized ID Generation
2
+ * Build an instance id: `${prefix}:${affix}` when an affix is given (the
3
+ * registry passes the instance key), otherwise `${prefix}:${timestamp}_${random}`.
3
4
  *
4
- * Provides consistent, collision-resistant ID generation for all BlaC subsystems.
5
- * Uses timestamp + counter + random suffix for uniqueness.
6
- */
7
- /**
8
- * Generate simple ID with timestamp and random (no counter tracking)
9
- *
10
- * Format: `${prefix}:${timestamp}_${random}`
11
- *
12
- * @param prefix - Prefix for the ID
13
- * @returns Branded ID string
5
+ * Ids are not unique: same-named classes share an id for the same affix, and
6
+ * the lazy `$blac.id` of an unregistered instance is always `${prefix}:main`.
14
7
  *
15
8
  * @example
16
9
  * ```ts
17
- * const id = generateSimpleId('CounterBloc');
18
- * // Returns: "CounterBloc:1698765432100_a3k9d7f2q"
10
+ * generateSimpleId('CounterBloc', 'default'); // "CounterBloc:default"
11
+ * generateSimpleId('CounterBloc'); // "CounterBloc:1698765432100_a3k9d7f2q"
19
12
  * ```
20
13
  */
21
14
  export declare function generateSimpleId(prefix: string, affix?: string): string;
@@ -1,66 +1,40 @@
1
- /**
2
- * Utility functions for accessing static properties on StateContainer classes
3
- */
4
1
  import type { EqualityFn } from '../config';
5
2
  import { StateContainerConstructor } from '../types/utilities';
6
3
  /**
7
4
  * Get a static property from a class constructor, walking the prototype
8
5
  * chain so subclasses inherit the value unless they define their own.
9
- * Type-safe helper that avoids (Type as any) casts
10
- *
11
- * @param Type - The class constructor
12
- * @param propName - The property name to access
13
- * @param defaultValue - Optional default value if property is undefined
14
- * @returns The property value or default
15
6
  */
16
- export declare function getStaticProp<V, T extends StateContainerConstructor = StateContainerConstructor>(Type: T, propName: string, defaultValue?: V): V | undefined;
7
+ export declare function getStaticProp<V, T extends StateContainerConstructor = StateContainerConstructor>(Type: T, propName: string): V | undefined;
17
8
  /**
18
9
  * Get a static property declared directly on `Type`, ignoring the prototype
19
10
  * chain, so subclasses never inherit a base class's value.
20
- *
21
- * @param Type - The class constructor
22
- * @param propName - The property name to access
23
- * @returns The own property value or `undefined`
24
11
  */
25
12
  export declare function getOwnStaticProp<V, T extends StateContainerConstructor = StateContainerConstructor>(Type: T, propName: string): V | undefined;
26
13
  /**
27
- * Check if a class is marked as keepAlive.
28
- * KeepAlive classes are never auto-disposed when ref count reaches 0.
14
+ * Whether a class is marked `keepAlive` (never auto-disposed at ref count 0).
29
15
  * Inherited: a subclass keeps a base class's `keepAlive` unless overridden.
30
- * @param Type - The class constructor to check
31
- * @returns true if the class has `static keepAlive = true`
32
16
  */
33
17
  export declare function isKeepAliveClass<T extends StateContainerConstructor>(Type: T): boolean;
34
18
  /**
35
- * Check if a class should be excluded from DevTools.
36
- * Used to prevent infinite loops when DevTools tracks itself.
37
- * Inherited: a subclass keeps a base class's exclusion unless overridden.
38
- * @param Type - The class constructor to check
39
- * @returns true if the class has `static __excludeFromDevTools = true`
19
+ * Whether a class is excluded from DevTools (avoids DevTools tracking itself
20
+ * into an infinite loop). Inherited unless overridden by a subclass.
40
21
  */
41
22
  export declare function isExcludedFromDevTools<T extends StateContainerConstructor>(Type: T): boolean;
42
23
  /**
43
24
  * Get the per-class equality function set via `@blac({ equality })`, if any.
44
25
  * Inherited: a subclass keeps a base class's equality fn unless overridden.
45
- * @param Type - The class constructor to check
46
- * @returns the equality function or `undefined`
47
26
  */
48
27
  export declare function getClassEquality<T extends StateContainerConstructor>(Type: T): EqualityFn | undefined;
49
28
  /**
50
- * Get a class's minification-safe identity name.
51
- * Own only: a subclass never inherits a base class's explicit `blacName`,
52
- * since that would collapse two distinct blocs onto one identity.
53
- * Falls back to `Type.name` (the runtime `constructor.name`) when unset.
54
- * @param Type - The class constructor to check
55
- * @returns the explicit `static blacName` or `Type.name`
29
+ * Get a class's minification-safe identity name (`static blacName`), falling
30
+ * back to `Type.name`. Own only: a subclass never inherits a base class's
31
+ * explicit `blacName`, since that would collapse two distinct blocs onto one
32
+ * identity.
56
33
  */
57
34
  export declare function getBlacName<T extends StateContainerConstructor>(Type: T): string;
58
35
  /**
59
- * Get the per-class key function set via `@blac({ key })` or `static key = …`, if any.
60
- * Used by the registry to derive a stable instance key from `args`.
36
+ * Get the per-class key function set via `@blac({ key })` or `static key = …`.
61
37
  * Own only: a subclass never inherits a base class's `key`, since a
62
38
  * different `Args` shape would derive the wrong instance key.
63
- * @param Type - The class constructor to check
64
- * @returns the key function or `undefined`
65
39
  */
66
40
  export declare function getClassKey(Type: unknown): ((args: any) => string) | undefined;
@@ -1,7 +1,2 @@
1
- /**
2
- * Deterministic, order-independent hash of serializable args.
3
- * Sorts object keys so {a,b} === {b,a}.
4
- * Throws (dev) if it encounters a function or non-plain object — those belong in `deps`.
5
- */
6
1
  export declare const DEFAULT_STRUCTURAL_KEY = "default";
7
2
  export declare function structuralKey(args: unknown): string;
@@ -36,15 +36,10 @@ type BlocInput = StateContainerConstructor | BlocRef<StateContainerConstructor>;
36
36
  */
37
37
  export interface WatchOptions {
38
38
  /**
39
- * When `false`, `watch` observes passively instead of owning the instance:
40
- * it does not create a missing instance, and it does not take a real
41
- * ownership ref on an existing one (so it never keeps an otherwise-
42
- * unreferenced instance alive).
43
- *
44
- * If the instance does not exist yet when `watch` is called, the callback
45
- * simply never fires for it — `watch` does not poll or wait for a later
46
- * creation. Call `watch` again once the instance is known to exist (e.g.
47
- * after some other owner has acquired it).
39
+ * When `false`, `watch` observes passively: it neither creates a missing
40
+ * instance nor takes a real ownership ref on an existing one. If the
41
+ * instance does not exist yet, the callback simply never fires — `watch`
42
+ * does not poll; call it again once the instance exists.
48
43
  *
49
44
  * Defaults to `true`.
50
45
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blac/core",
3
- "version": "2.0.22",
3
+ "version": "2.1.1",
4
4
  "license": "MIT",
5
5
  "author": "Brendan Mullins <jsnanigans@gmail.com>",
6
6
  "description": "Type-safe, class-based state management with proxy tracking and plugin system",
@@ -24,6 +24,9 @@
24
24
  ],
25
25
  "plugins": [
26
26
  "./dist/plugins.d.ts"
27
+ ],
28
+ "internal": [
29
+ "./dist/internal.d.ts"
27
30
  ]
28
31
  }
29
32
  },
@@ -57,6 +60,16 @@
57
60
  "types": "./dist/testing.d.cts",
58
61
  "default": "./dist/testing.cjs"
59
62
  }
63
+ },
64
+ "./internal": {
65
+ "import": {
66
+ "types": "./dist/internal.d.ts",
67
+ "default": "./dist/internal.js"
68
+ },
69
+ "require": {
70
+ "types": "./dist/internal.d.cts",
71
+ "default": "./dist/internal.cjs"
72
+ }
60
73
  }
61
74
  },
62
75
  "files": [
@@ -83,7 +96,7 @@
83
96
  }
84
97
  ],
85
98
  "dependencies": {
86
- "@dirtytalk/structural": "^0.1.2"
99
+ "@dirtytalk/structural": "^0.1.3"
87
100
  },
88
101
  "devDependencies": {
89
102
  "@size-limit/preset-small-lib": "^13.0.3",