@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.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);
|
|
254
|
+
}
|
|
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)));
|
|
451
264
|
}
|
|
452
|
-
class
|
|
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
|
|
@@ -1000,12 +809,12 @@ function createProxy(emit, watch) {
|
|
|
1000
809
|
}
|
|
1001
810
|
|
|
1002
811
|
const Parent = new WeakMap();
|
|
1003
|
-
const Oops$
|
|
812
|
+
const Oops$7 = issues({
|
|
1004
813
|
BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`,
|
|
1005
814
|
NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
|
|
1006
815
|
});
|
|
1007
816
|
function use$1(input, argument) {
|
|
1008
|
-
return
|
|
817
|
+
return add(function use(key) {
|
|
1009
818
|
const { state, subject } = this;
|
|
1010
819
|
const required = argument !== false;
|
|
1011
820
|
let pending;
|
|
@@ -1017,7 +826,7 @@ function use$1(input, argument) {
|
|
|
1017
826
|
input = new input();
|
|
1018
827
|
}
|
|
1019
828
|
else if (input && typeof input !== "object")
|
|
1020
|
-
throw Oops$
|
|
829
|
+
throw Oops$7.BadArgument(typeof input);
|
|
1021
830
|
if (input instanceof Map || input instanceof Set)
|
|
1022
831
|
return keyed(this, key, input);
|
|
1023
832
|
const onUpdate = (next) => {
|
|
@@ -1025,7 +834,7 @@ function use$1(input, argument) {
|
|
|
1025
834
|
if (next) {
|
|
1026
835
|
get = getRecursive(key, this);
|
|
1027
836
|
Parent.set(next, subject);
|
|
1028
|
-
|
|
837
|
+
Control.for(next);
|
|
1029
838
|
}
|
|
1030
839
|
if (typeof argument == "function")
|
|
1031
840
|
argument(next);
|
|
@@ -1034,8 +843,8 @@ function use$1(input, argument) {
|
|
|
1034
843
|
const suspend = () => {
|
|
1035
844
|
if (required === false)
|
|
1036
845
|
return;
|
|
1037
|
-
const issue = Oops$
|
|
1038
|
-
|
|
846
|
+
const issue = Oops$7.NotReady(subject, key);
|
|
847
|
+
assign(pending, {
|
|
1039
848
|
message: issue.message,
|
|
1040
849
|
stack: issue.stack
|
|
1041
850
|
});
|
|
@@ -1079,25 +888,204 @@ function use$1(input, argument) {
|
|
|
1079
888
|
});
|
|
1080
889
|
}
|
|
1081
890
|
|
|
1082
|
-
const Oops$
|
|
891
|
+
const Oops$6 = issues({
|
|
1083
892
|
Required: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
|
|
1084
893
|
Unexpected: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
|
|
1085
894
|
});
|
|
1086
|
-
function has(
|
|
1087
|
-
return
|
|
895
|
+
function has(Type, required) {
|
|
896
|
+
return add(function parent() {
|
|
1088
897
|
const child = this.subject;
|
|
1089
898
|
const value = Parent.get(child);
|
|
1090
|
-
const expected =
|
|
899
|
+
const expected = Type.name;
|
|
1091
900
|
if (!value) {
|
|
1092
901
|
if (required !== false)
|
|
1093
|
-
throw Oops$
|
|
902
|
+
throw Oops$6.Required(expected, child);
|
|
1094
903
|
}
|
|
1095
|
-
else if (!(value instanceof
|
|
1096
|
-
throw Oops$
|
|
904
|
+
else if (!(value instanceof Type))
|
|
905
|
+
throw Oops$6.Unexpected(expected, child, value);
|
|
1097
906
|
return { value };
|
|
1098
907
|
});
|
|
1099
908
|
}
|
|
1100
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
|
+
});
|
|
1101
1089
|
class Lookup {
|
|
1102
1090
|
constructor() {
|
|
1103
1091
|
this.table = new Map();
|
|
@@ -1134,20 +1122,19 @@ class Lookup {
|
|
|
1134
1122
|
} while (T !== Model);
|
|
1135
1123
|
}
|
|
1136
1124
|
pop() {
|
|
1125
|
+
const items = new Set();
|
|
1137
1126
|
for (const key of getOwnPropertySymbols(this)) {
|
|
1138
1127
|
const entry = getOwnPropertyDescriptor(this, key);
|
|
1139
1128
|
if (entry.writable && entry.value)
|
|
1140
|
-
entry.value
|
|
1129
|
+
items.add(entry.value);
|
|
1141
1130
|
}
|
|
1131
|
+
for (const model of items)
|
|
1132
|
+
model.kill();
|
|
1142
1133
|
}
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1134
|
+
}
|
|
1145
1135
|
const LookupContext = React__default["default"].createContext(new Lookup());
|
|
1146
1136
|
const useLookup = () => React__default["default"].useContext(LookupContext);
|
|
1147
|
-
|
|
1148
|
-
NotFound: (name) => `Couldn't find ${name} in context; did you forget to use a Provider?`
|
|
1149
|
-
});
|
|
1150
|
-
function useLocal(Type, arg) {
|
|
1137
|
+
function useContext(Type, arg) {
|
|
1151
1138
|
const instance = useLookup().get(Type);
|
|
1152
1139
|
if (!instance && arg !== false)
|
|
1153
1140
|
throw Oops$4.NotFound(Type.name);
|
|
@@ -1155,7 +1142,7 @@ function useLocal(Type, arg) {
|
|
|
1155
1142
|
case "string":
|
|
1156
1143
|
return instance[arg];
|
|
1157
1144
|
case "function":
|
|
1158
|
-
|
|
1145
|
+
React__default["default"].useLayoutEffect(() => arg(instance), []);
|
|
1159
1146
|
default:
|
|
1160
1147
|
return instance;
|
|
1161
1148
|
}
|
|
@@ -1168,7 +1155,7 @@ const Oops$3 = issues({
|
|
|
1168
1155
|
const PendingContext = new WeakMap();
|
|
1169
1156
|
const ContextWasUsed = new WeakMap();
|
|
1170
1157
|
function tap(type, argument) {
|
|
1171
|
-
return
|
|
1158
|
+
return add(function tap(key) {
|
|
1172
1159
|
if ("set" in type)
|
|
1173
1160
|
return {
|
|
1174
1161
|
recursive: true,
|
|
@@ -1190,7 +1177,7 @@ function tap(type, argument) {
|
|
|
1190
1177
|
});
|
|
1191
1178
|
return {
|
|
1192
1179
|
recursive: true,
|
|
1193
|
-
|
|
1180
|
+
suspend: true
|
|
1194
1181
|
};
|
|
1195
1182
|
});
|
|
1196
1183
|
}
|
|
@@ -1225,57 +1212,51 @@ function use(init) {
|
|
|
1225
1212
|
|
|
1226
1213
|
function useModel(source, arg, arg2) {
|
|
1227
1214
|
const instance = React__default["default"].useMemo(() => {
|
|
1228
|
-
const
|
|
1229
|
-
const instance =
|
|
1230
|
-
source.
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
if (typeof cb == "function")
|
|
1236
|
-
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);
|
|
1237
1222
|
return instance;
|
|
1238
1223
|
}, []);
|
|
1224
|
+
usePeerContext(instance);
|
|
1239
1225
|
if (Array.isArray(arg)) {
|
|
1240
1226
|
const update = React.useState(0)[1];
|
|
1241
1227
|
React__default["default"].useLayoutEffect(() => {
|
|
1242
1228
|
if (arg.length && instance instanceof Model)
|
|
1243
|
-
instance.on(arg, () => update(x => x + 1)
|
|
1229
|
+
instance.on(arg, () => update(x => x + 1));
|
|
1244
1230
|
return () => {
|
|
1245
1231
|
if (Model.isTypeof(source))
|
|
1246
|
-
instance.
|
|
1232
|
+
instance.kill();
|
|
1247
1233
|
};
|
|
1248
1234
|
}, []);
|
|
1249
1235
|
return instance;
|
|
1250
1236
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
instance[key] = arg[key];
|
|
1260
|
-
React__default["default"].useLayoutEffect(() => {
|
|
1261
|
-
local.active = true;
|
|
1262
|
-
});
|
|
1263
|
-
}
|
|
1264
|
-
React__default["default"].useLayoutEffect(() => {
|
|
1265
|
-
local.commit();
|
|
1266
|
-
return () => {
|
|
1267
|
-
local.release();
|
|
1268
|
-
if (Model.isTypeof(source))
|
|
1269
|
-
instance.destroy();
|
|
1270
|
-
};
|
|
1271
|
-
}, []);
|
|
1272
|
-
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];
|
|
1273
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;
|
|
1274
1255
|
}
|
|
1275
1256
|
|
|
1276
1257
|
function useFrom(target, compute, suspend) {
|
|
1277
1258
|
const local = use(refresh => {
|
|
1278
|
-
const sub =
|
|
1259
|
+
const sub = Control.for(target).subscribe(() => update);
|
|
1279
1260
|
const spy = sub.proxy;
|
|
1280
1261
|
let value = compute.call(spy, spy);
|
|
1281
1262
|
let update;
|
|
@@ -1314,84 +1295,79 @@ function useFrom(target, compute, suspend) {
|
|
|
1314
1295
|
return local.proxy;
|
|
1315
1296
|
}
|
|
1316
1297
|
|
|
1317
|
-
function useTap(source,
|
|
1298
|
+
function useTap(source, arg1, arg2) {
|
|
1318
1299
|
const instance = React.useMemo(() => {
|
|
1319
1300
|
if (typeof source == "object")
|
|
1320
1301
|
return () => source;
|
|
1321
|
-
if (
|
|
1322
|
-
return () => source.get();
|
|
1323
|
-
if (
|
|
1324
|
-
return () =>
|
|
1325
|
-
|
|
1326
|
-
return source;
|
|
1302
|
+
if (MVC.isTypeof(source))
|
|
1303
|
+
return () => source.get(true);
|
|
1304
|
+
if (Model.isTypeof(source))
|
|
1305
|
+
return () => useContext(source);
|
|
1306
|
+
return source;
|
|
1327
1307
|
}, [])();
|
|
1328
|
-
if (typeof
|
|
1329
|
-
return useFrom(instance,
|
|
1330
|
-
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)));
|
|
1331
1311
|
React__default["default"].useLayoutEffect(local.commit, []);
|
|
1332
|
-
if (
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
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;
|
|
1339
1318
|
}
|
|
1340
1319
|
|
|
1341
|
-
/* istanbul ignore file */
|
|
1342
1320
|
class MVC extends Model {
|
|
1343
|
-
tap(path, expect) {
|
|
1344
|
-
return useTap(this, path, expect);
|
|
1345
|
-
}
|
|
1346
|
-
use(arg, callback) {
|
|
1347
|
-
return useModel(this, arg, callback);
|
|
1348
|
-
}
|
|
1349
1321
|
static get(arg) {
|
|
1350
|
-
return
|
|
1322
|
+
return useContext(this, arg);
|
|
1351
1323
|
}
|
|
1352
1324
|
static tap(key, expect) {
|
|
1353
1325
|
return useTap(this, key, expect);
|
|
1354
1326
|
}
|
|
1355
|
-
static new(arg) {
|
|
1356
|
-
const instance = React__default["default"].useMemo(() => {
|
|
1357
|
-
const instance = new this();
|
|
1358
|
-
ensure(instance);
|
|
1359
|
-
if (typeof arg == "function")
|
|
1360
|
-
arg(instance);
|
|
1361
|
-
else if (arg)
|
|
1362
|
-
getOwnPropertyNames(instance).forEach(key => {
|
|
1363
|
-
if (key in arg)
|
|
1364
|
-
instance[key] = arg[key];
|
|
1365
|
-
});
|
|
1366
|
-
return instance;
|
|
1367
|
-
}, []);
|
|
1368
|
-
usePeerContext(instance.is);
|
|
1369
|
-
return instance;
|
|
1370
|
-
}
|
|
1371
1327
|
static use(a, b) {
|
|
1372
|
-
|
|
1373
|
-
usePeerContext(instance.is);
|
|
1374
|
-
return instance;
|
|
1375
|
-
}
|
|
1376
|
-
/**
|
|
1377
|
-
* @deprecated consider doing this manually - not worth minor efficiency gain over using.
|
|
1378
|
-
*/
|
|
1379
|
-
static uses(apply, keys) {
|
|
1380
|
-
return this.use(instance => {
|
|
1381
|
-
instance.import(apply, keys);
|
|
1382
|
-
});
|
|
1383
|
-
}
|
|
1384
|
-
/**
|
|
1385
|
-
* @deprecated is now replaced by overload of `use` method.
|
|
1386
|
-
*/
|
|
1387
|
-
static using(apply, keys) {
|
|
1388
|
-
return this.use(apply, keys);
|
|
1328
|
+
return useModel(this, a, b);
|
|
1389
1329
|
}
|
|
1390
1330
|
static meta(path, expect) {
|
|
1391
1331
|
return useTap(() => this, path, expect);
|
|
1392
1332
|
}
|
|
1393
1333
|
}
|
|
1394
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
|
+
|
|
1395
1371
|
const Oops$2 = issues({
|
|
1396
1372
|
BadProps: () => `Provider expects either a render function, 'get' or 'has' props.`
|
|
1397
1373
|
});
|
|
@@ -1401,66 +1377,66 @@ function Consumer(props) {
|
|
|
1401
1377
|
return children(useTap(type));
|
|
1402
1378
|
const callback = has || get;
|
|
1403
1379
|
if (typeof callback == "function")
|
|
1404
|
-
callback(
|
|
1380
|
+
callback(useContext(type, !!has));
|
|
1405
1381
|
else
|
|
1406
1382
|
throw Oops$2.BadProps();
|
|
1407
1383
|
return null;
|
|
1408
1384
|
}
|
|
1409
1385
|
|
|
1410
1386
|
const Oops$1 = issues({
|
|
1411
|
-
NoType: () => "Provider '
|
|
1412
|
-
PropDeprecated: () => "Provider `of` prop is deprecated, use `for` instead."
|
|
1387
|
+
NoType: () => "Provider 'for' prop must be Model, typeof Model or a collection of them."
|
|
1413
1388
|
});
|
|
1414
1389
|
function Provider(props) {
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1390
|
+
const context = useNewContext(props.for);
|
|
1391
|
+
const render = props.children;
|
|
1392
|
+
useAppliedProps(context, props);
|
|
1393
|
+
React__default["default"].useLayoutEffect(() => () => context.pop(), []);
|
|
1394
|
+
return React__default["default"].createElement(LookupContext.Provider, { value: context }, typeof render == "function"
|
|
1395
|
+
? React__default["default"].createElement(RenderFunction, { context, render })
|
|
1396
|
+
: render);
|
|
1397
|
+
}
|
|
1398
|
+
function useNewContext(include) {
|
|
1399
|
+
const ambient = useLookup();
|
|
1400
|
+
return React__default["default"].useMemo(() => {
|
|
1401
|
+
if (!include)
|
|
1421
1402
|
throw Oops$1.NoType();
|
|
1422
|
-
const layer =
|
|
1403
|
+
const layer = ambient.push();
|
|
1423
1404
|
function register(I) {
|
|
1424
1405
|
if (I instanceof Model)
|
|
1425
1406
|
layer.inject(I.constructor, I, false);
|
|
1426
1407
|
else
|
|
1427
|
-
layer.inject(I, I.
|
|
1408
|
+
layer.inject(I, I.new(), true);
|
|
1428
1409
|
}
|
|
1429
|
-
if (
|
|
1430
|
-
register(
|
|
1410
|
+
if (include instanceof Model || typeof include == "function")
|
|
1411
|
+
register(include);
|
|
1431
1412
|
else
|
|
1432
|
-
values(
|
|
1413
|
+
values(include).forEach(register);
|
|
1433
1414
|
for (const instance of layer.local)
|
|
1434
1415
|
if (instance)
|
|
1435
1416
|
for (const apply of getPending(instance))
|
|
1436
1417
|
apply(layer);
|
|
1437
1418
|
return layer;
|
|
1438
1419
|
}, []);
|
|
1439
|
-
const render = props.children;
|
|
1440
|
-
useAppliedProps(context, props);
|
|
1441
|
-
React__default["default"].useLayoutEffect(() => () => context.pop(), []);
|
|
1442
|
-
return React__default["default"].createElement(LookupContext.Provider, { value: context }, typeof render == "function"
|
|
1443
|
-
? React__default["default"].createElement(RenderFunction, { context, render })
|
|
1444
|
-
: render);
|
|
1445
1420
|
}
|
|
1446
|
-
function useAppliedProps(
|
|
1447
|
-
const
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
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
|
+
});
|
|
1455
1431
|
};
|
|
1456
1432
|
}, []);
|
|
1457
|
-
|
|
1433
|
+
apply(props);
|
|
1458
1434
|
}
|
|
1459
1435
|
function RenderFunction(props) {
|
|
1460
1436
|
const hook = use(refresh => {
|
|
1461
1437
|
const targets = props.context.local;
|
|
1462
1438
|
if (targets.length == 1)
|
|
1463
|
-
return
|
|
1439
|
+
return Control.for(targets[0]).subscribe(() => refresh);
|
|
1464
1440
|
return {};
|
|
1465
1441
|
});
|
|
1466
1442
|
if (hook.commit)
|
|
@@ -1469,17 +1445,31 @@ function RenderFunction(props) {
|
|
|
1469
1445
|
}
|
|
1470
1446
|
|
|
1471
1447
|
const Oops = issues({
|
|
1472
|
-
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.`,
|
|
1473
1449
|
AlreadyExists: (name) => `Shared instance of ${name} already exists! Consider unmounting existing, or use ${name}.reset() to force-delete it.`
|
|
1474
1450
|
});
|
|
1475
1451
|
const Active = new WeakMap();
|
|
1476
1452
|
class Global extends MVC {
|
|
1477
|
-
|
|
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) {
|
|
1478
1466
|
if (Active.has(this))
|
|
1479
|
-
|
|
1467
|
+
if (this.keepAlive)
|
|
1468
|
+
return Active.get(this);
|
|
1469
|
+
else
|
|
1470
|
+
throw Oops.AlreadyExists(this.name);
|
|
1480
1471
|
const instance = new this(...args);
|
|
1481
|
-
|
|
1482
|
-
Active.set(this, instance);
|
|
1472
|
+
Control.for(instance);
|
|
1483
1473
|
return instance;
|
|
1484
1474
|
}
|
|
1485
1475
|
/**
|
|
@@ -1487,10 +1477,8 @@ class Global extends MVC {
|
|
|
1487
1477
|
* Returns a thenable; resolves after successful update.
|
|
1488
1478
|
* If instance does not already exist, one will be created.
|
|
1489
1479
|
*/
|
|
1490
|
-
static set(
|
|
1491
|
-
|
|
1492
|
-
instance.import(updates);
|
|
1493
|
-
return instance.update(false);
|
|
1480
|
+
static set(values) {
|
|
1481
|
+
return this.get(true).update(values);
|
|
1494
1482
|
}
|
|
1495
1483
|
static get(arg) {
|
|
1496
1484
|
const instance = Active.get(this);
|
|
@@ -1504,24 +1492,19 @@ class Global extends MVC {
|
|
|
1504
1492
|
static reset() {
|
|
1505
1493
|
const current = Active.get(this);
|
|
1506
1494
|
if (current)
|
|
1507
|
-
current.
|
|
1495
|
+
current.kill(true);
|
|
1508
1496
|
}
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
Active.delete(this.constructor);
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1497
|
+
}
|
|
1498
|
+
Global.keepAlive = true;
|
|
1514
1499
|
|
|
1515
|
-
exports.CONTROL = CONTROL;
|
|
1516
1500
|
exports.Consumer = Consumer;
|
|
1501
|
+
exports.Debug = Debug;
|
|
1517
1502
|
exports.Global = Global;
|
|
1518
|
-
exports.
|
|
1503
|
+
exports.MVC = MVC;
|
|
1519
1504
|
exports.Model = MVC;
|
|
1520
1505
|
exports.Provider = Provider;
|
|
1521
|
-
exports.STATE = STATE;
|
|
1522
|
-
exports.WHY = WHY;
|
|
1523
1506
|
exports.act = act;
|
|
1524
|
-
exports.
|
|
1507
|
+
exports.add = add;
|
|
1525
1508
|
exports["default"] = MVC;
|
|
1526
1509
|
exports.get = get;
|
|
1527
1510
|
exports.has = has;
|
|
@@ -1529,8 +1512,8 @@ exports.ref = ref;
|
|
|
1529
1512
|
exports.set = set;
|
|
1530
1513
|
exports.tap = tap;
|
|
1531
1514
|
exports.use = use$1;
|
|
1515
|
+
exports.useContext = useContext;
|
|
1532
1516
|
exports.useFrom = useFrom;
|
|
1533
|
-
exports.useLocal = useLocal;
|
|
1534
1517
|
exports.useModel = useModel;
|
|
1535
1518
|
exports.useTap = useTap;
|
|
1536
1519
|
//# sourceMappingURL=index.js.map
|