@blac/react 2.1.1 → 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/buildTrackedProxy.d.cts +1 -3
- package/dist/buildTrackedProxy.d.ts +1 -3
- package/dist/depSession.d.cts +13 -5
- package/dist/depSession.d.ts +13 -5
- package/dist/index.cjs +252 -206
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +252 -206
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/depSession.d.cts
CHANGED
|
@@ -13,13 +13,21 @@ import { type PathSet } from '@dirtytalk/structural';
|
|
|
13
13
|
export declare class DepSession {
|
|
14
14
|
private readonly consumerId;
|
|
15
15
|
private readonly onChange;
|
|
16
|
-
private
|
|
17
|
-
private
|
|
18
|
-
private
|
|
19
|
-
private
|
|
16
|
+
private primary;
|
|
17
|
+
private primaryPaths;
|
|
18
|
+
private entries;
|
|
19
|
+
private subs;
|
|
20
|
+
private reconciled;
|
|
21
|
+
private lastPrimary;
|
|
22
|
+
private lastPrimaryPaths;
|
|
23
|
+
private lastEntries;
|
|
24
|
+
private disarms;
|
|
20
25
|
constructor(consumerId: string, onChange: () => void);
|
|
26
|
+
private get refId();
|
|
21
27
|
begin(primary: StateContainer, paths: PathSet): void;
|
|
22
|
-
record(dep: StateContainer, paths: PathSet, Type: StateContainerConstructor, key: string, args: unknown): void;
|
|
28
|
+
record(dep: StateContainer, paths: PathSet, Type: StateContainerConstructor, key: string, args: unknown, disarm: () => void): void;
|
|
29
|
+
/** Stops every dep tracked this render from recording further reads. */
|
|
30
|
+
disarm(): void;
|
|
23
31
|
/** Forces the next `reconcile` to run in full. */
|
|
24
32
|
invalidate(): void;
|
|
25
33
|
isUnchanged(): boolean;
|
package/dist/depSession.d.ts
CHANGED
|
@@ -13,13 +13,21 @@ import { type PathSet } from '@dirtytalk/structural';
|
|
|
13
13
|
export declare class DepSession {
|
|
14
14
|
private readonly consumerId;
|
|
15
15
|
private readonly onChange;
|
|
16
|
-
private
|
|
17
|
-
private
|
|
18
|
-
private
|
|
19
|
-
private
|
|
16
|
+
private primary;
|
|
17
|
+
private primaryPaths;
|
|
18
|
+
private entries;
|
|
19
|
+
private subs;
|
|
20
|
+
private reconciled;
|
|
21
|
+
private lastPrimary;
|
|
22
|
+
private lastPrimaryPaths;
|
|
23
|
+
private lastEntries;
|
|
24
|
+
private disarms;
|
|
20
25
|
constructor(consumerId: string, onChange: () => void);
|
|
26
|
+
private get refId();
|
|
21
27
|
begin(primary: StateContainer, paths: PathSet): void;
|
|
22
|
-
record(dep: StateContainer, paths: PathSet, Type: StateContainerConstructor, key: string, args: unknown): void;
|
|
28
|
+
record(dep: StateContainer, paths: PathSet, Type: StateContainerConstructor, key: string, args: unknown, disarm: () => void): void;
|
|
29
|
+
/** Stops every dep tracked this render from recording further reads. */
|
|
30
|
+
disarm(): void;
|
|
23
31
|
/** Forces the next `reconcile` to run in full. */
|
|
24
32
|
invalidate(): void;
|
|
25
33
|
isUnchanged(): boolean;
|
package/dist/index.cjs
CHANGED
|
@@ -77,6 +77,44 @@ function collectGetters(proto) {
|
|
|
77
77
|
gettersByProto.set(proto, getters);
|
|
78
78
|
return getters;
|
|
79
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
|
+
};
|
|
80
118
|
/**
|
|
81
119
|
* Build a per-consumer proxy for a bloc instance.
|
|
82
120
|
*
|
|
@@ -94,68 +132,12 @@ function collectGetters(proto) {
|
|
|
94
132
|
* Must cache wrappers per handle to avoid re-allocation.
|
|
95
133
|
*/
|
|
96
134
|
function buildTrackedProxy(instance, trackedStateRef, onDepHandle) {
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
const boundMethods = /* @__PURE__ */ new Map();
|
|
100
|
-
const wrapDepHandle = (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);
|
|
102
|
-
return value;
|
|
103
|
-
};
|
|
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;
|
|
135
|
+
const handler = new BlocHandler(collectGetters(Object.getPrototypeOf(instance)), supportsTrackedState(instance), trackedStateRef, onDepHandle);
|
|
136
|
+
return new Proxy(instance, handler);
|
|
156
137
|
}
|
|
157
138
|
//#endregion
|
|
158
139
|
//#region src/depSession.ts
|
|
140
|
+
const NO_PATHS$1 = (0, _dirtytalk_structural.emptyPathSet)();
|
|
159
141
|
/**
|
|
160
142
|
* The containers one `useBloc` consumer reads during a render: its primary
|
|
161
143
|
* bloc plus every dep reached through `this.<handle>.track()`.
|
|
@@ -169,61 +151,91 @@ var DepSession = class {
|
|
|
169
151
|
constructor(consumerId, onChange) {
|
|
170
152
|
this.consumerId = consumerId;
|
|
171
153
|
this.onChange = onChange;
|
|
172
|
-
this.
|
|
173
|
-
this.
|
|
174
|
-
this.
|
|
175
|
-
this.
|
|
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`;
|
|
176
166
|
}
|
|
177
167
|
begin(primary, paths) {
|
|
178
|
-
this.entries
|
|
179
|
-
this.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
});
|
|
168
|
+
this.entries?.clear();
|
|
169
|
+
if (this.disarms !== null) this.disarms.length = 0;
|
|
170
|
+
this.primary = primary;
|
|
171
|
+
this.primaryPaths = paths;
|
|
183
172
|
}
|
|
184
|
-
record(dep, paths, Type, key, args) {
|
|
185
|
-
|
|
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;
|
|
178
|
+
}
|
|
179
|
+
const entries = this.entries ??= /* @__PURE__ */ new Map();
|
|
180
|
+
const existing = entries.get(dep);
|
|
186
181
|
if (existing !== void 0) existing.paths = unionPaths(existing.paths, paths);
|
|
187
|
-
else
|
|
188
|
-
kind: "dep",
|
|
182
|
+
else entries.set(dep, {
|
|
189
183
|
paths,
|
|
190
184
|
Type,
|
|
191
185
|
key,
|
|
192
186
|
args
|
|
193
187
|
});
|
|
194
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
|
+
}
|
|
195
196
|
/** Forces the next `reconcile` to run in full. */
|
|
196
197
|
invalidate() {
|
|
197
|
-
this.
|
|
198
|
+
this.reconciled = false;
|
|
198
199
|
}
|
|
199
200
|
isUnchanged() {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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) {
|
|
203
207
|
const prev = last.get(container);
|
|
204
208
|
if (prev === void 0 || !sameEntry(prev, entry)) return false;
|
|
205
209
|
}
|
|
206
210
|
return true;
|
|
207
211
|
}
|
|
208
212
|
reconcile(registry) {
|
|
209
|
-
const { consumerId
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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);
|
|
224
|
+
}
|
|
213
225
|
}
|
|
226
|
+
if (entries === null) return;
|
|
214
227
|
for (const [dep, entry] of entries) {
|
|
215
|
-
if (entry.kind === "primary") continue;
|
|
216
228
|
if (dep.$blac.disposed) {
|
|
217
229
|
this.onChange();
|
|
218
230
|
continue;
|
|
219
231
|
}
|
|
220
|
-
const interest = expandWithAncestors(entry.paths, dep.interner);
|
|
221
232
|
dep.registerConsumerPaths(consumerId, entry.paths);
|
|
222
|
-
const existing = subs
|
|
233
|
+
const existing = subs?.get(dep);
|
|
223
234
|
if (existing) {
|
|
224
|
-
existing.
|
|
235
|
+
existing.subscription.setInterest(entry.paths);
|
|
225
236
|
continue;
|
|
226
237
|
}
|
|
238
|
+
const refId = this.refId;
|
|
227
239
|
if (registry.acquire(entry.Type, entry.key, {
|
|
228
240
|
canCreate: true,
|
|
229
241
|
countRef: true,
|
|
@@ -234,33 +246,32 @@ var DepSession = class {
|
|
|
234
246
|
this.onChange();
|
|
235
247
|
continue;
|
|
236
248
|
}
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
subs.set(dep, {
|
|
240
|
-
|
|
241
|
-
interestRef,
|
|
249
|
+
const subscription = dep.subscribePaths(this.onChange);
|
|
250
|
+
subscription.setInterest(entry.paths);
|
|
251
|
+
(this.subs ??= /* @__PURE__ */ new Map()).set(dep, {
|
|
252
|
+
subscription,
|
|
242
253
|
registry,
|
|
243
254
|
Type: entry.Type,
|
|
244
255
|
key: entry.key
|
|
245
256
|
});
|
|
246
257
|
}
|
|
247
|
-
this.lastReconciled = new Map(entries);
|
|
248
258
|
}
|
|
249
259
|
dispose() {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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;
|
|
253
266
|
}
|
|
254
267
|
drop(dep, sub) {
|
|
255
|
-
sub.unsubscribe();
|
|
268
|
+
sub.subscription.unsubscribe();
|
|
256
269
|
dep.unregisterConsumer(this.consumerId);
|
|
257
270
|
sub.registry.release(sub.Type, sub.key, false, this.refId);
|
|
258
271
|
}
|
|
259
272
|
};
|
|
260
273
|
function sameEntry(a, b) {
|
|
261
|
-
|
|
262
|
-
if (a.kind === "primary" || b.kind === "primary") return true;
|
|
263
|
-
return a.key === b.key && Object.is(a.args, b.args);
|
|
274
|
+
return (0, _dirtytalk_structural.pathSetEquals)(a.paths, b.paths) && a.key === b.key && Object.is(a.args, b.args);
|
|
264
275
|
}
|
|
265
276
|
function unionPaths(a, b) {
|
|
266
277
|
if (a === _dirtytalk_structural.ALL_PATHS || b === _dirtytalk_structural.ALL_PATHS) return _dirtytalk_structural.ALL_PATHS;
|
|
@@ -306,11 +317,11 @@ function makeDepWrapper(handle, registry, session, tracked, onDepHandle) {
|
|
|
306
317
|
const ref = { current: result.value };
|
|
307
318
|
cache = {
|
|
308
319
|
ref,
|
|
309
|
-
proxy: buildTrackedProxy(dep, ref, onDepHandle)
|
|
320
|
+
proxy: buildTrackedProxy(dep, ref, onDepHandle)
|
|
310
321
|
};
|
|
311
322
|
perDep.set(dep, cache);
|
|
312
323
|
} else cache.ref.current = result.value;
|
|
313
|
-
session.record(dep, result.paths, brand.Type, key, args);
|
|
324
|
+
session.record(dep, result.paths, brand.Type, key, args, result.disarm);
|
|
314
325
|
return [result.value, cache.proxy];
|
|
315
326
|
}
|
|
316
327
|
};
|
|
@@ -379,7 +390,6 @@ function useBloc(BlocClass, options) {
|
|
|
379
390
|
const registry = require_RegistryProvider.useRegistry();
|
|
380
391
|
const consumerRef = (0, react.useRef)(null);
|
|
381
392
|
const consumer = consumerRef.current ??= createConsumer();
|
|
382
|
-
const consumerId = consumer.id;
|
|
383
393
|
consumer.select = options?.select;
|
|
384
394
|
consumer.onMount = options?.onMount;
|
|
385
395
|
consumer.onUnmount = options?.onUnmount;
|
|
@@ -387,94 +397,18 @@ function useBloc(BlocClass, options) {
|
|
|
387
397
|
consumer.providerArgs = useProvidedArgs(BlocClass);
|
|
388
398
|
const effectiveArgs = resolveEffectiveArgs(consumer);
|
|
389
399
|
const instanceKey = registry.resolveKey(BlocClass, void 0, effectiveArgs);
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
const instance = registry.acquire(BlocClass, instanceKey, {
|
|
393
|
-
canCreate: true,
|
|
394
|
-
countRef: false,
|
|
395
|
-
args: effectiveArgs,
|
|
396
|
-
sweepIfUnowned: true
|
|
397
|
-
});
|
|
398
|
-
const depWrappers = /* @__PURE__ */ new Map();
|
|
399
|
-
const onDepHandle = (handle) => {
|
|
400
|
-
const cached = depWrappers.get(handle);
|
|
401
|
-
if (cached !== void 0) return cached;
|
|
402
|
-
const wrapper = makeDepWrapper(handle, registry, consumer.deps, consumer.tracked, onDepHandle);
|
|
403
|
-
depWrappers.set(handle, wrapper);
|
|
404
|
-
return wrapper;
|
|
405
|
-
};
|
|
406
|
-
const { proxy } = buildTrackedProxy(instance, consumer.tracked, onDepHandle);
|
|
407
|
-
return {
|
|
408
|
-
bloc: instance,
|
|
409
|
-
trackedBloc: proxy
|
|
410
|
-
};
|
|
411
|
-
}, [
|
|
412
|
-
BlocClass,
|
|
413
|
-
instanceKey,
|
|
414
|
-
rebindNonce,
|
|
415
|
-
registry
|
|
416
|
-
]);
|
|
417
|
-
consumer.container = bloc;
|
|
418
|
-
(0, react.useSyncExternalStore)((0, react.useMemo)(() => (onStoreChange) => {
|
|
419
|
-
consumer.notify = onStoreChange;
|
|
420
|
-
const container = consumer.container;
|
|
421
|
-
const unsubscribe = container.channel.subscribe(() => consumer.interest, () => {
|
|
422
|
-
if (consumer.isSelectMode) {
|
|
423
|
-
const select = consumer.select;
|
|
424
|
-
if (select) {
|
|
425
|
-
const next = select(container.state, container);
|
|
426
|
-
const prev = consumer.selection;
|
|
427
|
-
if (prev !== null && shallowArrayEqual(prev, next)) return;
|
|
428
|
-
consumer.selection = next;
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
consumer.bump();
|
|
432
|
-
});
|
|
433
|
-
container.registerConsumerPaths(consumerId, consumer.paths);
|
|
434
|
-
return () => {
|
|
435
|
-
consumer.notify = noop;
|
|
436
|
-
unsubscribe();
|
|
437
|
-
container.unregisterConsumer(consumerId);
|
|
438
|
-
};
|
|
439
|
-
}, [
|
|
440
|
-
BlocClass,
|
|
441
|
-
instanceKey,
|
|
442
|
-
consumerId,
|
|
443
|
-
rebindNonce,
|
|
444
|
-
registry
|
|
445
|
-
]), consumer.getSnapshot, consumer.getServerSnapshot);
|
|
446
|
-
(0, react.useLayoutEffect)(() => {
|
|
447
|
-
const live = registry.acquire(BlocClass, instanceKey, {
|
|
448
|
-
canCreate: true,
|
|
449
|
-
countRef: true,
|
|
450
|
-
refId: consumer.primaryRefId,
|
|
451
|
-
args: resolveEffectiveArgs(consumer)
|
|
452
|
-
});
|
|
453
|
-
consumer.ownedBloc = live;
|
|
454
|
-
consumer.onMount?.(live);
|
|
455
|
-
if (live !== bloc) {
|
|
456
|
-
consumer.rebindNonce++;
|
|
457
|
-
consumer.bump();
|
|
458
|
-
}
|
|
459
|
-
return () => {
|
|
460
|
-
consumer.onUnmount?.(consumer.ownedBloc ?? bloc);
|
|
461
|
-
registry.release(BlocClass, instanceKey, false, consumer.primaryRefId);
|
|
462
|
-
};
|
|
463
|
-
}, [
|
|
464
|
-
BlocClass,
|
|
465
|
-
instanceKey,
|
|
466
|
-
consumerId,
|
|
467
|
-
registry
|
|
468
|
-
]);
|
|
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);
|
|
469
402
|
const container = consumer.container;
|
|
470
403
|
const rawState = container.state;
|
|
404
|
+
consumer.renderedState = rawState;
|
|
471
405
|
const select = consumer.select;
|
|
472
406
|
consumer.isSelectMode = select !== void 0;
|
|
473
407
|
let state;
|
|
474
408
|
if (select !== void 0) {
|
|
475
409
|
state = rawState;
|
|
476
|
-
consumer.selection = select(rawState,
|
|
477
|
-
consumer
|
|
410
|
+
consumer.selection = select(rawState, container);
|
|
411
|
+
setInterest(consumer, _dirtytalk_structural.ALL_PATHS);
|
|
478
412
|
} else {
|
|
479
413
|
const tracked = (0, _dirtytalk_structural.trackRender)(rawState, container.interner, consumer.proxyCache ??= new _dirtytalk_structural.ProxyCache());
|
|
480
414
|
state = tracked.value;
|
|
@@ -483,34 +417,27 @@ function useBloc(BlocClass, options) {
|
|
|
483
417
|
consumer.disarm = tracked.disarm;
|
|
484
418
|
consumer.deps.begin(container, tracked.paths);
|
|
485
419
|
}
|
|
486
|
-
(0, react.useLayoutEffect)(
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
if (disarm !== null) {
|
|
490
|
-
consumer.disarm = null;
|
|
491
|
-
disarm();
|
|
492
|
-
}
|
|
493
|
-
const deps = consumer.deps;
|
|
494
|
-
if (consumer.isSelectMode) {
|
|
495
|
-
deps.invalidate();
|
|
496
|
-
return;
|
|
497
|
-
}
|
|
498
|
-
if (deps.isUnchanged()) return;
|
|
499
|
-
const primary = consumer.container;
|
|
500
|
-
primary.registerConsumerPaths(consumerId, consumer.paths);
|
|
501
|
-
consumer.interest = expandWithAncestors(consumer.paths, primary.interner);
|
|
502
|
-
deps.reconcile(registry);
|
|
503
|
-
});
|
|
504
|
-
(0, react.useEffect)(() => () => consumer.deps.dispose(), [consumerId]);
|
|
505
|
-
return [state, trackedBloc];
|
|
420
|
+
(0, react.useLayoutEffect)(consumer.commit);
|
|
421
|
+
(0, react.useLayoutEffect)(consumer.mount, NO_DEPS);
|
|
422
|
+
return [state, consumer.trackedBloc];
|
|
506
423
|
}
|
|
507
424
|
const noop = () => {};
|
|
425
|
+
const NO_PATHS = (0, _dirtytalk_structural.emptyPathSet)();
|
|
426
|
+
const NO_DEPS = [];
|
|
508
427
|
function createConsumer() {
|
|
509
428
|
const id = `useBloc-${nextConsumerId++}`;
|
|
510
429
|
const consumer = {
|
|
511
430
|
id,
|
|
512
431
|
primaryRefId: `useBloc@${id}`,
|
|
432
|
+
BlocClass: null,
|
|
433
|
+
instanceKey: "",
|
|
434
|
+
registry: null,
|
|
513
435
|
container: null,
|
|
436
|
+
trackedBloc: null,
|
|
437
|
+
subscribe: null,
|
|
438
|
+
boundNonce: 0,
|
|
439
|
+
rebindNonce: 0,
|
|
440
|
+
renderedState: void 0,
|
|
514
441
|
version: 0,
|
|
515
442
|
notify: noop,
|
|
516
443
|
bump: () => {
|
|
@@ -518,12 +445,17 @@ function createConsumer() {
|
|
|
518
445
|
consumer.notify();
|
|
519
446
|
},
|
|
520
447
|
getSnapshot: () => consumer.version,
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
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,
|
|
524
456
|
isSelectMode: false,
|
|
525
457
|
selection: null,
|
|
526
|
-
deps:
|
|
458
|
+
deps: null,
|
|
527
459
|
select: void 0,
|
|
528
460
|
onMount: void 0,
|
|
529
461
|
onUnmount: void 0,
|
|
@@ -532,11 +464,125 @@ function createConsumer() {
|
|
|
532
464
|
tracked: { current: null },
|
|
533
465
|
disarm: null,
|
|
534
466
|
proxyCache: null,
|
|
535
|
-
|
|
467
|
+
ownedClass: null,
|
|
468
|
+
ownedKey: "",
|
|
469
|
+
ownedRegistry: null,
|
|
536
470
|
ownedBloc: null
|
|
537
471
|
};
|
|
472
|
+
const teardown = () => {
|
|
473
|
+
release(consumer);
|
|
474
|
+
consumer.deps.dispose();
|
|
475
|
+
};
|
|
476
|
+
consumer.deps = new DepSession(id, consumer.bump);
|
|
538
477
|
return consumer;
|
|
539
478
|
}
|
|
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;
|
|
494
|
+
};
|
|
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;
|
|
529
|
+
}
|
|
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)
|
|
542
|
+
});
|
|
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
|
+
}
|
|
552
|
+
}
|
|
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);
|
|
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
|
+
};
|
|
540
586
|
const resolveEffectiveArgs = (consumer) => consumer.ownArgs !== void 0 ? consumer.ownArgs : consumer.providerArgs;
|
|
541
587
|
const shallowArrayEqual = (a, b) => {
|
|
542
588
|
if (a === b) return true;
|