@blac/react 2.0.0-rc.5 → 2.0.0-rc.8
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 +48 -312
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -450
- package/dist/index.d.ts +8 -449
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +48 -287
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +25 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/useBloc.d.ts +21 -0
- package/dist/useBloc.d.ts.map +1 -0
- package/dist/useBlocActions.d.ts +9 -0
- package/dist/useBlocActions.d.ts.map +1 -0
- package/dist/utils/instance-keys.d.ts +19 -0
- package/dist/utils/instance-keys.d.ts.map +1 -0
- package/package.json +19 -6
- package/dist/.tsbuildinfo +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,197 +1,33 @@
|
|
|
1
|
-
//#region rolldown:runtime
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __copyProps = (to, from, except, desc) => {
|
|
9
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
-
key = keys[i];
|
|
11
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
-
get: ((k) => from[k]).bind(null, key),
|
|
13
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
-
value: mod,
|
|
20
|
-
enumerable: true
|
|
21
|
-
}) : target, mod));
|
|
22
|
-
|
|
23
|
-
//#endregion
|
|
24
1
|
let react = require("react");
|
|
25
|
-
react = __toESM(react);
|
|
26
2
|
let __blac_core = require("@blac/core");
|
|
27
|
-
__blac_core = __toESM(__blac_core);
|
|
28
3
|
|
|
29
|
-
//#region src/
|
|
30
|
-
/**
|
|
31
|
-
* Cache for property descriptors to avoid repeated prototype chain walks
|
|
32
|
-
* Maps from object constructor to a map of property name to descriptor
|
|
33
|
-
*/
|
|
34
|
-
const descriptorCache = /* @__PURE__ */ new WeakMap();
|
|
35
|
-
/**
|
|
36
|
-
* Cache for proxied blocs to ensure same proxy is returned for same bloc instance
|
|
37
|
-
* This is important for identity checks (e.g., bloc1 === bloc2) across components
|
|
38
|
-
* using the same shared bloc instance.
|
|
39
|
-
*/
|
|
40
|
-
const blocProxyCache = /* @__PURE__ */ new WeakMap();
|
|
41
|
-
/**
|
|
42
|
-
* Map to store the currently active tracker during render.
|
|
43
|
-
* This allows the cached proxy to know which component's tracker to use.
|
|
44
|
-
* Set before render, cleared after render.
|
|
45
|
-
*/
|
|
46
|
-
const activeTrackerMap = /* @__PURE__ */ new WeakMap();
|
|
47
|
-
/**
|
|
48
|
-
* Get property descriptor for a given property, with caching
|
|
49
|
-
*
|
|
50
|
-
* @remarks
|
|
51
|
-
* Walks up the prototype chain once per class to find the descriptor,
|
|
52
|
-
* then caches the result for performance. This is critical because
|
|
53
|
-
* we need to distinguish getters from methods and properties.
|
|
54
|
-
*
|
|
55
|
-
* @param obj - The object to get the descriptor from
|
|
56
|
-
* @param prop - The property name or symbol
|
|
57
|
-
* @returns The property descriptor if found, undefined otherwise
|
|
58
|
-
*/
|
|
59
|
-
function getDescriptor(obj, prop) {
|
|
60
|
-
const constructor = obj.constructor;
|
|
61
|
-
let constructorCache = descriptorCache.get(constructor);
|
|
62
|
-
if (constructorCache?.has(prop)) return constructorCache.get(prop);
|
|
63
|
-
let current = obj;
|
|
64
|
-
let descriptor;
|
|
65
|
-
while (current && current !== Object.prototype) {
|
|
66
|
-
descriptor = Object.getOwnPropertyDescriptor(current, prop);
|
|
67
|
-
if (descriptor) break;
|
|
68
|
-
current = Object.getPrototypeOf(current);
|
|
69
|
-
}
|
|
70
|
-
if (!constructorCache) {
|
|
71
|
-
constructorCache = /* @__PURE__ */ new Map();
|
|
72
|
-
descriptorCache.set(constructor, constructorCache);
|
|
73
|
-
}
|
|
74
|
-
constructorCache.set(prop, descriptor);
|
|
75
|
-
return descriptor;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Check if a property is a getter (has a getter descriptor)
|
|
79
|
-
*
|
|
80
|
-
* @param obj - The object to check
|
|
81
|
-
* @param prop - The property name or symbol
|
|
82
|
-
* @returns True if the property is a getter, false otherwise
|
|
83
|
-
*/
|
|
84
|
-
function isGetter(obj, prop) {
|
|
85
|
-
return getDescriptor(obj, prop)?.get !== void 0;
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Create a new getter tracking state
|
|
89
|
-
*
|
|
90
|
-
* @returns A new GetterTrackingState initialized with empty collections
|
|
91
|
-
*/
|
|
92
|
-
function createGetterTracker() {
|
|
93
|
-
return {
|
|
94
|
-
trackedValues: /* @__PURE__ */ new Map(),
|
|
95
|
-
currentlyAccessing: /* @__PURE__ */ new Set(),
|
|
96
|
-
trackedGetters: /* @__PURE__ */ new Set(),
|
|
97
|
-
isTracking: false,
|
|
98
|
-
renderCache: /* @__PURE__ */ new Map(),
|
|
99
|
-
cacheValid: false
|
|
100
|
-
};
|
|
101
|
-
}
|
|
4
|
+
//#region src/utils/instance-keys.ts
|
|
102
5
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* @remarks
|
|
106
|
-
* This proxy wraps the bloc instance to track which getters are accessed
|
|
107
|
-
* during component render. When tracking is enabled (during render phase),
|
|
108
|
-
* it records accessed getters and stores their computed values for later
|
|
109
|
-
* comparison.
|
|
110
|
-
*
|
|
111
|
-
* IMPORTANT: This function caches proxies per bloc instance. Multiple components
|
|
112
|
-
* sharing the same bloc will get the same proxy instance. Each component sets
|
|
113
|
-
* its tracker in activeTrackerMap before render, and the proxy looks it up.
|
|
114
|
-
*
|
|
115
|
-
* @param bloc - The bloc instance to wrap
|
|
116
|
-
* @returns A proxied bloc that tracks getter access
|
|
6
|
+
* Instance key generation utilities for React integration
|
|
117
7
|
*/
|
|
118
|
-
function createBlocProxy(bloc) {
|
|
119
|
-
const cached = blocProxyCache.get(bloc);
|
|
120
|
-
if (cached) return cached;
|
|
121
|
-
const proxy = new Proxy(bloc, { get(target, prop, receiver) {
|
|
122
|
-
const tracker = activeTrackerMap.get(target);
|
|
123
|
-
if (tracker?.isTracking && isGetter(target, prop)) {
|
|
124
|
-
tracker.currentlyAccessing.add(prop);
|
|
125
|
-
if (tracker.cacheValid && tracker.renderCache.has(prop)) {
|
|
126
|
-
const cachedValue = tracker.renderCache.get(prop);
|
|
127
|
-
tracker.trackedValues.set(prop, cachedValue);
|
|
128
|
-
return cachedValue;
|
|
129
|
-
}
|
|
130
|
-
const value = getDescriptor(target, prop).get.call(target);
|
|
131
|
-
tracker.trackedValues.set(prop, value);
|
|
132
|
-
return value;
|
|
133
|
-
}
|
|
134
|
-
return Reflect.get(target, prop, receiver);
|
|
135
|
-
} });
|
|
136
|
-
blocProxyCache.set(bloc, proxy);
|
|
137
|
-
return proxy;
|
|
138
|
-
}
|
|
139
8
|
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* @remarks
|
|
143
|
-
* Re-computes all getters that were accessed during the last render and
|
|
144
|
-
* compares their new values with stored values using Object.is() (reference
|
|
145
|
-
* equality).
|
|
9
|
+
* Generate an instance key for a bloc
|
|
146
10
|
*
|
|
147
|
-
*
|
|
148
|
-
* -
|
|
149
|
-
* -
|
|
150
|
-
* -
|
|
151
|
-
* - Cache is populated even if no changes detected (useful for parent-triggered re-renders)
|
|
152
|
-
* - Trade-off: Give up early exit to ensure full cache population
|
|
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)
|
|
153
15
|
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
* @param bloc - The bloc instance
|
|
160
|
-
* @param tracker - The getter tracking state
|
|
161
|
-
* @returns True if any tracked getter value changed, false otherwise
|
|
162
|
-
*/
|
|
163
|
-
function hasGetterChanges(bloc, tracker) {
|
|
164
|
-
if (!tracker || tracker.trackedGetters.size === 0) return false;
|
|
165
|
-
tracker.renderCache.clear();
|
|
166
|
-
let hasAnyChange = false;
|
|
167
|
-
for (const prop of tracker.trackedGetters) try {
|
|
168
|
-
const descriptor = getDescriptor(bloc, prop);
|
|
169
|
-
if (!descriptor?.get) continue;
|
|
170
|
-
const newValue = descriptor.get.call(bloc);
|
|
171
|
-
const oldValue = tracker.trackedValues.get(prop);
|
|
172
|
-
tracker.renderCache.set(prop, newValue);
|
|
173
|
-
tracker.trackedValues.set(prop, newValue);
|
|
174
|
-
if (!Object.is(newValue, oldValue)) hasAnyChange = true;
|
|
175
|
-
} catch (error) {
|
|
176
|
-
console.warn(`[useBloc] Getter "${String(prop)}" threw error during change detection. Stopping tracking for this getter.`, error);
|
|
177
|
-
tracker.trackedGetters.delete(prop);
|
|
178
|
-
tracker.trackedValues.delete(prop);
|
|
179
|
-
tracker.cacheValid = false;
|
|
180
|
-
return true;
|
|
181
|
-
}
|
|
182
|
-
tracker.cacheValid = true;
|
|
183
|
-
return hasAnyChange;
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Generates instance ID for isolated blocs
|
|
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
|
|
187
20
|
*/
|
|
188
|
-
function
|
|
189
|
-
if (providedId) return providedId;
|
|
21
|
+
function generateInstanceKey(componentRef, isIsolated, providedId) {
|
|
22
|
+
if (providedId !== void 0) return typeof providedId === "number" ? String(providedId) : providedId;
|
|
190
23
|
if (isIsolated) {
|
|
191
|
-
if (!componentRef.__blocInstanceId) componentRef.__blocInstanceId =
|
|
24
|
+
if (!componentRef.__blocInstanceId) componentRef.__blocInstanceId = (0, __blac_core.generateIsolatedKey)();
|
|
192
25
|
return componentRef.__blocInstanceId;
|
|
193
26
|
}
|
|
194
27
|
}
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/useBloc.ts
|
|
195
31
|
function determineTrackingMode(options) {
|
|
196
32
|
return {
|
|
197
33
|
useManualDeps: options?.dependencies !== void 0,
|
|
@@ -199,75 +35,6 @@ function determineTrackingMode(options) {
|
|
|
199
35
|
};
|
|
200
36
|
}
|
|
201
37
|
/**
|
|
202
|
-
* Factory: Creates subscribe function for automatic proxy tracking mode
|
|
203
|
-
*/
|
|
204
|
-
function createAutoTrackSubscribe(instance, hookState) {
|
|
205
|
-
return (callback) => {
|
|
206
|
-
return instance.subscribe(() => {
|
|
207
|
-
const tracker = hookState.tracker || (hookState.tracker = (0, __blac_core.createTrackerState)());
|
|
208
|
-
let stateChanged = (0, __blac_core.hasChanges)(tracker, instance.state);
|
|
209
|
-
if (tracker.pathCache.size === 0 && hookState.getterTracker && hookState.getterTracker.trackedGetters.size > 0) stateChanged = false;
|
|
210
|
-
if (stateChanged) {
|
|
211
|
-
callback();
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
if (hasGetterChanges(instance, hookState.getterTracker)) callback();
|
|
215
|
-
});
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Factory: Creates subscribe function for manual dependencies mode
|
|
220
|
-
*/
|
|
221
|
-
function createManualDepsSubscribe(instance, hookState, options) {
|
|
222
|
-
return (callback) => {
|
|
223
|
-
return instance.subscribe(() => {
|
|
224
|
-
const newDeps = options.dependencies(instance.state, instance);
|
|
225
|
-
if (!hookState.manualDepsCache || !(0, __blac_core.shallowEqual)(hookState.manualDepsCache, newDeps)) {
|
|
226
|
-
hookState.manualDepsCache = newDeps;
|
|
227
|
-
callback();
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Factory: Creates subscribe function for no-tracking mode
|
|
234
|
-
*/
|
|
235
|
-
function createNoTrackSubscribe(instance) {
|
|
236
|
-
return (callback) => instance.subscribe(callback);
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* Factory: Creates getSnapshot function for automatic proxy tracking mode
|
|
240
|
-
*/
|
|
241
|
-
function createAutoTrackSnapshot(instance, hookState) {
|
|
242
|
-
return () => {
|
|
243
|
-
const tracker = hookState.tracker || (hookState.tracker = (0, __blac_core.createTrackerState)());
|
|
244
|
-
if ((0, __blac_core.hasTrackedData)(tracker)) (0, __blac_core.captureTrackedPaths)(tracker, instance.state);
|
|
245
|
-
if (hookState.getterTracker) {
|
|
246
|
-
if (hookState.getterTracker.currentlyAccessing.size > 0) hookState.getterTracker.trackedGetters = new Set(hookState.getterTracker.currentlyAccessing);
|
|
247
|
-
hookState.getterTracker.currentlyAccessing.clear();
|
|
248
|
-
hookState.getterTracker.isTracking = true;
|
|
249
|
-
activeTrackerMap.set(instance, hookState.getterTracker);
|
|
250
|
-
}
|
|
251
|
-
(0, __blac_core.startTracking)(tracker);
|
|
252
|
-
return (0, __blac_core.createProxy)(tracker, instance.state);
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
/**
|
|
256
|
-
* Factory: Creates getSnapshot function for manual dependencies mode
|
|
257
|
-
*/
|
|
258
|
-
function createManualDepsSnapshot(instance, hookState, options) {
|
|
259
|
-
return () => {
|
|
260
|
-
hookState.manualDepsCache = options.dependencies(instance.state, instance);
|
|
261
|
-
return instance.state;
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
/**
|
|
265
|
-
* Factory: Creates getSnapshot function for no-tracking mode
|
|
266
|
-
*/
|
|
267
|
-
function createNoTrackSnapshot(instance) {
|
|
268
|
-
return () => instance.state;
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
38
|
* Lifecycle: INITIAL MOUNT
|
|
272
39
|
* 1. useMemo runs once - creates bloc, subscribeFn, getSnapshotFn
|
|
273
40
|
* 2. useSyncExternalStore calls getSnapshotFn (1st time) - lazy creates tracker, starts tracking, returns proxy
|
|
@@ -286,57 +53,51 @@ function createNoTrackSnapshot(instance) {
|
|
|
286
53
|
*/
|
|
287
54
|
function useBloc(BlocClass, options) {
|
|
288
55
|
const componentRef = (0, react.useRef)({});
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const
|
|
293
|
-
const instance =
|
|
56
|
+
const Constructor = BlocClass;
|
|
57
|
+
const isIsolated = (0, __blac_core.isIsolatedClass)(BlocClass);
|
|
58
|
+
const [bloc, subscribe, getSnapshot, instanceKey, adapterState, rawInstance] = (0, react.useMemo)(() => {
|
|
59
|
+
const instanceKey$1 = generateInstanceKey(componentRef.current, isIsolated, options?.instanceId);
|
|
60
|
+
const instance = BlocClass.resolve(instanceKey$1, options?.staticProps);
|
|
294
61
|
const { useManualDeps, autoTrackEnabled } = determineTrackingMode(options);
|
|
295
|
-
const hookState$1 = {
|
|
296
|
-
tracker: null,
|
|
297
|
-
manualDepsCache: null,
|
|
298
|
-
getterTracker: null,
|
|
299
|
-
proxiedBloc: null
|
|
300
|
-
};
|
|
301
62
|
let subscribeFn;
|
|
302
63
|
let getSnapshotFn;
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
64
|
+
let adapterState$1;
|
|
65
|
+
if (useManualDeps && options?.dependencies) {
|
|
66
|
+
adapterState$1 = (0, __blac_core.initManualDepsState)(instance);
|
|
67
|
+
subscribeFn = (0, __blac_core.createManualDepsSubscribe)(instance, adapterState$1, { dependencies: options.dependencies });
|
|
68
|
+
getSnapshotFn = (0, __blac_core.createManualDepsSnapshot)(instance, adapterState$1, { dependencies: options.dependencies });
|
|
307
69
|
} else if (!autoTrackEnabled) {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
70
|
+
adapterState$1 = (0, __blac_core.initNoTrackState)(instance);
|
|
71
|
+
subscribeFn = (0, __blac_core.createNoTrackSubscribe)(instance);
|
|
72
|
+
getSnapshotFn = (0, __blac_core.createNoTrackSnapshot)(instance);
|
|
311
73
|
} else {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
hookState$1.proxiedBloc = createBlocProxy(instance);
|
|
74
|
+
adapterState$1 = (0, __blac_core.initAutoTrackState)(instance);
|
|
75
|
+
subscribeFn = (0, __blac_core.createAutoTrackSubscribe)(instance, adapterState$1);
|
|
76
|
+
getSnapshotFn = (0, __blac_core.createAutoTrackSnapshot)(instance, adapterState$1);
|
|
316
77
|
}
|
|
317
78
|
return [
|
|
318
|
-
|
|
79
|
+
adapterState$1.proxiedBloc,
|
|
319
80
|
subscribeFn,
|
|
320
81
|
getSnapshotFn,
|
|
321
|
-
|
|
322
|
-
|
|
82
|
+
instanceKey$1,
|
|
83
|
+
adapterState$1,
|
|
323
84
|
instance
|
|
324
85
|
];
|
|
325
|
-
}, [BlocClass]);
|
|
86
|
+
}, [BlocClass, options?.instanceId]);
|
|
326
87
|
const state = (0, react.useSyncExternalStore)(subscribe, getSnapshot);
|
|
88
|
+
const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
|
|
89
|
+
const externalDepsManager = (0, react.useRef)(new __blac_core.ExternalDependencyManager());
|
|
327
90
|
(0, react.useEffect)(() => {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
hookState.getterTracker.cacheValid = false;
|
|
331
|
-
activeTrackerMap.delete(rawInstance);
|
|
332
|
-
}
|
|
91
|
+
(0, __blac_core.disableGetterTracking)(adapterState, rawInstance);
|
|
92
|
+
externalDepsManager.current.updateSubscriptions(adapterState.getterTracker, rawInstance, forceUpdate);
|
|
333
93
|
});
|
|
334
94
|
(0, react.useEffect)(() => {
|
|
335
95
|
if (options?.onMount) options.onMount(bloc);
|
|
336
96
|
return () => {
|
|
97
|
+
externalDepsManager.current.cleanup();
|
|
337
98
|
if (options?.onUnmount) options.onUnmount(bloc);
|
|
338
|
-
|
|
339
|
-
if (
|
|
99
|
+
Constructor.release(instanceKey);
|
|
100
|
+
if (isIsolated && !rawInstance.isDisposed) rawInstance.dispose();
|
|
340
101
|
};
|
|
341
102
|
}, []);
|
|
342
103
|
return [
|
|
@@ -348,45 +109,20 @@ function useBloc(BlocClass, options) {
|
|
|
348
109
|
|
|
349
110
|
//#endregion
|
|
350
111
|
//#region src/useBlocActions.ts
|
|
351
|
-
/**
|
|
352
|
-
* Generates instance ID for isolated blocs
|
|
353
|
-
*/
|
|
354
|
-
function generateInstanceId(componentRef, isIsolated, providedId) {
|
|
355
|
-
if (providedId) return providedId;
|
|
356
|
-
if (isIsolated) {
|
|
357
|
-
if (!componentRef.__blocInstanceId) componentRef.__blocInstanceId = `isolated-${Math.random().toString(36).slice(2, 11)}`;
|
|
358
|
-
return componentRef.__blocInstanceId;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
/**
|
|
362
|
-
* React hook for accessing bloc instance without state subscription.
|
|
363
|
-
* Use this when you only need to call bloc methods/actions without reading state.
|
|
364
|
-
*
|
|
365
|
-
* Benefits over useBloc:
|
|
366
|
-
* - No state subscription overhead
|
|
367
|
-
* - No proxy tracking
|
|
368
|
-
* - Component never re-renders due to bloc state changes
|
|
369
|
-
* - Lighter weight for action-only components
|
|
370
|
-
*
|
|
371
|
-
* @template TBloc - The StateContainer type
|
|
372
|
-
* @param BlocClass - The bloc class constructor
|
|
373
|
-
* @param options - Optional configuration
|
|
374
|
-
* @returns The bloc instance
|
|
375
|
-
*/
|
|
376
112
|
function useBlocActions(BlocClass, options) {
|
|
377
113
|
const componentRef = (0, react.useRef)({});
|
|
378
114
|
const [bloc, instanceKey] = (0, react.useMemo)(() => {
|
|
379
|
-
const isIsolated = BlocClass
|
|
115
|
+
const isIsolated = (0, __blac_core.isIsolatedClass)(BlocClass);
|
|
380
116
|
const Constructor = BlocClass;
|
|
381
|
-
const
|
|
382
|
-
return [Constructor.
|
|
117
|
+
const instanceKey$1 = generateInstanceKey(componentRef.current, isIsolated, options?.instanceId);
|
|
118
|
+
return [options?.staticProps ? Constructor.resolve(instanceKey$1, options.staticProps) : Constructor.resolve(instanceKey$1), instanceKey$1];
|
|
383
119
|
}, [BlocClass]);
|
|
384
120
|
(0, react.useEffect)(() => {
|
|
385
121
|
if (options?.onMount) options.onMount(bloc);
|
|
386
122
|
return () => {
|
|
387
123
|
if (options?.onUnmount) options.onUnmount(bloc);
|
|
388
124
|
BlocClass.release(instanceKey);
|
|
389
|
-
if (BlocClass
|
|
125
|
+
if ((0, __blac_core.isIsolatedClass)(BlocClass) && !bloc.isDisposed) bloc.dispose();
|
|
390
126
|
};
|
|
391
127
|
}, []);
|
|
392
128
|
return bloc;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["descriptor: PropertyDescriptor | undefined","generateInstanceId","hookState: HookState<TBloc>","subscribeFn: (callback: () => void) => () => void","getSnapshotFn: () => ExtractState<TBloc>","hookState"],"sources":["../src/useBloc.ts","../src/useBlocActions.ts"],"sourcesContent":["/**\n * useBloc - hook for BlaC state management in React with automatic proxy tracking\n *\n * @example\n * ```tsx\n * // Basic usage - automatic tracking of accessed properties\n * function Counter() {\n * const [state, bloc] = useBloc(CounterBloc);\n * return (\n * <div>\n * <p>Count: {state.count}</p> // Only re-renders when count changes\n * <button onClick={bloc.increment}>+</button>\n * </div>\n * );\n * }\n * ```\n */\n\nimport { useMemo, useSyncExternalStore, useEffect, useRef } from 'react';\nimport {\n type AnyObject,\n type BlocConstructor,\n StateContainer,\n type ExtractState,\n // Import tracking utilities from core\n type TrackerState,\n createTrackerState,\n startTracking,\n createProxy,\n captureTrackedPaths,\n hasChanges,\n hasTrackedData,\n shallowEqual,\n} from '@blac/core';\nimport type { UseBlocOptions, UseBlocReturn, ComponentRef } from './types';\n\n/**\n * StateContainer constructor with required static methods\n */\ntype StateContainerConstructor<TBloc extends StateContainer<any>> =\n BlocConstructor<TBloc> & {\n getOrCreate(instanceKey?: string, ...args: any[]): TBloc;\n release(instanceKey?: string): void;\n };\n\n/**\n * State for tracking getter access and values during render\n *\n * @remarks\n * This tracks which getters are accessed during component render and stores\n * their computed values for comparison on subsequent state changes.\n * Only getters (properties with a getter descriptor) are tracked automatically.\n *\n * Similar to state tracking, we use two sets:\n * - `currentlyAccessing`: Temporary set for getters accessed during current render\n * - `trackedGetters`: Committed set of getters from last completed render\n *\n * PERFORMANCE: Render cache optimization\n * - When checking if we should re-render (hasGetterChanges), we compute all getters\n * - Store these computed values in `renderCache` for the upcoming render\n * - During render, if we access a cached getter, use the cached value instead of recomputing\n * - This ensures each getter is computed at most once per render cycle\n * - Cache is invalidated after render completes or when state changes again\n */\ninterface GetterTrackingState {\n /** Map of getter names to their last computed values (for comparison) */\n trackedValues: Map<string | symbol, unknown>;\n /** Temporary set of getters being accessed during current render */\n currentlyAccessing: Set<string | symbol>;\n /** Committed set of getters from last completed render (used for change detection) */\n trackedGetters: Set<string | symbol>;\n /** Flag to enable/disable tracking (only enabled during render phase) */\n isTracking: boolean;\n /** Cache of getter values computed during hasGetterChanges (valid for current render cycle) */\n renderCache: Map<string | symbol, unknown>;\n /** Flag indicating render cache is valid and can be used */\n cacheValid: boolean;\n}\n\n/**\n * Cache for property descriptors to avoid repeated prototype chain walks\n * Maps from object constructor to a map of property name to descriptor\n */\nconst descriptorCache = new WeakMap<\n Function,\n Map<string | symbol, PropertyDescriptor | undefined>\n>();\n\n/**\n * Cache for proxied blocs to ensure same proxy is returned for same bloc instance\n * This is important for identity checks (e.g., bloc1 === bloc2) across components\n * using the same shared bloc instance.\n */\nconst blocProxyCache = new WeakMap<StateContainer<any>, any>();\n\n/**\n * Map to store the currently active tracker during render.\n * This allows the cached proxy to know which component's tracker to use.\n * Set before render, cleared after render.\n */\nconst activeTrackerMap = new WeakMap<\n StateContainer<any>,\n GetterTrackingState\n>();\n\n/**\n * Get property descriptor for a given property, with caching\n *\n * @remarks\n * Walks up the prototype chain once per class to find the descriptor,\n * then caches the result for performance. This is critical because\n * we need to distinguish getters from methods and properties.\n *\n * @param obj - The object to get the descriptor from\n * @param prop - The property name or symbol\n * @returns The property descriptor if found, undefined otherwise\n */\nfunction getDescriptor(\n obj: any,\n prop: string | symbol,\n): PropertyDescriptor | undefined {\n const constructor = obj.constructor;\n\n // Try to get from cache\n let constructorCache = descriptorCache.get(constructor);\n if (constructorCache?.has(prop)) {\n return constructorCache.get(prop);\n }\n\n // Walk prototype chain to find descriptor\n let current = obj;\n let descriptor: PropertyDescriptor | undefined;\n\n while (current && current !== Object.prototype) {\n descriptor = Object.getOwnPropertyDescriptor(current, prop);\n if (descriptor) {\n break;\n }\n current = Object.getPrototypeOf(current);\n }\n\n // Cache the result\n if (!constructorCache) {\n constructorCache = new Map();\n descriptorCache.set(constructor, constructorCache);\n }\n constructorCache.set(prop, descriptor);\n\n return descriptor;\n}\n\n/**\n * Check if a property is a getter (has a getter descriptor)\n *\n * @param obj - The object to check\n * @param prop - The property name or symbol\n * @returns True if the property is a getter, false otherwise\n */\nfunction isGetter(obj: any, prop: string | symbol): boolean {\n const descriptor = getDescriptor(obj, prop);\n return descriptor?.get !== undefined;\n}\n\n/**\n * Create a new getter tracking state\n *\n * @returns A new GetterTrackingState initialized with empty collections\n */\nfunction createGetterTracker(): GetterTrackingState {\n return {\n trackedValues: new Map(),\n currentlyAccessing: new Set(),\n trackedGetters: new Set(),\n isTracking: false,\n renderCache: new Map(),\n cacheValid: false,\n };\n}\n\n/**\n * Create a proxy that intercepts getter access on a bloc instance\n *\n * @remarks\n * This proxy wraps the bloc instance to track which getters are accessed\n * during component render. When tracking is enabled (during render phase),\n * it records accessed getters and stores their computed values for later\n * comparison.\n *\n * IMPORTANT: This function caches proxies per bloc instance. Multiple components\n * sharing the same bloc will get the same proxy instance. Each component sets\n * its tracker in activeTrackerMap before render, and the proxy looks it up.\n *\n * @param bloc - The bloc instance to wrap\n * @returns A proxied bloc that tracks getter access\n */\nfunction createBlocProxy<TBloc extends StateContainer<AnyObject>>(\n bloc: TBloc,\n): TBloc {\n // Check cache first - return existing proxy if available\n const cached = blocProxyCache.get(bloc);\n if (cached) {\n return cached;\n }\n\n const proxy = new Proxy(bloc, {\n get(target, prop, receiver) {\n // Get the active tracker for this bloc (set by getSnapshot)\n const tracker = activeTrackerMap.get(target);\n\n // Only track during render phase (when tracker is active and tracking enabled)\n if (tracker?.isTracking && isGetter(target, prop)) {\n // Record that this getter was accessed during current render\n tracker.currentlyAccessing.add(prop);\n\n // Use cached value if available from previous change detection\n if (tracker.cacheValid && tracker.renderCache.has(prop)) {\n const cachedValue = tracker.renderCache.get(prop);\n // Also store in trackedValues for consistency\n tracker.trackedValues.set(prop, cachedValue);\n return cachedValue;\n }\n\n // Compute getter if no cache available (first access or cache invalidated)\n const descriptor = getDescriptor(target, prop);\n const value = descriptor!.get!.call(target);\n tracker.trackedValues.set(prop, value);\n return value;\n }\n\n // Default behavior for non-getters or when tracking disabled\n return Reflect.get(target, prop, receiver);\n },\n });\n\n blocProxyCache.set(bloc, proxy);\n return proxy;\n}\n\n/**\n * Check if any tracked getters have changed values\n *\n * @remarks\n * Re-computes all getters that were accessed during the last render and\n * compares their new values with stored values using Object.is() (reference\n * equality).\n *\n * OPTIMIZATION: Render cache population\n * - Computes ALL tracked getters (no early exit) to populate the render cache\n * - This ensures each getter is computed only once per render cycle\n * - If we're going to re-render, getters accessed during render will use cached values\n * - Cache is populated even if no changes detected (useful for parent-triggered re-renders)\n * - Trade-off: Give up early exit to ensure full cache population\n *\n * Error handling: If a getter throws during re-computation, we log a warning,\n * stop tracking that specific getter, and treat it as \"changed\" to trigger\n * a re-render. This prevents the tracking system from breaking while still\n * allowing React's error boundary to handle the error on the next render.\n *\n * @param bloc - The bloc instance\n * @param tracker - The getter tracking state\n * @returns True if any tracked getter value changed, false otherwise\n */\nfunction hasGetterChanges<TBloc extends StateContainer<AnyObject>>(\n bloc: TBloc,\n tracker: GetterTrackingState | null,\n): boolean {\n // Early return if no tracker or no getters tracked\n if (!tracker || tracker.trackedGetters.size === 0) {\n return false;\n }\n\n // Clear previous render cache\n tracker.renderCache.clear();\n\n let hasAnyChange = false;\n\n // Compute all getters to populate render cache (no early exit)\n for (const prop of tracker.trackedGetters) {\n try {\n const descriptor = getDescriptor(bloc, prop);\n if (!descriptor?.get) {\n // Getter no longer exists (shouldn't happen, but be defensive)\n continue;\n }\n\n const newValue = descriptor.get.call(bloc);\n const oldValue = tracker.trackedValues.get(prop);\n\n // Store in render cache for upcoming render (even if unchanged)\n tracker.renderCache.set(prop, newValue);\n\n // Update tracked values for next comparison\n tracker.trackedValues.set(prop, newValue);\n\n // Use Object.is for reference equality comparison\n if (!Object.is(newValue, oldValue)) {\n hasAnyChange = true;\n // Don't return early - continue computing and caching remaining getters\n }\n } catch (error) {\n // Getter threw an error during comparison\n console.warn(\n `[useBloc] Getter \"${String(prop)}\" threw error during change detection. Stopping tracking for this getter.`,\n error,\n );\n\n // Stop tracking this getter\n tracker.trackedGetters.delete(prop);\n tracker.trackedValues.delete(prop);\n\n // Treat as \"changed\" to trigger re-render\n // Still return early on error to avoid cascading failures\n tracker.cacheValid = false; // Invalidate cache due to error\n return true;\n }\n }\n\n // Mark cache as valid for the upcoming render\n tracker.cacheValid = true;\n\n return hasAnyChange;\n}\n\n/**\n * Generates instance ID for isolated blocs\n */\nfunction generateInstanceId(\n componentRef: ComponentRef,\n isIsolated: boolean,\n providedId?: string,\n): string | undefined {\n if (providedId) return providedId;\n\n if (isIsolated) {\n if (!componentRef.__blocInstanceId) {\n componentRef.__blocInstanceId = `isolated-${Math.random().toString(36).slice(2, 11)}`;\n }\n return componentRef.__blocInstanceId;\n }\n\n return undefined;\n}\n\n/**\n * Determines tracking mode from options\n */\ninterface TrackingMode {\n useManualDeps: boolean;\n autoTrackEnabled: boolean;\n}\n\nfunction determineTrackingMode<TBloc extends StateContainer<AnyObject>>(\n options?: UseBlocOptions<TBloc>,\n): TrackingMode {\n return {\n useManualDeps: options?.dependencies !== undefined,\n autoTrackEnabled: options?.autoTrack !== false,\n };\n}\n\n/**\n * Internal state for subscription and snapshot functions\n */\ninterface HookState<TBloc extends StateContainer<AnyObject>> {\n /** State property tracker (existing) */\n tracker: TrackerState<ExtractState<TBloc>> | null;\n /** Manual dependencies cache (existing) */\n manualDepsCache: unknown[] | null;\n /** Getter tracking state (new) */\n getterTracker: GetterTrackingState | null;\n /** Cached proxied bloc instance (new) */\n proxiedBloc: TBloc | null;\n}\n\n/**\n * Factory: Creates subscribe function for automatic proxy tracking mode\n */\nfunction createAutoTrackSubscribe<TBloc extends StateContainer<AnyObject>>(\n instance: TBloc,\n hookState: HookState<TBloc>,\n): (callback: () => void) => () => void {\n return (callback: () => void) => {\n return instance.subscribe(() => {\n const tracker =\n hookState.tracker ||\n (hookState.tracker = createTrackerState<ExtractState<TBloc>>());\n\n let stateChanged = hasChanges(tracker, instance.state);\n\n // Special case: if NO state properties were tracked (pathCache.size === 0)\n // but getters WERE tracked, then don't treat \"no state tracking\" as \"track everything\".\n // Only rely on getter changes in this case.\n if (\n tracker.pathCache.size === 0 &&\n hookState.getterTracker &&\n hookState.getterTracker.trackedGetters.size > 0\n ) {\n stateChanged = false; // Override - only getters are relevant\n }\n\n // EARLY EXIT: If state already changed, skip getter checks entirely\n if (stateChanged) {\n callback();\n return;\n }\n\n // Only check getters if state didn't change\n const getterChanged = hasGetterChanges(instance, hookState.getterTracker);\n\n if (getterChanged) {\n callback();\n }\n });\n };\n}\n\n/**\n * Factory: Creates subscribe function for manual dependencies mode\n */\nfunction createManualDepsSubscribe<TBloc extends StateContainer<AnyObject>>(\n instance: TBloc,\n hookState: HookState<TBloc>,\n options: UseBlocOptions<TBloc>,\n): (callback: () => void) => () => void {\n return (callback: () => void) => {\n return instance.subscribe(() => {\n const newDeps = options.dependencies!(instance.state, instance);\n if (\n !hookState.manualDepsCache ||\n !shallowEqual(hookState.manualDepsCache, newDeps)\n ) {\n hookState.manualDepsCache = newDeps;\n callback();\n }\n });\n };\n}\n\n/**\n * Factory: Creates subscribe function for no-tracking mode\n */\nfunction createNoTrackSubscribe<TBloc extends StateContainer<AnyObject>>(\n instance: TBloc,\n): (callback: () => void) => () => void {\n return (callback: () => void) => instance.subscribe(callback);\n}\n\n/**\n * Factory: Creates getSnapshot function for automatic proxy tracking mode\n */\nfunction createAutoTrackSnapshot<TBloc extends StateContainer<AnyObject>>(\n instance: TBloc,\n hookState: HookState<TBloc>,\n): () => ExtractState<TBloc> {\n return () => {\n const tracker =\n hookState.tracker ||\n (hookState.tracker = createTrackerState<ExtractState<TBloc>>());\n\n if (hasTrackedData(tracker)) {\n captureTrackedPaths(tracker, instance.state);\n }\n\n // Enable getter tracking during render and set as active tracker\n if (hookState.getterTracker) {\n // Capture getters from previous render (commit currentlyAccessing to trackedGetters)\n if (hookState.getterTracker.currentlyAccessing.size > 0) {\n hookState.getterTracker.trackedGetters = new Set(\n hookState.getterTracker.currentlyAccessing,\n );\n }\n\n // Clear and enable tracking for this render\n hookState.getterTracker.currentlyAccessing.clear();\n hookState.getterTracker.isTracking = true;\n\n // Set this component's tracker as the active one for this bloc\n activeTrackerMap.set(instance, hookState.getterTracker);\n }\n\n startTracking(tracker);\n return createProxy(tracker, instance.state);\n };\n}\n\n/**\n * Factory: Creates getSnapshot function for manual dependencies mode\n */\nfunction createManualDepsSnapshot<TBloc extends StateContainer<AnyObject>>(\n instance: TBloc,\n hookState: HookState<TBloc>,\n options: UseBlocOptions<TBloc>,\n): () => ExtractState<TBloc> {\n return () => {\n hookState.manualDepsCache = options.dependencies!(instance.state, instance);\n return instance.state;\n };\n}\n\n/**\n * Factory: Creates getSnapshot function for no-tracking mode\n */\nfunction createNoTrackSnapshot<TBloc extends StateContainer<AnyObject>>(\n instance: TBloc,\n): () => ExtractState<TBloc> {\n return () => instance.state;\n}\n\n/**\n * Lifecycle: INITIAL MOUNT\n * 1. useMemo runs once - creates bloc, subscribeFn, getSnapshotFn\n * 2. useSyncExternalStore calls getSnapshotFn (1st time) - lazy creates tracker, starts tracking, returns proxy\n * 3. Component renders - proxy tracks property accesses\n * 4. useSyncExternalStore calls getSnapshotFn (2nd time) - captures tracked paths, starts new tracking, returns proxy\n * 5. useSyncExternalStore calls subscribeFn - sets up state change listener\n *\n * Lifecycle: STATE CHANGE\n * 1. Bloc state changes\n * 2. subscribeFn callback checks hasChanges() - only re-renders if tracked paths changed\n * 3. If re-render: getSnapshotFn captures previous paths, starts tracking, returns proxy\n *\n * Lifecycle: RE-RENDER (parent re-render)\n * 1. useMemo returns cached values (same bloc, subscribeFn, getSnapshotFn)\n * 2. useSyncExternalStore calls getSnapshotFn - captures paths, starts tracking, returns proxy\n */\nexport function useBloc<TBloc extends StateContainer<AnyObject>>(\n BlocClass: BlocConstructor<TBloc>,\n options?: UseBlocOptions<TBloc>,\n): UseBlocReturn<TBloc> {\n // Component reference that persists across React Strict Mode remounts\n const componentRef = useRef<ComponentRef>({});\n\n const [bloc, subscribe, getSnapshot, instanceKey, hookState, rawInstance] =\n useMemo(() => {\n const isIsolated =\n (BlocClass as { isolated?: boolean }).isolated === true;\n const Constructor = BlocClass as StateContainerConstructor<TBloc>;\n\n // Generate instance key\n const instanceId = generateInstanceId(\n componentRef.current,\n isIsolated,\n options?.instanceId,\n );\n\n // Get or create bloc instance\n const instance = Constructor.getOrCreate(\n instanceId,\n options?.staticProps,\n );\n\n // Determine tracking mode\n const { useManualDeps, autoTrackEnabled } =\n determineTrackingMode(options);\n\n // Mutable state shared between subscribe and getSnapshot\n const hookState: HookState<TBloc> = {\n tracker: null,\n manualDepsCache: null,\n getterTracker: null,\n proxiedBloc: null,\n };\n\n // Create subscribe and getSnapshot functions based on tracking mode\n let subscribeFn: (callback: () => void) => () => void;\n let getSnapshotFn: () => ExtractState<TBloc>;\n\n if (useManualDeps) {\n // Manual dependencies mode - no automatic tracking\n subscribeFn = createManualDepsSubscribe(instance, hookState, options!);\n getSnapshotFn = createManualDepsSnapshot(instance, hookState, options!);\n hookState.proxiedBloc = instance; // Use raw instance\n } else if (!autoTrackEnabled) {\n // No tracking mode\n subscribeFn = createNoTrackSubscribe(instance);\n getSnapshotFn = createNoTrackSnapshot(instance);\n hookState.proxiedBloc = instance; // Use raw instance\n } else {\n // Auto-tracking mode - enable both state and getter tracking\n subscribeFn = createAutoTrackSubscribe(instance, hookState);\n getSnapshotFn = createAutoTrackSnapshot(instance, hookState);\n\n // Initialize getter tracker and create proxied bloc\n hookState.getterTracker = createGetterTracker();\n hookState.proxiedBloc = createBlocProxy(instance);\n }\n\n return [\n hookState.proxiedBloc!,\n subscribeFn,\n getSnapshotFn,\n instanceId,\n hookState,\n instance,\n ] as const;\n }, [BlocClass]);\n\n const state = useSyncExternalStore(subscribe, getSnapshot);\n\n // Disable getter tracking after each render and clear active tracker\n // Also invalidate render cache since this render cycle is complete\n useEffect(() => {\n if (hookState.getterTracker) {\n hookState.getterTracker.isTracking = false;\n hookState.getterTracker.cacheValid = false; // Invalidate cache after render\n activeTrackerMap.delete(rawInstance);\n }\n });\n\n // Mount/unmount lifecycle\n useEffect(() => {\n // Call onMount callback if provided\n if (options?.onMount) {\n options.onMount(bloc);\n }\n\n return () => {\n // Call onUnmount callback if provided\n if (options?.onUnmount) {\n options.onUnmount(bloc);\n }\n\n // Release bloc reference\n const Constructor = BlocClass as StateContainerConstructor<TBloc>;\n Constructor.release(instanceKey);\n\n // For isolated instances, dispose manually since registry doesn't track them\n const isIsolated =\n (BlocClass as { isolated?: boolean }).isolated === true;\n if (isIsolated && !rawInstance.isDisposed) {\n rawInstance.dispose();\n }\n };\n }, []);\n\n return [state, bloc, componentRef] as UseBlocReturn<TBloc>;\n}\n","/**\n * useBlocActions - hook for accessing bloc instance without state subscription\n *\n * @example\n * ```tsx\n * // Use when you only need to call actions, not read state\n * function ActionsOnly() {\n * const bloc = useBlocActions(CounterBloc);\n * return (\n * <div>\n * <button onClick={bloc.increment}>+</button>\n * <button onClick={bloc.decrement}>-</button>\n * </div>\n * );\n * }\n * ```\n */\n\nimport { useMemo, useEffect, useRef } from 'react';\nimport type { AnyObject, BlocConstructor, StateContainer } from '@blac/core';\nimport type { ComponentRef } from './types';\n\n/**\n * StateContainer constructor with required static methods\n */\ntype StateContainerConstructor<TBloc extends StateContainer<any>> =\n BlocConstructor<TBloc> & {\n getOrCreate(instanceKey?: string, ...args: any[]): TBloc;\n release(instanceKey?: string): void;\n };\n\n/**\n * Configuration options for the useBlocActions hook\n *\n * @template TBloc - The StateContainer type\n */\nexport interface UseBlocActionsOptions<\n TBloc extends StateContainer<AnyObject>,\n> {\n /**\n * Static props to pass to the Bloc constructor\n * Type should match the constructor's first parameter\n */\n staticProps?: AnyObject;\n\n /**\n * Custom instance ID for shared blocs\n * - For isolated blocs, each useBlocActions call gets its own instance\n * - For shared blocs, the same instanceId will share the same bloc instance\n */\n instanceId?: string;\n\n /**\n * Callback invoked when the component mounts\n *\n * @param bloc - The bloc instance\n */\n onMount?: (bloc: TBloc) => void;\n\n /**\n * Callback invoked when the component unmounts\n *\n * @param bloc - The bloc instance\n */\n onUnmount?: (bloc: TBloc) => void;\n}\n\n/**\n * Generates instance ID for isolated blocs\n */\nfunction generateInstanceId(\n componentRef: ComponentRef,\n isIsolated: boolean,\n providedId?: string,\n): string | undefined {\n if (providedId) return providedId;\n\n if (isIsolated) {\n if (!componentRef.__blocInstanceId) {\n componentRef.__blocInstanceId = `isolated-${Math.random().toString(36).slice(2, 11)}`;\n }\n return componentRef.__blocInstanceId;\n }\n\n return undefined;\n}\n\n/**\n * React hook for accessing bloc instance without state subscription.\n * Use this when you only need to call bloc methods/actions without reading state.\n *\n * Benefits over useBloc:\n * - No state subscription overhead\n * - No proxy tracking\n * - Component never re-renders due to bloc state changes\n * - Lighter weight for action-only components\n *\n * @template TBloc - The StateContainer type\n * @param BlocClass - The bloc class constructor\n * @param options - Optional configuration\n * @returns The bloc instance\n */\nexport function useBlocActions<TBloc extends StateContainer<AnyObject>>(\n BlocClass: BlocConstructor<TBloc>,\n options?: UseBlocActionsOptions<TBloc>,\n): TBloc {\n // Component reference that persists across React Strict Mode remounts\n const componentRef = useRef<ComponentRef>({});\n\n const [bloc, instanceKey] = useMemo(() => {\n const isIsolated = (BlocClass as { isolated?: boolean }).isolated === true;\n const Constructor = BlocClass as StateContainerConstructor<TBloc>;\n\n // Generate instance key\n const instanceId = generateInstanceId(\n componentRef.current,\n isIsolated,\n options?.instanceId,\n );\n\n // Get or create bloc instance\n const instance = Constructor.getOrCreate(instanceId, options?.staticProps);\n\n return [instance, instanceId] as const;\n }, [BlocClass]);\n\n // Mount/unmount lifecycle\n useEffect(() => {\n // Call onMount callback if provided\n if (options?.onMount) {\n options.onMount(bloc);\n }\n\n return () => {\n // Call onUnmount callback if provided\n if (options?.onUnmount) {\n options.onUnmount(bloc);\n }\n\n // Release bloc reference\n const Constructor = BlocClass as StateContainerConstructor<TBloc>;\n Constructor.release(instanceKey);\n\n // For isolated instances, dispose manually since registry doesn't track them\n const isIsolated =\n (BlocClass as { isolated?: boolean }).isolated === true;\n if (isIsolated && !bloc.isDisposed) {\n bloc.dispose();\n }\n };\n }, []);\n\n return bloc;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["instanceKey","subscribeFn: (callback: () => void) => () => void","getSnapshotFn: () => ExtractState<TBloc>","adapterState: AdapterState<TBloc>","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 BlocConstructor,\n StateContainer,\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} from '@blac/core';\nimport type { UseBlocOptions, UseBlocReturn, ComponentRef } from './types';\nimport { generateInstanceKey } from './utils/instance-keys';\n\ntype StateContainerConstructor<TBloc extends StateContainer<any>> =\n BlocConstructor<TBloc> & {\n resolve(instanceKey?: string, ...args: any[]): TBloc;\n release(instanceKey?: string): void;\n };\n\ninterface TrackingMode {\n useManualDeps: boolean;\n autoTrackEnabled: boolean;\n}\n\nfunction determineTrackingMode<TBloc extends StateContainer<any>>(\n options?: UseBlocOptions<TBloc>,\n): TrackingMode {\n return {\n useManualDeps: options?.dependencies !== undefined,\n autoTrackEnabled: options?.autoTrack !== false,\n };\n}\n\n/**\n * Lifecycle: INITIAL MOUNT\n * 1. useMemo runs once - creates bloc, subscribeFn, getSnapshotFn\n * 2. useSyncExternalStore calls getSnapshotFn (1st time) - lazy creates tracker, starts tracking, returns proxy\n * 3. Component renders - proxy tracks property accesses\n * 4. useSyncExternalStore calls getSnapshotFn (2nd time) - captures tracked paths, starts new tracking, returns proxy\n * 5. useSyncExternalStore calls subscribeFn - sets up state change listener\n *\n * Lifecycle: STATE CHANGE\n * 1. Bloc state changes\n * 2. subscribeFn callback checks hasChanges() - only re-renders if tracked paths changed\n * 3. If re-render: getSnapshotFn captures previous paths, starts tracking, returns proxy\n *\n * Lifecycle: RE-RENDER (parent re-render)\n * 1. useMemo returns cached values (same bloc, subscribeFn, getSnapshotFn)\n * 2. useSyncExternalStore calls getSnapshotFn - captures paths, starts tracking, returns proxy\n */\nexport function useBloc<T extends new (...args: any[]) => StateContainer<any>>(\n BlocClass: T & BlocConstructor<InstanceType<T>>,\n options?: UseBlocOptions<InstanceType<T>>,\n): UseBlocReturn<InstanceType<T>> {\n // Component reference that persists across React Strict Mode remounts\n type TBloc = InstanceType<T>;\n const componentRef = useRef<ComponentRef>({});\n const Constructor = BlocClass as StateContainerConstructor<TBloc>;\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<TBloc>,\n string | undefined,\n AdapterState<TBloc>,\n TBloc,\n ]\n >(() => {\n // Generate instance key\n const instanceKey = generateInstanceKey(\n componentRef.current,\n isIsolated,\n options?.instanceId,\n );\n\n // Get or create bloc instance with ownership (increments ref count)\n const instance = BlocClass.resolve(instanceKey, options?.staticProps);\n\n // Determine tracking mode\n const { useManualDeps, autoTrackEnabled } =\n determineTrackingMode(options);\n\n // Create subscribe and getSnapshot functions based on tracking mode\n let subscribeFn: (callback: () => void) => () => void;\n let getSnapshotFn: () => ExtractState<TBloc>;\n let adapterState: AdapterState<TBloc>;\n\n if (useManualDeps && options?.dependencies) {\n // Manual dependencies mode - no automatic tracking\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 // No tracking mode\n adapterState = initNoTrackState(instance);\n subscribeFn = createNoTrackSubscribe(instance);\n getSnapshotFn = createNoTrackSnapshot(instance);\n } else {\n // Auto-tracking mode - enable both state and getter tracking\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 // Force re-render mechanism for external bloc changes\n const [, forceUpdate] = useReducer((x: number) => x + 1, 0);\n\n // External dependency manager (persists across renders)\n const externalDepsManager = useRef(new ExternalDependencyManager());\n\n // Disable getter tracking and manage external bloc subscriptions after each render\n useEffect(() => {\n disableGetterTracking(adapterState, rawInstance);\n externalDepsManager.current.updateSubscriptions(\n adapterState.getterTracker,\n rawInstance,\n forceUpdate,\n );\n }); // Run on every render to pick up new dependencies and disable tracking\n\n // Mount/unmount lifecycle\n useEffect(() => {\n // Call onMount callback if provided\n if (options?.onMount) {\n options.onMount(bloc);\n }\n\n return () => {\n // Cleanup in proper order: subscriptions -> callbacks -> disposal\n\n // 1. Clean up external subscriptions FIRST (before bloc is disposed)\n externalDepsManager.current.cleanup();\n\n // 2. Call onUnmount callback if provided\n if (options?.onUnmount) {\n options.onUnmount(bloc);\n }\n\n // 3. Release bloc reference\n Constructor.release(instanceKey);\n\n // 4. For isolated instances, dispose manually since registry doesn't track them\n if (isIsolated && !rawInstance.isDisposed) {\n rawInstance.dispose();\n }\n };\n }, []);\n\n return [state, bloc, componentRef] as UseBlocReturn<TBloc>;\n}\n","import { useMemo, useEffect, useRef } from 'react';\nimport {\n type BlocConstructor,\n StateContainer,\n isIsolatedClass,\n} from '@blac/core';\nimport type { ComponentRef } from './types';\nimport { generateInstanceKey } from './utils/instance-keys';\n\ntype StateContainerConstructor<TBloc extends StateContainer<any>> =\n BlocConstructor<TBloc> & {\n resolve(instanceKey?: string, ...args: any[]): TBloc;\n release(instanceKey?: string): void;\n };\n\nexport interface UseBlocActionsOptions<TBloc> {\n staticProps?: any;\n instanceId?: string | number;\n onMount?: (bloc: TBloc) => void;\n onUnmount?: (bloc: TBloc) => void;\n}\n\nexport function useBlocActions<\n T extends new (...args: any[]) => StateContainer<any>,\n>(\n BlocClass: T & BlocConstructor<InstanceType<T>>,\n options?: UseBlocActionsOptions<InstanceType<T>>,\n): InstanceType<T> {\n // Component reference that persists across React Strict Mode remounts\n type TBloc = InstanceType<T>;\n const componentRef = useRef<ComponentRef>({});\n\n const [bloc, instanceKey] = useMemo(() => {\n const isIsolated = isIsolatedClass(BlocClass);\n const Constructor = BlocClass as StateContainerConstructor<TBloc>;\n\n // Generate instance key\n const instanceKey = generateInstanceKey(\n componentRef.current,\n isIsolated,\n options?.instanceId,\n );\n\n // Get or create bloc instance with ownership (increments ref count)\n const instance = options?.staticProps\n ? Constructor.resolve(instanceKey, options.staticProps)\n : Constructor.resolve(instanceKey);\n\n return [instance, instanceKey] as const;\n }, [BlocClass]);\n\n // Mount/unmount lifecycle\n useEffect(() => {\n // Call onMount callback if provided\n if (options?.onMount) {\n options.onMount(bloc);\n }\n\n return () => {\n // Call onUnmount callback if provided\n if (options?.onUnmount) {\n options.onUnmount(bloc);\n }\n\n // Release bloc reference\n const Constructor = BlocClass as StateContainerConstructor<TBloc>;\n Constructor.release(instanceKey);\n\n // For isolated instances, dispose manually since registry doesn't track them\n if (isIsolatedClass(BlocClass) && !bloc.isDisposed) {\n bloc.dispose();\n }\n };\n }, []);\n\n return bloc;\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;;;;;;ACIxB,SAAS,sBACP,SACc;AACd,QAAO;EACL,eAAe,SAAS,iBAAiB;EACzC,kBAAkB,SAAS,cAAc;EAC1C;;;;;;;;;;;;;;;;;;;AAoBH,SAAgB,QACd,WACA,SACgC;CAGhC,MAAM,iCAAoC,EAAE,CAAC;CAC7C,MAAM,cAAc;CACpB,MAAM,8CAA6B,UAAU;CAE7C,MAAM,CAAC,MAAM,WAAW,aAAa,aAAa,cAAc,wCAUtD;EAEN,MAAMA,gBAAc,oBAClB,aAAa,SACb,YACA,SAAS,WACV;EAGD,MAAM,WAAW,UAAU,QAAQA,eAAa,SAAS,YAAY;EAGrE,MAAM,EAAE,eAAe,qBACrB,sBAAsB,QAAQ;EAGhC,IAAIC;EACJ,IAAIC;EACJ,IAAIC;AAEJ,MAAI,iBAAiB,SAAS,cAAc;AAE1C,yDAAmC,SAAS;AAC5C,4DAAwC,UAAUC,gBAAc,EAC9D,cAAc,QAAQ,cACvB,CAAC;AACF,6DAAyC,UAAUA,gBAAc,EAC/D,cAAc,QAAQ,cACvB,CAAC;aACO,CAAC,kBAAkB;AAE5B,sDAAgC,SAAS;AACzC,yDAAqC,SAAS;AAC9C,0DAAsC,SAAS;SAC1C;AAEL,wDAAkC,SAAS;AAC3C,2DAAuC,UAAUA,eAAa;AAC9D,4DAAwC,UAAUA,eAAa;;AAGjE,SAAO;GACLA,eAAa;GACb;GACA;GACAJ;GACAI;GACA;GACD;IACA,CAAC,WAAW,SAAS,WAAW,CAAC;CAEtC,MAAM,wCAA6B,WAAW,YAAY;CAG1D,MAAM,GAAG,sCAA2B,MAAc,IAAI,GAAG,EAAE;CAG3D,MAAM,wCAA6B,IAAIC,uCAA2B,CAAC;AAGnE,4BAAgB;AACd,yCAAsB,cAAc,YAAY;AAChD,sBAAoB,QAAQ,oBAC1B,aAAa,eACb,aACA,YACD;GACD;AAGF,4BAAgB;AAEd,MAAI,SAAS,QACX,SAAQ,QAAQ,KAAK;AAGvB,eAAa;AAIX,uBAAoB,QAAQ,SAAS;AAGrC,OAAI,SAAS,UACX,SAAQ,UAAU,KAAK;AAIzB,eAAY,QAAQ,YAAY;AAGhC,OAAI,cAAc,CAAC,YAAY,WAC7B,aAAY,SAAS;;IAGxB,EAAE,CAAC;AAEN,QAAO;EAAC;EAAO;EAAM;EAAa;;;;;AChKpC,SAAgB,eAGd,WACA,SACiB;CAGjB,MAAM,iCAAoC,EAAE,CAAC;CAE7C,MAAM,CAAC,MAAM,wCAA6B;EACxC,MAAM,8CAA6B,UAAU;EAC7C,MAAM,cAAc;EAGpB,MAAMC,gBAAc,oBAClB,aAAa,SACb,YACA,SAAS,WACV;AAOD,SAAO,CAJU,SAAS,cACtB,YAAY,QAAQA,eAAa,QAAQ,YAAY,GACrD,YAAY,QAAQA,cAAY,EAElBA,cAAY;IAC7B,CAAC,UAAU,CAAC;AAGf,4BAAgB;AAEd,MAAI,SAAS,QACX,SAAQ,QAAQ,KAAK;AAGvB,eAAa;AAEX,OAAI,SAAS,UACX,SAAQ,UAAU,KAAK;AAKzB,GADoB,UACR,QAAQ,YAAY;AAGhC,wCAAoB,UAAU,IAAI,CAAC,KAAK,WACtC,MAAK,SAAS;;IAGjB,EAAE,CAAC;AAEN,QAAO"}
|