@blac/react 2.0.0-rc.15 → 2.0.0-rc.17

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/index.cjs CHANGED
@@ -26,12 +26,54 @@ function generateInstanceKey(componentRef, isIsolated, providedId) {
26
26
  }
27
27
  }
28
28
 
29
+ //#endregion
30
+ //#region src/config.ts
31
+ const defaultConfig = { autoTrack: true };
32
+ let globalConfig = { ...defaultConfig };
33
+ /**
34
+ * Configure global defaults for @blac/react hooks.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * import { configureBlacReact } from '@blac/react';
39
+ *
40
+ * // Disable auto-tracking globally
41
+ * configureBlacReact({
42
+ * autoTrack: false
43
+ * });
44
+ * ```
45
+ *
46
+ * @param config - Partial configuration to merge with defaults
47
+ */
48
+ function configureBlacReact(config) {
49
+ globalConfig = {
50
+ ...globalConfig,
51
+ ...config
52
+ };
53
+ }
54
+ /**
55
+ * Get the current global configuration.
56
+ * @internal
57
+ */
58
+ function getBlacReactConfig() {
59
+ return globalConfig;
60
+ }
61
+ /**
62
+ * Reset configuration to defaults (useful for testing).
63
+ * @internal
64
+ */
65
+ function resetBlacReactConfig() {
66
+ globalConfig = { ...defaultConfig };
67
+ }
68
+
29
69
  //#endregion
30
70
  //#region src/useBloc.ts
31
71
  function determineTrackingMode(options) {
72
+ const globalConfig$1 = getBlacReactConfig();
73
+ const autoTrackEnabled = options?.autoTrack !== void 0 ? options.autoTrack : globalConfig$1.autoTrack;
32
74
  return {
33
75
  useManualDeps: options?.dependencies !== void 0,
34
- autoTrackEnabled: options?.autoTrack !== false
76
+ autoTrackEnabled
35
77
  };
36
78
  }
37
79
  /**
@@ -42,9 +84,11 @@ function determineTrackingMode(options) {
42
84
  * - **Manual dependencies**: Explicit dependency array like useEffect
43
85
  * - **No tracking**: Returns full state without optimization
44
86
  *
45
- * @template S - Optional state type override for generic StateContainers (defaults to never)
87
+ * **Note:** This hook does NOT support stateless containers (StatelessCubit, StatelessVertex).
88
+ * Use `useBlocActions()` instead for stateless containers.
89
+ *
46
90
  * @template T - The state container constructor type (inferred from BlocClass)
47
- * @param BlocClass - The state container class to connect to
91
+ * @param BlocClass - The state container class to connect to (must not be stateless)
48
92
  * @param options - Configuration options for tracking mode and instance management
49
93
  * @returns Tuple with [state, bloc instance, ref]
50
94
  *
@@ -66,37 +110,44 @@ function determineTrackingMode(options) {
66
110
  * instanceId: 'unique-id'
67
111
  * });
68
112
  * ```
69
- *
70
- * @example With generic bloc and explicit state type
71
- * ```ts
72
- * const [state, myBloc] = useBloc<MyStateType>(GenericBloc);
73
- * // state is typed as MyStateType
74
- * ```
75
113
  */
76
114
  function useBloc(BlocClass, options) {
77
115
  const componentRef = (0, react.useRef)({});
78
- const isIsolated = (0, __blac_core.isIsolatedClass)(BlocClass);
79
116
  const initialPropsRef = (0, react.useRef)(options?.props);
117
+ const isStateless = (0, __blac_core.isStatelessClass)(BlocClass);
118
+ const isIsolated = (0, __blac_core.isIsolatedClass)(BlocClass);
80
119
  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
+ }
81
132
  const instanceKey$1 = generateInstanceKey(componentRef.current, isIsolated, options?.instanceId);
82
- const instance = BlocClass.resolve(instanceKey$1, { props: initialPropsRef.current });
133
+ const instance = (0, __blac_core.acquire)(BlocClass, instanceKey$1, { props: initialPropsRef.current });
83
134
  if (initialPropsRef.current !== void 0) instance.updateProps(initialPropsRef.current);
84
135
  const { useManualDeps, autoTrackEnabled } = determineTrackingMode(options);
85
136
  let subscribeFn;
86
137
  let getSnapshotFn;
87
138
  let adapterState$1;
88
139
  if (useManualDeps && options?.dependencies) {
89
- adapterState$1 = (0, __blac_core.initManualDepsState)(instance);
90
- subscribeFn = (0, __blac_core.createManualDepsSubscribe)(instance, adapterState$1, { dependencies: options.dependencies });
91
- getSnapshotFn = (0, __blac_core.createManualDepsSnapshot)(instance, adapterState$1, { dependencies: 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 });
92
143
  } else if (!autoTrackEnabled) {
93
- adapterState$1 = (0, __blac_core.initNoTrackState)(instance);
94
- subscribeFn = (0, __blac_core.createNoTrackSubscribe)(instance);
95
- getSnapshotFn = (0, __blac_core.createNoTrackSnapshot)(instance);
144
+ adapterState$1 = (0, __blac_core.noTrackInit)(instance);
145
+ subscribeFn = (0, __blac_core.noTrackSubscribe)(instance);
146
+ getSnapshotFn = (0, __blac_core.noTrackSnapshot)(instance);
96
147
  } else {
97
- adapterState$1 = (0, __blac_core.initAutoTrackState)(instance);
98
- subscribeFn = (0, __blac_core.createAutoTrackSubscribe)(instance, adapterState$1);
99
- getSnapshotFn = (0, __blac_core.createAutoTrackSnapshot)(instance, adapterState$1);
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);
100
151
  }
101
152
  return [
102
153
  adapterState$1.proxiedBloc,
@@ -106,23 +157,35 @@ function useBloc(BlocClass, options) {
106
157
  adapterState$1,
107
158
  instance
108
159
  ];
109
- }, [BlocClass, options?.instanceId]);
160
+ }, [
161
+ BlocClass,
162
+ isStateless,
163
+ isIsolated,
164
+ options?.instanceId
165
+ ]);
110
166
  const state = (0, react.useSyncExternalStore)(subscribe, getSnapshot);
111
167
  const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
112
- const externalDepsManager = (0, react.useRef)(new __blac_core.ExternalDependencyManager());
168
+ const externalDepsManager = (0, react.useRef)(new __blac_core.ExternalDepsManager());
113
169
  (0, react.useEffect)(() => {
170
+ if (isStateless) return;
114
171
  if (options?.props !== initialPropsRef.current) rawInstance.updateProps(options?.props);
115
- }, [options?.props, rawInstance]);
172
+ }, [
173
+ options?.props,
174
+ rawInstance,
175
+ isStateless
176
+ ]);
116
177
  (0, react.useEffect)(() => {
178
+ if (isStateless || !adapterState) return;
117
179
  (0, __blac_core.disableGetterTracking)(adapterState, rawInstance);
118
- externalDepsManager.current.updateSubscriptions(adapterState.getterTracker, rawInstance, forceUpdate);
180
+ externalDepsManager.current.updateSubscriptions(adapterState.getterState, rawInstance, forceUpdate);
119
181
  });
120
182
  (0, react.useEffect)(() => {
183
+ if (isStateless) return;
121
184
  if (options?.onMount) options.onMount(bloc);
122
185
  return () => {
123
186
  externalDepsManager.current.cleanup();
124
187
  if (options?.onUnmount) options.onUnmount(bloc);
125
- BlocClass.release(instanceKey);
188
+ (0, __blac_core.release)(BlocClass, instanceKey);
126
189
  if (isIsolated && !rawInstance.isDisposed) rawInstance.dispose();
127
190
  };
128
191
  }, []);
@@ -139,17 +202,28 @@ function useBloc(BlocClass, options) {
139
202
  * React hook that connects to a state container instance without triggering re-renders.
140
203
  * Use this when you only need to call actions on the bloc without subscribing to state changes.
141
204
  *
142
- * @template S - Optional state type override for generic StateContainers (defaults to never)
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
+ *
143
208
  * @template T - The state container constructor type (inferred from BlocClass)
144
- * @param BlocClass - The state container class to connect to
209
+ * @param BlocClass - The state container class to connect to (supports both stateful and stateless)
145
210
  * @param options - Configuration options for instance management and lifecycle
146
211
  * @returns The state container instance for calling actions
147
212
  *
148
- * @example Basic usage
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)
149
224
  * ```ts
150
225
  * const myBloc = useBlocActions(MyBloc);
151
- * // Call methods on the bloc without re-rendering
152
- * myBloc.someMethod();
226
+ * myBloc.someMethod(); // Won't cause re-renders
153
227
  * ```
154
228
  *
155
229
  * @example With isolated instance
@@ -158,12 +232,6 @@ function useBloc(BlocClass, options) {
158
232
  * instanceId: 'unique-id'
159
233
  * });
160
234
  * ```
161
- *
162
- * @example With generic bloc and explicit state type
163
- * ```ts
164
- * const myBloc = useBlocActions<MyStateType>(GenericBloc);
165
- * // myBloc.state is typed as MyStateType
166
- * ```
167
235
  */
168
236
  function useBlocActions(BlocClass, options) {
169
237
  const componentRef = (0, react.useRef)({});
@@ -171,13 +239,13 @@ function useBlocActions(BlocClass, options) {
171
239
  const [bloc, instanceKey] = (0, react.useMemo)(() => {
172
240
  const isIsolated = (0, __blac_core.isIsolatedClass)(BlocClass);
173
241
  const instanceKey$1 = generateInstanceKey(componentRef.current, isIsolated, options?.instanceId);
174
- return [BlocClass.resolve(instanceKey$1, { props: initialPropsRef.current }), instanceKey$1];
242
+ return [(0, __blac_core.acquire)(BlocClass, instanceKey$1, { props: initialPropsRef.current }), instanceKey$1];
175
243
  }, [BlocClass]);
176
244
  (0, react.useEffect)(() => {
177
245
  if (options?.onMount) options.onMount(bloc);
178
246
  return () => {
179
247
  if (options?.onUnmount) options.onUnmount(bloc);
180
- BlocClass.release(instanceKey);
248
+ (0, __blac_core.release)(BlocClass, instanceKey);
181
249
  if ((0, __blac_core.isIsolatedClass)(BlocClass) && !bloc.isDisposed) bloc.dispose();
182
250
  };
183
251
  }, []);
@@ -185,6 +253,8 @@ function useBlocActions(BlocClass, options) {
185
253
  }
186
254
 
187
255
  //#endregion
256
+ exports.configureBlacReact = configureBlacReact;
257
+ exports.resetBlacReactConfig = resetBlacReactConfig;
188
258
  exports.useBloc = useBloc;
189
259
  exports.useBlocActions = useBlocActions;
190
260
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["instanceKey","subscribeFn: (callback: () => void) => () => void","getSnapshotFn: () => ExtractState<T>","adapterState: AdapterState<T>","adapterState","ExternalDependencyManager","instanceKey"],"sources":["../src/utils/instance-keys.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","import {\n useMemo,\n useSyncExternalStore,\n useEffect,\n useRef,\n useReducer,\n} from 'react';\nimport {\n type ExtractState,\n type AdapterState,\n ExternalDependencyManager,\n createAutoTrackSubscribe,\n createManualDepsSubscribe,\n createNoTrackSubscribe,\n createAutoTrackSnapshot,\n createManualDepsSnapshot,\n createNoTrackSnapshot,\n initAutoTrackState,\n initManualDepsState,\n initNoTrackState,\n disableGetterTracking,\n isIsolatedClass,\n StateContainerConstructor,\n InstanceState,\n} from '@blac/core';\nimport type { UseBlocOptions, UseBlocReturn, ComponentRef } from './types';\nimport { generateInstanceKey } from './utils/instance-keys';\n\ninterface TrackingMode {\n useManualDeps: boolean;\n autoTrackEnabled: boolean;\n}\n\nfunction determineTrackingMode<TBloc extends StateContainerConstructor>(\n options?: UseBlocOptions<TBloc>,\n): TrackingMode {\n return {\n useManualDeps: options?.dependencies !== undefined,\n autoTrackEnabled: options?.autoTrack !== false,\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 S - Optional state type override for generic StateContainers (defaults to never)\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 *\n * @example With generic bloc and explicit state type\n * ```ts\n * const [state, myBloc] = useBloc<MyStateType>(GenericBloc);\n * // state is typed as MyStateType\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 const componentRef = useRef<ComponentRef>({});\n const isIsolated = isIsolatedClass(BlocClass);\n\n const initialPropsRef = useRef(options?.props);\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 = (BlocClass as any).resolve(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 = initManualDepsState(instance);\n subscribeFn = createManualDepsSubscribe(instance, adapterState, {\n dependencies: options.dependencies,\n });\n getSnapshotFn = createManualDepsSnapshot(instance, adapterState, {\n dependencies: options.dependencies,\n });\n } else if (!autoTrackEnabled) {\n adapterState = initNoTrackState(instance);\n subscribeFn = createNoTrackSubscribe(instance);\n getSnapshotFn = createNoTrackSnapshot(instance);\n } else {\n adapterState = initAutoTrackState(instance);\n subscribeFn = createAutoTrackSubscribe(instance, adapterState);\n getSnapshotFn = createAutoTrackSnapshot(instance, adapterState);\n }\n\n return [\n adapterState.proxiedBloc!,\n subscribeFn,\n getSnapshotFn,\n instanceKey,\n adapterState,\n instance,\n ];\n }, [BlocClass, 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 ExternalDependencyManager());\n\n useEffect(() => {\n if (options?.props !== initialPropsRef.current) {\n rawInstance.updateProps(options?.props);\n }\n }, [options?.props, rawInstance]);\n\n useEffect(() => {\n disableGetterTracking(adapterState, rawInstance);\n externalDepsManager.current.updateSubscriptions(\n adapterState.getterTracker,\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 (BlocClass as any).release(instanceKey);\n\n if (isIsolated && !rawInstance.isDisposed) {\n rawInstance.dispose();\n }\n };\n }, []);\n\n return [state, bloc, componentRef];\n}\n","import { useMemo, useEffect, useRef } from 'react';\nimport { StateContainerConstructor, isIsolatedClass } 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 * @template S - Optional state type override for generic StateContainers (defaults to never)\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 instance management and lifecycle\n * @returns The state container instance for calling actions\n *\n * @example Basic usage\n * ```ts\n * const myBloc = useBlocActions(MyBloc);\n * // Call methods on the bloc without re-rendering\n * myBloc.someMethod();\n * ```\n *\n * @example With isolated instance\n * ```ts\n * const myBloc = useBlocActions(MyBloc, {\n * instanceId: 'unique-id'\n * });\n * ```\n *\n * @example With generic bloc and explicit state type\n * ```ts\n * const myBloc = useBlocActions<MyStateType>(GenericBloc);\n * // myBloc.state is typed as MyStateType\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 = (BlocClass as any).resolve(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 (BlocClass as any).release(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.cjs","names":["defaultConfig: BlacReactConfig","globalConfig: BlacReactConfig","globalConfig","instance","instanceKey","subscribeFn: (callback: () => void) => () => void","getSnapshotFn: () => ExtractState<T>","adapterState: AdapterState<T>","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,yDAAwC;AAEvD,SAAO,aAAa;;;;;;AC1BxB,MAAMA,gBAAiC,EACrC,WAAW,MACZ;AAED,IAAIC,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,MAAMC,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,gDAA+B,UAAU;CAC/C,MAAM,8CAA6B,UAAU;CAE7C,MAAM,CAAC,MAAM,WAAW,aAAa,aAAa,cAAc,wCAUtD;AAEN,MAAI,aAAa;AACf,WAAQ,MACN,4DACM,UAAU,KAAK,kGAEtB;GACD,MAAMC,sCAAmB,WAAW,UAAU;AAE9C,UAAO;IACLA;gBACY;WACL,EAAE;IACT;IACA;IACAA;IACD;;EAGH,MAAMC,gBAAc,oBAClB,aAAa,SACb,YACA,SAAS,WACV;EAED,MAAM,oCAAmB,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,IAAIC;EACJ,IAAIC;EACJ,IAAIC;AAEJ,MAAI,iBAAiB,SAAS,cAAc;AAC1C,oDAA8B,SAAS;AACvC,sDAAkC,UAAUC,gBAAc,EACxD,cAAc,QAAQ,cACvB,CAAC;AACF,uDAAmC,UAAUA,gBAAc,EACzD,cAAc,QAAQ,cACvB,CAAC;aACO,CAAC,kBAAkB;AAC5B,iDAA2B,SAAS;AACpC,mDAA+B,SAAS;AACxC,oDAAgC,SAAS;SACpC;AACL,mDAA6B,SAAS;AACtC,qDAAiC,UAAUA,eAAa;AACxD,sDAAkC,UAAUA,eAAa;;AAG3D,SAAO;GACLA,eAAa;GACb;GACA;GACAJ;GACAI;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,iCAAqB,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,yCAAsB,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,4BAAQ,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,8CAA6B,UAAU;EAE7C,MAAMC,gBAAc,oBAClB,aAAa,SACb,YACA,SAAS,WACV;AAMD,SAAO,0BAJkB,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,4BAAQ,WAAW,YAAY;AAE/B,wCAAoB,UAAU,IAAI,CAAC,KAAK,WACtC,MAAK,SAAS;;IAGjB,EAAE,CAAC;AAEN,QAAO"}
package/dist/index.d.cts CHANGED
@@ -1,11 +1,45 @@
1
1
  import type { ExtractProps } from '@blac/core';
2
2
  import { ExtractState } from '@blac/core';
3
3
  import { InstanceReadonlyState } from '@blac/core';
4
+ import { IsStatelessContainer } from '@blac/core';
4
5
  import type { RefObject } from 'react';
5
6
  import { StateContainerConstructor } from '@blac/core';
6
7
 
8
+ /**
9
+ * Global configuration for @blac/react
10
+ */
11
+ export declare interface BlacReactConfig {
12
+ /** Enable automatic property tracking via Proxy (default: true) */
13
+ autoTrack: boolean;
14
+ }
15
+
7
16
  /* Excluded from this release type: ComponentRef */
8
17
 
18
+ /**
19
+ * Configure global defaults for @blac/react hooks.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * import { configureBlacReact } from '@blac/react';
24
+ *
25
+ * // Disable auto-tracking globally
26
+ * configureBlacReact({
27
+ * autoTrack: false
28
+ * });
29
+ * ```
30
+ *
31
+ * @param config - Partial configuration to merge with defaults
32
+ */
33
+ export declare function configureBlacReact(config: Partial<BlacReactConfig>): void;
34
+
35
+ /* Excluded from this release type: resetBlacReactConfig */
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
+
9
43
  /**
10
44
  * React hook that connects a component to a state container with automatic re-render on state changes.
11
45
  *
@@ -14,9 +48,11 @@ import { StateContainerConstructor } from '@blac/core';
14
48
  * - **Manual dependencies**: Explicit dependency array like useEffect
15
49
  * - **No tracking**: Returns full state without optimization
16
50
  *
17
- * @template S - Optional state type override for generic StateContainers (defaults to never)
51
+ * **Note:** This hook does NOT support stateless containers (StatelessCubit, StatelessVertex).
52
+ * Use `useBlocActions()` instead for stateless containers.
53
+ *
18
54
  * @template T - The state container constructor type (inferred from BlocClass)
19
- * @param BlocClass - The state container class to connect to
55
+ * @param BlocClass - The state container class to connect to (must not be stateless)
20
56
  * @param options - Configuration options for tracking mode and instance management
21
57
  * @returns Tuple with [state, bloc instance, ref]
22
58
  *
@@ -38,30 +74,35 @@ import { StateContainerConstructor } from '@blac/core';
38
74
  * instanceId: 'unique-id'
39
75
  * });
40
76
  * ```
41
- *
42
- * @example With generic bloc and explicit state type
43
- * ```ts
44
- * const [state, myBloc] = useBloc<MyStateType>(GenericBloc);
45
- * // state is typed as MyStateType
46
- * ```
47
77
  */
48
- export declare function useBloc<T extends StateContainerConstructor = StateContainerConstructor>(BlocClass: T, options?: UseBlocOptions<T>): UseBlocReturn<T, ExtractState<T>>;
78
+ export declare function useBloc<T extends StateContainerConstructor = StateContainerConstructor>(BlocClass: StatefulContainer<T>, options?: UseBlocOptions<T>): UseBlocReturn<T, ExtractState<T>>;
49
79
 
50
80
  /**
51
81
  * React hook that connects to a state container instance without triggering re-renders.
52
82
  * Use this when you only need to call actions on the bloc without subscribing to state changes.
53
83
  *
54
- * @template S - Optional state type override for generic StateContainers (defaults to never)
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
+ *
55
87
  * @template T - The state container constructor type (inferred from BlocClass)
56
- * @param BlocClass - The state container class to connect to
88
+ * @param BlocClass - The state container class to connect to (supports both stateful and stateless)
57
89
  * @param options - Configuration options for instance management and lifecycle
58
90
  * @returns The state container instance for calling actions
59
91
  *
60
- * @example Basic usage
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)
61
103
  * ```ts
62
104
  * const myBloc = useBlocActions(MyBloc);
63
- * // Call methods on the bloc without re-rendering
64
- * myBloc.someMethod();
105
+ * myBloc.someMethod(); // Won't cause re-renders
65
106
  * ```
66
107
  *
67
108
  * @example With isolated instance
@@ -70,12 +111,6 @@ export declare function useBloc<T extends StateContainerConstructor = StateConta
70
111
  * instanceId: 'unique-id'
71
112
  * });
72
113
  * ```
73
- *
74
- * @example With generic bloc and explicit state type
75
- * ```ts
76
- * const myBloc = useBlocActions<MyStateType>(GenericBloc);
77
- * // myBloc.state is typed as MyStateType
78
- * ```
79
114
  */
80
115
  export declare function useBlocActions<T extends StateContainerConstructor = StateContainerConstructor>(BlocClass: T, options?: UseBlocActionsOptions<InstanceType<T>>): InstanceType<T>;
81
116
 
package/dist/index.d.ts CHANGED
@@ -1,11 +1,45 @@
1
1
  import type { ExtractProps } from '@blac/core';
2
2
  import { ExtractState } from '@blac/core';
3
3
  import { InstanceReadonlyState } from '@blac/core';
4
+ import { IsStatelessContainer } from '@blac/core';
4
5
  import type { RefObject } from 'react';
5
6
  import { StateContainerConstructor } from '@blac/core';
6
7
 
8
+ /**
9
+ * Global configuration for @blac/react
10
+ */
11
+ export declare interface BlacReactConfig {
12
+ /** Enable automatic property tracking via Proxy (default: true) */
13
+ autoTrack: boolean;
14
+ }
15
+
7
16
  /* Excluded from this release type: ComponentRef */
8
17
 
18
+ /**
19
+ * Configure global defaults for @blac/react hooks.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * import { configureBlacReact } from '@blac/react';
24
+ *
25
+ * // Disable auto-tracking globally
26
+ * configureBlacReact({
27
+ * autoTrack: false
28
+ * });
29
+ * ```
30
+ *
31
+ * @param config - Partial configuration to merge with defaults
32
+ */
33
+ export declare function configureBlacReact(config: Partial<BlacReactConfig>): void;
34
+
35
+ /* Excluded from this release type: resetBlacReactConfig */
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
+
9
43
  /**
10
44
  * React hook that connects a component to a state container with automatic re-render on state changes.
11
45
  *
@@ -14,9 +48,11 @@ import { StateContainerConstructor } from '@blac/core';
14
48
  * - **Manual dependencies**: Explicit dependency array like useEffect
15
49
  * - **No tracking**: Returns full state without optimization
16
50
  *
17
- * @template S - Optional state type override for generic StateContainers (defaults to never)
51
+ * **Note:** This hook does NOT support stateless containers (StatelessCubit, StatelessVertex).
52
+ * Use `useBlocActions()` instead for stateless containers.
53
+ *
18
54
  * @template T - The state container constructor type (inferred from BlocClass)
19
- * @param BlocClass - The state container class to connect to
55
+ * @param BlocClass - The state container class to connect to (must not be stateless)
20
56
  * @param options - Configuration options for tracking mode and instance management
21
57
  * @returns Tuple with [state, bloc instance, ref]
22
58
  *
@@ -38,30 +74,35 @@ import { StateContainerConstructor } from '@blac/core';
38
74
  * instanceId: 'unique-id'
39
75
  * });
40
76
  * ```
41
- *
42
- * @example With generic bloc and explicit state type
43
- * ```ts
44
- * const [state, myBloc] = useBloc<MyStateType>(GenericBloc);
45
- * // state is typed as MyStateType
46
- * ```
47
77
  */
48
- export declare function useBloc<T extends StateContainerConstructor = StateContainerConstructor>(BlocClass: T, options?: UseBlocOptions<T>): UseBlocReturn<T, ExtractState<T>>;
78
+ export declare function useBloc<T extends StateContainerConstructor = StateContainerConstructor>(BlocClass: StatefulContainer<T>, options?: UseBlocOptions<T>): UseBlocReturn<T, ExtractState<T>>;
49
79
 
50
80
  /**
51
81
  * React hook that connects to a state container instance without triggering re-renders.
52
82
  * Use this when you only need to call actions on the bloc without subscribing to state changes.
53
83
  *
54
- * @template S - Optional state type override for generic StateContainers (defaults to never)
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
+ *
55
87
  * @template T - The state container constructor type (inferred from BlocClass)
56
- * @param BlocClass - The state container class to connect to
88
+ * @param BlocClass - The state container class to connect to (supports both stateful and stateless)
57
89
  * @param options - Configuration options for instance management and lifecycle
58
90
  * @returns The state container instance for calling actions
59
91
  *
60
- * @example Basic usage
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)
61
103
  * ```ts
62
104
  * const myBloc = useBlocActions(MyBloc);
63
- * // Call methods on the bloc without re-rendering
64
- * myBloc.someMethod();
105
+ * myBloc.someMethod(); // Won't cause re-renders
65
106
  * ```
66
107
  *
67
108
  * @example With isolated instance
@@ -70,12 +111,6 @@ export declare function useBloc<T extends StateContainerConstructor = StateConta
70
111
  * instanceId: 'unique-id'
71
112
  * });
72
113
  * ```
73
- *
74
- * @example With generic bloc and explicit state type
75
- * ```ts
76
- * const myBloc = useBlocActions<MyStateType>(GenericBloc);
77
- * // myBloc.state is typed as MyStateType
78
- * ```
79
114
  */
80
115
  export declare function useBlocActions<T extends StateContainerConstructor = StateContainerConstructor>(BlocClass: T, options?: UseBlocActionsOptions<InstanceType<T>>): InstanceType<T>;
81
116
 
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useMemo, useReducer, useRef, useSyncExternalStore } from "react";
2
- import { ExternalDependencyManager, createAutoTrackSnapshot, createAutoTrackSubscribe, createManualDepsSnapshot, createManualDepsSubscribe, createNoTrackSnapshot, createNoTrackSubscribe, disableGetterTracking, generateIsolatedKey, initAutoTrackState, initManualDepsState, initNoTrackState, isIsolatedClass } from "@blac/core";
2
+ import { ExternalDepsManager, acquire, autoTrackInit, autoTrackSnapshot, autoTrackSubscribe, disableGetterTracking, generateIsolatedKey, isIsolatedClass, isStatelessClass, manualDepsInit, manualDepsSnapshot, manualDepsSubscribe, noTrackInit, noTrackSnapshot, noTrackSubscribe, release } from "@blac/core";
3
3
 
4
4
  //#region src/utils/instance-keys.ts
5
5
  /**
@@ -26,12 +26,54 @@ function generateInstanceKey(componentRef, isIsolated, providedId) {
26
26
  }
27
27
  }
28
28
 
29
+ //#endregion
30
+ //#region src/config.ts
31
+ const defaultConfig = { autoTrack: true };
32
+ let globalConfig = { ...defaultConfig };
33
+ /**
34
+ * Configure global defaults for @blac/react hooks.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * import { configureBlacReact } from '@blac/react';
39
+ *
40
+ * // Disable auto-tracking globally
41
+ * configureBlacReact({
42
+ * autoTrack: false
43
+ * });
44
+ * ```
45
+ *
46
+ * @param config - Partial configuration to merge with defaults
47
+ */
48
+ function configureBlacReact(config) {
49
+ globalConfig = {
50
+ ...globalConfig,
51
+ ...config
52
+ };
53
+ }
54
+ /**
55
+ * Get the current global configuration.
56
+ * @internal
57
+ */
58
+ function getBlacReactConfig() {
59
+ return globalConfig;
60
+ }
61
+ /**
62
+ * Reset configuration to defaults (useful for testing).
63
+ * @internal
64
+ */
65
+ function resetBlacReactConfig() {
66
+ globalConfig = { ...defaultConfig };
67
+ }
68
+
29
69
  //#endregion
30
70
  //#region src/useBloc.ts
31
71
  function determineTrackingMode(options) {
72
+ const globalConfig$1 = getBlacReactConfig();
73
+ const autoTrackEnabled = options?.autoTrack !== void 0 ? options.autoTrack : globalConfig$1.autoTrack;
32
74
  return {
33
75
  useManualDeps: options?.dependencies !== void 0,
34
- autoTrackEnabled: options?.autoTrack !== false
76
+ autoTrackEnabled
35
77
  };
36
78
  }
37
79
  /**
@@ -42,9 +84,11 @@ function determineTrackingMode(options) {
42
84
  * - **Manual dependencies**: Explicit dependency array like useEffect
43
85
  * - **No tracking**: Returns full state without optimization
44
86
  *
45
- * @template S - Optional state type override for generic StateContainers (defaults to never)
87
+ * **Note:** This hook does NOT support stateless containers (StatelessCubit, StatelessVertex).
88
+ * Use `useBlocActions()` instead for stateless containers.
89
+ *
46
90
  * @template T - The state container constructor type (inferred from BlocClass)
47
- * @param BlocClass - The state container class to connect to
91
+ * @param BlocClass - The state container class to connect to (must not be stateless)
48
92
  * @param options - Configuration options for tracking mode and instance management
49
93
  * @returns Tuple with [state, bloc instance, ref]
50
94
  *
@@ -66,37 +110,44 @@ function determineTrackingMode(options) {
66
110
  * instanceId: 'unique-id'
67
111
  * });
68
112
  * ```
69
- *
70
- * @example With generic bloc and explicit state type
71
- * ```ts
72
- * const [state, myBloc] = useBloc<MyStateType>(GenericBloc);
73
- * // state is typed as MyStateType
74
- * ```
75
113
  */
76
114
  function useBloc(BlocClass, options) {
77
115
  const componentRef = useRef({});
78
- const isIsolated = isIsolatedClass(BlocClass);
79
116
  const initialPropsRef = useRef(options?.props);
117
+ const isStateless = isStatelessClass(BlocClass);
118
+ const isIsolated = isIsolatedClass(BlocClass);
80
119
  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
+ }
81
132
  const instanceKey$1 = generateInstanceKey(componentRef.current, isIsolated, options?.instanceId);
82
- const instance = BlocClass.resolve(instanceKey$1, { props: initialPropsRef.current });
133
+ const instance = acquire(BlocClass, instanceKey$1, { props: initialPropsRef.current });
83
134
  if (initialPropsRef.current !== void 0) instance.updateProps(initialPropsRef.current);
84
135
  const { useManualDeps, autoTrackEnabled } = determineTrackingMode(options);
85
136
  let subscribeFn;
86
137
  let getSnapshotFn;
87
138
  let adapterState$1;
88
139
  if (useManualDeps && options?.dependencies) {
89
- adapterState$1 = initManualDepsState(instance);
90
- subscribeFn = createManualDepsSubscribe(instance, adapterState$1, { dependencies: options.dependencies });
91
- getSnapshotFn = createManualDepsSnapshot(instance, adapterState$1, { dependencies: 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 });
92
143
  } else if (!autoTrackEnabled) {
93
- adapterState$1 = initNoTrackState(instance);
94
- subscribeFn = createNoTrackSubscribe(instance);
95
- getSnapshotFn = createNoTrackSnapshot(instance);
144
+ adapterState$1 = noTrackInit(instance);
145
+ subscribeFn = noTrackSubscribe(instance);
146
+ getSnapshotFn = noTrackSnapshot(instance);
96
147
  } else {
97
- adapterState$1 = initAutoTrackState(instance);
98
- subscribeFn = createAutoTrackSubscribe(instance, adapterState$1);
99
- getSnapshotFn = createAutoTrackSnapshot(instance, adapterState$1);
148
+ adapterState$1 = autoTrackInit(instance);
149
+ subscribeFn = autoTrackSubscribe(instance, adapterState$1);
150
+ getSnapshotFn = autoTrackSnapshot(instance, adapterState$1);
100
151
  }
101
152
  return [
102
153
  adapterState$1.proxiedBloc,
@@ -106,23 +157,35 @@ function useBloc(BlocClass, options) {
106
157
  adapterState$1,
107
158
  instance
108
159
  ];
109
- }, [BlocClass, options?.instanceId]);
160
+ }, [
161
+ BlocClass,
162
+ isStateless,
163
+ isIsolated,
164
+ options?.instanceId
165
+ ]);
110
166
  const state = useSyncExternalStore(subscribe, getSnapshot);
111
167
  const [, forceUpdate] = useReducer((x) => x + 1, 0);
112
- const externalDepsManager = useRef(new ExternalDependencyManager());
168
+ const externalDepsManager = useRef(new ExternalDepsManager());
113
169
  useEffect(() => {
170
+ if (isStateless) return;
114
171
  if (options?.props !== initialPropsRef.current) rawInstance.updateProps(options?.props);
115
- }, [options?.props, rawInstance]);
172
+ }, [
173
+ options?.props,
174
+ rawInstance,
175
+ isStateless
176
+ ]);
116
177
  useEffect(() => {
178
+ if (isStateless || !adapterState) return;
117
179
  disableGetterTracking(adapterState, rawInstance);
118
- externalDepsManager.current.updateSubscriptions(adapterState.getterTracker, rawInstance, forceUpdate);
180
+ externalDepsManager.current.updateSubscriptions(adapterState.getterState, rawInstance, forceUpdate);
119
181
  });
120
182
  useEffect(() => {
183
+ if (isStateless) return;
121
184
  if (options?.onMount) options.onMount(bloc);
122
185
  return () => {
123
186
  externalDepsManager.current.cleanup();
124
187
  if (options?.onUnmount) options.onUnmount(bloc);
125
- BlocClass.release(instanceKey);
188
+ release(BlocClass, instanceKey);
126
189
  if (isIsolated && !rawInstance.isDisposed) rawInstance.dispose();
127
190
  };
128
191
  }, []);
@@ -139,17 +202,28 @@ function useBloc(BlocClass, options) {
139
202
  * React hook that connects to a state container instance without triggering re-renders.
140
203
  * Use this when you only need to call actions on the bloc without subscribing to state changes.
141
204
  *
142
- * @template S - Optional state type override for generic StateContainers (defaults to never)
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
+ *
143
208
  * @template T - The state container constructor type (inferred from BlocClass)
144
- * @param BlocClass - The state container class to connect to
209
+ * @param BlocClass - The state container class to connect to (supports both stateful and stateless)
145
210
  * @param options - Configuration options for instance management and lifecycle
146
211
  * @returns The state container instance for calling actions
147
212
  *
148
- * @example Basic usage
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)
149
224
  * ```ts
150
225
  * const myBloc = useBlocActions(MyBloc);
151
- * // Call methods on the bloc without re-rendering
152
- * myBloc.someMethod();
226
+ * myBloc.someMethod(); // Won't cause re-renders
153
227
  * ```
154
228
  *
155
229
  * @example With isolated instance
@@ -158,12 +232,6 @@ function useBloc(BlocClass, options) {
158
232
  * instanceId: 'unique-id'
159
233
  * });
160
234
  * ```
161
- *
162
- * @example With generic bloc and explicit state type
163
- * ```ts
164
- * const myBloc = useBlocActions<MyStateType>(GenericBloc);
165
- * // myBloc.state is typed as MyStateType
166
- * ```
167
235
  */
168
236
  function useBlocActions(BlocClass, options) {
169
237
  const componentRef = useRef({});
@@ -171,13 +239,13 @@ function useBlocActions(BlocClass, options) {
171
239
  const [bloc, instanceKey] = useMemo(() => {
172
240
  const isIsolated = isIsolatedClass(BlocClass);
173
241
  const instanceKey$1 = generateInstanceKey(componentRef.current, isIsolated, options?.instanceId);
174
- return [BlocClass.resolve(instanceKey$1, { props: initialPropsRef.current }), instanceKey$1];
242
+ return [acquire(BlocClass, instanceKey$1, { props: initialPropsRef.current }), instanceKey$1];
175
243
  }, [BlocClass]);
176
244
  useEffect(() => {
177
245
  if (options?.onMount) options.onMount(bloc);
178
246
  return () => {
179
247
  if (options?.onUnmount) options.onUnmount(bloc);
180
- BlocClass.release(instanceKey);
248
+ release(BlocClass, instanceKey);
181
249
  if (isIsolatedClass(BlocClass) && !bloc.isDisposed) bloc.dispose();
182
250
  };
183
251
  }, []);
@@ -185,5 +253,5 @@ function useBlocActions(BlocClass, options) {
185
253
  }
186
254
 
187
255
  //#endregion
188
- export { useBloc, useBlocActions };
256
+ export { configureBlacReact, resetBlacReactConfig, useBloc, useBlocActions };
189
257
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["instanceKey","subscribeFn: (callback: () => void) => () => void","getSnapshotFn: () => ExtractState<T>","adapterState: AdapterState<T>","adapterState","instanceKey"],"sources":["../src/utils/instance-keys.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","import {\n useMemo,\n useSyncExternalStore,\n useEffect,\n useRef,\n useReducer,\n} from 'react';\nimport {\n type ExtractState,\n type AdapterState,\n ExternalDependencyManager,\n createAutoTrackSubscribe,\n createManualDepsSubscribe,\n createNoTrackSubscribe,\n createAutoTrackSnapshot,\n createManualDepsSnapshot,\n createNoTrackSnapshot,\n initAutoTrackState,\n initManualDepsState,\n initNoTrackState,\n disableGetterTracking,\n isIsolatedClass,\n StateContainerConstructor,\n InstanceState,\n} from '@blac/core';\nimport type { UseBlocOptions, UseBlocReturn, ComponentRef } from './types';\nimport { generateInstanceKey } from './utils/instance-keys';\n\ninterface TrackingMode {\n useManualDeps: boolean;\n autoTrackEnabled: boolean;\n}\n\nfunction determineTrackingMode<TBloc extends StateContainerConstructor>(\n options?: UseBlocOptions<TBloc>,\n): TrackingMode {\n return {\n useManualDeps: options?.dependencies !== undefined,\n autoTrackEnabled: options?.autoTrack !== false,\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 S - Optional state type override for generic StateContainers (defaults to never)\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 *\n * @example With generic bloc and explicit state type\n * ```ts\n * const [state, myBloc] = useBloc<MyStateType>(GenericBloc);\n * // state is typed as MyStateType\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 const componentRef = useRef<ComponentRef>({});\n const isIsolated = isIsolatedClass(BlocClass);\n\n const initialPropsRef = useRef(options?.props);\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 = (BlocClass as any).resolve(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 = initManualDepsState(instance);\n subscribeFn = createManualDepsSubscribe(instance, adapterState, {\n dependencies: options.dependencies,\n });\n getSnapshotFn = createManualDepsSnapshot(instance, adapterState, {\n dependencies: options.dependencies,\n });\n } else if (!autoTrackEnabled) {\n adapterState = initNoTrackState(instance);\n subscribeFn = createNoTrackSubscribe(instance);\n getSnapshotFn = createNoTrackSnapshot(instance);\n } else {\n adapterState = initAutoTrackState(instance);\n subscribeFn = createAutoTrackSubscribe(instance, adapterState);\n getSnapshotFn = createAutoTrackSnapshot(instance, adapterState);\n }\n\n return [\n adapterState.proxiedBloc!,\n subscribeFn,\n getSnapshotFn,\n instanceKey,\n adapterState,\n instance,\n ];\n }, [BlocClass, 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 ExternalDependencyManager());\n\n useEffect(() => {\n if (options?.props !== initialPropsRef.current) {\n rawInstance.updateProps(options?.props);\n }\n }, [options?.props, rawInstance]);\n\n useEffect(() => {\n disableGetterTracking(adapterState, rawInstance);\n externalDepsManager.current.updateSubscriptions(\n adapterState.getterTracker,\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 (BlocClass as any).release(instanceKey);\n\n if (isIsolated && !rawInstance.isDisposed) {\n rawInstance.dispose();\n }\n };\n }, []);\n\n return [state, bloc, componentRef];\n}\n","import { useMemo, useEffect, useRef } from 'react';\nimport { StateContainerConstructor, isIsolatedClass } 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 * @template S - Optional state type override for generic StateContainers (defaults to never)\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 instance management and lifecycle\n * @returns The state container instance for calling actions\n *\n * @example Basic usage\n * ```ts\n * const myBloc = useBlocActions(MyBloc);\n * // Call methods on the bloc without re-rendering\n * myBloc.someMethod();\n * ```\n *\n * @example With isolated instance\n * ```ts\n * const myBloc = useBlocActions(MyBloc, {\n * instanceId: 'unique-id'\n * });\n * ```\n *\n * @example With generic bloc and explicit state type\n * ```ts\n * const myBloc = useBlocActions<MyStateType>(GenericBloc);\n * // myBloc.state is typed as MyStateType\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 = (BlocClass as any).resolve(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 (BlocClass as any).release(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":["defaultConfig: BlacReactConfig","globalConfig: BlacReactConfig","globalConfig","instance","instanceKey","subscribeFn: (callback: () => void) => () => void","getSnapshotFn: () => ExtractState<T>","adapterState: AdapterState<T>","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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blac/react",
3
- "version": "2.0.0-rc.15",
3
+ "version": "2.0.0-rc.17",
4
4
  "license": "MIT",
5
5
  "author": "Brendan Mullins <jsnanigans@gmail.com>",
6
6
  "repository": {
@@ -49,7 +49,7 @@
49
49
  "peerDependencies": {
50
50
  "@types/react": "^18.0.0 || ^19.0.0",
51
51
  "react": "^18.0.0 || ^19.0.0",
52
- "@blac/core": "2.0.0-rc.15"
52
+ "@blac/core": "2.0.0-rc.17"
53
53
  },
54
54
  "peerDependenciesMeta": {
55
55
  "@types/react": {
@@ -78,7 +78,7 @@
78
78
  "tsdown": "^0.15.7",
79
79
  "typescript": "^5.9.2",
80
80
  "vitest": "3.2.4",
81
- "@blac/core": "2.0.0-rc.15"
81
+ "@blac/core": "2.0.0-rc.17"
82
82
  },
83
83
  "scripts": {
84
84
  "dev": "tsdown --watch",