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