@expressive/mvc 0.7.2 → 0.8.0
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/lib/index.d.ts +135 -166
- package/lib/index.esm.js +460 -475
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +462 -479
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useState, useMemo } from 'react';
|
|
2
2
|
|
|
3
3
|
class Issue extends Error {
|
|
4
4
|
constructor() {
|
|
@@ -18,10 +18,43 @@ function issues(register) {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const Oops$c = issues({
|
|
21
|
+
BadCallback: () => `Callback for effect-callback may only return a function.`
|
|
22
|
+
});
|
|
23
|
+
const { assign, create, defineProperty, entries, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
|
|
24
|
+
function createEffect(callback) {
|
|
25
|
+
let unSet;
|
|
26
|
+
return function (value) {
|
|
27
|
+
if (typeof unSet == "function")
|
|
28
|
+
unSet();
|
|
29
|
+
unSet = callback.call(this, value);
|
|
30
|
+
if (unSet instanceof Promise)
|
|
31
|
+
unSet = undefined;
|
|
32
|
+
if (unSet && typeof unSet !== "function")
|
|
33
|
+
throw Oops$c.BadCallback();
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function createValueEffect(callback) {
|
|
37
|
+
let unSet;
|
|
38
|
+
return function (value) {
|
|
39
|
+
if (typeof unSet == "function")
|
|
40
|
+
unSet(value);
|
|
41
|
+
const out = callback.call(this, value, this);
|
|
42
|
+
if (typeof out == "boolean")
|
|
43
|
+
return out;
|
|
44
|
+
if (!out || out instanceof Promise)
|
|
45
|
+
return;
|
|
46
|
+
if (typeof out == "function")
|
|
47
|
+
unSet = out;
|
|
48
|
+
else
|
|
49
|
+
throw Oops$c.BadCallback();
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const Oops$b = issues({
|
|
21
54
|
NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
|
|
22
55
|
});
|
|
23
56
|
function suspend(source, key) {
|
|
24
|
-
const error = Oops$
|
|
57
|
+
const error = Oops$b.NotReady(source.subject, key);
|
|
25
58
|
const promise = new Promise(resolve => {
|
|
26
59
|
const release = source.addListener(forKey => {
|
|
27
60
|
if (forKey == key)
|
|
@@ -33,7 +66,7 @@ function suspend(source, key) {
|
|
|
33
66
|
};
|
|
34
67
|
});
|
|
35
68
|
});
|
|
36
|
-
return
|
|
69
|
+
return assign(promise, {
|
|
37
70
|
toString: () => String(error),
|
|
38
71
|
name: "Suspense",
|
|
39
72
|
message: error.message,
|
|
@@ -61,7 +94,7 @@ function mayRetry(fn) {
|
|
|
61
94
|
return compute();
|
|
62
95
|
}
|
|
63
96
|
|
|
64
|
-
const Oops$
|
|
97
|
+
const Oops$a = issues({
|
|
65
98
|
Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`
|
|
66
99
|
});
|
|
67
100
|
const INFO = new WeakMap();
|
|
@@ -78,7 +111,7 @@ function computeMode(self, source, setter, key, required) {
|
|
|
78
111
|
return setter.call(sub.proxy, sub.proxy);
|
|
79
112
|
}
|
|
80
113
|
catch (err) {
|
|
81
|
-
Oops$
|
|
114
|
+
Oops$a.Failed(subject, key, initial).warn();
|
|
82
115
|
throw err;
|
|
83
116
|
}
|
|
84
117
|
};
|
|
@@ -147,259 +180,20 @@ function flush(control) {
|
|
|
147
180
|
KEYS.delete(control);
|
|
148
181
|
}
|
|
149
182
|
|
|
150
|
-
const
|
|
151
|
-
BadCallback: () => `Callback for effect-callback may only return a function.`
|
|
152
|
-
});
|
|
153
|
-
const { assign, create, defineProperty, entries, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
|
|
154
|
-
function createEffect(callback) {
|
|
155
|
-
let unSet;
|
|
156
|
-
return function (value) {
|
|
157
|
-
if (typeof unSet == "function")
|
|
158
|
-
unSet();
|
|
159
|
-
unSet = callback.call(this, value);
|
|
160
|
-
if (unSet instanceof Promise)
|
|
161
|
-
unSet = undefined;
|
|
162
|
-
if (unSet && typeof unSet !== "function")
|
|
163
|
-
throw Oops$a.BadCallback();
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
function createValueEffect(callback) {
|
|
167
|
-
let unSet;
|
|
168
|
-
return function (value) {
|
|
169
|
-
if (typeof unSet == "function")
|
|
170
|
-
unSet(value);
|
|
171
|
-
const out = callback.call(this, value, this);
|
|
172
|
-
if (typeof out == "boolean")
|
|
173
|
-
return out;
|
|
174
|
-
if (!out || out instanceof Promise)
|
|
175
|
-
return;
|
|
176
|
-
if (typeof out == "function")
|
|
177
|
-
unSet = out;
|
|
178
|
-
else
|
|
179
|
-
throw Oops$a.BadCallback();
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const Oops$9 = issues({
|
|
184
|
-
Timeout: (keys, timeout) => `No update for [${keys}] within ${timeout}.`,
|
|
185
|
-
StrictUpdate: () => `Strict update() did not find pending updates.`,
|
|
186
|
-
NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
|
|
187
|
-
});
|
|
188
|
-
const WHY = Symbol("UPDATE");
|
|
189
|
-
const LOCAL = Symbol("LOCAL");
|
|
190
|
-
const STATE = Symbol("STATE");
|
|
191
|
-
class Model {
|
|
192
|
-
constructor() {
|
|
193
|
-
defineProperty(this, CONTROL, {
|
|
194
|
-
value: new Controller(this)
|
|
195
|
-
});
|
|
196
|
-
defineProperty(this, "is", {
|
|
197
|
-
value: this
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
get [STATE]() {
|
|
201
|
-
return this.export();
|
|
202
|
-
}
|
|
203
|
-
get [WHY]() {
|
|
204
|
-
return UPDATE.get(this);
|
|
205
|
-
}
|
|
206
|
-
on(select, handler, squash, once) {
|
|
207
|
-
return ensure(this, control => {
|
|
208
|
-
if (typeof select == "function")
|
|
209
|
-
return control.addListener(select);
|
|
210
|
-
const keys = typeof select == "string" ? [select] :
|
|
211
|
-
select.length ? select : [...control.state.keys()];
|
|
212
|
-
for (const key of keys)
|
|
213
|
-
try {
|
|
214
|
-
void this[key];
|
|
215
|
-
}
|
|
216
|
-
catch (e) { }
|
|
217
|
-
const callback = squash
|
|
218
|
-
? () => {
|
|
219
|
-
handler.call(this, getUpdate(this));
|
|
220
|
-
}
|
|
221
|
-
: () => {
|
|
222
|
-
getUpdate(this)
|
|
223
|
-
.filter(k => keys.includes(k))
|
|
224
|
-
.forEach(k => {
|
|
225
|
-
handler.call(this, control.state.get(k), k);
|
|
226
|
-
});
|
|
227
|
-
};
|
|
228
|
-
const onEvent = once
|
|
229
|
-
? () => {
|
|
230
|
-
remove();
|
|
231
|
-
callback();
|
|
232
|
-
}
|
|
233
|
-
: callback;
|
|
234
|
-
const remove = control.addListener(key => {
|
|
235
|
-
if (keys.includes(key))
|
|
236
|
-
return onEvent;
|
|
237
|
-
});
|
|
238
|
-
return remove;
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
once(select, argument, squash) {
|
|
242
|
-
if (typeof argument == "function")
|
|
243
|
-
return this.on(select, argument, squash, true);
|
|
244
|
-
if (typeof select == "string")
|
|
245
|
-
select = [select];
|
|
246
|
-
return new Promise((resolve, reject) => {
|
|
247
|
-
const invoke = (key) => {
|
|
248
|
-
if (!key) {
|
|
249
|
-
clear();
|
|
250
|
-
reject(Oops$9.Timeout(select, typeof argument == "number"
|
|
251
|
-
? `${argument}ms`
|
|
252
|
-
: `lifetime of ${this}`));
|
|
253
|
-
}
|
|
254
|
-
else if (select.includes(key)) {
|
|
255
|
-
clear();
|
|
256
|
-
return resolve;
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
const clear = ensure(this, x => {
|
|
260
|
-
for (const key of select)
|
|
261
|
-
try {
|
|
262
|
-
void this[key];
|
|
263
|
-
}
|
|
264
|
-
catch (e) { }
|
|
265
|
-
return x.addListener(invoke);
|
|
266
|
-
});
|
|
267
|
-
if (typeof argument == "number")
|
|
268
|
-
setTimeout(invoke, argument);
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
effect(callback, select) {
|
|
272
|
-
const effect = createEffect(callback);
|
|
273
|
-
return ensure(this, control => {
|
|
274
|
-
let busy = false;
|
|
275
|
-
let inject = this.is;
|
|
276
|
-
const invoke = () => {
|
|
277
|
-
if (busy)
|
|
278
|
-
return;
|
|
279
|
-
const output = mayRetry(() => {
|
|
280
|
-
effect.call(inject, inject);
|
|
281
|
-
});
|
|
282
|
-
if (output instanceof Promise) {
|
|
283
|
-
output.finally(() => busy = false);
|
|
284
|
-
busy = true;
|
|
285
|
-
}
|
|
286
|
-
};
|
|
287
|
-
if (!select) {
|
|
288
|
-
const sub = control.subscribe(() => invoke);
|
|
289
|
-
inject = sub.proxy;
|
|
290
|
-
invoke();
|
|
291
|
-
return sub.commit();
|
|
292
|
-
}
|
|
293
|
-
invoke();
|
|
294
|
-
return control.addListener(key => {
|
|
295
|
-
if (key === null) {
|
|
296
|
-
if (!select.length)
|
|
297
|
-
invoke();
|
|
298
|
-
}
|
|
299
|
-
else if (select.includes(key))
|
|
300
|
-
return invoke;
|
|
301
|
-
});
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
// TODO: account for exotic properties
|
|
305
|
-
import(source, select) {
|
|
306
|
-
const { subject } = ensure(this);
|
|
307
|
-
if (!select)
|
|
308
|
-
select = getOwnPropertyNames(subject);
|
|
309
|
-
for (const key of select)
|
|
310
|
-
if (key in source)
|
|
311
|
-
this[key] = source[key];
|
|
312
|
-
}
|
|
313
|
-
export(subset) {
|
|
314
|
-
const { state } = ensure(this);
|
|
315
|
-
const output = {};
|
|
316
|
-
const keys = subset || state.keys();
|
|
317
|
-
for (const key of keys)
|
|
318
|
-
output[key] = state.get(key);
|
|
319
|
-
return output;
|
|
320
|
-
}
|
|
321
|
-
update(arg, tag) {
|
|
322
|
-
const target = ensure(this);
|
|
323
|
-
const { frame, waiting } = target;
|
|
324
|
-
if (typeof arg == "string") {
|
|
325
|
-
target.update(arg);
|
|
326
|
-
if (1 in arguments && arg in this) {
|
|
327
|
-
const method = this[arg];
|
|
328
|
-
if (typeof method == "function")
|
|
329
|
-
if (typeof tag != "boolean")
|
|
330
|
-
method.call(this, tag);
|
|
331
|
-
else if (tag)
|
|
332
|
-
method.call(this);
|
|
333
|
-
}
|
|
334
|
-
arg = undefined;
|
|
335
|
-
}
|
|
336
|
-
if (!frame.size && arg === true)
|
|
337
|
-
return Promise.reject(Oops$9.StrictUpdate());
|
|
338
|
-
return {
|
|
339
|
-
then: (callback) => {
|
|
340
|
-
if (!callback)
|
|
341
|
-
throw Oops$9.NoChaining();
|
|
342
|
-
if (frame.size || arg !== false)
|
|
343
|
-
waiting.add(() => {
|
|
344
|
-
callback(getUpdate(this));
|
|
345
|
-
});
|
|
346
|
-
else
|
|
347
|
-
callback(false);
|
|
348
|
-
}
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
/**
|
|
352
|
-
* Mark this instance for garbage-collection and send `willDestroy` event to all listeners.
|
|
353
|
-
*/
|
|
354
|
-
destroy() {
|
|
355
|
-
ensure(this).clear();
|
|
356
|
-
}
|
|
357
|
-
toString() {
|
|
358
|
-
return this.constructor.name;
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* Creates a new instance of this controller.
|
|
362
|
-
*
|
|
363
|
-
* Beyond `new this(...)`, method will activate managed-state.
|
|
364
|
-
*
|
|
365
|
-
* @param args - arguments sent to constructor
|
|
366
|
-
*/
|
|
367
|
-
static create(...args) {
|
|
368
|
-
const instance = new this(...args);
|
|
369
|
-
ensure(instance);
|
|
370
|
-
return instance;
|
|
371
|
-
}
|
|
372
|
-
/**
|
|
373
|
-
* Static equivalent of `x instanceof this`.
|
|
374
|
-
*
|
|
375
|
-
* Will determine if provided class is a subtype of this one.
|
|
376
|
-
*/
|
|
377
|
-
static isTypeof(maybe) {
|
|
378
|
-
return (typeof maybe == "function" &&
|
|
379
|
-
maybe.prototype instanceof this);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
183
|
+
const REGISTER$1 = new WeakMap();
|
|
383
184
|
class Subscriber {
|
|
384
185
|
constructor(parent, onUpdate) {
|
|
186
|
+
this.parent = parent;
|
|
385
187
|
this.onUpdate = onUpdate;
|
|
386
188
|
this.active = false;
|
|
387
189
|
this.dependant = new Set();
|
|
388
190
|
const proxy = create(parent.subject);
|
|
389
191
|
const using = new Map();
|
|
390
|
-
|
|
391
|
-
if (value !== undefined)
|
|
392
|
-
using.set(key, value);
|
|
393
|
-
else if (!using.has(key))
|
|
394
|
-
using.set(key, true);
|
|
395
|
-
};
|
|
396
|
-
LISTEN.set(proxy, listen);
|
|
397
|
-
defineProperty(proxy, LOCAL, { value: this });
|
|
192
|
+
REGISTER$1.set(proxy, this);
|
|
398
193
|
defineProperty(this, "proxy", {
|
|
399
194
|
configurable: true,
|
|
400
195
|
get() {
|
|
401
|
-
|
|
402
|
-
setTimeout(() => UPDATE.delete(proxy), 0);
|
|
196
|
+
hasUpdate(proxy);
|
|
403
197
|
return proxy;
|
|
404
198
|
}
|
|
405
199
|
});
|
|
@@ -417,12 +211,15 @@ class Subscriber {
|
|
|
417
211
|
notify = this.onUpdate(key, parent);
|
|
418
212
|
if (!notify)
|
|
419
213
|
return;
|
|
420
|
-
|
|
421
|
-
UPDATE.set(proxy, getUpdate(parent.subject).filter(k => using.has(k)));
|
|
422
|
-
};
|
|
423
|
-
parent.waiting.add(notate);
|
|
214
|
+
parent.waiting.add(() => addUpdate(proxy, using));
|
|
424
215
|
parent.waiting.add(notify);
|
|
425
216
|
});
|
|
217
|
+
this.add = (key, value) => {
|
|
218
|
+
if (value !== undefined)
|
|
219
|
+
using.set(key, value);
|
|
220
|
+
else if (!using.has(key))
|
|
221
|
+
using.set(key, true);
|
|
222
|
+
};
|
|
426
223
|
this.commit = () => {
|
|
427
224
|
this.active = true;
|
|
428
225
|
this.dependant.forEach(x => x.commit());
|
|
@@ -433,25 +230,42 @@ class Subscriber {
|
|
|
433
230
|
release();
|
|
434
231
|
};
|
|
435
232
|
}
|
|
233
|
+
static get(from) {
|
|
234
|
+
return REGISTER$1.get(from);
|
|
235
|
+
}
|
|
436
236
|
}
|
|
437
237
|
|
|
438
|
-
const
|
|
439
|
-
const
|
|
440
|
-
const CONTROL = Symbol("CONTROL");
|
|
238
|
+
const REGISTER = new WeakMap();
|
|
239
|
+
const UPDATE$1 = new WeakMap();
|
|
441
240
|
function getUpdate(subject) {
|
|
442
|
-
return UPDATE.get(subject);
|
|
241
|
+
return UPDATE$1.get(subject);
|
|
242
|
+
}
|
|
243
|
+
function setUpdate(subject, keys) {
|
|
244
|
+
UPDATE$1.set(subject, Array.from(keys));
|
|
245
|
+
setTimeout(() => UPDATE$1.delete(subject), 0);
|
|
246
|
+
}
|
|
247
|
+
/** Ensure a local update is dropped after use. */
|
|
248
|
+
function hasUpdate(proxy) {
|
|
249
|
+
if (UPDATE$1.has(proxy))
|
|
250
|
+
setTimeout(() => UPDATE$1.delete(proxy), 0);
|
|
251
|
+
}
|
|
252
|
+
/** Set local update for a subscribed context. */
|
|
253
|
+
function addUpdate(proxy, using) {
|
|
254
|
+
const parent = UPDATE$1.get(getPrototypeOf(proxy));
|
|
255
|
+
UPDATE$1.set(proxy, parent.filter(k => using.has(k)));
|
|
443
256
|
}
|
|
444
|
-
class
|
|
257
|
+
class Control {
|
|
445
258
|
constructor(subject) {
|
|
446
259
|
this.subject = subject;
|
|
447
260
|
this.frame = new Set();
|
|
448
261
|
this.waiting = new Set();
|
|
449
262
|
this.followers = new Set();
|
|
263
|
+
REGISTER.set(subject, this);
|
|
450
264
|
}
|
|
451
265
|
subscribe(callback) {
|
|
452
266
|
return new Subscriber(this, callback);
|
|
453
267
|
}
|
|
454
|
-
|
|
268
|
+
watch(key) {
|
|
455
269
|
const { subject, state } = this;
|
|
456
270
|
const { value } = getOwnPropertyDescriptor(subject, key);
|
|
457
271
|
if (typeof value == "function")
|
|
@@ -470,9 +284,9 @@ class Controller {
|
|
|
470
284
|
enumerable: false,
|
|
471
285
|
set: this.ref(key),
|
|
472
286
|
get() {
|
|
473
|
-
const
|
|
474
|
-
if (
|
|
475
|
-
|
|
287
|
+
const sub = Subscriber.get(this);
|
|
288
|
+
if (sub)
|
|
289
|
+
sub.add(key);
|
|
476
290
|
return state.get(key);
|
|
477
291
|
}
|
|
478
292
|
});
|
|
@@ -500,16 +314,13 @@ class Controller {
|
|
|
500
314
|
};
|
|
501
315
|
}
|
|
502
316
|
update(key) {
|
|
503
|
-
const { followers, frame,
|
|
317
|
+
const { followers, frame, waiting } = this;
|
|
504
318
|
if (frame.has(key))
|
|
505
319
|
return;
|
|
506
320
|
else if (!frame.size)
|
|
507
321
|
setTimeout(() => {
|
|
508
322
|
flush(this);
|
|
509
|
-
|
|
510
|
-
setTimeout(() => {
|
|
511
|
-
UPDATE.delete(subject);
|
|
512
|
-
}, 0);
|
|
323
|
+
setUpdate(this.subject, frame);
|
|
513
324
|
frame.clear();
|
|
514
325
|
for (const notify of waiting)
|
|
515
326
|
try {
|
|
@@ -532,36 +343,35 @@ class Controller {
|
|
|
532
343
|
this.followers.clear();
|
|
533
344
|
listeners.forEach(x => x(null, this));
|
|
534
345
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
346
|
+
static get(from) {
|
|
347
|
+
if (from && "is" in from)
|
|
348
|
+
from = from.is;
|
|
349
|
+
return REGISTER.get(from);
|
|
350
|
+
}
|
|
351
|
+
static for(subject, cb) {
|
|
352
|
+
const control = this.get(subject) || new this(subject);
|
|
353
|
+
if (!control.state) {
|
|
354
|
+
const { waiting } = control;
|
|
355
|
+
if (cb) {
|
|
356
|
+
let done;
|
|
357
|
+
waiting.add(() => {
|
|
358
|
+
done = cb(control);
|
|
359
|
+
});
|
|
360
|
+
return () => done && done();
|
|
361
|
+
}
|
|
362
|
+
control.state = new Map();
|
|
363
|
+
for (const key in subject)
|
|
364
|
+
control.watch(key);
|
|
365
|
+
control.waiting = new Set();
|
|
366
|
+
for (const cb of waiting)
|
|
367
|
+
cb();
|
|
551
368
|
}
|
|
552
|
-
|
|
553
|
-
for (const key in subject)
|
|
554
|
-
control.add(key);
|
|
555
|
-
const callback = Array.from(waiting);
|
|
556
|
-
waiting.clear();
|
|
557
|
-
for (const cb of callback)
|
|
558
|
-
cb();
|
|
369
|
+
return cb ? cb(control) : control;
|
|
559
370
|
}
|
|
560
|
-
return cb ? cb(control) : control;
|
|
561
371
|
}
|
|
562
372
|
|
|
563
373
|
const PENDING = new Map();
|
|
564
|
-
function
|
|
374
|
+
function add(fn, label) {
|
|
565
375
|
const name = label || fn.name || "pending";
|
|
566
376
|
const placeholder = Symbol(`${name} instruction`);
|
|
567
377
|
function setup(key) {
|
|
@@ -575,7 +385,7 @@ function apply(fn, label) {
|
|
|
575
385
|
defineProperty(subject, key, output);
|
|
576
386
|
return false;
|
|
577
387
|
}
|
|
578
|
-
let { get: onGet, set: onSet,
|
|
388
|
+
let { get: onGet, set: onSet, suspend: shouldSuspend } = output;
|
|
579
389
|
if ("value" in output)
|
|
580
390
|
state.set(key, output.value);
|
|
581
391
|
if ("recursive" in output && output.recursive)
|
|
@@ -595,12 +405,11 @@ function apply(fn, label) {
|
|
|
595
405
|
? this.ref(key, onSet)
|
|
596
406
|
: undefined,
|
|
597
407
|
get() {
|
|
598
|
-
if (!state.has(key) &&
|
|
408
|
+
if (!state.has(key) && shouldSuspend)
|
|
599
409
|
throw suspend(control, key);
|
|
600
|
-
const
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
listen(key);
|
|
410
|
+
const local = Subscriber.get(this);
|
|
411
|
+
if (local)
|
|
412
|
+
local.add(key);
|
|
604
413
|
return onGet
|
|
605
414
|
? onGet(local)
|
|
606
415
|
: state.get(key);
|
|
@@ -621,8 +430,8 @@ function getRecursive(key, from) {
|
|
|
621
430
|
if (reset)
|
|
622
431
|
reset();
|
|
623
432
|
const value = from.state.get(key);
|
|
624
|
-
if (value
|
|
625
|
-
const child =
|
|
433
|
+
if (Control.get(value)) {
|
|
434
|
+
const child = Control.for(value).subscribe(local.onUpdate);
|
|
626
435
|
if (local.active)
|
|
627
436
|
child.commit();
|
|
628
437
|
local.dependant.add(child);
|
|
@@ -668,15 +477,15 @@ function __awaiter(thisArg, _arguments, P, generator) {
|
|
|
668
477
|
});
|
|
669
478
|
}
|
|
670
479
|
|
|
671
|
-
const Oops$
|
|
480
|
+
const Oops$9 = issues({
|
|
672
481
|
DuplicatePending: (key) => `Invoked action ${key} but one is already active.`
|
|
673
482
|
});
|
|
674
483
|
function act(task) {
|
|
675
|
-
return
|
|
484
|
+
return add(function act(key) {
|
|
676
485
|
let pending = false;
|
|
677
486
|
const invoke = (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
678
487
|
if (pending)
|
|
679
|
-
return Promise.reject(Oops$
|
|
488
|
+
return Promise.reject(Oops$9.DuplicatePending(key));
|
|
680
489
|
pending = true;
|
|
681
490
|
this.update(key);
|
|
682
491
|
try {
|
|
@@ -719,8 +528,8 @@ function factoryMode(self, output, key, required) {
|
|
|
719
528
|
const suspend = () => {
|
|
720
529
|
if (required === false)
|
|
721
530
|
return undefined;
|
|
722
|
-
const issue = Oops$
|
|
723
|
-
|
|
531
|
+
const issue = Oops$8.NotReady(subject, key);
|
|
532
|
+
assign(pending, {
|
|
724
533
|
message: issue.message,
|
|
725
534
|
stack: issue.stack
|
|
726
535
|
});
|
|
@@ -736,7 +545,7 @@ function factoryMode(self, output, key, required) {
|
|
|
736
545
|
};
|
|
737
546
|
}
|
|
738
547
|
|
|
739
|
-
const Oops$
|
|
548
|
+
const Oops$8 = issues({
|
|
740
549
|
// BadSource: (model, property, got) =>
|
|
741
550
|
// `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
|
|
742
551
|
PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`,
|
|
@@ -746,14 +555,14 @@ const Oops$7 = issues({
|
|
|
746
555
|
Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`
|
|
747
556
|
});
|
|
748
557
|
function get(arg0, arg1, arg2) {
|
|
749
|
-
return
|
|
558
|
+
return add(function get(key) {
|
|
750
559
|
const { subject } = this;
|
|
751
560
|
const computeRequired = arg1 === true || arg2 === true;
|
|
752
561
|
const factoryRequired = arg1 !== false && arg2 !== false;
|
|
753
562
|
let getter;
|
|
754
563
|
// Easy mistake, using a peer, will always be unresolved.
|
|
755
564
|
if (typeof arg0 == "symbol")
|
|
756
|
-
throw Oops$
|
|
565
|
+
throw Oops$8.PeerNotAllowed(subject, key);
|
|
757
566
|
// if(typeof arg0 == "function" && /^[A-Z]/.test(arg0.name))
|
|
758
567
|
// throw Oops.BadSource(subject, key, arg0);
|
|
759
568
|
this.state.set(key, undefined);
|
|
@@ -775,7 +584,7 @@ function get(arg0, arg1, arg2) {
|
|
|
775
584
|
}
|
|
776
585
|
else if (typeof arg1 == "function") {
|
|
777
586
|
// replace source controller in-case it is different
|
|
778
|
-
source =
|
|
587
|
+
source = Control.for(arg0);
|
|
779
588
|
setter = arg1;
|
|
780
589
|
}
|
|
781
590
|
else
|
|
@@ -787,7 +596,7 @@ function get(arg0, arg1, arg2) {
|
|
|
787
596
|
init();
|
|
788
597
|
}
|
|
789
598
|
catch (err) {
|
|
790
|
-
Oops$
|
|
599
|
+
Oops$8.ComputeFailed(subject, key).warn();
|
|
791
600
|
throw err;
|
|
792
601
|
}
|
|
793
602
|
return () => {
|
|
@@ -799,12 +608,12 @@ function get(arg0, arg1, arg2) {
|
|
|
799
608
|
}
|
|
800
609
|
|
|
801
610
|
function ref(arg, mapper) {
|
|
802
|
-
return
|
|
611
|
+
return add(function ref(key) {
|
|
803
612
|
let value = {};
|
|
804
613
|
if (typeof arg != "object")
|
|
805
614
|
value = createRef(this, key, arg);
|
|
806
615
|
else
|
|
807
|
-
|
|
616
|
+
Control.for(arg).state.forEach((_val, key) => {
|
|
808
617
|
defineProperty(value, key, mapper ? {
|
|
809
618
|
configurable: true,
|
|
810
619
|
get() {
|
|
@@ -833,11 +642,11 @@ function set(value, argument) {
|
|
|
833
642
|
argument = value;
|
|
834
643
|
value = undefined;
|
|
835
644
|
}
|
|
836
|
-
return
|
|
645
|
+
return add(function set(key) {
|
|
837
646
|
if (value !== undefined)
|
|
838
647
|
this.state.set(key, value);
|
|
839
648
|
return {
|
|
840
|
-
|
|
649
|
+
suspend: true,
|
|
841
650
|
set: typeof argument == "function"
|
|
842
651
|
? createValueEffect(argument)
|
|
843
652
|
: undefined
|
|
@@ -992,12 +801,12 @@ function createProxy(emit, watch) {
|
|
|
992
801
|
}
|
|
993
802
|
|
|
994
803
|
const Parent = new WeakMap();
|
|
995
|
-
const Oops$
|
|
804
|
+
const Oops$7 = issues({
|
|
996
805
|
BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`,
|
|
997
806
|
NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
|
|
998
807
|
});
|
|
999
808
|
function use$1(input, argument) {
|
|
1000
|
-
return
|
|
809
|
+
return add(function use(key) {
|
|
1001
810
|
const { state, subject } = this;
|
|
1002
811
|
const required = argument !== false;
|
|
1003
812
|
let pending;
|
|
@@ -1009,7 +818,7 @@ function use$1(input, argument) {
|
|
|
1009
818
|
input = new input();
|
|
1010
819
|
}
|
|
1011
820
|
else if (input && typeof input !== "object")
|
|
1012
|
-
throw Oops$
|
|
821
|
+
throw Oops$7.BadArgument(typeof input);
|
|
1013
822
|
if (input instanceof Map || input instanceof Set)
|
|
1014
823
|
return keyed(this, key, input);
|
|
1015
824
|
const onUpdate = (next) => {
|
|
@@ -1017,7 +826,7 @@ function use$1(input, argument) {
|
|
|
1017
826
|
if (next) {
|
|
1018
827
|
get = getRecursive(key, this);
|
|
1019
828
|
Parent.set(next, subject);
|
|
1020
|
-
|
|
829
|
+
Control.for(next);
|
|
1021
830
|
}
|
|
1022
831
|
if (typeof argument == "function")
|
|
1023
832
|
argument(next);
|
|
@@ -1026,8 +835,8 @@ function use$1(input, argument) {
|
|
|
1026
835
|
const suspend = () => {
|
|
1027
836
|
if (required === false)
|
|
1028
837
|
return;
|
|
1029
|
-
const issue = Oops$
|
|
1030
|
-
|
|
838
|
+
const issue = Oops$7.NotReady(subject, key);
|
|
839
|
+
assign(pending, {
|
|
1031
840
|
message: issue.message,
|
|
1032
841
|
stack: issue.stack
|
|
1033
842
|
});
|
|
@@ -1071,25 +880,204 @@ function use$1(input, argument) {
|
|
|
1071
880
|
});
|
|
1072
881
|
}
|
|
1073
882
|
|
|
1074
|
-
const Oops$
|
|
883
|
+
const Oops$6 = issues({
|
|
1075
884
|
Required: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
|
|
1076
885
|
Unexpected: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
|
|
1077
886
|
});
|
|
1078
|
-
function has(
|
|
1079
|
-
return
|
|
887
|
+
function has(Type, required) {
|
|
888
|
+
return add(function parent() {
|
|
1080
889
|
const child = this.subject;
|
|
1081
890
|
const value = Parent.get(child);
|
|
1082
|
-
const expected =
|
|
891
|
+
const expected = Type.name;
|
|
1083
892
|
if (!value) {
|
|
1084
893
|
if (required !== false)
|
|
1085
|
-
throw Oops$
|
|
894
|
+
throw Oops$6.Required(expected, child);
|
|
1086
895
|
}
|
|
1087
|
-
else if (!(value instanceof
|
|
1088
|
-
throw Oops$
|
|
896
|
+
else if (!(value instanceof Type))
|
|
897
|
+
throw Oops$6.Unexpected(expected, child, value);
|
|
1089
898
|
return { value };
|
|
1090
899
|
});
|
|
1091
900
|
}
|
|
1092
901
|
|
|
902
|
+
const Oops$5 = issues({
|
|
903
|
+
Timeout: (keys, timeout) => `No update for [${keys}] within ${timeout}.`,
|
|
904
|
+
StrictUpdate: () => `Strict update() did not find pending updates.`,
|
|
905
|
+
NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
|
|
906
|
+
});
|
|
907
|
+
class Model {
|
|
908
|
+
constructor() {
|
|
909
|
+
new Control(this);
|
|
910
|
+
defineProperty(this, "is", { value: this });
|
|
911
|
+
}
|
|
912
|
+
on(select, argument, once) {
|
|
913
|
+
if (typeof select == "function")
|
|
914
|
+
return Control.for(this, x => x.addListener(select));
|
|
915
|
+
const single = typeof select == "string";
|
|
916
|
+
let keys = single ? [select] : select;
|
|
917
|
+
if (typeof argument !== "function")
|
|
918
|
+
return new Promise((resolve, reject) => {
|
|
919
|
+
const removeListener = Control.for(this, control => {
|
|
920
|
+
for (const key of keys)
|
|
921
|
+
try {
|
|
922
|
+
void this[key];
|
|
923
|
+
}
|
|
924
|
+
catch (e) { }
|
|
925
|
+
return control.addListener(key => {
|
|
926
|
+
if (!key) {
|
|
927
|
+
removeListener();
|
|
928
|
+
reject(Oops$5.Timeout(select, `lifetime of ${this}`));
|
|
929
|
+
}
|
|
930
|
+
else if (keys.includes(key)) {
|
|
931
|
+
removeListener();
|
|
932
|
+
return () => resolve(single ? control.state.get(key) : getUpdate(this));
|
|
933
|
+
}
|
|
934
|
+
});
|
|
935
|
+
});
|
|
936
|
+
if (typeof argument == "number")
|
|
937
|
+
setTimeout(() => {
|
|
938
|
+
removeListener();
|
|
939
|
+
reject(Oops$5.Timeout(select, `${argument}ms`));
|
|
940
|
+
}, argument);
|
|
941
|
+
});
|
|
942
|
+
return Control.for(this, control => {
|
|
943
|
+
if (!keys.length)
|
|
944
|
+
keys = [...control.state.keys()];
|
|
945
|
+
for (const key of keys)
|
|
946
|
+
try {
|
|
947
|
+
void this[key];
|
|
948
|
+
}
|
|
949
|
+
catch (e) { }
|
|
950
|
+
const callback = single
|
|
951
|
+
? () => { argument.call(this, control.state.get(select), select); }
|
|
952
|
+
: () => { argument.call(this, getUpdate(this)); };
|
|
953
|
+
const onEvent = once
|
|
954
|
+
? () => {
|
|
955
|
+
removeListener();
|
|
956
|
+
callback();
|
|
957
|
+
}
|
|
958
|
+
: callback;
|
|
959
|
+
const removeListener = control.addListener(key => {
|
|
960
|
+
if (keys.includes(key))
|
|
961
|
+
return onEvent;
|
|
962
|
+
});
|
|
963
|
+
return removeListener;
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
effect(callback, select) {
|
|
967
|
+
const effect = createEffect(callback);
|
|
968
|
+
return Control.for(this, control => {
|
|
969
|
+
let busy = false;
|
|
970
|
+
let inject = this.is;
|
|
971
|
+
const invoke = () => {
|
|
972
|
+
if (busy)
|
|
973
|
+
return;
|
|
974
|
+
const output = mayRetry(() => {
|
|
975
|
+
effect.call(inject, inject);
|
|
976
|
+
});
|
|
977
|
+
if (output instanceof Promise) {
|
|
978
|
+
output.finally(() => busy = false);
|
|
979
|
+
busy = true;
|
|
980
|
+
}
|
|
981
|
+
};
|
|
982
|
+
if (!select) {
|
|
983
|
+
const sub = control.subscribe(() => invoke);
|
|
984
|
+
inject = sub.proxy;
|
|
985
|
+
invoke();
|
|
986
|
+
return sub.commit();
|
|
987
|
+
}
|
|
988
|
+
invoke();
|
|
989
|
+
return control.addListener(key => {
|
|
990
|
+
if (key === null) {
|
|
991
|
+
if (!select.length)
|
|
992
|
+
invoke();
|
|
993
|
+
}
|
|
994
|
+
else if (select.includes(key))
|
|
995
|
+
return invoke;
|
|
996
|
+
});
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
export(subset) {
|
|
1000
|
+
const { state } = Control.for(this);
|
|
1001
|
+
const output = {};
|
|
1002
|
+
const keys = subset || state.keys();
|
|
1003
|
+
for (const key of keys)
|
|
1004
|
+
output[key] = state.get(key);
|
|
1005
|
+
return output;
|
|
1006
|
+
}
|
|
1007
|
+
update(arg1, arg2) {
|
|
1008
|
+
const control = Control.for(this);
|
|
1009
|
+
const { frame, state, waiting } = control;
|
|
1010
|
+
if (arg1 === true && !frame.size)
|
|
1011
|
+
return Promise.reject(Oops$5.StrictUpdate());
|
|
1012
|
+
if (typeof arg1 == "object") {
|
|
1013
|
+
for (const key in arg1)
|
|
1014
|
+
if (arg2 === true || (arg2 ? arg2.includes(key) : state.has(key))) {
|
|
1015
|
+
state.set(key, arg1[key]);
|
|
1016
|
+
control.update(key);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
else if (typeof arg1 == "string") {
|
|
1020
|
+
control.update(arg1);
|
|
1021
|
+
if (1 in arguments && arg1 in this) {
|
|
1022
|
+
const method = this[arg1];
|
|
1023
|
+
if (typeof method == "function")
|
|
1024
|
+
if (typeof arg2 != "boolean")
|
|
1025
|
+
method.call(this, arg2);
|
|
1026
|
+
else if (arg2)
|
|
1027
|
+
method.call(this);
|
|
1028
|
+
}
|
|
1029
|
+
arg1 = undefined;
|
|
1030
|
+
}
|
|
1031
|
+
return {
|
|
1032
|
+
then: (callback) => {
|
|
1033
|
+
if (!callback)
|
|
1034
|
+
throw Oops$5.NoChaining();
|
|
1035
|
+
if (frame.size || arg1 !== false)
|
|
1036
|
+
waiting.add(() => {
|
|
1037
|
+
callback(getUpdate(this));
|
|
1038
|
+
});
|
|
1039
|
+
else
|
|
1040
|
+
callback(false);
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Mark this instance for garbage-collection and send `willDestroy` event to all listeners.
|
|
1046
|
+
*/
|
|
1047
|
+
kill() {
|
|
1048
|
+
Control.for(this).clear();
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Creates a new instance of this controller.
|
|
1052
|
+
*
|
|
1053
|
+
* Beyond `new this(...)`, method will activate managed-state.
|
|
1054
|
+
*
|
|
1055
|
+
* @param args - arguments sent to constructor
|
|
1056
|
+
*/
|
|
1057
|
+
static new(...args) {
|
|
1058
|
+
const instance = new this(...args);
|
|
1059
|
+
Control.for(instance);
|
|
1060
|
+
return instance;
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Static equivalent of `x instanceof this`.
|
|
1064
|
+
*
|
|
1065
|
+
* Will determine if provided class is a subtype of this one.
|
|
1066
|
+
*/
|
|
1067
|
+
static isTypeof(maybe) {
|
|
1068
|
+
return (typeof maybe == "function" &&
|
|
1069
|
+
maybe.prototype instanceof this);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
defineProperty(Model.prototype, "toString", {
|
|
1073
|
+
value() {
|
|
1074
|
+
return this.constructor.name;
|
|
1075
|
+
}
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
const Oops$4 = issues({
|
|
1079
|
+
NotFound: (name) => `Couldn't find ${name} in context; did you forget to use a Provider?`
|
|
1080
|
+
});
|
|
1093
1081
|
class Lookup {
|
|
1094
1082
|
constructor() {
|
|
1095
1083
|
this.table = new Map();
|
|
@@ -1126,20 +1114,19 @@ class Lookup {
|
|
|
1126
1114
|
} while (T !== Model);
|
|
1127
1115
|
}
|
|
1128
1116
|
pop() {
|
|
1117
|
+
const items = new Set();
|
|
1129
1118
|
for (const key of getOwnPropertySymbols(this)) {
|
|
1130
1119
|
const entry = getOwnPropertyDescriptor(this, key);
|
|
1131
1120
|
if (entry.writable && entry.value)
|
|
1132
|
-
entry.value
|
|
1121
|
+
items.add(entry.value);
|
|
1133
1122
|
}
|
|
1123
|
+
for (const model of items)
|
|
1124
|
+
model.kill();
|
|
1134
1125
|
}
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1126
|
+
}
|
|
1137
1127
|
const LookupContext = React.createContext(new Lookup());
|
|
1138
1128
|
const useLookup = () => React.useContext(LookupContext);
|
|
1139
|
-
|
|
1140
|
-
NotFound: (name) => `Couldn't find ${name} in context; did you forget to use a Provider?`
|
|
1141
|
-
});
|
|
1142
|
-
function useLocal(Type, arg) {
|
|
1129
|
+
function useContext(Type, arg) {
|
|
1143
1130
|
const instance = useLookup().get(Type);
|
|
1144
1131
|
if (!instance && arg !== false)
|
|
1145
1132
|
throw Oops$4.NotFound(Type.name);
|
|
@@ -1147,7 +1134,7 @@ function useLocal(Type, arg) {
|
|
|
1147
1134
|
case "string":
|
|
1148
1135
|
return instance[arg];
|
|
1149
1136
|
case "function":
|
|
1150
|
-
useLayoutEffect(() => arg(instance), []);
|
|
1137
|
+
React.useLayoutEffect(() => arg(instance), []);
|
|
1151
1138
|
default:
|
|
1152
1139
|
return instance;
|
|
1153
1140
|
}
|
|
@@ -1160,7 +1147,7 @@ const Oops$3 = issues({
|
|
|
1160
1147
|
const PendingContext = new WeakMap();
|
|
1161
1148
|
const ContextWasUsed = new WeakMap();
|
|
1162
1149
|
function tap(type, argument) {
|
|
1163
|
-
return
|
|
1150
|
+
return add(function tap(key) {
|
|
1164
1151
|
if ("set" in type)
|
|
1165
1152
|
return {
|
|
1166
1153
|
recursive: true,
|
|
@@ -1182,7 +1169,7 @@ function tap(type, argument) {
|
|
|
1182
1169
|
});
|
|
1183
1170
|
return {
|
|
1184
1171
|
recursive: true,
|
|
1185
|
-
|
|
1172
|
+
suspend: true
|
|
1186
1173
|
};
|
|
1187
1174
|
});
|
|
1188
1175
|
}
|
|
@@ -1217,57 +1204,51 @@ function use(init) {
|
|
|
1217
1204
|
|
|
1218
1205
|
function useModel(source, arg, arg2) {
|
|
1219
1206
|
const instance = React.useMemo(() => {
|
|
1220
|
-
const
|
|
1221
|
-
const instance =
|
|
1222
|
-
source.
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
if (typeof cb == "function")
|
|
1228
|
-
cb(instance);
|
|
1207
|
+
const callback = arg2 || arg;
|
|
1208
|
+
const instance = Model.isTypeof(source) ?
|
|
1209
|
+
source.new() :
|
|
1210
|
+
source();
|
|
1211
|
+
Control.for(instance);
|
|
1212
|
+
if (typeof callback == "function")
|
|
1213
|
+
callback(instance);
|
|
1229
1214
|
return instance;
|
|
1230
1215
|
}, []);
|
|
1216
|
+
usePeerContext(instance);
|
|
1231
1217
|
if (Array.isArray(arg)) {
|
|
1232
1218
|
const update = useState(0)[1];
|
|
1233
1219
|
React.useLayoutEffect(() => {
|
|
1234
1220
|
if (arg.length && instance instanceof Model)
|
|
1235
|
-
instance.on(arg, () => update(x => x + 1)
|
|
1221
|
+
instance.on(arg, () => update(x => x + 1));
|
|
1236
1222
|
return () => {
|
|
1237
1223
|
if (Model.isTypeof(source))
|
|
1238
|
-
instance.
|
|
1224
|
+
instance.kill();
|
|
1239
1225
|
};
|
|
1240
1226
|
}, []);
|
|
1241
1227
|
return instance;
|
|
1242
1228
|
}
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
instance[key] = arg[key];
|
|
1252
|
-
React.useLayoutEffect(() => {
|
|
1253
|
-
local.active = true;
|
|
1254
|
-
});
|
|
1255
|
-
}
|
|
1256
|
-
React.useLayoutEffect(() => {
|
|
1257
|
-
local.commit();
|
|
1258
|
-
return () => {
|
|
1259
|
-
local.release();
|
|
1260
|
-
if (Model.isTypeof(source))
|
|
1261
|
-
instance.destroy();
|
|
1262
|
-
};
|
|
1263
|
-
}, []);
|
|
1264
|
-
return local.proxy;
|
|
1229
|
+
const local = use(refresh => (Control.for(instance).subscribe(() => refresh)));
|
|
1230
|
+
if (typeof arg == "object") {
|
|
1231
|
+
local.active = false;
|
|
1232
|
+
if (typeof arg2 !== "object")
|
|
1233
|
+
arg2 = getOwnPropertyNames(instance);
|
|
1234
|
+
for (const key of arg2)
|
|
1235
|
+
if (key in arg)
|
|
1236
|
+
instance[key] = arg[key];
|
|
1265
1237
|
}
|
|
1238
|
+
React.useLayoutEffect(() => {
|
|
1239
|
+
local.commit();
|
|
1240
|
+
return () => {
|
|
1241
|
+
local.release();
|
|
1242
|
+
if (Model.isTypeof(source))
|
|
1243
|
+
instance.kill();
|
|
1244
|
+
};
|
|
1245
|
+
}, []);
|
|
1246
|
+
return local.proxy;
|
|
1266
1247
|
}
|
|
1267
1248
|
|
|
1268
1249
|
function useFrom(target, compute, suspend) {
|
|
1269
1250
|
const local = use(refresh => {
|
|
1270
|
-
const sub =
|
|
1251
|
+
const sub = Control.for(target).subscribe(() => update);
|
|
1271
1252
|
const spy = sub.proxy;
|
|
1272
1253
|
let value = compute.call(spy, spy);
|
|
1273
1254
|
let update;
|
|
@@ -1306,84 +1287,79 @@ function useFrom(target, compute, suspend) {
|
|
|
1306
1287
|
return local.proxy;
|
|
1307
1288
|
}
|
|
1308
1289
|
|
|
1309
|
-
function useTap(source,
|
|
1290
|
+
function useTap(source, arg1, arg2) {
|
|
1310
1291
|
const instance = useMemo(() => {
|
|
1311
1292
|
if (typeof source == "object")
|
|
1312
1293
|
return () => source;
|
|
1313
|
-
if (
|
|
1314
|
-
return () => source.get();
|
|
1315
|
-
if (
|
|
1316
|
-
return () =>
|
|
1317
|
-
|
|
1318
|
-
return source;
|
|
1294
|
+
if (MVC.isTypeof(source))
|
|
1295
|
+
return () => source.get(true);
|
|
1296
|
+
if (Model.isTypeof(source))
|
|
1297
|
+
return () => useContext(source);
|
|
1298
|
+
return source;
|
|
1319
1299
|
}, [])();
|
|
1320
|
-
if (typeof
|
|
1321
|
-
return useFrom(instance,
|
|
1322
|
-
const local = use(refresh => (
|
|
1300
|
+
if (typeof arg1 == "function")
|
|
1301
|
+
return useFrom(instance, arg1, arg2);
|
|
1302
|
+
const local = use(refresh => (Control.for(instance).subscribe(() => refresh)));
|
|
1323
1303
|
React.useLayoutEffect(local.commit, []);
|
|
1324
|
-
if (
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
return local.proxy;
|
|
1304
|
+
if (arg1 === undefined)
|
|
1305
|
+
return local.proxy;
|
|
1306
|
+
const value = local.proxy[arg1];
|
|
1307
|
+
if (value === undefined && arg2)
|
|
1308
|
+
throw suspend(local.parent, arg1);
|
|
1309
|
+
return value;
|
|
1331
1310
|
}
|
|
1332
1311
|
|
|
1333
|
-
/* istanbul ignore file */
|
|
1334
1312
|
class MVC extends Model {
|
|
1335
|
-
tap(path, expect) {
|
|
1336
|
-
return useTap(this, path, expect);
|
|
1337
|
-
}
|
|
1338
|
-
use(arg, callback) {
|
|
1339
|
-
return useModel(this, arg, callback);
|
|
1340
|
-
}
|
|
1341
1313
|
static get(arg) {
|
|
1342
|
-
return
|
|
1314
|
+
return useContext(this, arg);
|
|
1343
1315
|
}
|
|
1344
1316
|
static tap(key, expect) {
|
|
1345
1317
|
return useTap(this, key, expect);
|
|
1346
1318
|
}
|
|
1347
|
-
static new(arg) {
|
|
1348
|
-
const instance = React.useMemo(() => {
|
|
1349
|
-
const instance = new this();
|
|
1350
|
-
ensure(instance);
|
|
1351
|
-
if (typeof arg == "function")
|
|
1352
|
-
arg(instance);
|
|
1353
|
-
else if (arg)
|
|
1354
|
-
getOwnPropertyNames(instance).forEach(key => {
|
|
1355
|
-
if (key in arg)
|
|
1356
|
-
instance[key] = arg[key];
|
|
1357
|
-
});
|
|
1358
|
-
return instance;
|
|
1359
|
-
}, []);
|
|
1360
|
-
usePeerContext(instance.is);
|
|
1361
|
-
return instance;
|
|
1362
|
-
}
|
|
1363
1319
|
static use(a, b) {
|
|
1364
|
-
|
|
1365
|
-
usePeerContext(instance.is);
|
|
1366
|
-
return instance;
|
|
1367
|
-
}
|
|
1368
|
-
/**
|
|
1369
|
-
* @deprecated consider doing this manually - not worth minor efficiency gain over using.
|
|
1370
|
-
*/
|
|
1371
|
-
static uses(apply, keys) {
|
|
1372
|
-
return this.use(instance => {
|
|
1373
|
-
instance.import(apply, keys);
|
|
1374
|
-
});
|
|
1375
|
-
}
|
|
1376
|
-
/**
|
|
1377
|
-
* @deprecated is now replaced by overload of `use` method.
|
|
1378
|
-
*/
|
|
1379
|
-
static using(apply, keys) {
|
|
1380
|
-
return this.use(apply, keys);
|
|
1320
|
+
return useModel(this, a, b);
|
|
1381
1321
|
}
|
|
1382
1322
|
static meta(path, expect) {
|
|
1383
1323
|
return useTap(() => this, path, expect);
|
|
1384
1324
|
}
|
|
1385
1325
|
}
|
|
1386
1326
|
|
|
1327
|
+
const LOCAL = Symbol("LOCAL");
|
|
1328
|
+
const STATE = Symbol("STATE");
|
|
1329
|
+
const UPDATE = Symbol("UPDATE");
|
|
1330
|
+
const CONTROL = Symbol("CONTROL");
|
|
1331
|
+
Object.defineProperties(Model.prototype, {
|
|
1332
|
+
[CONTROL]: {
|
|
1333
|
+
get() {
|
|
1334
|
+
return Control.get(this);
|
|
1335
|
+
}
|
|
1336
|
+
},
|
|
1337
|
+
[LOCAL]: {
|
|
1338
|
+
get() {
|
|
1339
|
+
return Subscriber.get(this);
|
|
1340
|
+
}
|
|
1341
|
+
},
|
|
1342
|
+
[STATE]: {
|
|
1343
|
+
get() {
|
|
1344
|
+
const { state } = Control.get(this);
|
|
1345
|
+
const output = {};
|
|
1346
|
+
state.forEach((value, key) => output[key] = value);
|
|
1347
|
+
return output;
|
|
1348
|
+
}
|
|
1349
|
+
},
|
|
1350
|
+
[UPDATE]: {
|
|
1351
|
+
get() {
|
|
1352
|
+
return getUpdate(this);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
});
|
|
1356
|
+
const Debug = {
|
|
1357
|
+
CONTROL,
|
|
1358
|
+
LOCAL,
|
|
1359
|
+
STATE,
|
|
1360
|
+
UPDATE
|
|
1361
|
+
};
|
|
1362
|
+
|
|
1387
1363
|
const Oops$2 = issues({
|
|
1388
1364
|
BadProps: () => `Provider expects either a render function, 'get' or 'has' props.`
|
|
1389
1365
|
});
|
|
@@ -1393,66 +1369,66 @@ function Consumer(props) {
|
|
|
1393
1369
|
return children(useTap(type));
|
|
1394
1370
|
const callback = has || get;
|
|
1395
1371
|
if (typeof callback == "function")
|
|
1396
|
-
callback(
|
|
1372
|
+
callback(useContext(type, !!has));
|
|
1397
1373
|
else
|
|
1398
1374
|
throw Oops$2.BadProps();
|
|
1399
1375
|
return null;
|
|
1400
1376
|
}
|
|
1401
1377
|
|
|
1402
1378
|
const Oops$1 = issues({
|
|
1403
|
-
NoType: () => "Provider '
|
|
1404
|
-
PropDeprecated: () => "Provider `of` prop is deprecated, use `for` instead."
|
|
1379
|
+
NoType: () => "Provider 'for' prop must be Model, typeof Model or a collection of them."
|
|
1405
1380
|
});
|
|
1406
1381
|
function Provider(props) {
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1382
|
+
const context = useNewContext(props.for);
|
|
1383
|
+
const render = props.children;
|
|
1384
|
+
useAppliedProps(context, props);
|
|
1385
|
+
React.useLayoutEffect(() => () => context.pop(), []);
|
|
1386
|
+
return React.createElement(LookupContext.Provider, { value: context }, typeof render == "function"
|
|
1387
|
+
? React.createElement(RenderFunction, { context, render })
|
|
1388
|
+
: render);
|
|
1389
|
+
}
|
|
1390
|
+
function useNewContext(include) {
|
|
1391
|
+
const ambient = useLookup();
|
|
1392
|
+
return React.useMemo(() => {
|
|
1393
|
+
if (!include)
|
|
1413
1394
|
throw Oops$1.NoType();
|
|
1414
|
-
const layer =
|
|
1395
|
+
const layer = ambient.push();
|
|
1415
1396
|
function register(I) {
|
|
1416
1397
|
if (I instanceof Model)
|
|
1417
1398
|
layer.inject(I.constructor, I, false);
|
|
1418
1399
|
else
|
|
1419
|
-
layer.inject(I, I.
|
|
1400
|
+
layer.inject(I, I.new(), true);
|
|
1420
1401
|
}
|
|
1421
|
-
if (
|
|
1422
|
-
register(
|
|
1402
|
+
if (include instanceof Model || typeof include == "function")
|
|
1403
|
+
register(include);
|
|
1423
1404
|
else
|
|
1424
|
-
values(
|
|
1405
|
+
values(include).forEach(register);
|
|
1425
1406
|
for (const instance of layer.local)
|
|
1426
1407
|
if (instance)
|
|
1427
1408
|
for (const apply of getPending(instance))
|
|
1428
1409
|
apply(layer);
|
|
1429
1410
|
return layer;
|
|
1430
1411
|
}, []);
|
|
1431
|
-
const render = props.children;
|
|
1432
|
-
useAppliedProps(context, props);
|
|
1433
|
-
React.useLayoutEffect(() => () => context.pop(), []);
|
|
1434
|
-
return React.createElement(LookupContext.Provider, { value: context }, typeof render == "function"
|
|
1435
|
-
? React.createElement(RenderFunction, { context, render })
|
|
1436
|
-
: render);
|
|
1437
1412
|
}
|
|
1438
|
-
function useAppliedProps(
|
|
1439
|
-
const
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1413
|
+
function useAppliedProps(context, props) {
|
|
1414
|
+
const apply = React.useMemo(() => {
|
|
1415
|
+
return (props) => {
|
|
1416
|
+
entries(props).forEach(([key, value]) => {
|
|
1417
|
+
if (key == "for" || key == "children")
|
|
1418
|
+
return;
|
|
1419
|
+
for (const into of context.local)
|
|
1420
|
+
if (into && key in into)
|
|
1421
|
+
into[key] = value;
|
|
1422
|
+
});
|
|
1447
1423
|
};
|
|
1448
1424
|
}, []);
|
|
1449
|
-
|
|
1425
|
+
apply(props);
|
|
1450
1426
|
}
|
|
1451
1427
|
function RenderFunction(props) {
|
|
1452
1428
|
const hook = use(refresh => {
|
|
1453
1429
|
const targets = props.context.local;
|
|
1454
1430
|
if (targets.length == 1)
|
|
1455
|
-
return
|
|
1431
|
+
return Control.for(targets[0]).subscribe(() => refresh);
|
|
1456
1432
|
return {};
|
|
1457
1433
|
});
|
|
1458
1434
|
if (hook.commit)
|
|
@@ -1461,17 +1437,31 @@ function RenderFunction(props) {
|
|
|
1461
1437
|
}
|
|
1462
1438
|
|
|
1463
1439
|
const Oops = issues({
|
|
1464
|
-
DoesNotExist: (name) => `Tried to access singleton ${name}, but none exist! Did you forget to initialize?\nCall ${name}.
|
|
1440
|
+
DoesNotExist: (name) => `Tried to access singleton ${name}, but none exist! Did you forget to initialize?\nCall ${name}.new() before attempting to access, or consider using ${name}.use() instead.`,
|
|
1465
1441
|
AlreadyExists: (name) => `Shared instance of ${name} already exists! Consider unmounting existing, or use ${name}.reset() to force-delete it.`
|
|
1466
1442
|
});
|
|
1467
1443
|
const Active = new WeakMap();
|
|
1468
1444
|
class Global extends MVC {
|
|
1469
|
-
|
|
1445
|
+
constructor() {
|
|
1446
|
+
super();
|
|
1447
|
+
Active.set(this.constructor, this);
|
|
1448
|
+
}
|
|
1449
|
+
kill(force) {
|
|
1450
|
+
const type = this.constructor;
|
|
1451
|
+
if (type.keepAlive && !force)
|
|
1452
|
+
return false;
|
|
1453
|
+
super.kill();
|
|
1454
|
+
Active.delete(type);
|
|
1455
|
+
return true;
|
|
1456
|
+
}
|
|
1457
|
+
static new(...args) {
|
|
1470
1458
|
if (Active.has(this))
|
|
1471
|
-
|
|
1459
|
+
if (this.keepAlive)
|
|
1460
|
+
return Active.get(this);
|
|
1461
|
+
else
|
|
1462
|
+
throw Oops.AlreadyExists(this.name);
|
|
1472
1463
|
const instance = new this(...args);
|
|
1473
|
-
|
|
1474
|
-
Active.set(this, instance);
|
|
1464
|
+
Control.for(instance);
|
|
1475
1465
|
return instance;
|
|
1476
1466
|
}
|
|
1477
1467
|
/**
|
|
@@ -1479,10 +1469,8 @@ class Global extends MVC {
|
|
|
1479
1469
|
* Returns a thenable; resolves after successful update.
|
|
1480
1470
|
* If instance does not already exist, one will be created.
|
|
1481
1471
|
*/
|
|
1482
|
-
static set(
|
|
1483
|
-
|
|
1484
|
-
instance.import(updates);
|
|
1485
|
-
return instance.update(false);
|
|
1472
|
+
static set(values) {
|
|
1473
|
+
return this.get(true).update(values);
|
|
1486
1474
|
}
|
|
1487
1475
|
static get(arg) {
|
|
1488
1476
|
const instance = Active.get(this);
|
|
@@ -1496,13 +1484,10 @@ class Global extends MVC {
|
|
|
1496
1484
|
static reset() {
|
|
1497
1485
|
const current = Active.get(this);
|
|
1498
1486
|
if (current)
|
|
1499
|
-
current.
|
|
1487
|
+
current.kill(true);
|
|
1500
1488
|
}
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
Active.delete(this.constructor);
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1489
|
+
}
|
|
1490
|
+
Global.keepAlive = true;
|
|
1506
1491
|
|
|
1507
|
-
export {
|
|
1492
|
+
export { Consumer, Debug, Global, MVC, MVC as Model, Provider, act, add, MVC as default, get, has, ref, set, tap, use$1 as use, useContext, useFrom, useModel, useTap };
|
|
1508
1493
|
//# sourceMappingURL=index.esm.js.map
|