@blac/react 2.0.22 → 2.1.2
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 +8 -11
- package/dist/buildTrackedProxy.d.ts +8 -11
- package/dist/depSession.d.cts +62 -0
- package/dist/depSession.d.ts +62 -0
- package/dist/expandWithAncestors.d.cts +14 -0
- package/dist/expandWithAncestors.d.ts +14 -0
- package/dist/index.cjs +401 -397
- 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 +397 -394
- 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,37 +57,9 @@ 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";
|
|
93
63
|
}
|
|
94
64
|
const gettersByProto = /* @__PURE__ */ new WeakMap();
|
|
95
65
|
function collectGetters(proto) {
|
|
@@ -107,53 +77,265 @@ function collectGetters(proto) {
|
|
|
107
77
|
gettersByProto.set(proto, getters);
|
|
108
78
|
return getters;
|
|
109
79
|
}
|
|
80
|
+
var BlocHandler = class {
|
|
81
|
+
constructor(getters, tracksState, trackedStateRef, onDepHandle) {
|
|
82
|
+
this.getters = getters;
|
|
83
|
+
this.tracksState = tracksState;
|
|
84
|
+
this.trackedStateRef = trackedStateRef;
|
|
85
|
+
this.onDepHandle = onDepHandle;
|
|
86
|
+
this.boundMethods = void 0;
|
|
87
|
+
}
|
|
88
|
+
get(target, key) {
|
|
89
|
+
const getter = this.getters.get(key);
|
|
90
|
+
if (getter !== void 0) {
|
|
91
|
+
const tracked = this.trackedStateRef.current;
|
|
92
|
+
return this.wrapDepHandle(tracked == null || !this.tracksState ? getter.call(target) : target[_blac_core_internal.WITH_TRACKED_STATE](tracked, getter, this.onDepHandle));
|
|
93
|
+
}
|
|
94
|
+
const value = Reflect.get(target, key, target);
|
|
95
|
+
if (typeof value === "function") {
|
|
96
|
+
const fn = value;
|
|
97
|
+
this.boundMethods ??= /* @__PURE__ */ new Map();
|
|
98
|
+
let cached = this.boundMethods.get(key);
|
|
99
|
+
if (cached === void 0 || cached.fn !== fn) {
|
|
100
|
+
cached = {
|
|
101
|
+
fn,
|
|
102
|
+
bound: fn.bind(target)
|
|
103
|
+
};
|
|
104
|
+
this.boundMethods.set(key, cached);
|
|
105
|
+
}
|
|
106
|
+
return this.wrapDepHandle(cached.bound);
|
|
107
|
+
}
|
|
108
|
+
return this.wrapDepHandle(value);
|
|
109
|
+
}
|
|
110
|
+
set(target, key, value) {
|
|
111
|
+
return Reflect.set(target, key, value, target);
|
|
112
|
+
}
|
|
113
|
+
wrapDepHandle(value) {
|
|
114
|
+
if (this.onDepHandle !== void 0 && (typeof value === "function" || typeof value === "object") && value !== null && value[_blac_core_internal.DEP_BRAND] !== void 0) return this.onDepHandle(value);
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
110
118
|
/**
|
|
111
119
|
* Build a per-consumer proxy for a bloc instance.
|
|
112
120
|
*
|
|
113
|
-
* Getters run with `this` bound to the
|
|
121
|
+
* Getters run with `this` bound to the real instance, not the proxy, so ES
|
|
114
122
|
* `#private` fields and methods work in user blocs. Path recording is done by
|
|
115
|
-
* `[WITH_TRACKED_STATE]`, which makes the instance's own `state` getter
|
|
116
|
-
* the render's tracking proxy for the duration
|
|
117
|
-
* chains stay tracked because the override
|
|
123
|
+
* `[WITH_TRACKED_STATE]`, which makes the instance's own `state` getter
|
|
124
|
+
* report the render's tracking proxy for the call's duration; nested getter
|
|
125
|
+
* chains stay tracked because the override remains set.
|
|
118
126
|
*
|
|
119
127
|
* One allocation per bloc acquisition (inside `useMemo`), so the proxy is
|
|
120
128
|
* stable across renders.
|
|
121
129
|
*
|
|
122
|
-
* @param onDepHandle -
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
* caching wrappers per handle to avoid re-allocation.
|
|
130
|
+
* @param onDepHandle - Invoked when a read returns a branded dep handle;
|
|
131
|
+
* returns the value (the session-bound wrapper) to expose in its place.
|
|
132
|
+
* Must cache wrappers per handle to avoid re-allocation.
|
|
126
133
|
*/
|
|
127
134
|
function buildTrackedProxy(instance, trackedStateRef, onDepHandle) {
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
135
|
+
const handler = new BlocHandler(collectGetters(Object.getPrototypeOf(instance)), supportsTrackedState(instance), trackedStateRef, onDepHandle);
|
|
136
|
+
return new Proxy(instance, handler);
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/depSession.ts
|
|
140
|
+
const NO_PATHS$1 = (0, _dirtytalk_structural.emptyPathSet)();
|
|
141
|
+
/**
|
|
142
|
+
* The containers one `useBloc` consumer reads during a render: its primary
|
|
143
|
+
* bloc plus every dep reached through `this.<handle>.track()`.
|
|
144
|
+
*
|
|
145
|
+
* The render records entries; the commit calls `reconcile`, which subscribes
|
|
146
|
+
* and takes a ref on new deps and drops the ones no longer read. `dispose`
|
|
147
|
+
* releases everything. Refs are only taken at commit, so an uncommitted render
|
|
148
|
+
* never leaks one.
|
|
149
|
+
*/
|
|
150
|
+
var DepSession = class {
|
|
151
|
+
constructor(consumerId, onChange) {
|
|
152
|
+
this.consumerId = consumerId;
|
|
153
|
+
this.onChange = onChange;
|
|
154
|
+
this.primary = null;
|
|
155
|
+
this.primaryPaths = NO_PATHS$1;
|
|
156
|
+
this.entries = null;
|
|
157
|
+
this.subs = null;
|
|
158
|
+
this.reconciled = false;
|
|
159
|
+
this.lastPrimary = null;
|
|
160
|
+
this.lastPrimaryPaths = NO_PATHS$1;
|
|
161
|
+
this.lastEntries = null;
|
|
162
|
+
this.disarms = null;
|
|
163
|
+
}
|
|
164
|
+
get refId() {
|
|
165
|
+
return `useBloc@${this.consumerId}:dep`;
|
|
166
|
+
}
|
|
167
|
+
begin(primary, paths) {
|
|
168
|
+
this.entries?.clear();
|
|
169
|
+
if (this.disarms !== null) this.disarms.length = 0;
|
|
170
|
+
this.primary = primary;
|
|
171
|
+
this.primaryPaths = paths;
|
|
172
|
+
}
|
|
173
|
+
record(dep, paths, Type, key, args, disarm) {
|
|
174
|
+
(this.disarms ??= []).push(disarm);
|
|
175
|
+
if (dep === this.primary) {
|
|
176
|
+
this.primaryPaths = unionPaths(this.primaryPaths, paths);
|
|
177
|
+
return;
|
|
140
178
|
}
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
179
|
+
const entries = this.entries ??= /* @__PURE__ */ new Map();
|
|
180
|
+
const existing = entries.get(dep);
|
|
181
|
+
if (existing !== void 0) existing.paths = unionPaths(existing.paths, paths);
|
|
182
|
+
else entries.set(dep, {
|
|
183
|
+
paths,
|
|
184
|
+
Type,
|
|
185
|
+
key,
|
|
186
|
+
args
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/** Stops every dep tracked this render from recording further reads. */
|
|
190
|
+
disarm() {
|
|
191
|
+
const disarms = this.disarms;
|
|
192
|
+
if (disarms === null) return;
|
|
193
|
+
for (const disarm of disarms) disarm();
|
|
194
|
+
disarms.length = 0;
|
|
195
|
+
}
|
|
196
|
+
/** Forces the next `reconcile` to run in full. */
|
|
197
|
+
invalidate() {
|
|
198
|
+
this.reconciled = false;
|
|
199
|
+
}
|
|
200
|
+
isUnchanged() {
|
|
201
|
+
if (!this.reconciled || this.lastPrimary !== this.primary || !(0, _dirtytalk_structural.pathSetEquals)(this.lastPrimaryPaths, this.primaryPaths)) return false;
|
|
202
|
+
const last = this.lastEntries;
|
|
203
|
+
const entries = this.entries;
|
|
204
|
+
if ((last?.size ?? 0) !== (entries?.size ?? 0)) return false;
|
|
205
|
+
if (last === null || entries === null) return true;
|
|
206
|
+
for (const [container, entry] of entries) {
|
|
207
|
+
const prev = last.get(container);
|
|
208
|
+
if (prev === void 0 || !sameEntry(prev, entry)) return false;
|
|
209
|
+
}
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
reconcile(registry) {
|
|
213
|
+
const { consumerId } = this;
|
|
214
|
+
const entries = this.entries;
|
|
215
|
+
this.reconciled = true;
|
|
216
|
+
this.lastPrimary = this.primary;
|
|
217
|
+
this.lastPrimaryPaths = this.primaryPaths;
|
|
218
|
+
this.lastEntries = entries === null || entries.size === 0 ? null : new Map(entries);
|
|
219
|
+
const subs = this.subs;
|
|
220
|
+
if (subs !== null) {
|
|
221
|
+
for (const [dep, sub] of subs) if (entries?.has(dep) !== true) {
|
|
222
|
+
this.drop(dep, sub);
|
|
223
|
+
subs.delete(dep);
|
|
147
224
|
}
|
|
148
|
-
return wrapDepHandle(bound);
|
|
149
225
|
}
|
|
150
|
-
|
|
151
|
-
|
|
226
|
+
if (entries === null) return;
|
|
227
|
+
for (const [dep, entry] of entries) {
|
|
228
|
+
if (dep.$blac.disposed) {
|
|
229
|
+
this.onChange();
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
dep.registerConsumerPaths(consumerId, entry.paths);
|
|
233
|
+
const existing = subs?.get(dep);
|
|
234
|
+
if (existing) {
|
|
235
|
+
existing.subscription.setInterest(entry.paths);
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
const refId = this.refId;
|
|
239
|
+
if (registry.acquire(entry.Type, entry.key, {
|
|
240
|
+
canCreate: true,
|
|
241
|
+
countRef: true,
|
|
242
|
+
refId,
|
|
243
|
+
args: entry.args
|
|
244
|
+
}) !== dep) {
|
|
245
|
+
registry.release(entry.Type, entry.key, false, refId);
|
|
246
|
+
this.onChange();
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
const subscription = dep.subscribePaths(this.onChange);
|
|
250
|
+
subscription.setInterest(entry.paths);
|
|
251
|
+
(this.subs ??= /* @__PURE__ */ new Map()).set(dep, {
|
|
252
|
+
subscription,
|
|
253
|
+
registry,
|
|
254
|
+
Type: entry.Type,
|
|
255
|
+
key: entry.key
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
dispose() {
|
|
260
|
+
const subs = this.subs;
|
|
261
|
+
if (subs !== null) {
|
|
262
|
+
for (const [dep, sub] of subs) this.drop(dep, sub);
|
|
263
|
+
subs.clear();
|
|
264
|
+
}
|
|
265
|
+
this.reconciled = false;
|
|
266
|
+
}
|
|
267
|
+
drop(dep, sub) {
|
|
268
|
+
sub.subscription.unsubscribe();
|
|
269
|
+
dep.unregisterConsumer(this.consumerId);
|
|
270
|
+
sub.registry.release(sub.Type, sub.key, false, this.refId);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
function sameEntry(a, b) {
|
|
274
|
+
return (0, _dirtytalk_structural.pathSetEquals)(a.paths, b.paths) && a.key === b.key && Object.is(a.args, b.args);
|
|
275
|
+
}
|
|
276
|
+
function unionPaths(a, b) {
|
|
277
|
+
if (a === _dirtytalk_structural.ALL_PATHS || b === _dirtytalk_structural.ALL_PATHS) return _dirtytalk_structural.ALL_PATHS;
|
|
278
|
+
const out = new Set(a);
|
|
279
|
+
for (const id of b) out.add(id);
|
|
280
|
+
return out;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Wrap a dep handle read inside a tracked getter. During a render (`tracked`
|
|
284
|
+
* is set) `.track()` records the dep's read paths into `session` and returns
|
|
285
|
+
* a tracked proxy, so the dep's own getters track too. Outside a render it
|
|
286
|
+
* returns the live `[state, dep]`, like the core handle.
|
|
287
|
+
*
|
|
288
|
+
* Args resolve per call, so one handle can reach several instances; proxies
|
|
289
|
+
* are cached per resolved instance.
|
|
290
|
+
*/
|
|
291
|
+
function makeDepWrapper(handle, registry, session, tracked, onDepHandle) {
|
|
292
|
+
const brand = handle[_blac_core_internal.DEP_BRAND];
|
|
293
|
+
const perDep = /* @__PURE__ */ new Map();
|
|
294
|
+
const proxyCache = new _dirtytalk_structural.ProxyCache();
|
|
295
|
+
const resolve = (options) => {
|
|
296
|
+
const args = options?.args ?? brand.defaultArgs;
|
|
297
|
+
const key = registry.resolveKey(brand.Type, void 0, args);
|
|
298
|
+
return {
|
|
299
|
+
dep: registry.acquire(brand.Type, key, {
|
|
300
|
+
canCreate: true,
|
|
301
|
+
countRef: false,
|
|
302
|
+
args,
|
|
303
|
+
sweepIfUnowned: tracked.current != null
|
|
304
|
+
}),
|
|
305
|
+
key,
|
|
306
|
+
args
|
|
307
|
+
};
|
|
308
|
+
};
|
|
309
|
+
const wrapper = {
|
|
310
|
+
untracked: (options) => resolve(options).dep,
|
|
311
|
+
track: (options) => {
|
|
312
|
+
const { dep, key, args } = resolve(options);
|
|
313
|
+
if (tracked.current == null) return [dep.state, dep];
|
|
314
|
+
const result = (0, _dirtytalk_structural.trackRender)(dep.state, dep.interner, proxyCache);
|
|
315
|
+
let cache = perDep.get(dep);
|
|
316
|
+
if (cache === void 0) {
|
|
317
|
+
const ref = { current: result.value };
|
|
318
|
+
cache = {
|
|
319
|
+
ref,
|
|
320
|
+
proxy: buildTrackedProxy(dep, ref, onDepHandle)
|
|
321
|
+
};
|
|
322
|
+
perDep.set(dep, cache);
|
|
323
|
+
} else cache.ref.current = result.value;
|
|
324
|
+
session.record(dep, result.paths, brand.Type, key, args, result.disarm);
|
|
325
|
+
return [result.value, cache.proxy];
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
Object.defineProperty(wrapper, _blac_core_internal.DEP_BRAND, {
|
|
329
|
+
value: brand,
|
|
330
|
+
enumerable: false,
|
|
331
|
+
writable: false,
|
|
332
|
+
configurable: false
|
|
333
|
+
});
|
|
334
|
+
return wrapper;
|
|
152
335
|
}
|
|
153
336
|
//#endregion
|
|
154
337
|
//#region src/useBloc.ts
|
|
155
338
|
let nextConsumerId = 0;
|
|
156
|
-
const ARGS_UNSET = Symbol("blac.argsKeyUnset");
|
|
157
339
|
/**
|
|
158
340
|
* React hook that connects a component to a state container with automatic
|
|
159
341
|
* re-render on state changes.
|
|
@@ -161,8 +343,7 @@ const ARGS_UNSET = Symbol("blac.argsKeyUnset");
|
|
|
161
343
|
* Two tracking modes:
|
|
162
344
|
* - **Auto-tracking** (default): the returned state value is a proxy that
|
|
163
345
|
* records read paths during render. The component re-renders when any
|
|
164
|
-
* recorded path changes.
|
|
165
|
-
* `trackRender` + the container's path-scoped `DirtyChannel`.
|
|
346
|
+
* recorded path changes.
|
|
166
347
|
* - **Manual select**: pass `options.select` to opt out of auto-tracking.
|
|
167
348
|
* The hook re-renders only when the returned array's elements change
|
|
168
349
|
* (per-index `Object.is`).
|
|
@@ -185,7 +366,7 @@ const ARGS_UNSET = Symbol("blac.argsKeyUnset");
|
|
|
185
366
|
* @typeParam T - The state container constructor type (inferred from BlocClass)
|
|
186
367
|
* @param BlocClass - The state container class to connect to
|
|
187
368
|
* @param options - Configuration options
|
|
188
|
-
* @returns Tuple of `[state, bloc
|
|
369
|
+
* @returns Tuple of `[state, bloc]`
|
|
189
370
|
*
|
|
190
371
|
* @example Basic usage
|
|
191
372
|
* ```ts
|
|
@@ -206,230 +387,57 @@ const ARGS_UNSET = Symbol("blac.argsKeyUnset");
|
|
|
206
387
|
* @public
|
|
207
388
|
*/
|
|
208
389
|
function useBloc(BlocClass, options) {
|
|
209
|
-
const registry =
|
|
390
|
+
const registry = require_RegistryProvider.useRegistry();
|
|
210
391
|
const consumerRef = (0, react.useRef)(null);
|
|
211
392
|
const consumer = consumerRef.current ??= createConsumer();
|
|
212
|
-
const consumerId = consumer.id;
|
|
213
393
|
consumer.select = options?.select;
|
|
214
394
|
consumer.onMount = options?.onMount;
|
|
215
395
|
consumer.onUnmount = options?.onUnmount;
|
|
216
|
-
|
|
217
|
-
consumer.
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const ownArgsKey = consumer.ownArgsKey;
|
|
223
|
-
const providerArgs = useProvidedArgs(BlocClass);
|
|
224
|
-
consumer.providerArgs = providerArgs;
|
|
225
|
-
if (!Object.is(consumer.providerArgsKeyFor, providerArgs)) {
|
|
226
|
-
consumer.providerArgsKeyFor = providerArgs;
|
|
227
|
-
consumer.providerArgsKey = providerArgs === void 0 ? void 0 : JSON.stringify(providerArgs);
|
|
228
|
-
}
|
|
229
|
-
const providerArgsKey = consumer.providerArgsKey;
|
|
230
|
-
const rebindNonce = consumer.rebindNonce;
|
|
231
|
-
const { bloc, instanceKey, trackedBloc } = (0, react.useMemo)(() => {
|
|
232
|
-
const effectiveArgs = resolveEffectiveArgs(consumer);
|
|
233
|
-
const resolvedKey = (0, _blac_core.resolveInstanceKey)(BlocClass, effectiveArgs);
|
|
234
|
-
const instance = registry.acquire(BlocClass, resolvedKey, {
|
|
235
|
-
canCreate: true,
|
|
236
|
-
countRef: false,
|
|
237
|
-
args: effectiveArgs,
|
|
238
|
-
sweepIfUnowned: true
|
|
239
|
-
});
|
|
240
|
-
const onDepHandle = (handle) => {
|
|
241
|
-
const cache = consumer.depWrappers ??= /* @__PURE__ */ new Map();
|
|
242
|
-
const cached = cache.get(handle);
|
|
243
|
-
if (cached !== void 0) return cached;
|
|
244
|
-
const wrapper = makeDepWrapper(handle, registry, consumer, onDepHandle);
|
|
245
|
-
cache.set(handle, wrapper);
|
|
246
|
-
return wrapper;
|
|
247
|
-
};
|
|
248
|
-
const { proxy } = buildTrackedProxy(instance, consumer.tracked, onDepHandle);
|
|
249
|
-
return {
|
|
250
|
-
bloc: instance,
|
|
251
|
-
instanceKey: resolvedKey,
|
|
252
|
-
trackedBloc: proxy
|
|
253
|
-
};
|
|
254
|
-
}, [
|
|
255
|
-
BlocClass,
|
|
256
|
-
ownArgsKey,
|
|
257
|
-
providerArgsKey,
|
|
258
|
-
rebindNonce,
|
|
259
|
-
registry
|
|
260
|
-
]);
|
|
261
|
-
consumer.container = bloc;
|
|
262
|
-
(0, react.useSyncExternalStore)((0, react.useMemo)(() => (onStoreChange) => {
|
|
263
|
-
consumer.notify = onStoreChange;
|
|
264
|
-
const container = consumer.container;
|
|
265
|
-
const unsubscribe = container.channel.subscribe(() => consumer.interest, () => {
|
|
266
|
-
if (consumer.isSelectMode) {
|
|
267
|
-
const select = consumer.select;
|
|
268
|
-
if (select) {
|
|
269
|
-
const next = select(container.state, container);
|
|
270
|
-
const prev = consumer.selection;
|
|
271
|
-
if (prev !== null && shallowArrayEqual(prev, next)) return;
|
|
272
|
-
consumer.selection = next;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
consumer.bump();
|
|
276
|
-
});
|
|
277
|
-
container.registerConsumerPaths(consumerId, consumer.paths);
|
|
278
|
-
return () => {
|
|
279
|
-
consumer.notify = noop;
|
|
280
|
-
unsubscribe();
|
|
281
|
-
container.unregisterConsumer(consumerId);
|
|
282
|
-
};
|
|
283
|
-
}, [
|
|
284
|
-
BlocClass,
|
|
285
|
-
instanceKey,
|
|
286
|
-
consumerId,
|
|
287
|
-
rebindNonce
|
|
288
|
-
]), consumer.getSnapshot, consumer.getServerSnapshot);
|
|
289
|
-
(0, react.useLayoutEffect)(() => {
|
|
290
|
-
const live = registry.acquire(BlocClass, instanceKey, {
|
|
291
|
-
canCreate: true,
|
|
292
|
-
countRef: true,
|
|
293
|
-
refId: consumer.primaryRefId,
|
|
294
|
-
args: resolveEffectiveArgs(consumer)
|
|
295
|
-
});
|
|
296
|
-
consumer.ownedBloc = live;
|
|
297
|
-
consumer.onMount?.(live);
|
|
298
|
-
if (live !== bloc) {
|
|
299
|
-
consumer.rebindNonce++;
|
|
300
|
-
consumer.bump();
|
|
301
|
-
}
|
|
302
|
-
return () => {
|
|
303
|
-
consumer.onUnmount?.(consumer.ownedBloc ?? bloc);
|
|
304
|
-
registry.release(BlocClass, instanceKey, false, consumer.primaryRefId);
|
|
305
|
-
};
|
|
306
|
-
}, [
|
|
307
|
-
BlocClass,
|
|
308
|
-
instanceKey,
|
|
309
|
-
consumerId,
|
|
310
|
-
registry
|
|
311
|
-
]);
|
|
396
|
+
consumer.ownArgs = options?.args;
|
|
397
|
+
consumer.providerArgs = useProvidedArgs(BlocClass);
|
|
398
|
+
const effectiveArgs = resolveEffectiveArgs(consumer);
|
|
399
|
+
const instanceKey = registry.resolveKey(BlocClass, void 0, effectiveArgs);
|
|
400
|
+
if (consumer.BlocClass !== BlocClass || consumer.instanceKey !== instanceKey || consumer.registry !== registry || consumer.boundNonce !== consumer.rebindNonce) bind(consumer, BlocClass, instanceKey, registry, effectiveArgs);
|
|
401
|
+
(0, react.useSyncExternalStore)(consumer.subscribe, consumer.getSnapshot, consumer.getSnapshot);
|
|
312
402
|
const container = consumer.container;
|
|
313
403
|
const rawState = container.state;
|
|
404
|
+
consumer.renderedState = rawState;
|
|
314
405
|
const select = consumer.select;
|
|
315
406
|
consumer.isSelectMode = select !== void 0;
|
|
316
407
|
let state;
|
|
317
408
|
if (select !== void 0) {
|
|
318
409
|
state = rawState;
|
|
319
|
-
|
|
320
|
-
consumer
|
|
410
|
+
consumer.selection = select(rawState, container);
|
|
411
|
+
setInterest(consumer, _dirtytalk_structural.ALL_PATHS);
|
|
321
412
|
} else {
|
|
322
413
|
const tracked = (0, _dirtytalk_structural.trackRender)(rawState, container.interner, consumer.proxyCache ??= new _dirtytalk_structural.ProxyCache());
|
|
323
414
|
state = tracked.value;
|
|
324
415
|
consumer.tracked.current = tracked.value;
|
|
325
416
|
consumer.paths = tracked.paths;
|
|
326
417
|
consumer.disarm = tracked.disarm;
|
|
327
|
-
|
|
328
|
-
session.clear();
|
|
329
|
-
session.set(container, {
|
|
330
|
-
kind: "primary",
|
|
331
|
-
paths: tracked.paths
|
|
332
|
-
});
|
|
418
|
+
consumer.deps.begin(container, tracked.paths);
|
|
333
419
|
}
|
|
334
|
-
(0, react.useLayoutEffect)(
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
if (disarm !== null) {
|
|
338
|
-
consumer.disarm = null;
|
|
339
|
-
disarm();
|
|
340
|
-
}
|
|
341
|
-
if (consumer.isSelectMode) {
|
|
342
|
-
consumer.lastReconcile = null;
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
const primary = consumer.container;
|
|
346
|
-
const paths = consumer.paths;
|
|
347
|
-
const session = consumer.session;
|
|
348
|
-
const last = consumer.lastReconcile;
|
|
349
|
-
if (last !== null && last.primaryContainer === primary && (0, _dirtytalk_structural.pathSetEquals)(last.primaryPaths, paths)) {
|
|
350
|
-
let unchanged = last.deps.size === session.size - 1;
|
|
351
|
-
if (unchanged) for (const [depContainer, entry] of session) {
|
|
352
|
-
if (entry.kind === "primary") continue;
|
|
353
|
-
const prevEntry = last.deps.get(depContainer);
|
|
354
|
-
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)) {
|
|
355
|
-
unchanged = false;
|
|
356
|
-
break;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
if (unchanged) return;
|
|
360
|
-
}
|
|
361
|
-
primary.registerConsumerPaths(consumerId, paths);
|
|
362
|
-
consumer.interest = expandWithAncestors(paths, primary.interner);
|
|
363
|
-
const subs = consumer.depSubs;
|
|
364
|
-
for (const [depContainer, sub] of subs) if (!session.has(depContainer)) {
|
|
365
|
-
sub.unsubscribe();
|
|
366
|
-
depContainer.unregisterConsumer(consumerId);
|
|
367
|
-
registry.release(sub.Type, sub.key, false, sub.refId);
|
|
368
|
-
subs.delete(depContainer);
|
|
369
|
-
}
|
|
370
|
-
for (const [depContainer, entry] of session) {
|
|
371
|
-
if (entry.kind === "primary") continue;
|
|
372
|
-
const interest = expandWithAncestors(entry.paths, depContainer.interner);
|
|
373
|
-
depContainer.registerConsumerPaths(consumerId, entry.paths);
|
|
374
|
-
const existing = subs.get(depContainer);
|
|
375
|
-
if (existing) {
|
|
376
|
-
existing.interestRef.current = interest;
|
|
377
|
-
continue;
|
|
378
|
-
}
|
|
379
|
-
registry.acquire(entry.Type, entry.key, {
|
|
380
|
-
canCreate: true,
|
|
381
|
-
countRef: true,
|
|
382
|
-
refId: entry.refId,
|
|
383
|
-
args: entry.args
|
|
384
|
-
});
|
|
385
|
-
const interestRef = { current: interest };
|
|
386
|
-
const unsubscribe = depContainer.channel.subscribe(() => interestRef.current, () => consumer.bump());
|
|
387
|
-
subs.set(depContainer, {
|
|
388
|
-
unsubscribe,
|
|
389
|
-
interestRef,
|
|
390
|
-
Type: entry.Type,
|
|
391
|
-
key: entry.key,
|
|
392
|
-
refId: entry.refId,
|
|
393
|
-
args: entry.args
|
|
394
|
-
});
|
|
395
|
-
}
|
|
396
|
-
const depsSignature = /* @__PURE__ */ new Map();
|
|
397
|
-
for (const [depContainer, entry] of session) {
|
|
398
|
-
if (entry.kind === "primary") continue;
|
|
399
|
-
depsSignature.set(depContainer, {
|
|
400
|
-
paths: entry.paths,
|
|
401
|
-
key: entry.key,
|
|
402
|
-
refId: entry.refId,
|
|
403
|
-
args: entry.args
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
consumer.lastReconcile = {
|
|
407
|
-
primaryContainer: primary,
|
|
408
|
-
primaryPaths: paths,
|
|
409
|
-
deps: depsSignature
|
|
410
|
-
};
|
|
411
|
-
});
|
|
412
|
-
(0, react.useEffect)(() => {
|
|
413
|
-
const subs = consumer.depSubs;
|
|
414
|
-
return () => {
|
|
415
|
-
for (const [depContainer, sub] of subs) {
|
|
416
|
-
sub.unsubscribe();
|
|
417
|
-
depContainer.unregisterConsumer(consumerId);
|
|
418
|
-
registry.release(sub.Type, sub.key, false, sub.refId);
|
|
419
|
-
}
|
|
420
|
-
subs.clear();
|
|
421
|
-
};
|
|
422
|
-
}, [consumerId, registry]);
|
|
423
|
-
return [state, trackedBloc];
|
|
420
|
+
(0, react.useLayoutEffect)(consumer.commit);
|
|
421
|
+
(0, react.useLayoutEffect)(consumer.mount, NO_DEPS);
|
|
422
|
+
return [state, consumer.trackedBloc];
|
|
424
423
|
}
|
|
425
424
|
const noop = () => {};
|
|
425
|
+
const NO_PATHS = (0, _dirtytalk_structural.emptyPathSet)();
|
|
426
|
+
const NO_DEPS = [];
|
|
426
427
|
function createConsumer() {
|
|
427
428
|
const id = `useBloc-${nextConsumerId++}`;
|
|
428
429
|
const consumer = {
|
|
429
430
|
id,
|
|
430
431
|
primaryRefId: `useBloc@${id}`,
|
|
431
|
-
|
|
432
|
+
BlocClass: null,
|
|
433
|
+
instanceKey: "",
|
|
434
|
+
registry: null,
|
|
432
435
|
container: null,
|
|
436
|
+
trackedBloc: null,
|
|
437
|
+
subscribe: null,
|
|
438
|
+
boundNonce: 0,
|
|
439
|
+
rebindNonce: 0,
|
|
440
|
+
renderedState: void 0,
|
|
433
441
|
version: 0,
|
|
434
442
|
notify: noop,
|
|
435
443
|
bump: () => {
|
|
@@ -437,164 +445,159 @@ function createConsumer() {
|
|
|
437
445
|
consumer.notify();
|
|
438
446
|
},
|
|
439
447
|
getSnapshot: () => consumer.version,
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
448
|
+
onChange: () => onChange(consumer),
|
|
449
|
+
unsubscribe: () => unsubscribe(consumer),
|
|
450
|
+
commit: () => commit(consumer),
|
|
451
|
+
mount: () => teardown,
|
|
452
|
+
interest: NO_PATHS,
|
|
453
|
+
subscription: null,
|
|
454
|
+
subscribedTo: null,
|
|
455
|
+
paths: NO_PATHS,
|
|
443
456
|
isSelectMode: false,
|
|
444
457
|
selection: null,
|
|
445
|
-
|
|
446
|
-
depSubs: /* @__PURE__ */ new Map(),
|
|
447
|
-
lastReconcile: null,
|
|
458
|
+
deps: null,
|
|
448
459
|
select: void 0,
|
|
449
460
|
onMount: void 0,
|
|
450
461
|
onUnmount: void 0,
|
|
451
462
|
ownArgs: void 0,
|
|
452
|
-
ownArgsKeyFor: ARGS_UNSET,
|
|
453
|
-
ownArgsKey: void 0,
|
|
454
463
|
providerArgs: void 0,
|
|
455
|
-
providerArgsKeyFor: ARGS_UNSET,
|
|
456
|
-
providerArgsKey: void 0,
|
|
457
464
|
tracked: { current: null },
|
|
458
465
|
disarm: null,
|
|
459
466
|
proxyCache: null,
|
|
460
|
-
|
|
461
|
-
|
|
467
|
+
ownedClass: null,
|
|
468
|
+
ownedKey: "",
|
|
469
|
+
ownedRegistry: null,
|
|
462
470
|
ownedBloc: null
|
|
463
471
|
};
|
|
472
|
+
const teardown = () => {
|
|
473
|
+
release(consumer);
|
|
474
|
+
consumer.deps.dispose();
|
|
475
|
+
};
|
|
476
|
+
consumer.deps = new DepSession(id, consumer.bump);
|
|
464
477
|
return consumer;
|
|
465
478
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
*
|
|
482
|
-
* Args resolve at call time (`options.args ?? defaultArgs`), so a single handle
|
|
483
|
-
* can resolve different dep instances across calls; tracked-proxy state is
|
|
484
|
-
* therefore cached per resolved instance, not per handle. Guards against a
|
|
485
|
-
* container re-entering tracking within the same render (mutual A↔B deps): if
|
|
486
|
-
* the dep already has a non-primary session entry this render, reuse its proxy
|
|
487
|
-
* + union its paths instead of re-acquiring.
|
|
488
|
-
*/
|
|
489
|
-
function makeDepWrapper(handle, registry, consumer, onDepHandle) {
|
|
490
|
-
const brand = handle[_blac_core.DEP_BRAND];
|
|
491
|
-
const refId = consumer.depRefId;
|
|
492
|
-
const perDep = /* @__PURE__ */ new Map();
|
|
493
|
-
const proxyCache = new _dirtytalk_structural.ProxyCache();
|
|
494
|
-
const resolve = (options) => {
|
|
495
|
-
const args = options?.args ?? brand.defaultArgs;
|
|
496
|
-
const key = registry.resolveKey(brand.Type, void 0, args);
|
|
497
|
-
return {
|
|
498
|
-
dep: registry.ensure(brand.Type, key, args),
|
|
499
|
-
key,
|
|
500
|
-
args
|
|
501
|
-
};
|
|
479
|
+
function bind(consumer, BlocClass, instanceKey, registry, args) {
|
|
480
|
+
const instance = registry.acquire(BlocClass, instanceKey, {
|
|
481
|
+
canCreate: true,
|
|
482
|
+
countRef: false,
|
|
483
|
+
args,
|
|
484
|
+
sweepIfUnowned: true
|
|
485
|
+
});
|
|
486
|
+
let depWrappers;
|
|
487
|
+
const onDepHandle = (handle) => {
|
|
488
|
+
depWrappers ??= /* @__PURE__ */ new Map();
|
|
489
|
+
const cached = depWrappers.get(handle);
|
|
490
|
+
if (cached !== void 0) return cached;
|
|
491
|
+
const wrapper = makeDepWrapper(handle, registry, consumer.deps, consumer.tracked, onDepHandle);
|
|
492
|
+
depWrappers.set(handle, wrapper);
|
|
493
|
+
return wrapper;
|
|
502
494
|
};
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
495
|
+
consumer.BlocClass = BlocClass;
|
|
496
|
+
consumer.instanceKey = instanceKey;
|
|
497
|
+
consumer.registry = registry;
|
|
498
|
+
consumer.boundNonce = consumer.rebindNonce;
|
|
499
|
+
consumer.container = instance;
|
|
500
|
+
consumer.trackedBloc = buildTrackedProxy(instance, consumer.tracked, onDepHandle);
|
|
501
|
+
consumer.subscribe = (onStoreChange) => subscribe(consumer, onStoreChange);
|
|
502
|
+
}
|
|
503
|
+
function subscribe(consumer, onStoreChange) {
|
|
504
|
+
consumer.notify = onStoreChange;
|
|
505
|
+
const container = consumer.container;
|
|
506
|
+
const subscription = container.subscribePaths(consumer.onChange);
|
|
507
|
+
subscription.setInterest(consumer.interest);
|
|
508
|
+
consumer.subscription = subscription;
|
|
509
|
+
consumer.subscribedTo = container;
|
|
510
|
+
container.registerConsumerPaths(consumer.id, consumer.paths);
|
|
511
|
+
return consumer.unsubscribe;
|
|
512
|
+
}
|
|
513
|
+
function unsubscribe(consumer) {
|
|
514
|
+
consumer.notify = noop;
|
|
515
|
+
consumer.subscription?.unsubscribe();
|
|
516
|
+
consumer.subscription = null;
|
|
517
|
+
consumer.subscribedTo?.unregisterConsumer(consumer.id);
|
|
518
|
+
consumer.subscribedTo = null;
|
|
519
|
+
}
|
|
520
|
+
function onChange(consumer) {
|
|
521
|
+
if (consumer.isSelectMode) {
|
|
522
|
+
const select = consumer.select;
|
|
523
|
+
const container = consumer.subscribedTo;
|
|
524
|
+
if (select && container !== null) {
|
|
525
|
+
const next = select(container.state, container);
|
|
526
|
+
const prev = consumer.selection;
|
|
527
|
+
if (prev !== null && shallowArrayEqual(prev, next)) return;
|
|
528
|
+
consumer.selection = next;
|
|
530
529
|
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
530
|
+
}
|
|
531
|
+
consumer.bump();
|
|
532
|
+
}
|
|
533
|
+
function claim(consumer) {
|
|
534
|
+
const { BlocClass, instanceKey, registry } = consumer;
|
|
535
|
+
if (consumer.ownedClass === BlocClass && consumer.ownedKey === instanceKey && consumer.ownedRegistry === registry) return;
|
|
536
|
+
release(consumer);
|
|
537
|
+
const live = registry.acquire(BlocClass, instanceKey, {
|
|
538
|
+
canCreate: true,
|
|
539
|
+
countRef: true,
|
|
540
|
+
refId: consumer.primaryRefId,
|
|
541
|
+
args: resolveEffectiveArgs(consumer)
|
|
537
542
|
});
|
|
538
|
-
|
|
543
|
+
consumer.ownedClass = BlocClass;
|
|
544
|
+
consumer.ownedKey = instanceKey;
|
|
545
|
+
consumer.ownedRegistry = registry;
|
|
546
|
+
consumer.ownedBloc = live;
|
|
547
|
+
consumer.onMount?.(live);
|
|
548
|
+
if (live !== consumer.container) {
|
|
549
|
+
consumer.rebindNonce++;
|
|
550
|
+
consumer.bump();
|
|
551
|
+
}
|
|
539
552
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
if (
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
553
|
+
function release(consumer) {
|
|
554
|
+
const { ownedClass, ownedRegistry } = consumer;
|
|
555
|
+
if (ownedClass === null || ownedRegistry === null) return;
|
|
556
|
+
consumer.onUnmount?.(consumer.ownedBloc);
|
|
557
|
+
consumer.ownedClass = null;
|
|
558
|
+
consumer.ownedRegistry = null;
|
|
559
|
+
ownedRegistry.release(ownedClass, consumer.ownedKey, false, consumer.primaryRefId);
|
|
546
560
|
}
|
|
561
|
+
function commit(consumer) {
|
|
562
|
+
claim(consumer);
|
|
563
|
+
consumer.tracked.current = null;
|
|
564
|
+
const disarm = consumer.disarm;
|
|
565
|
+
if (disarm !== null) {
|
|
566
|
+
consumer.disarm = null;
|
|
567
|
+
disarm();
|
|
568
|
+
}
|
|
569
|
+
const deps = consumer.deps;
|
|
570
|
+
deps.disarm();
|
|
571
|
+
if (consumer.isSelectMode) {
|
|
572
|
+
deps.invalidate();
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
const primary = consumer.container;
|
|
576
|
+
if (primary.state !== consumer.renderedState) consumer.bump();
|
|
577
|
+
if (deps.isUnchanged()) return;
|
|
578
|
+
primary.registerConsumerPaths(consumer.id, consumer.paths);
|
|
579
|
+
setInterest(consumer, consumer.paths);
|
|
580
|
+
deps.reconcile(consumer.registry);
|
|
581
|
+
}
|
|
582
|
+
const setInterest = (consumer, interest) => {
|
|
583
|
+
consumer.interest = interest;
|
|
584
|
+
consumer.subscription?.setInterest(interest);
|
|
585
|
+
};
|
|
586
|
+
const resolveEffectiveArgs = (consumer) => consumer.ownArgs !== void 0 ? consumer.ownArgs : consumer.providerArgs;
|
|
547
587
|
const shallowArrayEqual = (a, b) => {
|
|
548
588
|
if (a === b) return true;
|
|
549
589
|
if (a.length !== b.length) return false;
|
|
550
590
|
for (let i = 0; i < a.length; i++) if (!Object.is(a[i], b[i])) return false;
|
|
551
591
|
return true;
|
|
552
592
|
};
|
|
553
|
-
/**
|
|
554
|
-
* Expand a PathSet to include an *ancestor-watch* id for every ancestor of
|
|
555
|
-
* every tracked leaf.
|
|
556
|
-
*
|
|
557
|
-
* The auto-tracker records leaf paths (e.g. `'items.length'`), but
|
|
558
|
-
* `StructuralContainer.patch` can only mark the parent (`'items'`) when it
|
|
559
|
-
* replaces a value atomically (arrays, `null`, primitives — it can't see
|
|
560
|
-
* inside). Without expansion, a subscriber with interest `{'items.length'}`
|
|
561
|
-
* would miss a `patch`-triggered atomic-replacement of `items`.
|
|
562
|
-
*
|
|
563
|
-
* Ancestors are added under the interner's *ancestor-watch* lane
|
|
564
|
-
* (`ancestorWatchIds`), NOT as normal ids. The source emits a matching
|
|
565
|
-
* ancestor-watch mark only for paths it replaces atomically — never for a
|
|
566
|
-
* plain-object structural pulse-up. So `{'items.length'}` wakes when the array
|
|
567
|
-
* `items` is replaced, but `{'user.email'}` does NOT wake when a sibling
|
|
568
|
-
* `user.name` changes and pulses `user` up: pulse-up `user` is a normal id and
|
|
569
|
-
* the ancestor-watch `user` only intersects another ancestor-watch `user`.
|
|
570
|
-
*
|
|
571
|
-
* Example: leaf `'a.b.c'` adds ancestor-watch ids for `'a.b'` and `'a'` (but
|
|
572
|
-
* NOT the `''` root — a root change is covered by `ALL_PATHS` from the source,
|
|
573
|
-
* and `''` would wake this consumer on every field change).
|
|
574
|
-
*
|
|
575
|
-
* Returns `paths` itself when no leaf has an ancestor (top-level fields only);
|
|
576
|
-
* the set is frozen by then (see `Consumer.disarm`), so sharing it is safe.
|
|
577
|
-
*/
|
|
578
|
-
function expandWithAncestors(paths, interner) {
|
|
579
|
-
if (paths === _dirtytalk_structural.ALL_PATHS) return _dirtytalk_structural.ALL_PATHS;
|
|
580
|
-
const leafPaths = paths;
|
|
581
|
-
let expanded;
|
|
582
|
-
for (const id of leafPaths) {
|
|
583
|
-
const watch = interner.ancestorWatchIds(id);
|
|
584
|
-
if (watch.length === 0) continue;
|
|
585
|
-
expanded ??= new Set(leafPaths);
|
|
586
|
-
for (let i = 0; i < watch.length; i++) expanded.add(watch[i]);
|
|
587
|
-
}
|
|
588
|
-
return expanded ?? paths;
|
|
589
|
-
}
|
|
590
593
|
//#endregion
|
|
591
594
|
//#region src/useBlocDeps.ts
|
|
592
595
|
/**
|
|
593
596
|
* Feed live values from the component into a bloc's `deps` lane.
|
|
594
597
|
*
|
|
595
598
|
* Each calling component is one owner: its slice is shallow-merged into the
|
|
596
|
-
* bloc's merged `deps` view
|
|
597
|
-
*
|
|
599
|
+
* bloc's merged `deps` view and withdrawn on unmount. The bloc observes
|
|
600
|
+
* changes through `onDepsChanged`.
|
|
598
601
|
*
|
|
599
602
|
* ```tsx
|
|
600
603
|
* const [state, bloc] = useBloc(CanvasCubit);
|
|
@@ -611,17 +614,17 @@ function useBlocDeps(bloc, slice) {
|
|
|
611
614
|
const latest = (0, react.useRef)(slice);
|
|
612
615
|
latest.current = slice;
|
|
613
616
|
(0, react.useEffect)(() => {
|
|
614
|
-
bloc[
|
|
617
|
+
bloc[_blac_core_internal.APPLY_DEPS](ownerId, latest.current);
|
|
615
618
|
});
|
|
616
619
|
(0, react.useEffect)(() => {
|
|
617
620
|
return () => {
|
|
618
|
-
bloc[
|
|
621
|
+
bloc[_blac_core_internal.REMOVE_DEPS_OWNER](ownerId);
|
|
619
622
|
};
|
|
620
623
|
}, [bloc, ownerId]);
|
|
621
624
|
}
|
|
622
625
|
//#endregion
|
|
623
626
|
exports.BlocProvider = BlocProvider;
|
|
624
|
-
exports.RegistryProvider = RegistryProvider;
|
|
627
|
+
exports.RegistryProvider = require_RegistryProvider.RegistryProvider;
|
|
625
628
|
Object.defineProperty(exports, "untracked", {
|
|
626
629
|
enumerable: true,
|
|
627
630
|
get: function() {
|
|
@@ -631,5 +634,6 @@ Object.defineProperty(exports, "untracked", {
|
|
|
631
634
|
exports.useBloc = useBloc;
|
|
632
635
|
exports.useBlocDeps = useBlocDeps;
|
|
633
636
|
exports.useProvidedArgs = useProvidedArgs;
|
|
637
|
+
exports.useRegistry = require_RegistryProvider.useRegistry;
|
|
634
638
|
|
|
635
639
|
//# sourceMappingURL=index.cjs.map
|