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