@blac/react 2.1.1 → 2.1.2
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/dist/buildTrackedProxy.d.cts +1 -3
- package/dist/buildTrackedProxy.d.ts +1 -3
- package/dist/depSession.d.cts +13 -5
- package/dist/depSession.d.ts +13 -5
- package/dist/index.cjs +252 -206
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +252 -206
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/BlocProvider.tsx","../src/buildTrackedProxy.ts","../src/expandWithAncestors.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/**\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): { proxy: 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 getters = collectGetters(Object.getPrototypeOf(instance) as object);\n const tracksState = supportsTrackedState(instance as object);\n\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 const boundMethods = new Map<\n string | symbol,\n { fn: Method; bound: Method }\n >();\n\n const wrapDepHandle = (value: unknown): unknown => {\n if (\n onDepHandle !== undefined &&\n (typeof value === 'function' || typeof value === 'object') &&\n value !== null &&\n (value as Record<symbol, unknown>)[DEP_BRAND] !== undefined\n ) {\n return onDepHandle(value as object);\n }\n return value;\n };\n\n const proxy = new Proxy(instance as object, {\n get(target, key) {\n const getter = getters.get(key);\n if (getter !== undefined) {\n const tracked = trackedStateRef.current;\n // `[WITH_TRACKED_STATE]` invokes the getter with `this` = the real\n // instance, so no per-read thunk is needed.\n return wrapDepHandle(\n tracked == null || !tracksState\n ? getter.call(target)\n : (target as TrackedStateTarget)[WITH_TRACKED_STATE](\n tracked,\n getter,\n 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 let cached = boundMethods.get(key);\n if (cached === undefined || cached.fn !== fn) {\n cached = { fn, bound: fn.bind(target) };\n boundMethods.set(key, cached);\n }\n return wrapDepHandle(cached.bound);\n }\n return wrapDepHandle(value);\n },\n set(target, key, value) {\n return Reflect.set(target, key, value, target);\n },\n }) as T;\n\n return { proxy };\n}\n","import {\n ALL_PATHS,\n type PathInterner,\n type PathSet,\n} from '@dirtytalk/structural';\n\n/**\n * Add an ancestor-watch id for every ancestor of every tracked leaf, so a\n * `patch` that replaces a parent atomically (e.g. the array `items`) wakes a\n * consumer that read a child (`items.length`).\n *\n * Ancestor-watch ids only match marks the source emits for atomic\n * replacements, never a plain-object pulse-up: `{'user.email'}` does not wake\n * when a sibling `user.name` changes. The `''` root is never added; a root\n * change already marks `ALL_PATHS`.\n *\n * Returns `paths` itself when no leaf has an ancestor.\n */\nexport function expandWithAncestors(\n paths: PathSet,\n interner: PathInterner,\n): PathSet {\n if (paths === ALL_PATHS) return ALL_PATHS;\n const leafPaths = paths as Set<number>;\n let expanded: Set<number> | undefined;\n for (const id of leafPaths) {\n const watch = interner.ancestorWatchIds(id);\n if (watch.length === 0) continue;\n expanded ??= new Set<number>(leafPaths);\n for (let i = 0; i < watch.length; i++) expanded.add(watch[i]);\n }\n return expanded ?? paths;\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 pathSetEquals,\n trackRender,\n ProxyCache,\n type PathSet,\n} from '@dirtytalk/structural';\nimport { buildTrackedProxy } from './buildTrackedProxy';\nimport { expandWithAncestors } from './expandWithAncestors';\n\ntype SessionEntry =\n | { kind: 'primary'; paths: PathSet }\n | {\n kind: 'dep';\n paths: PathSet;\n Type: StateContainerConstructor;\n key: string;\n args: unknown;\n };\n\ninterface DepSub {\n unsubscribe: () => void;\n interestRef: { current: PathSet };\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 private readonly entries = new Map<StateContainer, SessionEntry>();\n private readonly subs = new Map<StateContainer, DepSub>();\n private lastReconciled: Map<StateContainer, SessionEntry> | null = null;\n private readonly refId: string;\n\n constructor(\n private readonly consumerId: string,\n private readonly onChange: () => void,\n ) {\n this.refId = `useBloc@${consumerId}:dep`;\n }\n\n begin(primary: StateContainer, paths: PathSet): void {\n this.entries.clear();\n this.entries.set(primary, { kind: 'primary', paths });\n }\n\n record(\n dep: StateContainer,\n paths: PathSet,\n Type: StateContainerConstructor,\n key: string,\n args: unknown,\n ): void {\n const existing = this.entries.get(dep);\n if (existing !== undefined) {\n existing.paths = unionPaths(existing.paths, paths);\n } else {\n this.entries.set(dep, { kind: 'dep', paths, Type, key, args });\n }\n }\n\n /** Forces the next `reconcile` to run in full. */\n invalidate(): void {\n this.lastReconciled = null;\n }\n\n isUnchanged(): boolean {\n const last = this.lastReconciled;\n if (last === null || last.size !== this.entries.size) return false;\n for (const [container, entry] of this.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, refId, subs, entries } = this;\n\n for (const [dep, sub] of subs) {\n if (!entries.has(dep)) {\n this.drop(dep, sub);\n subs.delete(dep);\n }\n }\n\n for (const [dep, entry] of entries) {\n if (entry.kind === 'primary') continue;\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 const interest = expandWithAncestors(entry.paths, dep.interner);\n dep.registerConsumerPaths(consumerId, entry.paths);\n const existing = subs.get(dep);\n if (existing) {\n existing.interestRef.current = interest;\n continue;\n }\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 interestRef: { current: PathSet } = { current: interest };\n const unsubscribe = dep.channel.subscribe(\n () => interestRef.current,\n this.onChange,\n );\n subs.set(dep, {\n unsubscribe,\n interestRef,\n registry,\n Type: entry.Type,\n key: entry.key,\n });\n }\n\n this.lastReconciled = new Map(entries);\n }\n\n dispose(): void {\n for (const [dep, sub] of this.subs) this.drop(dep, sub);\n this.subs.clear();\n this.lastReconciled = null;\n }\n\n private drop(dep: StateContainer, sub: DepSub): void {\n sub.unsubscribe();\n dep.unregisterConsumer(this.consumerId);\n sub.registry.release(sub.Type, sub.key, false, this.refId);\n }\n}\n\nfunction sameEntry(a: SessionEntry, b: SessionEntry): boolean {\n if (a.kind !== b.kind || !pathSetEquals(a.paths, b.paths)) return false;\n if (a.kind === 'primary' || b.kind === 'primary') return true;\n return a.key === b.key && Object.is(a.args, b.args);\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).proxy };\n perDep.set(dep, cache);\n } else {\n cache.ref.current = result.value;\n }\n session.record(dep, result.paths, brand.Type, key, args);\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 {\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from 'react';\nimport {\n type ExtractArgs,\n type ExtractState,\n type InstanceState,\n type StateContainer,\n type StateContainerConstructor,\n} from '@blac/core';\nimport {\n ALL_PATHS,\n emptyPathSet,\n trackRender,\n ProxyCache,\n type PathSet,\n} from '@dirtytalk/structural';\nimport { useProvidedArgs } from './BlocProvider';\nimport { useRegistry } from './RegistryProvider';\nimport { buildTrackedProxy } from './buildTrackedProxy';\nimport { DepSession, makeDepWrapper, type DepHandleLike } from './depSession';\nimport { expandWithAncestors } from './expandWithAncestors';\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:\n * ```ts\n * const id = useId();\n * const [state, bloc] = useBloc(MyBloc, { args: { _id: id } });\n * ```\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 type TBloc = InstanceState<T>;\n\n const registry = useRegistry();\n\n const consumerRef = useRef<Consumer | null>(null);\n const consumer = (consumerRef.current ??= createConsumer());\n const consumerId = consumer.id;\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) as\n | ExtractArgs<T>\n | undefined;\n const instanceKey = registry.resolveKey(BlocClass, undefined, effectiveArgs);\n\n // Bumped by the ownership effect when the rendered instance was disposed and\n // recreated before commit (a same-commit owner handoff, or StrictMode's\n // double effect). Rebuilds the memo and the subscription against the live\n // instance, since the tracked proxy can't be retargeted in place.\n const rebindNonce = consumer.rebindNonce;\n\n const { bloc, trackedBloc } = useMemo<{\n bloc: TBloc;\n trackedBloc: TBloc;\n }>(() => {\n // No ref during render; the ownership effect claims it on commit. The\n // registry sweeps the instance if no commit ever does (SSR, discarded\n // renders).\n const instance = registry.acquire(BlocClass, instanceKey, {\n canCreate: true,\n countRef: false,\n args: effectiveArgs,\n sweepIfUnowned: true,\n }) as TBloc;\n\n // Dep proxies get `onDepHandle` too, so nested `.track()` calls (A→B→C)\n // record into this consumer's session.\n const depWrappers = new Map<object, unknown>();\n const onDepHandle = (handle: object): unknown => {\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 const { proxy } = buildTrackedProxy(\n instance as object,\n consumer.tracked,\n onDepHandle,\n );\n\n return { bloc: instance, trackedBloc: proxy as TBloc };\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n }, [BlocClass, instanceKey, rebindNonce, registry]);\n\n consumer.container = bloc as unknown as StateContainer;\n\n // Keyed on `BlocClass` as well as the key: classes without args or a\n // `static key` share the default key, so `useBloc(cond ? A : B)` must still\n // re-subscribe on a class swap.\n const subscribe = useMemo(\n () => (onStoreChange: () => void) => {\n consumer.notify = onStoreChange;\n const container = consumer.container;\n const unsubscribe = container.channel.subscribe(\n () => consumer.interest,\n () => {\n if (consumer.isSelectMode) {\n const select = consumer.select;\n if (select) {\n const next = select(\n container.state as ExtractState<T>,\n container as unknown as InstanceState<T>,\n );\n const prev = consumer.selection;\n if (prev !== null && shallowArrayEqual(prev, next)) return;\n consumer.selection = next;\n }\n }\n consumer.bump();\n },\n );\n container.registerConsumerPaths(consumerId, 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 () => {\n consumer.notify = noop;\n unsubscribe();\n container.unregisterConsumer(consumerId);\n };\n },\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n [BlocClass, instanceKey, consumerId, rebindNonce, registry],\n );\n\n useSyncExternalStore(\n subscribe,\n consumer.getSnapshot,\n consumer.getServerSnapshot,\n );\n\n // Ownership is claimed at commit so acquire and release pair exactly. Not\n // keyed on `rebindNonce`: a rebind must not release and re-acquire the ref.\n useLayoutEffect(() => {\n const live = registry.acquire(BlocClass, instanceKey, {\n canCreate: true,\n countRef: true,\n refId: consumer.primaryRefId,\n args: resolveEffectiveArgs(consumer),\n }) as TBloc;\n consumer.ownedBloc = live;\n consumer.onMount?.(live as InstanceType<T>);\n // The render captured a stale instance; re-render against the live one.\n if (live !== bloc) {\n consumer.rebindNonce++;\n consumer.bump();\n }\n return () => {\n // Before release, so the bloc is still alive in the callback.\n consumer.onUnmount?.((consumer.ownedBloc ?? bloc) as InstanceType<T>);\n registry.release(BlocClass, instanceKey, false, consumer.primaryRefId);\n };\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n }, [BlocClass, instanceKey, consumerId, registry]);\n\n const container = consumer.container;\n const rawState = container.state as ExtractState<T>;\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, bloc as InstanceState<T>);\n consumer.interest = 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 // oxlint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(() => {\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 if (consumer.isSelectMode) {\n // A later switch back to auto-track must reconcile in full.\n deps.invalidate();\n return;\n }\n if (deps.isUnchanged()) return;\n\n const primary = consumer.container;\n primary.registerConsumerPaths(consumerId, consumer.paths);\n consumer.interest = expandWithAncestors(consumer.paths, primary.interner);\n deps.reconcile(registry);\n });\n\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n useEffect(() => () => consumer.deps.dispose(), [consumerId]);\n\n return [state, trackedBloc] as UseBlocReturn<T, ExtractState<T>>;\n}\n\nconst noop = (): void => {};\n\n/**\n * Everything one `useBloc` call keeps between renders.\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 /** Written only by the render memo. */\n container: StateContainer;\n version: number;\n /** `noop` while unsubscribed. */\n notify: () => void;\n bump: () => void;\n getSnapshot: () => number;\n getServerSnapshot: () => number;\n /** Expanded leaf paths, or `ALL_PATHS` in select mode. */\n interest: PathSet;\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 rebindNonce: number;\n /** The instance the ownership effect holds a ref on. */\n ownedBloc: unknown;\n}\n\nfunction createConsumer(): Consumer {\n const id = `useBloc-${nextConsumerId++}`;\n const consumer: Consumer = {\n id,\n primaryRefId: `useBloc@${id}`,\n container: null as unknown as StateContainer,\n version: 0,\n notify: noop,\n bump: () => {\n consumer.version++;\n consumer.notify();\n },\n getSnapshot: () => consumer.version,\n getServerSnapshot: () => consumer.version,\n interest: emptyPathSet(),\n paths: emptyPathSet(),\n isSelectMode: false,\n selection: null,\n deps: new DepSession(id, () => consumer.bump()),\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 rebindNonce: 0,\n ownedBloc: null,\n };\n return consumer;\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 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 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:\n * ```ts\n * const id = useId();\n * const [state, bloc] = useBloc(MyBloc, { args: { _id: id } });\n * ```\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 instanceKey = registry.resolveKey(BlocClass, undefined, effectiveArgs);\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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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blac/react",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
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,7 +78,7 @@
|
|
|
78
78
|
}
|
|
79
79
|
],
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"@dirtytalk/structural": "^0.1.
|
|
81
|
+
"@dirtytalk/structural": "^0.1.4"
|
|
82
82
|
},
|
|
83
83
|
"peerDependencies": {
|
|
84
84
|
"@testing-library/react": "^14.0.0 || ^15.0.0 || ^16.0.0",
|