@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/index.js CHANGED
@@ -1,6 +1,33 @@
1
- import { _ as resetBlacConfig, a as ON_DISPOSE, c as WITH_TRACKED_STATE, d as isExcludedFromDevTools, f as isKeepAliveClass, g as getBlacConfig, h as configureBlac, i as INIT_CONFIG, l as getBlacName, n as globalRegistry, o as REMOVE_DEPS_OWNER, p as BLAC_STATIC_PROPS, r as APPLY_DEPS, t as StateContainerRegistry, v as shallowEqualState } from "./StateContainerRegistry-BsH_s0pJ.js";
2
- import { a as DEP_BRAND, c as getStats, i as Cubit, l as setRegistry, n as acquire, o as StateContainer, r as resolveInstanceKey, s as getRegistry, t as ensure } from "./ensure-CneMkpbI.js";
1
+ import { _ as configureBlac, b as shallowEqualState, f as isExcludedFromDevTools, m as BLAC_STATIC_PROPS, n as globalRegistry, o as ON_DISPOSE, p as isKeepAliveClass, t as StateContainerRegistry, u as getBlacName, v as getBlacConfig, y as resetBlacConfig } from "./StateContainerRegistry-gQbEutkN.js";
2
+ import { a as getStats, i as getRegistry, n as StateContainer, o as setRegistry } from "./StateContainer-Bh2ipNYj.js";
3
+ import { n as acquire, r as resolveInstanceKey, t as ensure } from "./ensure-Bi_yXk6f.js";
3
4
  import { ALL_PATHS, ALL_PATHS as ALL_PATHS$1 } from "@dirtytalk/structural";
5
+ //#region src/core/Cubit.ts
6
+ /**
7
+ * `Cubit<S>` is a `StateContainer<S>` that exposes mutation publicly.
8
+ *
9
+ * That is the whole difference, and it is a real one: on `StateContainer`
10
+ * `emit` / `patch` / `update` are `protected`, so a container mutates itself
11
+ * from its own methods and callers go through the API it chooses to publish.
12
+ * `Cubit` re-declares the three as `public` for the cases where a caller
13
+ * legitimately drives state from outside — test helpers, devtools
14
+ * time-travel, benchmarks.
15
+ *
16
+ * Reach for `StateContainer` when business logic should live in the class,
17
+ * and `Cubit` when the caller owns the transitions.
18
+ */
19
+ var Cubit = class extends StateContainer {
20
+ emit(next) {
21
+ super.emit(next);
22
+ }
23
+ patch(partial) {
24
+ super.patch(partial);
25
+ }
26
+ update(fn) {
27
+ super.update(fn);
28
+ }
29
+ };
30
+ //#endregion
4
31
  //#region src/registry/borrow.ts
5
32
  function resolveBorrowKey(BlocClass, target) {
6
33
  return getRegistry().resolveKey(BlocClass, void 0, target?.args);
@@ -104,11 +131,11 @@ function register(BlocClass) {
104
131
  */
105
132
  function blac(options) {
106
133
  return function(target, _context) {
107
- if ("keepAlive" in options && options.keepAlive) target[BLAC_STATIC_PROPS.KEEP_ALIVE] = true;
108
- if ("excludeFromDevTools" in options && options.excludeFromDevTools) target[BLAC_STATIC_PROPS.EXCLUDE_FROM_DEVTOOLS] = true;
109
- if ("equality" in options && typeof options.equality === "function") target[BLAC_STATIC_PROPS.EQUALITY] = options.equality;
110
- if ("key" in options && typeof options.key === "function") target[BLAC_STATIC_PROPS.KEY] = options.key;
111
- if ("name" in options && typeof options.name === "string") target[BLAC_STATIC_PROPS.BLAC_NAME] = options.name;
134
+ if (options.keepAlive !== void 0) target[BLAC_STATIC_PROPS.KEEP_ALIVE] = options.keepAlive;
135
+ if (options.excludeFromDevTools) target[BLAC_STATIC_PROPS.EXCLUDE_FROM_DEVTOOLS] = true;
136
+ if (typeof options.equality === "function") target[BLAC_STATIC_PROPS.EQUALITY] = options.equality;
137
+ if (typeof options.key === "function") target[BLAC_STATIC_PROPS.KEY] = options.key;
138
+ if (typeof options.name === "string") target[BLAC_STATIC_PROPS.BLAC_NAME] = options.name;
112
139
  return target;
113
140
  };
114
141
  }
@@ -138,8 +165,7 @@ function instance(BlocClass, args) {
138
165
  function isBlocRef(input) {
139
166
  return typeof input === "object" && input !== null && BLOC_REF_MARKER in input;
140
167
  }
141
- function toWatchTarget(input) {
142
- const registry = getRegistry();
168
+ function toWatchTarget(registry, input) {
143
169
  if (isBlocRef(input)) return {
144
170
  blocClass: input.blocClass,
145
171
  key: input.instanceId,
@@ -157,8 +183,8 @@ let watchRefSeq = 0;
157
183
  * through so a not-yet-created instance is initialized correctly. The
158
184
  * caller is responsible for releasing `refId` in cleanup.
159
185
  */
160
- function resolveBloc(target, refId) {
161
- return getRegistry().acquire(target.blocClass, target.key, {
186
+ function resolveBloc(registry, target, refId) {
187
+ return registry.acquire(target.blocClass, target.key, {
162
188
  countRef: true,
163
189
  refId,
164
190
  args: target.args
@@ -168,8 +194,7 @@ function resolveBloc(target, refId) {
168
194
  * Passive lookup for `{ create: false }`: never creates the instance and
169
195
  * never takes a ref. Returns `undefined` when no instance currently exists.
170
196
  */
171
- function resolveBlocPassive(target) {
172
- const registry = getRegistry();
197
+ function resolveBlocPassive(registry, target) {
173
198
  try {
174
199
  return registry.acquire(target.blocClass, target.key, {
175
200
  canCreate: false,
@@ -188,10 +213,11 @@ function watchImpl(blocsOrBloc, callback, options) {
188
213
  const inputs = isSingle ? [blocsOrBloc] : blocsOrBloc;
189
214
  const registry = getRegistry();
190
215
  const create = options?.create ?? true;
191
- const targets = inputs.map(toWatchTarget);
216
+ const targets = inputs.map((input) => toWatchTarget(registry, input));
192
217
  const refIds = targets.map(() => `_watch_${watchRefSeq++}`);
218
+ const resolveAt = (index) => create ? resolveBloc(registry, targets[index], refIds[index]) : resolveBlocPassive(registry, targets[index]);
193
219
  let disposed = false;
194
- const instances = targets.map((target, i) => create ? resolveBloc(target, refIds[i]) : resolveBlocPassive(target));
220
+ const instances = [];
195
221
  const channelUnsubs = [];
196
222
  const disposedUnsubs = [];
197
223
  const cleanup = () => {
@@ -201,7 +227,7 @@ function watchImpl(blocsOrBloc, callback, options) {
201
227
  for (const unsub of disposedUnsubs) unsub?.();
202
228
  channelUnsubs.length = 0;
203
229
  disposedUnsubs.length = 0;
204
- if (create) for (let i = 0; i < targets.length; i++) registry.release(targets[i].blocClass, targets[i].key, false, refIds[i]);
230
+ if (create) for (let i = 0; i < instances.length; i++) registry.release(targets[i].blocClass, targets[i].key, false, refIds[i]);
205
231
  };
206
232
  const runCallback = () => {
207
233
  if (disposed) return;
@@ -213,7 +239,7 @@ function watchImpl(blocsOrBloc, callback, options) {
213
239
  };
214
240
  const resubscribeAt = (index) => {
215
241
  if (disposed) return;
216
- instances[index] = create ? resolveBloc(targets[index], refIds[index]) : resolveBlocPassive(targets[index]);
242
+ instances[index] = resolveAt(index);
217
243
  subscribeAt(index);
218
244
  subscribeDisposeAt(index);
219
245
  runCallback();
@@ -225,23 +251,24 @@ function watchImpl(blocsOrBloc, callback, options) {
225
251
  queueMicrotask(() => resubscribeAt(index));
226
252
  });
227
253
  };
228
- for (let i = 0; i < instances.length; i++) subscribeAt(i);
229
- for (let i = 0; i < instances.length; i++) subscribeDisposeAt(i);
230
- runCallback();
254
+ try {
255
+ for (let i = 0; i < targets.length; i++) instances.push(resolveAt(i));
256
+ for (let i = 0; i < instances.length; i++) subscribeAt(i);
257
+ for (let i = 0; i < instances.length; i++) subscribeDisposeAt(i);
258
+ runCallback();
259
+ } catch (error) {
260
+ cleanup();
261
+ throw error;
262
+ }
231
263
  return cleanup;
232
264
  }
233
265
  const watch = Object.assign(watchImpl, { STOP });
234
266
  //#endregion
235
267
  //#region src/types/branded.ts
236
- /**
237
- * Create a branded InstanceId from a string
238
- * @param id - The string ID to brand
239
- * @returns Branded InstanceId
240
- */
241
268
  function instanceId(id) {
242
269
  return id;
243
270
  }
244
271
  //#endregion
245
- export { ALL_PATHS, APPLY_DEPS, Cubit, DEP_BRAND, INIT_CONFIG, ON_DISPOSE, REMOVE_DEPS_OWNER, StateContainer, StateContainerRegistry, WITH_TRACKED_STATE, acquire, blac, borrow, borrowSafe, clear, clearAll, configureBlac, ensure, forEach, getAll, getBlacConfig, getBlacName, getRefCount, getRefIds, getRegistry, getStats, globalRegistry, hasInstance, instance, instanceId, isExcludedFromDevTools, isKeepAliveClass, register, release, resetBlacConfig, resolveInstanceKey, setRegistry, shallowEqualState, watch };
272
+ export { ALL_PATHS, Cubit, StateContainer, StateContainerRegistry, acquire, blac, borrow, borrowSafe, clear, clearAll, configureBlac, ensure, forEach, getAll, getBlacConfig, getBlacName, getRefCount, getRefIds, getRegistry, getStats, globalRegistry, hasInstance, instance, instanceId, isExcludedFromDevTools, isKeepAliveClass, register, release, resetBlacConfig, resolveInstanceKey, setRegistry, shallowEqualState, watch };
246
273
 
247
274
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["ALL_PATHS"],"sources":["../src/registry/borrow.ts","../src/registry/release.ts","../src/registry/queries.ts","../src/registry/management.ts","../src/decorators/blac.ts","../src/watch/watch.ts","../src/types/branded.ts"],"sourcesContent":["import { getRegistry } from './config';\nimport type {\n ExtractArgs,\n StateContainerConstructor,\n} from '../types/utilities';\n\n/**\n * How to identify the instance to borrow: the same `args` shape `useBloc`\n * accepts, resolved through the class's `static key(args)` so call sites use\n * the *same* shape they pass to `useBloc` instead of hand-deriving the key.\n *\n * `borrowSafe(ChannelBloc, { args: { channelId } })` and\n * `useBloc(ChannelBloc, { args: { channelId } })` therefore always agree.\n */\nexport type BorrowTarget<T extends StateContainerConstructor> = {\n args?: ExtractArgs<T>;\n};\n\nfunction resolveBorrowKey<T extends StateContainerConstructor>(\n BlocClass: T,\n target: BorrowTarget<T> | undefined,\n): string {\n return getRegistry().resolveKey(BlocClass, undefined, target?.args);\n}\n\nexport function borrow<T extends StateContainerConstructor>(\n BlocClass: T,\n target?: BorrowTarget<T>,\n): InstanceType<T> {\n return getRegistry().borrow(BlocClass, resolveBorrowKey(BlocClass, target));\n}\n\nexport function borrowSafe<T extends StateContainerConstructor>(\n BlocClass: T,\n target?: BorrowTarget<T>,\n):\n | { error: Error; instance: null }\n | { error: null; instance: InstanceType<T> } {\n return getRegistry().borrowSafe(\n BlocClass,\n resolveBorrowKey(BlocClass, target),\n );\n}\n","import { getRegistry } from './config';\nimport type {\n ExtractArgs,\n StateContainerConstructor,\n} from '../types/utilities';\n\n/**\n * Release a reference to an instance. Instance identity is derived purely from\n * `args` (must match the `args` it was acquired with).\n *\n * @param opts - `args` derives the instance key (must match acquire);\n * `refId` picks the ref to drop, otherwise one arbitrary ref goes;\n * `forceDispose` disposes immediately regardless of remaining refs.\n */\nexport function release<T extends StateContainerConstructor>(\n BlocClass: T,\n opts?: { args?: ExtractArgs<T>; refId?: string; forceDispose?: boolean },\n): void {\n const registry = getRegistry();\n const key = registry.resolveKey(BlocClass, undefined, opts?.args);\n registry.release(BlocClass, key, opts?.forceDispose ?? false, opts?.refId);\n}\n","import { getRegistry } from './config';\nimport type {\n ExtractArgs,\n StateContainerConstructor,\n InstanceReadonlyState,\n} from '../types/utilities';\n\n/**\n * @public\n */\nexport function hasInstance<T extends StateContainerConstructor>(\n BlocClass: T,\n opts?: { args?: ExtractArgs<T> },\n): boolean {\n const registry = getRegistry();\n const key = registry.resolveKey(BlocClass, undefined, opts?.args);\n return registry.hasInstance(BlocClass, key);\n}\n\n/**\n * @public\n */\nexport function getRefCount<T extends StateContainerConstructor>(\n BlocClass: T,\n opts?: { args?: ExtractArgs<T> },\n): number {\n const registry = getRegistry();\n const key = registry.resolveKey(BlocClass, undefined, opts?.args);\n return registry.getRefCount(BlocClass, key);\n}\n\n/**\n * @public\n */\nexport function getRefIds<T extends StateContainerConstructor>(\n BlocClass: T,\n opts?: { args?: ExtractArgs<T> },\n): string[] {\n const registry = getRegistry();\n const key = registry.resolveKey(BlocClass, undefined, opts?.args);\n return registry.getRefIds(BlocClass, key);\n}\n\n/**\n * @public\n */\nexport function getAll<T extends StateContainerConstructor>(\n BlocClass: T,\n): InstanceReadonlyState<T>[] {\n return getRegistry().getAll(BlocClass);\n}\n\n/**\n * @public\n */\nexport function forEach<T extends StateContainerConstructor>(\n BlocClass: T,\n callback: (instance: InstanceReadonlyState<T>) => void,\n): void {\n getRegistry().forEach(BlocClass, callback);\n}\n","import { getRegistry } from './config';\nimport type { StateContainerConstructor } from '../types/utilities';\n\nexport function clear<T extends StateContainerConstructor>(BlocClass: T): void {\n getRegistry().clear(BlocClass);\n}\n\nexport function clearAll(): void {\n getRegistry().clearAll();\n}\n\n/**\n * @public\n */\nexport function register<T extends StateContainerConstructor>(\n BlocClass: T,\n): void {\n getRegistry().register(BlocClass);\n}\n","import { BLAC_STATIC_PROPS } from '../constants';\nimport type { EqualityFn } from '../config';\n\n/**\n * Configuration options for the `@blac` decorator. Any combination may be\n * given at once.\n */\nexport interface BlacOptions {\n /** Mark bloc to never be auto-disposed when ref count reaches 0 */\n keepAlive?: true;\n /** Exclude bloc from DevTools tracking (prevents infinite loops) */\n excludeFromDevTools?: true;\n /**\n * Override the global equality check for this bloc.\n * Return `true` to skip the emit (states considered equal).\n */\n equality?: EqualityFn;\n /**\n * Override the default structural-hash identity key.\n * The function receives `args` and returns the instance key string.\n * Distinct return values → distinct instances; identical values → shared instance.\n * Non-identity fields can be excluded so they don't fork new instances.\n */\n key?: (args: any) => string;\n /**\n * Explicit, minification-safe identity for this bloc class. Falls back to\n * `constructor.name` when unset, which collides across classes once a\n * minifier shortens class names.\n */\n name?: string;\n}\n\n/**\n * Decorator to configure StateContainer classes.\n *\n * @example Decorator syntax (requires experimentalDecorators or TC39 decorators)\n * ```ts\n * @blac({ keepAlive: true })\n * class AuthBloc extends Cubit<AuthState> {}\n *\n * @blac({ excludeFromDevTools: true })\n * class InternalBloc extends Cubit<InternalState> {}\n *\n * @blac({ keepAlive: true, key: (args) => args.userId })\n * class UserBloc extends Cubit<UserState> {}\n * ```\n *\n * @example Function syntax (no decorator support needed)\n * ```ts\n * const AuthBloc = blac({ keepAlive: true })(\n * class extends Cubit<AuthState> {}\n * );\n * ```\n */\nexport function blac(options: BlacOptions) {\n return function <T extends new (...args: any[]) => any>(\n target: T,\n _context?: ClassDecoratorContext,\n ): T {\n if ('keepAlive' in options && options.keepAlive) {\n (target as any)[BLAC_STATIC_PROPS.KEEP_ALIVE] = true;\n }\n if ('excludeFromDevTools' in options && options.excludeFromDevTools) {\n (target as any)[BLAC_STATIC_PROPS.EXCLUDE_FROM_DEVTOOLS] = true;\n }\n if ('equality' in options && typeof options.equality === 'function') {\n (target as any)[BLAC_STATIC_PROPS.EQUALITY] = options.equality;\n }\n if ('key' in options && typeof options.key === 'function') {\n (target as any)[BLAC_STATIC_PROPS.KEY] = options.key;\n }\n if ('name' in options && typeof options.name === 'string') {\n (target as any)[BLAC_STATIC_PROPS.BLAC_NAME] = options.name;\n }\n return target;\n };\n}\n","import { ALL_PATHS } from '@dirtytalk/structural';\nimport { ON_DISPOSE } from '../core/symbols';\nimport { getRegistry } from '../registry';\nimport { resolveInstanceKey } from '../registry/acquire';\nimport type {\n ExtractArgs,\n StateContainerConstructor,\n StateContainerInstance,\n} from '../types/utilities';\n\nconst STOP: unique symbol = Symbol('watch.STOP');\ntype StopSymbol = typeof STOP;\n\nconst BLOC_REF_MARKER = Symbol('BlocRef');\n\n/**\n * Reference to a specific bloc instance, identified by class + the resolved\n * key derived from its `args`.\n */\nexport interface BlocRef<T extends StateContainerConstructor> {\n [BLOC_REF_MARKER]: true;\n blocClass: T;\n /** @internal The resolved instance key (derived from `args`). */\n instanceId: string;\n /**\n * @internal The `args` used to derive `instanceId`. Forwarded to\n * `registry.acquire` so a not-yet-created instance is initialized with\n * these args instead of `undefined`.\n */\n args?: unknown;\n}\n\n/**\n * Create a reference to a specific bloc instance, identified by its `args`.\n * The key is derived the same way `useBloc`/`acquire` derive it.\n *\n * @example\n * ```ts\n * watch(instance(UserBloc, { userId: 'user-123' }), (userBloc) => {\n * console.log(userBloc.state.name);\n * });\n * ```\n */\nexport function instance<T extends StateContainerConstructor>(\n BlocClass: T,\n args?: ExtractArgs<T>,\n): BlocRef<T> {\n return {\n [BLOC_REF_MARKER]: true,\n blocClass: BlocClass,\n instanceId: resolveInstanceKey(BlocClass, args),\n args,\n };\n}\n\nfunction isBlocRef(\n input: unknown,\n): input is BlocRef<StateContainerConstructor> {\n return (\n typeof input === 'object' && input !== null && BLOC_REF_MARKER in input\n );\n}\n\ntype BlocInput = StateContainerConstructor | BlocRef<StateContainerConstructor>;\n\n/**\n * Options for `watch`.\n */\nexport interface WatchOptions {\n /**\n * When `false`, `watch` observes passively instead of owning the instance:\n * it does not create a missing instance, and it does not take a real\n * ownership ref on an existing one (so it never keeps an otherwise-\n * unreferenced instance alive).\n *\n * If the instance does not exist yet when `watch` is called, the callback\n * simply never fires for it — `watch` does not poll or wait for a later\n * creation. Call `watch` again once the instance is known to exist (e.g.\n * after some other owner has acquired it).\n *\n * Defaults to `true`.\n */\n create?: boolean;\n}\n\ntype ExtractInstance<T> =\n T extends BlocRef<infer C>\n ? InstanceType<C>\n : T extends StateContainerConstructor\n ? InstanceType<T>\n : never;\n\ntype ExtractInstances<T extends readonly BlocInput[]> = {\n [K in keyof T]: ExtractInstance<T[K]>;\n};\n\n/**\n * Watch function signature for single bloc.\n */\nexport interface WatchSingleFn {\n <T extends StateContainerConstructor>(\n bloc: T | BlocRef<T>,\n callback: (bloc: InstanceType<T>) => void | StopSymbol,\n options?: WatchOptions,\n ): () => void;\n\n STOP: StopSymbol;\n}\n\n/**\n * Watch function signature for multiple blocs.\n */\nexport interface WatchMultipleFn {\n <T extends readonly BlocInput[]>(\n blocs: T,\n callback: (blocs: ExtractInstances<T>) => void | StopSymbol,\n options?: WatchOptions,\n ): () => void;\n\n STOP: StopSymbol;\n}\n\n/**\n * Combined watch function type.\n */\nexport interface WatchFn extends WatchSingleFn {\n <T extends readonly BlocInput[]>(\n blocs: T,\n callback: (blocs: ExtractInstances<T>) => void | StopSymbol,\n options?: WatchOptions,\n ): () => void;\n}\n\n/**\n * Class + resolved key + args needed to (re-)acquire a specific instance.\n * `watch` keeps this around per input so it can re-acquire the same logical\n * instance if the underlying container is disposed elsewhere.\n */\ninterface WatchTarget {\n blocClass: StateContainerConstructor;\n key: string;\n args: unknown;\n}\n\nfunction toWatchTarget(input: BlocInput): WatchTarget {\n const registry = getRegistry();\n if (isBlocRef(input)) {\n return {\n blocClass: input.blocClass,\n key: input.instanceId,\n args: input.args,\n };\n }\n return {\n blocClass: input,\n key: registry.resolveKey(input, undefined, undefined),\n args: undefined,\n };\n}\n\nlet watchRefSeq = 0;\n\n/**\n * Acquire a real ref (countRef: true) for the target, carrying its `args`\n * through so a not-yet-created instance is initialized correctly. The\n * caller is responsible for releasing `refId` in cleanup.\n */\nfunction resolveBloc(\n target: WatchTarget,\n refId: string,\n): StateContainerInstance {\n const registry = getRegistry();\n return registry.acquire(target.blocClass, target.key, {\n countRef: true,\n refId,\n args: target.args,\n });\n}\n\n/**\n * Passive lookup for `{ create: false }`: never creates the instance and\n * never takes a ref. Returns `undefined` when no instance currently exists.\n */\nfunction resolveBlocPassive(\n target: WatchTarget,\n): StateContainerInstance | undefined {\n const registry = getRegistry();\n try {\n return registry.acquire(target.blocClass, target.key, {\n canCreate: false,\n countRef: false,\n args: target.args,\n });\n } catch {\n return undefined;\n }\n}\n\nfunction isArray(input: unknown): input is readonly BlocInput[] {\n return Array.isArray(input);\n}\n\n/**\n * Watch one or more blocs for state changes.\n *\n * Thin wrapper around `container.channel.subscribe(ALL_PATHS, ...)`. The\n * callback fires once immediately, then on every state change of any of the\n * passed blocs. Returning `watch.STOP` from the callback tears down all\n * subscriptions.\n *\n * Note: subscriptions are microtask-deferred (per the DirtyChannel default\n * scheduler), so callbacks land asynchronously after `emit()`.\n *\n * By default `watch` creates the instance if it does not exist and holds a\n * real ownership ref until `unwatch`, matching `acquire`. Pass\n * `{ create: false }` to observe passively instead — see `WatchOptions`.\n *\n * @example Single bloc\n * ```ts\n * const unwatch = watch(UserBloc, (userBloc) => {\n * console.log(userBloc.state.name);\n * });\n * ```\n *\n * @example Multiple blocs\n * ```ts\n * const unwatch = watch(\n * [UserBloc, SettingsBloc] as const,\n * ([userBloc, settingsBloc]) => {\n * console.log(userBloc.state.name, settingsBloc.state.theme);\n * }\n * );\n * ```\n *\n * @example With specific instance\n * ```ts\n * const unwatch = watch(\n * instance(UserBloc, { userId: 'user-123' }),\n * (userBloc) => {\n * console.log(userBloc.state.name);\n * }\n * );\n * ```\n *\n * @example Stop watching from callback\n * ```ts\n * const unwatch = watch(UserBloc, (userBloc) => {\n * if (userBloc.state.done) {\n * return watch.STOP;\n * }\n * });\n * ```\n */\nfunction watchImpl<T extends StateContainerConstructor>(\n bloc: T | BlocRef<T>,\n callback: (bloc: InstanceType<T>) => void | StopSymbol,\n options?: WatchOptions,\n): () => void;\n\nfunction watchImpl<T extends readonly BlocInput[]>(\n blocs: T,\n callback: (blocs: ExtractInstances<T>) => void | StopSymbol,\n options?: WatchOptions,\n): () => void;\n\nfunction watchImpl(\n blocsOrBloc: BlocInput | readonly BlocInput[],\n callback: (blocsOrBloc: any) => void | StopSymbol,\n options?: WatchOptions,\n): () => void {\n const isSingle = !isArray(blocsOrBloc);\n const inputs = isSingle ? [blocsOrBloc] : blocsOrBloc;\n const registry = getRegistry();\n const create = options?.create ?? true;\n\n const targets = inputs.map(toWatchTarget);\n const refIds = targets.map(() => `_watch_${watchRefSeq++}`);\n\n let disposed = false;\n const instances: Array<StateContainerInstance | undefined> = targets.map(\n (target, i) =>\n create ? resolveBloc(target, refIds[i]) : resolveBlocPassive(target),\n );\n const channelUnsubs: Array<(() => void) | undefined> = [];\n const disposedUnsubs: Array<(() => void) | undefined> = [];\n\n const cleanup = () => {\n if (disposed) return;\n disposed = true;\n for (const unsub of channelUnsubs) unsub?.();\n for (const unsub of disposedUnsubs) unsub?.();\n channelUnsubs.length = 0;\n disposedUnsubs.length = 0;\n if (create) {\n for (let i = 0; i < targets.length; i++) {\n registry.release(\n targets[i].blocClass,\n targets[i].key,\n false,\n refIds[i],\n );\n }\n }\n };\n\n const runCallback = () => {\n if (disposed) return;\n if (instances.some((i) => i === undefined)) return;\n const arg = isSingle ? instances[0] : instances;\n const result = callback(arg);\n if (result === STOP) cleanup();\n };\n\n const subscribeAt = (index: number) => {\n channelUnsubs[index] = instances[index]?.channel.subscribe(\n () => ALL_PATHS,\n runCallback,\n );\n };\n\n // On external dispose, tear down the stale subscription and re-acquire +\n // resubscribe (microtask-deferred, so it never runs mid-mutation of the\n // registry that triggered the dispose, e.g. `clearAll()`), then notify.\n const resubscribeAt = (index: number) => {\n if (disposed) return;\n instances[index] = create\n ? resolveBloc(targets[index], refIds[index])\n : resolveBlocPassive(targets[index]);\n subscribeAt(index);\n subscribeDisposeAt(index);\n runCallback();\n };\n\n const subscribeDisposeAt = (index: number) => {\n disposedUnsubs[index] = instances[index]?.[ON_DISPOSE](() => {\n channelUnsubs[index]?.();\n channelUnsubs[index] = undefined;\n queueMicrotask(() => resubscribeAt(index));\n });\n };\n\n for (let i = 0; i < instances.length; i++) {\n subscribeAt(i);\n }\n\n for (let i = 0; i < instances.length; i++) {\n subscribeDisposeAt(i);\n }\n\n // Fire once immediately so the consumer sees the current state.\n runCallback();\n\n return cleanup;\n}\n\nexport const watch: WatchFn = Object.assign(watchImpl, { STOP }) as WatchFn;\n","declare const brand: unique symbol;\n\n/**\n * Utility type for creating branded/nominal types.\n * Prevents accidental type confusion between similar primitive types.\n * @typeParam T - The base type\n * @typeParam B - The brand identifier\n */\nexport type Brand<T, B> = T & { [brand]: B };\n\n/**\n * Branded string type for type-safe IDs.\n * @typeParam B - The brand identifier\n */\nexport type BrandedId<B> = Brand<string, B>;\n\n/**\n * Branded string type for state container instance IDs\n */\nexport type InstanceId = Brand<string, 'InstanceId'>;\n\n/**\n * Create a branded InstanceId from a string\n * @param id - The string ID to brand\n * @returns Branded InstanceId\n */\nexport function instanceId(id: string): InstanceId {\n return id as InstanceId;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["ALL_PATHS"],"sources":["../src/core/Cubit.ts","../src/registry/borrow.ts","../src/registry/release.ts","../src/registry/queries.ts","../src/registry/management.ts","../src/decorators/blac.ts","../src/watch/watch.ts","../src/types/branded.ts"],"sourcesContent":["import { StateContainer } from './StateContainer';\nimport type { DeepPartial } from '@dirtytalk/structural';\n\n/**\n * `Cubit<S>` is a `StateContainer<S>` that exposes mutation publicly.\n *\n * That is the whole difference, and it is a real one: on `StateContainer`\n * `emit` / `patch` / `update` are `protected`, so a container mutates itself\n * from its own methods and callers go through the API it chooses to publish.\n * `Cubit` re-declares the three as `public` for the cases where a caller\n * legitimately drives state from outside — test helpers, devtools\n * time-travel, benchmarks.\n *\n * Reach for `StateContainer` when business logic should live in the class,\n * and `Cubit` when the caller owns the transitions.\n */\nexport abstract class Cubit<\n S extends object = any,\n Args = void,\n Deps extends object = Record<string, never>,\n> extends StateContainer<S, Args, Deps> {\n override emit(next: S): void {\n super.emit(next);\n }\n\n override patch(partial: DeepPartial<S>): void {\n super.patch(partial);\n }\n\n override update(fn: (state: S) => S): void {\n super.update(fn);\n }\n}\n","import { getRegistry } from './config';\nimport type {\n ExtractArgs,\n StateContainerConstructor,\n} from '../types/utilities';\n\n/**\n * How to identify the instance to borrow: the same `args` shape `useBloc`\n * accepts, resolved through the class's `static key(args)` so call sites use\n * the *same* shape they pass to `useBloc` instead of hand-deriving the key.\n *\n * `borrowSafe(ChannelBloc, { args: { channelId } })` and\n * `useBloc(ChannelBloc, { args: { channelId } })` therefore always agree.\n */\nexport type BorrowTarget<T extends StateContainerConstructor> = {\n args?: ExtractArgs<T>;\n};\n\nfunction resolveBorrowKey<T extends StateContainerConstructor>(\n BlocClass: T,\n target: BorrowTarget<T> | undefined,\n): string {\n return getRegistry().resolveKey(BlocClass, undefined, target?.args);\n}\n\nexport function borrow<T extends StateContainerConstructor>(\n BlocClass: T,\n target?: BorrowTarget<T>,\n): InstanceType<T> {\n return getRegistry().borrow(BlocClass, resolveBorrowKey(BlocClass, target));\n}\n\nexport function borrowSafe<T extends StateContainerConstructor>(\n BlocClass: T,\n target?: BorrowTarget<T>,\n):\n | { error: Error; instance: null }\n | { error: null; instance: InstanceType<T> } {\n return getRegistry().borrowSafe(\n BlocClass,\n resolveBorrowKey(BlocClass, target),\n );\n}\n","import { getRegistry } from './config';\nimport type {\n ExtractArgs,\n StateContainerConstructor,\n} from '../types/utilities';\n\n/**\n * Release a reference to an instance. Instance identity is derived purely from\n * `args` (must match the `args` it was acquired with).\n *\n * @param opts - `args` derives the instance key (must match acquire);\n * `refId` picks the ref to drop, otherwise one arbitrary ref goes;\n * `forceDispose` disposes immediately regardless of remaining refs.\n */\nexport function release<T extends StateContainerConstructor>(\n BlocClass: T,\n opts?: { args?: ExtractArgs<T>; refId?: string; forceDispose?: boolean },\n): void {\n const registry = getRegistry();\n const key = registry.resolveKey(BlocClass, undefined, opts?.args);\n registry.release(BlocClass, key, opts?.forceDispose ?? false, opts?.refId);\n}\n","import { getRegistry } from './config';\nimport type {\n ExtractArgs,\n StateContainerConstructor,\n InstanceReadonlyState,\n} from '../types/utilities';\n\n/**\n * @public\n */\nexport function hasInstance<T extends StateContainerConstructor>(\n BlocClass: T,\n opts?: { args?: ExtractArgs<T> },\n): boolean {\n const registry = getRegistry();\n const key = registry.resolveKey(BlocClass, undefined, opts?.args);\n return registry.hasInstance(BlocClass, key);\n}\n\n/**\n * @public\n */\nexport function getRefCount<T extends StateContainerConstructor>(\n BlocClass: T,\n opts?: { args?: ExtractArgs<T> },\n): number {\n const registry = getRegistry();\n const key = registry.resolveKey(BlocClass, undefined, opts?.args);\n return registry.getRefCount(BlocClass, key);\n}\n\n/**\n * @public\n */\nexport function getRefIds<T extends StateContainerConstructor>(\n BlocClass: T,\n opts?: { args?: ExtractArgs<T> },\n): string[] {\n const registry = getRegistry();\n const key = registry.resolveKey(BlocClass, undefined, opts?.args);\n return registry.getRefIds(BlocClass, key);\n}\n\n/**\n * @public\n */\nexport function getAll<T extends StateContainerConstructor>(\n BlocClass: T,\n): InstanceReadonlyState<T>[] {\n return getRegistry().getAll(BlocClass);\n}\n\n/**\n * @public\n */\nexport function forEach<T extends StateContainerConstructor>(\n BlocClass: T,\n callback: (instance: InstanceReadonlyState<T>) => void,\n): void {\n getRegistry().forEach(BlocClass, callback);\n}\n","import { getRegistry } from './config';\nimport type { StateContainerConstructor } from '../types/utilities';\n\nexport function clear<T extends StateContainerConstructor>(BlocClass: T): void {\n getRegistry().clear(BlocClass);\n}\n\nexport function clearAll(): void {\n getRegistry().clearAll();\n}\n\n/**\n * @public\n */\nexport function register<T extends StateContainerConstructor>(\n BlocClass: T,\n): void {\n getRegistry().register(BlocClass);\n}\n","import { BLAC_STATIC_PROPS } from '../constants';\nimport type { EqualityFn } from '../config';\n\n/**\n * Configuration options for the `@blac` decorator. Any combination may be\n * given at once.\n */\nexport interface BlacOptions {\n /**\n * Keep the bloc alive when its ref count reaches 0. `false` turns off a\n * `keepAlive` inherited from a base class.\n */\n keepAlive?: boolean;\n /** Exclude bloc from DevTools tracking (prevents infinite loops) */\n excludeFromDevTools?: true;\n /**\n * Override the global equality check for this bloc.\n * Return `true` to skip the emit (states considered equal).\n */\n equality?: EqualityFn;\n /**\n * Override the default structural-hash identity key.\n * The function receives `args` and returns the instance key string.\n * Distinct return values → distinct instances; identical values → shared instance.\n * Non-identity fields can be excluded so they don't fork new instances.\n */\n key?: (args: any) => string;\n /**\n * Explicit, minification-safe identity for this bloc class. Falls back to\n * `constructor.name` when unset, which collides across classes once a\n * minifier shortens class names.\n */\n name?: string;\n}\n\n/**\n * Decorator to configure StateContainer classes.\n *\n * @example Decorator syntax (requires experimentalDecorators or TC39 decorators)\n * ```ts\n * @blac({ keepAlive: true })\n * class AuthBloc extends Cubit<AuthState> {}\n *\n * @blac({ excludeFromDevTools: true })\n * class InternalBloc extends Cubit<InternalState> {}\n *\n * @blac({ keepAlive: true, key: (args) => args.userId })\n * class UserBloc extends Cubit<UserState> {}\n * ```\n *\n * @example Function syntax (no decorator support needed)\n * ```ts\n * const AuthBloc = blac({ keepAlive: true })(\n * class extends Cubit<AuthState> {}\n * );\n * ```\n */\nexport function blac(options: BlacOptions) {\n return function <T extends new (...args: any[]) => any>(\n target: T,\n _context?: ClassDecoratorContext,\n ): T {\n if (options.keepAlive !== undefined) {\n (target as any)[BLAC_STATIC_PROPS.KEEP_ALIVE] = options.keepAlive;\n }\n if (options.excludeFromDevTools) {\n (target as any)[BLAC_STATIC_PROPS.EXCLUDE_FROM_DEVTOOLS] = true;\n }\n if (typeof options.equality === 'function') {\n (target as any)[BLAC_STATIC_PROPS.EQUALITY] = options.equality;\n }\n if (typeof options.key === 'function') {\n (target as any)[BLAC_STATIC_PROPS.KEY] = options.key;\n }\n if (typeof options.name === 'string') {\n (target as any)[BLAC_STATIC_PROPS.BLAC_NAME] = options.name;\n }\n return target;\n };\n}\n","import { ALL_PATHS } from '@dirtytalk/structural';\nimport { ON_DISPOSE } from '../core/symbols';\nimport { getRegistry } from '../registry';\nimport { resolveInstanceKey } from '../registry/acquire';\nimport type { StateContainerRegistry } from '../core/StateContainerRegistry';\nimport type {\n ExtractArgs,\n StateContainerConstructor,\n StateContainerInstance,\n} from '../types/utilities';\n\nconst STOP: unique symbol = Symbol('watch.STOP');\ntype StopSymbol = typeof STOP;\n\nconst BLOC_REF_MARKER = Symbol('BlocRef');\n\n/**\n * Reference to a specific bloc instance, identified by class + the resolved\n * key derived from its `args`.\n */\nexport interface BlocRef<T extends StateContainerConstructor> {\n [BLOC_REF_MARKER]: true;\n blocClass: T;\n /** @internal The resolved instance key (derived from `args`). */\n instanceId: string;\n /**\n * @internal The `args` used to derive `instanceId`. Forwarded to\n * `registry.acquire` so a not-yet-created instance is initialized with\n * these args instead of `undefined`.\n */\n args?: unknown;\n}\n\n/**\n * Create a reference to a specific bloc instance, identified by its `args`.\n * The key is derived the same way `useBloc`/`acquire` derive it.\n *\n * @example\n * ```ts\n * watch(instance(UserBloc, { userId: 'user-123' }), (userBloc) => {\n * console.log(userBloc.state.name);\n * });\n * ```\n */\nexport function instance<T extends StateContainerConstructor>(\n BlocClass: T,\n args?: ExtractArgs<T>,\n): BlocRef<T> {\n return {\n [BLOC_REF_MARKER]: true,\n blocClass: BlocClass,\n instanceId: resolveInstanceKey(BlocClass, args),\n args,\n };\n}\n\nfunction isBlocRef(\n input: unknown,\n): input is BlocRef<StateContainerConstructor> {\n return (\n typeof input === 'object' && input !== null && BLOC_REF_MARKER in input\n );\n}\n\ntype BlocInput = StateContainerConstructor | BlocRef<StateContainerConstructor>;\n\n/**\n * Options for `watch`.\n */\nexport interface WatchOptions {\n /**\n * When `false`, `watch` observes passively: it neither creates a missing\n * instance nor takes a real ownership ref on an existing one. If the\n * instance does not exist yet, the callback simply never fires — `watch`\n * does not poll; call it again once the instance exists.\n *\n * Defaults to `true`.\n */\n create?: boolean;\n}\n\ntype ExtractInstance<T> =\n T extends BlocRef<infer C>\n ? InstanceType<C>\n : T extends StateContainerConstructor\n ? InstanceType<T>\n : never;\n\ntype ExtractInstances<T extends readonly BlocInput[]> = {\n [K in keyof T]: ExtractInstance<T[K]>;\n};\n\n/**\n * Watch function signature for single bloc.\n */\nexport interface WatchSingleFn {\n <T extends StateContainerConstructor>(\n bloc: T | BlocRef<T>,\n callback: (bloc: InstanceType<T>) => void | StopSymbol,\n options?: WatchOptions,\n ): () => void;\n\n STOP: StopSymbol;\n}\n\n/**\n * Watch function signature for multiple blocs.\n */\nexport interface WatchMultipleFn {\n <T extends readonly BlocInput[]>(\n blocs: T,\n callback: (blocs: ExtractInstances<T>) => void | StopSymbol,\n options?: WatchOptions,\n ): () => void;\n\n STOP: StopSymbol;\n}\n\n/**\n * Combined watch function type.\n */\nexport interface WatchFn extends WatchSingleFn {\n <T extends readonly BlocInput[]>(\n blocs: T,\n callback: (blocs: ExtractInstances<T>) => void | StopSymbol,\n options?: WatchOptions,\n ): () => void;\n}\n\n/**\n * Class + resolved key + args needed to (re-)acquire a specific instance.\n * `watch` keeps this around per input so it can re-acquire the same logical\n * instance if the underlying container is disposed elsewhere.\n */\ninterface WatchTarget {\n blocClass: StateContainerConstructor;\n key: string;\n args: unknown;\n}\n\nfunction toWatchTarget(\n registry: StateContainerRegistry,\n input: BlocInput,\n): WatchTarget {\n if (isBlocRef(input)) {\n return {\n blocClass: input.blocClass,\n key: input.instanceId,\n args: input.args,\n };\n }\n return {\n blocClass: input,\n key: registry.resolveKey(input, undefined, undefined),\n args: undefined,\n };\n}\n\nlet watchRefSeq = 0;\n\n/**\n * Acquire a real ref (countRef: true) for the target, carrying its `args`\n * through so a not-yet-created instance is initialized correctly. The\n * caller is responsible for releasing `refId` in cleanup.\n */\nfunction resolveBloc(\n registry: StateContainerRegistry,\n target: WatchTarget,\n refId: string,\n): StateContainerInstance {\n return registry.acquire(target.blocClass, target.key, {\n countRef: true,\n refId,\n args: target.args,\n });\n}\n\n/**\n * Passive lookup for `{ create: false }`: never creates the instance and\n * never takes a ref. Returns `undefined` when no instance currently exists.\n */\nfunction resolveBlocPassive(\n registry: StateContainerRegistry,\n target: WatchTarget,\n): StateContainerInstance | undefined {\n try {\n return registry.acquire(target.blocClass, target.key, {\n canCreate: false,\n countRef: false,\n args: target.args,\n });\n } catch {\n return undefined;\n }\n}\n\nfunction isArray(input: unknown): input is readonly BlocInput[] {\n return Array.isArray(input);\n}\n\n/**\n * Watch one or more blocs for state changes.\n *\n * Thin wrapper around `container.channel.subscribe(ALL_PATHS, ...)`. The\n * callback fires once immediately, then on every state change of any of the\n * passed blocs. Returning `watch.STOP` from the callback tears down all\n * subscriptions. Subscriptions are microtask-deferred, so callbacks land\n * asynchronously after `emit()`.\n *\n * By default `watch` creates the instance if it does not exist and holds a\n * real ownership ref until `unwatch`, matching `acquire`. Pass\n * `{ create: false }` to observe passively instead — see `WatchOptions`.\n *\n * @example Single bloc\n * ```ts\n * const unwatch = watch(UserBloc, (userBloc) => {\n * console.log(userBloc.state.name);\n * });\n * ```\n *\n * @example Multiple blocs\n * ```ts\n * const unwatch = watch(\n * [UserBloc, SettingsBloc] as const,\n * ([userBloc, settingsBloc]) => {\n * console.log(userBloc.state.name, settingsBloc.state.theme);\n * }\n * );\n * ```\n *\n * @example With specific instance\n * ```ts\n * const unwatch = watch(\n * instance(UserBloc, { userId: 'user-123' }),\n * (userBloc) => {\n * console.log(userBloc.state.name);\n * }\n * );\n * ```\n *\n * @example Stop watching from callback\n * ```ts\n * const unwatch = watch(UserBloc, (userBloc) => {\n * if (userBloc.state.done) {\n * return watch.STOP;\n * }\n * });\n * ```\n */\nfunction watchImpl<T extends StateContainerConstructor>(\n bloc: T | BlocRef<T>,\n callback: (bloc: InstanceType<T>) => void | StopSymbol,\n options?: WatchOptions,\n): () => void;\n\nfunction watchImpl<T extends readonly BlocInput[]>(\n blocs: T,\n callback: (blocs: ExtractInstances<T>) => void | StopSymbol,\n options?: WatchOptions,\n): () => void;\n\nfunction watchImpl(\n blocsOrBloc: BlocInput | readonly BlocInput[],\n callback: (blocsOrBloc: any) => void | StopSymbol,\n options?: WatchOptions,\n): () => void {\n const isSingle = !isArray(blocsOrBloc);\n const inputs = isSingle ? [blocsOrBloc] : blocsOrBloc;\n const registry = getRegistry();\n const create = options?.create ?? true;\n\n const targets = inputs.map((input) => toWatchTarget(registry, input));\n const refIds = targets.map(() => `_watch_${watchRefSeq++}`);\n const resolveAt = (index: number) =>\n create\n ? resolveBloc(registry, targets[index], refIds[index])\n : resolveBlocPassive(registry, targets[index]);\n\n let disposed = false;\n const instances: Array<StateContainerInstance | undefined> = [];\n const channelUnsubs: Array<(() => void) | undefined> = [];\n const disposedUnsubs: Array<(() => void) | undefined> = [];\n\n const cleanup = () => {\n if (disposed) return;\n disposed = true;\n for (const unsub of channelUnsubs) unsub?.();\n for (const unsub of disposedUnsubs) unsub?.();\n channelUnsubs.length = 0;\n disposedUnsubs.length = 0;\n if (create) {\n for (let i = 0; i < instances.length; i++) {\n registry.release(\n targets[i].blocClass,\n targets[i].key,\n false,\n refIds[i],\n );\n }\n }\n };\n\n const runCallback = () => {\n if (disposed) return;\n if (instances.some((i) => i === undefined)) return;\n const arg = isSingle ? instances[0] : instances;\n const result = callback(arg);\n if (result === STOP) cleanup();\n };\n\n const subscribeAt = (index: number) => {\n channelUnsubs[index] = instances[index]?.channel.subscribe(\n () => ALL_PATHS,\n runCallback,\n );\n };\n\n // On external dispose, tear down the stale subscription and re-acquire +\n // resubscribe (microtask-deferred, so it never runs mid-mutation of the\n // registry that triggered the dispose, e.g. `clearAll()`), then notify.\n const resubscribeAt = (index: number) => {\n if (disposed) return;\n instances[index] = resolveAt(index);\n subscribeAt(index);\n subscribeDisposeAt(index);\n runCallback();\n };\n\n const subscribeDisposeAt = (index: number) => {\n disposedUnsubs[index] = instances[index]?.[ON_DISPOSE](() => {\n channelUnsubs[index]?.();\n channelUnsubs[index] = undefined;\n queueMicrotask(() => resubscribeAt(index));\n });\n };\n\n try {\n for (let i = 0; i < targets.length; i++) instances.push(resolveAt(i));\n for (let i = 0; i < instances.length; i++) subscribeAt(i);\n for (let i = 0; i < instances.length; i++) subscribeDisposeAt(i);\n // Fire once immediately so the consumer sees the current state.\n runCallback();\n } catch (error) {\n cleanup();\n throw error;\n }\n\n return cleanup;\n}\n\nexport const watch: WatchFn = Object.assign(watchImpl, { STOP }) as WatchFn;\n","declare const brand: unique symbol;\n\n/**\n * Nominal type helper, preventing accidental confusion between similar\n * primitive types.\n * @typeParam T - The base type\n * @typeParam B - The brand identifier\n */\nexport type Brand<T, B> = T & { [brand]: B };\n\n/**\n * Branded string type for type-safe IDs.\n * @typeParam B - The brand identifier\n */\nexport type BrandedId<B> = Brand<string, B>;\n\n/** Branded string type for state container instance IDs. */\nexport type InstanceId = Brand<string, 'InstanceId'>;\n\nexport function instanceId(id: string): InstanceId {\n return id as InstanceId;\n}\n"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_StateContainerRegistry = require("./StateContainerRegistry-DjQQtlCW.cjs");
3
+ const require_StateContainer = require("./StateContainer-BUyuxKb3.cjs");
4
+ exports.APPLY_DEPS = require_StateContainerRegistry.APPLY_DEPS;
5
+ exports.DEP_BRAND = require_StateContainer.DEP_BRAND;
6
+ exports.INIT_CONFIG = require_StateContainerRegistry.INIT_CONFIG;
7
+ exports.INSERT_INSTANCE = require_StateContainerRegistry.INSERT_INSTANCE;
8
+ exports.ON_DISPOSE = require_StateContainerRegistry.ON_DISPOSE;
9
+ exports.REMOVE_DEPS_OWNER = require_StateContainerRegistry.REMOVE_DEPS_OWNER;
10
+ exports.WITH_TRACKED_STATE = require_StateContainerRegistry.WITH_TRACKED_STATE;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Framework hooks used by `@blac/react` and the testing helpers. Not public
3
+ * API; may change in any release.
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+ export { DEP_BRAND } from './core/StateContainer';
8
+ export { APPLY_DEPS, REMOVE_DEPS_OWNER, INIT_CONFIG, INSERT_INSTANCE, ON_DISPOSE, WITH_TRACKED_STATE, } from './core/symbols';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Framework hooks used by `@blac/react` and the testing helpers. Not public
3
+ * API; may change in any release.
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+ export { DEP_BRAND } from './core/StateContainer';
8
+ export { APPLY_DEPS, REMOVE_DEPS_OWNER, INIT_CONFIG, INSERT_INSTANCE, ON_DISPOSE, WITH_TRACKED_STATE, } from './core/symbols';
@@ -0,0 +1,3 @@
1
+ import { a as INSERT_INSTANCE, i as INIT_CONFIG, l as WITH_TRACKED_STATE, o as ON_DISPOSE, r as APPLY_DEPS, s as REMOVE_DEPS_OWNER } from "./StateContainerRegistry-gQbEutkN.js";
2
+ import { t as DEP_BRAND } from "./StateContainer-Bh2ipNYj.js";
3
+ export { APPLY_DEPS, DEP_BRAND, INIT_CONFIG, INSERT_INSTANCE, ON_DISPOSE, REMOVE_DEPS_OWNER, WITH_TRACKED_STATE };
@@ -17,15 +17,10 @@ export interface InstanceMetadata {
17
17
  args?: unknown;
18
18
  }
19
19
  /**
20
- * Per-event context delivered to plugin hooks.
21
- *
22
- * `PluginContext` is per-container — it identifies the bloc, not the event.
23
- * Per-event data (e.g. the changed `PathSet`) is passed via dedicated method
24
- * arguments, never via the context object.
25
- *
26
- * The `container` field is the focal bloc for the event. For `onInstall`
27
- * (which fires once at install time, not bound to any container) the
28
- * `container` field is `undefined`.
20
+ * Per-event context delivered to plugin hooks. Identifies the focal
21
+ * container for the event (`undefined` only for `onInstall`, which fires
22
+ * once at install time before any container is known); per-event data (e.g.
23
+ * the changed `PathSet`) is passed via method arguments, not this object.
29
24
  */
30
25
  export interface PluginContext {
31
26
  /**
@@ -53,21 +48,10 @@ export interface PluginContext {
53
48
  /**
54
49
  * BlaC plugin hook surface.
55
50
  *
56
- * State-change events carry the `PathSet` of paths that changed in the flush.
57
- * Lifecycle events (`onCreated`, `onDestroyed`) are not tied to a state change
58
- * and so do not carry `paths`.
59
- *
60
- * Hook firing model:
61
- * - `onCreated` fires synchronously when a container is first acquired.
62
- * - `onStateChange` fires once per channel flush (microtask-coalesced).
63
- * `prev` is the state before the first emit of the flush; `next` is the
64
- * state at flush time; `paths` is the set of paths marked during the flush.
65
- * - `onDestroyed` fires synchronously when a container is disposed (after
66
- * its system 'dispose' event).
67
- *
68
- * The ref/deps hooks (`onRefAcquired`, `onRefReleased`, `onDepsChanged`) are
69
- * devtools-only — they drive devtools-connect and are orthogonal to the
70
- * state-change event payload.
51
+ * `onStateChange` fires once per channel flush (microtask-coalesced) with
52
+ * the changed `PathSet`; lifecycle hooks (`onCreated`, `onDestroyed`) fire
53
+ * synchronously and carry no `paths`. The ref/deps hooks (`onRefAcquired`,
54
+ * `onRefReleased`, `onDepsChanged`) are devtools-only.
71
55
  */
72
56
  export interface BlacPlugin {
73
57
  readonly name: string;
@@ -3,25 +3,16 @@ import type { BlacPlugin, PluginConfig } from './BlacPlugin';
3
3
  /**
4
4
  * Manages plugin lifecycle for the BlaC state management system.
5
5
  *
6
- * The manager hooks into two surfaces:
6
+ * Hooks into registry lifecycle events (synchronous) and per-container
7
+ * channel flushes (microtask-coalesced): while at least one installed
8
+ * plugin implements `onStateChange`, the manager subscribes each container
9
+ * with `ALL_PATHS` interest and dispatches
10
+ * `onStateChange(ctx, prev, next, paths)` on every flush.
7
11
  *
8
- * 1. **Registry lifecycle events** (`created`, `disposed`, `refAcquired`,
9
- * `refReleased`, `depsChanged`, `activated`, `deactivated`) — synchronous,
10
- * fired by the registry.
11
- *
12
- * 2. **Per-container channel flushes** — microtask-coalesced. For each
13
- * container, the manager subscribes once at create-time with
14
- * `ALL_PATHS` interest and stashes `prevState`. On every flush it
15
- * captures the new state + the changed `PathSet` and dispatches
16
- * `onStateChange(ctx, prev, next, paths)` to every enabled plugin.
17
- *
18
- * The `ALL_PATHS` subscription cost: plugins counted as a consumer with
19
- * `ALL_PATHS` interest will defeat the single-consumer-skip optimization
20
- * in `StructuralContainer`. This is the intended trade-off — devtools /
21
- * persist plugins genuinely want every change, and stateful plugins
22
- * (logging) can decode `paths` via `container.interner.lookup(id)` to log
23
- * only relevant fields. Plugins that want low overhead should remain
24
- * uninstalled or environment-gated.
12
+ * `ALL_PATHS` interest defeats the single-consumer-skip optimization in
13
+ * `StructuralContainer` — an intended trade-off for plugins that genuinely
14
+ * want every change (devtools/persist). Plugins that want low overhead
15
+ * should remain uninstalled or environment-gated.
25
16
  *
26
17
  * @example
27
18
  * ```ts
@@ -34,19 +25,18 @@ export declare class PluginManager {
34
25
  private registry;
35
26
  private lifecycleUnsubscribers;
36
27
  /**
37
- * Per-container channel-bridge bookkeeping. Subscribed at `created`,
38
- * torn down at `disposed` and at `destroy()`. Holds the rolling `prevState`
39
- * snapshot the manager hands plugins on each flush.
28
+ * Per-container channel-bridge bookkeeping, subscribed at `created` and
29
+ * torn down at `disposed`/`destroy()`. Holds the rolling `prevState`
30
+ * snapshot handed to plugins on each flush.
40
31
  *
41
- * Strong, not a WeakMap, because `destroy()` has to enumerate the live
42
- * bridges to unsubscribe them. Safe: entries are removed on `disposed`, and
43
- * the owning registry holds its own strong reference over that same window.
32
+ * Strong, not a `WeakMap`, because `destroy()` must enumerate live bridges
33
+ * to unsubscribe them; entries are removed on `disposed`, so this never
34
+ * outlives the registry's own reference.
44
35
  */
45
36
  private containerBridges;
46
37
  /**
47
- * One `PluginContext` per container. The context closes over `registry` and
48
- * `container` only — both stable for the container's lifetime — so it is
49
- * safe to reuse instead of rebuilding its method table on every dispatch.
38
+ * One `PluginContext` per container, safe to reuse since it closes only
39
+ * over `registry`/`container`, both stable for the container's lifetime.
50
40
  */
51
41
  private contextCache;
52
42
  /**
@@ -92,59 +82,46 @@ export declare class PluginManager {
92
82
  /**
93
83
  * Wire registry lifecycle events into plugin dispatch.
94
84
  *
95
- * `onStateChange` is NOT wired through `registry.on('stateChanged', …)` —
96
- * that event lacks the `PathSet` payload. Instead, on each `created` we
97
- * subscribe to the container's channel directly so we get
98
- * `(paths)` and capture `(prev, next)` via the per-container snapshot.
85
+ * `onStateChange` is not wired through `registry.on('stateChanged', …)` —
86
+ * that event lacks the `PathSet` payload. Instead each `created` subscribes
87
+ * to the container's channel directly for `paths`, pairing it with
88
+ * `(prev, next)` from the per-container snapshot.
99
89
  */
100
90
  private setupLifecycleHooks;
101
91
  /**
102
- * Subscribe to the container's channel with `ALL_PATHS` interest, so we
103
- * fire on every flush. Capture `prev` from the snapshot taken on the
104
- * previous flush (or at create-time for the first flush) and pass the
105
- * channel's `paths` argument straight through to plugins.
106
- *
107
- * Per-container bookkeeping is stored in `containerBridges`, a strong `Map`
108
- * keyed by the container. Entries are removed explicitly — on the
109
- * container's `disposed` event, or for all of them in `destroy()`.
92
+ * Subscribe to the container's channel with `ALL_PATHS` interest so it
93
+ * fires on every flush. `prev` is the snapshot from the previous flush (or
94
+ * create-time for the first); the channel's `paths` is passed straight
95
+ * through to plugins.
110
96
  */
111
97
  private attachStateBridge;
112
98
  private detachStateBridge;
99
+ private hasStateChangePlugin;
100
+ private detachAllStateBridges;
113
101
  /**
114
- * Backfill a newly-installed plugin with the app's existing instances.
115
- *
116
- * Iterates every registered `Type` and its live instances (`getAll`
117
- * already skips disposed), attaching the state bridge (idempotent — see
118
- * `attachStateBridge`) and, if the plugin implements `onCreated`, invoking
119
- * it directly for each instance. Scoped to this single plugin only — the
120
- * broadcast `notifyPlugins` would re-notify *every* plugin of *every*
121
- * instance, which is wrong here.
102
+ * Backfill a newly-installed plugin with existing instances: attach the
103
+ * state bridge (idempotent) if it implements `onStateChange`, and invoke
104
+ * `onCreated` for each. Scoped to this plugin only — `notifyPlugins` would
105
+ * wrongly re-notify every plugin of every instance.
122
106
  */
123
107
  private backfillPlugin;
124
108
  /**
125
- * Channel-flush callback. Snapshots `next`, hands `(prev, next, paths)`
126
- * to every enabled plugin's `onStateChange`, then updates `prevState`
127
- * for the next flush.
128
- *
129
- * Note: `prev` is captured once and reused across plugins — every
130
- * plugin sees the same `prev`/`next`/`paths` regardless of dispatch
131
- * order, matching the "snapshot prev once per flush" invariant.
109
+ * Channel-flush callback. Hands `(prev, next, paths)` to every enabled
110
+ * plugin's `onStateChange`, then updates `prevState` for the next flush.
111
+ * `prev` is captured once and reused across plugins, so every plugin sees
112
+ * the same values regardless of dispatch order.
132
113
  */
133
114
  private dispatchStateChange;
134
115
  /**
135
- * Notify all plugins of a lifecycle event.
136
- *
137
- * Builds a fresh `PluginContext` per dispatch (instance becomes
138
- * `ctx.container`), so plugins can reach the focal bloc through
139
- * `ctx.container`. The context is built lazily on the first matching
140
- * hook and reused for every other plugin in this dispatch — zero
141
- * builds if no enabled plugin implements `hookName`.
116
+ * Notify all plugins of a lifecycle event. Builds a `PluginContext` lazily
117
+ * on the first matching hook and reuses it for the rest of this dispatch —
118
+ * zero builds if no enabled plugin implements `hookName`.
142
119
  */
143
120
  private notifyPlugins;
144
121
  /**
145
122
  * Get the `PluginContext` for a focal container, building it once and
146
- * reusing it thereafter. Eviction is handled by the `WeakMap`: the context
147
- * dies with the container. `undefined` (install-time) is never cached.
123
+ * reusing it thereafter (evicted via the `WeakMap` when the container
124
+ * dies). `undefined` (install-time) is never cached.
148
125
  */
149
126
  private buildContext;
150
127
  private createContext;
@@ -153,7 +130,7 @@ export declare class PluginManager {
153
130
  */
154
131
  private shouldEnablePlugin;
155
132
  /**
156
- * Get current environment
133
+ * Same rule as `IS_DEV`: an unknown environment counts as production.
157
134
  */
158
135
  private getCurrentEnvironment;
159
136
  }