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