@blac/react 2.0.0-rc-8 → 2.0.0-rc-10
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.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +27 -64
- package/dist/index.es.js +85 -374
- package/dist/index.es.js.map +1 -1
- package/package.json +10 -9
- package/src/__tests__/useBloc.tracking.test.tsx +198 -0
- package/src/index.ts +2 -4
- package/src/useBloc.ts +130 -0
- package/src/useExternalBlocStore.ts +113 -288
- package/src/DependencyTracker.ts +0 -350
- package/src/useBloc.tsx +0 -218
package/dist/index.es.js
CHANGED
|
@@ -1,387 +1,98 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
props: h,
|
|
11
|
-
instanceRef: n
|
|
12
|
-
}), [c, o, h, n]), v = p(K());
|
|
13
|
-
D(() => {
|
|
14
|
-
v.current = K();
|
|
15
|
-
}, [K]);
|
|
16
|
-
const R = p(void 0), E = p(void 0), M = p(void 0), t = p(void 0);
|
|
17
|
-
t.current !== ((k = v.current) == null ? void 0 : k.uid) && (t.current = (F = v.current) == null ? void 0 : F.uid);
|
|
18
|
-
const l = D(
|
|
19
|
-
() => (u, C) => {
|
|
20
|
-
const d = v.current;
|
|
21
|
-
if (!d)
|
|
22
|
-
return [[], []];
|
|
23
|
-
const S = C ?? R.current;
|
|
24
|
-
let y;
|
|
25
|
-
if (f)
|
|
26
|
-
y = [f(u, S, d), []];
|
|
27
|
-
else if (d.defaultDependencySelector)
|
|
28
|
-
y = [d.defaultDependencySelector(u, S, d), []];
|
|
29
|
-
else if (typeof u != "object")
|
|
30
|
-
y = [[u], []];
|
|
31
|
-
else {
|
|
32
|
-
const m = [], P = [];
|
|
33
|
-
for (const T of b.current)
|
|
34
|
-
T in u && m.push(u[T]);
|
|
35
|
-
for (const T of B.current)
|
|
36
|
-
if (T in d)
|
|
37
|
-
try {
|
|
38
|
-
const w = d[T];
|
|
39
|
-
typeof w != "function" && P.push(w);
|
|
40
|
-
} catch (w) {
|
|
41
|
-
I.instance.log("useBloc Error", w);
|
|
42
|
-
}
|
|
43
|
-
b.current.size === 0 && B.current.size === 0 && (A.current || m.push(u)), y = [m, P];
|
|
44
|
-
}
|
|
45
|
-
return R.current = u, y;
|
|
1
|
+
import { BlacAdapter as C, generateUUID as g, Blac as w } from "@blac/core";
|
|
2
|
+
import { useRef as n, useMemo as l, useEffect as B, useSyncExternalStore as v } from "react";
|
|
3
|
+
function K(c, e) {
|
|
4
|
+
const f = n(0);
|
|
5
|
+
f.current++;
|
|
6
|
+
const d = n({}), t = l(() => new C(
|
|
7
|
+
{
|
|
8
|
+
componentRef: d,
|
|
9
|
+
blocConstructor: c
|
|
46
10
|
},
|
|
47
|
-
|
|
48
|
-
),
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
console.error({
|
|
63
|
-
e: P,
|
|
64
|
-
blocInstance: C,
|
|
65
|
-
dependencyArray: l
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
// Pass the dependency array to control when the subscription is updated
|
|
70
|
-
dependencyArray: l,
|
|
71
|
-
// Use the provided id to identify this subscription
|
|
72
|
-
id: n
|
|
73
|
-
};
|
|
74
|
-
I.activateBloc(C);
|
|
75
|
-
const y = C._observer.subscribe(S), m = () => {
|
|
76
|
-
a.current.delete(u), y();
|
|
77
|
-
};
|
|
78
|
-
return a.current.set(u, { observer: S, unsubscribe: m }), m;
|
|
79
|
-
},
|
|
80
|
-
// Return an immutable snapshot of the current bloc state
|
|
81
|
-
getSnapshot: () => {
|
|
82
|
-
const u = v.current;
|
|
83
|
-
if (!u)
|
|
84
|
-
return {};
|
|
85
|
-
const C = u.state, d = l(C, R.current), S = E.current;
|
|
86
|
-
let y = !1;
|
|
87
|
-
if (!S)
|
|
88
|
-
y = !0;
|
|
89
|
-
else if (S.length !== d.length)
|
|
90
|
-
y = !0;
|
|
91
|
-
else
|
|
92
|
-
for (let m = 0; m < d.length; m++) {
|
|
93
|
-
const P = S[m] || [], T = d[m] || [];
|
|
94
|
-
if (P.length !== T.length) {
|
|
95
|
-
y = !0;
|
|
96
|
-
break;
|
|
97
|
-
}
|
|
98
|
-
for (let w = 0; w < T.length; w++)
|
|
99
|
-
if (!Object.is(P[w], T[w])) {
|
|
100
|
-
y = !0;
|
|
101
|
-
break;
|
|
102
|
-
}
|
|
103
|
-
if (y) break;
|
|
104
|
-
}
|
|
105
|
-
return E.current = d, !y && M.current ? M.current : (M.current = C, C);
|
|
106
|
-
},
|
|
107
|
-
// Server snapshot mirrors the client snapshot in this implementation
|
|
108
|
-
getServerSnapshot: () => {
|
|
109
|
-
const u = v.current;
|
|
110
|
-
return u ? u.state : {};
|
|
111
|
-
}
|
|
112
|
-
}), []);
|
|
113
|
-
return {
|
|
114
|
-
usedKeys: b,
|
|
115
|
-
usedClassPropKeys: B,
|
|
116
|
-
externalStore: x,
|
|
117
|
-
instance: v,
|
|
118
|
-
rid: n,
|
|
119
|
-
hasProxyTracking: A
|
|
120
|
-
};
|
|
121
|
-
};
|
|
122
|
-
class U {
|
|
123
|
-
constructor(e = {}) {
|
|
124
|
-
g(this, "stateKeys", /* @__PURE__ */ new Set());
|
|
125
|
-
g(this, "classKeys", /* @__PURE__ */ new Set());
|
|
126
|
-
g(this, "batchedCallbacks", /* @__PURE__ */ new Set());
|
|
127
|
-
g(this, "flushScheduled", !1);
|
|
128
|
-
g(this, "flushTimeoutId");
|
|
129
|
-
g(this, "metrics", {
|
|
130
|
-
stateAccessCount: 0,
|
|
131
|
-
classAccessCount: 0,
|
|
132
|
-
proxyCreationCount: 0,
|
|
133
|
-
batchFlushCount: 0,
|
|
134
|
-
averageResolutionTime: 0,
|
|
135
|
-
memoryUsageKB: 0
|
|
136
|
-
});
|
|
137
|
-
g(this, "resolutionTimes", []);
|
|
138
|
-
g(this, "config");
|
|
139
|
-
g(this, "stateProxyCache", /* @__PURE__ */ new WeakMap());
|
|
140
|
-
g(this, "classProxyCache", /* @__PURE__ */ new WeakMap());
|
|
141
|
-
g(this, "lastStateSnapshot", null);
|
|
142
|
-
g(this, "lastClassSnapshot", null);
|
|
143
|
-
this.config = {
|
|
144
|
-
enableBatching: !0,
|
|
145
|
-
batchTimeout: 0,
|
|
146
|
-
// Use React's scheduler
|
|
147
|
-
enableMetrics: process.env.NODE_ENV === "development",
|
|
148
|
-
maxCacheSize: 1e3,
|
|
149
|
-
enableDeepTracking: !1,
|
|
150
|
-
...e
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
trackStateAccess(e) {
|
|
154
|
-
this.config.enableMetrics && this.metrics.stateAccessCount++, this.stateKeys.add(e), this.config.enableBatching && this.scheduleFlush();
|
|
155
|
-
}
|
|
156
|
-
trackClassAccess(e) {
|
|
157
|
-
this.config.enableMetrics && this.metrics.classAccessCount++, this.classKeys.add(e), this.config.enableBatching && this.scheduleFlush();
|
|
158
|
-
}
|
|
159
|
-
createStateProxy(e, s) {
|
|
160
|
-
const h = this.stateProxyCache.get(e);
|
|
161
|
-
if (h)
|
|
162
|
-
return h;
|
|
163
|
-
const f = this.config.enableMetrics ? performance.now() : 0, n = new Proxy(e, {
|
|
164
|
-
get: (r, i) => {
|
|
165
|
-
typeof i == "string" && (this.trackStateAccess(i), s == null || s(i));
|
|
166
|
-
const o = r[i];
|
|
167
|
-
return this.config.enableDeepTracking && o && typeof o == "object" && !Array.isArray(o) ? this.createStateProxy(o) : o;
|
|
168
|
-
},
|
|
169
|
-
has: (r, i) => i in r,
|
|
170
|
-
ownKeys: (r) => Reflect.ownKeys(r),
|
|
171
|
-
getOwnPropertyDescriptor: (r, i) => Reflect.getOwnPropertyDescriptor(r, i)
|
|
172
|
-
});
|
|
173
|
-
if (this.stateProxyCache.set(e, n), this.config.enableMetrics) {
|
|
174
|
-
this.metrics.proxyCreationCount++;
|
|
175
|
-
const r = performance.now();
|
|
176
|
-
this.resolutionTimes.push(r - f), this.updateAverageResolutionTime();
|
|
177
|
-
}
|
|
178
|
-
return n;
|
|
179
|
-
}
|
|
180
|
-
createClassProxy(e, s) {
|
|
181
|
-
const h = this.classProxyCache.get(e);
|
|
182
|
-
if (h)
|
|
183
|
-
return h;
|
|
184
|
-
const f = this.config.enableMetrics ? performance.now() : 0, n = new Proxy(e, {
|
|
185
|
-
get: (r, i) => {
|
|
186
|
-
const o = r[i];
|
|
187
|
-
return typeof i == "string" && typeof o != "function" && (this.trackClassAccess(i), s == null || s(i)), o;
|
|
11
|
+
e
|
|
12
|
+
), []);
|
|
13
|
+
t.resetConsumerTracking();
|
|
14
|
+
const s = n(0);
|
|
15
|
+
B(() => {
|
|
16
|
+
s.current++, t.options = e;
|
|
17
|
+
}, [e]);
|
|
18
|
+
const m = n(0);
|
|
19
|
+
B(() => (m.current++, t.mount(), () => {
|
|
20
|
+
t.unmount();
|
|
21
|
+
}), [t.blocInstance]);
|
|
22
|
+
const I = n(0), r = l(() => (I.current++, (o) => {
|
|
23
|
+
const y = t.createSubscription({
|
|
24
|
+
onChange: () => {
|
|
25
|
+
o();
|
|
188
26
|
}
|
|
189
27
|
});
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const r = performance.now();
|
|
193
|
-
this.resolutionTimes.push(r - f), this.updateAverageResolutionTime();
|
|
194
|
-
}
|
|
195
|
-
return n;
|
|
196
|
-
}
|
|
197
|
-
getStateKeys() {
|
|
198
|
-
return new Set(this.stateKeys);
|
|
199
|
-
}
|
|
200
|
-
getClassKeys() {
|
|
201
|
-
return new Set(this.classKeys);
|
|
202
|
-
}
|
|
203
|
-
reset() {
|
|
204
|
-
this.stateKeys.clear(), this.classKeys.clear(), this.cancelScheduledFlush();
|
|
205
|
-
}
|
|
206
|
-
subscribe(e) {
|
|
207
|
-
return this.batchedCallbacks.add(e), () => {
|
|
208
|
-
this.batchedCallbacks.delete(e);
|
|
28
|
+
return () => {
|
|
29
|
+
y();
|
|
209
30
|
};
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const n = [];
|
|
219
|
-
for (const r of this.classKeys)
|
|
220
|
-
if (r in s)
|
|
221
|
-
try {
|
|
222
|
-
const i = s[r];
|
|
223
|
-
typeof i != "function" && n.push(i);
|
|
224
|
-
} catch {
|
|
225
|
-
}
|
|
226
|
-
if (this.config.enableMetrics) {
|
|
227
|
-
const r = performance.now();
|
|
228
|
-
this.resolutionTimes.push(r - h), this.updateAverageResolutionTime();
|
|
31
|
+
}), [t.blocInstance]), u = n(0), a = n(0), b = v(
|
|
32
|
+
r,
|
|
33
|
+
// Get snapshot
|
|
34
|
+
() => (u.current++, t.blocInstance.state),
|
|
35
|
+
// Get server snapshot (same as client for now)
|
|
36
|
+
() => {
|
|
37
|
+
const o = t.blocInstance;
|
|
38
|
+
return a.current++, o.state;
|
|
229
39
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
return {
|
|
235
|
-
stateAccessCount: 0,
|
|
236
|
-
classAccessCount: 0,
|
|
237
|
-
proxyCreationCount: 0,
|
|
238
|
-
batchFlushCount: 0,
|
|
239
|
-
averageResolutionTime: 0,
|
|
240
|
-
memoryUsageKB: 0
|
|
241
|
-
};
|
|
242
|
-
const e = this.stateKeys.size * 50 + this.classKeys.size * 50 + (this.stateProxyCache instanceof WeakMap ? 100 : 0) + (this.classProxyCache instanceof WeakMap ? 100 : 0);
|
|
243
|
-
return {
|
|
244
|
-
...this.metrics,
|
|
245
|
-
memoryUsageKB: Math.round(e / 1024)
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
clearCaches() {
|
|
249
|
-
this.stateProxyCache = /* @__PURE__ */ new WeakMap(), this.classProxyCache = /* @__PURE__ */ new WeakMap(), this.resolutionTimes = [], this.config.enableMetrics && (this.metrics = {
|
|
250
|
-
stateAccessCount: 0,
|
|
251
|
-
classAccessCount: 0,
|
|
252
|
-
proxyCreationCount: 0,
|
|
253
|
-
batchFlushCount: 0,
|
|
254
|
-
averageResolutionTime: 0,
|
|
255
|
-
memoryUsageKB: 0
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
scheduleFlush() {
|
|
259
|
-
this.flushScheduled || (this.flushScheduled = !0, this.config.batchTimeout > 0 ? this.flushTimeoutId = setTimeout(() => {
|
|
260
|
-
this.flushBatchedChanges();
|
|
261
|
-
}, this.config.batchTimeout) : Promise.resolve().then(() => this.flushBatchedChanges()));
|
|
262
|
-
}
|
|
263
|
-
cancelScheduledFlush() {
|
|
264
|
-
this.flushTimeoutId && (clearTimeout(this.flushTimeoutId), this.flushTimeoutId = void 0), this.flushScheduled = !1;
|
|
265
|
-
}
|
|
266
|
-
flushBatchedChanges() {
|
|
267
|
-
if (!this.flushScheduled)
|
|
268
|
-
return;
|
|
269
|
-
this.flushScheduled = !1, this.flushTimeoutId = void 0, this.config.enableMetrics && this.metrics.batchFlushCount++;
|
|
270
|
-
const e = /* @__PURE__ */ new Set([...this.stateKeys, ...this.classKeys]);
|
|
271
|
-
for (const s of this.batchedCallbacks)
|
|
272
|
-
try {
|
|
273
|
-
s(e);
|
|
274
|
-
} catch (h) {
|
|
275
|
-
console.error("Error in dependency change callback:", h);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
updateAverageResolutionTime() {
|
|
279
|
-
if (this.resolutionTimes.length === 0)
|
|
280
|
-
return;
|
|
281
|
-
this.resolutionTimes.length > 100 && (this.resolutionTimes = this.resolutionTimes.slice(-100));
|
|
282
|
-
const e = this.resolutionTimes.reduce((s, h) => s + h, 0);
|
|
283
|
-
this.metrics.averageResolutionTime = e / this.resolutionTimes.length;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
function V(c) {
|
|
287
|
-
return new U(c);
|
|
40
|
+
), S = n(0), i = l(() => (S.current++, t.getProxyState(b)), [b]), x = n(0), h = l(() => (x.current++, t.getProxyBlocInstance()), [t.blocInstance]);
|
|
41
|
+
return B(() => {
|
|
42
|
+
t.updateLastNotified(d.current);
|
|
43
|
+
}), [i, h];
|
|
288
44
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
() => {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
`[useBloc] State snapshot is undefined for bloc ${c.name}. Bloc may not be properly initialized.`
|
|
306
|
-
);
|
|
307
|
-
return t;
|
|
308
|
-
},
|
|
309
|
-
s.getServerSnapshot ? () => {
|
|
310
|
-
const t = s.getServerSnapshot();
|
|
311
|
-
if (t === void 0)
|
|
312
|
-
throw new Error(
|
|
313
|
-
`[useBloc] Server state snapshot is undefined for bloc ${c.name}. Bloc may not be properly initialized.`
|
|
314
|
-
);
|
|
315
|
-
return t;
|
|
316
|
-
} : void 0
|
|
317
|
-
), b = p(null);
|
|
318
|
-
b.current || (b.current = new U({
|
|
319
|
-
enableBatching: !0,
|
|
320
|
-
enableMetrics: process.env.NODE_ENV === "development",
|
|
321
|
-
enableDeepTracking: !1
|
|
322
|
-
}));
|
|
323
|
-
const B = p(/* @__PURE__ */ new WeakMap()), A = p(/* @__PURE__ */ new WeakMap()), K = D(() => {
|
|
324
|
-
if (e != null && e.selector || (i.current = !0, typeof o != "object" || o === null))
|
|
325
|
-
return o;
|
|
326
|
-
let t = B.current.get(o);
|
|
327
|
-
return t || (t = new Proxy(o, {
|
|
328
|
-
get(l, a) {
|
|
329
|
-
var k;
|
|
330
|
-
return typeof a == "string" && (h.current.add(a), (k = b.current) == null || k.trackStateAccess(a)), l[a];
|
|
331
|
-
},
|
|
332
|
-
has(l, a) {
|
|
333
|
-
return a in l;
|
|
45
|
+
function M(c, e = {}) {
|
|
46
|
+
const f = n(`external-${g()}`), d = n(/* @__PURE__ */ new Set()), t = n(/* @__PURE__ */ new Set()), s = n(null), m = l(() => {
|
|
47
|
+
const r = w.getInstance();
|
|
48
|
+
if (c.constructor.isolated || c.isolated) {
|
|
49
|
+
const a = new c(e == null ? void 0 : e.props), b = (e == null ? void 0 : e.id) || `${c.name}_${g()}`;
|
|
50
|
+
return a._updateId(b), r.activateBloc(a), a;
|
|
51
|
+
}
|
|
52
|
+
return r.getBloc(c, {
|
|
53
|
+
id: e == null ? void 0 : e.id,
|
|
54
|
+
props: e == null ? void 0 : e.props
|
|
55
|
+
});
|
|
56
|
+
}, [c, e == null ? void 0 : e.id]), I = l(
|
|
57
|
+
() => ({
|
|
58
|
+
getSnapshot: () => {
|
|
59
|
+
const r = s.current;
|
|
60
|
+
return r ? r.state : void 0;
|
|
334
61
|
},
|
|
335
|
-
|
|
336
|
-
|
|
62
|
+
subscribe: (r) => {
|
|
63
|
+
const u = s.current;
|
|
64
|
+
if (!u)
|
|
65
|
+
return () => {
|
|
66
|
+
};
|
|
67
|
+
const a = (S, i) => {
|
|
68
|
+
try {
|
|
69
|
+
d.current.clear(), t.current.clear(), e.selector && u && e.selector(S, i, u), r.length > 0 ? r(S) : r();
|
|
70
|
+
} catch (x) {
|
|
71
|
+
console.error("Listener error in useExternalBlocStore:", x);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
return u._observer.subscribe({
|
|
75
|
+
id: f.current,
|
|
76
|
+
fn: a
|
|
77
|
+
});
|
|
337
78
|
},
|
|
338
|
-
|
|
339
|
-
|
|
79
|
+
getServerSnapshot: () => {
|
|
80
|
+
const r = s.current;
|
|
81
|
+
return r ? r.state : void 0;
|
|
340
82
|
}
|
|
341
|
-
}),
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
if (!l)
|
|
352
|
-
throw new Error(
|
|
353
|
-
`[useBloc] Bloc target is null for ${c.name}. This should never happen - bloc target must be defined.`
|
|
354
|
-
);
|
|
355
|
-
const x = l[a];
|
|
356
|
-
return typeof x != "function" && typeof a == "string" && (f.current.add(a), (k = b.current) == null || k.trackClassAccess(a)), x;
|
|
357
|
-
}
|
|
358
|
-
}), A.current.set(n.current, t)), t;
|
|
359
|
-
}, [(E = n.current) == null ? void 0 : E.uid]), R = p({});
|
|
360
|
-
if (O(() => {
|
|
361
|
-
var l;
|
|
362
|
-
const t = n.current;
|
|
363
|
-
if (t)
|
|
364
|
-
return t._addConsumer(r, R.current), (l = e == null ? void 0 : e.onMount) == null || l.call(e, t), () => {
|
|
365
|
-
var a, x;
|
|
366
|
-
t && ((a = e == null ? void 0 : e.onUnmount) == null || a.call(e, t), t._removeConsumer(r), (x = b.current) == null || x.reset());
|
|
367
|
-
};
|
|
368
|
-
}, [(M = n.current) == null ? void 0 : M.uid, r]), O(() => {
|
|
369
|
-
if (process.env.NODE_ENV === "development" && b.current) {
|
|
370
|
-
const t = b.current.getMetrics();
|
|
371
|
-
(t.stateAccessCount > 0 || t.classAccessCount > 0) && console.debug(`[useBloc] ${c.name} Performance Metrics:`, t);
|
|
372
|
-
}
|
|
373
|
-
}), K === void 0)
|
|
374
|
-
throw new Error(
|
|
375
|
-
`[useBloc] State is undefined for ${c.name}. This should never happen - state must be defined.`
|
|
376
|
-
);
|
|
377
|
-
if (!v)
|
|
378
|
-
throw new Error(
|
|
379
|
-
`[useBloc] Instance is null for ${c.name}. This should never happen - instance must be defined.`
|
|
380
|
-
);
|
|
381
|
-
return [K, v];
|
|
83
|
+
}),
|
|
84
|
+
[]
|
|
85
|
+
);
|
|
86
|
+
return s.current = m, {
|
|
87
|
+
externalStore: I,
|
|
88
|
+
instance: s,
|
|
89
|
+
usedKeys: d,
|
|
90
|
+
usedClassPropKeys: t,
|
|
91
|
+
rid: f.current
|
|
92
|
+
};
|
|
382
93
|
}
|
|
383
94
|
export {
|
|
384
|
-
|
|
385
|
-
|
|
95
|
+
K as useBloc,
|
|
96
|
+
M as useExternalBlocStore
|
|
386
97
|
};
|
|
387
98
|
//# sourceMappingURL=index.es.js.map
|
package/dist/index.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.es.js","sources":["../src/useExternalBlocStore.ts","../src/DependencyTracker.ts","../src/useBloc.tsx"],"sourcesContent":["import { Blac, BlacObserver, BlocBase, BlocBaseAbstract, BlocConstructor, BlocHookDependencyArrayFn, BlocState, generateUUID } from '@blac/core';\nimport { useCallback, useMemo, useRef } from 'react';\nimport { BlocHookOptions } from './useBloc';\n\nexport interface ExternalStore<\n B extends BlocConstructor<BlocBase<any>>\n> {\n /**\n * Subscribes to changes in the store and returns an unsubscribe function.\n * @param onStoreChange - Callback function that will be called whenever the store changes\n * @returns A function that can be called to unsubscribe from store changes\n */\n subscribe: (onStoreChange: (state: BlocState<InstanceType<B>>) => void) => () => void;\n\n /**\n * Gets the current snapshot of the store state.\n * @returns The current state of the store\n */\n getSnapshot: () => BlocState<InstanceType<B>> | undefined;\n\n /**\n * Gets the server snapshot of the store state.\n * This is optional and defaults to the same value as getSnapshot.\n * @returns The server state of the store\n */\n getServerSnapshot?: () => BlocState<InstanceType<B>> | undefined;\n}\n\nexport interface ExternalBlacStore<\n B extends BlocConstructor<BlocBase<any>>\n> {\n usedKeys: React.RefObject<Set<string>>;\n usedClassPropKeys: React.RefObject<Set<string>>;\n externalStore: ExternalStore<B>;\n instance: React.RefObject<InstanceType<B>>;\n rid: string;\n hasProxyTracking: React.RefObject<boolean>;\n}\n\n/**\n * Creates an external store that wraps a Bloc instance, providing a React-compatible interface\n * for subscribing to and accessing bloc state.\n */\nconst useExternalBlocStore = <\n B extends BlocConstructor<BlocBase<any>>\n>(\n bloc: B,\n options: BlocHookOptions<InstanceType<B>> | undefined,\n): ExternalBlacStore<B> => {\n const { id: blocId, props, selector } = options ?? {};\n\n const rid = useMemo(() => {\n return generateUUID();\n }, []);\n\n const base = bloc as unknown as BlocBaseAbstract;\n\n const isIsolated = base.isolated;\n const effectiveBlocId = isIsolated ? rid : blocId;\n\n const usedKeys = useRef<Set<string>>(new Set());\n const usedClassPropKeys = useRef<Set<string>>(new Set());\n \n // Track whether proxy-based dependency tracking has been initialized\n // This helps distinguish between direct external store usage and useBloc proxy usage\n const hasProxyTracking = useRef<boolean>(false);\n\n const getBloc = useCallback(() => {\n return Blac.getBloc(bloc, {\n id: effectiveBlocId,\n props,\n instanceRef: rid\n });\n }, [bloc, effectiveBlocId, props, rid]);\n\n const blocInstance = useRef<InstanceType<B>>(getBloc());\n\n // Update the instance when dependencies change\n useMemo(() => {\n blocInstance.current = getBloc();\n }, [getBloc]);\n\n // Track previous state and dependencies for selector\n const previousStateRef = useRef<BlocState<InstanceType<B>> | undefined>(undefined);\n const lastDependenciesRef = useRef<unknown[][] | undefined>(undefined);\n const lastStableSnapshot = useRef<BlocState<InstanceType<B>> | undefined>(undefined);\n \n // Track bloc instance uid to prevent unnecessary store recreation\n const blocUidRef = useRef<string | undefined>(undefined);\n if (blocUidRef.current !== blocInstance.current?.uid) {\n blocUidRef.current = blocInstance.current?.uid;\n }\n\n const dependencyArray = useMemo(\n () =>\n (newState: BlocState<InstanceType<B>>, oldState?: BlocState<InstanceType<B>>): unknown[][] => {\n const instance = blocInstance.current;\n\n if (!instance) {\n return [[], []]; // [stateArray, classArray]\n }\n\n // Use the provided oldState or fall back to our tracked previous state\n const previousState = oldState ?? previousStateRef.current;\n \n let currentDependencies: unknown[][];\n\n // Use custom dependency selector if provided\n if (selector) {\n const flatDeps = selector(newState, previousState, instance);\n // Wrap flat custom selector result in the two-array structure for consistency\n currentDependencies = [flatDeps, []]; // [customSelectorDeps, classArray]\n }\n // Fall back to bloc's default dependency selector if available\n else if (instance.defaultDependencySelector) {\n const flatDeps = instance.defaultDependencySelector(newState, previousState, instance);\n // Wrap flat default selector result in the two-array structure for consistency\n currentDependencies = [flatDeps, []]; // [defaultSelectorDeps, classArray]\n }\n // For primitive states, use default selector\n else if (typeof newState !== 'object') {\n // Default behavior for primitive states: re-render if the state itself changes.\n currentDependencies = [[newState], []]; // [primitiveStateArray, classArray]\n }\n else {\n // For object states, track which properties were actually used\n const stateDependencies: unknown[] = [];\n const classDependencies: unknown[] = [];\n \n // Add state property values that were accessed\n for (const key of usedKeys.current) {\n if (key in newState) {\n stateDependencies.push(newState[key as keyof typeof newState]);\n }\n }\n\n // Add class property values that were accessed\n for (const key of usedClassPropKeys.current) {\n if (key in instance) {\n try {\n const value = instance[key as keyof InstanceType<B>];\n if (typeof value !== 'function') {\n classDependencies.push(value);\n }\n } catch (error) {\n Blac.instance.log('useBloc Error', error);\n }\n }\n }\n\n // If no properties have been accessed through proxy\n if (usedKeys.current.size === 0 && usedClassPropKeys.current.size === 0) {\n // If proxy tracking has never been initialized, this is direct external store usage\n // In this case, always track the entire state to ensure notifications\n if (!hasProxyTracking.current) {\n stateDependencies.push(newState);\n }\n // If proxy tracking was initialized but no properties accessed, \n // return empty dependencies to prevent unnecessary re-renders\n }\n\n currentDependencies = [stateDependencies, classDependencies];\n }\n\n // Update tracked state\n previousStateRef.current = newState;\n\n\n // Return the dependencies for BlacObserver to compare\n return currentDependencies;\n },\n [],\n );\n\n // Store active subscriptions to reuse observers\n const activeObservers = useRef<Map<Function, { observer: BlacObserver<BlocState<InstanceType<B>>>, unsubscribe: () => void }>>(new Map());\n\n const state: ExternalStore<B> = useMemo(() => {\n return {\n subscribe: (listener: (state: BlocState<InstanceType<B>>) => void) => {\n \n const currentInstance = blocInstance.current;\n if (!currentInstance) {\n return () => {}; // Return no-op if no instance\n }\n\n // Check if we already have an observer for this listener\n const existing = activeObservers.current.get(listener);\n if (existing) {\n return existing.unsubscribe;\n }\n const observer: BlacObserver<BlocState<InstanceType<B>>> = {\n fn: () => {\n try {\n \n // Only reset dependency tracking if we're not using a custom selector\n // Custom selectors override proxy-based tracking entirely\n if (!selector && !currentInstance.defaultDependencySelector) {\n usedKeys.current = new Set();\n usedClassPropKeys.current = new Set();\n }\n\n // Only trigger listener if there are actual subscriptions\n listener(currentInstance.state);\n } catch (e) {\n // Log any errors that occur during the listener callback\n // This ensures errors in listeners don't break the entire application\n console.error({\n e,\n blocInstance: currentInstance,\n dependencyArray,\n });\n }\n },\n // Pass the dependency array to control when the subscription is updated\n dependencyArray,\n // Use the provided id to identify this subscription\n id: rid,\n }\n\n Blac.activateBloc(currentInstance);\n\n // Subscribe to the bloc's observer with the provided listener function\n // This will trigger the callback whenever the bloc's state changes\n const unSub = currentInstance._observer.subscribe(observer);\n\n const unsubscribe = () => {\n activeObservers.current.delete(listener);\n unSub();\n };\n\n // Store the observer and unsubscribe function\n activeObservers.current.set(listener, { observer, unsubscribe });\n\n // Return an unsubscribe function that can be called to clean up the subscription\n return unsubscribe;\n },\n // Return an immutable snapshot of the current bloc state\n getSnapshot: (): BlocState<InstanceType<B>> | undefined => {\n const instance = blocInstance.current;\n if (!instance) {\n return {} as BlocState<InstanceType<B>>;\n }\n\n const currentState = instance.state;\n const currentDependencies = dependencyArray(currentState, previousStateRef.current);\n \n // Check if dependencies have changed using the two-array comparison logic\n const lastDeps = lastDependenciesRef.current;\n let dependenciesChanged = false;\n \n if (!lastDeps) {\n // First time - dependencies changed\n dependenciesChanged = true;\n } else if (lastDeps.length !== currentDependencies.length) {\n // Array structure changed\n dependenciesChanged = true;\n } else {\n // Compare each array (state and class dependencies)\n for (let arrayIndex = 0; arrayIndex < currentDependencies.length; arrayIndex++) {\n const lastArray = lastDeps[arrayIndex] || [];\n const newArray = currentDependencies[arrayIndex] || [];\n \n if (lastArray.length !== newArray.length) {\n dependenciesChanged = true;\n break;\n }\n \n // Compare each dependency value using Object.is\n for (let i = 0; i < newArray.length; i++) {\n if (!Object.is(lastArray[i], newArray[i])) {\n dependenciesChanged = true;\n break;\n }\n }\n \n if (dependenciesChanged) break;\n }\n }\n \n // Update dependency tracking\n lastDependenciesRef.current = currentDependencies;\n \n // If dependencies haven't changed, return the same snapshot reference\n // This prevents React from re-rendering when dependencies are stable\n if (!dependenciesChanged && lastStableSnapshot.current) {\n return lastStableSnapshot.current;\n }\n \n // Dependencies changed - update and return new snapshot\n lastStableSnapshot.current = currentState;\n return currentState;\n },\n // Server snapshot mirrors the client snapshot in this implementation\n getServerSnapshot: (): BlocState<InstanceType<B>> | undefined => {\n const instance = blocInstance.current;\n if (!instance) {\n return {} as BlocState<InstanceType<B>>;\n }\n return instance.state;\n },\n }\n }, []); // Store is stable - individual methods handle instance changes\n\n return {\n usedKeys,\n usedClassPropKeys,\n externalStore: state,\n instance: blocInstance,\n rid,\n hasProxyTracking,\n };\n};\n\nexport default useExternalBlocStore;\n","\nexport interface DependencyMetrics {\n stateAccessCount: number;\n classAccessCount: number;\n proxyCreationCount: number;\n batchFlushCount: number;\n averageResolutionTime: number;\n memoryUsageKB: number;\n}\n\nexport interface DependencyTrackerConfig {\n enableBatching: boolean;\n batchTimeout: number;\n enableMetrics: boolean;\n maxCacheSize: number;\n enableDeepTracking: boolean;\n}\n\nexport type DependencyChangeCallback = (changedKeys: Set<string>) => void;\n\nexport class DependencyTracker {\n private stateKeys = new Set<string>();\n private classKeys = new Set<string>();\n private batchedCallbacks = new Set<DependencyChangeCallback>();\n private flushScheduled = false;\n private flushTimeoutId: ReturnType<typeof setTimeout> | undefined;\n \n private metrics: DependencyMetrics = {\n stateAccessCount: 0,\n classAccessCount: 0,\n proxyCreationCount: 0,\n batchFlushCount: 0,\n averageResolutionTime: 0,\n memoryUsageKB: 0,\n };\n \n private resolutionTimes: number[] = [];\n \n private config: DependencyTrackerConfig;\n \n private stateProxyCache = new WeakMap<object, object>();\n private classProxyCache = new WeakMap<object, object>();\n \n private lastStateSnapshot: unknown = null;\n private lastClassSnapshot: unknown = null;\n \n constructor(config: Partial<DependencyTrackerConfig> = {}) {\n this.config = {\n enableBatching: true,\n batchTimeout: 0, // Use React's scheduler\n enableMetrics: process.env.NODE_ENV === 'development',\n maxCacheSize: 1000,\n enableDeepTracking: false,\n ...config,\n };\n }\n\n public trackStateAccess(key: string): void {\n if (this.config.enableMetrics) {\n this.metrics.stateAccessCount++;\n }\n \n this.stateKeys.add(key);\n \n if (this.config.enableBatching) {\n this.scheduleFlush();\n }\n }\n\n public trackClassAccess(key: string): void {\n if (this.config.enableMetrics) {\n this.metrics.classAccessCount++;\n }\n \n this.classKeys.add(key);\n \n if (this.config.enableBatching) {\n this.scheduleFlush();\n }\n }\n\n public createStateProxy<T extends object>(target: T, onAccess?: (prop: string) => void): T {\n const cachedProxy = this.stateProxyCache.get(target);\n if (cachedProxy) {\n return cachedProxy as T;\n }\n\n const startTime = this.config.enableMetrics ? performance.now() : 0;\n\n const proxy = new Proxy(target, {\n get: (obj: T, prop: string | symbol) => {\n if (typeof prop === 'string') {\n this.trackStateAccess(prop);\n onAccess?.(prop);\n }\n \n const value = obj[prop as keyof T];\n \n if (\n this.config.enableDeepTracking &&\n value &&\n typeof value === 'object' &&\n !Array.isArray(value)\n ) {\n return this.createStateProxy(value as object);\n }\n \n return value;\n },\n \n has: (obj: T, prop: string | symbol) => {\n return prop in obj;\n },\n \n ownKeys: (obj: T) => {\n return Reflect.ownKeys(obj);\n },\n \n getOwnPropertyDescriptor: (obj: T, prop: string | symbol) => {\n return Reflect.getOwnPropertyDescriptor(obj, prop);\n },\n });\n\n this.stateProxyCache.set(target, proxy);\n \n if (this.config.enableMetrics) {\n this.metrics.proxyCreationCount++;\n const endTime = performance.now();\n this.resolutionTimes.push(endTime - startTime);\n this.updateAverageResolutionTime();\n }\n\n return proxy;\n }\n\n public createClassProxy<T extends object>(target: T, onAccess?: (prop: string) => void): T {\n const cachedProxy = this.classProxyCache.get(target);\n if (cachedProxy) {\n return cachedProxy as T;\n }\n\n const startTime = this.config.enableMetrics ? performance.now() : 0;\n\n const proxy = new Proxy(target, {\n get: (obj: T, prop: string | symbol) => {\n const value = obj[prop as keyof T];\n \n if (typeof prop === 'string' && typeof value !== 'function') {\n this.trackClassAccess(prop);\n onAccess?.(prop);\n }\n \n return value;\n },\n });\n\n this.classProxyCache.set(target, proxy);\n \n if (this.config.enableMetrics) {\n this.metrics.proxyCreationCount++;\n const endTime = performance.now();\n this.resolutionTimes.push(endTime - startTime);\n this.updateAverageResolutionTime();\n }\n\n return proxy;\n }\n\n public getStateKeys(): Set<string> {\n return new Set(this.stateKeys);\n }\n\n public getClassKeys(): Set<string> {\n return new Set(this.classKeys);\n }\n\n public reset(): void {\n this.stateKeys.clear();\n this.classKeys.clear();\n this.cancelScheduledFlush();\n }\n\n public subscribe(callback: DependencyChangeCallback): () => void {\n this.batchedCallbacks.add(callback);\n \n return () => {\n this.batchedCallbacks.delete(callback);\n };\n }\n\n public computeDependencyArray<TState, TClass>(\n state: TState,\n classInstance: TClass,\n ): unknown[] {\n const startTime = this.config.enableMetrics ? performance.now() : 0;\n \n if (typeof state !== 'object' || state === null) {\n return [[state]];\n }\n\n const stateValues: unknown[] = [];\n for (const key of this.stateKeys) {\n if (key in (state as object)) {\n stateValues.push((state as any)[key]);\n }\n }\n\n const classValues: unknown[] = [];\n for (const key of this.classKeys) {\n if (key in (classInstance as object)) {\n try {\n const value = (classInstance as any)[key];\n if (typeof value !== 'function') {\n classValues.push(value);\n }\n } catch (error) {\n }\n }\n }\n\n if (this.config.enableMetrics) {\n const endTime = performance.now();\n this.resolutionTimes.push(endTime - startTime);\n this.updateAverageResolutionTime();\n }\n\n if (stateValues.length === 0 && classValues.length === 0) {\n return [[]];\n }\n \n if (classValues.length === 0) {\n return [stateValues];\n }\n \n if (stateValues.length === 0) {\n return [classValues];\n }\n \n return [stateValues, classValues];\n }\n\n public getMetrics(): DependencyMetrics {\n if (!this.config.enableMetrics) {\n return {\n stateAccessCount: 0,\n classAccessCount: 0,\n proxyCreationCount: 0,\n batchFlushCount: 0,\n averageResolutionTime: 0,\n memoryUsageKB: 0,\n };\n }\n\n const estimatedMemory = \n (this.stateKeys.size * 50) +\n (this.classKeys.size * 50) +\n (this.stateProxyCache instanceof WeakMap ? 100 : 0) +\n (this.classProxyCache instanceof WeakMap ? 100 : 0);\n\n return {\n ...this.metrics,\n memoryUsageKB: Math.round(estimatedMemory / 1024),\n };\n }\n\n public clearCaches(): void {\n this.stateProxyCache = new WeakMap();\n this.classProxyCache = new WeakMap();\n this.resolutionTimes = [];\n \n if (this.config.enableMetrics) {\n this.metrics = {\n stateAccessCount: 0,\n classAccessCount: 0,\n proxyCreationCount: 0,\n batchFlushCount: 0,\n averageResolutionTime: 0,\n memoryUsageKB: 0,\n };\n }\n }\n\n private scheduleFlush(): void {\n if (this.flushScheduled) {\n return;\n }\n\n this.flushScheduled = true;\n\n if (this.config.batchTimeout > 0) {\n this.flushTimeoutId = setTimeout(() => {\n this.flushBatchedChanges();\n }, this.config.batchTimeout);\n } else {\n Promise.resolve().then(() => this.flushBatchedChanges());\n }\n }\n\n private cancelScheduledFlush(): void {\n if (this.flushTimeoutId) {\n clearTimeout(this.flushTimeoutId);\n this.flushTimeoutId = undefined;\n }\n this.flushScheduled = false;\n }\n\n private flushBatchedChanges(): void {\n if (!this.flushScheduled) {\n return;\n }\n\n this.flushScheduled = false;\n this.flushTimeoutId = undefined;\n\n if (this.config.enableMetrics) {\n this.metrics.batchFlushCount++;\n }\n\n const allChangedKeys = new Set([...this.stateKeys, ...this.classKeys]);\n \n for (const callback of this.batchedCallbacks) {\n try {\n callback(allChangedKeys);\n } catch (error) {\n console.error('Error in dependency change callback:', error);\n }\n }\n }\n\n private updateAverageResolutionTime(): void {\n if (this.resolutionTimes.length === 0) {\n return;\n }\n\n if (this.resolutionTimes.length > 100) {\n this.resolutionTimes = this.resolutionTimes.slice(-100);\n }\n\n const sum = this.resolutionTimes.reduce((a, b) => a + b, 0);\n this.metrics.averageResolutionTime = sum / this.resolutionTimes.length;\n }\n}\n\nexport function createDependencyTracker(\n config?: Partial<DependencyTrackerConfig>,\n): DependencyTracker {\n return new DependencyTracker(config);\n}\n\nexport const defaultDependencyTracker = createDependencyTracker();","import {\n BlocBase,\n BlocConstructor,\n BlocHookDependencyArrayFn,\n BlocState,\n InferPropsFromGeneric,\n} from '@blac/core';\nimport { useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\nimport useExternalBlocStore from './useExternalBlocStore';\nimport { DependencyTracker } from './DependencyTracker';\n\n/**\n * Type definition for the return type of the useBloc hook\n * @template B - Bloc constructor type\n */\ntype HookTypes<B extends BlocConstructor<BlocBase<any>>> = [\n BlocState<InstanceType<B>>,\n InstanceType<B>,\n];\n\n/**\n * Configuration options for the useBloc hook\n */\nexport interface BlocHookOptions<B extends BlocBase<any>> {\n id?: string;\n selector?: BlocHookDependencyArrayFn<BlocState<B>>;\n props?: InferPropsFromGeneric<B>;\n onMount?: (bloc: B) => void;\n onUnmount?: (bloc: B) => void;\n}\n\n/**\n * Default dependency selector that wraps the entire state in an array\n * @template T - State type\n * @returns {Array<Array<T>>} Dependency array containing the entire state\n */\n\n/**\n * React hook for integrating with Blac state management\n *\n * This hook connects a React component to a Bloc state container, providing\n * automatic re-rendering when relevant state changes, dependency tracking,\n * and proper lifecycle management.\n *\n * @template B - Bloc constructor type\n * @template O - BlocHookOptions type\n * @param {B} bloc - Bloc constructor class\n * @param {O} [options] - Configuration options for the hook\n * @returns {HookTypes<B>} Tuple containing [state, bloc instance]\n *\n * @example\n * const [state, counterBloc] = useBloc(CounterBloc);\n * // Access state\n * console.log(state.count);\n * // Call bloc methods\n * counterBloc.increment();\n */\nexport default function useBloc<B extends BlocConstructor<BlocBase<any>>>(\n bloc: B,\n options?: BlocHookOptions<InstanceType<B>>,\n): HookTypes<B> {\n const {\n externalStore,\n usedKeys,\n usedClassPropKeys,\n instance,\n rid,\n hasProxyTracking,\n } = useExternalBlocStore(bloc, options);\n\n const state = useSyncExternalStore<BlocState<InstanceType<B>>>(\n externalStore.subscribe,\n () => {\n const snapshot = externalStore.getSnapshot();\n if (snapshot === undefined) {\n throw new Error(\n `[useBloc] State snapshot is undefined for bloc ${bloc.name}. Bloc may not be properly initialized.`,\n );\n }\n return snapshot;\n },\n externalStore.getServerSnapshot\n ? () => {\n const serverSnapshot = externalStore.getServerSnapshot!();\n if (serverSnapshot === undefined) {\n throw new Error(\n `[useBloc] Server state snapshot is undefined for bloc ${bloc.name}. Bloc may not be properly initialized.`,\n );\n }\n return serverSnapshot;\n }\n : undefined,\n );\n\n const dependencyTracker = useRef<DependencyTracker | null>(null);\n if (!dependencyTracker.current) {\n dependencyTracker.current = new DependencyTracker({\n enableBatching: true,\n enableMetrics: process.env.NODE_ENV === 'development',\n enableDeepTracking: false,\n });\n }\n\n const stateProxyCache = useRef<WeakMap<object, object>>(new WeakMap());\n const classProxyCache = useRef<WeakMap<object, object>>(new WeakMap());\n\n const returnState = useMemo(() => {\n // If a custom selector is provided, don't use proxy tracking\n if (options?.selector) {\n return state;\n }\n\n hasProxyTracking.current = true;\n\n if (typeof state !== 'object' || state === null) {\n return state;\n }\n\n let proxy = stateProxyCache.current.get(state);\n if (!proxy) {\n proxy = new Proxy(state, {\n get(target, prop) {\n if (typeof prop === 'string') {\n usedKeys.current.add(prop);\n dependencyTracker.current?.trackStateAccess(prop);\n }\n const value = target[prop as keyof typeof target];\n return value;\n },\n has(target, prop) {\n return prop in target;\n },\n ownKeys(target) {\n return Reflect.ownKeys(target);\n },\n getOwnPropertyDescriptor(target, prop) {\n return Reflect.getOwnPropertyDescriptor(target, prop);\n },\n });\n stateProxyCache.current.set(state as object, proxy as object);\n }\n return proxy;\n }, [state]);\n\n const returnClass = useMemo(() => {\n if (!instance.current) {\n throw new Error(\n `[useBloc] Bloc instance is null for ${bloc.name}. This should never happen - bloc instance must be defined.`,\n );\n }\n\n let proxy = classProxyCache.current.get(instance.current);\n if (!proxy) {\n proxy = new Proxy(instance.current, {\n get(target, prop) {\n if (!target) {\n throw new Error(\n `[useBloc] Bloc target is null for ${bloc.name}. This should never happen - bloc target must be defined.`,\n );\n }\n const value = target[prop as keyof InstanceType<B>];\n if (typeof value !== 'function' && typeof prop === 'string') {\n usedClassPropKeys.current.add(prop);\n dependencyTracker.current?.trackClassAccess(prop);\n }\n return value;\n },\n });\n classProxyCache.current.set(instance.current, proxy);\n }\n return proxy;\n }, [instance.current?.uid]);\n\n const componentRef = useRef({});\n\n useEffect(() => {\n const currentInstance = instance.current;\n if (!currentInstance) return;\n\n currentInstance._addConsumer(rid, componentRef.current);\n\n options?.onMount?.(currentInstance);\n\n return () => {\n if (!currentInstance) {\n return;\n }\n options?.onUnmount?.(currentInstance);\n currentInstance._removeConsumer(rid);\n\n dependencyTracker.current?.reset();\n };\n }, [instance.current?.uid, rid]);\n useEffect(() => {\n if (process.env.NODE_ENV === 'development' && dependencyTracker.current) {\n const metrics = dependencyTracker.current.getMetrics();\n if (metrics.stateAccessCount > 0 || metrics.classAccessCount > 0) {\n console.debug(`[useBloc] ${bloc.name} Performance Metrics:`, metrics);\n }\n }\n });\n\n if (returnState === undefined) {\n throw new Error(\n `[useBloc] State is undefined for ${bloc.name}. This should never happen - state must be defined.`,\n );\n }\n if (!returnClass) {\n throw new Error(\n `[useBloc] Instance is null for ${bloc.name}. This should never happen - instance must be defined.`,\n );\n }\n\n return [returnState, returnClass] as [\n BlocState<InstanceType<B>>,\n InstanceType<B>,\n ];\n}\n"],"names":["useExternalBlocStore","bloc","options","blocId","props","selector","rid","useMemo","generateUUID","effectiveBlocId","usedKeys","useRef","usedClassPropKeys","hasProxyTracking","getBloc","useCallback","Blac","blocInstance","previousStateRef","lastDependenciesRef","lastStableSnapshot","blocUidRef","_a","_b","dependencyArray","newState","oldState","instance","previousState","currentDependencies","stateDependencies","classDependencies","key","value","error","activeObservers","state","listener","currentInstance","existing","observer","e","unSub","unsubscribe","currentState","lastDeps","dependenciesChanged","arrayIndex","lastArray","newArray","i","DependencyTracker","config","__publicField","target","onAccess","cachedProxy","startTime","proxy","obj","prop","endTime","callback","classInstance","stateValues","classValues","estimatedMemory","allChangedKeys","sum","a","b","createDependencyTracker","useBloc","externalStore","useSyncExternalStore","snapshot","serverSnapshot","dependencyTracker","stateProxyCache","classProxyCache","returnState","returnClass","componentRef","useEffect","metrics"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.es.js","sources":["../src/useBloc.ts","../src/useExternalBlocStore.ts"],"sourcesContent":["import {\n AdapterOptions,\n BlacAdapter,\n BlocBase,\n BlocConstructor,\n BlocState,\n} from '@blac/core';\nimport { useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\n\n/**\n * Type definition for the return type of the useBloc hook\n */\ntype HookTypes<B extends BlocConstructor<BlocBase<any>>> = [\n BlocState<InstanceType<B>>,\n InstanceType<B>,\n];\n\n/**\n * React hook for integrating with Blac state management\n */\nfunction useBloc<B extends BlocConstructor<BlocBase<any>>>(\n blocConstructor: B,\n options?: AdapterOptions<InstanceType<B>>,\n): HookTypes<B> {\n // Create a unique identifier for this hook instance\n const renderCount = useRef(0);\n renderCount.current++;\n\n const componentRef = useRef<object>({});\n\n // Track adapter creation\n const adapter = useMemo(() => {\n const newAdapter = new BlacAdapter<B>(\n {\n componentRef: componentRef,\n blocConstructor,\n },\n options,\n );\n return newAdapter;\n }, []);\n\n // Reset tracking at the start of each render to ensure we only track\n // properties accessed during the current render\n adapter.resetConsumerTracking();\n\n // Track options changes\n const optionsChangeCount = useRef(0);\n useEffect(() => {\n optionsChangeCount.current++;\n adapter.options = options;\n }, [options]);\n\n // Register as consumer and handle lifecycle\n const mountEffectCount = useRef(0);\n useEffect(() => {\n mountEffectCount.current++;\n adapter.mount();\n\n return () => {\n adapter.unmount();\n };\n }, [adapter.blocInstance]);\n\n // Subscribe to state changes using useSyncExternalStore\n const subscribeMemoCount = useRef(0);\n const subscribe = useMemo(() => {\n subscribeMemoCount.current++;\n let subscriptionCount = 0;\n\n return (onStoreChange: () => void) => {\n subscriptionCount++;\n const unsubscribe = adapter.createSubscription({\n onChange: () => {\n onStoreChange();\n },\n });\n\n return () => {\n unsubscribe();\n };\n };\n }, [adapter.blocInstance]);\n\n const snapshotCount = useRef(0);\n const serverSnapshotCount = useRef(0);\n\n const rawState: BlocState<InstanceType<B>> = useSyncExternalStore(\n subscribe,\n // Get snapshot\n () => {\n snapshotCount.current++;\n const bloc = adapter.blocInstance;\n const state = bloc.state;\n return state;\n },\n // Get server snapshot (same as client for now)\n () => {\n const bloc = adapter.blocInstance;\n serverSnapshotCount.current++;\n const state = bloc.state;\n return state;\n },\n );\n\n // Create proxies for fine-grained tracking (if enabled)\n const stateMemoCount = useRef(0);\n const finalState = useMemo(() => {\n stateMemoCount.current++;\n const proxyState = adapter.getProxyState(rawState);\n return proxyState;\n }, [rawState]);\n\n const blocMemoCount = useRef(0);\n const finalBloc = useMemo(() => {\n blocMemoCount.current++;\n const proxyBloc = adapter.getProxyBlocInstance();\n return proxyBloc;\n }, [adapter.blocInstance]);\n\n // Mark consumer as rendered after each render\n useEffect(() => {\n adapter.updateLastNotified(componentRef.current);\n });\n\n // Log final hook return\n return [finalState, finalBloc];\n}\n\nexport default useBloc;\n","import {\n Blac,\n BlocBase,\n BlocConstructor,\n BlocState,\n InferPropsFromGeneric,\n generateUUID,\n} from '@blac/core';\nimport { useCallback, useMemo, useRef } from 'react';\n\ninterface ExternalStoreOptions<B extends BlocBase<any>> {\n id?: string;\n props?: InferPropsFromGeneric<B>;\n selector?: (\n currentState: BlocState<B>,\n previousState: BlocState<B>,\n instance: B,\n ) => any[];\n}\n\ninterface ExternalStore<T> {\n getSnapshot: () => T | undefined;\n subscribe: (listener: () => void) => () => void;\n getServerSnapshot?: () => T | undefined;\n}\n\ninterface ExternalBlocStoreResult<B extends BlocBase<any>> {\n externalStore: ExternalStore<BlocState<B>>;\n instance: { current: B | null };\n usedKeys: { current: Set<string> };\n usedClassPropKeys: { current: Set<string> };\n rid: string;\n}\n\n/**\n * Hook for using an external bloc store\n * Provides low-level access to bloc instances for use with React's useSyncExternalStore\n */\nexport default function useExternalBlocStore<\n B extends BlocConstructor<BlocBase<any>>,\n>(\n blocConstructor: B,\n options: ExternalStoreOptions<InstanceType<B>> = {},\n): ExternalBlocStoreResult<InstanceType<B>> {\n const ridRef = useRef<string>(`external-${generateUUID()}`);\n const usedKeysRef = useRef<Set<string>>(new Set());\n const usedClassPropKeysRef = useRef<Set<string>>(new Set());\n const instanceRef = useRef<InstanceType<B> | null>(null);\n\n // Get or create bloc instance\n const bloc = useMemo(() => {\n const blac = Blac.getInstance();\n const base = blocConstructor as unknown as BlocBase<any>;\n\n // For isolated blocs, always create a new instance\n if (\n (base.constructor as any).isolated ||\n (blocConstructor as any).isolated\n ) {\n const newBloc = new blocConstructor(options?.props) as InstanceType<B>;\n const uniqueId =\n options?.id || `${blocConstructor.name}_${generateUUID()}`;\n newBloc._updateId(uniqueId);\n blac.activateBloc(newBloc);\n return newBloc;\n }\n\n // For shared blocs, use the existing getBloc logic\n return blac.getBloc(blocConstructor, {\n id: options?.id,\n props: options?.props,\n });\n }, [blocConstructor, options?.id]);\n\n // Create external store interface\n const externalStore = useMemo<ExternalStore<BlocState<InstanceType<B>>>>(\n () => ({\n getSnapshot: () => {\n const currentInstance = instanceRef.current;\n return currentInstance ? currentInstance.state : undefined;\n },\n subscribe: (listener: () => void) => {\n const currentInstance = instanceRef.current;\n if (!currentInstance) {\n // Return no-op unsubscribe if no instance\n return () => {};\n }\n\n // Wrap listener to handle errors gracefully\n const safeListener = (\n newState: BlocState<InstanceType<B>>,\n oldState: BlocState<InstanceType<B>>,\n ) => {\n try {\n // Reset tracking keys on each listener call\n usedKeysRef.current.clear();\n usedClassPropKeysRef.current.clear();\n\n // Call selector if provided\n if (options.selector && currentInstance) {\n options.selector(newState, oldState, currentInstance);\n }\n\n // Call listener with state if it expects it\n if (listener.length > 0) {\n (listener as any)(newState);\n } else {\n listener();\n }\n } catch (error) {\n console.error('Listener error in useExternalBlocStore:', error);\n // Don't rethrow to prevent breaking other listeners\n }\n };\n\n const unsubscribe = currentInstance._observer.subscribe({\n id: ridRef.current,\n fn: safeListener,\n });\n return unsubscribe;\n },\n getServerSnapshot: () => {\n const currentInstance = instanceRef.current;\n return currentInstance ? currentInstance.state : undefined;\n },\n }),\n [],\n );\n\n // Update instance ref\n instanceRef.current = bloc;\n\n return {\n externalStore,\n instance: instanceRef,\n usedKeys: usedKeysRef,\n usedClassPropKeys: usedClassPropKeysRef,\n rid: ridRef.current,\n };\n}\n"],"names":["useBloc","blocConstructor","options","renderCount","useRef","componentRef","adapter","useMemo","BlacAdapter","optionsChangeCount","useEffect","mountEffectCount","subscribeMemoCount","subscribe","onStoreChange","unsubscribe","snapshotCount","serverSnapshotCount","rawState","useSyncExternalStore","bloc","stateMemoCount","finalState","blocMemoCount","finalBloc","useExternalBlocStore","ridRef","generateUUID","usedKeysRef","usedClassPropKeysRef","instanceRef","blac","Blac","newBloc","uniqueId","externalStore","currentInstance","listener","safeListener","newState","oldState","error"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blac/react",
|
|
3
|
-
"version": "2.0.0-rc-
|
|
3
|
+
"version": "2.0.0-rc-10",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Brendan Mullins <jsnanigans@gmail.com>",
|
|
6
6
|
"main": "dist/index.cjs.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"@types/react-dom": "^18.0.0 || ^19.0.0",
|
|
31
31
|
"react": "^18.0.0 || ^19.0.0",
|
|
32
32
|
"react-dom": "^18.0.0 || ^19.0.0",
|
|
33
|
-
"@blac/core": "2.0.0-rc-
|
|
33
|
+
"@blac/core": "2.0.0-rc-10"
|
|
34
34
|
},
|
|
35
35
|
"peerDependenciesMeta": {
|
|
36
36
|
"@types/react": {
|
|
@@ -41,17 +41,17 @@
|
|
|
41
41
|
}
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@testing-library/dom": "^10.4.
|
|
44
|
+
"@testing-library/dom": "^10.4.1",
|
|
45
45
|
"@testing-library/jest-dom": "^6.6.3",
|
|
46
46
|
"@testing-library/react": "^16.3.0",
|
|
47
47
|
"@testing-library/user-event": "^14.6.1",
|
|
48
|
-
"@types/react": "^19.1.
|
|
49
|
-
"@vitejs/plugin-react": "^4.
|
|
50
|
-
"@vitest/browser": "^3.
|
|
51
|
-
"@vitest/coverage-v8": "^3.
|
|
52
|
-
"happy-dom": "^
|
|
48
|
+
"@types/react": "^19.1.8",
|
|
49
|
+
"@vitejs/plugin-react": "^4.7.0",
|
|
50
|
+
"@vitest/browser": "^3.2.4",
|
|
51
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
52
|
+
"happy-dom": "^18.0.1",
|
|
53
53
|
"jsdom": "^24.1.1",
|
|
54
|
-
"prettier": "^3.
|
|
54
|
+
"prettier": "^3.6.2",
|
|
55
55
|
"react": "^19.1.0",
|
|
56
56
|
"react-dom": "^19.1.0",
|
|
57
57
|
"typescript": "^5.8.3",
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
|
63
63
|
"prettier": "prettier --write ./src",
|
|
64
|
+
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx}\"",
|
|
64
65
|
"test": "vitest run --config vitest.config.ts",
|
|
65
66
|
"test:watch": "vitest --watch --config vitest.config.ts",
|
|
66
67
|
"typecheck": "tsc --noEmit",
|