@expressive/mvc 0.7.2 → 0.8.1
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 +140 -168
- package/lib/index.esm.js +482 -498
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +484 -502
- 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,266 +188,28 @@ 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
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
198
|
+
this.watch = new Map();
|
|
199
|
+
this.commit = () => {
|
|
200
|
+
const release = this.parent.addListener(this.event);
|
|
201
|
+
this.active = true;
|
|
202
|
+
this.dependant.forEach(x => x.commit());
|
|
203
|
+
return this.release = () => {
|
|
204
|
+
this.dependant.forEach(x => x.release());
|
|
205
|
+
release();
|
|
206
|
+
};
|
|
403
207
|
};
|
|
404
|
-
|
|
405
|
-
defineProperty(proxy, LOCAL, { value: this });
|
|
406
|
-
defineProperty(this, "proxy", {
|
|
407
|
-
configurable: true,
|
|
408
|
-
get() {
|
|
409
|
-
if (UPDATE.has(proxy))
|
|
410
|
-
setTimeout(() => UPDATE.delete(proxy), 0);
|
|
411
|
-
return proxy;
|
|
412
|
-
}
|
|
413
|
-
});
|
|
414
|
-
const release = parent.addListener(key => {
|
|
208
|
+
this.event = (key) => {
|
|
415
209
|
if (!this.active)
|
|
416
210
|
return;
|
|
417
|
-
const
|
|
211
|
+
const { parent, watch } = this;
|
|
212
|
+
const handler = watch.get(key);
|
|
418
213
|
let notify;
|
|
419
214
|
if (typeof handler == "function") {
|
|
420
215
|
const callback = handler();
|
|
@@ -425,41 +220,59 @@ class Subscriber {
|
|
|
425
220
|
notify = this.onUpdate(key, parent);
|
|
426
221
|
if (!notify)
|
|
427
222
|
return;
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
};
|
|
431
|
-
parent.waiting.add(notate);
|
|
223
|
+
parent.waiting.add(() => {
|
|
224
|
+
this.latest = parent.latest.filter(k => watch.has(k));
|
|
225
|
+
});
|
|
432
226
|
parent.waiting.add(notify);
|
|
433
|
-
});
|
|
434
|
-
this.commit = () => {
|
|
435
|
-
this.active = true;
|
|
436
|
-
this.dependant.forEach(x => x.commit());
|
|
437
|
-
return this.release;
|
|
438
|
-
};
|
|
439
|
-
this.release = () => {
|
|
440
|
-
this.dependant.forEach(x => x.release());
|
|
441
|
-
release();
|
|
442
227
|
};
|
|
228
|
+
const proxy = create(parent.subject);
|
|
229
|
+
const reset = () => this.latest = undefined;
|
|
230
|
+
REGISTER$1.set(proxy, this);
|
|
231
|
+
defineProperty(this, "proxy", {
|
|
232
|
+
configurable: true,
|
|
233
|
+
get() {
|
|
234
|
+
setTimeout(reset, 0);
|
|
235
|
+
return proxy;
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
static get(from) {
|
|
240
|
+
return REGISTER$1.get(from);
|
|
241
|
+
}
|
|
242
|
+
get using() {
|
|
243
|
+
return Array.from(this.watch.keys());
|
|
244
|
+
}
|
|
245
|
+
assign(apply, keys) {
|
|
246
|
+
const { waiting, subject } = this.parent;
|
|
247
|
+
this.active = false;
|
|
248
|
+
if (typeof keys !== "object")
|
|
249
|
+
keys = getOwnPropertyNames(subject);
|
|
250
|
+
for (const key of keys)
|
|
251
|
+
if (key in apply)
|
|
252
|
+
subject[key] = apply[key];
|
|
253
|
+
waiting.add(() => this.active = true);
|
|
254
|
+
}
|
|
255
|
+
add(key, value) {
|
|
256
|
+
if (value !== undefined)
|
|
257
|
+
this.watch.set(key, value);
|
|
258
|
+
else if (!this.watch.has(key))
|
|
259
|
+
this.watch.set(key, true);
|
|
443
260
|
}
|
|
444
261
|
}
|
|
445
262
|
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
const CONTROL = Symbol("CONTROL");
|
|
449
|
-
function getUpdate(subject) {
|
|
450
|
-
return UPDATE.get(subject);
|
|
451
|
-
}
|
|
452
|
-
class Controller {
|
|
263
|
+
const REGISTER = new WeakMap();
|
|
264
|
+
class Control {
|
|
453
265
|
constructor(subject) {
|
|
454
266
|
this.subject = subject;
|
|
455
267
|
this.frame = new Set();
|
|
456
268
|
this.waiting = new Set();
|
|
457
269
|
this.followers = new Set();
|
|
270
|
+
REGISTER.set(subject, this);
|
|
458
271
|
}
|
|
459
272
|
subscribe(callback) {
|
|
460
273
|
return new Subscriber(this, callback);
|
|
461
274
|
}
|
|
462
|
-
|
|
275
|
+
watch(key) {
|
|
463
276
|
const { subject, state } = this;
|
|
464
277
|
const { value } = getOwnPropertyDescriptor(subject, key);
|
|
465
278
|
if (typeof value == "function")
|
|
@@ -478,9 +291,9 @@ class Controller {
|
|
|
478
291
|
enumerable: false,
|
|
479
292
|
set: this.ref(key),
|
|
480
293
|
get() {
|
|
481
|
-
const
|
|
482
|
-
if (
|
|
483
|
-
|
|
294
|
+
const sub = Subscriber.get(this);
|
|
295
|
+
if (sub)
|
|
296
|
+
sub.add(key);
|
|
484
297
|
return state.get(key);
|
|
485
298
|
}
|
|
486
299
|
});
|
|
@@ -508,16 +321,14 @@ class Controller {
|
|
|
508
321
|
};
|
|
509
322
|
}
|
|
510
323
|
update(key) {
|
|
511
|
-
const { followers, frame,
|
|
324
|
+
const { followers, frame, waiting } = this;
|
|
512
325
|
if (frame.has(key))
|
|
513
326
|
return;
|
|
514
327
|
else if (!frame.size)
|
|
515
328
|
setTimeout(() => {
|
|
516
329
|
flush(this);
|
|
517
|
-
|
|
518
|
-
setTimeout(() =>
|
|
519
|
-
UPDATE.delete(subject);
|
|
520
|
-
}, 0);
|
|
330
|
+
this.latest = Array.from(frame);
|
|
331
|
+
setTimeout(() => this.latest = undefined, 0);
|
|
521
332
|
frame.clear();
|
|
522
333
|
for (const notify of waiting)
|
|
523
334
|
try {
|
|
@@ -540,41 +351,44 @@ 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
|
-
|
|
573
|
-
|
|
382
|
+
/**
|
|
383
|
+
* Run instruction as controller sets itself up.
|
|
384
|
+
* This will specialize the behavior of a given property.
|
|
385
|
+
*/
|
|
386
|
+
function add(instruction, label) {
|
|
387
|
+
const name = label || instruction.name || "pending";
|
|
574
388
|
const placeholder = Symbol(`${name} instruction`);
|
|
575
389
|
function setup(key) {
|
|
576
390
|
const { subject, state } = this;
|
|
577
|
-
let output =
|
|
391
|
+
let output = instruction.call(this, key, this);
|
|
578
392
|
if (typeof output == "function")
|
|
579
393
|
output = { get: output };
|
|
580
394
|
if (typeof output != "object")
|
|
@@ -583,7 +397,7 @@ function apply(fn, label) {
|
|
|
583
397
|
defineProperty(subject, key, output);
|
|
584
398
|
return false;
|
|
585
399
|
}
|
|
586
|
-
let { get: onGet, set: onSet,
|
|
400
|
+
let { get: onGet, set: onSet, suspend: shouldSuspend } = output;
|
|
587
401
|
if ("value" in output)
|
|
588
402
|
state.set(key, output.value);
|
|
589
403
|
if ("recursive" in output && output.recursive)
|
|
@@ -603,12 +417,11 @@ function apply(fn, label) {
|
|
|
603
417
|
? this.ref(key, onSet)
|
|
604
418
|
: undefined,
|
|
605
419
|
get() {
|
|
606
|
-
if (!state.has(key) &&
|
|
420
|
+
if (!state.has(key) && shouldSuspend)
|
|
607
421
|
throw suspend(control, key);
|
|
608
|
-
const
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
listen(key);
|
|
422
|
+
const local = Subscriber.get(this);
|
|
423
|
+
if (local)
|
|
424
|
+
local.add(key);
|
|
612
425
|
return onGet
|
|
613
426
|
? onGet(local)
|
|
614
427
|
: state.get(key);
|
|
@@ -629,8 +442,8 @@ function getRecursive(key, from) {
|
|
|
629
442
|
if (reset)
|
|
630
443
|
reset();
|
|
631
444
|
const value = from.state.get(key);
|
|
632
|
-
if (value
|
|
633
|
-
const child =
|
|
445
|
+
if (Control.get(value)) {
|
|
446
|
+
const child = Control.for(value).subscribe(local.onUpdate);
|
|
634
447
|
if (local.active)
|
|
635
448
|
child.commit();
|
|
636
449
|
local.dependant.add(child);
|
|
@@ -676,15 +489,15 @@ function __awaiter(thisArg, _arguments, P, generator) {
|
|
|
676
489
|
});
|
|
677
490
|
}
|
|
678
491
|
|
|
679
|
-
const Oops$
|
|
492
|
+
const Oops$9 = issues({
|
|
680
493
|
DuplicatePending: (key) => `Invoked action ${key} but one is already active.`
|
|
681
494
|
});
|
|
682
495
|
function act(task) {
|
|
683
|
-
return
|
|
496
|
+
return add(function act(key) {
|
|
684
497
|
let pending = false;
|
|
685
498
|
const invoke = (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
686
499
|
if (pending)
|
|
687
|
-
return Promise.reject(Oops$
|
|
500
|
+
return Promise.reject(Oops$9.DuplicatePending(key));
|
|
688
501
|
pending = true;
|
|
689
502
|
this.update(key);
|
|
690
503
|
try {
|
|
@@ -727,8 +540,8 @@ function factoryMode(self, output, key, required) {
|
|
|
727
540
|
const suspend = () => {
|
|
728
541
|
if (required === false)
|
|
729
542
|
return undefined;
|
|
730
|
-
const issue = Oops$
|
|
731
|
-
|
|
543
|
+
const issue = Oops$8.NotReady(subject, key);
|
|
544
|
+
assign(pending, {
|
|
732
545
|
message: issue.message,
|
|
733
546
|
stack: issue.stack
|
|
734
547
|
});
|
|
@@ -744,7 +557,7 @@ function factoryMode(self, output, key, required) {
|
|
|
744
557
|
};
|
|
745
558
|
}
|
|
746
559
|
|
|
747
|
-
const Oops$
|
|
560
|
+
const Oops$8 = issues({
|
|
748
561
|
// BadSource: (model, property, got) =>
|
|
749
562
|
// `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
|
|
750
563
|
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 +567,14 @@ const Oops$7 = issues({
|
|
|
754
567
|
Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`
|
|
755
568
|
});
|
|
756
569
|
function get(arg0, arg1, arg2) {
|
|
757
|
-
return
|
|
570
|
+
return add(function get(key) {
|
|
758
571
|
const { subject } = this;
|
|
759
572
|
const computeRequired = arg1 === true || arg2 === true;
|
|
760
573
|
const factoryRequired = arg1 !== false && arg2 !== false;
|
|
761
574
|
let getter;
|
|
762
575
|
// Easy mistake, using a peer, will always be unresolved.
|
|
763
576
|
if (typeof arg0 == "symbol")
|
|
764
|
-
throw Oops$
|
|
577
|
+
throw Oops$8.PeerNotAllowed(subject, key);
|
|
765
578
|
// if(typeof arg0 == "function" && /^[A-Z]/.test(arg0.name))
|
|
766
579
|
// throw Oops.BadSource(subject, key, arg0);
|
|
767
580
|
this.state.set(key, undefined);
|
|
@@ -783,7 +596,7 @@ function get(arg0, arg1, arg2) {
|
|
|
783
596
|
}
|
|
784
597
|
else if (typeof arg1 == "function") {
|
|
785
598
|
// replace source controller in-case it is different
|
|
786
|
-
source =
|
|
599
|
+
source = Control.for(arg0);
|
|
787
600
|
setter = arg1;
|
|
788
601
|
}
|
|
789
602
|
else
|
|
@@ -795,7 +608,7 @@ function get(arg0, arg1, arg2) {
|
|
|
795
608
|
init();
|
|
796
609
|
}
|
|
797
610
|
catch (err) {
|
|
798
|
-
Oops$
|
|
611
|
+
Oops$8.ComputeFailed(subject, key).warn();
|
|
799
612
|
throw err;
|
|
800
613
|
}
|
|
801
614
|
return () => {
|
|
@@ -807,12 +620,12 @@ function get(arg0, arg1, arg2) {
|
|
|
807
620
|
}
|
|
808
621
|
|
|
809
622
|
function ref(arg, mapper) {
|
|
810
|
-
return
|
|
623
|
+
return add(function ref(key) {
|
|
811
624
|
let value = {};
|
|
812
625
|
if (typeof arg != "object")
|
|
813
626
|
value = createRef(this, key, arg);
|
|
814
627
|
else
|
|
815
|
-
|
|
628
|
+
Control.for(arg).state.forEach((_val, key) => {
|
|
816
629
|
defineProperty(value, key, mapper ? {
|
|
817
630
|
configurable: true,
|
|
818
631
|
get() {
|
|
@@ -841,11 +654,11 @@ function set(value, argument) {
|
|
|
841
654
|
argument = value;
|
|
842
655
|
value = undefined;
|
|
843
656
|
}
|
|
844
|
-
return
|
|
657
|
+
return add(function set(key) {
|
|
845
658
|
if (value !== undefined)
|
|
846
659
|
this.state.set(key, value);
|
|
847
660
|
return {
|
|
848
|
-
|
|
661
|
+
suspend: true,
|
|
849
662
|
set: typeof argument == "function"
|
|
850
663
|
? createValueEffect(argument)
|
|
851
664
|
: undefined
|
|
@@ -1000,12 +813,12 @@ function createProxy(emit, watch) {
|
|
|
1000
813
|
}
|
|
1001
814
|
|
|
1002
815
|
const Parent = new WeakMap();
|
|
1003
|
-
const Oops$
|
|
816
|
+
const Oops$7 = issues({
|
|
1004
817
|
BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`,
|
|
1005
818
|
NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
|
|
1006
819
|
});
|
|
1007
820
|
function use$1(input, argument) {
|
|
1008
|
-
return
|
|
821
|
+
return add(function use(key) {
|
|
1009
822
|
const { state, subject } = this;
|
|
1010
823
|
const required = argument !== false;
|
|
1011
824
|
let pending;
|
|
@@ -1017,7 +830,7 @@ function use$1(input, argument) {
|
|
|
1017
830
|
input = new input();
|
|
1018
831
|
}
|
|
1019
832
|
else if (input && typeof input !== "object")
|
|
1020
|
-
throw Oops$
|
|
833
|
+
throw Oops$7.BadArgument(typeof input);
|
|
1021
834
|
if (input instanceof Map || input instanceof Set)
|
|
1022
835
|
return keyed(this, key, input);
|
|
1023
836
|
const onUpdate = (next) => {
|
|
@@ -1025,7 +838,7 @@ function use$1(input, argument) {
|
|
|
1025
838
|
if (next) {
|
|
1026
839
|
get = getRecursive(key, this);
|
|
1027
840
|
Parent.set(next, subject);
|
|
1028
|
-
|
|
841
|
+
Control.for(next);
|
|
1029
842
|
}
|
|
1030
843
|
if (typeof argument == "function")
|
|
1031
844
|
argument(next);
|
|
@@ -1034,8 +847,8 @@ function use$1(input, argument) {
|
|
|
1034
847
|
const suspend = () => {
|
|
1035
848
|
if (required === false)
|
|
1036
849
|
return;
|
|
1037
|
-
const issue = Oops$
|
|
1038
|
-
|
|
850
|
+
const issue = Oops$7.NotReady(subject, key);
|
|
851
|
+
assign(pending, {
|
|
1039
852
|
message: issue.message,
|
|
1040
853
|
stack: issue.stack
|
|
1041
854
|
});
|
|
@@ -1079,25 +892,204 @@ function use$1(input, argument) {
|
|
|
1079
892
|
});
|
|
1080
893
|
}
|
|
1081
894
|
|
|
1082
|
-
const Oops$
|
|
895
|
+
const Oops$6 = issues({
|
|
1083
896
|
Required: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
|
|
1084
897
|
Unexpected: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
|
|
1085
898
|
});
|
|
1086
|
-
function has(
|
|
1087
|
-
return
|
|
899
|
+
function has(Type, required) {
|
|
900
|
+
return add(function parent() {
|
|
1088
901
|
const child = this.subject;
|
|
1089
902
|
const value = Parent.get(child);
|
|
1090
|
-
const expected =
|
|
903
|
+
const expected = Type.name;
|
|
1091
904
|
if (!value) {
|
|
1092
905
|
if (required !== false)
|
|
1093
|
-
throw Oops$
|
|
906
|
+
throw Oops$6.Required(expected, child);
|
|
1094
907
|
}
|
|
1095
|
-
else if (!(value instanceof
|
|
1096
|
-
throw Oops$
|
|
908
|
+
else if (!(value instanceof Type))
|
|
909
|
+
throw Oops$6.Unexpected(expected, child, value);
|
|
1097
910
|
return { value };
|
|
1098
911
|
});
|
|
1099
912
|
}
|
|
1100
913
|
|
|
914
|
+
const Oops$5 = issues({
|
|
915
|
+
Timeout: (keys, timeout) => `No update for [${keys}] within ${timeout}.`,
|
|
916
|
+
StrictUpdate: () => `Strict update() did not find pending updates.`,
|
|
917
|
+
NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
|
|
918
|
+
});
|
|
919
|
+
class Model {
|
|
920
|
+
constructor() {
|
|
921
|
+
new Control(this);
|
|
922
|
+
defineProperty(this, "is", { value: this });
|
|
923
|
+
}
|
|
924
|
+
on(select, argument, once) {
|
|
925
|
+
if (typeof select == "function")
|
|
926
|
+
return Control.for(this, x => x.addListener(select));
|
|
927
|
+
const single = typeof select == "string";
|
|
928
|
+
let keys = single ? [select] : select;
|
|
929
|
+
if (typeof argument !== "function")
|
|
930
|
+
return new Promise((resolve, reject) => {
|
|
931
|
+
const removeListener = Control.for(this, control => {
|
|
932
|
+
for (const key of keys)
|
|
933
|
+
try {
|
|
934
|
+
void this[key];
|
|
935
|
+
}
|
|
936
|
+
catch (e) { }
|
|
937
|
+
return control.addListener(key => {
|
|
938
|
+
if (!key) {
|
|
939
|
+
removeListener();
|
|
940
|
+
reject(Oops$5.Timeout(select, `lifetime of ${this}`));
|
|
941
|
+
}
|
|
942
|
+
else if (keys.includes(key)) {
|
|
943
|
+
removeListener();
|
|
944
|
+
return () => resolve(single ? control.state.get(key) : control.latest);
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
});
|
|
948
|
+
if (typeof argument == "number")
|
|
949
|
+
setTimeout(() => {
|
|
950
|
+
removeListener();
|
|
951
|
+
reject(Oops$5.Timeout(select, `${argument}ms`));
|
|
952
|
+
}, argument);
|
|
953
|
+
});
|
|
954
|
+
return Control.for(this, control => {
|
|
955
|
+
if (!keys.length)
|
|
956
|
+
keys = [...control.state.keys()];
|
|
957
|
+
for (const key of keys)
|
|
958
|
+
try {
|
|
959
|
+
void this[key];
|
|
960
|
+
}
|
|
961
|
+
catch (e) { }
|
|
962
|
+
const callback = single
|
|
963
|
+
? () => { argument.call(this, control.state.get(select), select); }
|
|
964
|
+
: () => { argument.call(this, control.latest); };
|
|
965
|
+
const onEvent = once
|
|
966
|
+
? () => {
|
|
967
|
+
removeListener();
|
|
968
|
+
callback();
|
|
969
|
+
}
|
|
970
|
+
: callback;
|
|
971
|
+
const removeListener = control.addListener(key => {
|
|
972
|
+
if (keys.includes(key))
|
|
973
|
+
return onEvent;
|
|
974
|
+
});
|
|
975
|
+
return removeListener;
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
effect(callback, select) {
|
|
979
|
+
const effect = createEffect(callback);
|
|
980
|
+
return Control.for(this, control => {
|
|
981
|
+
let busy = false;
|
|
982
|
+
let inject = this.is;
|
|
983
|
+
const invoke = () => {
|
|
984
|
+
if (busy)
|
|
985
|
+
return;
|
|
986
|
+
const output = mayRetry(() => {
|
|
987
|
+
effect.call(inject, inject);
|
|
988
|
+
});
|
|
989
|
+
if (output instanceof Promise) {
|
|
990
|
+
output.finally(() => busy = false);
|
|
991
|
+
busy = true;
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
if (!select) {
|
|
995
|
+
const sub = control.subscribe(() => invoke);
|
|
996
|
+
inject = sub.proxy;
|
|
997
|
+
invoke();
|
|
998
|
+
return sub.commit();
|
|
999
|
+
}
|
|
1000
|
+
invoke();
|
|
1001
|
+
return control.addListener(key => {
|
|
1002
|
+
if (key === null) {
|
|
1003
|
+
if (!select.length)
|
|
1004
|
+
invoke();
|
|
1005
|
+
}
|
|
1006
|
+
else if (select.includes(key))
|
|
1007
|
+
return invoke;
|
|
1008
|
+
});
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
export(subset) {
|
|
1012
|
+
const { state } = Control.for(this);
|
|
1013
|
+
const output = {};
|
|
1014
|
+
const keys = subset || state.keys();
|
|
1015
|
+
for (const key of keys)
|
|
1016
|
+
output[key] = state.get(key);
|
|
1017
|
+
return output;
|
|
1018
|
+
}
|
|
1019
|
+
update(arg1, arg2) {
|
|
1020
|
+
const control = Control.for(this);
|
|
1021
|
+
const { frame, state, waiting } = control;
|
|
1022
|
+
if (arg1 === true && !frame.size)
|
|
1023
|
+
return Promise.reject(Oops$5.StrictUpdate());
|
|
1024
|
+
if (typeof arg1 == "object") {
|
|
1025
|
+
for (const key in arg1)
|
|
1026
|
+
if (arg2 === true || (arg2 ? arg2.includes(key) : state.has(key))) {
|
|
1027
|
+
state.set(key, arg1[key]);
|
|
1028
|
+
control.update(key);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
else if (typeof arg1 == "string") {
|
|
1032
|
+
control.update(arg1);
|
|
1033
|
+
if (1 in arguments && arg1 in this) {
|
|
1034
|
+
const method = this[arg1];
|
|
1035
|
+
if (typeof method == "function")
|
|
1036
|
+
if (typeof arg2 != "boolean")
|
|
1037
|
+
method.call(this, arg2);
|
|
1038
|
+
else if (arg2)
|
|
1039
|
+
method.call(this);
|
|
1040
|
+
}
|
|
1041
|
+
arg1 = undefined;
|
|
1042
|
+
}
|
|
1043
|
+
return {
|
|
1044
|
+
then: (callback) => {
|
|
1045
|
+
if (!callback)
|
|
1046
|
+
throw Oops$5.NoChaining();
|
|
1047
|
+
if (frame.size || arg1 !== false)
|
|
1048
|
+
waiting.add(() => {
|
|
1049
|
+
callback(control.latest);
|
|
1050
|
+
});
|
|
1051
|
+
else
|
|
1052
|
+
callback(false);
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Mark this instance for garbage-collection and send `willDestroy` event to all listeners.
|
|
1058
|
+
*/
|
|
1059
|
+
kill() {
|
|
1060
|
+
Control.for(this).clear();
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Creates a new instance of this controller.
|
|
1064
|
+
*
|
|
1065
|
+
* Beyond `new this(...)`, method will activate managed-state.
|
|
1066
|
+
*
|
|
1067
|
+
* @param args - arguments sent to constructor
|
|
1068
|
+
*/
|
|
1069
|
+
static new(...args) {
|
|
1070
|
+
const instance = new this(...args);
|
|
1071
|
+
Control.for(instance);
|
|
1072
|
+
return instance;
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Static equivalent of `x instanceof this`.
|
|
1076
|
+
*
|
|
1077
|
+
* Will determine if provided class is a subtype of this one.
|
|
1078
|
+
*/
|
|
1079
|
+
static isTypeof(maybe) {
|
|
1080
|
+
return (typeof maybe == "function" &&
|
|
1081
|
+
maybe.prototype instanceof this);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
defineProperty(Model.prototype, "toString", {
|
|
1085
|
+
value() {
|
|
1086
|
+
return this.constructor.name;
|
|
1087
|
+
}
|
|
1088
|
+
});
|
|
1089
|
+
|
|
1090
|
+
const Oops$4 = issues({
|
|
1091
|
+
NotFound: (name) => `Couldn't find ${name} in context; did you forget to use a Provider?`
|
|
1092
|
+
});
|
|
1101
1093
|
class Lookup {
|
|
1102
1094
|
constructor() {
|
|
1103
1095
|
this.table = new Map();
|
|
@@ -1134,20 +1126,19 @@ class Lookup {
|
|
|
1134
1126
|
} while (T !== Model);
|
|
1135
1127
|
}
|
|
1136
1128
|
pop() {
|
|
1129
|
+
const items = new Set();
|
|
1137
1130
|
for (const key of getOwnPropertySymbols(this)) {
|
|
1138
1131
|
const entry = getOwnPropertyDescriptor(this, key);
|
|
1139
1132
|
if (entry.writable && entry.value)
|
|
1140
|
-
entry.value
|
|
1133
|
+
items.add(entry.value);
|
|
1141
1134
|
}
|
|
1135
|
+
for (const model of items)
|
|
1136
|
+
model.kill();
|
|
1142
1137
|
}
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1138
|
+
}
|
|
1145
1139
|
const LookupContext = React__default["default"].createContext(new Lookup());
|
|
1146
1140
|
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) {
|
|
1141
|
+
function useContext(Type, arg) {
|
|
1151
1142
|
const instance = useLookup().get(Type);
|
|
1152
1143
|
if (!instance && arg !== false)
|
|
1153
1144
|
throw Oops$4.NotFound(Type.name);
|
|
@@ -1155,7 +1146,7 @@ function useLocal(Type, arg) {
|
|
|
1155
1146
|
case "string":
|
|
1156
1147
|
return instance[arg];
|
|
1157
1148
|
case "function":
|
|
1158
|
-
|
|
1149
|
+
React__default["default"].useLayoutEffect(() => arg(instance), []);
|
|
1159
1150
|
default:
|
|
1160
1151
|
return instance;
|
|
1161
1152
|
}
|
|
@@ -1168,7 +1159,7 @@ const Oops$3 = issues({
|
|
|
1168
1159
|
const PendingContext = new WeakMap();
|
|
1169
1160
|
const ContextWasUsed = new WeakMap();
|
|
1170
1161
|
function tap(type, argument) {
|
|
1171
|
-
return
|
|
1162
|
+
return add(function tap(key) {
|
|
1172
1163
|
if ("set" in type)
|
|
1173
1164
|
return {
|
|
1174
1165
|
recursive: true,
|
|
@@ -1190,7 +1181,7 @@ function tap(type, argument) {
|
|
|
1190
1181
|
});
|
|
1191
1182
|
return {
|
|
1192
1183
|
recursive: true,
|
|
1193
|
-
|
|
1184
|
+
suspend: true
|
|
1194
1185
|
};
|
|
1195
1186
|
});
|
|
1196
1187
|
}
|
|
@@ -1225,57 +1216,45 @@ function use(init) {
|
|
|
1225
1216
|
|
|
1226
1217
|
function useModel(source, arg, arg2) {
|
|
1227
1218
|
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);
|
|
1219
|
+
const callback = arg2 || arg;
|
|
1220
|
+
const instance = Model.isTypeof(source) ?
|
|
1221
|
+
source.new() :
|
|
1222
|
+
source();
|
|
1223
|
+
Control.for(instance);
|
|
1224
|
+
if (typeof callback == "function")
|
|
1225
|
+
callback(instance);
|
|
1237
1226
|
return instance;
|
|
1238
1227
|
}, []);
|
|
1228
|
+
usePeerContext(instance);
|
|
1239
1229
|
if (Array.isArray(arg)) {
|
|
1240
1230
|
const update = React.useState(0)[1];
|
|
1241
1231
|
React__default["default"].useLayoutEffect(() => {
|
|
1242
1232
|
if (arg.length && instance instanceof Model)
|
|
1243
|
-
instance.on(arg, () => update(x => x + 1)
|
|
1233
|
+
instance.on(arg, () => update(x => x + 1));
|
|
1244
1234
|
return () => {
|
|
1245
1235
|
if (Model.isTypeof(source))
|
|
1246
|
-
instance.
|
|
1236
|
+
instance.kill();
|
|
1247
1237
|
};
|
|
1248
1238
|
}, []);
|
|
1249
1239
|
return instance;
|
|
1250
1240
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
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;
|
|
1273
|
-
}
|
|
1241
|
+
const local = use(refresh => (Control.for(instance).subscribe(() => refresh)));
|
|
1242
|
+
if (typeof arg == "object")
|
|
1243
|
+
local.assign(arg, arg2);
|
|
1244
|
+
React__default["default"].useLayoutEffect(() => {
|
|
1245
|
+
local.commit();
|
|
1246
|
+
return () => {
|
|
1247
|
+
local.release();
|
|
1248
|
+
if (Model.isTypeof(source))
|
|
1249
|
+
instance.kill();
|
|
1250
|
+
};
|
|
1251
|
+
}, []);
|
|
1252
|
+
return local.proxy;
|
|
1274
1253
|
}
|
|
1275
1254
|
|
|
1276
1255
|
function useFrom(target, compute, suspend) {
|
|
1277
1256
|
const local = use(refresh => {
|
|
1278
|
-
const sub =
|
|
1257
|
+
const sub = Control.for(target).subscribe(() => update);
|
|
1279
1258
|
const spy = sub.proxy;
|
|
1280
1259
|
let value = compute.call(spy, spy);
|
|
1281
1260
|
let update;
|
|
@@ -1314,84 +1293,80 @@ function useFrom(target, compute, suspend) {
|
|
|
1314
1293
|
return local.proxy;
|
|
1315
1294
|
}
|
|
1316
1295
|
|
|
1317
|
-
function useTap(source,
|
|
1296
|
+
function useTap(source, arg1, arg2) {
|
|
1318
1297
|
const instance = React.useMemo(() => {
|
|
1319
1298
|
if (typeof source == "object")
|
|
1320
1299
|
return () => source;
|
|
1321
|
-
if (
|
|
1322
|
-
return () => source.get();
|
|
1323
|
-
if (
|
|
1324
|
-
return () =>
|
|
1325
|
-
|
|
1326
|
-
return source;
|
|
1300
|
+
if (MVC.isTypeof(source))
|
|
1301
|
+
return () => source.get(true);
|
|
1302
|
+
if (Model.isTypeof(source))
|
|
1303
|
+
return () => useContext(source);
|
|
1304
|
+
return source;
|
|
1327
1305
|
}, [])();
|
|
1328
|
-
if (typeof
|
|
1329
|
-
return useFrom(instance,
|
|
1330
|
-
const local = use(refresh => (
|
|
1306
|
+
if (typeof arg1 == "function")
|
|
1307
|
+
return useFrom(instance, arg1, arg2);
|
|
1308
|
+
const local = use(refresh => (Control.for(instance).subscribe(() => refresh)));
|
|
1331
1309
|
React__default["default"].useLayoutEffect(local.commit, []);
|
|
1332
|
-
if (
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
return local.proxy;
|
|
1310
|
+
if (arg1 === undefined)
|
|
1311
|
+
return local.proxy;
|
|
1312
|
+
const value = local.proxy[arg1];
|
|
1313
|
+
if (value === undefined && arg2)
|
|
1314
|
+
throw suspend(local.parent, arg1);
|
|
1315
|
+
return value;
|
|
1339
1316
|
}
|
|
1340
1317
|
|
|
1341
|
-
/* istanbul ignore file */
|
|
1342
1318
|
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
1319
|
static get(arg) {
|
|
1350
|
-
return
|
|
1320
|
+
return useContext(this, arg);
|
|
1351
1321
|
}
|
|
1352
1322
|
static tap(key, expect) {
|
|
1353
1323
|
return useTap(this, key, expect);
|
|
1354
1324
|
}
|
|
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
1325
|
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);
|
|
1326
|
+
return useModel(this, a, b);
|
|
1389
1327
|
}
|
|
1390
1328
|
static meta(path, expect) {
|
|
1391
1329
|
return useTap(() => this, path, expect);
|
|
1392
1330
|
}
|
|
1393
1331
|
}
|
|
1394
1332
|
|
|
1333
|
+
const LOCAL = "$debug local";
|
|
1334
|
+
const STATE = "$debug state";
|
|
1335
|
+
const UPDATE = "$debug update";
|
|
1336
|
+
const CONTROL = "$debug controller";
|
|
1337
|
+
Object.defineProperties(Model.prototype, {
|
|
1338
|
+
[CONTROL]: {
|
|
1339
|
+
get() {
|
|
1340
|
+
return Control.get(this);
|
|
1341
|
+
}
|
|
1342
|
+
},
|
|
1343
|
+
[LOCAL]: {
|
|
1344
|
+
get() {
|
|
1345
|
+
return Subscriber.get(this);
|
|
1346
|
+
}
|
|
1347
|
+
},
|
|
1348
|
+
[STATE]: {
|
|
1349
|
+
get() {
|
|
1350
|
+
const { state } = Control.get(this);
|
|
1351
|
+
const output = {};
|
|
1352
|
+
state.forEach((value, key) => output[key] = value);
|
|
1353
|
+
return output;
|
|
1354
|
+
}
|
|
1355
|
+
},
|
|
1356
|
+
[UPDATE]: {
|
|
1357
|
+
get() {
|
|
1358
|
+
const source = Subscriber.get(this) || Control.get(this);
|
|
1359
|
+
return source && source.latest;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
});
|
|
1363
|
+
const Debug = {
|
|
1364
|
+
CONTROL,
|
|
1365
|
+
LOCAL,
|
|
1366
|
+
STATE,
|
|
1367
|
+
UPDATE
|
|
1368
|
+
};
|
|
1369
|
+
|
|
1395
1370
|
const Oops$2 = issues({
|
|
1396
1371
|
BadProps: () => `Provider expects either a render function, 'get' or 'has' props.`
|
|
1397
1372
|
});
|
|
@@ -1401,66 +1376,66 @@ function Consumer(props) {
|
|
|
1401
1376
|
return children(useTap(type));
|
|
1402
1377
|
const callback = has || get;
|
|
1403
1378
|
if (typeof callback == "function")
|
|
1404
|
-
callback(
|
|
1379
|
+
callback(useContext(type, !!has));
|
|
1405
1380
|
else
|
|
1406
1381
|
throw Oops$2.BadProps();
|
|
1407
1382
|
return null;
|
|
1408
1383
|
}
|
|
1409
1384
|
|
|
1410
1385
|
const Oops$1 = issues({
|
|
1411
|
-
NoType: () => "Provider '
|
|
1412
|
-
PropDeprecated: () => "Provider `of` prop is deprecated, use `for` instead."
|
|
1386
|
+
NoType: () => "Provider 'for' prop must be Model, typeof Model or a collection of them."
|
|
1413
1387
|
});
|
|
1414
1388
|
function Provider(props) {
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1389
|
+
const context = useNewContext(props.for);
|
|
1390
|
+
const render = props.children;
|
|
1391
|
+
useAppliedProps(context, props);
|
|
1392
|
+
React__default["default"].useLayoutEffect(() => () => context.pop(), []);
|
|
1393
|
+
return React__default["default"].createElement(LookupContext.Provider, { value: context }, typeof render == "function"
|
|
1394
|
+
? React__default["default"].createElement(RenderFunction, { context, render })
|
|
1395
|
+
: render);
|
|
1396
|
+
}
|
|
1397
|
+
function useNewContext(include) {
|
|
1398
|
+
const ambient = useLookup();
|
|
1399
|
+
return React__default["default"].useMemo(() => {
|
|
1400
|
+
if (!include)
|
|
1421
1401
|
throw Oops$1.NoType();
|
|
1422
|
-
const layer =
|
|
1402
|
+
const layer = ambient.push();
|
|
1423
1403
|
function register(I) {
|
|
1424
1404
|
if (I instanceof Model)
|
|
1425
1405
|
layer.inject(I.constructor, I, false);
|
|
1426
1406
|
else
|
|
1427
|
-
layer.inject(I, I.
|
|
1407
|
+
layer.inject(I, I.new(), true);
|
|
1428
1408
|
}
|
|
1429
|
-
if (
|
|
1430
|
-
register(
|
|
1409
|
+
if (include instanceof Model || typeof include == "function")
|
|
1410
|
+
register(include);
|
|
1431
1411
|
else
|
|
1432
|
-
values(
|
|
1412
|
+
values(include).forEach(register);
|
|
1433
1413
|
for (const instance of layer.local)
|
|
1434
1414
|
if (instance)
|
|
1435
1415
|
for (const apply of getPending(instance))
|
|
1436
1416
|
apply(layer);
|
|
1437
1417
|
return layer;
|
|
1438
1418
|
}, []);
|
|
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
1419
|
}
|
|
1446
|
-
function useAppliedProps(
|
|
1447
|
-
const
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1420
|
+
function useAppliedProps(context, props) {
|
|
1421
|
+
const apply = React__default["default"].useMemo(() => {
|
|
1422
|
+
return (props) => {
|
|
1423
|
+
entries(props).forEach(([key, value]) => {
|
|
1424
|
+
if (key == "for" || key == "children")
|
|
1425
|
+
return;
|
|
1426
|
+
for (const into of context.local)
|
|
1427
|
+
if (into && key in into)
|
|
1428
|
+
into[key] = value;
|
|
1429
|
+
});
|
|
1455
1430
|
};
|
|
1456
1431
|
}, []);
|
|
1457
|
-
|
|
1432
|
+
apply(props);
|
|
1458
1433
|
}
|
|
1459
1434
|
function RenderFunction(props) {
|
|
1460
1435
|
const hook = use(refresh => {
|
|
1461
1436
|
const targets = props.context.local;
|
|
1462
1437
|
if (targets.length == 1)
|
|
1463
|
-
return
|
|
1438
|
+
return Control.for(targets[0]).subscribe(() => refresh);
|
|
1464
1439
|
return {};
|
|
1465
1440
|
});
|
|
1466
1441
|
if (hook.commit)
|
|
@@ -1469,17 +1444,31 @@ function RenderFunction(props) {
|
|
|
1469
1444
|
}
|
|
1470
1445
|
|
|
1471
1446
|
const Oops = issues({
|
|
1472
|
-
DoesNotExist: (name) => `Tried to access singleton ${name}, but none exist! Did you forget to initialize?\nCall ${name}.
|
|
1447
|
+
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
1448
|
AlreadyExists: (name) => `Shared instance of ${name} already exists! Consider unmounting existing, or use ${name}.reset() to force-delete it.`
|
|
1474
1449
|
});
|
|
1475
1450
|
const Active = new WeakMap();
|
|
1476
1451
|
class Global extends MVC {
|
|
1477
|
-
|
|
1452
|
+
constructor() {
|
|
1453
|
+
super();
|
|
1454
|
+
Active.set(this.constructor, this);
|
|
1455
|
+
}
|
|
1456
|
+
kill(force) {
|
|
1457
|
+
const type = this.constructor;
|
|
1458
|
+
if (type.keepAlive && !force)
|
|
1459
|
+
return false;
|
|
1460
|
+
super.kill();
|
|
1461
|
+
Active.delete(type);
|
|
1462
|
+
return true;
|
|
1463
|
+
}
|
|
1464
|
+
static new(...args) {
|
|
1478
1465
|
if (Active.has(this))
|
|
1479
|
-
|
|
1466
|
+
if (this.keepAlive)
|
|
1467
|
+
return Active.get(this);
|
|
1468
|
+
else
|
|
1469
|
+
throw Oops.AlreadyExists(this.name);
|
|
1480
1470
|
const instance = new this(...args);
|
|
1481
|
-
|
|
1482
|
-
Active.set(this, instance);
|
|
1471
|
+
Control.for(instance);
|
|
1483
1472
|
return instance;
|
|
1484
1473
|
}
|
|
1485
1474
|
/**
|
|
@@ -1487,10 +1476,8 @@ class Global extends MVC {
|
|
|
1487
1476
|
* Returns a thenable; resolves after successful update.
|
|
1488
1477
|
* If instance does not already exist, one will be created.
|
|
1489
1478
|
*/
|
|
1490
|
-
static set(
|
|
1491
|
-
|
|
1492
|
-
instance.import(updates);
|
|
1493
|
-
return instance.update(false);
|
|
1479
|
+
static set(values) {
|
|
1480
|
+
return this.get(true).update(values);
|
|
1494
1481
|
}
|
|
1495
1482
|
static get(arg) {
|
|
1496
1483
|
const instance = Active.get(this);
|
|
@@ -1504,24 +1491,19 @@ class Global extends MVC {
|
|
|
1504
1491
|
static reset() {
|
|
1505
1492
|
const current = Active.get(this);
|
|
1506
1493
|
if (current)
|
|
1507
|
-
current.
|
|
1494
|
+
current.kill(true);
|
|
1508
1495
|
}
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
Active.delete(this.constructor);
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1496
|
+
}
|
|
1497
|
+
Global.keepAlive = true;
|
|
1514
1498
|
|
|
1515
|
-
exports.CONTROL = CONTROL;
|
|
1516
1499
|
exports.Consumer = Consumer;
|
|
1500
|
+
exports.Debug = Debug;
|
|
1517
1501
|
exports.Global = Global;
|
|
1518
|
-
exports.
|
|
1502
|
+
exports.MVC = MVC;
|
|
1519
1503
|
exports.Model = MVC;
|
|
1520
1504
|
exports.Provider = Provider;
|
|
1521
|
-
exports.STATE = STATE;
|
|
1522
|
-
exports.WHY = WHY;
|
|
1523
1505
|
exports.act = act;
|
|
1524
|
-
exports.
|
|
1506
|
+
exports.add = add;
|
|
1525
1507
|
exports["default"] = MVC;
|
|
1526
1508
|
exports.get = get;
|
|
1527
1509
|
exports.has = has;
|
|
@@ -1529,8 +1511,8 @@ exports.ref = ref;
|
|
|
1529
1511
|
exports.set = set;
|
|
1530
1512
|
exports.tap = tap;
|
|
1531
1513
|
exports.use = use$1;
|
|
1514
|
+
exports.useContext = useContext;
|
|
1532
1515
|
exports.useFrom = useFrom;
|
|
1533
|
-
exports.useLocal = useLocal;
|
|
1534
1516
|
exports.useModel = useModel;
|
|
1535
1517
|
exports.useTap = useTap;
|
|
1536
1518
|
//# sourceMappingURL=index.js.map
|