@blac/react 2.0.0-rn-12 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,260 @@
1
+ let react = require("react");
2
+ let _blac_core = require("@blac/core");
3
+
4
+ //#region src/utils/instance-keys.ts
5
+ /**
6
+ * Instance key generation utilities for React integration
7
+ */
8
+ /**
9
+ * Generate an instance key for a bloc
10
+ *
11
+ * Logic:
12
+ * - If user provides instanceId, use it (convert number to string)
13
+ * - If isolated, generate or reuse a unique key for this component
14
+ * - Otherwise, return undefined (use default key)
15
+ *
16
+ * @param componentRef - React component reference (persists across remounts)
17
+ * @param isIsolated - Whether the bloc is isolated
18
+ * @param providedId - User-provided instance ID (from options)
19
+ * @returns Instance key string or undefined for default
20
+ */
21
+ function generateInstanceKey(componentRef, isIsolated, providedId) {
22
+ if (providedId !== void 0) return typeof providedId === "number" ? String(providedId) : providedId;
23
+ if (isIsolated) {
24
+ if (!componentRef.__blocInstanceId) componentRef.__blocInstanceId = (0, _blac_core.generateIsolatedKey)();
25
+ return componentRef.__blocInstanceId;
26
+ }
27
+ }
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
+
69
+ //#endregion
70
+ //#region src/useBloc.ts
71
+ function determineTrackingMode(options) {
72
+ const globalConfig$1 = getBlacReactConfig();
73
+ const autoTrackEnabled = options?.autoTrack !== void 0 ? options.autoTrack : globalConfig$1.autoTrack;
74
+ return {
75
+ useManualDeps: options?.dependencies !== void 0,
76
+ autoTrackEnabled
77
+ };
78
+ }
79
+ /**
80
+ * React hook that connects a component to a state container with automatic re-render on state changes.
81
+ *
82
+ * Supports three tracking modes:
83
+ * - **Auto-tracking** (default): Automatically detects accessed state properties via Proxy
84
+ * - **Manual dependencies**: Explicit dependency array like useEffect
85
+ * - **No tracking**: Returns full state without optimization
86
+ *
87
+ * **Note:** This hook does NOT support stateless containers (StatelessCubit, StatelessVertex).
88
+ * Use `useBlocActions()` instead for stateless containers.
89
+ *
90
+ * @template T - The state container constructor type (inferred from BlocClass)
91
+ * @param BlocClass - The state container class to connect to (must not be stateless)
92
+ * @param options - Configuration options for tracking mode and instance management
93
+ * @returns Tuple with [state, bloc instance, ref]
94
+ *
95
+ * @example Basic usage
96
+ * ```ts
97
+ * const [state, myBloc, ref] = useBloc(MyBloc);
98
+ * ```
99
+ *
100
+ * @example With manual dependencies
101
+ * ```ts
102
+ * const [state, myBloc] = useBloc(MyBloc, {
103
+ * dependencies: (state) => [state.count]
104
+ * });
105
+ * ```
106
+ *
107
+ * @example With isolated instance
108
+ * ```ts
109
+ * const [state, myBloc] = useBloc(MyBloc, {
110
+ * instanceId: 'unique-id'
111
+ * });
112
+ * ```
113
+ */
114
+ function useBloc(BlocClass, options) {
115
+ 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);
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
+ }
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);
135
+ const { useManualDeps, autoTrackEnabled } = determineTrackingMode(options);
136
+ let subscribeFn;
137
+ let getSnapshotFn;
138
+ let adapterState$1;
139
+ 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 });
143
+ } 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);
147
+ } 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);
151
+ }
152
+ return [
153
+ adapterState$1.proxiedBloc,
154
+ subscribeFn,
155
+ getSnapshotFn,
156
+ instanceKey$1,
157
+ adapterState$1,
158
+ instance
159
+ ];
160
+ }, [
161
+ BlocClass,
162
+ isStateless,
163
+ isIsolated,
164
+ options?.instanceId
165
+ ]);
166
+ const state = (0, react.useSyncExternalStore)(subscribe, getSnapshot);
167
+ 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
+ ]);
177
+ (0, react.useEffect)(() => {
178
+ if (isStateless || !adapterState) return;
179
+ (0, _blac_core.disableGetterTracking)(adapterState, rawInstance);
180
+ externalDepsManager.current.updateSubscriptions(adapterState.getterState, rawInstance, forceUpdate);
181
+ });
182
+ (0, react.useEffect)(() => {
183
+ if (isStateless) return;
184
+ if (options?.onMount) options.onMount(bloc);
185
+ return () => {
186
+ externalDepsManager.current.cleanup();
187
+ if (options?.onUnmount) options.onUnmount(bloc);
188
+ (0, _blac_core.release)(BlocClass, instanceKey);
189
+ if (isIsolated && !rawInstance.isDisposed) rawInstance.dispose();
190
+ };
191
+ }, []);
192
+ return [
193
+ state,
194
+ bloc,
195
+ componentRef
196
+ ];
197
+ }
198
+
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
+ //#endregion
256
+ exports.configureBlacReact = configureBlacReact;
257
+ exports.resetBlacReactConfig = resetBlacReactConfig;
258
+ exports.useBloc = useBloc;
259
+ exports.useBlocActions = useBlocActions;
260
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +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":"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"}
@@ -0,0 +1,165 @@
1
+ import type { ExtractProps } from '@blac/core';
2
+ import { ExtractState } from '@blac/core';
3
+ import { InstanceReadonlyState } from '@blac/core';
4
+ import { IsStatelessContainer } from '@blac/core';
5
+ import type { RefObject } from 'react';
6
+ import { StateContainerConstructor } from '@blac/core';
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
+
16
+ /* Excluded from this release type: ComponentRef */
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
+
43
+ /**
44
+ * React hook that connects a component to a state container with automatic re-render on state changes.
45
+ *
46
+ * Supports three tracking modes:
47
+ * - **Auto-tracking** (default): Automatically detects accessed state properties via Proxy
48
+ * - **Manual dependencies**: Explicit dependency array like useEffect
49
+ * - **No tracking**: Returns full state without optimization
50
+ *
51
+ * **Note:** This hook does NOT support stateless containers (StatelessCubit, StatelessVertex).
52
+ * Use `useBlocActions()` instead for stateless containers.
53
+ *
54
+ * @template T - The state container constructor type (inferred from BlocClass)
55
+ * @param BlocClass - The state container class to connect to (must not be stateless)
56
+ * @param options - Configuration options for tracking mode and instance management
57
+ * @returns Tuple with [state, bloc instance, ref]
58
+ *
59
+ * @example Basic usage
60
+ * ```ts
61
+ * const [state, myBloc, ref] = useBloc(MyBloc);
62
+ * ```
63
+ *
64
+ * @example With manual dependencies
65
+ * ```ts
66
+ * const [state, myBloc] = useBloc(MyBloc, {
67
+ * dependencies: (state) => [state.count]
68
+ * });
69
+ * ```
70
+ *
71
+ * @example With isolated instance
72
+ * ```ts
73
+ * const [state, myBloc] = useBloc(MyBloc, {
74
+ * instanceId: 'unique-id'
75
+ * });
76
+ * ```
77
+ */
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
+ }
132
+
133
+ /**
134
+ * Configuration options for useBloc hook
135
+ * @template TBloc - The state container type
136
+ * @template TProps - Props type passed to the container
137
+ */
138
+ export declare interface UseBlocOptions<TBloc extends StateContainerConstructor, TProps = ExtractProps<TBloc>> {
139
+ /** Props passed to bloc constructor or updateProps */
140
+ props?: TProps;
141
+ /** Custom instance identifier for shared or isolated instances */
142
+ instanceId?: string | number;
143
+ /** Manual dependency array like useEffect (disables autoTrack) */
144
+ dependencies?: (state: ExtractState<TBloc>, bloc: InstanceReadonlyState<TBloc>) => unknown[];
145
+ /** Enable automatic property tracking via Proxy (default: true) */
146
+ autoTrack?: boolean;
147
+ /** Disable caching for getter tracking */
148
+ disableGetterCache?: boolean;
149
+ /** Callback invoked when bloc instance mounts */
150
+ onMount?: (bloc: InstanceType<TBloc>) => void;
151
+ /** Callback invoked when bloc instance unmounts */
152
+ onUnmount?: (bloc: InstanceType<TBloc>) => void;
153
+ }
154
+
155
+ /**
156
+ * Tuple return type from useBloc hook containing state, bloc instance, and ref
157
+ * - [0] Current state value (with optional state type override)
158
+ * - [1] State container instance (bloc) for calling actions
159
+ * - [2] Ref object for accessing component ref (advanced use cases)
160
+ *
161
+ * @template TBloc - The state container type
162
+ */
163
+ export declare type UseBlocReturn<TBloc extends StateContainerConstructor, S = ExtractState<TBloc>> = [S, InstanceReadonlyState<TBloc>, RefObject<ComponentRef>];
164
+
165
+ export { }
package/dist/index.d.ts CHANGED
@@ -1,59 +1,165 @@
1
- import { BlocBase } from '@blac/core';
2
- import { BlocConstructor } from '@blac/core';
3
- import { BlocState } from '@blac/core';
4
- import { InferPropsFromGeneric } from '@blac/core';
5
-
6
- declare interface ExternalBlocStoreResult<B extends BlocBase<any>> {
7
- externalStore: ExternalStore<BlocState<B>>;
8
- instance: {
9
- current: B | null;
10
- };
11
- usedKeys: {
12
- current: Set<string>;
13
- };
14
- usedClassPropKeys: {
15
- current: Set<string>;
16
- };
17
- rid: string;
18
- }
1
+ import type { ExtractProps } from '@blac/core';
2
+ import { ExtractState } from '@blac/core';
3
+ import { InstanceReadonlyState } from '@blac/core';
4
+ import { IsStatelessContainer } from '@blac/core';
5
+ import type { RefObject } from 'react';
6
+ import { StateContainerConstructor } from '@blac/core';
19
7
 
20
- declare interface ExternalStore<T> {
21
- getSnapshot: () => T | undefined;
22
- subscribe: (listener: () => void) => () => void;
23
- getServerSnapshot?: () => T | undefined;
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;
24
14
  }
25
15
 
26
- declare interface ExternalStoreOptions<B extends BlocBase<any>> {
27
- id?: string;
28
- props?: InferPropsFromGeneric<B>;
29
- selector?: (currentState: BlocState<B>, previousState: BlocState<B>, instance: B) => any[];
30
- }
16
+ /* Excluded from this release type: ComponentRef */
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 */
31
36
 
32
37
  /**
33
- * Type definition for the return type of the useBloc hook
38
+ * Type that produces a TypeScript error when a stateless container is used.
39
+ * Evaluates to `never` for stateless containers, causing a type mismatch.
34
40
  */
35
- declare type HookTypes<B extends BlocConstructor<BlocBase<any>>> = [
36
- BlocState<InstanceType<B>>,
37
- InstanceType<B>
38
- ];
41
+ declare type StatefulContainer<T extends StateContainerConstructor> = IsStatelessContainer<T> extends true ? never : T;
39
42
 
40
43
  /**
41
- * React hook for integrating with Blac state management
44
+ * React hook that connects a component to a state container with automatic re-render on state changes.
45
+ *
46
+ * Supports three tracking modes:
47
+ * - **Auto-tracking** (default): Automatically detects accessed state properties via Proxy
48
+ * - **Manual dependencies**: Explicit dependency array like useEffect
49
+ * - **No tracking**: Returns full state without optimization
50
+ *
51
+ * **Note:** This hook does NOT support stateless containers (StatelessCubit, StatelessVertex).
52
+ * Use `useBlocActions()` instead for stateless containers.
53
+ *
54
+ * @template T - The state container constructor type (inferred from BlocClass)
55
+ * @param BlocClass - The state container class to connect to (must not be stateless)
56
+ * @param options - Configuration options for tracking mode and instance management
57
+ * @returns Tuple with [state, bloc instance, ref]
58
+ *
59
+ * @example Basic usage
60
+ * ```ts
61
+ * const [state, myBloc, ref] = useBloc(MyBloc);
62
+ * ```
63
+ *
64
+ * @example With manual dependencies
65
+ * ```ts
66
+ * const [state, myBloc] = useBloc(MyBloc, {
67
+ * dependencies: (state) => [state.count]
68
+ * });
69
+ * ```
70
+ *
71
+ * @example With isolated instance
72
+ * ```ts
73
+ * const [state, myBloc] = useBloc(MyBloc, {
74
+ * instanceId: 'unique-id'
75
+ * });
76
+ * ```
42
77
  */
43
- declare function useBloc<B extends BlocConstructor<BlocBase<any>>>(blocConstructor: B, options?: {
44
- props?: ConstructorParameters<B>[0];
45
- id?: string;
46
- dependencies?: (bloc: InstanceType<B>) => unknown[];
47
- onMount?: (bloc: InstanceType<B>) => void;
48
- onUnmount?: (bloc: InstanceType<B>) => void;
49
- }): HookTypes<B>;
50
- export { useBloc }
51
- export { useBloc as useBlocNext }
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
+ }
132
+
133
+ /**
134
+ * Configuration options for useBloc hook
135
+ * @template TBloc - The state container type
136
+ * @template TProps - Props type passed to the container
137
+ */
138
+ export declare interface UseBlocOptions<TBloc extends StateContainerConstructor, TProps = ExtractProps<TBloc>> {
139
+ /** Props passed to bloc constructor or updateProps */
140
+ props?: TProps;
141
+ /** Custom instance identifier for shared or isolated instances */
142
+ instanceId?: string | number;
143
+ /** Manual dependency array like useEffect (disables autoTrack) */
144
+ dependencies?: (state: ExtractState<TBloc>, bloc: InstanceReadonlyState<TBloc>) => unknown[];
145
+ /** Enable automatic property tracking via Proxy (default: true) */
146
+ autoTrack?: boolean;
147
+ /** Disable caching for getter tracking */
148
+ disableGetterCache?: boolean;
149
+ /** Callback invoked when bloc instance mounts */
150
+ onMount?: (bloc: InstanceType<TBloc>) => void;
151
+ /** Callback invoked when bloc instance unmounts */
152
+ onUnmount?: (bloc: InstanceType<TBloc>) => void;
153
+ }
52
154
 
53
155
  /**
54
- * Hook for using an external bloc store
55
- * Provides low-level access to bloc instances for use with React's useSyncExternalStore
156
+ * Tuple return type from useBloc hook containing state, bloc instance, and ref
157
+ * - [0] Current state value (with optional state type override)
158
+ * - [1] State container instance (bloc) for calling actions
159
+ * - [2] Ref object for accessing component ref (advanced use cases)
160
+ *
161
+ * @template TBloc - The state container type
56
162
  */
57
- export declare function useExternalBlocStore<B extends BlocConstructor<BlocBase<any>>>(blocConstructor: B, options?: ExternalStoreOptions<InstanceType<B>>): ExternalBlocStoreResult<InstanceType<B>>;
163
+ export declare type UseBlocReturn<TBloc extends StateContainerConstructor, S = ExtractState<TBloc>> = [S, InstanceReadonlyState<TBloc>, RefObject<ComponentRef>];
58
164
 
59
165
  export { }