@blac/react 2.0.1 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -102,7 +102,6 @@ useBloc(MyBloc, {
102
102
 
103
103
  // Instance management
104
104
  instanceId: 'unique-id', // Custom instance identifier
105
- props: { userId: 123 }, // Props passed to bloc
106
105
 
107
106
  // Lifecycle callbacks
108
107
  onMount: (bloc) => console.log('Mounted', bloc),
@@ -143,7 +142,6 @@ function IncrementButton() {
143
142
  ```tsx
144
143
  useBlocActions(MyBloc, {
145
144
  instanceId: 'unique-id', // Custom instance identifier
146
- props: { config: value }, // Props passed to bloc
147
145
  onMount: (bloc) => bloc.initialize(),
148
146
  onUnmount: (bloc) => bloc.cleanup(),
149
147
  });
@@ -249,18 +247,16 @@ function useBlocActions<T extends StateContainerConstructor>(
249
247
  | `autoTrack` | `boolean` | Enable auto-tracking (default: true) |
250
248
  | `dependencies` | `(state) => any[]` | Manual dependency selector |
251
249
  | `instanceId` | `string \| number` | Custom instance identifier |
252
- | `props` | `P` | Props passed to bloc constructor |
253
250
  | `onMount` | `(bloc) => void` | Called when component mounts |
254
251
  | `onUnmount` | `(bloc) => void` | Called when component unmounts |
255
252
 
256
253
  ### UseBlocActionsOptions
257
254
 
258
- | Option | Type | Description |
259
- | ------------ | ------------------ | -------------------------------- |
260
- | `instanceId` | `string \| number` | Custom instance identifier |
261
- | `props` | `P` | Props passed to bloc constructor |
262
- | `onMount` | `(bloc) => void` | Called when component mounts |
263
- | `onUnmount` | `(bloc) => void` | Called when component unmounts |
255
+ | Option | Type | Description |
256
+ | ------------ | ------------------ | ------------------------------ |
257
+ | `instanceId` | `string \| number` | Custom instance identifier |
258
+ | `onMount` | `(bloc) => void` | Called when component mounts |
259
+ | `onUnmount` | `(bloc) => void` | Called when component unmounts |
264
260
 
265
261
  ## Compatibility
266
262
 
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  let react = require("react");
2
- let _blac_core = require("@blac/core");
2
+ let _blac_adapter = require("@blac/adapter");
3
3
 
4
4
  //#region src/utils/instance-keys.ts
5
5
  /**
@@ -21,7 +21,7 @@ let _blac_core = require("@blac/core");
21
21
  function generateInstanceKey(componentRef, isIsolated, providedId) {
22
22
  if (providedId !== void 0) return typeof providedId === "number" ? String(providedId) : providedId;
23
23
  if (isIsolated) {
24
- if (!componentRef.__blocInstanceId) componentRef.__blocInstanceId = (0, _blac_core.generateIsolatedKey)();
24
+ if (!componentRef.__blocInstanceId) componentRef.__blocInstanceId = (0, _blac_adapter.generateIsolatedKey)();
25
25
  return componentRef.__blocInstanceId;
26
26
  }
27
27
  }
@@ -69,8 +69,8 @@ function resetBlacReactConfig() {
69
69
  //#endregion
70
70
  //#region src/useBloc.ts
71
71
  function determineTrackingMode(options) {
72
- const globalConfig$1 = getBlacReactConfig();
73
- const autoTrackEnabled = options?.autoTrack !== void 0 ? options.autoTrack : globalConfig$1.autoTrack;
72
+ const globalConfig = getBlacReactConfig();
73
+ const autoTrackEnabled = options?.autoTrack !== void 0 ? options.autoTrack : globalConfig.autoTrack;
74
74
  return {
75
75
  useManualDeps: options?.dependencies !== void 0,
76
76
  autoTrackEnabled
@@ -84,11 +84,8 @@ function determineTrackingMode(options) {
84
84
  * - **Manual dependencies**: Explicit dependency array like useEffect
85
85
  * - **No tracking**: Returns full state without optimization
86
86
  *
87
- * **Note:** This hook does NOT support stateless containers (StatelessCubit, StatelessVertex).
88
- * Use `useBlocActions()` instead for stateless containers.
89
- *
90
87
  * @template T - The state container constructor type (inferred from BlocClass)
91
- * @param BlocClass - The state container class to connect to (must not be stateless)
88
+ * @param BlocClass - The state container class to connect to
92
89
  * @param options - Configuration options for tracking mode and instance management
93
90
  * @returns Tuple with [state, bloc instance, ref]
94
91
  *
@@ -113,79 +110,53 @@ function determineTrackingMode(options) {
113
110
  */
114
111
  function useBloc(BlocClass, options) {
115
112
  const componentRef = (0, react.useRef)({});
116
- const initialPropsRef = (0, react.useRef)(options?.props);
117
- const isStateless = (0, _blac_core.isStatelessClass)(BlocClass);
118
- const isIsolated = (0, _blac_core.isIsolatedClass)(BlocClass);
113
+ const isIsolated = (0, _blac_adapter.isIsolatedClass)(BlocClass);
119
114
  const [bloc, subscribe, getSnapshot, instanceKey, adapterState, rawInstance] = (0, react.useMemo)(() => {
120
- if (isStateless) {
121
- console.error(`[BlaC] useBloc() does not support stateless containers. "${BlocClass.name}" is a StatelessCubit or StatelessVertex. Use useBlocActions() instead for stateless containers.`);
122
- const instance$1 = (0, _blac_core.acquire)(BlocClass, "default");
123
- return [
124
- instance$1,
125
- () => () => {},
126
- () => ({}),
127
- void 0,
128
- null,
129
- instance$1
130
- ];
131
- }
132
- const instanceKey$1 = generateInstanceKey(componentRef.current, isIsolated, options?.instanceId);
133
- const instance = (0, _blac_core.acquire)(BlocClass, instanceKey$1, { props: initialPropsRef.current });
134
- if (initialPropsRef.current !== void 0) instance.updateProps(initialPropsRef.current);
115
+ const instanceKey = generateInstanceKey(componentRef.current, isIsolated, options?.instanceId);
116
+ const instance = (0, _blac_adapter.acquire)(BlocClass, instanceKey);
135
117
  const { useManualDeps, autoTrackEnabled } = determineTrackingMode(options);
136
118
  let subscribeFn;
137
119
  let getSnapshotFn;
138
- let adapterState$1;
120
+ let adapterState;
139
121
  if (useManualDeps && options?.dependencies) {
140
- adapterState$1 = (0, _blac_core.manualDepsInit)(instance);
141
- subscribeFn = (0, _blac_core.manualDepsSubscribe)(instance, adapterState$1, { dependencies: options.dependencies });
142
- getSnapshotFn = (0, _blac_core.manualDepsSnapshot)(instance, adapterState$1, { dependencies: options.dependencies });
122
+ adapterState = (0, _blac_adapter.manualDepsInit)(instance);
123
+ subscribeFn = (0, _blac_adapter.manualDepsSubscribe)(instance, adapterState, { dependencies: options.dependencies });
124
+ getSnapshotFn = (0, _blac_adapter.manualDepsSnapshot)(instance, adapterState, { dependencies: options.dependencies });
143
125
  } else if (!autoTrackEnabled) {
144
- adapterState$1 = (0, _blac_core.noTrackInit)(instance);
145
- subscribeFn = (0, _blac_core.noTrackSubscribe)(instance);
146
- getSnapshotFn = (0, _blac_core.noTrackSnapshot)(instance);
126
+ adapterState = (0, _blac_adapter.noTrackInit)(instance);
127
+ subscribeFn = (0, _blac_adapter.noTrackSubscribe)(instance);
128
+ getSnapshotFn = (0, _blac_adapter.noTrackSnapshot)(instance);
147
129
  } else {
148
- adapterState$1 = (0, _blac_core.autoTrackInit)(instance);
149
- subscribeFn = (0, _blac_core.autoTrackSubscribe)(instance, adapterState$1);
150
- getSnapshotFn = (0, _blac_core.autoTrackSnapshot)(instance, adapterState$1);
130
+ adapterState = (0, _blac_adapter.autoTrackInit)(instance);
131
+ subscribeFn = (0, _blac_adapter.autoTrackSubscribe)(instance, adapterState);
132
+ getSnapshotFn = (0, _blac_adapter.autoTrackSnapshot)(instance, adapterState);
151
133
  }
152
134
  return [
153
- adapterState$1.proxiedBloc,
135
+ adapterState.proxiedBloc,
154
136
  subscribeFn,
155
137
  getSnapshotFn,
156
- instanceKey$1,
157
- adapterState$1,
138
+ instanceKey,
139
+ adapterState,
158
140
  instance
159
141
  ];
160
142
  }, [
161
143
  BlocClass,
162
- isStateless,
163
144
  isIsolated,
164
145
  options?.instanceId
165
146
  ]);
166
147
  const state = (0, react.useSyncExternalStore)(subscribe, getSnapshot);
167
148
  const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
168
- const externalDepsManager = (0, react.useRef)(new _blac_core.ExternalDepsManager());
169
- (0, react.useEffect)(() => {
170
- if (isStateless) return;
171
- if (options?.props !== initialPropsRef.current) rawInstance.updateProps(options?.props);
172
- }, [
173
- options?.props,
174
- rawInstance,
175
- isStateless
176
- ]);
149
+ const externalDepsManager = (0, react.useRef)(new _blac_adapter.ExternalDepsManager());
177
150
  (0, react.useEffect)(() => {
178
- if (isStateless || !adapterState) return;
179
- (0, _blac_core.disableGetterTracking)(adapterState, rawInstance);
151
+ (0, _blac_adapter.disableGetterTracking)(adapterState, rawInstance);
180
152
  externalDepsManager.current.updateSubscriptions(adapterState.getterState, rawInstance, forceUpdate);
181
153
  });
182
154
  (0, react.useEffect)(() => {
183
- if (isStateless) return;
184
155
  if (options?.onMount) options.onMount(bloc);
185
156
  return () => {
186
157
  externalDepsManager.current.cleanup();
187
158
  if (options?.onUnmount) options.onUnmount(bloc);
188
- (0, _blac_core.release)(BlocClass, instanceKey);
159
+ (0, _blac_adapter.release)(BlocClass, instanceKey);
189
160
  if (isIsolated && !rawInstance.isDisposed) rawInstance.dispose();
190
161
  };
191
162
  }, []);
@@ -196,65 +167,8 @@ function useBloc(BlocClass, options) {
196
167
  ];
197
168
  }
198
169
 
199
- //#endregion
200
- //#region src/useBlocActions.ts
201
- /**
202
- * React hook that connects to a state container instance without triggering re-renders.
203
- * Use this when you only need to call actions on the bloc without subscribing to state changes.
204
- *
205
- * **This is the recommended hook for stateless containers** (StatelessCubit, StatelessVertex).
206
- * It also works with regular Cubit/Vertex when you don't need state subscriptions.
207
- *
208
- * @template T - The state container constructor type (inferred from BlocClass)
209
- * @param BlocClass - The state container class to connect to (supports both stateful and stateless)
210
- * @param options - Configuration options for instance management and lifecycle
211
- * @returns The state container instance for calling actions
212
- *
213
- * @example Basic usage with stateless container
214
- * ```ts
215
- * class AnalyticsService extends StatelessCubit {
216
- * trackEvent(name: string) { ... }
217
- * }
218
- *
219
- * const analytics = useBlocActions(AnalyticsService);
220
- * analytics.trackEvent('page_view');
221
- * ```
222
- *
223
- * @example With stateful container (no re-renders)
224
- * ```ts
225
- * const myBloc = useBlocActions(MyBloc);
226
- * myBloc.someMethod(); // Won't cause re-renders
227
- * ```
228
- *
229
- * @example With isolated instance
230
- * ```ts
231
- * const myBloc = useBlocActions(MyBloc, {
232
- * instanceId: 'unique-id'
233
- * });
234
- * ```
235
- */
236
- function useBlocActions(BlocClass, options) {
237
- const componentRef = (0, react.useRef)({});
238
- const initialPropsRef = (0, react.useRef)(options?.props);
239
- const [bloc, instanceKey] = (0, react.useMemo)(() => {
240
- const isIsolated = (0, _blac_core.isIsolatedClass)(BlocClass);
241
- const instanceKey$1 = generateInstanceKey(componentRef.current, isIsolated, options?.instanceId);
242
- return [(0, _blac_core.acquire)(BlocClass, instanceKey$1, { props: initialPropsRef.current }), instanceKey$1];
243
- }, [BlocClass]);
244
- (0, react.useEffect)(() => {
245
- if (options?.onMount) options.onMount(bloc);
246
- return () => {
247
- if (options?.onUnmount) options.onUnmount(bloc);
248
- (0, _blac_core.release)(BlocClass, instanceKey);
249
- if ((0, _blac_core.isIsolatedClass)(BlocClass) && !bloc.isDisposed) bloc.dispose();
250
- };
251
- }, []);
252
- return bloc;
253
- }
254
-
255
170
  //#endregion
256
171
  exports.configureBlacReact = configureBlacReact;
257
172
  exports.resetBlacReactConfig = resetBlacReactConfig;
258
173
  exports.useBloc = useBloc;
259
- exports.useBlocActions = useBlocActions;
260
174
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["globalConfig","instance","instanceKey","adapterState","ExternalDepsManager","instanceKey"],"sources":["../src/utils/instance-keys.ts","../src/config.ts","../src/useBloc.ts","../src/useBlocActions.ts"],"sourcesContent":["/**\n * Instance key generation utilities for React integration\n */\n\nimport { generateIsolatedKey } from '@blac/core';\nimport type { ComponentRef } from '../types';\n\n/**\n * Generate an instance key for a bloc\n *\n * Logic:\n * - If user provides instanceId, use it (convert number to string)\n * - If isolated, generate or reuse a unique key for this component\n * - Otherwise, return undefined (use default key)\n *\n * @param componentRef - React component reference (persists across remounts)\n * @param isIsolated - Whether the bloc is isolated\n * @param providedId - User-provided instance ID (from options)\n * @returns Instance key string or undefined for default\n */\nexport function generateInstanceKey(\n componentRef: ComponentRef,\n isIsolated: boolean,\n providedId?: string | number,\n): string | undefined {\n // User explicitly provided an ID - use it\n if (providedId !== undefined) {\n return typeof providedId === 'number' ? String(providedId) : providedId;\n }\n\n // Isolated bloc - generate unique key per component\n if (isIsolated) {\n if (!componentRef.__blocInstanceId) {\n componentRef.__blocInstanceId = generateIsolatedKey();\n }\n return componentRef.__blocInstanceId;\n }\n\n // Shared bloc - use default key (undefined)\n return undefined;\n}\n","/**\n * Global configuration for @blac/react\n */\n\nexport interface BlacReactConfig {\n /** Enable automatic property tracking via Proxy (default: true) */\n autoTrack: boolean;\n}\n\nconst defaultConfig: BlacReactConfig = {\n autoTrack: true,\n};\n\nlet globalConfig: BlacReactConfig = { ...defaultConfig };\n\n/**\n * Configure global defaults for @blac/react hooks.\n *\n * @example\n * ```ts\n * import { configureBlacReact } from '@blac/react';\n *\n * // Disable auto-tracking globally\n * configureBlacReact({\n * autoTrack: false\n * });\n * ```\n *\n * @param config - Partial configuration to merge with defaults\n */\nexport function configureBlacReact(config: Partial<BlacReactConfig>): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\n/**\n * Get the current global configuration.\n * @internal\n */\nexport function getBlacReactConfig(): BlacReactConfig {\n return globalConfig;\n}\n\n/**\n * Reset configuration to defaults (useful for testing).\n * @internal\n */\nexport function resetBlacReactConfig(): void {\n globalConfig = { ...defaultConfig };\n}\n","import {\n useMemo,\n useSyncExternalStore,\n useEffect,\n useRef,\n useReducer,\n} from 'react';\nimport {\n type ExtractState,\n type AdapterState,\n type IsStatelessContainer,\n ExternalDepsManager,\n autoTrackSubscribe,\n manualDepsSubscribe,\n noTrackSubscribe,\n autoTrackSnapshot,\n manualDepsSnapshot,\n noTrackSnapshot,\n autoTrackInit,\n manualDepsInit,\n noTrackInit,\n disableGetterTracking,\n isIsolatedClass,\n isStatelessClass,\n StateContainerConstructor,\n InstanceState,\n acquire,\n release,\n} from '@blac/core';\nimport type { UseBlocOptions, UseBlocReturn, ComponentRef } from './types';\nimport { generateInstanceKey } from './utils/instance-keys';\nimport { getBlacReactConfig } from './config';\n\ninterface TrackingMode {\n useManualDeps: boolean;\n autoTrackEnabled: boolean;\n}\n\nfunction determineTrackingMode<TBloc extends StateContainerConstructor>(\n options?: UseBlocOptions<TBloc>,\n): TrackingMode {\n const globalConfig = getBlacReactConfig();\n const autoTrackEnabled =\n options?.autoTrack !== undefined\n ? options.autoTrack\n : globalConfig.autoTrack;\n\n return {\n useManualDeps: options?.dependencies !== undefined,\n autoTrackEnabled,\n };\n}\n\n/**\n * Type that produces a TypeScript error when a stateless container is used.\n * Evaluates to `never` for stateless containers, causing a type mismatch.\n */\ntype StatefulContainer<T extends StateContainerConstructor> =\n IsStatelessContainer<T> extends true ? never : T;\n\n/**\n * React hook that connects a component to a state container with automatic re-render on state changes.\n *\n * Supports three tracking modes:\n * - **Auto-tracking** (default): Automatically detects accessed state properties via Proxy\n * - **Manual dependencies**: Explicit dependency array like useEffect\n * - **No tracking**: Returns full state without optimization\n *\n * **Note:** This hook does NOT support stateless containers (StatelessCubit, StatelessVertex).\n * Use `useBlocActions()` instead for stateless containers.\n *\n * @template T - The state container constructor type (inferred from BlocClass)\n * @param BlocClass - The state container class to connect to (must not be stateless)\n * @param options - Configuration options for tracking mode and instance management\n * @returns Tuple with [state, bloc instance, ref]\n *\n * @example Basic usage\n * ```ts\n * const [state, myBloc, ref] = useBloc(MyBloc);\n * ```\n *\n * @example With manual dependencies\n * ```ts\n * const [state, myBloc] = useBloc(MyBloc, {\n * dependencies: (state) => [state.count]\n * });\n * ```\n *\n * @example With isolated instance\n * ```ts\n * const [state, myBloc] = useBloc(MyBloc, {\n * instanceId: 'unique-id'\n * });\n * ```\n */\nexport function useBloc<\n T extends StateContainerConstructor = StateContainerConstructor,\n>(\n BlocClass: StatefulContainer<T>,\n options?: UseBlocOptions<T>,\n): UseBlocReturn<T, ExtractState<T>> {\n type TBloc = InstanceState<T>;\n\n const componentRef = useRef<ComponentRef>({});\n const initialPropsRef = useRef(options?.props);\n const isStateless = isStatelessClass(BlocClass);\n const isIsolated = isIsolatedClass(BlocClass);\n\n const [bloc, subscribe, getSnapshot, instanceKey, adapterState, rawInstance] =\n useMemo<\n readonly [\n TBloc,\n (callback: () => void) => () => void,\n () => ExtractState<T>,\n string | undefined,\n AdapterState<T> | null,\n TBloc,\n ]\n >(() => {\n // Runtime check for stateless containers - log error but don't crash\n if (isStateless) {\n console.error(\n `[BlaC] useBloc() does not support stateless containers. ` +\n `\"${BlocClass.name}\" is a StatelessCubit or StatelessVertex. ` +\n `Use useBlocActions() instead for stateless containers.`,\n );\n const instance = acquire(BlocClass, 'default') as TBloc;\n // Return minimal working functions that won't subscribe to anything\n return [\n instance,\n () => () => {},\n () => ({}) as ExtractState<T>,\n undefined,\n null,\n instance,\n ];\n }\n\n const instanceKey = generateInstanceKey(\n componentRef.current,\n isIsolated,\n options?.instanceId,\n );\n\n const instance = acquire(BlocClass, instanceKey, {\n props: initialPropsRef.current,\n }) as TBloc;\n\n if (initialPropsRef.current !== undefined) {\n instance.updateProps(initialPropsRef.current);\n }\n\n const { useManualDeps, autoTrackEnabled } =\n determineTrackingMode(options);\n\n let subscribeFn: (callback: () => void) => () => void;\n let getSnapshotFn: () => ExtractState<T>;\n let adapterState: AdapterState<T>;\n\n if (useManualDeps && options?.dependencies) {\n adapterState = manualDepsInit(instance);\n subscribeFn = manualDepsSubscribe(instance, adapterState, {\n dependencies: options.dependencies,\n });\n getSnapshotFn = manualDepsSnapshot(instance, adapterState, {\n dependencies: options.dependencies,\n });\n } else if (!autoTrackEnabled) {\n adapterState = noTrackInit(instance);\n subscribeFn = noTrackSubscribe(instance);\n getSnapshotFn = noTrackSnapshot(instance);\n } else {\n adapterState = autoTrackInit(instance);\n subscribeFn = autoTrackSubscribe(instance, adapterState);\n getSnapshotFn = autoTrackSnapshot(instance, adapterState);\n }\n\n return [\n adapterState.proxiedBloc as TBloc,\n subscribeFn,\n getSnapshotFn,\n instanceKey,\n adapterState,\n instance,\n ];\n }, [BlocClass, isStateless, isIsolated, options?.instanceId]);\n\n const state = useSyncExternalStore(subscribe, getSnapshot);\n\n const [, forceUpdate] = useReducer((x: number) => x + 1, 0);\n\n const externalDepsManager = useRef(new ExternalDepsManager());\n\n useEffect(() => {\n if (isStateless) return;\n if (options?.props !== initialPropsRef.current) {\n rawInstance.updateProps(options?.props);\n }\n }, [options?.props, rawInstance, isStateless]);\n\n useEffect(() => {\n if (isStateless || !adapterState) return;\n disableGetterTracking(adapterState, rawInstance);\n externalDepsManager.current.updateSubscriptions(\n adapterState.getterState,\n rawInstance,\n forceUpdate,\n );\n });\n\n useEffect(() => {\n if (isStateless) return;\n\n if (options?.onMount) {\n options.onMount(bloc as InstanceType<T>);\n }\n\n return () => {\n externalDepsManager.current.cleanup();\n\n if (options?.onUnmount) {\n options.onUnmount(bloc as InstanceType<T>);\n }\n\n release(BlocClass, instanceKey);\n\n if (isIsolated && !rawInstance.isDisposed) {\n rawInstance.dispose();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return [state, bloc, componentRef] as UseBlocReturn<T, ExtractState<T>>;\n}\n","import { useMemo, useEffect, useRef } from 'react';\nimport {\n StateContainerConstructor,\n isIsolatedClass,\n acquire,\n release,\n} from '@blac/core';\nimport type { ComponentRef } from './types';\nimport { generateInstanceKey } from './utils/instance-keys';\n\n/**\n * Configuration options for useBlocActions hook\n * @template TBloc - The state container type\n * @template TProps - Props type passed to the container\n */\nexport interface UseBlocActionsOptions<TBloc, TProps = any> {\n /** Props passed to bloc constructor or updateProps */\n props?: TProps;\n /** Custom instance identifier for shared or isolated instances */\n instanceId?: string | number;\n /** Callback invoked when bloc instance mounts */\n onMount?: (bloc: TBloc) => void;\n /** Callback invoked when bloc instance unmounts */\n onUnmount?: (bloc: TBloc) => void;\n}\n\n/**\n * React hook that connects to a state container instance without triggering re-renders.\n * Use this when you only need to call actions on the bloc without subscribing to state changes.\n *\n * **This is the recommended hook for stateless containers** (StatelessCubit, StatelessVertex).\n * It also works with regular Cubit/Vertex when you don't need state subscriptions.\n *\n * @template T - The state container constructor type (inferred from BlocClass)\n * @param BlocClass - The state container class to connect to (supports both stateful and stateless)\n * @param options - Configuration options for instance management and lifecycle\n * @returns The state container instance for calling actions\n *\n * @example Basic usage with stateless container\n * ```ts\n * class AnalyticsService extends StatelessCubit {\n * trackEvent(name: string) { ... }\n * }\n *\n * const analytics = useBlocActions(AnalyticsService);\n * analytics.trackEvent('page_view');\n * ```\n *\n * @example With stateful container (no re-renders)\n * ```ts\n * const myBloc = useBlocActions(MyBloc);\n * myBloc.someMethod(); // Won't cause re-renders\n * ```\n *\n * @example With isolated instance\n * ```ts\n * const myBloc = useBlocActions(MyBloc, {\n * instanceId: 'unique-id'\n * });\n * ```\n */\nexport function useBlocActions<\n T extends StateContainerConstructor = StateContainerConstructor,\n>(\n BlocClass: T,\n options?: UseBlocActionsOptions<InstanceType<T>>,\n): InstanceType<T> {\n const componentRef = useRef<ComponentRef>({});\n const initialPropsRef = useRef(options?.props);\n\n const [bloc, instanceKey] = useMemo(() => {\n const isIsolated = isIsolatedClass(BlocClass);\n\n const instanceKey = generateInstanceKey(\n componentRef.current,\n isIsolated,\n options?.instanceId,\n );\n\n const instance = acquire(BlocClass, instanceKey, {\n props: initialPropsRef.current,\n });\n\n return [instance, instanceKey] as const;\n }, [BlocClass]);\n\n useEffect(() => {\n if (options?.onMount) {\n options.onMount(bloc);\n }\n\n return () => {\n if (options?.onUnmount) {\n options.onUnmount(bloc);\n }\n\n release(BlocClass, instanceKey);\n\n if (isIsolatedClass(BlocClass) && !bloc.isDisposed) {\n bloc.dispose();\n }\n };\n }, []);\n\n return bloc as InstanceType<T>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,oBACd,cACA,YACA,YACoB;AAEpB,KAAI,eAAe,OACjB,QAAO,OAAO,eAAe,WAAW,OAAO,WAAW,GAAG;AAI/D,KAAI,YAAY;AACd,MAAI,CAAC,aAAa,iBAChB,cAAa,wDAAwC;AAEvD,SAAO,aAAa;;;;;;AC1BxB,MAAM,gBAAiC,EACrC,WAAW,MACZ;AAED,IAAI,eAAgC,EAAE,GAAG,eAAe;;;;;;;;;;;;;;;;AAiBxD,SAAgB,mBAAmB,QAAwC;AACzE,gBAAe;EAAE,GAAG;EAAc,GAAG;EAAQ;;;;;;AAO/C,SAAgB,qBAAsC;AACpD,QAAO;;;;;;AAOT,SAAgB,uBAA6B;AAC3C,gBAAe,EAAE,GAAG,eAAe;;;;;ACTrC,SAAS,sBACP,SACc;CACd,MAAMA,iBAAe,oBAAoB;CACzC,MAAM,mBACJ,SAAS,cAAc,SACnB,QAAQ,YACRA,eAAa;AAEnB,QAAO;EACL,eAAe,SAAS,iBAAiB;EACzC;EACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CH,SAAgB,QAGd,WACA,SACmC;CAGnC,MAAM,iCAAoC,EAAE,CAAC;CAC7C,MAAM,oCAAyB,SAAS,MAAM;CAC9C,MAAM,+CAA+B,UAAU;CAC/C,MAAM,6CAA6B,UAAU;CAE7C,MAAM,CAAC,MAAM,WAAW,aAAa,aAAa,cAAc,wCAUtD;AAEN,MAAI,aAAa;AACf,WAAQ,MACN,4DACM,UAAU,KAAK,kGAEtB;GACD,MAAMC,qCAAmB,WAAW,UAAU;AAE9C,UAAO;IACLA;gBACY;WACL,EAAE;IACT;IACA;IACAA;IACD;;EAGH,MAAMC,gBAAc,oBAClB,aAAa,SACb,YACA,SAAS,WACV;EAED,MAAM,mCAAmB,WAAWA,eAAa,EAC/C,OAAO,gBAAgB,SACxB,CAAC;AAEF,MAAI,gBAAgB,YAAY,OAC9B,UAAS,YAAY,gBAAgB,QAAQ;EAG/C,MAAM,EAAE,eAAe,qBACrB,sBAAsB,QAAQ;EAEhC,IAAI;EACJ,IAAI;EACJ,IAAIC;AAEJ,MAAI,iBAAiB,SAAS,cAAc;AAC1C,mDAA8B,SAAS;AACvC,qDAAkC,UAAUA,gBAAc,EACxD,cAAc,QAAQ,cACvB,CAAC;AACF,sDAAmC,UAAUA,gBAAc,EACzD,cAAc,QAAQ,cACvB,CAAC;aACO,CAAC,kBAAkB;AAC5B,gDAA2B,SAAS;AACpC,kDAA+B,SAAS;AACxC,mDAAgC,SAAS;SACpC;AACL,kDAA6B,SAAS;AACtC,oDAAiC,UAAUA,eAAa;AACxD,qDAAkC,UAAUA,eAAa;;AAG3D,SAAO;GACLA,eAAa;GACb;GACA;GACAD;GACAC;GACA;GACD;IACA;EAAC;EAAW;EAAa;EAAY,SAAS;EAAW,CAAC;CAE/D,MAAM,wCAA6B,WAAW,YAAY;CAE1D,MAAM,GAAG,sCAA2B,MAAc,IAAI,GAAG,EAAE;CAE3D,MAAM,wCAA6B,IAAIC,gCAAqB,CAAC;AAE7D,4BAAgB;AACd,MAAI,YAAa;AACjB,MAAI,SAAS,UAAU,gBAAgB,QACrC,aAAY,YAAY,SAAS,MAAM;IAExC;EAAC,SAAS;EAAO;EAAa;EAAY,CAAC;AAE9C,4BAAgB;AACd,MAAI,eAAe,CAAC,aAAc;AAClC,wCAAsB,cAAc,YAAY;AAChD,sBAAoB,QAAQ,oBAC1B,aAAa,aACb,aACA,YACD;GACD;AAEF,4BAAgB;AACd,MAAI,YAAa;AAEjB,MAAI,SAAS,QACX,SAAQ,QAAQ,KAAwB;AAG1C,eAAa;AACX,uBAAoB,QAAQ,SAAS;AAErC,OAAI,SAAS,UACX,SAAQ,UAAU,KAAwB;AAG5C,2BAAQ,WAAW,YAAY;AAE/B,OAAI,cAAc,CAAC,YAAY,WAC7B,aAAY,SAAS;;IAIxB,EAAE,CAAC;AAEN,QAAO;EAAC;EAAO;EAAM;EAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC5KpC,SAAgB,eAGd,WACA,SACiB;CACjB,MAAM,iCAAoC,EAAE,CAAC;CAC7C,MAAM,oCAAyB,SAAS,MAAM;CAE9C,MAAM,CAAC,MAAM,wCAA6B;EACxC,MAAM,6CAA6B,UAAU;EAE7C,MAAMC,gBAAc,oBAClB,aAAa,SACb,YACA,SAAS,WACV;AAMD,SAAO,yBAJkB,WAAWA,eAAa,EAC/C,OAAO,gBAAgB,SACxB,CAAC,EAEgBA,cAAY;IAC7B,CAAC,UAAU,CAAC;AAEf,4BAAgB;AACd,MAAI,SAAS,QACX,SAAQ,QAAQ,KAAK;AAGvB,eAAa;AACX,OAAI,SAAS,UACX,SAAQ,UAAU,KAAK;AAGzB,2BAAQ,WAAW,YAAY;AAE/B,uCAAoB,UAAU,IAAI,CAAC,KAAK,WACtC,MAAK,SAAS;;IAGjB,EAAE,CAAC;AAEN,QAAO"}
1
+ {"version":3,"file":"index.cjs","names":["ExternalDepsManager"],"sources":["../src/utils/instance-keys.ts","../src/config.ts","../src/useBloc.ts"],"sourcesContent":["/**\n * Instance key generation utilities for React integration\n */\n\nimport { generateIsolatedKey } from '@blac/adapter';\nimport type { ComponentRef } from '../types';\n\n/**\n * Generate an instance key for a bloc\n *\n * Logic:\n * - If user provides instanceId, use it (convert number to string)\n * - If isolated, generate or reuse a unique key for this component\n * - Otherwise, return undefined (use default key)\n *\n * @param componentRef - React component reference (persists across remounts)\n * @param isIsolated - Whether the bloc is isolated\n * @param providedId - User-provided instance ID (from options)\n * @returns Instance key string or undefined for default\n */\nexport function generateInstanceKey(\n componentRef: ComponentRef,\n isIsolated: boolean,\n providedId?: string | number,\n): string | undefined {\n // User explicitly provided an ID - use it\n if (providedId !== undefined) {\n return typeof providedId === 'number' ? String(providedId) : providedId;\n }\n\n // Isolated bloc - generate unique key per component\n if (isIsolated) {\n if (!componentRef.__blocInstanceId) {\n componentRef.__blocInstanceId = generateIsolatedKey();\n }\n return componentRef.__blocInstanceId;\n }\n\n // Shared bloc - use default key (undefined)\n return undefined;\n}\n","/**\n * Global configuration for @blac/react\n */\n\nexport interface BlacReactConfig {\n /** Enable automatic property tracking via Proxy (default: true) */\n autoTrack: boolean;\n}\n\nconst defaultConfig: BlacReactConfig = {\n autoTrack: true,\n};\n\nlet globalConfig: BlacReactConfig = { ...defaultConfig };\n\n/**\n * Configure global defaults for @blac/react hooks.\n *\n * @example\n * ```ts\n * import { configureBlacReact } from '@blac/react';\n *\n * // Disable auto-tracking globally\n * configureBlacReact({\n * autoTrack: false\n * });\n * ```\n *\n * @param config - Partial configuration to merge with defaults\n */\nexport function configureBlacReact(config: Partial<BlacReactConfig>): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\n/**\n * Get the current global configuration.\n * @internal\n */\nexport function getBlacReactConfig(): BlacReactConfig {\n return globalConfig;\n}\n\n/**\n * Reset configuration to defaults (useful for testing).\n * @internal\n */\nexport function resetBlacReactConfig(): void {\n globalConfig = { ...defaultConfig };\n}\n","import {\n useMemo,\n useSyncExternalStore,\n useEffect,\n useRef,\n useReducer,\n} from 'react';\nimport {\n type ExtractState,\n type AdapterState,\n ExternalDepsManager,\n autoTrackSubscribe,\n manualDepsSubscribe,\n noTrackSubscribe,\n autoTrackSnapshot,\n manualDepsSnapshot,\n noTrackSnapshot,\n autoTrackInit,\n manualDepsInit,\n noTrackInit,\n disableGetterTracking,\n isIsolatedClass,\n type StateContainerConstructor,\n type InstanceState,\n acquire,\n release,\n} from '@blac/adapter';\nimport type { UseBlocOptions, UseBlocReturn, ComponentRef } from './types';\nimport { generateInstanceKey } from './utils/instance-keys';\nimport { getBlacReactConfig } from './config';\n\ninterface TrackingMode {\n useManualDeps: boolean;\n autoTrackEnabled: boolean;\n}\n\nfunction determineTrackingMode<TBloc extends StateContainerConstructor>(\n options?: UseBlocOptions<TBloc>,\n): TrackingMode {\n const globalConfig = getBlacReactConfig();\n const autoTrackEnabled =\n options?.autoTrack !== undefined\n ? options.autoTrack\n : globalConfig.autoTrack;\n\n return {\n useManualDeps: options?.dependencies !== undefined,\n autoTrackEnabled,\n };\n}\n\n/**\n * React hook that connects a component to a state container with automatic re-render on state changes.\n *\n * Supports three tracking modes:\n * - **Auto-tracking** (default): Automatically detects accessed state properties via Proxy\n * - **Manual dependencies**: Explicit dependency array like useEffect\n * - **No tracking**: Returns full state without optimization\n *\n * @template T - The state container constructor type (inferred from BlocClass)\n * @param BlocClass - The state container class to connect to\n * @param options - Configuration options for tracking mode and instance management\n * @returns Tuple with [state, bloc instance, ref]\n *\n * @example Basic usage\n * ```ts\n * const [state, myBloc, ref] = useBloc(MyBloc);\n * ```\n *\n * @example With manual dependencies\n * ```ts\n * const [state, myBloc] = useBloc(MyBloc, {\n * dependencies: (state) => [state.count]\n * });\n * ```\n *\n * @example With isolated instance\n * ```ts\n * const [state, myBloc] = useBloc(MyBloc, {\n * instanceId: 'unique-id'\n * });\n * ```\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 componentRef = useRef<ComponentRef>({});\n const isIsolated = isIsolatedClass(BlocClass);\n\n const [bloc, subscribe, getSnapshot, instanceKey, adapterState, rawInstance] =\n useMemo<\n readonly [\n TBloc,\n (callback: () => void) => () => void,\n () => ExtractState<T>,\n string | undefined,\n AdapterState<T>,\n TBloc,\n ]\n >(() => {\n const instanceKey = generateInstanceKey(\n componentRef.current,\n isIsolated,\n options?.instanceId,\n );\n\n const instance = acquire(BlocClass, instanceKey) as TBloc;\n\n const { useManualDeps, autoTrackEnabled } =\n determineTrackingMode(options);\n\n let subscribeFn: (callback: () => void) => () => void;\n let getSnapshotFn: () => ExtractState<T>;\n let adapterState: AdapterState<T>;\n\n if (useManualDeps && options?.dependencies) {\n adapterState = manualDepsInit(instance);\n subscribeFn = manualDepsSubscribe(instance, adapterState, {\n dependencies: options.dependencies,\n });\n getSnapshotFn = manualDepsSnapshot(instance, adapterState, {\n dependencies: options.dependencies,\n });\n } else if (!autoTrackEnabled) {\n adapterState = noTrackInit(instance);\n subscribeFn = noTrackSubscribe(instance);\n getSnapshotFn = noTrackSnapshot(instance);\n } else {\n adapterState = autoTrackInit(instance);\n subscribeFn = autoTrackSubscribe(instance, adapterState);\n getSnapshotFn = autoTrackSnapshot(instance, adapterState);\n }\n\n return [\n adapterState.proxiedBloc as TBloc,\n subscribeFn,\n getSnapshotFn,\n instanceKey,\n adapterState,\n instance,\n ];\n }, [BlocClass, isIsolated, options?.instanceId]);\n\n const state = useSyncExternalStore(subscribe, getSnapshot);\n\n const [, forceUpdate] = useReducer((x: number) => x + 1, 0);\n\n const externalDepsManager = useRef(new ExternalDepsManager());\n\n useEffect(() => {\n disableGetterTracking(adapterState, rawInstance);\n externalDepsManager.current.updateSubscriptions(\n adapterState.getterState,\n rawInstance,\n forceUpdate,\n );\n });\n\n useEffect(() => {\n if (options?.onMount) {\n options.onMount(bloc as InstanceType<T>);\n }\n\n return () => {\n externalDepsManager.current.cleanup();\n\n if (options?.onUnmount) {\n options.onUnmount(bloc as InstanceType<T>);\n }\n\n release(BlocClass, instanceKey);\n\n if (isIsolated && !rawInstance.isDisposed) {\n rawInstance.dispose();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return [state, bloc, componentRef] as UseBlocReturn<T, ExtractState<T>>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,oBACd,cACA,YACA,YACoB;AAEpB,KAAI,eAAe,OACjB,QAAO,OAAO,eAAe,WAAW,OAAO,WAAW,GAAG;AAI/D,KAAI,YAAY;AACd,MAAI,CAAC,aAAa,iBAChB,cAAa,2DAAwC;AAEvD,SAAO,aAAa;;;;;;AC1BxB,MAAM,gBAAiC,EACrC,WAAW,MACZ;AAED,IAAI,eAAgC,EAAE,GAAG,eAAe;;;;;;;;;;;;;;;;AAiBxD,SAAgB,mBAAmB,QAAwC;AACzE,gBAAe;EAAE,GAAG;EAAc,GAAG;EAAQ;;;;;;AAO/C,SAAgB,qBAAsC;AACpD,QAAO;;;;;;AAOT,SAAgB,uBAA6B;AAC3C,gBAAe,EAAE,GAAG,eAAe;;;;;ACXrC,SAAS,sBACP,SACc;CACd,MAAM,eAAe,oBAAoB;CACzC,MAAM,mBACJ,SAAS,cAAc,SACnB,QAAQ,YACR,aAAa;AAEnB,QAAO;EACL,eAAe,SAAS,iBAAiB;EACzC;EACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCH,SAAgB,QAGd,WACA,SACmC;CAGnC,MAAM,iCAAoC,EAAE,CAAC;CAC7C,MAAM,gDAA6B,UAAU;CAE7C,MAAM,CAAC,MAAM,WAAW,aAAa,aAAa,cAAc,wCAUtD;EACN,MAAM,cAAc,oBAClB,aAAa,SACb,YACA,SAAS,WACV;EAED,MAAM,sCAAmB,WAAW,YAAY;EAEhD,MAAM,EAAE,eAAe,qBACrB,sBAAsB,QAAQ;EAEhC,IAAI;EACJ,IAAI;EACJ,IAAI;AAEJ,MAAI,iBAAiB,SAAS,cAAc;AAC1C,oDAA8B,SAAS;AACvC,wDAAkC,UAAU,cAAc,EACxD,cAAc,QAAQ,cACvB,CAAC;AACF,yDAAmC,UAAU,cAAc,EACzD,cAAc,QAAQ,cACvB,CAAC;aACO,CAAC,kBAAkB;AAC5B,iDAA2B,SAAS;AACpC,qDAA+B,SAAS;AACxC,sDAAgC,SAAS;SACpC;AACL,mDAA6B,SAAS;AACtC,uDAAiC,UAAU,aAAa;AACxD,wDAAkC,UAAU,aAAa;;AAG3D,SAAO;GACL,aAAa;GACb;GACA;GACA;GACA;GACA;GACD;IACA;EAAC;EAAW;EAAY,SAAS;EAAW,CAAC;CAElD,MAAM,wCAA6B,WAAW,YAAY;CAE1D,MAAM,GAAG,sCAA2B,MAAc,IAAI,GAAG,EAAE;CAE3D,MAAM,wCAA6B,IAAIA,mCAAqB,CAAC;AAE7D,4BAAgB;AACd,2CAAsB,cAAc,YAAY;AAChD,sBAAoB,QAAQ,oBAC1B,aAAa,aACb,aACA,YACD;GACD;AAEF,4BAAgB;AACd,MAAI,SAAS,QACX,SAAQ,QAAQ,KAAwB;AAG1C,eAAa;AACX,uBAAoB,QAAQ,SAAS;AAErC,OAAI,SAAS,UACX,SAAQ,UAAU,KAAwB;AAG5C,8BAAQ,WAAW,YAAY;AAE/B,OAAI,cAAc,CAAC,YAAY,WAC7B,aAAY,SAAS;;IAIxB,EAAE,CAAC;AAEN,QAAO;EAAC;EAAO;EAAM;EAAa"}
package/dist/index.d.cts CHANGED
@@ -1,9 +1,9 @@
1
- import type { ExtractProps } from '@blac/core';
2
- import { ExtractState } from '@blac/core';
1
+ import type { ExtractState } from '@blac/core';
2
+ import { ExtractState as ExtractState_2 } from '@blac/adapter';
3
3
  import { InstanceReadonlyState } from '@blac/core';
4
- import { IsStatelessContainer } from '@blac/core';
5
4
  import type { RefObject } from 'react';
6
- import { StateContainerConstructor } from '@blac/core';
5
+ import { StateContainerConstructor } from '@blac/adapter';
6
+ import type { StateContainerConstructor as StateContainerConstructor_2 } from '@blac/core';
7
7
 
8
8
  /**
9
9
  * Global configuration for @blac/react
@@ -34,12 +34,6 @@ export declare function configureBlacReact(config: Partial<BlacReactConfig>): vo
34
34
 
35
35
  /* Excluded from this release type: resetBlacReactConfig */
36
36
 
37
- /**
38
- * Type that produces a TypeScript error when a stateless container is used.
39
- * Evaluates to `never` for stateless containers, causing a type mismatch.
40
- */
41
- declare type StatefulContainer<T extends StateContainerConstructor> = IsStatelessContainer<T> extends true ? never : T;
42
-
43
37
  /**
44
38
  * React hook that connects a component to a state container with automatic re-render on state changes.
45
39
  *
@@ -48,11 +42,8 @@ declare type StatefulContainer<T extends StateContainerConstructor> = IsStateles
48
42
  * - **Manual dependencies**: Explicit dependency array like useEffect
49
43
  * - **No tracking**: Returns full state without optimization
50
44
  *
51
- * **Note:** This hook does NOT support stateless containers (StatelessCubit, StatelessVertex).
52
- * Use `useBlocActions()` instead for stateless containers.
53
- *
54
45
  * @template T - The state container constructor type (inferred from BlocClass)
55
- * @param BlocClass - The state container class to connect to (must not be stateless)
46
+ * @param BlocClass - The state container class to connect to
56
47
  * @param options - Configuration options for tracking mode and instance management
57
48
  * @returns Tuple with [state, bloc instance, ref]
58
49
  *
@@ -75,69 +66,13 @@ declare type StatefulContainer<T extends StateContainerConstructor> = IsStateles
75
66
  * });
76
67
  * ```
77
68
  */
78
- export declare function useBloc<T extends StateContainerConstructor = StateContainerConstructor>(BlocClass: StatefulContainer<T>, options?: UseBlocOptions<T>): UseBlocReturn<T, ExtractState<T>>;
79
-
80
- /**
81
- * React hook that connects to a state container instance without triggering re-renders.
82
- * Use this when you only need to call actions on the bloc without subscribing to state changes.
83
- *
84
- * **This is the recommended hook for stateless containers** (StatelessCubit, StatelessVertex).
85
- * It also works with regular Cubit/Vertex when you don't need state subscriptions.
86
- *
87
- * @template T - The state container constructor type (inferred from BlocClass)
88
- * @param BlocClass - The state container class to connect to (supports both stateful and stateless)
89
- * @param options - Configuration options for instance management and lifecycle
90
- * @returns The state container instance for calling actions
91
- *
92
- * @example Basic usage with stateless container
93
- * ```ts
94
- * class AnalyticsService extends StatelessCubit {
95
- * trackEvent(name: string) { ... }
96
- * }
97
- *
98
- * const analytics = useBlocActions(AnalyticsService);
99
- * analytics.trackEvent('page_view');
100
- * ```
101
- *
102
- * @example With stateful container (no re-renders)
103
- * ```ts
104
- * const myBloc = useBlocActions(MyBloc);
105
- * myBloc.someMethod(); // Won't cause re-renders
106
- * ```
107
- *
108
- * @example With isolated instance
109
- * ```ts
110
- * const myBloc = useBlocActions(MyBloc, {
111
- * instanceId: 'unique-id'
112
- * });
113
- * ```
114
- */
115
- export declare function useBlocActions<T extends StateContainerConstructor = StateContainerConstructor>(BlocClass: T, options?: UseBlocActionsOptions<InstanceType<T>>): InstanceType<T>;
116
-
117
- /**
118
- * Configuration options for useBlocActions hook
119
- * @template TBloc - The state container type
120
- * @template TProps - Props type passed to the container
121
- */
122
- export declare interface UseBlocActionsOptions<TBloc, TProps = any> {
123
- /** Props passed to bloc constructor or updateProps */
124
- props?: TProps;
125
- /** Custom instance identifier for shared or isolated instances */
126
- instanceId?: string | number;
127
- /** Callback invoked when bloc instance mounts */
128
- onMount?: (bloc: TBloc) => void;
129
- /** Callback invoked when bloc instance unmounts */
130
- onUnmount?: (bloc: TBloc) => void;
131
- }
69
+ export declare function useBloc<T extends StateContainerConstructor = StateContainerConstructor>(BlocClass: T, options?: UseBlocOptions<T>): UseBlocReturn<T, ExtractState_2<T>>;
132
70
 
133
71
  /**
134
72
  * Configuration options for useBloc hook
135
73
  * @template TBloc - The state container type
136
- * @template TProps - Props type passed to the container
137
74
  */
138
- export declare interface UseBlocOptions<TBloc extends StateContainerConstructor, TProps = ExtractProps<TBloc>> {
139
- /** Props passed to bloc constructor or updateProps */
140
- props?: TProps;
75
+ export declare interface UseBlocOptions<TBloc extends StateContainerConstructor_2> {
141
76
  /** Custom instance identifier for shared or isolated instances */
142
77
  instanceId?: string | number;
143
78
  /** Manual dependency array like useEffect (disables autoTrack) */
@@ -160,6 +95,6 @@ export declare interface UseBlocOptions<TBloc extends StateContainerConstructor,
160
95
  *
161
96
  * @template TBloc - The state container type
162
97
  */
163
- export declare type UseBlocReturn<TBloc extends StateContainerConstructor, S = ExtractState<TBloc>> = [S, InstanceReadonlyState<TBloc>, RefObject<ComponentRef>];
98
+ export declare type UseBlocReturn<TBloc extends StateContainerConstructor_2, S = ExtractState<TBloc>> = [S, InstanceReadonlyState<TBloc>, RefObject<ComponentRef>];
164
99
 
165
100
  export { }
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import type { ExtractProps } from '@blac/core';
2
- import { ExtractState } from '@blac/core';
1
+ import type { ExtractState } from '@blac/core';
2
+ import { ExtractState as ExtractState_2 } from '@blac/adapter';
3
3
  import { InstanceReadonlyState } from '@blac/core';
4
- import { IsStatelessContainer } from '@blac/core';
5
4
  import type { RefObject } from 'react';
6
- import { StateContainerConstructor } from '@blac/core';
5
+ import { StateContainerConstructor } from '@blac/adapter';
6
+ import type { StateContainerConstructor as StateContainerConstructor_2 } from '@blac/core';
7
7
 
8
8
  /**
9
9
  * Global configuration for @blac/react
@@ -34,12 +34,6 @@ export declare function configureBlacReact(config: Partial<BlacReactConfig>): vo
34
34
 
35
35
  /* Excluded from this release type: resetBlacReactConfig */
36
36
 
37
- /**
38
- * Type that produces a TypeScript error when a stateless container is used.
39
- * Evaluates to `never` for stateless containers, causing a type mismatch.
40
- */
41
- declare type StatefulContainer<T extends StateContainerConstructor> = IsStatelessContainer<T> extends true ? never : T;
42
-
43
37
  /**
44
38
  * React hook that connects a component to a state container with automatic re-render on state changes.
45
39
  *
@@ -48,11 +42,8 @@ declare type StatefulContainer<T extends StateContainerConstructor> = IsStateles
48
42
  * - **Manual dependencies**: Explicit dependency array like useEffect
49
43
  * - **No tracking**: Returns full state without optimization
50
44
  *
51
- * **Note:** This hook does NOT support stateless containers (StatelessCubit, StatelessVertex).
52
- * Use `useBlocActions()` instead for stateless containers.
53
- *
54
45
  * @template T - The state container constructor type (inferred from BlocClass)
55
- * @param BlocClass - The state container class to connect to (must not be stateless)
46
+ * @param BlocClass - The state container class to connect to
56
47
  * @param options - Configuration options for tracking mode and instance management
57
48
  * @returns Tuple with [state, bloc instance, ref]
58
49
  *
@@ -75,69 +66,13 @@ declare type StatefulContainer<T extends StateContainerConstructor> = IsStateles
75
66
  * });
76
67
  * ```
77
68
  */
78
- export declare function useBloc<T extends StateContainerConstructor = StateContainerConstructor>(BlocClass: StatefulContainer<T>, options?: UseBlocOptions<T>): UseBlocReturn<T, ExtractState<T>>;
79
-
80
- /**
81
- * React hook that connects to a state container instance without triggering re-renders.
82
- * Use this when you only need to call actions on the bloc without subscribing to state changes.
83
- *
84
- * **This is the recommended hook for stateless containers** (StatelessCubit, StatelessVertex).
85
- * It also works with regular Cubit/Vertex when you don't need state subscriptions.
86
- *
87
- * @template T - The state container constructor type (inferred from BlocClass)
88
- * @param BlocClass - The state container class to connect to (supports both stateful and stateless)
89
- * @param options - Configuration options for instance management and lifecycle
90
- * @returns The state container instance for calling actions
91
- *
92
- * @example Basic usage with stateless container
93
- * ```ts
94
- * class AnalyticsService extends StatelessCubit {
95
- * trackEvent(name: string) { ... }
96
- * }
97
- *
98
- * const analytics = useBlocActions(AnalyticsService);
99
- * analytics.trackEvent('page_view');
100
- * ```
101
- *
102
- * @example With stateful container (no re-renders)
103
- * ```ts
104
- * const myBloc = useBlocActions(MyBloc);
105
- * myBloc.someMethod(); // Won't cause re-renders
106
- * ```
107
- *
108
- * @example With isolated instance
109
- * ```ts
110
- * const myBloc = useBlocActions(MyBloc, {
111
- * instanceId: 'unique-id'
112
- * });
113
- * ```
114
- */
115
- export declare function useBlocActions<T extends StateContainerConstructor = StateContainerConstructor>(BlocClass: T, options?: UseBlocActionsOptions<InstanceType<T>>): InstanceType<T>;
116
-
117
- /**
118
- * Configuration options for useBlocActions hook
119
- * @template TBloc - The state container type
120
- * @template TProps - Props type passed to the container
121
- */
122
- export declare interface UseBlocActionsOptions<TBloc, TProps = any> {
123
- /** Props passed to bloc constructor or updateProps */
124
- props?: TProps;
125
- /** Custom instance identifier for shared or isolated instances */
126
- instanceId?: string | number;
127
- /** Callback invoked when bloc instance mounts */
128
- onMount?: (bloc: TBloc) => void;
129
- /** Callback invoked when bloc instance unmounts */
130
- onUnmount?: (bloc: TBloc) => void;
131
- }
69
+ export declare function useBloc<T extends StateContainerConstructor = StateContainerConstructor>(BlocClass: T, options?: UseBlocOptions<T>): UseBlocReturn<T, ExtractState_2<T>>;
132
70
 
133
71
  /**
134
72
  * Configuration options for useBloc hook
135
73
  * @template TBloc - The state container type
136
- * @template TProps - Props type passed to the container
137
74
  */
138
- export declare interface UseBlocOptions<TBloc extends StateContainerConstructor, TProps = ExtractProps<TBloc>> {
139
- /** Props passed to bloc constructor or updateProps */
140
- props?: TProps;
75
+ export declare interface UseBlocOptions<TBloc extends StateContainerConstructor_2> {
141
76
  /** Custom instance identifier for shared or isolated instances */
142
77
  instanceId?: string | number;
143
78
  /** Manual dependency array like useEffect (disables autoTrack) */
@@ -160,6 +95,6 @@ export declare interface UseBlocOptions<TBloc extends StateContainerConstructor,
160
95
  *
161
96
  * @template TBloc - The state container type
162
97
  */
163
- export declare type UseBlocReturn<TBloc extends StateContainerConstructor, S = ExtractState<TBloc>> = [S, InstanceReadonlyState<TBloc>, RefObject<ComponentRef>];
98
+ export declare type UseBlocReturn<TBloc extends StateContainerConstructor_2, S = ExtractState<TBloc>> = [S, InstanceReadonlyState<TBloc>, RefObject<ComponentRef>];
164
99
 
165
100
  export { }
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useMemo, useReducer, useRef, useSyncExternalStore } from "react";
2
- import { ExternalDepsManager, acquire, autoTrackInit, autoTrackSnapshot, autoTrackSubscribe, disableGetterTracking, generateIsolatedKey, isIsolatedClass, isStatelessClass, manualDepsInit, manualDepsSnapshot, manualDepsSubscribe, noTrackInit, noTrackSnapshot, noTrackSubscribe, release } from "@blac/core";
2
+ import { ExternalDepsManager, acquire, autoTrackInit, autoTrackSnapshot, autoTrackSubscribe, disableGetterTracking, generateIsolatedKey, isIsolatedClass, manualDepsInit, manualDepsSnapshot, manualDepsSubscribe, noTrackInit, noTrackSnapshot, noTrackSubscribe, release } from "@blac/adapter";
3
3
 
4
4
  //#region src/utils/instance-keys.ts
5
5
  /**
@@ -69,8 +69,8 @@ function resetBlacReactConfig() {
69
69
  //#endregion
70
70
  //#region src/useBloc.ts
71
71
  function determineTrackingMode(options) {
72
- const globalConfig$1 = getBlacReactConfig();
73
- const autoTrackEnabled = options?.autoTrack !== void 0 ? options.autoTrack : globalConfig$1.autoTrack;
72
+ const globalConfig = getBlacReactConfig();
73
+ const autoTrackEnabled = options?.autoTrack !== void 0 ? options.autoTrack : globalConfig.autoTrack;
74
74
  return {
75
75
  useManualDeps: options?.dependencies !== void 0,
76
76
  autoTrackEnabled
@@ -84,11 +84,8 @@ function determineTrackingMode(options) {
84
84
  * - **Manual dependencies**: Explicit dependency array like useEffect
85
85
  * - **No tracking**: Returns full state without optimization
86
86
  *
87
- * **Note:** This hook does NOT support stateless containers (StatelessCubit, StatelessVertex).
88
- * Use `useBlocActions()` instead for stateless containers.
89
- *
90
87
  * @template T - The state container constructor type (inferred from BlocClass)
91
- * @param BlocClass - The state container class to connect to (must not be stateless)
88
+ * @param BlocClass - The state container class to connect to
92
89
  * @param options - Configuration options for tracking mode and instance management
93
90
  * @returns Tuple with [state, bloc instance, ref]
94
91
  *
@@ -113,53 +110,37 @@ function determineTrackingMode(options) {
113
110
  */
114
111
  function useBloc(BlocClass, options) {
115
112
  const componentRef = useRef({});
116
- const initialPropsRef = useRef(options?.props);
117
- const isStateless = isStatelessClass(BlocClass);
118
113
  const isIsolated = isIsolatedClass(BlocClass);
119
114
  const [bloc, subscribe, getSnapshot, instanceKey, adapterState, rawInstance] = useMemo(() => {
120
- if (isStateless) {
121
- console.error(`[BlaC] useBloc() does not support stateless containers. "${BlocClass.name}" is a StatelessCubit or StatelessVertex. Use useBlocActions() instead for stateless containers.`);
122
- const instance$1 = acquire(BlocClass, "default");
123
- return [
124
- instance$1,
125
- () => () => {},
126
- () => ({}),
127
- void 0,
128
- null,
129
- instance$1
130
- ];
131
- }
132
- const instanceKey$1 = generateInstanceKey(componentRef.current, isIsolated, options?.instanceId);
133
- const instance = acquire(BlocClass, instanceKey$1, { props: initialPropsRef.current });
134
- if (initialPropsRef.current !== void 0) instance.updateProps(initialPropsRef.current);
115
+ const instanceKey = generateInstanceKey(componentRef.current, isIsolated, options?.instanceId);
116
+ const instance = acquire(BlocClass, instanceKey);
135
117
  const { useManualDeps, autoTrackEnabled } = determineTrackingMode(options);
136
118
  let subscribeFn;
137
119
  let getSnapshotFn;
138
- let adapterState$1;
120
+ let adapterState;
139
121
  if (useManualDeps && options?.dependencies) {
140
- adapterState$1 = manualDepsInit(instance);
141
- subscribeFn = manualDepsSubscribe(instance, adapterState$1, { dependencies: options.dependencies });
142
- getSnapshotFn = manualDepsSnapshot(instance, adapterState$1, { dependencies: options.dependencies });
122
+ adapterState = manualDepsInit(instance);
123
+ subscribeFn = manualDepsSubscribe(instance, adapterState, { dependencies: options.dependencies });
124
+ getSnapshotFn = manualDepsSnapshot(instance, adapterState, { dependencies: options.dependencies });
143
125
  } else if (!autoTrackEnabled) {
144
- adapterState$1 = noTrackInit(instance);
126
+ adapterState = noTrackInit(instance);
145
127
  subscribeFn = noTrackSubscribe(instance);
146
128
  getSnapshotFn = noTrackSnapshot(instance);
147
129
  } else {
148
- adapterState$1 = autoTrackInit(instance);
149
- subscribeFn = autoTrackSubscribe(instance, adapterState$1);
150
- getSnapshotFn = autoTrackSnapshot(instance, adapterState$1);
130
+ adapterState = autoTrackInit(instance);
131
+ subscribeFn = autoTrackSubscribe(instance, adapterState);
132
+ getSnapshotFn = autoTrackSnapshot(instance, adapterState);
151
133
  }
152
134
  return [
153
- adapterState$1.proxiedBloc,
135
+ adapterState.proxiedBloc,
154
136
  subscribeFn,
155
137
  getSnapshotFn,
156
- instanceKey$1,
157
- adapterState$1,
138
+ instanceKey,
139
+ adapterState,
158
140
  instance
159
141
  ];
160
142
  }, [
161
143
  BlocClass,
162
- isStateless,
163
144
  isIsolated,
164
145
  options?.instanceId
165
146
  ]);
@@ -167,20 +148,10 @@ function useBloc(BlocClass, options) {
167
148
  const [, forceUpdate] = useReducer((x) => x + 1, 0);
168
149
  const externalDepsManager = useRef(new ExternalDepsManager());
169
150
  useEffect(() => {
170
- if (isStateless) return;
171
- if (options?.props !== initialPropsRef.current) rawInstance.updateProps(options?.props);
172
- }, [
173
- options?.props,
174
- rawInstance,
175
- isStateless
176
- ]);
177
- useEffect(() => {
178
- if (isStateless || !adapterState) return;
179
151
  disableGetterTracking(adapterState, rawInstance);
180
152
  externalDepsManager.current.updateSubscriptions(adapterState.getterState, rawInstance, forceUpdate);
181
153
  });
182
154
  useEffect(() => {
183
- if (isStateless) return;
184
155
  if (options?.onMount) options.onMount(bloc);
185
156
  return () => {
186
157
  externalDepsManager.current.cleanup();
@@ -197,61 +168,5 @@ function useBloc(BlocClass, options) {
197
168
  }
198
169
 
199
170
  //#endregion
200
- //#region src/useBlocActions.ts
201
- /**
202
- * React hook that connects to a state container instance without triggering re-renders.
203
- * Use this when you only need to call actions on the bloc without subscribing to state changes.
204
- *
205
- * **This is the recommended hook for stateless containers** (StatelessCubit, StatelessVertex).
206
- * It also works with regular Cubit/Vertex when you don't need state subscriptions.
207
- *
208
- * @template T - The state container constructor type (inferred from BlocClass)
209
- * @param BlocClass - The state container class to connect to (supports both stateful and stateless)
210
- * @param options - Configuration options for instance management and lifecycle
211
- * @returns The state container instance for calling actions
212
- *
213
- * @example Basic usage with stateless container
214
- * ```ts
215
- * class AnalyticsService extends StatelessCubit {
216
- * trackEvent(name: string) { ... }
217
- * }
218
- *
219
- * const analytics = useBlocActions(AnalyticsService);
220
- * analytics.trackEvent('page_view');
221
- * ```
222
- *
223
- * @example With stateful container (no re-renders)
224
- * ```ts
225
- * const myBloc = useBlocActions(MyBloc);
226
- * myBloc.someMethod(); // Won't cause re-renders
227
- * ```
228
- *
229
- * @example With isolated instance
230
- * ```ts
231
- * const myBloc = useBlocActions(MyBloc, {
232
- * instanceId: 'unique-id'
233
- * });
234
- * ```
235
- */
236
- function useBlocActions(BlocClass, options) {
237
- const componentRef = useRef({});
238
- const initialPropsRef = useRef(options?.props);
239
- const [bloc, instanceKey] = useMemo(() => {
240
- const isIsolated = isIsolatedClass(BlocClass);
241
- const instanceKey$1 = generateInstanceKey(componentRef.current, isIsolated, options?.instanceId);
242
- return [acquire(BlocClass, instanceKey$1, { props: initialPropsRef.current }), instanceKey$1];
243
- }, [BlocClass]);
244
- useEffect(() => {
245
- if (options?.onMount) options.onMount(bloc);
246
- return () => {
247
- if (options?.onUnmount) options.onUnmount(bloc);
248
- release(BlocClass, instanceKey);
249
- if (isIsolatedClass(BlocClass) && !bloc.isDisposed) bloc.dispose();
250
- };
251
- }, []);
252
- return bloc;
253
- }
254
-
255
- //#endregion
256
- export { configureBlacReact, resetBlacReactConfig, useBloc, useBlocActions };
171
+ export { configureBlacReact, resetBlacReactConfig, useBloc };
257
172
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["globalConfig","instance","instanceKey","adapterState","instanceKey"],"sources":["../src/utils/instance-keys.ts","../src/config.ts","../src/useBloc.ts","../src/useBlocActions.ts"],"sourcesContent":["/**\n * Instance key generation utilities for React integration\n */\n\nimport { generateIsolatedKey } from '@blac/core';\nimport type { ComponentRef } from '../types';\n\n/**\n * Generate an instance key for a bloc\n *\n * Logic:\n * - If user provides instanceId, use it (convert number to string)\n * - If isolated, generate or reuse a unique key for this component\n * - Otherwise, return undefined (use default key)\n *\n * @param componentRef - React component reference (persists across remounts)\n * @param isIsolated - Whether the bloc is isolated\n * @param providedId - User-provided instance ID (from options)\n * @returns Instance key string or undefined for default\n */\nexport function generateInstanceKey(\n componentRef: ComponentRef,\n isIsolated: boolean,\n providedId?: string | number,\n): string | undefined {\n // User explicitly provided an ID - use it\n if (providedId !== undefined) {\n return typeof providedId === 'number' ? String(providedId) : providedId;\n }\n\n // Isolated bloc - generate unique key per component\n if (isIsolated) {\n if (!componentRef.__blocInstanceId) {\n componentRef.__blocInstanceId = generateIsolatedKey();\n }\n return componentRef.__blocInstanceId;\n }\n\n // Shared bloc - use default key (undefined)\n return undefined;\n}\n","/**\n * Global configuration for @blac/react\n */\n\nexport interface BlacReactConfig {\n /** Enable automatic property tracking via Proxy (default: true) */\n autoTrack: boolean;\n}\n\nconst defaultConfig: BlacReactConfig = {\n autoTrack: true,\n};\n\nlet globalConfig: BlacReactConfig = { ...defaultConfig };\n\n/**\n * Configure global defaults for @blac/react hooks.\n *\n * @example\n * ```ts\n * import { configureBlacReact } from '@blac/react';\n *\n * // Disable auto-tracking globally\n * configureBlacReact({\n * autoTrack: false\n * });\n * ```\n *\n * @param config - Partial configuration to merge with defaults\n */\nexport function configureBlacReact(config: Partial<BlacReactConfig>): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\n/**\n * Get the current global configuration.\n * @internal\n */\nexport function getBlacReactConfig(): BlacReactConfig {\n return globalConfig;\n}\n\n/**\n * Reset configuration to defaults (useful for testing).\n * @internal\n */\nexport function resetBlacReactConfig(): void {\n globalConfig = { ...defaultConfig };\n}\n","import {\n useMemo,\n useSyncExternalStore,\n useEffect,\n useRef,\n useReducer,\n} from 'react';\nimport {\n type ExtractState,\n type AdapterState,\n type IsStatelessContainer,\n ExternalDepsManager,\n autoTrackSubscribe,\n manualDepsSubscribe,\n noTrackSubscribe,\n autoTrackSnapshot,\n manualDepsSnapshot,\n noTrackSnapshot,\n autoTrackInit,\n manualDepsInit,\n noTrackInit,\n disableGetterTracking,\n isIsolatedClass,\n isStatelessClass,\n StateContainerConstructor,\n InstanceState,\n acquire,\n release,\n} from '@blac/core';\nimport type { UseBlocOptions, UseBlocReturn, ComponentRef } from './types';\nimport { generateInstanceKey } from './utils/instance-keys';\nimport { getBlacReactConfig } from './config';\n\ninterface TrackingMode {\n useManualDeps: boolean;\n autoTrackEnabled: boolean;\n}\n\nfunction determineTrackingMode<TBloc extends StateContainerConstructor>(\n options?: UseBlocOptions<TBloc>,\n): TrackingMode {\n const globalConfig = getBlacReactConfig();\n const autoTrackEnabled =\n options?.autoTrack !== undefined\n ? options.autoTrack\n : globalConfig.autoTrack;\n\n return {\n useManualDeps: options?.dependencies !== undefined,\n autoTrackEnabled,\n };\n}\n\n/**\n * Type that produces a TypeScript error when a stateless container is used.\n * Evaluates to `never` for stateless containers, causing a type mismatch.\n */\ntype StatefulContainer<T extends StateContainerConstructor> =\n IsStatelessContainer<T> extends true ? never : T;\n\n/**\n * React hook that connects a component to a state container with automatic re-render on state changes.\n *\n * Supports three tracking modes:\n * - **Auto-tracking** (default): Automatically detects accessed state properties via Proxy\n * - **Manual dependencies**: Explicit dependency array like useEffect\n * - **No tracking**: Returns full state without optimization\n *\n * **Note:** This hook does NOT support stateless containers (StatelessCubit, StatelessVertex).\n * Use `useBlocActions()` instead for stateless containers.\n *\n * @template T - The state container constructor type (inferred from BlocClass)\n * @param BlocClass - The state container class to connect to (must not be stateless)\n * @param options - Configuration options for tracking mode and instance management\n * @returns Tuple with [state, bloc instance, ref]\n *\n * @example Basic usage\n * ```ts\n * const [state, myBloc, ref] = useBloc(MyBloc);\n * ```\n *\n * @example With manual dependencies\n * ```ts\n * const [state, myBloc] = useBloc(MyBloc, {\n * dependencies: (state) => [state.count]\n * });\n * ```\n *\n * @example With isolated instance\n * ```ts\n * const [state, myBloc] = useBloc(MyBloc, {\n * instanceId: 'unique-id'\n * });\n * ```\n */\nexport function useBloc<\n T extends StateContainerConstructor = StateContainerConstructor,\n>(\n BlocClass: StatefulContainer<T>,\n options?: UseBlocOptions<T>,\n): UseBlocReturn<T, ExtractState<T>> {\n type TBloc = InstanceState<T>;\n\n const componentRef = useRef<ComponentRef>({});\n const initialPropsRef = useRef(options?.props);\n const isStateless = isStatelessClass(BlocClass);\n const isIsolated = isIsolatedClass(BlocClass);\n\n const [bloc, subscribe, getSnapshot, instanceKey, adapterState, rawInstance] =\n useMemo<\n readonly [\n TBloc,\n (callback: () => void) => () => void,\n () => ExtractState<T>,\n string | undefined,\n AdapterState<T> | null,\n TBloc,\n ]\n >(() => {\n // Runtime check for stateless containers - log error but don't crash\n if (isStateless) {\n console.error(\n `[BlaC] useBloc() does not support stateless containers. ` +\n `\"${BlocClass.name}\" is a StatelessCubit or StatelessVertex. ` +\n `Use useBlocActions() instead for stateless containers.`,\n );\n const instance = acquire(BlocClass, 'default') as TBloc;\n // Return minimal working functions that won't subscribe to anything\n return [\n instance,\n () => () => {},\n () => ({}) as ExtractState<T>,\n undefined,\n null,\n instance,\n ];\n }\n\n const instanceKey = generateInstanceKey(\n componentRef.current,\n isIsolated,\n options?.instanceId,\n );\n\n const instance = acquire(BlocClass, instanceKey, {\n props: initialPropsRef.current,\n }) as TBloc;\n\n if (initialPropsRef.current !== undefined) {\n instance.updateProps(initialPropsRef.current);\n }\n\n const { useManualDeps, autoTrackEnabled } =\n determineTrackingMode(options);\n\n let subscribeFn: (callback: () => void) => () => void;\n let getSnapshotFn: () => ExtractState<T>;\n let adapterState: AdapterState<T>;\n\n if (useManualDeps && options?.dependencies) {\n adapterState = manualDepsInit(instance);\n subscribeFn = manualDepsSubscribe(instance, adapterState, {\n dependencies: options.dependencies,\n });\n getSnapshotFn = manualDepsSnapshot(instance, adapterState, {\n dependencies: options.dependencies,\n });\n } else if (!autoTrackEnabled) {\n adapterState = noTrackInit(instance);\n subscribeFn = noTrackSubscribe(instance);\n getSnapshotFn = noTrackSnapshot(instance);\n } else {\n adapterState = autoTrackInit(instance);\n subscribeFn = autoTrackSubscribe(instance, adapterState);\n getSnapshotFn = autoTrackSnapshot(instance, adapterState);\n }\n\n return [\n adapterState.proxiedBloc as TBloc,\n subscribeFn,\n getSnapshotFn,\n instanceKey,\n adapterState,\n instance,\n ];\n }, [BlocClass, isStateless, isIsolated, options?.instanceId]);\n\n const state = useSyncExternalStore(subscribe, getSnapshot);\n\n const [, forceUpdate] = useReducer((x: number) => x + 1, 0);\n\n const externalDepsManager = useRef(new ExternalDepsManager());\n\n useEffect(() => {\n if (isStateless) return;\n if (options?.props !== initialPropsRef.current) {\n rawInstance.updateProps(options?.props);\n }\n }, [options?.props, rawInstance, isStateless]);\n\n useEffect(() => {\n if (isStateless || !adapterState) return;\n disableGetterTracking(adapterState, rawInstance);\n externalDepsManager.current.updateSubscriptions(\n adapterState.getterState,\n rawInstance,\n forceUpdate,\n );\n });\n\n useEffect(() => {\n if (isStateless) return;\n\n if (options?.onMount) {\n options.onMount(bloc as InstanceType<T>);\n }\n\n return () => {\n externalDepsManager.current.cleanup();\n\n if (options?.onUnmount) {\n options.onUnmount(bloc as InstanceType<T>);\n }\n\n release(BlocClass, instanceKey);\n\n if (isIsolated && !rawInstance.isDisposed) {\n rawInstance.dispose();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return [state, bloc, componentRef] as UseBlocReturn<T, ExtractState<T>>;\n}\n","import { useMemo, useEffect, useRef } from 'react';\nimport {\n StateContainerConstructor,\n isIsolatedClass,\n acquire,\n release,\n} from '@blac/core';\nimport type { ComponentRef } from './types';\nimport { generateInstanceKey } from './utils/instance-keys';\n\n/**\n * Configuration options for useBlocActions hook\n * @template TBloc - The state container type\n * @template TProps - Props type passed to the container\n */\nexport interface UseBlocActionsOptions<TBloc, TProps = any> {\n /** Props passed to bloc constructor or updateProps */\n props?: TProps;\n /** Custom instance identifier for shared or isolated instances */\n instanceId?: string | number;\n /** Callback invoked when bloc instance mounts */\n onMount?: (bloc: TBloc) => void;\n /** Callback invoked when bloc instance unmounts */\n onUnmount?: (bloc: TBloc) => void;\n}\n\n/**\n * React hook that connects to a state container instance without triggering re-renders.\n * Use this when you only need to call actions on the bloc without subscribing to state changes.\n *\n * **This is the recommended hook for stateless containers** (StatelessCubit, StatelessVertex).\n * It also works with regular Cubit/Vertex when you don't need state subscriptions.\n *\n * @template T - The state container constructor type (inferred from BlocClass)\n * @param BlocClass - The state container class to connect to (supports both stateful and stateless)\n * @param options - Configuration options for instance management and lifecycle\n * @returns The state container instance for calling actions\n *\n * @example Basic usage with stateless container\n * ```ts\n * class AnalyticsService extends StatelessCubit {\n * trackEvent(name: string) { ... }\n * }\n *\n * const analytics = useBlocActions(AnalyticsService);\n * analytics.trackEvent('page_view');\n * ```\n *\n * @example With stateful container (no re-renders)\n * ```ts\n * const myBloc = useBlocActions(MyBloc);\n * myBloc.someMethod(); // Won't cause re-renders\n * ```\n *\n * @example With isolated instance\n * ```ts\n * const myBloc = useBlocActions(MyBloc, {\n * instanceId: 'unique-id'\n * });\n * ```\n */\nexport function useBlocActions<\n T extends StateContainerConstructor = StateContainerConstructor,\n>(\n BlocClass: T,\n options?: UseBlocActionsOptions<InstanceType<T>>,\n): InstanceType<T> {\n const componentRef = useRef<ComponentRef>({});\n const initialPropsRef = useRef(options?.props);\n\n const [bloc, instanceKey] = useMemo(() => {\n const isIsolated = isIsolatedClass(BlocClass);\n\n const instanceKey = generateInstanceKey(\n componentRef.current,\n isIsolated,\n options?.instanceId,\n );\n\n const instance = acquire(BlocClass, instanceKey, {\n props: initialPropsRef.current,\n });\n\n return [instance, instanceKey] as const;\n }, [BlocClass]);\n\n useEffect(() => {\n if (options?.onMount) {\n options.onMount(bloc);\n }\n\n return () => {\n if (options?.onUnmount) {\n options.onUnmount(bloc);\n }\n\n release(BlocClass, instanceKey);\n\n if (isIsolatedClass(BlocClass) && !bloc.isDisposed) {\n bloc.dispose();\n }\n };\n }, []);\n\n return bloc as InstanceType<T>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/utils/instance-keys.ts","../src/config.ts","../src/useBloc.ts"],"sourcesContent":["/**\n * Instance key generation utilities for React integration\n */\n\nimport { generateIsolatedKey } from '@blac/adapter';\nimport type { ComponentRef } from '../types';\n\n/**\n * Generate an instance key for a bloc\n *\n * Logic:\n * - If user provides instanceId, use it (convert number to string)\n * - If isolated, generate or reuse a unique key for this component\n * - Otherwise, return undefined (use default key)\n *\n * @param componentRef - React component reference (persists across remounts)\n * @param isIsolated - Whether the bloc is isolated\n * @param providedId - User-provided instance ID (from options)\n * @returns Instance key string or undefined for default\n */\nexport function generateInstanceKey(\n componentRef: ComponentRef,\n isIsolated: boolean,\n providedId?: string | number,\n): string | undefined {\n // User explicitly provided an ID - use it\n if (providedId !== undefined) {\n return typeof providedId === 'number' ? String(providedId) : providedId;\n }\n\n // Isolated bloc - generate unique key per component\n if (isIsolated) {\n if (!componentRef.__blocInstanceId) {\n componentRef.__blocInstanceId = generateIsolatedKey();\n }\n return componentRef.__blocInstanceId;\n }\n\n // Shared bloc - use default key (undefined)\n return undefined;\n}\n","/**\n * Global configuration for @blac/react\n */\n\nexport interface BlacReactConfig {\n /** Enable automatic property tracking via Proxy (default: true) */\n autoTrack: boolean;\n}\n\nconst defaultConfig: BlacReactConfig = {\n autoTrack: true,\n};\n\nlet globalConfig: BlacReactConfig = { ...defaultConfig };\n\n/**\n * Configure global defaults for @blac/react hooks.\n *\n * @example\n * ```ts\n * import { configureBlacReact } from '@blac/react';\n *\n * // Disable auto-tracking globally\n * configureBlacReact({\n * autoTrack: false\n * });\n * ```\n *\n * @param config - Partial configuration to merge with defaults\n */\nexport function configureBlacReact(config: Partial<BlacReactConfig>): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\n/**\n * Get the current global configuration.\n * @internal\n */\nexport function getBlacReactConfig(): BlacReactConfig {\n return globalConfig;\n}\n\n/**\n * Reset configuration to defaults (useful for testing).\n * @internal\n */\nexport function resetBlacReactConfig(): void {\n globalConfig = { ...defaultConfig };\n}\n","import {\n useMemo,\n useSyncExternalStore,\n useEffect,\n useRef,\n useReducer,\n} from 'react';\nimport {\n type ExtractState,\n type AdapterState,\n ExternalDepsManager,\n autoTrackSubscribe,\n manualDepsSubscribe,\n noTrackSubscribe,\n autoTrackSnapshot,\n manualDepsSnapshot,\n noTrackSnapshot,\n autoTrackInit,\n manualDepsInit,\n noTrackInit,\n disableGetterTracking,\n isIsolatedClass,\n type StateContainerConstructor,\n type InstanceState,\n acquire,\n release,\n} from '@blac/adapter';\nimport type { UseBlocOptions, UseBlocReturn, ComponentRef } from './types';\nimport { generateInstanceKey } from './utils/instance-keys';\nimport { getBlacReactConfig } from './config';\n\ninterface TrackingMode {\n useManualDeps: boolean;\n autoTrackEnabled: boolean;\n}\n\nfunction determineTrackingMode<TBloc extends StateContainerConstructor>(\n options?: UseBlocOptions<TBloc>,\n): TrackingMode {\n const globalConfig = getBlacReactConfig();\n const autoTrackEnabled =\n options?.autoTrack !== undefined\n ? options.autoTrack\n : globalConfig.autoTrack;\n\n return {\n useManualDeps: options?.dependencies !== undefined,\n autoTrackEnabled,\n };\n}\n\n/**\n * React hook that connects a component to a state container with automatic re-render on state changes.\n *\n * Supports three tracking modes:\n * - **Auto-tracking** (default): Automatically detects accessed state properties via Proxy\n * - **Manual dependencies**: Explicit dependency array like useEffect\n * - **No tracking**: Returns full state without optimization\n *\n * @template T - The state container constructor type (inferred from BlocClass)\n * @param BlocClass - The state container class to connect to\n * @param options - Configuration options for tracking mode and instance management\n * @returns Tuple with [state, bloc instance, ref]\n *\n * @example Basic usage\n * ```ts\n * const [state, myBloc, ref] = useBloc(MyBloc);\n * ```\n *\n * @example With manual dependencies\n * ```ts\n * const [state, myBloc] = useBloc(MyBloc, {\n * dependencies: (state) => [state.count]\n * });\n * ```\n *\n * @example With isolated instance\n * ```ts\n * const [state, myBloc] = useBloc(MyBloc, {\n * instanceId: 'unique-id'\n * });\n * ```\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 componentRef = useRef<ComponentRef>({});\n const isIsolated = isIsolatedClass(BlocClass);\n\n const [bloc, subscribe, getSnapshot, instanceKey, adapterState, rawInstance] =\n useMemo<\n readonly [\n TBloc,\n (callback: () => void) => () => void,\n () => ExtractState<T>,\n string | undefined,\n AdapterState<T>,\n TBloc,\n ]\n >(() => {\n const instanceKey = generateInstanceKey(\n componentRef.current,\n isIsolated,\n options?.instanceId,\n );\n\n const instance = acquire(BlocClass, instanceKey) as TBloc;\n\n const { useManualDeps, autoTrackEnabled } =\n determineTrackingMode(options);\n\n let subscribeFn: (callback: () => void) => () => void;\n let getSnapshotFn: () => ExtractState<T>;\n let adapterState: AdapterState<T>;\n\n if (useManualDeps && options?.dependencies) {\n adapterState = manualDepsInit(instance);\n subscribeFn = manualDepsSubscribe(instance, adapterState, {\n dependencies: options.dependencies,\n });\n getSnapshotFn = manualDepsSnapshot(instance, adapterState, {\n dependencies: options.dependencies,\n });\n } else if (!autoTrackEnabled) {\n adapterState = noTrackInit(instance);\n subscribeFn = noTrackSubscribe(instance);\n getSnapshotFn = noTrackSnapshot(instance);\n } else {\n adapterState = autoTrackInit(instance);\n subscribeFn = autoTrackSubscribe(instance, adapterState);\n getSnapshotFn = autoTrackSnapshot(instance, adapterState);\n }\n\n return [\n adapterState.proxiedBloc as TBloc,\n subscribeFn,\n getSnapshotFn,\n instanceKey,\n adapterState,\n instance,\n ];\n }, [BlocClass, isIsolated, options?.instanceId]);\n\n const state = useSyncExternalStore(subscribe, getSnapshot);\n\n const [, forceUpdate] = useReducer((x: number) => x + 1, 0);\n\n const externalDepsManager = useRef(new ExternalDepsManager());\n\n useEffect(() => {\n disableGetterTracking(adapterState, rawInstance);\n externalDepsManager.current.updateSubscriptions(\n adapterState.getterState,\n rawInstance,\n forceUpdate,\n );\n });\n\n useEffect(() => {\n if (options?.onMount) {\n options.onMount(bloc as InstanceType<T>);\n }\n\n return () => {\n externalDepsManager.current.cleanup();\n\n if (options?.onUnmount) {\n options.onUnmount(bloc as InstanceType<T>);\n }\n\n release(BlocClass, instanceKey);\n\n if (isIsolated && !rawInstance.isDisposed) {\n rawInstance.dispose();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return [state, bloc, componentRef] as UseBlocReturn<T, ExtractState<T>>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,oBACd,cACA,YACA,YACoB;AAEpB,KAAI,eAAe,OACjB,QAAO,OAAO,eAAe,WAAW,OAAO,WAAW,GAAG;AAI/D,KAAI,YAAY;AACd,MAAI,CAAC,aAAa,iBAChB,cAAa,mBAAmB,qBAAqB;AAEvD,SAAO,aAAa;;;;;;AC1BxB,MAAM,gBAAiC,EACrC,WAAW,MACZ;AAED,IAAI,eAAgC,EAAE,GAAG,eAAe;;;;;;;;;;;;;;;;AAiBxD,SAAgB,mBAAmB,QAAwC;AACzE,gBAAe;EAAE,GAAG;EAAc,GAAG;EAAQ;;;;;;AAO/C,SAAgB,qBAAsC;AACpD,QAAO;;;;;;AAOT,SAAgB,uBAA6B;AAC3C,gBAAe,EAAE,GAAG,eAAe;;;;;ACXrC,SAAS,sBACP,SACc;CACd,MAAM,eAAe,oBAAoB;CACzC,MAAM,mBACJ,SAAS,cAAc,SACnB,QAAQ,YACR,aAAa;AAEnB,QAAO;EACL,eAAe,SAAS,iBAAiB;EACzC;EACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCH,SAAgB,QAGd,WACA,SACmC;CAGnC,MAAM,eAAe,OAAqB,EAAE,CAAC;CAC7C,MAAM,aAAa,gBAAgB,UAAU;CAE7C,MAAM,CAAC,MAAM,WAAW,aAAa,aAAa,cAAc,eAC9D,cASQ;EACN,MAAM,cAAc,oBAClB,aAAa,SACb,YACA,SAAS,WACV;EAED,MAAM,WAAW,QAAQ,WAAW,YAAY;EAEhD,MAAM,EAAE,eAAe,qBACrB,sBAAsB,QAAQ;EAEhC,IAAI;EACJ,IAAI;EACJ,IAAI;AAEJ,MAAI,iBAAiB,SAAS,cAAc;AAC1C,kBAAe,eAAe,SAAS;AACvC,iBAAc,oBAAoB,UAAU,cAAc,EACxD,cAAc,QAAQ,cACvB,CAAC;AACF,mBAAgB,mBAAmB,UAAU,cAAc,EACzD,cAAc,QAAQ,cACvB,CAAC;aACO,CAAC,kBAAkB;AAC5B,kBAAe,YAAY,SAAS;AACpC,iBAAc,iBAAiB,SAAS;AACxC,mBAAgB,gBAAgB,SAAS;SACpC;AACL,kBAAe,cAAc,SAAS;AACtC,iBAAc,mBAAmB,UAAU,aAAa;AACxD,mBAAgB,kBAAkB,UAAU,aAAa;;AAG3D,SAAO;GACL,aAAa;GACb;GACA;GACA;GACA;GACA;GACD;IACA;EAAC;EAAW;EAAY,SAAS;EAAW,CAAC;CAElD,MAAM,QAAQ,qBAAqB,WAAW,YAAY;CAE1D,MAAM,GAAG,eAAe,YAAY,MAAc,IAAI,GAAG,EAAE;CAE3D,MAAM,sBAAsB,OAAO,IAAI,qBAAqB,CAAC;AAE7D,iBAAgB;AACd,wBAAsB,cAAc,YAAY;AAChD,sBAAoB,QAAQ,oBAC1B,aAAa,aACb,aACA,YACD;GACD;AAEF,iBAAgB;AACd,MAAI,SAAS,QACX,SAAQ,QAAQ,KAAwB;AAG1C,eAAa;AACX,uBAAoB,QAAQ,SAAS;AAErC,OAAI,SAAS,UACX,SAAQ,UAAU,KAAwB;AAG5C,WAAQ,WAAW,YAAY;AAE/B,OAAI,cAAc,CAAC,YAAY,WAC7B,aAAY,SAAS;;IAIxB,EAAE,CAAC;AAEN,QAAO;EAAC;EAAO;EAAM;EAAa"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blac/react",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "license": "MIT",
5
5
  "author": "Brendan Mullins <jsnanigans@gmail.com>",
6
6
  "repository": {
@@ -46,10 +46,12 @@
46
46
  "bloc",
47
47
  "bloc-pattern"
48
48
  ],
49
+ "dependencies": {
50
+ "@blac/adapter": "2.0.3"
51
+ },
49
52
  "peerDependencies": {
50
53
  "@types/react": "^18.0.0 || ^19.0.0",
51
- "react": "^18.0.0 || ^19.0.0",
52
- "@blac/core": "2.0.1"
54
+ "react": "^18.0.0 || ^19.0.0"
53
55
  },
54
56
  "peerDependenciesMeta": {
55
57
  "@types/react": {
@@ -78,7 +80,7 @@
78
80
  "tsdown": "^0.15.7",
79
81
  "typescript": "^5.9.2",
80
82
  "vitest": "3.2.4",
81
- "@blac/core": "2.0.1"
83
+ "@blac/core": "2.0.3"
82
84
  },
83
85
  "scripts": {
84
86
  "dev": "tsdown --watch",