@blac/react 2.1.3 → 2.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) Facebook, Inc. and its affiliates.
3
+ Copyright (c) 2025 Brendan Mullins
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  React bindings for BlaC — `useBloc` hook with proxy-based automatic re-render optimization.
4
4
 
5
- **[Documentation](https://blac-docs.pages.dev/react/getting-started)** · **[npm](https://www.npmjs.com/package/@blac/react)**
5
+ **[Documentation](https://blac.42.dog/react/getting-started)** · **[npm](https://www.npmjs.com/package/@blac/react)**
6
6
 
7
7
  > [!WARNING]
8
8
  > **BlaC v2 is in pre-release (beta).** While in beta, **breaking API changes may
@@ -343,7 +343,7 @@ class UserCardCubit extends Cubit<UserCardState, { userId: string }> {
343
343
  import { renderWithBloc } from '@blac/react/testing';
344
344
  ```
345
345
 
346
- See the [testing docs](https://blac-docs.pages.dev/testing/react) for details.
346
+ See the [testing docs](https://blac.42.dog/testing/react) for details.
347
347
 
348
348
  ## License
349
349
 
@@ -14,6 +14,7 @@ export declare class DepSession {
14
14
  private readonly consumerId;
15
15
  private readonly onChange;
16
16
  private primary;
17
+ private primaryParts;
17
18
  private primaryPaths;
18
19
  private entries;
19
20
  private subs;
@@ -26,6 +27,11 @@ export declare class DepSession {
26
27
  private get refId();
27
28
  begin(primary: StateContainer, paths: PathSet): void;
28
29
  record(dep: StateContainer, paths: PathSet, Type: StateContainerConstructor, key: string, args: unknown, disarm: () => void): void;
30
+ /**
31
+ * Merge each container's per-call sets once the render's reads are done.
32
+ * Returns the primary's paths, widened by deps that resolved to it.
33
+ */
34
+ settle(): PathSet;
29
35
  /** Stops every dep tracked this render from recording further reads. */
30
36
  disarm(): void;
31
37
  /** Forces the next `reconcile` to run in full. */
@@ -14,6 +14,7 @@ export declare class DepSession {
14
14
  private readonly consumerId;
15
15
  private readonly onChange;
16
16
  private primary;
17
+ private primaryParts;
17
18
  private primaryPaths;
18
19
  private entries;
19
20
  private subs;
@@ -26,6 +27,11 @@ export declare class DepSession {
26
27
  private get refId();
27
28
  begin(primary: StateContainer, paths: PathSet): void;
28
29
  record(dep: StateContainer, paths: PathSet, Type: StateContainerConstructor, key: string, args: unknown, disarm: () => void): void;
30
+ /**
31
+ * Merge each container's per-call sets once the render's reads are done.
32
+ * Returns the primary's paths, widened by deps that resolved to it.
33
+ */
34
+ settle(): PathSet;
29
35
  /** Stops every dep tracked this render from recording further reads. */
30
36
  disarm(): void;
31
37
  /** Forces the next `reconcile` to run in full. */
package/dist/index.cjs CHANGED
@@ -153,6 +153,7 @@ var DepSession = class {
153
153
  this.consumerId = consumerId;
154
154
  this.onChange = onChange;
155
155
  this.primary = null;
156
+ this.primaryParts = [];
156
157
  this.primaryPaths = NO_PATHS$1;
157
158
  this.entries = null;
158
159
  this.subs = null;
@@ -169,24 +170,34 @@ var DepSession = class {
169
170
  this.entries?.clear();
170
171
  if (this.disarms !== null) this.disarms.length = 0;
171
172
  this.primary = primary;
172
- this.primaryPaths = paths;
173
+ this.primaryParts = [paths];
173
174
  }
174
175
  record(dep, paths, Type, key, args, disarm) {
175
176
  (this.disarms ??= []).push(disarm);
176
177
  if (dep === this.primary) {
177
- this.primaryPaths = unionPaths(this.primaryPaths, paths);
178
+ this.primaryParts.push(paths);
178
179
  return;
179
180
  }
180
181
  const entries = this.entries ??= /* @__PURE__ */ new Map();
181
182
  const existing = entries.get(dep);
182
- if (existing !== void 0) existing.paths = unionPaths(existing.paths, paths);
183
+ if (existing !== void 0) existing.parts.push(paths);
183
184
  else entries.set(dep, {
185
+ parts: [paths],
184
186
  paths,
185
187
  Type,
186
188
  key,
187
189
  args
188
190
  });
189
191
  }
192
+ /**
193
+ * Merge each container's per-call sets once the render's reads are done.
194
+ * Returns the primary's paths, widened by deps that resolved to it.
195
+ */
196
+ settle() {
197
+ this.primaryPaths = mergeParts(this.primaryParts);
198
+ if (this.entries !== null) for (const entry of this.entries.values()) entry.paths = mergeParts(entry.parts);
199
+ return this.primaryPaths;
200
+ }
190
201
  /** Stops every dep tracked this render from recording further reads. */
191
202
  disarm() {
192
203
  const disarms = this.disarms;
@@ -274,10 +285,13 @@ var DepSession = class {
274
285
  function sameEntry(a, b) {
275
286
  return (0, _dirtytalk_structural.pathSetEquals)(a.paths, b.paths) && a.key === b.key && Object.is(a.args, b.args);
276
287
  }
277
- function unionPaths(a, b) {
278
- if (a === _dirtytalk_structural.ALL_PATHS || b === _dirtytalk_structural.ALL_PATHS) return _dirtytalk_structural.ALL_PATHS;
279
- const out = new Set(a);
280
- for (const id of b) out.add(id);
288
+ function mergeParts(parts) {
289
+ if (parts.length === 1) return parts[0];
290
+ const out = /* @__PURE__ */ new Set();
291
+ for (const part of parts) {
292
+ if (part === _dirtytalk_structural.ALL_PATHS) return _dirtytalk_structural.ALL_PATHS;
293
+ for (const id of part) out.add(id);
294
+ }
281
295
  return out;
282
296
  }
283
297
  /**
@@ -291,7 +305,7 @@ function unionPaths(a, b) {
291
305
  */
292
306
  function makeDepWrapper(handle, registry, session, tracked, onDepHandle) {
293
307
  const brand = handle[_blac_core_internal.DEP_BRAND];
294
- const perDep = /* @__PURE__ */ new Map();
308
+ const perDep = /* @__PURE__ */ new WeakMap();
295
309
  const proxyCache = new _dirtytalk_structural.ProxyCache();
296
310
  const resolve = (options) => {
297
311
  const args = options?.args ?? brand.defaultArgs;
@@ -573,6 +587,7 @@ function commit(consumer) {
573
587
  }
574
588
  const primary = consumer.container;
575
589
  if (primary.state !== consumer.renderedState) consumer.bump();
590
+ consumer.paths = deps.settle();
576
591
  if (deps.isUnchanged()) return;
577
592
  primary.registerConsumerPaths(consumer.id, consumer.paths);
578
593
  setInterest(consumer, consumer.paths);
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["WITH_TRACKED_STATE","DEP_BRAND","NO_PATHS","ALL_PATHS","DEP_BRAND","ProxyCache","useRegistry","ALL_PATHS","ProxyCache","APPLY_DEPS","REMOVE_DEPS_OWNER"],"sources":["../src/BlocProvider.tsx","../src/buildTrackedProxy.ts","../src/depSession.ts","../src/useBloc.ts","../src/useBlocDeps.ts"],"sourcesContent":["import {\n createContext,\n useContext,\n useMemo,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport type { ExtractArgs, StateContainerConstructor } from '@blac/core';\n\n/**\n * Internal context value: a Map from bloc constructor to the args object\n * provided by the nearest BlocProvider for that bloc class.\n *\n * Each provider copies its parent's map and adds its own entry, so nested\n * providers for different blocs compose without clobbering each other.\n */\ntype ProvidedArgsMap = Map<StateContainerConstructor, unknown>;\n\nconst ProvidedArgsContext = createContext<ProvidedArgsMap>(new Map());\n\n/**\n * Props for {@link BlocProvider}.\n * @public\n */\nexport interface BlocProviderProps<T extends StateContainerConstructor> {\n /**\n * The bloc class whose args are being provided to descendants.\n */\n bloc: T;\n /**\n * Args that descendant `useBloc(bloc)` calls will resolve to when no\n * own `args` are given.\n */\n args: ExtractArgs<T>;\n children: ReactNode;\n}\n\n/**\n * Provides args to descendant `useBloc` calls for a specific bloc class via\n * React context. Own `args` on the `useBloc` call always win.\n *\n * Multiple `BlocProvider` wrappers for different bloc classes compose: each\n * merges its entry into the inherited map, so nested providers for different\n * blocs do not interfere.\n *\n * @example\n * ```tsx\n * <BlocProvider bloc={UserBloc} args={{ userId: 'alice' }}>\n * <UserProfile />\n * </BlocProvider>\n * ```\n *\n * @example Per-mount private instance\n * ```tsx\n * const id = useId();\n * <BlocProvider bloc={CartBloc} args={{ _id: id }}>\n * <CartWidget />\n * </BlocProvider>\n * ```\n * @public\n */\nexport function BlocProvider<T extends StateContainerConstructor>({\n bloc,\n args,\n children,\n}: BlocProviderProps<T>): ReactElement {\n const parentMap = useContext(ProvidedArgsContext);\n\n // Keyed on the args content, not identity, so an inline `args={{ ... }}`\n // literal does not rebuild the map (and re-render every consumer) on\n // every render, while a real field change still reaches `useProvidedArgs`.\n const argsKey = JSON.stringify(args);\n\n // Merge our entry into a new Map so sibling/parent providers for other blocs\n // are preserved.\n const mergedMap = useMemo(() => {\n const next = new Map(parentMap);\n next.set(bloc, args);\n return next;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [parentMap, bloc, argsKey]);\n\n return (\n <ProvidedArgsContext.Provider value={mergedMap}>\n {children}\n </ProvidedArgsContext.Provider>\n );\n}\n\n/**\n * Returns the args provided by the nearest {@link BlocProvider} for the given\n * bloc class, or `undefined` when called outside a matching provider.\n *\n * Used by `useBloc` to inherit provider args when no own `args` are given.\n * @public\n */\nexport function useProvidedArgs<T extends StateContainerConstructor>(\n BlocClass: T,\n): ExtractArgs<T> | undefined {\n const map = useContext(ProvidedArgsContext);\n return map.get(BlocClass) as ExtractArgs<T> | undefined;\n}\n","import { DEP_BRAND, WITH_TRACKED_STATE } from '@blac/core/internal';\n\ninterface TrackedStateTarget {\n [WITH_TRACKED_STATE]<R>(\n tracked: unknown,\n fn: () => R,\n onDepHandle?: (handle: object) => unknown,\n ): R;\n}\n\nfunction supportsTrackedState(value: object): value is TrackedStateTarget {\n return (\n typeof (value as TrackedStateTarget)[WITH_TRACKED_STATE] === 'function'\n );\n}\n\ntype Getter = (this: object) => unknown;\ntype Method = (...args: unknown[]) => unknown;\n\n// Prototype getters per class prototype. The chain is static, so walking it\n// once per class (not once per mounted component) keeps the get trap O(1).\nconst gettersByProto = new WeakMap<object, Map<string | symbol, Getter>>();\n\nfunction collectGetters(proto: object): Map<string | symbol, Getter> {\n const cached = gettersByProto.get(proto);\n if (cached !== undefined) return cached;\n const getters = new Map<string | symbol, Getter>();\n let p: object | null = proto;\n while (p !== null && p !== Object.prototype) {\n for (const key of Reflect.ownKeys(p)) {\n const desc = Object.getOwnPropertyDescriptor(p, key);\n // A descriptor getter, always `.call`ed with an explicit receiver.\n // oxlint-disable-next-line typescript/unbound-method\n if (desc?.get && !getters.has(key)) getters.set(key, desc.get);\n }\n p = Object.getPrototypeOf(p);\n }\n gettersByProto.set(proto, getters);\n return getters;\n}\n\n// Methods on the prototype, so a mount allocates the handler and the proxy\n// only, not a set of closures.\nclass BlocHandler implements ProxyHandler<object> {\n // Bound methods are cached so `bloc.method` keeps a stable identity across\n // reads — an unstable one would defeat memoisation in consumers. Keyed by\n // the underlying function too, so a reassigned method is re-bound.\n private boundMethods:\n | Map<string | symbol, { fn: Method; bound: Method }>\n | undefined = undefined;\n\n constructor(\n private readonly getters: Map<string | symbol, Getter>,\n private readonly tracksState: boolean,\n private readonly trackedStateRef: { current: unknown },\n private readonly onDepHandle: ((handle: object) => unknown) | undefined,\n ) {}\n\n get(target: object, key: string | symbol): unknown {\n const getter = this.getters.get(key);\n if (getter !== undefined) {\n const tracked = this.trackedStateRef.current;\n // `[WITH_TRACKED_STATE]` invokes the getter with `this` = the real\n // instance, so no per-read thunk is needed.\n return this.wrapDepHandle(\n tracked == null || !this.tracksState\n ? getter.call(target)\n : (target as TrackedStateTarget)[WITH_TRACKED_STATE](\n tracked,\n getter,\n this.onDepHandle,\n ),\n );\n }\n\n // Receiver is the real instance, so own fields keep their `#private`\n // brand. Methods are bound to it too, otherwise `bloc.method()` would\n // call with `this` = proxy and fail the same brand check.\n const value = Reflect.get(target, key, target);\n if (typeof value === 'function') {\n const fn = value as Method;\n this.boundMethods ??= new Map();\n let cached = this.boundMethods.get(key);\n if (cached === undefined || cached.fn !== fn) {\n cached = { fn, bound: fn.bind(target) };\n this.boundMethods.set(key, cached);\n }\n return this.wrapDepHandle(cached.bound);\n }\n return this.wrapDepHandle(value);\n }\n\n set(target: object, key: string | symbol, value: unknown): boolean {\n return Reflect.set(target, key, value, target);\n }\n\n private wrapDepHandle(value: unknown): unknown {\n if (\n this.onDepHandle !== undefined &&\n (typeof value === 'function' || typeof value === 'object') &&\n value !== null &&\n (value as Record<symbol, unknown>)[DEP_BRAND] !== undefined\n ) {\n return this.onDepHandle(value as object);\n }\n return value;\n }\n}\n\n/**\n * Build a per-consumer proxy for a bloc instance.\n *\n * Getters run with `this` bound to the real instance, not the proxy, so ES\n * `#private` fields and methods work in user blocs. Path recording is done by\n * `[WITH_TRACKED_STATE]`, which makes the instance's own `state` getter\n * report the render's tracking proxy for the call's duration; nested getter\n * chains stay tracked because the override remains set.\n *\n * One allocation per bloc acquisition (inside `useMemo`), so the proxy is\n * stable across renders.\n *\n * @param onDepHandle - Invoked when a read returns a branded dep handle;\n * returns the value (the session-bound wrapper) to expose in its place.\n * Must cache wrappers per handle to avoid re-allocation.\n */\nexport function buildTrackedProxy<T extends object>(\n instance: T,\n trackedStateRef: { current: unknown },\n onDepHandle?: (handle: object) => unknown,\n): T {\n // Both string- and symbol-keyed prototype getters (excluding\n // Object.prototype). Arrow-function class properties are own values, not\n // getters, and pass through unmodified.\n const handler = new BlocHandler(\n collectGetters(Object.getPrototypeOf(instance) as object),\n supportsTrackedState(instance),\n trackedStateRef,\n onDepHandle,\n );\n return new Proxy(instance, handler as ProxyHandler<T>);\n}\n","import type {\n StateContainer,\n StateContainerConstructor,\n StateContainerRegistry,\n} from '@blac/core';\nimport { DEP_BRAND } from '@blac/core/internal';\nimport {\n ALL_PATHS,\n emptyPathSet,\n pathSetEquals,\n trackRender,\n ProxyCache,\n type PathSet,\n type PathSubscription,\n} from '@dirtytalk/structural';\nimport { buildTrackedProxy } from './buildTrackedProxy';\n\nconst NO_PATHS: PathSet = emptyPathSet();\n\ninterface DepEntry {\n paths: PathSet;\n Type: StateContainerConstructor;\n key: string;\n args: unknown;\n}\n\ninterface DepSub {\n subscription: PathSubscription;\n registry: StateContainerRegistry;\n Type: StateContainerConstructor;\n key: string;\n}\n\n/**\n * The containers one `useBloc` consumer reads during a render: its primary\n * bloc plus every dep reached through `this.<handle>.track()`.\n *\n * The render records entries; the commit calls `reconcile`, which subscribes\n * and takes a ref on new deps and drops the ones no longer read. `dispose`\n * releases everything. Refs are only taken at commit, so an uncommitted render\n * never leaks one.\n */\nexport class DepSession {\n // Most consumers read no deps, so everything dep-related is allocated on the\n // first dep a render records.\n private primary: StateContainer | null = null;\n private primaryPaths: PathSet = NO_PATHS;\n private entries: Map<StateContainer, DepEntry> | null = null;\n private subs: Map<StateContainer, DepSub> | null = null;\n private reconciled = false;\n private lastPrimary: StateContainer | null = null;\n private lastPrimaryPaths: PathSet = NO_PATHS;\n private lastEntries: Map<StateContainer, DepEntry> | null = null;\n private disarms: Array<() => void> | null = null;\n\n constructor(\n private readonly consumerId: string,\n private readonly onChange: () => void,\n ) {}\n\n private get refId(): string {\n return `useBloc@${this.consumerId}:dep`;\n }\n\n begin(primary: StateContainer, paths: PathSet): void {\n this.entries?.clear();\n if (this.disarms !== null) this.disarms.length = 0;\n this.primary = primary;\n this.primaryPaths = paths;\n }\n\n record(\n dep: StateContainer,\n paths: PathSet,\n Type: StateContainerConstructor,\n key: string,\n args: unknown,\n disarm: () => void,\n ): void {\n (this.disarms ??= []).push(disarm);\n // A dep that resolves to the primary only widens the primary's entry.\n if (dep === this.primary) {\n this.primaryPaths = unionPaths(this.primaryPaths, paths);\n return;\n }\n const entries = (this.entries ??= new Map());\n const existing = entries.get(dep);\n if (existing !== undefined) {\n existing.paths = unionPaths(existing.paths, paths);\n } else {\n entries.set(dep, { paths, Type, key, args });\n }\n }\n\n /** Stops every dep tracked this render from recording further reads. */\n disarm(): void {\n const disarms = this.disarms;\n if (disarms === null) return;\n for (const disarm of disarms) disarm();\n disarms.length = 0;\n }\n\n /** Forces the next `reconcile` to run in full. */\n invalidate(): void {\n this.reconciled = false;\n }\n\n isUnchanged(): boolean {\n if (\n !this.reconciled ||\n this.lastPrimary !== this.primary ||\n !pathSetEquals(this.lastPrimaryPaths, this.primaryPaths)\n ) {\n return false;\n }\n const last = this.lastEntries;\n const entries = this.entries;\n if ((last?.size ?? 0) !== (entries?.size ?? 0)) return false;\n if (last === null || entries === null) return true;\n for (const [container, entry] of entries) {\n const prev = last.get(container);\n if (prev === undefined || !sameEntry(prev, entry)) return false;\n }\n return true;\n }\n\n reconcile(registry: StateContainerRegistry): void {\n const { consumerId } = this;\n const entries = this.entries;\n this.reconciled = true;\n this.lastPrimary = this.primary;\n this.lastPrimaryPaths = this.primaryPaths;\n this.lastEntries =\n entries === null || entries.size === 0 ? null : new Map(entries);\n\n const subs = this.subs;\n if (subs !== null) {\n for (const [dep, sub] of subs) {\n if (entries?.has(dep) !== true) {\n this.drop(dep, sub);\n subs.delete(dep);\n }\n }\n }\n if (entries === null) return;\n\n for (const [dep, entry] of entries) {\n // StrictMode's simulated unmount can dispose a dep this render captured;\n // re-render so the session resolves the live instance.\n if (dep.$blac.disposed) {\n this.onChange();\n continue;\n }\n dep.registerConsumerPaths(consumerId, entry.paths);\n const existing = subs?.get(dep);\n if (existing) {\n existing.subscription.setInterest(entry.paths);\n continue;\n }\n const refId = this.refId;\n const live = registry.acquire(entry.Type, entry.key, {\n canCreate: true,\n countRef: true,\n refId,\n args: entry.args,\n });\n // The key now maps to another instance; re-render against that one.\n if (live !== dep) {\n registry.release(entry.Type, entry.key, false, refId);\n this.onChange();\n continue;\n }\n const subscription = dep.subscribePaths(this.onChange);\n subscription.setInterest(entry.paths);\n (this.subs ??= new Map()).set(dep, {\n subscription,\n registry,\n Type: entry.Type,\n key: entry.key,\n });\n }\n }\n\n dispose(): void {\n const subs = this.subs;\n if (subs !== null) {\n for (const [dep, sub] of subs) this.drop(dep, sub);\n subs.clear();\n }\n this.reconciled = false;\n }\n\n private drop(dep: StateContainer, sub: DepSub): void {\n sub.subscription.unsubscribe();\n dep.unregisterConsumer(this.consumerId);\n sub.registry.release(sub.Type, sub.key, false, this.refId);\n }\n}\n\nfunction sameEntry(a: DepEntry, b: DepEntry): boolean {\n return (\n pathSetEquals(a.paths, b.paths) &&\n a.key === b.key &&\n Object.is(a.args, b.args)\n );\n}\n\nfunction unionPaths(a: PathSet, b: PathSet): PathSet {\n if (a === ALL_PATHS || b === ALL_PATHS) return ALL_PATHS;\n const out = new Set<number>(a as Set<number>);\n for (const id of b as Set<number>) out.add(id);\n return out;\n}\n\ninterface DepAccessOptionsLike {\n args?: unknown;\n}\n\n/** Structural shape of a branded `depend()` handle as seen from React. */\nexport interface DepHandleLike {\n track(options?: DepAccessOptionsLike): [unknown, StateContainer];\n untracked(options?: DepAccessOptionsLike): StateContainer;\n readonly [DEP_BRAND]: {\n Type: StateContainerConstructor;\n defaultArgs?: unknown;\n };\n}\n\n/**\n * Wrap a dep handle read inside a tracked getter. During a render (`tracked`\n * is set) `.track()` records the dep's read paths into `session` and returns\n * a tracked proxy, so the dep's own getters track too. Outside a render it\n * returns the live `[state, dep]`, like the core handle.\n *\n * Args resolve per call, so one handle can reach several instances; proxies\n * are cached per resolved instance.\n */\nexport function makeDepWrapper(\n handle: DepHandleLike,\n registry: StateContainerRegistry,\n session: DepSession,\n tracked: { current: unknown },\n onDepHandle: (handle: object) => unknown,\n): DepHandleLike {\n const brand = handle[DEP_BRAND];\n const perDep = new Map<\n StateContainer,\n { ref: { current: unknown }; proxy: StateContainer }\n >();\n const proxyCache = new ProxyCache();\n\n const resolve = (options?: DepAccessOptionsLike) => {\n const args = options?.args ?? brand.defaultArgs;\n const key = registry.resolveKey(brand.Type, undefined, args);\n const dep = registry.acquire(brand.Type, key, {\n canCreate: true,\n countRef: false,\n args,\n sweepIfUnowned: tracked.current != null,\n }) as unknown as StateContainer;\n return { dep, key, args };\n };\n\n const wrapper = {\n untracked: (options?: DepAccessOptionsLike) => resolve(options).dep,\n track: (options?: DepAccessOptionsLike) => {\n const { dep, key, args } = resolve(options);\n if (tracked.current == null) return [dep.state, dep];\n\n const result = trackRender(dep.state, dep.interner, proxyCache);\n let cache = perDep.get(dep);\n if (cache === undefined) {\n const ref = { current: result.value as unknown };\n cache = { ref, proxy: buildTrackedProxy(dep, ref, onDepHandle) };\n perDep.set(dep, cache);\n } else {\n cache.ref.current = result.value;\n }\n session.record(dep, result.paths, brand.Type, key, args, result.disarm);\n return [result.value, cache.proxy];\n },\n } as DepHandleLike;\n\n Object.defineProperty(wrapper, DEP_BRAND, {\n value: brand,\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n return wrapper;\n}\n","import { useLayoutEffect, useRef, useSyncExternalStore } from 'react';\nimport {\n isIsolatedClass,\n type ExtractArgs,\n type ExtractState,\n type InstanceState,\n type StateContainer,\n type StateContainerConstructor,\n type StateContainerRegistry,\n} from '@blac/core';\nimport {\n ALL_PATHS,\n emptyPathSet,\n trackRender,\n ProxyCache,\n type PathSet,\n type PathSubscription,\n} from '@dirtytalk/structural';\nimport { useProvidedArgs } from './BlocProvider';\nimport { useRegistry } from './RegistryProvider';\nimport { buildTrackedProxy } from './buildTrackedProxy';\nimport { DepSession, makeDepWrapper, type DepHandleLike } from './depSession';\nimport type { UseBlocOptions, UseBlocReturn } from './types';\n\nlet nextConsumerId = 0;\n\n/**\n * React hook that connects a component to a state container with automatic\n * re-render on state changes.\n *\n * Two tracking modes:\n * - **Auto-tracking** (default): the returned state value is a proxy that\n * records read paths during render. The component re-renders when any\n * recorded path changes.\n * - **Manual select**: pass `options.select` to opt out of auto-tracking.\n * The hook re-renders only when the returned array's elements change\n * (per-index `Object.is`).\n *\n * Lifecycle:\n * - The bloc is acquired from the registry on mount and released on\n * unmount. The instance key is derived from `options.args` (own args),\n * then the surrounding {@link BlocProvider} context args for this bloc,\n * then the default key (no args).\n * - `options.onMount` fires after the bloc is acquired; `options.onUnmount`\n * fires *before* the registry releases its ref, so the bloc is still\n * alive when the callback runs.\n *\n * Per-mount private instance: mark the class `@blac({ isolated: true })`, or\n * pass a unique key arg yourself (e.g. `useId()` in `args`).\n *\n * @typeParam T - The state container constructor type (inferred from BlocClass)\n * @param BlocClass - The state container class to connect to\n * @param options - Configuration options\n * @returns Tuple of `[state, bloc]`\n *\n * @example Basic usage\n * ```ts\n * const [state, bloc] = useBloc(MyBloc);\n * ```\n *\n * @example Manual select\n * ```ts\n * const [state, bloc] = useBloc(MyBloc, {\n * select: (state) => [state.count],\n * });\n * ```\n *\n * @example Args-based shared instance\n * ```ts\n * const [state, bloc] = useBloc(UserBloc, { args: { userId: 'alice' } });\n * ```\n * @public\n */\nexport function useBloc<\n T extends StateContainerConstructor = StateContainerConstructor,\n>(\n BlocClass: T,\n options?: UseBlocOptions<T>,\n): UseBlocReturn<T, ExtractState<T>> {\n const registry = useRegistry();\n\n const consumerRef = useRef<Consumer | null>(null);\n const consumer = (consumerRef.current ??= createConsumer());\n\n consumer.select = options?.select;\n consumer.onMount = options?.onMount;\n consumer.onUnmount = options?.onUnmount;\n\n consumer.ownArgs = (options as { args?: ExtractArgs<T> } | undefined)?.args;\n consumer.providerArgs = useProvidedArgs(BlocClass);\n const effectiveArgs = resolveEffectiveArgs(consumer);\n const sharedKey = registry.resolveKey(BlocClass, undefined, effectiveArgs);\n const instanceKey = isIsolatedClass(BlocClass)\n ? `${sharedKey}#${consumer.id}`\n : sharedKey;\n\n // A memo kept on the consumer rather than in `useMemo`: no hook, deps array\n // or factory closure per mount. `rebindNonce` is bumped by the commit when\n // the rendered instance was disposed and recreated before commit (a\n // same-commit owner handoff, or StrictMode's double effect); the tracked\n // proxy can't be retargeted in place. Keyed on `BlocClass` as well as the\n // key: classes without args or a `static key` share the default key, so\n // `useBloc(cond ? A : B)` must still re-subscribe on a class swap.\n if (\n consumer.BlocClass !== BlocClass ||\n consumer.instanceKey !== instanceKey ||\n consumer.registry !== registry ||\n consumer.boundNonce !== consumer.rebindNonce\n ) {\n bind(consumer, BlocClass, instanceKey, registry, effectiveArgs);\n }\n\n useSyncExternalStore(\n consumer.subscribe,\n consumer.getSnapshot,\n consumer.getSnapshot,\n );\n\n const container = consumer.container;\n const rawState = container.state as ExtractState<T>;\n consumer.renderedState = rawState;\n const select = consumer.select;\n consumer.isSelectMode = select !== undefined;\n let state: ExtractState<T>;\n if (select !== undefined) {\n state = rawState;\n // Recomputed every render: the selector may close over props.\n consumer.selection = select(rawState, container as InstanceState<T>);\n setInterest(consumer, ALL_PATHS);\n } else {\n const tracked = trackRender(\n rawState,\n container.interner,\n (consumer.proxyCache ??= new ProxyCache()),\n );\n state = tracked.value as ExtractState<T>;\n consumer.tracked.current = tracked.value;\n consumer.paths = tracked.paths;\n // Run at commit, so reads that outlive the render (effects, handlers)\n // don't add paths.\n consumer.disarm = tracked.disarm;\n // `tracked.paths` is still empty here and fills during JSX, so paths are\n // registered at commit, not now.\n consumer.deps.begin(container, tracked.paths);\n }\n\n // Stable callbacks that read this render's inputs from the consumer.\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(consumer.commit);\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(consumer.mount, NO_DEPS);\n\n return [state, consumer.trackedBloc] as UseBlocReturn<T, ExtractState<T>>;\n}\n\nconst noop = (): void => {};\n\n// Initial `interest` and `paths`; neither is ever mutated in place.\nconst NO_PATHS: PathSet = emptyPathSet();\nconst NO_DEPS: [] = [];\n\n/**\n * Everything one `useBloc` call keeps between renders. Its callbacks are\n * created once, so a render allocates no closures.\n *\n * `bump` increments `version` before calling `notify`: uSES requires\n * `getSnapshot()` to reflect the change by the time it is notified.\n */\ninterface Consumer {\n id: string;\n primaryRefId: string;\n // The render's inputs, read by the commit of that render.\n BlocClass: StateContainerConstructor;\n instanceKey: string;\n registry: StateContainerRegistry;\n /** Written only by `bind`. */\n container: StateContainer;\n trackedBloc: unknown;\n subscribe: (onStoreChange: () => void) => () => void;\n boundNonce: number;\n rebindNonce: number;\n renderedState: unknown;\n version: number;\n /** `noop` while unsubscribed. */\n notify: () => void;\n bump: () => void;\n getSnapshot: () => number;\n onChange: () => void;\n unsubscribe: () => void;\n commit: () => void;\n mount: () => () => void;\n /** The committed `paths`, or `ALL_PATHS` in select mode. */\n interest: PathSet;\n /** `null` while unsubscribed. */\n subscription: PathSubscription | null;\n subscribedTo: StateContainer | null;\n paths: PathSet;\n isSelectMode: boolean;\n selection: unknown[] | null;\n deps: DepSession;\n // Typed loosely: `T` is per call site.\n select: ((state: any, bloc: any) => unknown[]) | undefined;\n onMount: ((bloc: any) => void) | undefined;\n onUnmount: ((bloc: any) => void) | undefined;\n ownArgs: unknown;\n providerArgs: unknown;\n /** The render's tracking proxy; `null` outside a tracked render. */\n tracked: { current: unknown };\n disarm: (() => void) | null;\n proxyCache: ProxyCache | null;\n // The instance the consumer holds a ref on, claimed at commit so acquire\n // and release pair exactly.\n ownedClass: StateContainerConstructor | null;\n ownedKey: string;\n ownedRegistry: StateContainerRegistry | null;\n ownedBloc: unknown;\n}\n\nfunction createConsumer(): Consumer {\n const id = `useBloc-${nextConsumerId++}`;\n const consumer: Consumer = {\n id,\n primaryRefId: `useBloc@${id}`,\n BlocClass: null as unknown as StateContainerConstructor,\n instanceKey: '',\n registry: null as unknown as StateContainerRegistry,\n container: null as unknown as StateContainer,\n trackedBloc: null,\n subscribe: null as unknown as Consumer['subscribe'],\n boundNonce: 0,\n rebindNonce: 0,\n renderedState: undefined,\n version: 0,\n notify: noop,\n bump: () => {\n consumer.version++;\n consumer.notify();\n },\n getSnapshot: () => consumer.version,\n onChange: () => onChange(consumer),\n unsubscribe: () => unsubscribe(consumer),\n commit: () => commit(consumer),\n mount: () => teardown,\n interest: NO_PATHS,\n subscription: null,\n subscribedTo: null,\n paths: NO_PATHS,\n isSelectMode: false,\n selection: null,\n deps: null as unknown as DepSession,\n select: undefined,\n onMount: undefined,\n onUnmount: undefined,\n ownArgs: undefined,\n providerArgs: undefined,\n tracked: { current: null },\n disarm: null,\n proxyCache: null,\n ownedClass: null,\n ownedKey: '',\n ownedRegistry: null,\n ownedBloc: null,\n };\n const teardown = (): void => {\n release(consumer);\n consumer.deps.dispose();\n };\n consumer.deps = new DepSession(id, consumer.bump);\n return consumer;\n}\n\nfunction bind(\n consumer: Consumer,\n BlocClass: StateContainerConstructor,\n instanceKey: string,\n registry: StateContainerRegistry,\n args: unknown,\n): void {\n // No ref during render; the commit claims it. The registry sweeps the\n // instance if no commit ever does (SSR, discarded renders).\n const instance = registry.acquire(BlocClass, instanceKey, {\n canCreate: true,\n countRef: false,\n args: args as ExtractArgs<StateContainerConstructor>,\n sweepIfUnowned: true,\n }) as unknown as StateContainer;\n\n // Dep proxies get `onDepHandle` too, so nested `.track()` calls (A→B→C)\n // record into this consumer's session.\n let depWrappers: Map<object, unknown> | undefined;\n const onDepHandle = (handle: object): unknown => {\n depWrappers ??= new Map();\n const cached = depWrappers.get(handle);\n if (cached !== undefined) return cached;\n const wrapper = makeDepWrapper(\n handle as DepHandleLike,\n registry,\n consumer.deps,\n consumer.tracked,\n onDepHandle,\n );\n depWrappers.set(handle, wrapper);\n return wrapper;\n };\n\n consumer.BlocClass = BlocClass;\n consumer.instanceKey = instanceKey;\n consumer.registry = registry;\n consumer.boundNonce = consumer.rebindNonce;\n consumer.container = instance;\n consumer.trackedBloc = buildTrackedProxy(\n instance,\n consumer.tracked,\n onDepHandle,\n );\n // A new identity per instance, so uSES re-subscribes.\n consumer.subscribe = (onStoreChange) => subscribe(consumer, onStoreChange);\n}\n\nfunction subscribe(consumer: Consumer, onStoreChange: () => void): () => void {\n consumer.notify = onStoreChange;\n const container = consumer.container;\n const subscription = container.subscribePaths(consumer.onChange);\n subscription.setInterest(consumer.interest);\n consumer.subscription = subscription;\n consumer.subscribedTo = container;\n container.registerConsumerPaths(consumer.id, consumer.paths);\n // An emit raised during render is still pending in the channel, so its\n // next flush delivers it to this subscriber.\n return consumer.unsubscribe;\n}\n\nfunction unsubscribe(consumer: Consumer): void {\n consumer.notify = noop;\n consumer.subscription?.unsubscribe();\n consumer.subscription = null;\n consumer.subscribedTo?.unregisterConsumer(consumer.id);\n consumer.subscribedTo = null;\n}\n\nfunction onChange(consumer: Consumer): void {\n if (consumer.isSelectMode) {\n const select = consumer.select;\n const container = consumer.subscribedTo;\n if (select && container !== null) {\n const next = select(container.state, container);\n const prev = consumer.selection;\n if (prev !== null && shallowArrayEqual(prev, next)) return;\n consumer.selection = next;\n }\n }\n consumer.bump();\n}\n\nfunction claim(consumer: Consumer): void {\n const { BlocClass, instanceKey, registry } = consumer;\n if (\n consumer.ownedClass === BlocClass &&\n consumer.ownedKey === instanceKey &&\n consumer.ownedRegistry === registry\n ) {\n return;\n }\n release(consumer);\n const live = registry.acquire(BlocClass, instanceKey, {\n canCreate: true,\n countRef: true,\n refId: consumer.primaryRefId,\n args: resolveEffectiveArgs(\n consumer,\n ) as ExtractArgs<StateContainerConstructor>,\n });\n consumer.ownedClass = BlocClass;\n consumer.ownedKey = instanceKey;\n consumer.ownedRegistry = registry;\n consumer.ownedBloc = live;\n consumer.onMount?.(live);\n // The render captured a stale instance; re-render against the live one.\n if (live !== (consumer.container as unknown)) {\n consumer.rebindNonce++;\n consumer.bump();\n }\n}\n\nfunction release(consumer: Consumer): void {\n const { ownedClass, ownedRegistry } = consumer;\n if (ownedClass === null || ownedRegistry === null) return;\n // Before release, so the bloc is still alive in the callback.\n consumer.onUnmount?.(consumer.ownedBloc);\n consumer.ownedClass = null;\n consumer.ownedRegistry = null;\n ownedRegistry.release(\n ownedClass,\n consumer.ownedKey,\n false,\n consumer.primaryRefId,\n );\n}\n\nfunction commit(consumer: Consumer): void {\n claim(consumer);\n // Getters called after commit read live state, not this render's proxy.\n consumer.tracked.current = null;\n const disarm = consumer.disarm;\n if (disarm !== null) {\n consumer.disarm = null;\n disarm();\n }\n const deps = consumer.deps;\n deps.disarm();\n if (consumer.isSelectMode) {\n // A later switch back to auto-track must reconcile in full.\n deps.invalidate();\n return;\n }\n const primary = consumer.container;\n // A change between render and commit was diffed against the previous\n // interest, which may lack paths only this render read.\n if (primary.state !== consumer.renderedState) consumer.bump();\n if (deps.isUnchanged()) return;\n\n primary.registerConsumerPaths(consumer.id, consumer.paths);\n setInterest(consumer, consumer.paths);\n deps.reconcile(consumer.registry);\n}\n\nconst setInterest = (consumer: Consumer, interest: PathSet): void => {\n consumer.interest = interest;\n consumer.subscription?.setInterest(interest);\n};\n\n// Own args win over provider args. Shared by render and the ownership effect,\n// which recreates a disposed instance and must not drop its args.\nconst resolveEffectiveArgs = (consumer: Consumer): unknown =>\n consumer.ownArgs !== undefined ? consumer.ownArgs : consumer.providerArgs;\n\nconst shallowArrayEqual = (a: unknown[], b: unknown[]): boolean => {\n if (a === b) return true;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!Object.is(a[i], b[i])) return false;\n }\n return true;\n};\n","import { useEffect, useId, useRef } from 'react';\nimport type { StateContainer } from '@blac/core';\nimport { APPLY_DEPS, REMOVE_DEPS_OWNER } from '@blac/core/internal';\n\n/**\n * Feed live values from the component into a bloc's `deps` lane.\n *\n * Each calling component is one owner: its slice is shallow-merged into the\n * bloc's merged `deps` view and withdrawn on unmount. The bloc observes\n * changes through `onDepsChanged`.\n *\n * ```tsx\n * const [state, bloc] = useBloc(CanvasCubit);\n * useBlocDeps(bloc, { canvas, onTick });\n * ```\n *\n * Use this for values a bloc must *read* but should not own — DOM handles,\n * callbacks, values from other hooks. Pass identity-stable values (`useMemo` /\n * `useCallback`) as you would to any hook; a new function identity each render\n * counts as a change.\n */\nexport function useBlocDeps<D extends object>(\n bloc: StateContainer<any, any, D>,\n slice: Partial<D>,\n): void {\n const ownerId = useId();\n const latest = useRef(slice);\n latest.current = slice;\n\n // `APPLY_DEPS` shallow-compares and no-ops on an unchanged slice, so applying\n // on every commit is cheaper than asking callers to maintain a dep array.\n useEffect(() => {\n bloc[APPLY_DEPS](ownerId, latest.current);\n });\n\n useEffect(() => {\n return () => {\n bloc[REMOVE_DEPS_OWNER](ownerId);\n };\n }, [bloc, ownerId]);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["WITH_TRACKED_STATE","DEP_BRAND","NO_PATHS","ALL_PATHS","DEP_BRAND","ProxyCache","useRegistry","ALL_PATHS","ProxyCache","APPLY_DEPS","REMOVE_DEPS_OWNER"],"sources":["../src/BlocProvider.tsx","../src/buildTrackedProxy.ts","../src/depSession.ts","../src/useBloc.ts","../src/useBlocDeps.ts"],"sourcesContent":["import {\n createContext,\n useContext,\n useMemo,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport type { ExtractArgs, StateContainerConstructor } from '@blac/core';\n\n/**\n * Internal context value: a Map from bloc constructor to the args object\n * provided by the nearest BlocProvider for that bloc class.\n *\n * Each provider copies its parent's map and adds its own entry, so nested\n * providers for different blocs compose without clobbering each other.\n */\ntype ProvidedArgsMap = Map<StateContainerConstructor, unknown>;\n\nconst ProvidedArgsContext = createContext<ProvidedArgsMap>(new Map());\n\n/**\n * Props for {@link BlocProvider}.\n * @public\n */\nexport interface BlocProviderProps<T extends StateContainerConstructor> {\n /**\n * The bloc class whose args are being provided to descendants.\n */\n bloc: T;\n /**\n * Args that descendant `useBloc(bloc)` calls will resolve to when no\n * own `args` are given.\n */\n args: ExtractArgs<T>;\n children: ReactNode;\n}\n\n/**\n * Provides args to descendant `useBloc` calls for a specific bloc class via\n * React context. Own `args` on the `useBloc` call always win.\n *\n * Multiple `BlocProvider` wrappers for different bloc classes compose: each\n * merges its entry into the inherited map, so nested providers for different\n * blocs do not interfere.\n *\n * @example\n * ```tsx\n * <BlocProvider bloc={UserBloc} args={{ userId: 'alice' }}>\n * <UserProfile />\n * </BlocProvider>\n * ```\n *\n * @example Per-mount private instance\n * ```tsx\n * const id = useId();\n * <BlocProvider bloc={CartBloc} args={{ _id: id }}>\n * <CartWidget />\n * </BlocProvider>\n * ```\n * @public\n */\nexport function BlocProvider<T extends StateContainerConstructor>({\n bloc,\n args,\n children,\n}: BlocProviderProps<T>): ReactElement {\n const parentMap = useContext(ProvidedArgsContext);\n\n // Keyed on the args content, not identity, so an inline `args={{ ... }}`\n // literal does not rebuild the map (and re-render every consumer) on\n // every render, while a real field change still reaches `useProvidedArgs`.\n const argsKey = JSON.stringify(args);\n\n // Merge our entry into a new Map so sibling/parent providers for other blocs\n // are preserved.\n const mergedMap = useMemo(() => {\n const next = new Map(parentMap);\n next.set(bloc, args);\n return next;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [parentMap, bloc, argsKey]);\n\n return (\n <ProvidedArgsContext.Provider value={mergedMap}>\n {children}\n </ProvidedArgsContext.Provider>\n );\n}\n\n/**\n * Returns the args provided by the nearest {@link BlocProvider} for the given\n * bloc class, or `undefined` when called outside a matching provider.\n *\n * Used by `useBloc` to inherit provider args when no own `args` are given.\n * @public\n */\nexport function useProvidedArgs<T extends StateContainerConstructor>(\n BlocClass: T,\n): ExtractArgs<T> | undefined {\n const map = useContext(ProvidedArgsContext);\n return map.get(BlocClass) as ExtractArgs<T> | undefined;\n}\n","import { DEP_BRAND, WITH_TRACKED_STATE } from '@blac/core/internal';\n\ninterface TrackedStateTarget {\n [WITH_TRACKED_STATE]<R>(\n tracked: unknown,\n fn: () => R,\n onDepHandle?: (handle: object) => unknown,\n ): R;\n}\n\nfunction supportsTrackedState(value: object): value is TrackedStateTarget {\n return (\n typeof (value as TrackedStateTarget)[WITH_TRACKED_STATE] === 'function'\n );\n}\n\ntype Getter = (this: object) => unknown;\ntype Method = (...args: unknown[]) => unknown;\n\n// Prototype getters per class prototype. The chain is static, so walking it\n// once per class (not once per mounted component) keeps the get trap O(1).\nconst gettersByProto = new WeakMap<object, Map<string | symbol, Getter>>();\n\nfunction collectGetters(proto: object): Map<string | symbol, Getter> {\n const cached = gettersByProto.get(proto);\n if (cached !== undefined) return cached;\n const getters = new Map<string | symbol, Getter>();\n let p: object | null = proto;\n while (p !== null && p !== Object.prototype) {\n for (const key of Reflect.ownKeys(p)) {\n const desc = Object.getOwnPropertyDescriptor(p, key);\n // A descriptor getter, always `.call`ed with an explicit receiver.\n // oxlint-disable-next-line typescript/unbound-method\n if (desc?.get && !getters.has(key)) getters.set(key, desc.get);\n }\n p = Object.getPrototypeOf(p);\n }\n gettersByProto.set(proto, getters);\n return getters;\n}\n\n// Methods on the prototype, so a mount allocates the handler and the proxy\n// only, not a set of closures.\nclass BlocHandler implements ProxyHandler<object> {\n // Bound methods are cached so `bloc.method` keeps a stable identity across\n // reads — an unstable one would defeat memoisation in consumers. Keyed by\n // the underlying function too, so a reassigned method is re-bound.\n private boundMethods:\n | Map<string | symbol, { fn: Method; bound: Method }>\n | undefined = undefined;\n\n constructor(\n private readonly getters: Map<string | symbol, Getter>,\n private readonly tracksState: boolean,\n private readonly trackedStateRef: { current: unknown },\n private readonly onDepHandle: ((handle: object) => unknown) | undefined,\n ) {}\n\n get(target: object, key: string | symbol): unknown {\n const getter = this.getters.get(key);\n if (getter !== undefined) {\n const tracked = this.trackedStateRef.current;\n // `[WITH_TRACKED_STATE]` invokes the getter with `this` = the real\n // instance, so no per-read thunk is needed.\n return this.wrapDepHandle(\n tracked == null || !this.tracksState\n ? getter.call(target)\n : (target as TrackedStateTarget)[WITH_TRACKED_STATE](\n tracked,\n getter,\n this.onDepHandle,\n ),\n );\n }\n\n // Receiver is the real instance, so own fields keep their `#private`\n // brand. Methods are bound to it too, otherwise `bloc.method()` would\n // call with `this` = proxy and fail the same brand check.\n const value = Reflect.get(target, key, target);\n if (typeof value === 'function') {\n const fn = value as Method;\n this.boundMethods ??= new Map();\n let cached = this.boundMethods.get(key);\n if (cached === undefined || cached.fn !== fn) {\n cached = { fn, bound: fn.bind(target) };\n this.boundMethods.set(key, cached);\n }\n return this.wrapDepHandle(cached.bound);\n }\n return this.wrapDepHandle(value);\n }\n\n set(target: object, key: string | symbol, value: unknown): boolean {\n return Reflect.set(target, key, value, target);\n }\n\n private wrapDepHandle(value: unknown): unknown {\n if (\n this.onDepHandle !== undefined &&\n (typeof value === 'function' || typeof value === 'object') &&\n value !== null &&\n (value as Record<symbol, unknown>)[DEP_BRAND] !== undefined\n ) {\n return this.onDepHandle(value as object);\n }\n return value;\n }\n}\n\n/**\n * Build a per-consumer proxy for a bloc instance.\n *\n * Getters run with `this` bound to the real instance, not the proxy, so ES\n * `#private` fields and methods work in user blocs. Path recording is done by\n * `[WITH_TRACKED_STATE]`, which makes the instance's own `state` getter\n * report the render's tracking proxy for the call's duration; nested getter\n * chains stay tracked because the override remains set.\n *\n * One allocation per bloc acquisition (inside `useMemo`), so the proxy is\n * stable across renders.\n *\n * @param onDepHandle - Invoked when a read returns a branded dep handle;\n * returns the value (the session-bound wrapper) to expose in its place.\n * Must cache wrappers per handle to avoid re-allocation.\n */\nexport function buildTrackedProxy<T extends object>(\n instance: T,\n trackedStateRef: { current: unknown },\n onDepHandle?: (handle: object) => unknown,\n): T {\n // Both string- and symbol-keyed prototype getters (excluding\n // Object.prototype). Arrow-function class properties are own values, not\n // getters, and pass through unmodified.\n const handler = new BlocHandler(\n collectGetters(Object.getPrototypeOf(instance) as object),\n supportsTrackedState(instance),\n trackedStateRef,\n onDepHandle,\n );\n return new Proxy(instance, handler as ProxyHandler<T>);\n}\n","import type {\n StateContainer,\n StateContainerConstructor,\n StateContainerRegistry,\n} from '@blac/core';\nimport { DEP_BRAND } from '@blac/core/internal';\nimport {\n ALL_PATHS,\n emptyPathSet,\n pathSetEquals,\n trackRender,\n ProxyCache,\n type PathSet,\n type PathSubscription,\n} from '@dirtytalk/structural';\nimport { buildTrackedProxy } from './buildTrackedProxy';\n\nconst NO_PATHS: PathSet = emptyPathSet();\n\ninterface DepEntry {\n // One live set per `.track()` call, merged at commit: reads keep landing in\n // each call's own set until the render ends.\n parts: PathSet[];\n paths: PathSet;\n Type: StateContainerConstructor;\n key: string;\n args: unknown;\n}\n\ninterface DepSub {\n subscription: PathSubscription;\n registry: StateContainerRegistry;\n Type: StateContainerConstructor;\n key: string;\n}\n\n/**\n * The containers one `useBloc` consumer reads during a render: its primary\n * bloc plus every dep reached through `this.<handle>.track()`.\n *\n * The render records entries; the commit calls `reconcile`, which subscribes\n * and takes a ref on new deps and drops the ones no longer read. `dispose`\n * releases everything. Refs are only taken at commit, so an uncommitted render\n * never leaks one.\n */\nexport class DepSession {\n // Most consumers read no deps, so everything dep-related is allocated on the\n // first dep a render records.\n private primary: StateContainer | null = null;\n private primaryParts: PathSet[] = [];\n private primaryPaths: PathSet = NO_PATHS;\n private entries: Map<StateContainer, DepEntry> | null = null;\n private subs: Map<StateContainer, DepSub> | null = null;\n private reconciled = false;\n private lastPrimary: StateContainer | null = null;\n private lastPrimaryPaths: PathSet = NO_PATHS;\n private lastEntries: Map<StateContainer, DepEntry> | null = null;\n private disarms: Array<() => void> | null = null;\n\n constructor(\n private readonly consumerId: string,\n private readonly onChange: () => void,\n ) {}\n\n private get refId(): string {\n return `useBloc@${this.consumerId}:dep`;\n }\n\n begin(primary: StateContainer, paths: PathSet): void {\n this.entries?.clear();\n if (this.disarms !== null) this.disarms.length = 0;\n this.primary = primary;\n this.primaryParts = [paths];\n }\n\n record(\n dep: StateContainer,\n paths: PathSet,\n Type: StateContainerConstructor,\n key: string,\n args: unknown,\n disarm: () => void,\n ): void {\n (this.disarms ??= []).push(disarm);\n // A dep that resolves to the primary only widens the primary's entry.\n if (dep === this.primary) {\n this.primaryParts.push(paths);\n return;\n }\n const entries = (this.entries ??= new Map());\n const existing = entries.get(dep);\n if (existing !== undefined) {\n existing.parts.push(paths);\n } else {\n entries.set(dep, { parts: [paths], paths, Type, key, args });\n }\n }\n\n /**\n * Merge each container's per-call sets once the render's reads are done.\n * Returns the primary's paths, widened by deps that resolved to it.\n */\n settle(): PathSet {\n this.primaryPaths = mergeParts(this.primaryParts);\n if (this.entries !== null) {\n for (const entry of this.entries.values()) {\n entry.paths = mergeParts(entry.parts);\n }\n }\n return this.primaryPaths;\n }\n\n /** Stops every dep tracked this render from recording further reads. */\n disarm(): void {\n const disarms = this.disarms;\n if (disarms === null) return;\n for (const disarm of disarms) disarm();\n disarms.length = 0;\n }\n\n /** Forces the next `reconcile` to run in full. */\n invalidate(): void {\n this.reconciled = false;\n }\n\n isUnchanged(): boolean {\n if (\n !this.reconciled ||\n this.lastPrimary !== this.primary ||\n !pathSetEquals(this.lastPrimaryPaths, this.primaryPaths)\n ) {\n return false;\n }\n const last = this.lastEntries;\n const entries = this.entries;\n if ((last?.size ?? 0) !== (entries?.size ?? 0)) return false;\n if (last === null || entries === null) return true;\n for (const [container, entry] of entries) {\n const prev = last.get(container);\n if (prev === undefined || !sameEntry(prev, entry)) return false;\n }\n return true;\n }\n\n reconcile(registry: StateContainerRegistry): void {\n const { consumerId } = this;\n const entries = this.entries;\n this.reconciled = true;\n this.lastPrimary = this.primary;\n this.lastPrimaryPaths = this.primaryPaths;\n this.lastEntries =\n entries === null || entries.size === 0 ? null : new Map(entries);\n\n const subs = this.subs;\n if (subs !== null) {\n for (const [dep, sub] of subs) {\n if (entries?.has(dep) !== true) {\n this.drop(dep, sub);\n subs.delete(dep);\n }\n }\n }\n if (entries === null) return;\n\n for (const [dep, entry] of entries) {\n // StrictMode's simulated unmount can dispose a dep this render captured;\n // re-render so the session resolves the live instance.\n if (dep.$blac.disposed) {\n this.onChange();\n continue;\n }\n dep.registerConsumerPaths(consumerId, entry.paths);\n const existing = subs?.get(dep);\n if (existing) {\n existing.subscription.setInterest(entry.paths);\n continue;\n }\n const refId = this.refId;\n const live = registry.acquire(entry.Type, entry.key, {\n canCreate: true,\n countRef: true,\n refId,\n args: entry.args,\n });\n // The key now maps to another instance; re-render against that one.\n if (live !== dep) {\n registry.release(entry.Type, entry.key, false, refId);\n this.onChange();\n continue;\n }\n const subscription = dep.subscribePaths(this.onChange);\n subscription.setInterest(entry.paths);\n (this.subs ??= new Map()).set(dep, {\n subscription,\n registry,\n Type: entry.Type,\n key: entry.key,\n });\n }\n }\n\n dispose(): void {\n const subs = this.subs;\n if (subs !== null) {\n for (const [dep, sub] of subs) this.drop(dep, sub);\n subs.clear();\n }\n this.reconciled = false;\n }\n\n private drop(dep: StateContainer, sub: DepSub): void {\n sub.subscription.unsubscribe();\n dep.unregisterConsumer(this.consumerId);\n sub.registry.release(sub.Type, sub.key, false, this.refId);\n }\n}\n\nfunction sameEntry(a: DepEntry, b: DepEntry): boolean {\n return (\n pathSetEquals(a.paths, b.paths) &&\n a.key === b.key &&\n Object.is(a.args, b.args)\n );\n}\n\nfunction mergeParts(parts: PathSet[]): PathSet {\n if (parts.length === 1) return parts[0];\n const out = new Set<number>();\n for (const part of parts) {\n if (part === ALL_PATHS) return ALL_PATHS;\n for (const id of part) out.add(id);\n }\n return out;\n}\n\ninterface DepAccessOptionsLike {\n args?: unknown;\n}\n\n/** Structural shape of a branded `depend()` handle as seen from React. */\nexport interface DepHandleLike {\n track(options?: DepAccessOptionsLike): [unknown, StateContainer];\n untracked(options?: DepAccessOptionsLike): StateContainer;\n readonly [DEP_BRAND]: {\n Type: StateContainerConstructor;\n defaultArgs?: unknown;\n };\n}\n\n/**\n * Wrap a dep handle read inside a tracked getter. During a render (`tracked`\n * is set) `.track()` records the dep's read paths into `session` and returns\n * a tracked proxy, so the dep's own getters track too. Outside a render it\n * returns the live `[state, dep]`, like the core handle.\n *\n * Args resolve per call, so one handle can reach several instances; proxies\n * are cached per resolved instance.\n */\nexport function makeDepWrapper(\n handle: DepHandleLike,\n registry: StateContainerRegistry,\n session: DepSession,\n tracked: { current: unknown },\n onDepHandle: (handle: object) => unknown,\n): DepHandleLike {\n const brand = handle[DEP_BRAND];\n // Weak, so instances a getter stops reaching (e.g. by switching args) can\n // be collected once disposed.\n const perDep = new WeakMap<\n StateContainer,\n { ref: { current: unknown }; proxy: StateContainer }\n >();\n const proxyCache = new ProxyCache();\n\n const resolve = (options?: DepAccessOptionsLike) => {\n const args = options?.args ?? brand.defaultArgs;\n const key = registry.resolveKey(brand.Type, undefined, args);\n const dep = registry.acquire(brand.Type, key, {\n canCreate: true,\n countRef: false,\n args,\n sweepIfUnowned: tracked.current != null,\n }) as unknown as StateContainer;\n return { dep, key, args };\n };\n\n const wrapper = {\n untracked: (options?: DepAccessOptionsLike) => resolve(options).dep,\n track: (options?: DepAccessOptionsLike) => {\n const { dep, key, args } = resolve(options);\n if (tracked.current == null) return [dep.state, dep];\n\n const result = trackRender(dep.state, dep.interner, proxyCache);\n let cache = perDep.get(dep);\n if (cache === undefined) {\n const ref = { current: result.value as unknown };\n cache = { ref, proxy: buildTrackedProxy(dep, ref, onDepHandle) };\n perDep.set(dep, cache);\n } else {\n cache.ref.current = result.value;\n }\n session.record(dep, result.paths, brand.Type, key, args, result.disarm);\n return [result.value, cache.proxy];\n },\n } as DepHandleLike;\n\n Object.defineProperty(wrapper, DEP_BRAND, {\n value: brand,\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n return wrapper;\n}\n","import { useLayoutEffect, useRef, useSyncExternalStore } from 'react';\nimport {\n isIsolatedClass,\n type ExtractArgs,\n type ExtractState,\n type InstanceState,\n type StateContainer,\n type StateContainerConstructor,\n type StateContainerRegistry,\n} from '@blac/core';\nimport {\n ALL_PATHS,\n emptyPathSet,\n trackRender,\n ProxyCache,\n type PathSet,\n type PathSubscription,\n} from '@dirtytalk/structural';\nimport { useProvidedArgs } from './BlocProvider';\nimport { useRegistry } from './RegistryProvider';\nimport { buildTrackedProxy } from './buildTrackedProxy';\nimport { DepSession, makeDepWrapper, type DepHandleLike } from './depSession';\nimport type { UseBlocOptions, UseBlocReturn } from './types';\n\nlet nextConsumerId = 0;\n\n/**\n * React hook that connects a component to a state container with automatic\n * re-render on state changes.\n *\n * Two tracking modes:\n * - **Auto-tracking** (default): the returned state value is a proxy that\n * records read paths during render. The component re-renders when any\n * recorded path changes.\n * - **Manual select**: pass `options.select` to opt out of auto-tracking.\n * The hook re-renders only when the returned array's elements change\n * (per-index `Object.is`).\n *\n * Lifecycle:\n * - The bloc is acquired from the registry on mount and released on\n * unmount. The instance key is derived from `options.args` (own args),\n * then the surrounding {@link BlocProvider} context args for this bloc,\n * then the default key (no args).\n * - `options.onMount` fires after the bloc is acquired; `options.onUnmount`\n * fires *before* the registry releases its ref, so the bloc is still\n * alive when the callback runs.\n *\n * Per-mount private instance: mark the class `@blac({ isolated: true })`, or\n * pass a unique key arg yourself (e.g. `useId()` in `args`).\n *\n * @typeParam T - The state container constructor type (inferred from BlocClass)\n * @param BlocClass - The state container class to connect to\n * @param options - Configuration options\n * @returns Tuple of `[state, bloc]`\n *\n * @example Basic usage\n * ```ts\n * const [state, bloc] = useBloc(MyBloc);\n * ```\n *\n * @example Manual select\n * ```ts\n * const [state, bloc] = useBloc(MyBloc, {\n * select: (state) => [state.count],\n * });\n * ```\n *\n * @example Args-based shared instance\n * ```ts\n * const [state, bloc] = useBloc(UserBloc, { args: { userId: 'alice' } });\n * ```\n * @public\n */\nexport function useBloc<\n T extends StateContainerConstructor = StateContainerConstructor,\n>(\n BlocClass: T,\n options?: UseBlocOptions<T>,\n): UseBlocReturn<T, ExtractState<T>> {\n const registry = useRegistry();\n\n const consumerRef = useRef<Consumer | null>(null);\n const consumer = (consumerRef.current ??= createConsumer());\n\n consumer.select = options?.select;\n consumer.onMount = options?.onMount;\n consumer.onUnmount = options?.onUnmount;\n\n consumer.ownArgs = (options as { args?: ExtractArgs<T> } | undefined)?.args;\n consumer.providerArgs = useProvidedArgs(BlocClass);\n const effectiveArgs = resolveEffectiveArgs(consumer);\n const sharedKey = registry.resolveKey(BlocClass, undefined, effectiveArgs);\n const instanceKey = isIsolatedClass(BlocClass)\n ? `${sharedKey}#${consumer.id}`\n : sharedKey;\n\n // A memo kept on the consumer rather than in `useMemo`: no hook, deps array\n // or factory closure per mount. `rebindNonce` is bumped by the commit when\n // the rendered instance was disposed and recreated before commit (a\n // same-commit owner handoff, or StrictMode's double effect); the tracked\n // proxy can't be retargeted in place. Keyed on `BlocClass` as well as the\n // key: classes without args or a `static key` share the default key, so\n // `useBloc(cond ? A : B)` must still re-subscribe on a class swap.\n if (\n consumer.BlocClass !== BlocClass ||\n consumer.instanceKey !== instanceKey ||\n consumer.registry !== registry ||\n consumer.boundNonce !== consumer.rebindNonce\n ) {\n bind(consumer, BlocClass, instanceKey, registry, effectiveArgs);\n }\n\n useSyncExternalStore(\n consumer.subscribe,\n consumer.getSnapshot,\n consumer.getSnapshot,\n );\n\n const container = consumer.container;\n const rawState = container.state as ExtractState<T>;\n consumer.renderedState = rawState;\n const select = consumer.select;\n consumer.isSelectMode = select !== undefined;\n let state: ExtractState<T>;\n if (select !== undefined) {\n state = rawState;\n // Recomputed every render: the selector may close over props.\n consumer.selection = select(rawState, container as InstanceState<T>);\n setInterest(consumer, ALL_PATHS);\n } else {\n const tracked = trackRender(\n rawState,\n container.interner,\n (consumer.proxyCache ??= new ProxyCache()),\n );\n state = tracked.value as ExtractState<T>;\n consumer.tracked.current = tracked.value;\n consumer.paths = tracked.paths;\n // Run at commit, so reads that outlive the render (effects, handlers)\n // don't add paths.\n consumer.disarm = tracked.disarm;\n // `tracked.paths` is still empty here and fills during JSX, so paths are\n // registered at commit, not now.\n consumer.deps.begin(container, tracked.paths);\n }\n\n // Stable callbacks that read this render's inputs from the consumer.\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(consumer.commit);\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(consumer.mount, NO_DEPS);\n\n return [state, consumer.trackedBloc] as UseBlocReturn<T, ExtractState<T>>;\n}\n\nconst noop = (): void => {};\n\n// Initial `interest` and `paths`; neither is ever mutated in place.\nconst NO_PATHS: PathSet = emptyPathSet();\nconst NO_DEPS: [] = [];\n\n/**\n * Everything one `useBloc` call keeps between renders. Its callbacks are\n * created once, so a render allocates no closures.\n *\n * `bump` increments `version` before calling `notify`: uSES requires\n * `getSnapshot()` to reflect the change by the time it is notified.\n */\ninterface Consumer {\n id: string;\n primaryRefId: string;\n // The render's inputs, read by the commit of that render.\n BlocClass: StateContainerConstructor;\n instanceKey: string;\n registry: StateContainerRegistry;\n /** Written only by `bind`. */\n container: StateContainer;\n trackedBloc: unknown;\n subscribe: (onStoreChange: () => void) => () => void;\n boundNonce: number;\n rebindNonce: number;\n renderedState: unknown;\n version: number;\n /** `noop` while unsubscribed. */\n notify: () => void;\n bump: () => void;\n getSnapshot: () => number;\n onChange: () => void;\n unsubscribe: () => void;\n commit: () => void;\n mount: () => () => void;\n /** The committed `paths`, or `ALL_PATHS` in select mode. */\n interest: PathSet;\n /** `null` while unsubscribed. */\n subscription: PathSubscription | null;\n subscribedTo: StateContainer | null;\n paths: PathSet;\n isSelectMode: boolean;\n selection: unknown[] | null;\n deps: DepSession;\n // Typed loosely: `T` is per call site.\n select: ((state: any, bloc: any) => unknown[]) | undefined;\n onMount: ((bloc: any) => void) | undefined;\n onUnmount: ((bloc: any) => void) | undefined;\n ownArgs: unknown;\n providerArgs: unknown;\n /** The render's tracking proxy; `null` outside a tracked render. */\n tracked: { current: unknown };\n disarm: (() => void) | null;\n proxyCache: ProxyCache | null;\n // The instance the consumer holds a ref on, claimed at commit so acquire\n // and release pair exactly.\n ownedClass: StateContainerConstructor | null;\n ownedKey: string;\n ownedRegistry: StateContainerRegistry | null;\n ownedBloc: unknown;\n}\n\nfunction createConsumer(): Consumer {\n const id = `useBloc-${nextConsumerId++}`;\n const consumer: Consumer = {\n id,\n primaryRefId: `useBloc@${id}`,\n BlocClass: null as unknown as StateContainerConstructor,\n instanceKey: '',\n registry: null as unknown as StateContainerRegistry,\n container: null as unknown as StateContainer,\n trackedBloc: null,\n subscribe: null as unknown as Consumer['subscribe'],\n boundNonce: 0,\n rebindNonce: 0,\n renderedState: undefined,\n version: 0,\n notify: noop,\n bump: () => {\n consumer.version++;\n consumer.notify();\n },\n getSnapshot: () => consumer.version,\n onChange: () => onChange(consumer),\n unsubscribe: () => unsubscribe(consumer),\n commit: () => commit(consumer),\n mount: () => teardown,\n interest: NO_PATHS,\n subscription: null,\n subscribedTo: null,\n paths: NO_PATHS,\n isSelectMode: false,\n selection: null,\n deps: null as unknown as DepSession,\n select: undefined,\n onMount: undefined,\n onUnmount: undefined,\n ownArgs: undefined,\n providerArgs: undefined,\n tracked: { current: null },\n disarm: null,\n proxyCache: null,\n ownedClass: null,\n ownedKey: '',\n ownedRegistry: null,\n ownedBloc: null,\n };\n const teardown = (): void => {\n release(consumer);\n consumer.deps.dispose();\n };\n consumer.deps = new DepSession(id, consumer.bump);\n return consumer;\n}\n\nfunction bind(\n consumer: Consumer,\n BlocClass: StateContainerConstructor,\n instanceKey: string,\n registry: StateContainerRegistry,\n args: unknown,\n): void {\n // No ref during render; the commit claims it. The registry sweeps the\n // instance if no commit ever does (SSR, discarded renders).\n const instance = registry.acquire(BlocClass, instanceKey, {\n canCreate: true,\n countRef: false,\n args: args as ExtractArgs<StateContainerConstructor>,\n sweepIfUnowned: true,\n }) as unknown as StateContainer;\n\n // Dep proxies get `onDepHandle` too, so nested `.track()` calls (A→B→C)\n // record into this consumer's session.\n let depWrappers: Map<object, unknown> | undefined;\n const onDepHandle = (handle: object): unknown => {\n depWrappers ??= new Map();\n const cached = depWrappers.get(handle);\n if (cached !== undefined) return cached;\n const wrapper = makeDepWrapper(\n handle as DepHandleLike,\n registry,\n consumer.deps,\n consumer.tracked,\n onDepHandle,\n );\n depWrappers.set(handle, wrapper);\n return wrapper;\n };\n\n consumer.BlocClass = BlocClass;\n consumer.instanceKey = instanceKey;\n consumer.registry = registry;\n consumer.boundNonce = consumer.rebindNonce;\n consumer.container = instance;\n consumer.trackedBloc = buildTrackedProxy(\n instance,\n consumer.tracked,\n onDepHandle,\n );\n // A new identity per instance, so uSES re-subscribes.\n consumer.subscribe = (onStoreChange) => subscribe(consumer, onStoreChange);\n}\n\nfunction subscribe(consumer: Consumer, onStoreChange: () => void): () => void {\n consumer.notify = onStoreChange;\n const container = consumer.container;\n const subscription = container.subscribePaths(consumer.onChange);\n subscription.setInterest(consumer.interest);\n consumer.subscription = subscription;\n consumer.subscribedTo = container;\n container.registerConsumerPaths(consumer.id, consumer.paths);\n // An emit raised during render is still pending in the channel, so its\n // next flush delivers it to this subscriber.\n return consumer.unsubscribe;\n}\n\nfunction unsubscribe(consumer: Consumer): void {\n consumer.notify = noop;\n consumer.subscription?.unsubscribe();\n consumer.subscription = null;\n consumer.subscribedTo?.unregisterConsumer(consumer.id);\n consumer.subscribedTo = null;\n}\n\nfunction onChange(consumer: Consumer): void {\n if (consumer.isSelectMode) {\n const select = consumer.select;\n const container = consumer.subscribedTo;\n if (select && container !== null) {\n const next = select(container.state, container);\n const prev = consumer.selection;\n if (prev !== null && shallowArrayEqual(prev, next)) return;\n consumer.selection = next;\n }\n }\n consumer.bump();\n}\n\nfunction claim(consumer: Consumer): void {\n const { BlocClass, instanceKey, registry } = consumer;\n if (\n consumer.ownedClass === BlocClass &&\n consumer.ownedKey === instanceKey &&\n consumer.ownedRegistry === registry\n ) {\n return;\n }\n release(consumer);\n const live = registry.acquire(BlocClass, instanceKey, {\n canCreate: true,\n countRef: true,\n refId: consumer.primaryRefId,\n args: resolveEffectiveArgs(\n consumer,\n ) as ExtractArgs<StateContainerConstructor>,\n });\n consumer.ownedClass = BlocClass;\n consumer.ownedKey = instanceKey;\n consumer.ownedRegistry = registry;\n consumer.ownedBloc = live;\n consumer.onMount?.(live);\n // The render captured a stale instance; re-render against the live one.\n if (live !== (consumer.container as unknown)) {\n consumer.rebindNonce++;\n consumer.bump();\n }\n}\n\nfunction release(consumer: Consumer): void {\n const { ownedClass, ownedRegistry } = consumer;\n if (ownedClass === null || ownedRegistry === null) return;\n // Before release, so the bloc is still alive in the callback.\n consumer.onUnmount?.(consumer.ownedBloc);\n consumer.ownedClass = null;\n consumer.ownedRegistry = null;\n ownedRegistry.release(\n ownedClass,\n consumer.ownedKey,\n false,\n consumer.primaryRefId,\n );\n}\n\nfunction commit(consumer: Consumer): void {\n claim(consumer);\n // Getters called after commit read live state, not this render's proxy.\n consumer.tracked.current = null;\n const disarm = consumer.disarm;\n if (disarm !== null) {\n consumer.disarm = null;\n disarm();\n }\n const deps = consumer.deps;\n deps.disarm();\n if (consumer.isSelectMode) {\n // A later switch back to auto-track must reconcile in full.\n deps.invalidate();\n return;\n }\n const primary = consumer.container;\n // A change between render and commit was diffed against the previous\n // interest, which may lack paths only this render read.\n if (primary.state !== consumer.renderedState) consumer.bump();\n consumer.paths = deps.settle();\n if (deps.isUnchanged()) return;\n\n primary.registerConsumerPaths(consumer.id, consumer.paths);\n setInterest(consumer, consumer.paths);\n deps.reconcile(consumer.registry);\n}\n\nconst setInterest = (consumer: Consumer, interest: PathSet): void => {\n consumer.interest = interest;\n consumer.subscription?.setInterest(interest);\n};\n\n// Own args win over provider args. Shared by render and the ownership effect,\n// which recreates a disposed instance and must not drop its args.\nconst resolveEffectiveArgs = (consumer: Consumer): unknown =>\n consumer.ownArgs !== undefined ? consumer.ownArgs : consumer.providerArgs;\n\nconst shallowArrayEqual = (a: unknown[], b: unknown[]): boolean => {\n if (a === b) return true;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!Object.is(a[i], b[i])) return false;\n }\n return true;\n};\n","import { useEffect, useId, useRef } from 'react';\nimport type { StateContainer } from '@blac/core';\nimport { APPLY_DEPS, REMOVE_DEPS_OWNER } from '@blac/core/internal';\n\n/**\n * Feed live values from the component into a bloc's `deps` lane.\n *\n * Each calling component is one owner: its slice is shallow-merged into the\n * bloc's merged `deps` view and withdrawn on unmount. The bloc observes\n * changes through `onDepsChanged`.\n *\n * ```tsx\n * const [state, bloc] = useBloc(CanvasCubit);\n * useBlocDeps(bloc, { canvas, onTick });\n * ```\n *\n * Use this for values a bloc must *read* but should not own — DOM handles,\n * callbacks, values from other hooks. Pass identity-stable values (`useMemo` /\n * `useCallback`) as you would to any hook; a new function identity each render\n * counts as a change.\n */\nexport function useBlocDeps<D extends object>(\n bloc: StateContainer<any, any, D>,\n slice: Partial<D>,\n): void {\n const ownerId = useId();\n const latest = useRef(slice);\n latest.current = slice;\n\n // `APPLY_DEPS` shallow-compares and no-ops on an unchanged slice, so applying\n // on every commit is cheaper than asking callers to maintain a dep array.\n useEffect(() => {\n bloc[APPLY_DEPS](ownerId, latest.current);\n });\n\n useEffect(() => {\n return () => {\n bloc[REMOVE_DEPS_OWNER](ownerId);\n };\n }, [bloc, ownerId]);\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -152,6 +152,7 @@ var DepSession = class {
152
152
  this.consumerId = consumerId;
153
153
  this.onChange = onChange;
154
154
  this.primary = null;
155
+ this.primaryParts = [];
155
156
  this.primaryPaths = NO_PATHS$1;
156
157
  this.entries = null;
157
158
  this.subs = null;
@@ -168,24 +169,34 @@ var DepSession = class {
168
169
  this.entries?.clear();
169
170
  if (this.disarms !== null) this.disarms.length = 0;
170
171
  this.primary = primary;
171
- this.primaryPaths = paths;
172
+ this.primaryParts = [paths];
172
173
  }
173
174
  record(dep, paths, Type, key, args, disarm) {
174
175
  (this.disarms ??= []).push(disarm);
175
176
  if (dep === this.primary) {
176
- this.primaryPaths = unionPaths(this.primaryPaths, paths);
177
+ this.primaryParts.push(paths);
177
178
  return;
178
179
  }
179
180
  const entries = this.entries ??= /* @__PURE__ */ new Map();
180
181
  const existing = entries.get(dep);
181
- if (existing !== void 0) existing.paths = unionPaths(existing.paths, paths);
182
+ if (existing !== void 0) existing.parts.push(paths);
182
183
  else entries.set(dep, {
184
+ parts: [paths],
183
185
  paths,
184
186
  Type,
185
187
  key,
186
188
  args
187
189
  });
188
190
  }
191
+ /**
192
+ * Merge each container's per-call sets once the render's reads are done.
193
+ * Returns the primary's paths, widened by deps that resolved to it.
194
+ */
195
+ settle() {
196
+ this.primaryPaths = mergeParts(this.primaryParts);
197
+ if (this.entries !== null) for (const entry of this.entries.values()) entry.paths = mergeParts(entry.parts);
198
+ return this.primaryPaths;
199
+ }
189
200
  /** Stops every dep tracked this render from recording further reads. */
190
201
  disarm() {
191
202
  const disarms = this.disarms;
@@ -273,10 +284,13 @@ var DepSession = class {
273
284
  function sameEntry(a, b) {
274
285
  return pathSetEquals(a.paths, b.paths) && a.key === b.key && Object.is(a.args, b.args);
275
286
  }
276
- function unionPaths(a, b) {
277
- if (a === ALL_PATHS || b === ALL_PATHS) return ALL_PATHS;
278
- const out = new Set(a);
279
- for (const id of b) out.add(id);
287
+ function mergeParts(parts) {
288
+ if (parts.length === 1) return parts[0];
289
+ const out = /* @__PURE__ */ new Set();
290
+ for (const part of parts) {
291
+ if (part === ALL_PATHS) return ALL_PATHS;
292
+ for (const id of part) out.add(id);
293
+ }
280
294
  return out;
281
295
  }
282
296
  /**
@@ -290,7 +304,7 @@ function unionPaths(a, b) {
290
304
  */
291
305
  function makeDepWrapper(handle, registry, session, tracked, onDepHandle) {
292
306
  const brand = handle[DEP_BRAND];
293
- const perDep = /* @__PURE__ */ new Map();
307
+ const perDep = /* @__PURE__ */ new WeakMap();
294
308
  const proxyCache = new ProxyCache();
295
309
  const resolve = (options) => {
296
310
  const args = options?.args ?? brand.defaultArgs;
@@ -572,6 +586,7 @@ function commit(consumer) {
572
586
  }
573
587
  const primary = consumer.container;
574
588
  if (primary.state !== consumer.renderedState) consumer.bump();
589
+ consumer.paths = deps.settle();
575
590
  if (deps.isUnchanged()) return;
576
591
  primary.registerConsumerPaths(consumer.id, consumer.paths);
577
592
  setInterest(consumer, consumer.paths);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["NO_PATHS"],"sources":["../src/BlocProvider.tsx","../src/buildTrackedProxy.ts","../src/depSession.ts","../src/useBloc.ts","../src/useBlocDeps.ts"],"sourcesContent":["import {\n createContext,\n useContext,\n useMemo,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport type { ExtractArgs, StateContainerConstructor } from '@blac/core';\n\n/**\n * Internal context value: a Map from bloc constructor to the args object\n * provided by the nearest BlocProvider for that bloc class.\n *\n * Each provider copies its parent's map and adds its own entry, so nested\n * providers for different blocs compose without clobbering each other.\n */\ntype ProvidedArgsMap = Map<StateContainerConstructor, unknown>;\n\nconst ProvidedArgsContext = createContext<ProvidedArgsMap>(new Map());\n\n/**\n * Props for {@link BlocProvider}.\n * @public\n */\nexport interface BlocProviderProps<T extends StateContainerConstructor> {\n /**\n * The bloc class whose args are being provided to descendants.\n */\n bloc: T;\n /**\n * Args that descendant `useBloc(bloc)` calls will resolve to when no\n * own `args` are given.\n */\n args: ExtractArgs<T>;\n children: ReactNode;\n}\n\n/**\n * Provides args to descendant `useBloc` calls for a specific bloc class via\n * React context. Own `args` on the `useBloc` call always win.\n *\n * Multiple `BlocProvider` wrappers for different bloc classes compose: each\n * merges its entry into the inherited map, so nested providers for different\n * blocs do not interfere.\n *\n * @example\n * ```tsx\n * <BlocProvider bloc={UserBloc} args={{ userId: 'alice' }}>\n * <UserProfile />\n * </BlocProvider>\n * ```\n *\n * @example Per-mount private instance\n * ```tsx\n * const id = useId();\n * <BlocProvider bloc={CartBloc} args={{ _id: id }}>\n * <CartWidget />\n * </BlocProvider>\n * ```\n * @public\n */\nexport function BlocProvider<T extends StateContainerConstructor>({\n bloc,\n args,\n children,\n}: BlocProviderProps<T>): ReactElement {\n const parentMap = useContext(ProvidedArgsContext);\n\n // Keyed on the args content, not identity, so an inline `args={{ ... }}`\n // literal does not rebuild the map (and re-render every consumer) on\n // every render, while a real field change still reaches `useProvidedArgs`.\n const argsKey = JSON.stringify(args);\n\n // Merge our entry into a new Map so sibling/parent providers for other blocs\n // are preserved.\n const mergedMap = useMemo(() => {\n const next = new Map(parentMap);\n next.set(bloc, args);\n return next;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [parentMap, bloc, argsKey]);\n\n return (\n <ProvidedArgsContext.Provider value={mergedMap}>\n {children}\n </ProvidedArgsContext.Provider>\n );\n}\n\n/**\n * Returns the args provided by the nearest {@link BlocProvider} for the given\n * bloc class, or `undefined` when called outside a matching provider.\n *\n * Used by `useBloc` to inherit provider args when no own `args` are given.\n * @public\n */\nexport function useProvidedArgs<T extends StateContainerConstructor>(\n BlocClass: T,\n): ExtractArgs<T> | undefined {\n const map = useContext(ProvidedArgsContext);\n return map.get(BlocClass) as ExtractArgs<T> | undefined;\n}\n","import { DEP_BRAND, WITH_TRACKED_STATE } from '@blac/core/internal';\n\ninterface TrackedStateTarget {\n [WITH_TRACKED_STATE]<R>(\n tracked: unknown,\n fn: () => R,\n onDepHandle?: (handle: object) => unknown,\n ): R;\n}\n\nfunction supportsTrackedState(value: object): value is TrackedStateTarget {\n return (\n typeof (value as TrackedStateTarget)[WITH_TRACKED_STATE] === 'function'\n );\n}\n\ntype Getter = (this: object) => unknown;\ntype Method = (...args: unknown[]) => unknown;\n\n// Prototype getters per class prototype. The chain is static, so walking it\n// once per class (not once per mounted component) keeps the get trap O(1).\nconst gettersByProto = new WeakMap<object, Map<string | symbol, Getter>>();\n\nfunction collectGetters(proto: object): Map<string | symbol, Getter> {\n const cached = gettersByProto.get(proto);\n if (cached !== undefined) return cached;\n const getters = new Map<string | symbol, Getter>();\n let p: object | null = proto;\n while (p !== null && p !== Object.prototype) {\n for (const key of Reflect.ownKeys(p)) {\n const desc = Object.getOwnPropertyDescriptor(p, key);\n // A descriptor getter, always `.call`ed with an explicit receiver.\n // oxlint-disable-next-line typescript/unbound-method\n if (desc?.get && !getters.has(key)) getters.set(key, desc.get);\n }\n p = Object.getPrototypeOf(p);\n }\n gettersByProto.set(proto, getters);\n return getters;\n}\n\n// Methods on the prototype, so a mount allocates the handler and the proxy\n// only, not a set of closures.\nclass BlocHandler implements ProxyHandler<object> {\n // Bound methods are cached so `bloc.method` keeps a stable identity across\n // reads — an unstable one would defeat memoisation in consumers. Keyed by\n // the underlying function too, so a reassigned method is re-bound.\n private boundMethods:\n | Map<string | symbol, { fn: Method; bound: Method }>\n | undefined = undefined;\n\n constructor(\n private readonly getters: Map<string | symbol, Getter>,\n private readonly tracksState: boolean,\n private readonly trackedStateRef: { current: unknown },\n private readonly onDepHandle: ((handle: object) => unknown) | undefined,\n ) {}\n\n get(target: object, key: string | symbol): unknown {\n const getter = this.getters.get(key);\n if (getter !== undefined) {\n const tracked = this.trackedStateRef.current;\n // `[WITH_TRACKED_STATE]` invokes the getter with `this` = the real\n // instance, so no per-read thunk is needed.\n return this.wrapDepHandle(\n tracked == null || !this.tracksState\n ? getter.call(target)\n : (target as TrackedStateTarget)[WITH_TRACKED_STATE](\n tracked,\n getter,\n this.onDepHandle,\n ),\n );\n }\n\n // Receiver is the real instance, so own fields keep their `#private`\n // brand. Methods are bound to it too, otherwise `bloc.method()` would\n // call with `this` = proxy and fail the same brand check.\n const value = Reflect.get(target, key, target);\n if (typeof value === 'function') {\n const fn = value as Method;\n this.boundMethods ??= new Map();\n let cached = this.boundMethods.get(key);\n if (cached === undefined || cached.fn !== fn) {\n cached = { fn, bound: fn.bind(target) };\n this.boundMethods.set(key, cached);\n }\n return this.wrapDepHandle(cached.bound);\n }\n return this.wrapDepHandle(value);\n }\n\n set(target: object, key: string | symbol, value: unknown): boolean {\n return Reflect.set(target, key, value, target);\n }\n\n private wrapDepHandle(value: unknown): unknown {\n if (\n this.onDepHandle !== undefined &&\n (typeof value === 'function' || typeof value === 'object') &&\n value !== null &&\n (value as Record<symbol, unknown>)[DEP_BRAND] !== undefined\n ) {\n return this.onDepHandle(value as object);\n }\n return value;\n }\n}\n\n/**\n * Build a per-consumer proxy for a bloc instance.\n *\n * Getters run with `this` bound to the real instance, not the proxy, so ES\n * `#private` fields and methods work in user blocs. Path recording is done by\n * `[WITH_TRACKED_STATE]`, which makes the instance's own `state` getter\n * report the render's tracking proxy for the call's duration; nested getter\n * chains stay tracked because the override remains set.\n *\n * One allocation per bloc acquisition (inside `useMemo`), so the proxy is\n * stable across renders.\n *\n * @param onDepHandle - Invoked when a read returns a branded dep handle;\n * returns the value (the session-bound wrapper) to expose in its place.\n * Must cache wrappers per handle to avoid re-allocation.\n */\nexport function buildTrackedProxy<T extends object>(\n instance: T,\n trackedStateRef: { current: unknown },\n onDepHandle?: (handle: object) => unknown,\n): T {\n // Both string- and symbol-keyed prototype getters (excluding\n // Object.prototype). Arrow-function class properties are own values, not\n // getters, and pass through unmodified.\n const handler = new BlocHandler(\n collectGetters(Object.getPrototypeOf(instance) as object),\n supportsTrackedState(instance),\n trackedStateRef,\n onDepHandle,\n );\n return new Proxy(instance, handler as ProxyHandler<T>);\n}\n","import type {\n StateContainer,\n StateContainerConstructor,\n StateContainerRegistry,\n} from '@blac/core';\nimport { DEP_BRAND } from '@blac/core/internal';\nimport {\n ALL_PATHS,\n emptyPathSet,\n pathSetEquals,\n trackRender,\n ProxyCache,\n type PathSet,\n type PathSubscription,\n} from '@dirtytalk/structural';\nimport { buildTrackedProxy } from './buildTrackedProxy';\n\nconst NO_PATHS: PathSet = emptyPathSet();\n\ninterface DepEntry {\n paths: PathSet;\n Type: StateContainerConstructor;\n key: string;\n args: unknown;\n}\n\ninterface DepSub {\n subscription: PathSubscription;\n registry: StateContainerRegistry;\n Type: StateContainerConstructor;\n key: string;\n}\n\n/**\n * The containers one `useBloc` consumer reads during a render: its primary\n * bloc plus every dep reached through `this.<handle>.track()`.\n *\n * The render records entries; the commit calls `reconcile`, which subscribes\n * and takes a ref on new deps and drops the ones no longer read. `dispose`\n * releases everything. Refs are only taken at commit, so an uncommitted render\n * never leaks one.\n */\nexport class DepSession {\n // Most consumers read no deps, so everything dep-related is allocated on the\n // first dep a render records.\n private primary: StateContainer | null = null;\n private primaryPaths: PathSet = NO_PATHS;\n private entries: Map<StateContainer, DepEntry> | null = null;\n private subs: Map<StateContainer, DepSub> | null = null;\n private reconciled = false;\n private lastPrimary: StateContainer | null = null;\n private lastPrimaryPaths: PathSet = NO_PATHS;\n private lastEntries: Map<StateContainer, DepEntry> | null = null;\n private disarms: Array<() => void> | null = null;\n\n constructor(\n private readonly consumerId: string,\n private readonly onChange: () => void,\n ) {}\n\n private get refId(): string {\n return `useBloc@${this.consumerId}:dep`;\n }\n\n begin(primary: StateContainer, paths: PathSet): void {\n this.entries?.clear();\n if (this.disarms !== null) this.disarms.length = 0;\n this.primary = primary;\n this.primaryPaths = paths;\n }\n\n record(\n dep: StateContainer,\n paths: PathSet,\n Type: StateContainerConstructor,\n key: string,\n args: unknown,\n disarm: () => void,\n ): void {\n (this.disarms ??= []).push(disarm);\n // A dep that resolves to the primary only widens the primary's entry.\n if (dep === this.primary) {\n this.primaryPaths = unionPaths(this.primaryPaths, paths);\n return;\n }\n const entries = (this.entries ??= new Map());\n const existing = entries.get(dep);\n if (existing !== undefined) {\n existing.paths = unionPaths(existing.paths, paths);\n } else {\n entries.set(dep, { paths, Type, key, args });\n }\n }\n\n /** Stops every dep tracked this render from recording further reads. */\n disarm(): void {\n const disarms = this.disarms;\n if (disarms === null) return;\n for (const disarm of disarms) disarm();\n disarms.length = 0;\n }\n\n /** Forces the next `reconcile` to run in full. */\n invalidate(): void {\n this.reconciled = false;\n }\n\n isUnchanged(): boolean {\n if (\n !this.reconciled ||\n this.lastPrimary !== this.primary ||\n !pathSetEquals(this.lastPrimaryPaths, this.primaryPaths)\n ) {\n return false;\n }\n const last = this.lastEntries;\n const entries = this.entries;\n if ((last?.size ?? 0) !== (entries?.size ?? 0)) return false;\n if (last === null || entries === null) return true;\n for (const [container, entry] of entries) {\n const prev = last.get(container);\n if (prev === undefined || !sameEntry(prev, entry)) return false;\n }\n return true;\n }\n\n reconcile(registry: StateContainerRegistry): void {\n const { consumerId } = this;\n const entries = this.entries;\n this.reconciled = true;\n this.lastPrimary = this.primary;\n this.lastPrimaryPaths = this.primaryPaths;\n this.lastEntries =\n entries === null || entries.size === 0 ? null : new Map(entries);\n\n const subs = this.subs;\n if (subs !== null) {\n for (const [dep, sub] of subs) {\n if (entries?.has(dep) !== true) {\n this.drop(dep, sub);\n subs.delete(dep);\n }\n }\n }\n if (entries === null) return;\n\n for (const [dep, entry] of entries) {\n // StrictMode's simulated unmount can dispose a dep this render captured;\n // re-render so the session resolves the live instance.\n if (dep.$blac.disposed) {\n this.onChange();\n continue;\n }\n dep.registerConsumerPaths(consumerId, entry.paths);\n const existing = subs?.get(dep);\n if (existing) {\n existing.subscription.setInterest(entry.paths);\n continue;\n }\n const refId = this.refId;\n const live = registry.acquire(entry.Type, entry.key, {\n canCreate: true,\n countRef: true,\n refId,\n args: entry.args,\n });\n // The key now maps to another instance; re-render against that one.\n if (live !== dep) {\n registry.release(entry.Type, entry.key, false, refId);\n this.onChange();\n continue;\n }\n const subscription = dep.subscribePaths(this.onChange);\n subscription.setInterest(entry.paths);\n (this.subs ??= new Map()).set(dep, {\n subscription,\n registry,\n Type: entry.Type,\n key: entry.key,\n });\n }\n }\n\n dispose(): void {\n const subs = this.subs;\n if (subs !== null) {\n for (const [dep, sub] of subs) this.drop(dep, sub);\n subs.clear();\n }\n this.reconciled = false;\n }\n\n private drop(dep: StateContainer, sub: DepSub): void {\n sub.subscription.unsubscribe();\n dep.unregisterConsumer(this.consumerId);\n sub.registry.release(sub.Type, sub.key, false, this.refId);\n }\n}\n\nfunction sameEntry(a: DepEntry, b: DepEntry): boolean {\n return (\n pathSetEquals(a.paths, b.paths) &&\n a.key === b.key &&\n Object.is(a.args, b.args)\n );\n}\n\nfunction unionPaths(a: PathSet, b: PathSet): PathSet {\n if (a === ALL_PATHS || b === ALL_PATHS) return ALL_PATHS;\n const out = new Set<number>(a as Set<number>);\n for (const id of b as Set<number>) out.add(id);\n return out;\n}\n\ninterface DepAccessOptionsLike {\n args?: unknown;\n}\n\n/** Structural shape of a branded `depend()` handle as seen from React. */\nexport interface DepHandleLike {\n track(options?: DepAccessOptionsLike): [unknown, StateContainer];\n untracked(options?: DepAccessOptionsLike): StateContainer;\n readonly [DEP_BRAND]: {\n Type: StateContainerConstructor;\n defaultArgs?: unknown;\n };\n}\n\n/**\n * Wrap a dep handle read inside a tracked getter. During a render (`tracked`\n * is set) `.track()` records the dep's read paths into `session` and returns\n * a tracked proxy, so the dep's own getters track too. Outside a render it\n * returns the live `[state, dep]`, like the core handle.\n *\n * Args resolve per call, so one handle can reach several instances; proxies\n * are cached per resolved instance.\n */\nexport function makeDepWrapper(\n handle: DepHandleLike,\n registry: StateContainerRegistry,\n session: DepSession,\n tracked: { current: unknown },\n onDepHandle: (handle: object) => unknown,\n): DepHandleLike {\n const brand = handle[DEP_BRAND];\n const perDep = new Map<\n StateContainer,\n { ref: { current: unknown }; proxy: StateContainer }\n >();\n const proxyCache = new ProxyCache();\n\n const resolve = (options?: DepAccessOptionsLike) => {\n const args = options?.args ?? brand.defaultArgs;\n const key = registry.resolveKey(brand.Type, undefined, args);\n const dep = registry.acquire(brand.Type, key, {\n canCreate: true,\n countRef: false,\n args,\n sweepIfUnowned: tracked.current != null,\n }) as unknown as StateContainer;\n return { dep, key, args };\n };\n\n const wrapper = {\n untracked: (options?: DepAccessOptionsLike) => resolve(options).dep,\n track: (options?: DepAccessOptionsLike) => {\n const { dep, key, args } = resolve(options);\n if (tracked.current == null) return [dep.state, dep];\n\n const result = trackRender(dep.state, dep.interner, proxyCache);\n let cache = perDep.get(dep);\n if (cache === undefined) {\n const ref = { current: result.value as unknown };\n cache = { ref, proxy: buildTrackedProxy(dep, ref, onDepHandle) };\n perDep.set(dep, cache);\n } else {\n cache.ref.current = result.value;\n }\n session.record(dep, result.paths, brand.Type, key, args, result.disarm);\n return [result.value, cache.proxy];\n },\n } as DepHandleLike;\n\n Object.defineProperty(wrapper, DEP_BRAND, {\n value: brand,\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n return wrapper;\n}\n","import { useLayoutEffect, useRef, useSyncExternalStore } from 'react';\nimport {\n isIsolatedClass,\n type ExtractArgs,\n type ExtractState,\n type InstanceState,\n type StateContainer,\n type StateContainerConstructor,\n type StateContainerRegistry,\n} from '@blac/core';\nimport {\n ALL_PATHS,\n emptyPathSet,\n trackRender,\n ProxyCache,\n type PathSet,\n type PathSubscription,\n} from '@dirtytalk/structural';\nimport { useProvidedArgs } from './BlocProvider';\nimport { useRegistry } from './RegistryProvider';\nimport { buildTrackedProxy } from './buildTrackedProxy';\nimport { DepSession, makeDepWrapper, type DepHandleLike } from './depSession';\nimport type { UseBlocOptions, UseBlocReturn } from './types';\n\nlet nextConsumerId = 0;\n\n/**\n * React hook that connects a component to a state container with automatic\n * re-render on state changes.\n *\n * Two tracking modes:\n * - **Auto-tracking** (default): the returned state value is a proxy that\n * records read paths during render. The component re-renders when any\n * recorded path changes.\n * - **Manual select**: pass `options.select` to opt out of auto-tracking.\n * The hook re-renders only when the returned array's elements change\n * (per-index `Object.is`).\n *\n * Lifecycle:\n * - The bloc is acquired from the registry on mount and released on\n * unmount. The instance key is derived from `options.args` (own args),\n * then the surrounding {@link BlocProvider} context args for this bloc,\n * then the default key (no args).\n * - `options.onMount` fires after the bloc is acquired; `options.onUnmount`\n * fires *before* the registry releases its ref, so the bloc is still\n * alive when the callback runs.\n *\n * Per-mount private instance: mark the class `@blac({ isolated: true })`, or\n * pass a unique key arg yourself (e.g. `useId()` in `args`).\n *\n * @typeParam T - The state container constructor type (inferred from BlocClass)\n * @param BlocClass - The state container class to connect to\n * @param options - Configuration options\n * @returns Tuple of `[state, bloc]`\n *\n * @example Basic usage\n * ```ts\n * const [state, bloc] = useBloc(MyBloc);\n * ```\n *\n * @example Manual select\n * ```ts\n * const [state, bloc] = useBloc(MyBloc, {\n * select: (state) => [state.count],\n * });\n * ```\n *\n * @example Args-based shared instance\n * ```ts\n * const [state, bloc] = useBloc(UserBloc, { args: { userId: 'alice' } });\n * ```\n * @public\n */\nexport function useBloc<\n T extends StateContainerConstructor = StateContainerConstructor,\n>(\n BlocClass: T,\n options?: UseBlocOptions<T>,\n): UseBlocReturn<T, ExtractState<T>> {\n const registry = useRegistry();\n\n const consumerRef = useRef<Consumer | null>(null);\n const consumer = (consumerRef.current ??= createConsumer());\n\n consumer.select = options?.select;\n consumer.onMount = options?.onMount;\n consumer.onUnmount = options?.onUnmount;\n\n consumer.ownArgs = (options as { args?: ExtractArgs<T> } | undefined)?.args;\n consumer.providerArgs = useProvidedArgs(BlocClass);\n const effectiveArgs = resolveEffectiveArgs(consumer);\n const sharedKey = registry.resolveKey(BlocClass, undefined, effectiveArgs);\n const instanceKey = isIsolatedClass(BlocClass)\n ? `${sharedKey}#${consumer.id}`\n : sharedKey;\n\n // A memo kept on the consumer rather than in `useMemo`: no hook, deps array\n // or factory closure per mount. `rebindNonce` is bumped by the commit when\n // the rendered instance was disposed and recreated before commit (a\n // same-commit owner handoff, or StrictMode's double effect); the tracked\n // proxy can't be retargeted in place. Keyed on `BlocClass` as well as the\n // key: classes without args or a `static key` share the default key, so\n // `useBloc(cond ? A : B)` must still re-subscribe on a class swap.\n if (\n consumer.BlocClass !== BlocClass ||\n consumer.instanceKey !== instanceKey ||\n consumer.registry !== registry ||\n consumer.boundNonce !== consumer.rebindNonce\n ) {\n bind(consumer, BlocClass, instanceKey, registry, effectiveArgs);\n }\n\n useSyncExternalStore(\n consumer.subscribe,\n consumer.getSnapshot,\n consumer.getSnapshot,\n );\n\n const container = consumer.container;\n const rawState = container.state as ExtractState<T>;\n consumer.renderedState = rawState;\n const select = consumer.select;\n consumer.isSelectMode = select !== undefined;\n let state: ExtractState<T>;\n if (select !== undefined) {\n state = rawState;\n // Recomputed every render: the selector may close over props.\n consumer.selection = select(rawState, container as InstanceState<T>);\n setInterest(consumer, ALL_PATHS);\n } else {\n const tracked = trackRender(\n rawState,\n container.interner,\n (consumer.proxyCache ??= new ProxyCache()),\n );\n state = tracked.value as ExtractState<T>;\n consumer.tracked.current = tracked.value;\n consumer.paths = tracked.paths;\n // Run at commit, so reads that outlive the render (effects, handlers)\n // don't add paths.\n consumer.disarm = tracked.disarm;\n // `tracked.paths` is still empty here and fills during JSX, so paths are\n // registered at commit, not now.\n consumer.deps.begin(container, tracked.paths);\n }\n\n // Stable callbacks that read this render's inputs from the consumer.\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(consumer.commit);\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(consumer.mount, NO_DEPS);\n\n return [state, consumer.trackedBloc] as UseBlocReturn<T, ExtractState<T>>;\n}\n\nconst noop = (): void => {};\n\n// Initial `interest` and `paths`; neither is ever mutated in place.\nconst NO_PATHS: PathSet = emptyPathSet();\nconst NO_DEPS: [] = [];\n\n/**\n * Everything one `useBloc` call keeps between renders. Its callbacks are\n * created once, so a render allocates no closures.\n *\n * `bump` increments `version` before calling `notify`: uSES requires\n * `getSnapshot()` to reflect the change by the time it is notified.\n */\ninterface Consumer {\n id: string;\n primaryRefId: string;\n // The render's inputs, read by the commit of that render.\n BlocClass: StateContainerConstructor;\n instanceKey: string;\n registry: StateContainerRegistry;\n /** Written only by `bind`. */\n container: StateContainer;\n trackedBloc: unknown;\n subscribe: (onStoreChange: () => void) => () => void;\n boundNonce: number;\n rebindNonce: number;\n renderedState: unknown;\n version: number;\n /** `noop` while unsubscribed. */\n notify: () => void;\n bump: () => void;\n getSnapshot: () => number;\n onChange: () => void;\n unsubscribe: () => void;\n commit: () => void;\n mount: () => () => void;\n /** The committed `paths`, or `ALL_PATHS` in select mode. */\n interest: PathSet;\n /** `null` while unsubscribed. */\n subscription: PathSubscription | null;\n subscribedTo: StateContainer | null;\n paths: PathSet;\n isSelectMode: boolean;\n selection: unknown[] | null;\n deps: DepSession;\n // Typed loosely: `T` is per call site.\n select: ((state: any, bloc: any) => unknown[]) | undefined;\n onMount: ((bloc: any) => void) | undefined;\n onUnmount: ((bloc: any) => void) | undefined;\n ownArgs: unknown;\n providerArgs: unknown;\n /** The render's tracking proxy; `null` outside a tracked render. */\n tracked: { current: unknown };\n disarm: (() => void) | null;\n proxyCache: ProxyCache | null;\n // The instance the consumer holds a ref on, claimed at commit so acquire\n // and release pair exactly.\n ownedClass: StateContainerConstructor | null;\n ownedKey: string;\n ownedRegistry: StateContainerRegistry | null;\n ownedBloc: unknown;\n}\n\nfunction createConsumer(): Consumer {\n const id = `useBloc-${nextConsumerId++}`;\n const consumer: Consumer = {\n id,\n primaryRefId: `useBloc@${id}`,\n BlocClass: null as unknown as StateContainerConstructor,\n instanceKey: '',\n registry: null as unknown as StateContainerRegistry,\n container: null as unknown as StateContainer,\n trackedBloc: null,\n subscribe: null as unknown as Consumer['subscribe'],\n boundNonce: 0,\n rebindNonce: 0,\n renderedState: undefined,\n version: 0,\n notify: noop,\n bump: () => {\n consumer.version++;\n consumer.notify();\n },\n getSnapshot: () => consumer.version,\n onChange: () => onChange(consumer),\n unsubscribe: () => unsubscribe(consumer),\n commit: () => commit(consumer),\n mount: () => teardown,\n interest: NO_PATHS,\n subscription: null,\n subscribedTo: null,\n paths: NO_PATHS,\n isSelectMode: false,\n selection: null,\n deps: null as unknown as DepSession,\n select: undefined,\n onMount: undefined,\n onUnmount: undefined,\n ownArgs: undefined,\n providerArgs: undefined,\n tracked: { current: null },\n disarm: null,\n proxyCache: null,\n ownedClass: null,\n ownedKey: '',\n ownedRegistry: null,\n ownedBloc: null,\n };\n const teardown = (): void => {\n release(consumer);\n consumer.deps.dispose();\n };\n consumer.deps = new DepSession(id, consumer.bump);\n return consumer;\n}\n\nfunction bind(\n consumer: Consumer,\n BlocClass: StateContainerConstructor,\n instanceKey: string,\n registry: StateContainerRegistry,\n args: unknown,\n): void {\n // No ref during render; the commit claims it. The registry sweeps the\n // instance if no commit ever does (SSR, discarded renders).\n const instance = registry.acquire(BlocClass, instanceKey, {\n canCreate: true,\n countRef: false,\n args: args as ExtractArgs<StateContainerConstructor>,\n sweepIfUnowned: true,\n }) as unknown as StateContainer;\n\n // Dep proxies get `onDepHandle` too, so nested `.track()` calls (A→B→C)\n // record into this consumer's session.\n let depWrappers: Map<object, unknown> | undefined;\n const onDepHandle = (handle: object): unknown => {\n depWrappers ??= new Map();\n const cached = depWrappers.get(handle);\n if (cached !== undefined) return cached;\n const wrapper = makeDepWrapper(\n handle as DepHandleLike,\n registry,\n consumer.deps,\n consumer.tracked,\n onDepHandle,\n );\n depWrappers.set(handle, wrapper);\n return wrapper;\n };\n\n consumer.BlocClass = BlocClass;\n consumer.instanceKey = instanceKey;\n consumer.registry = registry;\n consumer.boundNonce = consumer.rebindNonce;\n consumer.container = instance;\n consumer.trackedBloc = buildTrackedProxy(\n instance,\n consumer.tracked,\n onDepHandle,\n );\n // A new identity per instance, so uSES re-subscribes.\n consumer.subscribe = (onStoreChange) => subscribe(consumer, onStoreChange);\n}\n\nfunction subscribe(consumer: Consumer, onStoreChange: () => void): () => void {\n consumer.notify = onStoreChange;\n const container = consumer.container;\n const subscription = container.subscribePaths(consumer.onChange);\n subscription.setInterest(consumer.interest);\n consumer.subscription = subscription;\n consumer.subscribedTo = container;\n container.registerConsumerPaths(consumer.id, consumer.paths);\n // An emit raised during render is still pending in the channel, so its\n // next flush delivers it to this subscriber.\n return consumer.unsubscribe;\n}\n\nfunction unsubscribe(consumer: Consumer): void {\n consumer.notify = noop;\n consumer.subscription?.unsubscribe();\n consumer.subscription = null;\n consumer.subscribedTo?.unregisterConsumer(consumer.id);\n consumer.subscribedTo = null;\n}\n\nfunction onChange(consumer: Consumer): void {\n if (consumer.isSelectMode) {\n const select = consumer.select;\n const container = consumer.subscribedTo;\n if (select && container !== null) {\n const next = select(container.state, container);\n const prev = consumer.selection;\n if (prev !== null && shallowArrayEqual(prev, next)) return;\n consumer.selection = next;\n }\n }\n consumer.bump();\n}\n\nfunction claim(consumer: Consumer): void {\n const { BlocClass, instanceKey, registry } = consumer;\n if (\n consumer.ownedClass === BlocClass &&\n consumer.ownedKey === instanceKey &&\n consumer.ownedRegistry === registry\n ) {\n return;\n }\n release(consumer);\n const live = registry.acquire(BlocClass, instanceKey, {\n canCreate: true,\n countRef: true,\n refId: consumer.primaryRefId,\n args: resolveEffectiveArgs(\n consumer,\n ) as ExtractArgs<StateContainerConstructor>,\n });\n consumer.ownedClass = BlocClass;\n consumer.ownedKey = instanceKey;\n consumer.ownedRegistry = registry;\n consumer.ownedBloc = live;\n consumer.onMount?.(live);\n // The render captured a stale instance; re-render against the live one.\n if (live !== (consumer.container as unknown)) {\n consumer.rebindNonce++;\n consumer.bump();\n }\n}\n\nfunction release(consumer: Consumer): void {\n const { ownedClass, ownedRegistry } = consumer;\n if (ownedClass === null || ownedRegistry === null) return;\n // Before release, so the bloc is still alive in the callback.\n consumer.onUnmount?.(consumer.ownedBloc);\n consumer.ownedClass = null;\n consumer.ownedRegistry = null;\n ownedRegistry.release(\n ownedClass,\n consumer.ownedKey,\n false,\n consumer.primaryRefId,\n );\n}\n\nfunction commit(consumer: Consumer): void {\n claim(consumer);\n // Getters called after commit read live state, not this render's proxy.\n consumer.tracked.current = null;\n const disarm = consumer.disarm;\n if (disarm !== null) {\n consumer.disarm = null;\n disarm();\n }\n const deps = consumer.deps;\n deps.disarm();\n if (consumer.isSelectMode) {\n // A later switch back to auto-track must reconcile in full.\n deps.invalidate();\n return;\n }\n const primary = consumer.container;\n // A change between render and commit was diffed against the previous\n // interest, which may lack paths only this render read.\n if (primary.state !== consumer.renderedState) consumer.bump();\n if (deps.isUnchanged()) return;\n\n primary.registerConsumerPaths(consumer.id, consumer.paths);\n setInterest(consumer, consumer.paths);\n deps.reconcile(consumer.registry);\n}\n\nconst setInterest = (consumer: Consumer, interest: PathSet): void => {\n consumer.interest = interest;\n consumer.subscription?.setInterest(interest);\n};\n\n// Own args win over provider args. Shared by render and the ownership effect,\n// which recreates a disposed instance and must not drop its args.\nconst resolveEffectiveArgs = (consumer: Consumer): unknown =>\n consumer.ownArgs !== undefined ? consumer.ownArgs : consumer.providerArgs;\n\nconst shallowArrayEqual = (a: unknown[], b: unknown[]): boolean => {\n if (a === b) return true;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!Object.is(a[i], b[i])) return false;\n }\n return true;\n};\n","import { useEffect, useId, useRef } from 'react';\nimport type { StateContainer } from '@blac/core';\nimport { APPLY_DEPS, REMOVE_DEPS_OWNER } from '@blac/core/internal';\n\n/**\n * Feed live values from the component into a bloc's `deps` lane.\n *\n * Each calling component is one owner: its slice is shallow-merged into the\n * bloc's merged `deps` view and withdrawn on unmount. The bloc observes\n * changes through `onDepsChanged`.\n *\n * ```tsx\n * const [state, bloc] = useBloc(CanvasCubit);\n * useBlocDeps(bloc, { canvas, onTick });\n * ```\n *\n * Use this for values a bloc must *read* but should not own — DOM handles,\n * callbacks, values from other hooks. Pass identity-stable values (`useMemo` /\n * `useCallback`) as you would to any hook; a new function identity each render\n * counts as a change.\n */\nexport function useBlocDeps<D extends object>(\n bloc: StateContainer<any, any, D>,\n slice: Partial<D>,\n): void {\n const ownerId = useId();\n const latest = useRef(slice);\n latest.current = slice;\n\n // `APPLY_DEPS` shallow-compares and no-ops on an unchanged slice, so applying\n // on every commit is cheaper than asking callers to maintain a dep array.\n useEffect(() => {\n bloc[APPLY_DEPS](ownerId, latest.current);\n });\n\n useEffect(() => {\n return () => {\n bloc[REMOVE_DEPS_OWNER](ownerId);\n };\n }, [bloc, ownerId]);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["NO_PATHS"],"sources":["../src/BlocProvider.tsx","../src/buildTrackedProxy.ts","../src/depSession.ts","../src/useBloc.ts","../src/useBlocDeps.ts"],"sourcesContent":["import {\n createContext,\n useContext,\n useMemo,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport type { ExtractArgs, StateContainerConstructor } from '@blac/core';\n\n/**\n * Internal context value: a Map from bloc constructor to the args object\n * provided by the nearest BlocProvider for that bloc class.\n *\n * Each provider copies its parent's map and adds its own entry, so nested\n * providers for different blocs compose without clobbering each other.\n */\ntype ProvidedArgsMap = Map<StateContainerConstructor, unknown>;\n\nconst ProvidedArgsContext = createContext<ProvidedArgsMap>(new Map());\n\n/**\n * Props for {@link BlocProvider}.\n * @public\n */\nexport interface BlocProviderProps<T extends StateContainerConstructor> {\n /**\n * The bloc class whose args are being provided to descendants.\n */\n bloc: T;\n /**\n * Args that descendant `useBloc(bloc)` calls will resolve to when no\n * own `args` are given.\n */\n args: ExtractArgs<T>;\n children: ReactNode;\n}\n\n/**\n * Provides args to descendant `useBloc` calls for a specific bloc class via\n * React context. Own `args` on the `useBloc` call always win.\n *\n * Multiple `BlocProvider` wrappers for different bloc classes compose: each\n * merges its entry into the inherited map, so nested providers for different\n * blocs do not interfere.\n *\n * @example\n * ```tsx\n * <BlocProvider bloc={UserBloc} args={{ userId: 'alice' }}>\n * <UserProfile />\n * </BlocProvider>\n * ```\n *\n * @example Per-mount private instance\n * ```tsx\n * const id = useId();\n * <BlocProvider bloc={CartBloc} args={{ _id: id }}>\n * <CartWidget />\n * </BlocProvider>\n * ```\n * @public\n */\nexport function BlocProvider<T extends StateContainerConstructor>({\n bloc,\n args,\n children,\n}: BlocProviderProps<T>): ReactElement {\n const parentMap = useContext(ProvidedArgsContext);\n\n // Keyed on the args content, not identity, so an inline `args={{ ... }}`\n // literal does not rebuild the map (and re-render every consumer) on\n // every render, while a real field change still reaches `useProvidedArgs`.\n const argsKey = JSON.stringify(args);\n\n // Merge our entry into a new Map so sibling/parent providers for other blocs\n // are preserved.\n const mergedMap = useMemo(() => {\n const next = new Map(parentMap);\n next.set(bloc, args);\n return next;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [parentMap, bloc, argsKey]);\n\n return (\n <ProvidedArgsContext.Provider value={mergedMap}>\n {children}\n </ProvidedArgsContext.Provider>\n );\n}\n\n/**\n * Returns the args provided by the nearest {@link BlocProvider} for the given\n * bloc class, or `undefined` when called outside a matching provider.\n *\n * Used by `useBloc` to inherit provider args when no own `args` are given.\n * @public\n */\nexport function useProvidedArgs<T extends StateContainerConstructor>(\n BlocClass: T,\n): ExtractArgs<T> | undefined {\n const map = useContext(ProvidedArgsContext);\n return map.get(BlocClass) as ExtractArgs<T> | undefined;\n}\n","import { DEP_BRAND, WITH_TRACKED_STATE } from '@blac/core/internal';\n\ninterface TrackedStateTarget {\n [WITH_TRACKED_STATE]<R>(\n tracked: unknown,\n fn: () => R,\n onDepHandle?: (handle: object) => unknown,\n ): R;\n}\n\nfunction supportsTrackedState(value: object): value is TrackedStateTarget {\n return (\n typeof (value as TrackedStateTarget)[WITH_TRACKED_STATE] === 'function'\n );\n}\n\ntype Getter = (this: object) => unknown;\ntype Method = (...args: unknown[]) => unknown;\n\n// Prototype getters per class prototype. The chain is static, so walking it\n// once per class (not once per mounted component) keeps the get trap O(1).\nconst gettersByProto = new WeakMap<object, Map<string | symbol, Getter>>();\n\nfunction collectGetters(proto: object): Map<string | symbol, Getter> {\n const cached = gettersByProto.get(proto);\n if (cached !== undefined) return cached;\n const getters = new Map<string | symbol, Getter>();\n let p: object | null = proto;\n while (p !== null && p !== Object.prototype) {\n for (const key of Reflect.ownKeys(p)) {\n const desc = Object.getOwnPropertyDescriptor(p, key);\n // A descriptor getter, always `.call`ed with an explicit receiver.\n // oxlint-disable-next-line typescript/unbound-method\n if (desc?.get && !getters.has(key)) getters.set(key, desc.get);\n }\n p = Object.getPrototypeOf(p);\n }\n gettersByProto.set(proto, getters);\n return getters;\n}\n\n// Methods on the prototype, so a mount allocates the handler and the proxy\n// only, not a set of closures.\nclass BlocHandler implements ProxyHandler<object> {\n // Bound methods are cached so `bloc.method` keeps a stable identity across\n // reads — an unstable one would defeat memoisation in consumers. Keyed by\n // the underlying function too, so a reassigned method is re-bound.\n private boundMethods:\n | Map<string | symbol, { fn: Method; bound: Method }>\n | undefined = undefined;\n\n constructor(\n private readonly getters: Map<string | symbol, Getter>,\n private readonly tracksState: boolean,\n private readonly trackedStateRef: { current: unknown },\n private readonly onDepHandle: ((handle: object) => unknown) | undefined,\n ) {}\n\n get(target: object, key: string | symbol): unknown {\n const getter = this.getters.get(key);\n if (getter !== undefined) {\n const tracked = this.trackedStateRef.current;\n // `[WITH_TRACKED_STATE]` invokes the getter with `this` = the real\n // instance, so no per-read thunk is needed.\n return this.wrapDepHandle(\n tracked == null || !this.tracksState\n ? getter.call(target)\n : (target as TrackedStateTarget)[WITH_TRACKED_STATE](\n tracked,\n getter,\n this.onDepHandle,\n ),\n );\n }\n\n // Receiver is the real instance, so own fields keep their `#private`\n // brand. Methods are bound to it too, otherwise `bloc.method()` would\n // call with `this` = proxy and fail the same brand check.\n const value = Reflect.get(target, key, target);\n if (typeof value === 'function') {\n const fn = value as Method;\n this.boundMethods ??= new Map();\n let cached = this.boundMethods.get(key);\n if (cached === undefined || cached.fn !== fn) {\n cached = { fn, bound: fn.bind(target) };\n this.boundMethods.set(key, cached);\n }\n return this.wrapDepHandle(cached.bound);\n }\n return this.wrapDepHandle(value);\n }\n\n set(target: object, key: string | symbol, value: unknown): boolean {\n return Reflect.set(target, key, value, target);\n }\n\n private wrapDepHandle(value: unknown): unknown {\n if (\n this.onDepHandle !== undefined &&\n (typeof value === 'function' || typeof value === 'object') &&\n value !== null &&\n (value as Record<symbol, unknown>)[DEP_BRAND] !== undefined\n ) {\n return this.onDepHandle(value as object);\n }\n return value;\n }\n}\n\n/**\n * Build a per-consumer proxy for a bloc instance.\n *\n * Getters run with `this` bound to the real instance, not the proxy, so ES\n * `#private` fields and methods work in user blocs. Path recording is done by\n * `[WITH_TRACKED_STATE]`, which makes the instance's own `state` getter\n * report the render's tracking proxy for the call's duration; nested getter\n * chains stay tracked because the override remains set.\n *\n * One allocation per bloc acquisition (inside `useMemo`), so the proxy is\n * stable across renders.\n *\n * @param onDepHandle - Invoked when a read returns a branded dep handle;\n * returns the value (the session-bound wrapper) to expose in its place.\n * Must cache wrappers per handle to avoid re-allocation.\n */\nexport function buildTrackedProxy<T extends object>(\n instance: T,\n trackedStateRef: { current: unknown },\n onDepHandle?: (handle: object) => unknown,\n): T {\n // Both string- and symbol-keyed prototype getters (excluding\n // Object.prototype). Arrow-function class properties are own values, not\n // getters, and pass through unmodified.\n const handler = new BlocHandler(\n collectGetters(Object.getPrototypeOf(instance) as object),\n supportsTrackedState(instance),\n trackedStateRef,\n onDepHandle,\n );\n return new Proxy(instance, handler as ProxyHandler<T>);\n}\n","import type {\n StateContainer,\n StateContainerConstructor,\n StateContainerRegistry,\n} from '@blac/core';\nimport { DEP_BRAND } from '@blac/core/internal';\nimport {\n ALL_PATHS,\n emptyPathSet,\n pathSetEquals,\n trackRender,\n ProxyCache,\n type PathSet,\n type PathSubscription,\n} from '@dirtytalk/structural';\nimport { buildTrackedProxy } from './buildTrackedProxy';\n\nconst NO_PATHS: PathSet = emptyPathSet();\n\ninterface DepEntry {\n // One live set per `.track()` call, merged at commit: reads keep landing in\n // each call's own set until the render ends.\n parts: PathSet[];\n paths: PathSet;\n Type: StateContainerConstructor;\n key: string;\n args: unknown;\n}\n\ninterface DepSub {\n subscription: PathSubscription;\n registry: StateContainerRegistry;\n Type: StateContainerConstructor;\n key: string;\n}\n\n/**\n * The containers one `useBloc` consumer reads during a render: its primary\n * bloc plus every dep reached through `this.<handle>.track()`.\n *\n * The render records entries; the commit calls `reconcile`, which subscribes\n * and takes a ref on new deps and drops the ones no longer read. `dispose`\n * releases everything. Refs are only taken at commit, so an uncommitted render\n * never leaks one.\n */\nexport class DepSession {\n // Most consumers read no deps, so everything dep-related is allocated on the\n // first dep a render records.\n private primary: StateContainer | null = null;\n private primaryParts: PathSet[] = [];\n private primaryPaths: PathSet = NO_PATHS;\n private entries: Map<StateContainer, DepEntry> | null = null;\n private subs: Map<StateContainer, DepSub> | null = null;\n private reconciled = false;\n private lastPrimary: StateContainer | null = null;\n private lastPrimaryPaths: PathSet = NO_PATHS;\n private lastEntries: Map<StateContainer, DepEntry> | null = null;\n private disarms: Array<() => void> | null = null;\n\n constructor(\n private readonly consumerId: string,\n private readonly onChange: () => void,\n ) {}\n\n private get refId(): string {\n return `useBloc@${this.consumerId}:dep`;\n }\n\n begin(primary: StateContainer, paths: PathSet): void {\n this.entries?.clear();\n if (this.disarms !== null) this.disarms.length = 0;\n this.primary = primary;\n this.primaryParts = [paths];\n }\n\n record(\n dep: StateContainer,\n paths: PathSet,\n Type: StateContainerConstructor,\n key: string,\n args: unknown,\n disarm: () => void,\n ): void {\n (this.disarms ??= []).push(disarm);\n // A dep that resolves to the primary only widens the primary's entry.\n if (dep === this.primary) {\n this.primaryParts.push(paths);\n return;\n }\n const entries = (this.entries ??= new Map());\n const existing = entries.get(dep);\n if (existing !== undefined) {\n existing.parts.push(paths);\n } else {\n entries.set(dep, { parts: [paths], paths, Type, key, args });\n }\n }\n\n /**\n * Merge each container's per-call sets once the render's reads are done.\n * Returns the primary's paths, widened by deps that resolved to it.\n */\n settle(): PathSet {\n this.primaryPaths = mergeParts(this.primaryParts);\n if (this.entries !== null) {\n for (const entry of this.entries.values()) {\n entry.paths = mergeParts(entry.parts);\n }\n }\n return this.primaryPaths;\n }\n\n /** Stops every dep tracked this render from recording further reads. */\n disarm(): void {\n const disarms = this.disarms;\n if (disarms === null) return;\n for (const disarm of disarms) disarm();\n disarms.length = 0;\n }\n\n /** Forces the next `reconcile` to run in full. */\n invalidate(): void {\n this.reconciled = false;\n }\n\n isUnchanged(): boolean {\n if (\n !this.reconciled ||\n this.lastPrimary !== this.primary ||\n !pathSetEquals(this.lastPrimaryPaths, this.primaryPaths)\n ) {\n return false;\n }\n const last = this.lastEntries;\n const entries = this.entries;\n if ((last?.size ?? 0) !== (entries?.size ?? 0)) return false;\n if (last === null || entries === null) return true;\n for (const [container, entry] of entries) {\n const prev = last.get(container);\n if (prev === undefined || !sameEntry(prev, entry)) return false;\n }\n return true;\n }\n\n reconcile(registry: StateContainerRegistry): void {\n const { consumerId } = this;\n const entries = this.entries;\n this.reconciled = true;\n this.lastPrimary = this.primary;\n this.lastPrimaryPaths = this.primaryPaths;\n this.lastEntries =\n entries === null || entries.size === 0 ? null : new Map(entries);\n\n const subs = this.subs;\n if (subs !== null) {\n for (const [dep, sub] of subs) {\n if (entries?.has(dep) !== true) {\n this.drop(dep, sub);\n subs.delete(dep);\n }\n }\n }\n if (entries === null) return;\n\n for (const [dep, entry] of entries) {\n // StrictMode's simulated unmount can dispose a dep this render captured;\n // re-render so the session resolves the live instance.\n if (dep.$blac.disposed) {\n this.onChange();\n continue;\n }\n dep.registerConsumerPaths(consumerId, entry.paths);\n const existing = subs?.get(dep);\n if (existing) {\n existing.subscription.setInterest(entry.paths);\n continue;\n }\n const refId = this.refId;\n const live = registry.acquire(entry.Type, entry.key, {\n canCreate: true,\n countRef: true,\n refId,\n args: entry.args,\n });\n // The key now maps to another instance; re-render against that one.\n if (live !== dep) {\n registry.release(entry.Type, entry.key, false, refId);\n this.onChange();\n continue;\n }\n const subscription = dep.subscribePaths(this.onChange);\n subscription.setInterest(entry.paths);\n (this.subs ??= new Map()).set(dep, {\n subscription,\n registry,\n Type: entry.Type,\n key: entry.key,\n });\n }\n }\n\n dispose(): void {\n const subs = this.subs;\n if (subs !== null) {\n for (const [dep, sub] of subs) this.drop(dep, sub);\n subs.clear();\n }\n this.reconciled = false;\n }\n\n private drop(dep: StateContainer, sub: DepSub): void {\n sub.subscription.unsubscribe();\n dep.unregisterConsumer(this.consumerId);\n sub.registry.release(sub.Type, sub.key, false, this.refId);\n }\n}\n\nfunction sameEntry(a: DepEntry, b: DepEntry): boolean {\n return (\n pathSetEquals(a.paths, b.paths) &&\n a.key === b.key &&\n Object.is(a.args, b.args)\n );\n}\n\nfunction mergeParts(parts: PathSet[]): PathSet {\n if (parts.length === 1) return parts[0];\n const out = new Set<number>();\n for (const part of parts) {\n if (part === ALL_PATHS) return ALL_PATHS;\n for (const id of part) out.add(id);\n }\n return out;\n}\n\ninterface DepAccessOptionsLike {\n args?: unknown;\n}\n\n/** Structural shape of a branded `depend()` handle as seen from React. */\nexport interface DepHandleLike {\n track(options?: DepAccessOptionsLike): [unknown, StateContainer];\n untracked(options?: DepAccessOptionsLike): StateContainer;\n readonly [DEP_BRAND]: {\n Type: StateContainerConstructor;\n defaultArgs?: unknown;\n };\n}\n\n/**\n * Wrap a dep handle read inside a tracked getter. During a render (`tracked`\n * is set) `.track()` records the dep's read paths into `session` and returns\n * a tracked proxy, so the dep's own getters track too. Outside a render it\n * returns the live `[state, dep]`, like the core handle.\n *\n * Args resolve per call, so one handle can reach several instances; proxies\n * are cached per resolved instance.\n */\nexport function makeDepWrapper(\n handle: DepHandleLike,\n registry: StateContainerRegistry,\n session: DepSession,\n tracked: { current: unknown },\n onDepHandle: (handle: object) => unknown,\n): DepHandleLike {\n const brand = handle[DEP_BRAND];\n // Weak, so instances a getter stops reaching (e.g. by switching args) can\n // be collected once disposed.\n const perDep = new WeakMap<\n StateContainer,\n { ref: { current: unknown }; proxy: StateContainer }\n >();\n const proxyCache = new ProxyCache();\n\n const resolve = (options?: DepAccessOptionsLike) => {\n const args = options?.args ?? brand.defaultArgs;\n const key = registry.resolveKey(brand.Type, undefined, args);\n const dep = registry.acquire(brand.Type, key, {\n canCreate: true,\n countRef: false,\n args,\n sweepIfUnowned: tracked.current != null,\n }) as unknown as StateContainer;\n return { dep, key, args };\n };\n\n const wrapper = {\n untracked: (options?: DepAccessOptionsLike) => resolve(options).dep,\n track: (options?: DepAccessOptionsLike) => {\n const { dep, key, args } = resolve(options);\n if (tracked.current == null) return [dep.state, dep];\n\n const result = trackRender(dep.state, dep.interner, proxyCache);\n let cache = perDep.get(dep);\n if (cache === undefined) {\n const ref = { current: result.value as unknown };\n cache = { ref, proxy: buildTrackedProxy(dep, ref, onDepHandle) };\n perDep.set(dep, cache);\n } else {\n cache.ref.current = result.value;\n }\n session.record(dep, result.paths, brand.Type, key, args, result.disarm);\n return [result.value, cache.proxy];\n },\n } as DepHandleLike;\n\n Object.defineProperty(wrapper, DEP_BRAND, {\n value: brand,\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n return wrapper;\n}\n","import { useLayoutEffect, useRef, useSyncExternalStore } from 'react';\nimport {\n isIsolatedClass,\n type ExtractArgs,\n type ExtractState,\n type InstanceState,\n type StateContainer,\n type StateContainerConstructor,\n type StateContainerRegistry,\n} from '@blac/core';\nimport {\n ALL_PATHS,\n emptyPathSet,\n trackRender,\n ProxyCache,\n type PathSet,\n type PathSubscription,\n} from '@dirtytalk/structural';\nimport { useProvidedArgs } from './BlocProvider';\nimport { useRegistry } from './RegistryProvider';\nimport { buildTrackedProxy } from './buildTrackedProxy';\nimport { DepSession, makeDepWrapper, type DepHandleLike } from './depSession';\nimport type { UseBlocOptions, UseBlocReturn } from './types';\n\nlet nextConsumerId = 0;\n\n/**\n * React hook that connects a component to a state container with automatic\n * re-render on state changes.\n *\n * Two tracking modes:\n * - **Auto-tracking** (default): the returned state value is a proxy that\n * records read paths during render. The component re-renders when any\n * recorded path changes.\n * - **Manual select**: pass `options.select` to opt out of auto-tracking.\n * The hook re-renders only when the returned array's elements change\n * (per-index `Object.is`).\n *\n * Lifecycle:\n * - The bloc is acquired from the registry on mount and released on\n * unmount. The instance key is derived from `options.args` (own args),\n * then the surrounding {@link BlocProvider} context args for this bloc,\n * then the default key (no args).\n * - `options.onMount` fires after the bloc is acquired; `options.onUnmount`\n * fires *before* the registry releases its ref, so the bloc is still\n * alive when the callback runs.\n *\n * Per-mount private instance: mark the class `@blac({ isolated: true })`, or\n * pass a unique key arg yourself (e.g. `useId()` in `args`).\n *\n * @typeParam T - The state container constructor type (inferred from BlocClass)\n * @param BlocClass - The state container class to connect to\n * @param options - Configuration options\n * @returns Tuple of `[state, bloc]`\n *\n * @example Basic usage\n * ```ts\n * const [state, bloc] = useBloc(MyBloc);\n * ```\n *\n * @example Manual select\n * ```ts\n * const [state, bloc] = useBloc(MyBloc, {\n * select: (state) => [state.count],\n * });\n * ```\n *\n * @example Args-based shared instance\n * ```ts\n * const [state, bloc] = useBloc(UserBloc, { args: { userId: 'alice' } });\n * ```\n * @public\n */\nexport function useBloc<\n T extends StateContainerConstructor = StateContainerConstructor,\n>(\n BlocClass: T,\n options?: UseBlocOptions<T>,\n): UseBlocReturn<T, ExtractState<T>> {\n const registry = useRegistry();\n\n const consumerRef = useRef<Consumer | null>(null);\n const consumer = (consumerRef.current ??= createConsumer());\n\n consumer.select = options?.select;\n consumer.onMount = options?.onMount;\n consumer.onUnmount = options?.onUnmount;\n\n consumer.ownArgs = (options as { args?: ExtractArgs<T> } | undefined)?.args;\n consumer.providerArgs = useProvidedArgs(BlocClass);\n const effectiveArgs = resolveEffectiveArgs(consumer);\n const sharedKey = registry.resolveKey(BlocClass, undefined, effectiveArgs);\n const instanceKey = isIsolatedClass(BlocClass)\n ? `${sharedKey}#${consumer.id}`\n : sharedKey;\n\n // A memo kept on the consumer rather than in `useMemo`: no hook, deps array\n // or factory closure per mount. `rebindNonce` is bumped by the commit when\n // the rendered instance was disposed and recreated before commit (a\n // same-commit owner handoff, or StrictMode's double effect); the tracked\n // proxy can't be retargeted in place. Keyed on `BlocClass` as well as the\n // key: classes without args or a `static key` share the default key, so\n // `useBloc(cond ? A : B)` must still re-subscribe on a class swap.\n if (\n consumer.BlocClass !== BlocClass ||\n consumer.instanceKey !== instanceKey ||\n consumer.registry !== registry ||\n consumer.boundNonce !== consumer.rebindNonce\n ) {\n bind(consumer, BlocClass, instanceKey, registry, effectiveArgs);\n }\n\n useSyncExternalStore(\n consumer.subscribe,\n consumer.getSnapshot,\n consumer.getSnapshot,\n );\n\n const container = consumer.container;\n const rawState = container.state as ExtractState<T>;\n consumer.renderedState = rawState;\n const select = consumer.select;\n consumer.isSelectMode = select !== undefined;\n let state: ExtractState<T>;\n if (select !== undefined) {\n state = rawState;\n // Recomputed every render: the selector may close over props.\n consumer.selection = select(rawState, container as InstanceState<T>);\n setInterest(consumer, ALL_PATHS);\n } else {\n const tracked = trackRender(\n rawState,\n container.interner,\n (consumer.proxyCache ??= new ProxyCache()),\n );\n state = tracked.value as ExtractState<T>;\n consumer.tracked.current = tracked.value;\n consumer.paths = tracked.paths;\n // Run at commit, so reads that outlive the render (effects, handlers)\n // don't add paths.\n consumer.disarm = tracked.disarm;\n // `tracked.paths` is still empty here and fills during JSX, so paths are\n // registered at commit, not now.\n consumer.deps.begin(container, tracked.paths);\n }\n\n // Stable callbacks that read this render's inputs from the consumer.\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(consumer.commit);\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(consumer.mount, NO_DEPS);\n\n return [state, consumer.trackedBloc] as UseBlocReturn<T, ExtractState<T>>;\n}\n\nconst noop = (): void => {};\n\n// Initial `interest` and `paths`; neither is ever mutated in place.\nconst NO_PATHS: PathSet = emptyPathSet();\nconst NO_DEPS: [] = [];\n\n/**\n * Everything one `useBloc` call keeps between renders. Its callbacks are\n * created once, so a render allocates no closures.\n *\n * `bump` increments `version` before calling `notify`: uSES requires\n * `getSnapshot()` to reflect the change by the time it is notified.\n */\ninterface Consumer {\n id: string;\n primaryRefId: string;\n // The render's inputs, read by the commit of that render.\n BlocClass: StateContainerConstructor;\n instanceKey: string;\n registry: StateContainerRegistry;\n /** Written only by `bind`. */\n container: StateContainer;\n trackedBloc: unknown;\n subscribe: (onStoreChange: () => void) => () => void;\n boundNonce: number;\n rebindNonce: number;\n renderedState: unknown;\n version: number;\n /** `noop` while unsubscribed. */\n notify: () => void;\n bump: () => void;\n getSnapshot: () => number;\n onChange: () => void;\n unsubscribe: () => void;\n commit: () => void;\n mount: () => () => void;\n /** The committed `paths`, or `ALL_PATHS` in select mode. */\n interest: PathSet;\n /** `null` while unsubscribed. */\n subscription: PathSubscription | null;\n subscribedTo: StateContainer | null;\n paths: PathSet;\n isSelectMode: boolean;\n selection: unknown[] | null;\n deps: DepSession;\n // Typed loosely: `T` is per call site.\n select: ((state: any, bloc: any) => unknown[]) | undefined;\n onMount: ((bloc: any) => void) | undefined;\n onUnmount: ((bloc: any) => void) | undefined;\n ownArgs: unknown;\n providerArgs: unknown;\n /** The render's tracking proxy; `null` outside a tracked render. */\n tracked: { current: unknown };\n disarm: (() => void) | null;\n proxyCache: ProxyCache | null;\n // The instance the consumer holds a ref on, claimed at commit so acquire\n // and release pair exactly.\n ownedClass: StateContainerConstructor | null;\n ownedKey: string;\n ownedRegistry: StateContainerRegistry | null;\n ownedBloc: unknown;\n}\n\nfunction createConsumer(): Consumer {\n const id = `useBloc-${nextConsumerId++}`;\n const consumer: Consumer = {\n id,\n primaryRefId: `useBloc@${id}`,\n BlocClass: null as unknown as StateContainerConstructor,\n instanceKey: '',\n registry: null as unknown as StateContainerRegistry,\n container: null as unknown as StateContainer,\n trackedBloc: null,\n subscribe: null as unknown as Consumer['subscribe'],\n boundNonce: 0,\n rebindNonce: 0,\n renderedState: undefined,\n version: 0,\n notify: noop,\n bump: () => {\n consumer.version++;\n consumer.notify();\n },\n getSnapshot: () => consumer.version,\n onChange: () => onChange(consumer),\n unsubscribe: () => unsubscribe(consumer),\n commit: () => commit(consumer),\n mount: () => teardown,\n interest: NO_PATHS,\n subscription: null,\n subscribedTo: null,\n paths: NO_PATHS,\n isSelectMode: false,\n selection: null,\n deps: null as unknown as DepSession,\n select: undefined,\n onMount: undefined,\n onUnmount: undefined,\n ownArgs: undefined,\n providerArgs: undefined,\n tracked: { current: null },\n disarm: null,\n proxyCache: null,\n ownedClass: null,\n ownedKey: '',\n ownedRegistry: null,\n ownedBloc: null,\n };\n const teardown = (): void => {\n release(consumer);\n consumer.deps.dispose();\n };\n consumer.deps = new DepSession(id, consumer.bump);\n return consumer;\n}\n\nfunction bind(\n consumer: Consumer,\n BlocClass: StateContainerConstructor,\n instanceKey: string,\n registry: StateContainerRegistry,\n args: unknown,\n): void {\n // No ref during render; the commit claims it. The registry sweeps the\n // instance if no commit ever does (SSR, discarded renders).\n const instance = registry.acquire(BlocClass, instanceKey, {\n canCreate: true,\n countRef: false,\n args: args as ExtractArgs<StateContainerConstructor>,\n sweepIfUnowned: true,\n }) as unknown as StateContainer;\n\n // Dep proxies get `onDepHandle` too, so nested `.track()` calls (A→B→C)\n // record into this consumer's session.\n let depWrappers: Map<object, unknown> | undefined;\n const onDepHandle = (handle: object): unknown => {\n depWrappers ??= new Map();\n const cached = depWrappers.get(handle);\n if (cached !== undefined) return cached;\n const wrapper = makeDepWrapper(\n handle as DepHandleLike,\n registry,\n consumer.deps,\n consumer.tracked,\n onDepHandle,\n );\n depWrappers.set(handle, wrapper);\n return wrapper;\n };\n\n consumer.BlocClass = BlocClass;\n consumer.instanceKey = instanceKey;\n consumer.registry = registry;\n consumer.boundNonce = consumer.rebindNonce;\n consumer.container = instance;\n consumer.trackedBloc = buildTrackedProxy(\n instance,\n consumer.tracked,\n onDepHandle,\n );\n // A new identity per instance, so uSES re-subscribes.\n consumer.subscribe = (onStoreChange) => subscribe(consumer, onStoreChange);\n}\n\nfunction subscribe(consumer: Consumer, onStoreChange: () => void): () => void {\n consumer.notify = onStoreChange;\n const container = consumer.container;\n const subscription = container.subscribePaths(consumer.onChange);\n subscription.setInterest(consumer.interest);\n consumer.subscription = subscription;\n consumer.subscribedTo = container;\n container.registerConsumerPaths(consumer.id, consumer.paths);\n // An emit raised during render is still pending in the channel, so its\n // next flush delivers it to this subscriber.\n return consumer.unsubscribe;\n}\n\nfunction unsubscribe(consumer: Consumer): void {\n consumer.notify = noop;\n consumer.subscription?.unsubscribe();\n consumer.subscription = null;\n consumer.subscribedTo?.unregisterConsumer(consumer.id);\n consumer.subscribedTo = null;\n}\n\nfunction onChange(consumer: Consumer): void {\n if (consumer.isSelectMode) {\n const select = consumer.select;\n const container = consumer.subscribedTo;\n if (select && container !== null) {\n const next = select(container.state, container);\n const prev = consumer.selection;\n if (prev !== null && shallowArrayEqual(prev, next)) return;\n consumer.selection = next;\n }\n }\n consumer.bump();\n}\n\nfunction claim(consumer: Consumer): void {\n const { BlocClass, instanceKey, registry } = consumer;\n if (\n consumer.ownedClass === BlocClass &&\n consumer.ownedKey === instanceKey &&\n consumer.ownedRegistry === registry\n ) {\n return;\n }\n release(consumer);\n const live = registry.acquire(BlocClass, instanceKey, {\n canCreate: true,\n countRef: true,\n refId: consumer.primaryRefId,\n args: resolveEffectiveArgs(\n consumer,\n ) as ExtractArgs<StateContainerConstructor>,\n });\n consumer.ownedClass = BlocClass;\n consumer.ownedKey = instanceKey;\n consumer.ownedRegistry = registry;\n consumer.ownedBloc = live;\n consumer.onMount?.(live);\n // The render captured a stale instance; re-render against the live one.\n if (live !== (consumer.container as unknown)) {\n consumer.rebindNonce++;\n consumer.bump();\n }\n}\n\nfunction release(consumer: Consumer): void {\n const { ownedClass, ownedRegistry } = consumer;\n if (ownedClass === null || ownedRegistry === null) return;\n // Before release, so the bloc is still alive in the callback.\n consumer.onUnmount?.(consumer.ownedBloc);\n consumer.ownedClass = null;\n consumer.ownedRegistry = null;\n ownedRegistry.release(\n ownedClass,\n consumer.ownedKey,\n false,\n consumer.primaryRefId,\n );\n}\n\nfunction commit(consumer: Consumer): void {\n claim(consumer);\n // Getters called after commit read live state, not this render's proxy.\n consumer.tracked.current = null;\n const disarm = consumer.disarm;\n if (disarm !== null) {\n consumer.disarm = null;\n disarm();\n }\n const deps = consumer.deps;\n deps.disarm();\n if (consumer.isSelectMode) {\n // A later switch back to auto-track must reconcile in full.\n deps.invalidate();\n return;\n }\n const primary = consumer.container;\n // A change between render and commit was diffed against the previous\n // interest, which may lack paths only this render read.\n if (primary.state !== consumer.renderedState) consumer.bump();\n consumer.paths = deps.settle();\n if (deps.isUnchanged()) return;\n\n primary.registerConsumerPaths(consumer.id, consumer.paths);\n setInterest(consumer, consumer.paths);\n deps.reconcile(consumer.registry);\n}\n\nconst setInterest = (consumer: Consumer, interest: PathSet): void => {\n consumer.interest = interest;\n consumer.subscription?.setInterest(interest);\n};\n\n// Own args win over provider args. Shared by render and the ownership effect,\n// which recreates a disposed instance and must not drop its args.\nconst resolveEffectiveArgs = (consumer: Consumer): unknown =>\n consumer.ownArgs !== undefined ? consumer.ownArgs : consumer.providerArgs;\n\nconst shallowArrayEqual = (a: unknown[], b: unknown[]): boolean => {\n if (a === b) return true;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!Object.is(a[i], b[i])) return false;\n }\n return true;\n};\n","import { useEffect, useId, useRef } from 'react';\nimport type { StateContainer } from '@blac/core';\nimport { APPLY_DEPS, REMOVE_DEPS_OWNER } from '@blac/core/internal';\n\n/**\n * Feed live values from the component into a bloc's `deps` lane.\n *\n * Each calling component is one owner: its slice is shallow-merged into the\n * bloc's merged `deps` view and withdrawn on unmount. The bloc observes\n * changes through `onDepsChanged`.\n *\n * ```tsx\n * const [state, bloc] = useBloc(CanvasCubit);\n * useBlocDeps(bloc, { canvas, onTick });\n * ```\n *\n * Use this for values a bloc must *read* but should not own — DOM handles,\n * callbacks, values from other hooks. Pass identity-stable values (`useMemo` /\n * `useCallback`) as you would to any hook; a new function identity each render\n * counts as a change.\n */\nexport function useBlocDeps<D extends object>(\n bloc: StateContainer<any, any, D>,\n slice: Partial<D>,\n): void {\n const ownerId = useId();\n const latest = useRef(slice);\n latest.current = slice;\n\n // `APPLY_DEPS` shallow-compares and no-ops on an unchanged slice, so applying\n // on every commit is cheaper than asking callers to maintain a dep array.\n useEffect(() => {\n bloc[APPLY_DEPS](ownerId, latest.current);\n });\n\n useEffect(() => {\n return () => {\n bloc[REMOVE_DEPS_OWNER](ownerId);\n };\n }, [bloc, ownerId]);\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blac/react",
3
- "version": "2.1.3",
3
+ "version": "2.1.4",
4
4
  "license": "MIT",
5
5
  "author": "Brendan Mullins <jsnanigans@gmail.com>",
6
6
  "description": "React bindings for BlaC — useBloc hook with automatic re-render optimization",
@@ -78,13 +78,13 @@
78
78
  }
79
79
  ],
80
80
  "dependencies": {
81
- "@dirtytalk/structural": "^0.1.4"
81
+ "@dirtytalk/structural": "^0.1.5"
82
82
  },
83
83
  "peerDependencies": {
84
84
  "@testing-library/react": "^14.0.0 || ^15.0.0 || ^16.0.0",
85
85
  "@types/react": "^18.0.0 || ^19.0.0",
86
86
  "react": "^18.0.0 || ^19.0.0",
87
- "@blac/core": "^2.1.2"
87
+ "@blac/core": "^2.1.3"
88
88
  },
89
89
  "peerDependenciesMeta": {
90
90
  "@types/react": {
@@ -121,7 +121,7 @@
121
121
  "vite": "npm:@voidzero-dev/vite-plus-core@0.1.24",
122
122
  "vite-plus": "^0.1.24",
123
123
  "vitest": "npm:@voidzero-dev/vite-plus-test@0.1.24",
124
- "@blac/core": "2.1.2"
124
+ "@blac/core": "2.1.3"
125
125
  },
126
126
  "scripts": {
127
127
  "dev": "vp pack --watch",