@blac/react 2.0.21 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BlocProvider.d.cts +3 -6
- package/dist/BlocProvider.d.ts +3 -6
- package/dist/RegistryProvider-BjSXfLeK.js +40 -0
- package/dist/RegistryProvider-BjSXfLeK.js.map +1 -0
- package/dist/RegistryProvider-F2qb_prD.cjs +51 -0
- package/dist/RegistryProvider-F2qb_prD.cjs.map +1 -0
- package/dist/RegistryProvider.d.cts +9 -6
- package/dist/RegistryProvider.d.ts +9 -6
- package/dist/buildTrackedProxy.d.cts +7 -8
- package/dist/buildTrackedProxy.d.ts +7 -8
- package/dist/depSession.d.cts +54 -0
- package/dist/depSession.d.ts +54 -0
- package/dist/expandWithAncestors.d.cts +14 -0
- package/dist/expandWithAncestors.d.ts +14 -0
- package/dist/index.cjs +321 -356
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -8
- package/dist/index.d.ts +1 -8
- package/dist/index.js +316 -352
- package/dist/index.js.map +1 -1
- package/dist/testing.cjs +24 -43
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +1 -2
- package/dist/testing.d.ts +1 -2
- package/dist/testing.js +25 -44
- package/dist/testing.js.map +1 -1
- package/dist/types.d.cts +2 -4
- package/dist/types.d.ts +2 -4
- package/dist/useBloc.d.cts +2 -3
- package/dist/useBloc.d.ts +2 -3
- package/dist/useBlocDeps.d.cts +3 -3
- package/dist/useBlocDeps.d.ts +3 -3
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_RegistryProvider = require("./RegistryProvider-F2qb_prD.cjs");
|
|
2
3
|
let react = require("react");
|
|
3
|
-
let _blac_core = require("@blac/core");
|
|
4
4
|
let _dirtytalk_structural = require("@dirtytalk/structural");
|
|
5
5
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
6
|
+
let _blac_core_internal = require("@blac/core/internal");
|
|
6
7
|
//#region src/BlocProvider.tsx
|
|
7
8
|
const ProvidedArgsContext = (0, react.createContext)(/* @__PURE__ */ new Map());
|
|
8
9
|
/**
|
|
9
10
|
* Provides args to descendant `useBloc` calls for a specific bloc class via
|
|
10
|
-
* React context.
|
|
11
|
-
*
|
|
12
|
-
* Descendants calling `useBloc(Bloc)` without their own `args` resolve to the
|
|
13
|
-
* args supplied here. Own `args` on the `useBloc` call always win.
|
|
11
|
+
* React context. Own `args` on the `useBloc` call always win.
|
|
14
12
|
*
|
|
15
13
|
* Multiple `BlocProvider` wrappers for different bloc classes compose: each
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* merges its entry into the inherited map, so nested providers for different
|
|
15
|
+
* blocs do not interfere.
|
|
18
16
|
*
|
|
19
17
|
* @example
|
|
20
18
|
* ```tsx
|
|
@@ -41,7 +39,7 @@ function BlocProvider({ bloc, args, children }) {
|
|
|
41
39
|
}, [
|
|
42
40
|
parentMap,
|
|
43
41
|
bloc,
|
|
44
|
-
|
|
42
|
+
JSON.stringify(args)
|
|
45
43
|
]);
|
|
46
44
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProvidedArgsContext.Provider, {
|
|
47
45
|
value: mergedMap,
|
|
@@ -59,96 +57,274 @@ function useProvidedArgs(BlocClass) {
|
|
|
59
57
|
return (0, react.useContext)(ProvidedArgsContext).get(BlocClass);
|
|
60
58
|
}
|
|
61
59
|
//#endregion
|
|
62
|
-
//#region src/RegistryProvider.tsx
|
|
63
|
-
/**
|
|
64
|
-
* Internal context value: a registry to use in place of the module-global
|
|
65
|
-
* default (`getRegistry()`), or `null` when no provider is present.
|
|
66
|
-
*/
|
|
67
|
-
const RegistryContext = (0, react.createContext)(null);
|
|
68
|
-
/**
|
|
69
|
-
* Scopes descendant `useBloc` calls to a specific registry instead of the
|
|
70
|
-
* module-global default.
|
|
71
|
-
*
|
|
72
|
-
* Useful for test isolation, SSR isolation, and micro-frontends that must
|
|
73
|
-
* not share bloc instances with the host page.
|
|
74
|
-
*
|
|
75
|
-
* @example
|
|
76
|
-
* ```tsx
|
|
77
|
-
* <RegistryProvider registry={new StateContainerRegistry()}>
|
|
78
|
-
* <App />
|
|
79
|
-
* </RegistryProvider>
|
|
80
|
-
* ```
|
|
81
|
-
* @public
|
|
82
|
-
*/
|
|
83
|
-
function RegistryProvider({ registry, children }) {
|
|
84
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RegistryContext.Provider, {
|
|
85
|
-
value: registry,
|
|
86
|
-
children
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
//#endregion
|
|
90
60
|
//#region src/buildTrackedProxy.ts
|
|
91
61
|
function supportsTrackedState(value) {
|
|
92
|
-
return typeof value[
|
|
62
|
+
return typeof value[_blac_core_internal.WITH_TRACKED_STATE] === "function";
|
|
63
|
+
}
|
|
64
|
+
const gettersByProto = /* @__PURE__ */ new WeakMap();
|
|
65
|
+
function collectGetters(proto) {
|
|
66
|
+
const cached = gettersByProto.get(proto);
|
|
67
|
+
if (cached !== void 0) return cached;
|
|
68
|
+
const getters = /* @__PURE__ */ new Map();
|
|
69
|
+
let p = proto;
|
|
70
|
+
while (p !== null && p !== Object.prototype) {
|
|
71
|
+
for (const key of Reflect.ownKeys(p)) {
|
|
72
|
+
const desc = Object.getOwnPropertyDescriptor(p, key);
|
|
73
|
+
if (desc?.get && !getters.has(key)) getters.set(key, desc.get);
|
|
74
|
+
}
|
|
75
|
+
p = Object.getPrototypeOf(p);
|
|
76
|
+
}
|
|
77
|
+
gettersByProto.set(proto, getters);
|
|
78
|
+
return getters;
|
|
93
79
|
}
|
|
94
80
|
/**
|
|
95
81
|
* Build a per-consumer proxy for a bloc instance.
|
|
96
82
|
*
|
|
97
|
-
* Getters run with `this` bound to the
|
|
83
|
+
* Getters run with `this` bound to the real instance, not the proxy, so ES
|
|
98
84
|
* `#private` fields and methods work in user blocs. Path recording is done by
|
|
99
|
-
* `[WITH_TRACKED_STATE]`, which makes the instance's own `state` getter
|
|
100
|
-
* the render's tracking proxy for the duration
|
|
101
|
-
* chains stay tracked because the override
|
|
85
|
+
* `[WITH_TRACKED_STATE]`, which makes the instance's own `state` getter
|
|
86
|
+
* report the render's tracking proxy for the call's duration; nested getter
|
|
87
|
+
* chains stay tracked because the override remains set.
|
|
102
88
|
*
|
|
103
89
|
* One allocation per bloc acquisition (inside `useMemo`), so the proxy is
|
|
104
90
|
* stable across renders.
|
|
105
91
|
*
|
|
106
|
-
* @param onDepHandle -
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
* caching wrappers per handle to avoid re-allocation.
|
|
92
|
+
* @param onDepHandle - Invoked when a read returns a branded dep handle;
|
|
93
|
+
* returns the value (the session-bound wrapper) to expose in its place.
|
|
94
|
+
* Must cache wrappers per handle to avoid re-allocation.
|
|
110
95
|
*/
|
|
111
96
|
function buildTrackedProxy(instance, trackedStateRef, onDepHandle) {
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
while (proto && proto !== Object.prototype) {
|
|
115
|
-
const keys = [...Object.getOwnPropertyNames(proto), ...Object.getOwnPropertySymbols(proto)];
|
|
116
|
-
for (const key of keys) {
|
|
117
|
-
const desc = Object.getOwnPropertyDescriptor(proto, key);
|
|
118
|
-
if (desc?.get && !getterDescs.has(key)) getterDescs.set(key, desc);
|
|
119
|
-
}
|
|
120
|
-
proto = Object.getPrototypeOf(proto);
|
|
121
|
-
}
|
|
97
|
+
const getters = collectGetters(Object.getPrototypeOf(instance));
|
|
98
|
+
const tracksState = supportsTrackedState(instance);
|
|
122
99
|
const boundMethods = /* @__PURE__ */ new Map();
|
|
123
100
|
const wrapDepHandle = (value) => {
|
|
124
|
-
if (onDepHandle !== void 0 && (typeof value === "function" || typeof value === "object") && value !== null && value[
|
|
101
|
+
if (onDepHandle !== void 0 && (typeof value === "function" || typeof value === "object") && value !== null && value[_blac_core_internal.DEP_BRAND] !== void 0) return onDepHandle(value);
|
|
125
102
|
return value;
|
|
126
103
|
};
|
|
127
|
-
return { proxy: new Proxy(instance, {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
104
|
+
return { proxy: new Proxy(instance, {
|
|
105
|
+
get(target, key) {
|
|
106
|
+
const getter = getters.get(key);
|
|
107
|
+
if (getter !== void 0) {
|
|
108
|
+
const tracked = trackedStateRef.current;
|
|
109
|
+
return wrapDepHandle(tracked == null || !tracksState ? getter.call(target) : target[_blac_core_internal.WITH_TRACKED_STATE](tracked, getter, onDepHandle));
|
|
110
|
+
}
|
|
111
|
+
const value = Reflect.get(target, key, target);
|
|
112
|
+
if (typeof value === "function") {
|
|
113
|
+
const fn = value;
|
|
114
|
+
let cached = boundMethods.get(key);
|
|
115
|
+
if (cached === void 0 || cached.fn !== fn) {
|
|
116
|
+
cached = {
|
|
117
|
+
fn,
|
|
118
|
+
bound: fn.bind(target)
|
|
119
|
+
};
|
|
120
|
+
boundMethods.set(key, cached);
|
|
121
|
+
}
|
|
122
|
+
return wrapDepHandle(cached.bound);
|
|
123
|
+
}
|
|
124
|
+
return wrapDepHandle(value);
|
|
125
|
+
},
|
|
126
|
+
set(target, key, value) {
|
|
127
|
+
return Reflect.set(target, key, value, target);
|
|
128
|
+
}
|
|
129
|
+
}) };
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/expandWithAncestors.ts
|
|
133
|
+
/**
|
|
134
|
+
* Add an ancestor-watch id for every ancestor of every tracked leaf, so a
|
|
135
|
+
* `patch` that replaces a parent atomically (e.g. the array `items`) wakes a
|
|
136
|
+
* consumer that read a child (`items.length`).
|
|
137
|
+
*
|
|
138
|
+
* Ancestor-watch ids only match marks the source emits for atomic
|
|
139
|
+
* replacements, never a plain-object pulse-up: `{'user.email'}` does not wake
|
|
140
|
+
* when a sibling `user.name` changes. The `''` root is never added; a root
|
|
141
|
+
* change already marks `ALL_PATHS`.
|
|
142
|
+
*
|
|
143
|
+
* Returns `paths` itself when no leaf has an ancestor.
|
|
144
|
+
*/
|
|
145
|
+
function expandWithAncestors(paths, interner) {
|
|
146
|
+
if (paths === _dirtytalk_structural.ALL_PATHS) return _dirtytalk_structural.ALL_PATHS;
|
|
147
|
+
const leafPaths = paths;
|
|
148
|
+
let expanded;
|
|
149
|
+
for (const id of leafPaths) {
|
|
150
|
+
const watch = interner.ancestorWatchIds(id);
|
|
151
|
+
if (watch.length === 0) continue;
|
|
152
|
+
expanded ??= new Set(leafPaths);
|
|
153
|
+
for (let i = 0; i < watch.length; i++) expanded.add(watch[i]);
|
|
154
|
+
}
|
|
155
|
+
return expanded ?? paths;
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/depSession.ts
|
|
159
|
+
/**
|
|
160
|
+
* The containers one `useBloc` consumer reads during a render: its primary
|
|
161
|
+
* bloc plus every dep reached through `this.<handle>.track()`.
|
|
162
|
+
*
|
|
163
|
+
* The render records entries; the commit calls `reconcile`, which subscribes
|
|
164
|
+
* and takes a ref on new deps and drops the ones no longer read. `dispose`
|
|
165
|
+
* releases everything. Refs are only taken at commit, so an uncommitted render
|
|
166
|
+
* never leaks one.
|
|
167
|
+
*/
|
|
168
|
+
var DepSession = class {
|
|
169
|
+
constructor(consumerId, onChange) {
|
|
170
|
+
this.consumerId = consumerId;
|
|
171
|
+
this.onChange = onChange;
|
|
172
|
+
this.entries = /* @__PURE__ */ new Map();
|
|
173
|
+
this.subs = /* @__PURE__ */ new Map();
|
|
174
|
+
this.lastReconciled = null;
|
|
175
|
+
this.refId = `useBloc@${consumerId}:dep`;
|
|
176
|
+
}
|
|
177
|
+
begin(primary, paths) {
|
|
178
|
+
this.entries.clear();
|
|
179
|
+
this.entries.set(primary, {
|
|
180
|
+
kind: "primary",
|
|
181
|
+
paths
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
record(dep, paths, Type, key, args) {
|
|
185
|
+
const existing = this.entries.get(dep);
|
|
186
|
+
if (existing !== void 0) existing.paths = unionPaths(existing.paths, paths);
|
|
187
|
+
else this.entries.set(dep, {
|
|
188
|
+
kind: "dep",
|
|
189
|
+
paths,
|
|
190
|
+
Type,
|
|
191
|
+
key,
|
|
192
|
+
args
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
/** Forces the next `reconcile` to run in full. */
|
|
196
|
+
invalidate() {
|
|
197
|
+
this.lastReconciled = null;
|
|
198
|
+
}
|
|
199
|
+
isUnchanged() {
|
|
200
|
+
const last = this.lastReconciled;
|
|
201
|
+
if (last === null || last.size !== this.entries.size) return false;
|
|
202
|
+
for (const [container, entry] of this.entries) {
|
|
203
|
+
const prev = last.get(container);
|
|
204
|
+
if (prev === void 0 || !sameEntry(prev, entry)) return false;
|
|
205
|
+
}
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
reconcile(registry) {
|
|
209
|
+
const { consumerId, refId, subs, entries } = this;
|
|
210
|
+
for (const [dep, sub] of subs) if (!entries.has(dep)) {
|
|
211
|
+
this.drop(dep, sub);
|
|
212
|
+
subs.delete(dep);
|
|
133
213
|
}
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
boundMethods.set(key, bound);
|
|
214
|
+
for (const [dep, entry] of entries) {
|
|
215
|
+
if (entry.kind === "primary") continue;
|
|
216
|
+
if (dep.$blac.disposed) {
|
|
217
|
+
this.onChange();
|
|
218
|
+
continue;
|
|
140
219
|
}
|
|
141
|
-
|
|
220
|
+
const interest = expandWithAncestors(entry.paths, dep.interner);
|
|
221
|
+
dep.registerConsumerPaths(consumerId, entry.paths);
|
|
222
|
+
const existing = subs.get(dep);
|
|
223
|
+
if (existing) {
|
|
224
|
+
existing.interestRef.current = interest;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (registry.acquire(entry.Type, entry.key, {
|
|
228
|
+
canCreate: true,
|
|
229
|
+
countRef: true,
|
|
230
|
+
refId,
|
|
231
|
+
args: entry.args
|
|
232
|
+
}) !== dep) {
|
|
233
|
+
registry.release(entry.Type, entry.key, false, refId);
|
|
234
|
+
this.onChange();
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
const interestRef = { current: interest };
|
|
238
|
+
const unsubscribe = dep.channel.subscribe(() => interestRef.current, this.onChange);
|
|
239
|
+
subs.set(dep, {
|
|
240
|
+
unsubscribe,
|
|
241
|
+
interestRef,
|
|
242
|
+
registry,
|
|
243
|
+
Type: entry.Type,
|
|
244
|
+
key: entry.key
|
|
245
|
+
});
|
|
142
246
|
}
|
|
143
|
-
|
|
144
|
-
}
|
|
247
|
+
this.lastReconciled = new Map(entries);
|
|
248
|
+
}
|
|
249
|
+
dispose() {
|
|
250
|
+
for (const [dep, sub] of this.subs) this.drop(dep, sub);
|
|
251
|
+
this.subs.clear();
|
|
252
|
+
this.lastReconciled = null;
|
|
253
|
+
}
|
|
254
|
+
drop(dep, sub) {
|
|
255
|
+
sub.unsubscribe();
|
|
256
|
+
dep.unregisterConsumer(this.consumerId);
|
|
257
|
+
sub.registry.release(sub.Type, sub.key, false, this.refId);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
function sameEntry(a, b) {
|
|
261
|
+
if (a.kind !== b.kind || !(0, _dirtytalk_structural.pathSetEquals)(a.paths, b.paths)) return false;
|
|
262
|
+
if (a.kind === "primary" || b.kind === "primary") return true;
|
|
263
|
+
return a.key === b.key && Object.is(a.args, b.args);
|
|
264
|
+
}
|
|
265
|
+
function unionPaths(a, b) {
|
|
266
|
+
if (a === _dirtytalk_structural.ALL_PATHS || b === _dirtytalk_structural.ALL_PATHS) return _dirtytalk_structural.ALL_PATHS;
|
|
267
|
+
const out = new Set(a);
|
|
268
|
+
for (const id of b) out.add(id);
|
|
269
|
+
return out;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Wrap a dep handle read inside a tracked getter. During a render (`tracked`
|
|
273
|
+
* is set) `.track()` records the dep's read paths into `session` and returns
|
|
274
|
+
* a tracked proxy, so the dep's own getters track too. Outside a render it
|
|
275
|
+
* returns the live `[state, dep]`, like the core handle.
|
|
276
|
+
*
|
|
277
|
+
* Args resolve per call, so one handle can reach several instances; proxies
|
|
278
|
+
* are cached per resolved instance.
|
|
279
|
+
*/
|
|
280
|
+
function makeDepWrapper(handle, registry, session, tracked, onDepHandle) {
|
|
281
|
+
const brand = handle[_blac_core_internal.DEP_BRAND];
|
|
282
|
+
const perDep = /* @__PURE__ */ new Map();
|
|
283
|
+
const proxyCache = new _dirtytalk_structural.ProxyCache();
|
|
284
|
+
const resolve = (options) => {
|
|
285
|
+
const args = options?.args ?? brand.defaultArgs;
|
|
286
|
+
const key = registry.resolveKey(brand.Type, void 0, args);
|
|
287
|
+
return {
|
|
288
|
+
dep: registry.acquire(brand.Type, key, {
|
|
289
|
+
canCreate: true,
|
|
290
|
+
countRef: false,
|
|
291
|
+
args,
|
|
292
|
+
sweepIfUnowned: tracked.current != null
|
|
293
|
+
}),
|
|
294
|
+
key,
|
|
295
|
+
args
|
|
296
|
+
};
|
|
297
|
+
};
|
|
298
|
+
const wrapper = {
|
|
299
|
+
untracked: (options) => resolve(options).dep,
|
|
300
|
+
track: (options) => {
|
|
301
|
+
const { dep, key, args } = resolve(options);
|
|
302
|
+
if (tracked.current == null) return [dep.state, dep];
|
|
303
|
+
const result = (0, _dirtytalk_structural.trackRender)(dep.state, dep.interner, proxyCache);
|
|
304
|
+
let cache = perDep.get(dep);
|
|
305
|
+
if (cache === void 0) {
|
|
306
|
+
const ref = { current: result.value };
|
|
307
|
+
cache = {
|
|
308
|
+
ref,
|
|
309
|
+
proxy: buildTrackedProxy(dep, ref, onDepHandle).proxy
|
|
310
|
+
};
|
|
311
|
+
perDep.set(dep, cache);
|
|
312
|
+
} else cache.ref.current = result.value;
|
|
313
|
+
session.record(dep, result.paths, brand.Type, key, args);
|
|
314
|
+
return [result.value, cache.proxy];
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
Object.defineProperty(wrapper, _blac_core_internal.DEP_BRAND, {
|
|
318
|
+
value: brand,
|
|
319
|
+
enumerable: false,
|
|
320
|
+
writable: false,
|
|
321
|
+
configurable: false
|
|
322
|
+
});
|
|
323
|
+
return wrapper;
|
|
145
324
|
}
|
|
146
325
|
//#endregion
|
|
147
326
|
//#region src/useBloc.ts
|
|
148
327
|
let nextConsumerId = 0;
|
|
149
|
-
const ARGS_UNSET = Symbol("blac.argsKeyUnset");
|
|
150
|
-
const primaryRefId = (consumerId) => `useBloc@${consumerId}`;
|
|
151
|
-
const depRefId = (consumerId) => `useBloc@${consumerId}:dep`;
|
|
152
328
|
/**
|
|
153
329
|
* React hook that connects a component to a state container with automatic
|
|
154
330
|
* re-render on state changes.
|
|
@@ -156,8 +332,7 @@ const depRefId = (consumerId) => `useBloc@${consumerId}:dep`;
|
|
|
156
332
|
* Two tracking modes:
|
|
157
333
|
* - **Auto-tracking** (default): the returned state value is a proxy that
|
|
158
334
|
* records read paths during render. The component re-renders when any
|
|
159
|
-
* recorded path changes.
|
|
160
|
-
* `trackRender` + the container's path-scoped `DirtyChannel`.
|
|
335
|
+
* recorded path changes.
|
|
161
336
|
* - **Manual select**: pass `options.select` to opt out of auto-tracking.
|
|
162
337
|
* The hook re-renders only when the returned array's elements change
|
|
163
338
|
* (per-index `Object.is`).
|
|
@@ -180,7 +355,7 @@ const depRefId = (consumerId) => `useBloc@${consumerId}:dep`;
|
|
|
180
355
|
* @typeParam T - The state container constructor type (inferred from BlocClass)
|
|
181
356
|
* @param BlocClass - The state container class to connect to
|
|
182
357
|
* @param options - Configuration options
|
|
183
|
-
* @returns Tuple of `[state, bloc
|
|
358
|
+
* @returns Tuple of `[state, bloc]`
|
|
184
359
|
*
|
|
185
360
|
* @example Basic usage
|
|
186
361
|
* ```ts
|
|
@@ -201,77 +376,41 @@ const depRefId = (consumerId) => `useBloc@${consumerId}:dep`;
|
|
|
201
376
|
* @public
|
|
202
377
|
*/
|
|
203
378
|
function useBloc(BlocClass, options) {
|
|
204
|
-
const registry =
|
|
205
|
-
const consumerIdRef = (0, react.useRef)(null);
|
|
206
|
-
if (consumerIdRef.current === null) consumerIdRef.current = `useBloc-${nextConsumerId++}`;
|
|
207
|
-
const consumerId = consumerIdRef.current;
|
|
208
|
-
const selectRef = (0, react.useRef)(options?.select);
|
|
209
|
-
selectRef.current = options?.select;
|
|
210
|
-
const onMountRef = (0, react.useRef)(options?.onMount);
|
|
211
|
-
onMountRef.current = options?.onMount;
|
|
212
|
-
const onUnmountRef = (0, react.useRef)(options?.onUnmount);
|
|
213
|
-
onUnmountRef.current = options?.onUnmount;
|
|
214
|
-
const ownArgs = options?.args;
|
|
215
|
-
const ownArgsRef = (0, react.useRef)(ownArgs);
|
|
216
|
-
ownArgsRef.current = ownArgs;
|
|
217
|
-
const ownArgsKeyRef = (0, react.useRef)({
|
|
218
|
-
ref: ARGS_UNSET,
|
|
219
|
-
key: void 0
|
|
220
|
-
});
|
|
221
|
-
if (!Object.is(ownArgsKeyRef.current.ref, ownArgs)) ownArgsKeyRef.current = {
|
|
222
|
-
ref: ownArgs,
|
|
223
|
-
key: ownArgs === void 0 ? void 0 : JSON.stringify(ownArgs)
|
|
224
|
-
};
|
|
225
|
-
const ownArgsKey = ownArgsKeyRef.current.key;
|
|
226
|
-
const providerArgs = useProvidedArgs(BlocClass);
|
|
227
|
-
const providerArgsRef = (0, react.useRef)(providerArgs);
|
|
228
|
-
providerArgsRef.current = providerArgs;
|
|
229
|
-
const providerArgsKeyRef = (0, react.useRef)({
|
|
230
|
-
ref: ARGS_UNSET,
|
|
231
|
-
key: void 0
|
|
232
|
-
});
|
|
233
|
-
if (!Object.is(providerArgsKeyRef.current.ref, providerArgs)) providerArgsKeyRef.current = {
|
|
234
|
-
ref: providerArgs,
|
|
235
|
-
key: providerArgs === void 0 ? void 0 : JSON.stringify(providerArgs)
|
|
236
|
-
};
|
|
237
|
-
const providerArgsKey = providerArgsKeyRef.current.key;
|
|
238
|
-
const resolveEffectiveArgs = () => ownArgsRef.current !== void 0 ? ownArgsRef.current : providerArgsRef.current;
|
|
239
|
-
const trackedStateRef = (0, react.useRef)(null);
|
|
240
|
-
const proxyCacheRef = (0, react.useRef)(new _dirtytalk_structural.ProxyCache());
|
|
241
|
-
const depWrapperCacheRef = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
242
|
-
const rebindNonceRef = (0, react.useRef)(0);
|
|
243
|
-
const rebindNonce = rebindNonceRef.current;
|
|
379
|
+
const registry = require_RegistryProvider.useRegistry();
|
|
244
380
|
const consumerRef = (0, react.useRef)(null);
|
|
245
|
-
consumerRef.current ??= createConsumer();
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
381
|
+
const consumer = consumerRef.current ??= createConsumer();
|
|
382
|
+
const consumerId = consumer.id;
|
|
383
|
+
consumer.select = options?.select;
|
|
384
|
+
consumer.onMount = options?.onMount;
|
|
385
|
+
consumer.onUnmount = options?.onUnmount;
|
|
386
|
+
consumer.ownArgs = options?.args;
|
|
387
|
+
consumer.providerArgs = useProvidedArgs(BlocClass);
|
|
388
|
+
const effectiveArgs = resolveEffectiveArgs(consumer);
|
|
389
|
+
const instanceKey = registry.resolveKey(BlocClass, void 0, effectiveArgs);
|
|
390
|
+
const rebindNonce = consumer.rebindNonce;
|
|
391
|
+
const { bloc, trackedBloc } = (0, react.useMemo)(() => {
|
|
392
|
+
const instance = registry.acquire(BlocClass, instanceKey, {
|
|
252
393
|
canCreate: true,
|
|
253
394
|
countRef: false,
|
|
254
395
|
args: effectiveArgs,
|
|
255
396
|
sweepIfUnowned: true
|
|
256
397
|
});
|
|
398
|
+
const depWrappers = /* @__PURE__ */ new Map();
|
|
257
399
|
const onDepHandle = (handle) => {
|
|
258
|
-
const
|
|
259
|
-
const cached = cache.get(handle);
|
|
400
|
+
const cached = depWrappers.get(handle);
|
|
260
401
|
if (cached !== void 0) return cached;
|
|
261
|
-
const wrapper = makeDepWrapper(handle,
|
|
262
|
-
|
|
402
|
+
const wrapper = makeDepWrapper(handle, registry, consumer.deps, consumer.tracked, onDepHandle);
|
|
403
|
+
depWrappers.set(handle, wrapper);
|
|
263
404
|
return wrapper;
|
|
264
405
|
};
|
|
265
|
-
const { proxy } = buildTrackedProxy(instance,
|
|
406
|
+
const { proxy } = buildTrackedProxy(instance, consumer.tracked, onDepHandle);
|
|
266
407
|
return {
|
|
267
408
|
bloc: instance,
|
|
268
|
-
instanceKey: resolvedKey,
|
|
269
409
|
trackedBloc: proxy
|
|
270
410
|
};
|
|
271
411
|
}, [
|
|
272
412
|
BlocClass,
|
|
273
|
-
|
|
274
|
-
providerArgsKey,
|
|
413
|
+
instanceKey,
|
|
275
414
|
rebindNonce,
|
|
276
415
|
registry
|
|
277
416
|
]);
|
|
@@ -281,7 +420,7 @@ function useBloc(BlocClass, options) {
|
|
|
281
420
|
const container = consumer.container;
|
|
282
421
|
const unsubscribe = container.channel.subscribe(() => consumer.interest, () => {
|
|
283
422
|
if (consumer.isSelectMode) {
|
|
284
|
-
const select =
|
|
423
|
+
const select = consumer.select;
|
|
285
424
|
if (select) {
|
|
286
425
|
const next = select(container.state, container);
|
|
287
426
|
const prev = consumer.selection;
|
|
@@ -301,24 +440,25 @@ function useBloc(BlocClass, options) {
|
|
|
301
440
|
BlocClass,
|
|
302
441
|
instanceKey,
|
|
303
442
|
consumerId,
|
|
304
|
-
rebindNonce
|
|
443
|
+
rebindNonce,
|
|
444
|
+
registry
|
|
305
445
|
]), consumer.getSnapshot, consumer.getServerSnapshot);
|
|
306
446
|
(0, react.useLayoutEffect)(() => {
|
|
307
447
|
const live = registry.acquire(BlocClass, instanceKey, {
|
|
308
448
|
canCreate: true,
|
|
309
449
|
countRef: true,
|
|
310
|
-
refId: primaryRefId
|
|
311
|
-
args: resolveEffectiveArgs()
|
|
450
|
+
refId: consumer.primaryRefId,
|
|
451
|
+
args: resolveEffectiveArgs(consumer)
|
|
312
452
|
});
|
|
313
|
-
|
|
314
|
-
|
|
453
|
+
consumer.ownedBloc = live;
|
|
454
|
+
consumer.onMount?.(live);
|
|
315
455
|
if (live !== bloc) {
|
|
316
|
-
|
|
456
|
+
consumer.rebindNonce++;
|
|
317
457
|
consumer.bump();
|
|
318
458
|
}
|
|
319
459
|
return () => {
|
|
320
|
-
|
|
321
|
-
registry.release(BlocClass, instanceKey, false, primaryRefId
|
|
460
|
+
consumer.onUnmount?.(consumer.ownedBloc ?? bloc);
|
|
461
|
+
registry.release(BlocClass, instanceKey, false, consumer.primaryRefId);
|
|
322
462
|
};
|
|
323
463
|
}, [
|
|
324
464
|
BlocClass,
|
|
@@ -328,115 +468,48 @@ function useBloc(BlocClass, options) {
|
|
|
328
468
|
]);
|
|
329
469
|
const container = consumer.container;
|
|
330
470
|
const rawState = container.state;
|
|
331
|
-
const select =
|
|
471
|
+
const select = consumer.select;
|
|
332
472
|
consumer.isSelectMode = select !== void 0;
|
|
333
473
|
let state;
|
|
334
474
|
if (select !== void 0) {
|
|
335
475
|
state = rawState;
|
|
336
|
-
|
|
476
|
+
consumer.selection = select(rawState, bloc);
|
|
337
477
|
consumer.interest = _dirtytalk_structural.ALL_PATHS;
|
|
338
478
|
} else {
|
|
339
|
-
const tracked = (0, _dirtytalk_structural.trackRender)(rawState, container.interner,
|
|
479
|
+
const tracked = (0, _dirtytalk_structural.trackRender)(rawState, container.interner, consumer.proxyCache ??= new _dirtytalk_structural.ProxyCache());
|
|
340
480
|
state = tracked.value;
|
|
341
|
-
|
|
481
|
+
consumer.tracked.current = tracked.value;
|
|
342
482
|
consumer.paths = tracked.paths;
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
session.set(container, {
|
|
346
|
-
kind: "primary",
|
|
347
|
-
paths: tracked.paths
|
|
348
|
-
});
|
|
349
|
-
queueMicrotask(tracked.disarm);
|
|
483
|
+
consumer.disarm = tracked.disarm;
|
|
484
|
+
consumer.deps.begin(container, tracked.paths);
|
|
350
485
|
}
|
|
351
486
|
(0, react.useLayoutEffect)(() => {
|
|
352
|
-
|
|
487
|
+
consumer.tracked.current = null;
|
|
488
|
+
const disarm = consumer.disarm;
|
|
489
|
+
if (disarm !== null) {
|
|
490
|
+
consumer.disarm = null;
|
|
491
|
+
disarm();
|
|
492
|
+
}
|
|
493
|
+
const deps = consumer.deps;
|
|
353
494
|
if (consumer.isSelectMode) {
|
|
354
|
-
|
|
495
|
+
deps.invalidate();
|
|
355
496
|
return;
|
|
356
497
|
}
|
|
498
|
+
if (deps.isUnchanged()) return;
|
|
357
499
|
const primary = consumer.container;
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
if (last !== null && last.primaryContainer === primary && (0, _dirtytalk_structural.pathSetEquals)(last.primaryPaths, paths)) {
|
|
362
|
-
let unchanged = last.deps.size === session.size - 1;
|
|
363
|
-
if (unchanged) for (const [depContainer, entry] of session) {
|
|
364
|
-
if (entry.kind === "primary") continue;
|
|
365
|
-
const prevEntry = last.deps.get(depContainer);
|
|
366
|
-
if (prevEntry === void 0 || prevEntry.key !== entry.key || prevEntry.refId !== entry.refId || !Object.is(prevEntry.args, entry.args) || !(0, _dirtytalk_structural.pathSetEquals)(prevEntry.paths, entry.paths)) {
|
|
367
|
-
unchanged = false;
|
|
368
|
-
break;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
if (unchanged) return;
|
|
372
|
-
}
|
|
373
|
-
primary.registerConsumerPaths(consumerId, paths);
|
|
374
|
-
consumer.interest = expandWithAncestors(paths, primary.interner);
|
|
375
|
-
const subs = consumer.depSubs;
|
|
376
|
-
for (const [depContainer, sub] of subs) if (!session.has(depContainer)) {
|
|
377
|
-
sub.unsubscribe();
|
|
378
|
-
depContainer.unregisterConsumer(consumerId);
|
|
379
|
-
registry.release(sub.Type, sub.key, false, sub.refId);
|
|
380
|
-
subs.delete(depContainer);
|
|
381
|
-
}
|
|
382
|
-
for (const [depContainer, entry] of session) {
|
|
383
|
-
if (entry.kind === "primary") continue;
|
|
384
|
-
const interest = expandWithAncestors(entry.paths, depContainer.interner);
|
|
385
|
-
depContainer.registerConsumerPaths(consumerId, entry.paths);
|
|
386
|
-
const existing = subs.get(depContainer);
|
|
387
|
-
if (existing) {
|
|
388
|
-
existing.interestRef.current = interest;
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
registry.acquire(entry.Type, entry.key, {
|
|
392
|
-
canCreate: true,
|
|
393
|
-
countRef: true,
|
|
394
|
-
refId: entry.refId,
|
|
395
|
-
args: entry.args
|
|
396
|
-
});
|
|
397
|
-
const interestRef = { current: interest };
|
|
398
|
-
const unsubscribe = depContainer.channel.subscribe(() => interestRef.current, () => consumer.bump());
|
|
399
|
-
subs.set(depContainer, {
|
|
400
|
-
unsubscribe,
|
|
401
|
-
interestRef,
|
|
402
|
-
Type: entry.Type,
|
|
403
|
-
key: entry.key,
|
|
404
|
-
refId: entry.refId,
|
|
405
|
-
args: entry.args
|
|
406
|
-
});
|
|
407
|
-
}
|
|
408
|
-
const depsSignature = /* @__PURE__ */ new Map();
|
|
409
|
-
for (const [depContainer, entry] of session) {
|
|
410
|
-
if (entry.kind === "primary") continue;
|
|
411
|
-
depsSignature.set(depContainer, {
|
|
412
|
-
paths: entry.paths,
|
|
413
|
-
key: entry.key,
|
|
414
|
-
refId: entry.refId,
|
|
415
|
-
args: entry.args
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
consumer.lastReconcile = {
|
|
419
|
-
primaryContainer: primary,
|
|
420
|
-
primaryPaths: paths,
|
|
421
|
-
deps: depsSignature
|
|
422
|
-
};
|
|
500
|
+
primary.registerConsumerPaths(consumerId, consumer.paths);
|
|
501
|
+
consumer.interest = expandWithAncestors(consumer.paths, primary.interner);
|
|
502
|
+
deps.reconcile(registry);
|
|
423
503
|
});
|
|
424
|
-
(0, react.useEffect)(() =>
|
|
425
|
-
const subs = consumer.depSubs;
|
|
426
|
-
return () => {
|
|
427
|
-
for (const [depContainer, sub] of subs) {
|
|
428
|
-
sub.unsubscribe();
|
|
429
|
-
depContainer.unregisterConsumer(consumerId);
|
|
430
|
-
registry.release(sub.Type, sub.key, false, sub.refId);
|
|
431
|
-
}
|
|
432
|
-
subs.clear();
|
|
433
|
-
};
|
|
434
|
-
}, [consumerId, registry]);
|
|
504
|
+
(0, react.useEffect)(() => () => consumer.deps.dispose(), [consumerId]);
|
|
435
505
|
return [state, trackedBloc];
|
|
436
506
|
}
|
|
437
507
|
const noop = () => {};
|
|
438
508
|
function createConsumer() {
|
|
509
|
+
const id = `useBloc-${nextConsumerId++}`;
|
|
439
510
|
const consumer = {
|
|
511
|
+
id,
|
|
512
|
+
primaryRefId: `useBloc@${id}`,
|
|
440
513
|
container: null,
|
|
441
514
|
version: 0,
|
|
442
515
|
notify: noop,
|
|
@@ -450,144 +523,35 @@ function createConsumer() {
|
|
|
450
523
|
paths: (0, _dirtytalk_structural.emptyPathSet)(),
|
|
451
524
|
isSelectMode: false,
|
|
452
525
|
selection: null,
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
526
|
+
deps: new DepSession(id, () => consumer.bump()),
|
|
527
|
+
select: void 0,
|
|
528
|
+
onMount: void 0,
|
|
529
|
+
onUnmount: void 0,
|
|
530
|
+
ownArgs: void 0,
|
|
531
|
+
providerArgs: void 0,
|
|
532
|
+
tracked: { current: null },
|
|
533
|
+
disarm: null,
|
|
534
|
+
proxyCache: null,
|
|
535
|
+
rebindNonce: 0,
|
|
536
|
+
ownedBloc: null
|
|
456
537
|
};
|
|
457
538
|
return consumer;
|
|
458
539
|
}
|
|
459
|
-
|
|
460
|
-
* Build the per-consumer wrapper that replaces a branded dep handle inside a
|
|
461
|
-
* tracked getter's `this`. The wrapper exposes the same accessors as the core
|
|
462
|
-
* handle and overrides `.track()`:
|
|
463
|
-
*
|
|
464
|
-
* - **Inside a render** (`trackedStateRef.current != null`): resolve (ENSURE,
|
|
465
|
-
* no ref) the dep, `trackRender` its state, merge the recorded paths into the
|
|
466
|
-
* session entry, build/reuse a tracked proxy for the dep so its OWN getters
|
|
467
|
-
* track too, and return `[trackedValue, depProxy]`. The ownership ref is taken
|
|
468
|
-
* by the layout-effect reconcile pass, not here.
|
|
469
|
-
* - **Outside a render**: degrade to live `[dep.state, dep]` — matches the core
|
|
470
|
-
* base impl, safe in event handlers/effects/methods.
|
|
471
|
-
*
|
|
472
|
-
* `.untracked()` always returns the live instance with no subscription.
|
|
473
|
-
*
|
|
474
|
-
* Args resolve at call time (`options.args ?? defaultArgs`), so a single handle
|
|
475
|
-
* can resolve different dep instances across calls; tracked-proxy state is
|
|
476
|
-
* therefore cached per resolved instance, not per handle. Guards against a
|
|
477
|
-
* container re-entering tracking within the same render (mutual A↔B deps): if
|
|
478
|
-
* the dep already has a non-primary session entry this render, reuse its proxy
|
|
479
|
-
* + union its paths instead of re-acquiring.
|
|
480
|
-
*/
|
|
481
|
-
function makeDepWrapper(handle, consumerId, registry, trackedStateRef, consumer, onDepHandle) {
|
|
482
|
-
const brand = handle[_blac_core.DEP_BRAND];
|
|
483
|
-
const refId = depRefId(consumerId);
|
|
484
|
-
const perDep = /* @__PURE__ */ new Map();
|
|
485
|
-
const proxyCache = new _dirtytalk_structural.ProxyCache();
|
|
486
|
-
const resolve = (options) => {
|
|
487
|
-
const args = options?.args ?? brand.defaultArgs;
|
|
488
|
-
const key = registry.resolveKey(brand.Type, void 0, args);
|
|
489
|
-
return {
|
|
490
|
-
dep: registry.ensure(brand.Type, key, args),
|
|
491
|
-
key,
|
|
492
|
-
args
|
|
493
|
-
};
|
|
494
|
-
};
|
|
495
|
-
const wrapper = {
|
|
496
|
-
untracked: (options) => resolve(options).dep,
|
|
497
|
-
track: (options) => {
|
|
498
|
-
const { dep, key, args } = resolve(options);
|
|
499
|
-
if (trackedStateRef.current == null) return [dep.state, dep];
|
|
500
|
-
const session = consumer.session;
|
|
501
|
-
const existing = session.get(dep);
|
|
502
|
-
const tracked = (0, _dirtytalk_structural.trackRender)(dep.state, dep.interner, proxyCache);
|
|
503
|
-
let cache = perDep.get(dep);
|
|
504
|
-
if (cache === void 0) {
|
|
505
|
-
const ref = { current: tracked.value };
|
|
506
|
-
cache = {
|
|
507
|
-
ref,
|
|
508
|
-
proxy: buildTrackedProxy(dep, ref, onDepHandle).proxy
|
|
509
|
-
};
|
|
510
|
-
perDep.set(dep, cache);
|
|
511
|
-
} else cache.ref.current = tracked.value;
|
|
512
|
-
if (existing !== void 0) existing.paths = unionPaths(existing.paths, tracked.paths);
|
|
513
|
-
else session.set(dep, {
|
|
514
|
-
kind: "dep",
|
|
515
|
-
paths: tracked.paths,
|
|
516
|
-
Type: brand.Type,
|
|
517
|
-
key,
|
|
518
|
-
refId,
|
|
519
|
-
args
|
|
520
|
-
});
|
|
521
|
-
return [tracked.value, cache.proxy];
|
|
522
|
-
}
|
|
523
|
-
};
|
|
524
|
-
Object.defineProperty(wrapper, _blac_core.DEP_BRAND, {
|
|
525
|
-
value: brand,
|
|
526
|
-
enumerable: false,
|
|
527
|
-
writable: false,
|
|
528
|
-
configurable: false
|
|
529
|
-
});
|
|
530
|
-
return wrapper;
|
|
531
|
-
}
|
|
532
|
-
/** Union two PathSets (ALL_PATHS dominates). */
|
|
533
|
-
function unionPaths(a, b) {
|
|
534
|
-
if (a === _dirtytalk_structural.ALL_PATHS || b === _dirtytalk_structural.ALL_PATHS) return _dirtytalk_structural.ALL_PATHS;
|
|
535
|
-
const out = new Set(a);
|
|
536
|
-
for (const id of b) out.add(id);
|
|
537
|
-
return out;
|
|
538
|
-
}
|
|
540
|
+
const resolveEffectiveArgs = (consumer) => consumer.ownArgs !== void 0 ? consumer.ownArgs : consumer.providerArgs;
|
|
539
541
|
const shallowArrayEqual = (a, b) => {
|
|
540
542
|
if (a === b) return true;
|
|
541
543
|
if (a.length !== b.length) return false;
|
|
542
544
|
for (let i = 0; i < a.length; i++) if (!Object.is(a[i], b[i])) return false;
|
|
543
545
|
return true;
|
|
544
546
|
};
|
|
545
|
-
/**
|
|
546
|
-
* Expand a PathSet to include an *ancestor-watch* id for every ancestor of
|
|
547
|
-
* every tracked leaf.
|
|
548
|
-
*
|
|
549
|
-
* The auto-tracker records leaf paths (e.g. `'items.length'`), but
|
|
550
|
-
* `StructuralContainer.patch` can only mark the parent (`'items'`) when it
|
|
551
|
-
* replaces a value atomically (arrays, `null`, primitives — it can't see
|
|
552
|
-
* inside). Without expansion, a subscriber with interest `{'items.length'}`
|
|
553
|
-
* would miss a `patch`-triggered atomic-replacement of `items`.
|
|
554
|
-
*
|
|
555
|
-
* Ancestors are added under the interner's *ancestor-watch* lane
|
|
556
|
-
* (`internAncestor`), NOT as normal ids. The source emits a matching
|
|
557
|
-
* ancestor-watch mark only for paths it replaces atomically — never for a
|
|
558
|
-
* plain-object structural pulse-up. So `{'items.length'}` wakes when the array
|
|
559
|
-
* `items` is replaced, but `{'user.email'}` does NOT wake when a sibling
|
|
560
|
-
* `user.name` changes and pulses `user` up: pulse-up `user` is a normal id and
|
|
561
|
-
* the ancestor-watch `user` only intersects another ancestor-watch `user`.
|
|
562
|
-
*
|
|
563
|
-
* Example: leaf `'a.b.c'` adds ancestor-watch ids for `'a.b'` and `'a'` (but
|
|
564
|
-
* NOT the `''` root — a root change is covered by `ALL_PATHS` from the source,
|
|
565
|
-
* and `''` would wake this consumer on every field change).
|
|
566
|
-
*/
|
|
567
|
-
function expandWithAncestors(paths, interner) {
|
|
568
|
-
if (paths === _dirtytalk_structural.ALL_PATHS) return _dirtytalk_structural.ALL_PATHS;
|
|
569
|
-
const leafPaths = paths;
|
|
570
|
-
if (leafPaths.size === 0) return paths;
|
|
571
|
-
const expanded = new Set(leafPaths);
|
|
572
|
-
for (const id of leafPaths) {
|
|
573
|
-
const str = interner.lookup(id);
|
|
574
|
-
let idx = str.lastIndexOf(".");
|
|
575
|
-
while (idx > 0) {
|
|
576
|
-
const ancestor = str.slice(0, idx);
|
|
577
|
-
expanded.add(interner.internAncestor(ancestor));
|
|
578
|
-
idx = ancestor.lastIndexOf(".");
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
return expanded;
|
|
582
|
-
}
|
|
583
547
|
//#endregion
|
|
584
548
|
//#region src/useBlocDeps.ts
|
|
585
549
|
/**
|
|
586
550
|
* Feed live values from the component into a bloc's `deps` lane.
|
|
587
551
|
*
|
|
588
552
|
* Each calling component is one owner: its slice is shallow-merged into the
|
|
589
|
-
* bloc's merged `deps` view
|
|
590
|
-
*
|
|
553
|
+
* bloc's merged `deps` view and withdrawn on unmount. The bloc observes
|
|
554
|
+
* changes through `onDepsChanged`.
|
|
591
555
|
*
|
|
592
556
|
* ```tsx
|
|
593
557
|
* const [state, bloc] = useBloc(CanvasCubit);
|
|
@@ -604,17 +568,17 @@ function useBlocDeps(bloc, slice) {
|
|
|
604
568
|
const latest = (0, react.useRef)(slice);
|
|
605
569
|
latest.current = slice;
|
|
606
570
|
(0, react.useEffect)(() => {
|
|
607
|
-
bloc[
|
|
571
|
+
bloc[_blac_core_internal.APPLY_DEPS](ownerId, latest.current);
|
|
608
572
|
});
|
|
609
573
|
(0, react.useEffect)(() => {
|
|
610
574
|
return () => {
|
|
611
|
-
bloc[
|
|
575
|
+
bloc[_blac_core_internal.REMOVE_DEPS_OWNER](ownerId);
|
|
612
576
|
};
|
|
613
577
|
}, [bloc, ownerId]);
|
|
614
578
|
}
|
|
615
579
|
//#endregion
|
|
616
580
|
exports.BlocProvider = BlocProvider;
|
|
617
|
-
exports.RegistryProvider = RegistryProvider;
|
|
581
|
+
exports.RegistryProvider = require_RegistryProvider.RegistryProvider;
|
|
618
582
|
Object.defineProperty(exports, "untracked", {
|
|
619
583
|
enumerable: true,
|
|
620
584
|
get: function() {
|
|
@@ -624,5 +588,6 @@ Object.defineProperty(exports, "untracked", {
|
|
|
624
588
|
exports.useBloc = useBloc;
|
|
625
589
|
exports.useBlocDeps = useBlocDeps;
|
|
626
590
|
exports.useProvidedArgs = useProvidedArgs;
|
|
591
|
+
exports.useRegistry = require_RegistryProvider.useRegistry;
|
|
627
592
|
|
|
628
593
|
//# sourceMappingURL=index.cjs.map
|