@expressive/mvc 0.16.0 → 0.17.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 +105 -158
- package/lib/index.esm.js +537 -552
- package/lib/index.js +536 -556
- package/package.json +1 -1
package/lib/index.esm.js
CHANGED
|
@@ -1,4 +1,21 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { Suspense } from 'react';
|
|
2
|
+
|
|
3
|
+
class Issue extends Error {
|
|
4
|
+
constructor() {
|
|
5
|
+
super(...arguments);
|
|
6
|
+
// drop first line (error factory) in stack trace.
|
|
7
|
+
this.stack = this.stack.replace(/\n.+/, "");
|
|
8
|
+
}
|
|
9
|
+
warn() {
|
|
10
|
+
console.warn(this.message);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function issues(register) {
|
|
14
|
+
const Library = {};
|
|
15
|
+
for (const name in register)
|
|
16
|
+
Library[name] = (...args) => new Issue(register[name].apply(null, args));
|
|
17
|
+
return Library;
|
|
18
|
+
}
|
|
2
19
|
|
|
3
20
|
const { assign, create, defineProperties, defineProperty, entries, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
|
|
4
21
|
const UID_CACHE = new WeakMap();
|
|
@@ -14,28 +31,82 @@ function uid(object) {
|
|
|
14
31
|
return uid;
|
|
15
32
|
}
|
|
16
33
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.
|
|
34
|
+
const REGISTER$1 = new WeakMap();
|
|
35
|
+
class Subscriber {
|
|
36
|
+
constructor(parent, onUpdate, strict) {
|
|
37
|
+
this.parent = parent;
|
|
38
|
+
this.onUpdate = onUpdate;
|
|
39
|
+
this.strict = strict;
|
|
40
|
+
this.active = false;
|
|
41
|
+
this.dependant = new Set();
|
|
42
|
+
this.watch = new Map();
|
|
43
|
+
const proxy = create(parent.subject);
|
|
44
|
+
const reset = () => this.latest = undefined;
|
|
45
|
+
REGISTER$1.set(proxy, this);
|
|
46
|
+
defineProperty(this, "proxy", {
|
|
47
|
+
configurable: true,
|
|
48
|
+
get() {
|
|
49
|
+
setTimeout(reset, 0);
|
|
50
|
+
return proxy;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
this.clear = parent.addListener(key => {
|
|
54
|
+
if (this.active)
|
|
55
|
+
this.notify(key);
|
|
56
|
+
});
|
|
22
57
|
}
|
|
23
|
-
|
|
24
|
-
|
|
58
|
+
get using() {
|
|
59
|
+
return Array.from(this.watch.keys());
|
|
60
|
+
}
|
|
61
|
+
apply(values, keys) {
|
|
62
|
+
const { waiting, subject } = this.parent;
|
|
63
|
+
this.active = false;
|
|
64
|
+
if (!keys)
|
|
65
|
+
keys = getOwnPropertyNames(subject);
|
|
66
|
+
for (const key of keys)
|
|
67
|
+
if (key in values)
|
|
68
|
+
subject[key] = values[key];
|
|
69
|
+
waiting.add(() => this.active = true);
|
|
70
|
+
}
|
|
71
|
+
follow(key, value) {
|
|
72
|
+
if (value !== undefined)
|
|
73
|
+
this.watch.set(key, value);
|
|
74
|
+
else if (!this.watch.has(key))
|
|
75
|
+
this.watch.set(key, true);
|
|
76
|
+
}
|
|
77
|
+
commit() {
|
|
78
|
+
this.active = true;
|
|
79
|
+
this.dependant.forEach(x => x.commit());
|
|
80
|
+
}
|
|
81
|
+
release() {
|
|
82
|
+
this.clear();
|
|
83
|
+
this.dependant.forEach(x => x.release());
|
|
84
|
+
}
|
|
85
|
+
notify(key) {
|
|
86
|
+
const { parent, watch } = this;
|
|
87
|
+
const handler = watch.get(key);
|
|
88
|
+
if (!handler)
|
|
89
|
+
return;
|
|
90
|
+
if (typeof handler == "function")
|
|
91
|
+
handler();
|
|
92
|
+
const notify = this.onUpdate(key, parent);
|
|
93
|
+
if (notify) {
|
|
94
|
+
parent.waiting.add(update => {
|
|
95
|
+
this.latest = update.filter(k => watch.has(k));
|
|
96
|
+
});
|
|
97
|
+
parent.waiting.add(notify);
|
|
98
|
+
}
|
|
25
99
|
}
|
|
26
100
|
}
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
for (const name in register)
|
|
30
|
-
Library[name] = (...args) => new Issue(register[name].apply(null, args));
|
|
31
|
-
return Library;
|
|
101
|
+
function subscriber(from) {
|
|
102
|
+
return REGISTER$1.get(from);
|
|
32
103
|
}
|
|
33
104
|
|
|
34
|
-
const Oops$
|
|
105
|
+
const Oops$c = issues({
|
|
35
106
|
NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
|
|
36
107
|
});
|
|
37
108
|
function suspend(source, key) {
|
|
38
|
-
const error = Oops$
|
|
109
|
+
const error = Oops$c.NotReady(source.subject, key);
|
|
39
110
|
const promise = new Promise(resolve => {
|
|
40
111
|
const release = source.addListener(forKey => {
|
|
41
112
|
if (forKey == key)
|
|
@@ -75,98 +146,87 @@ function mayRetry(fn) {
|
|
|
75
146
|
return compute();
|
|
76
147
|
}
|
|
77
148
|
|
|
78
|
-
const
|
|
79
|
-
|
|
149
|
+
const Parent = new WeakMap();
|
|
150
|
+
const Oops$b = issues({
|
|
151
|
+
BadAssignment: (parent, expected, got) => `Property ${parent} expected Model of type ${expected} but got ${got}.`,
|
|
152
|
+
Required: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
|
|
153
|
+
Unexpected: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
|
|
80
154
|
});
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
let pending = KEYS.get(self);
|
|
89
|
-
if (!order)
|
|
90
|
-
ORDER.set(self, order = new Map());
|
|
91
|
-
if (!pending)
|
|
92
|
-
KEYS.set(self, pending = new Set());
|
|
93
|
-
const compute = (initial) => {
|
|
94
|
-
try {
|
|
95
|
-
return setter.call(sub.proxy, sub.proxy);
|
|
96
|
-
}
|
|
97
|
-
catch (err) {
|
|
98
|
-
Oops$e.Failed(subject, key, initial).warn();
|
|
99
|
-
throw err;
|
|
100
|
-
}
|
|
155
|
+
function getParentForGetInstruction(type, relativeTo, required) {
|
|
156
|
+
const item = getParent(type, relativeTo);
|
|
157
|
+
return (_refresh) => {
|
|
158
|
+
if (item)
|
|
159
|
+
return item;
|
|
160
|
+
if (required)
|
|
161
|
+
throw Oops$b.Required(type.name, relativeTo);
|
|
101
162
|
};
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
163
|
+
}
|
|
164
|
+
function getParent(type, from) {
|
|
165
|
+
const value = Parent.get(from);
|
|
166
|
+
if (value && !(value instanceof type))
|
|
167
|
+
throw Oops$b.Unexpected(type.name, from, value);
|
|
168
|
+
return value;
|
|
169
|
+
}
|
|
170
|
+
function getRecursive(key, from) {
|
|
171
|
+
const context = new WeakMap();
|
|
172
|
+
const { state } = from;
|
|
173
|
+
return (local) => {
|
|
174
|
+
if (!state.has(key))
|
|
175
|
+
throw suspend(from, key);
|
|
176
|
+
if (!local)
|
|
177
|
+
return state.get(key);
|
|
178
|
+
if (!context.has(local)) {
|
|
179
|
+
let reset;
|
|
180
|
+
const init = () => {
|
|
181
|
+
if (reset)
|
|
182
|
+
reset();
|
|
183
|
+
const value = state.get(key);
|
|
184
|
+
if (value && controller(value)) {
|
|
185
|
+
const child = new Subscriber(control(value), local.onUpdate);
|
|
186
|
+
if (local.active)
|
|
187
|
+
child.commit();
|
|
188
|
+
local.dependant.add(child);
|
|
189
|
+
context.set(local, child.proxy);
|
|
190
|
+
reset = () => {
|
|
191
|
+
child.release();
|
|
192
|
+
local.dependant.delete(child);
|
|
193
|
+
context.set(local, undefined);
|
|
194
|
+
reset = undefined;
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
local.follow(key, init);
|
|
199
|
+
init();
|
|
116
200
|
}
|
|
201
|
+
return context.get(local);
|
|
117
202
|
};
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
catch (e) {
|
|
132
|
-
throw e;
|
|
133
|
-
}
|
|
134
|
-
finally {
|
|
135
|
-
sub.commit();
|
|
136
|
-
order.set(refresh, order.size);
|
|
137
|
-
}
|
|
203
|
+
}
|
|
204
|
+
function setRecursive(controller, key, initial) {
|
|
205
|
+
const { state, subject } = controller;
|
|
206
|
+
const Type = initial.constructor;
|
|
207
|
+
let get;
|
|
208
|
+
const onUpdate = (next) => {
|
|
209
|
+
state.set(key, next);
|
|
210
|
+
if (!(next instanceof Type))
|
|
211
|
+
throw Oops$b.BadAssignment(`${subject}.${key}`, Type.name, String(next));
|
|
212
|
+
get = getRecursive(key, controller);
|
|
213
|
+
Parent.set(next, subject);
|
|
214
|
+
control(next);
|
|
215
|
+
return true;
|
|
138
216
|
};
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
217
|
+
onUpdate(initial);
|
|
218
|
+
defineProperty(subject, key, {
|
|
219
|
+
set: controller.ref(key, onUpdate),
|
|
220
|
+
get() {
|
|
221
|
+
const local = subscriber(this);
|
|
222
|
+
if (local)
|
|
223
|
+
local.follow(key);
|
|
224
|
+
return get(local);
|
|
144
225
|
}
|
|
145
|
-
|
|
146
|
-
if (value === undefined && required)
|
|
147
|
-
throw suspend(self, key);
|
|
148
|
-
return value;
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
function flush(control) {
|
|
152
|
-
const pending = KEYS.get(control);
|
|
153
|
-
if (!pending || !pending.size)
|
|
154
|
-
return;
|
|
155
|
-
const priority = ORDER.get(control);
|
|
156
|
-
while (pending.size) {
|
|
157
|
-
let compute;
|
|
158
|
-
for (const item of pending)
|
|
159
|
-
if (!compute || priority.get(item) < priority.get(compute))
|
|
160
|
-
compute = item;
|
|
161
|
-
pending.delete(compute);
|
|
162
|
-
const key = INFO.get(compute);
|
|
163
|
-
if (!control.frame.has(key))
|
|
164
|
-
compute();
|
|
165
|
-
}
|
|
166
|
-
pending.clear();
|
|
226
|
+
});
|
|
167
227
|
}
|
|
168
228
|
|
|
169
|
-
const Oops$
|
|
229
|
+
const Oops$a = issues({
|
|
170
230
|
BadCallback: () => `Callback for effect-callback may only return a function.`
|
|
171
231
|
});
|
|
172
232
|
function createEffect(model, callback, keys) {
|
|
@@ -180,7 +240,7 @@ function createEffect(model, callback, keys) {
|
|
|
180
240
|
if (unSet instanceof Promise)
|
|
181
241
|
unSet = undefined;
|
|
182
242
|
if (unSet && typeof unSet !== "function")
|
|
183
|
-
throw Oops$
|
|
243
|
+
throw Oops$a.BadCallback();
|
|
184
244
|
}
|
|
185
245
|
function invoke() {
|
|
186
246
|
if (busy)
|
|
@@ -204,10 +264,11 @@ function createEffect(model, callback, keys) {
|
|
|
204
264
|
return invoke;
|
|
205
265
|
});
|
|
206
266
|
}
|
|
207
|
-
const sub = controller
|
|
267
|
+
const sub = new Subscriber(controller, () => invoke);
|
|
208
268
|
model = sub.proxy;
|
|
209
269
|
invoke();
|
|
210
|
-
|
|
270
|
+
sub.commit();
|
|
271
|
+
return () => sub.release();
|
|
211
272
|
});
|
|
212
273
|
}
|
|
213
274
|
function createValueEffect(callback) {
|
|
@@ -223,11 +284,11 @@ function createValueEffect(callback) {
|
|
|
223
284
|
if (typeof out == "function")
|
|
224
285
|
unSet = out;
|
|
225
286
|
else
|
|
226
|
-
throw Oops$
|
|
287
|
+
throw Oops$a.BadCallback();
|
|
227
288
|
};
|
|
228
289
|
}
|
|
229
290
|
|
|
230
|
-
const Oops$
|
|
291
|
+
const Oops$9 = issues({
|
|
231
292
|
Timeout: (keys, timeout) => `No update seen for [${keys}] within ${timeout}.`,
|
|
232
293
|
KeysExpected: (key) => `Key "${key}" was expected in current update.`,
|
|
233
294
|
StrictUpdate: () => `Expected update but none in progress.`,
|
|
@@ -263,13 +324,13 @@ function awaitUpdate(source, arg1, arg2) {
|
|
|
263
324
|
const timeout = (message) => {
|
|
264
325
|
const k = keys ? keys.join(", ") : "any";
|
|
265
326
|
removeListener();
|
|
266
|
-
reject(Oops$
|
|
327
|
+
reject(Oops$9.Timeout(k, message));
|
|
267
328
|
};
|
|
268
329
|
const removeListener = control(source, controller => {
|
|
269
330
|
const pending = controller.frame;
|
|
270
331
|
if (keys === null) {
|
|
271
332
|
if (pending.size)
|
|
272
|
-
reject(Oops$
|
|
333
|
+
reject(Oops$9.StrictNoUpdate());
|
|
273
334
|
else
|
|
274
335
|
resolve(null);
|
|
275
336
|
return;
|
|
@@ -277,7 +338,7 @@ function awaitUpdate(source, arg1, arg2) {
|
|
|
277
338
|
if (keys) {
|
|
278
339
|
if (keys.length) {
|
|
279
340
|
if (arg2 === 0 && single && !pending.has(keys[0])) {
|
|
280
|
-
reject(Oops$
|
|
341
|
+
reject(Oops$9.KeysExpected(keys[0]));
|
|
281
342
|
return;
|
|
282
343
|
}
|
|
283
344
|
else
|
|
@@ -289,7 +350,7 @@ function awaitUpdate(source, arg1, arg2) {
|
|
|
289
350
|
}
|
|
290
351
|
}
|
|
291
352
|
else if (!pending.size && arg1 === true || arg2 === 0) {
|
|
292
|
-
reject(Oops$
|
|
353
|
+
reject(Oops$9.StrictUpdate());
|
|
293
354
|
return;
|
|
294
355
|
}
|
|
295
356
|
else
|
|
@@ -312,68 +373,7 @@ function awaitUpdate(source, arg1, arg2) {
|
|
|
312
373
|
});
|
|
313
374
|
}
|
|
314
375
|
|
|
315
|
-
const
|
|
316
|
-
class Subscriber {
|
|
317
|
-
constructor(parent, onUpdate, suspend) {
|
|
318
|
-
this.parent = parent;
|
|
319
|
-
this.onUpdate = onUpdate;
|
|
320
|
-
this.suspend = suspend;
|
|
321
|
-
this.active = false;
|
|
322
|
-
this.dependant = new Set();
|
|
323
|
-
this.watch = new Map();
|
|
324
|
-
const proxy = create(parent.subject);
|
|
325
|
-
const reset = () => this.latest = undefined;
|
|
326
|
-
REGISTER$1.set(proxy, this);
|
|
327
|
-
defineProperty(this, "proxy", {
|
|
328
|
-
configurable: true,
|
|
329
|
-
get() {
|
|
330
|
-
setTimeout(reset, 0);
|
|
331
|
-
return proxy;
|
|
332
|
-
}
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
get using() {
|
|
336
|
-
return Array.from(this.watch.keys());
|
|
337
|
-
}
|
|
338
|
-
add(key, value) {
|
|
339
|
-
if (value !== undefined)
|
|
340
|
-
this.watch.set(key, value);
|
|
341
|
-
else if (!this.watch.has(key))
|
|
342
|
-
this.watch.set(key, true);
|
|
343
|
-
}
|
|
344
|
-
commit() {
|
|
345
|
-
const release = this.parent.addListener(key => {
|
|
346
|
-
if (this.active)
|
|
347
|
-
this.onEvent(key);
|
|
348
|
-
});
|
|
349
|
-
this.active = true;
|
|
350
|
-
this.dependant.forEach(x => x.commit());
|
|
351
|
-
return this.release = () => {
|
|
352
|
-
this.dependant.forEach(x => x.release());
|
|
353
|
-
release();
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
onEvent(key) {
|
|
357
|
-
const { parent, watch } = this;
|
|
358
|
-
let handler = watch.get(key);
|
|
359
|
-
if (typeof handler == "function")
|
|
360
|
-
handler = handler();
|
|
361
|
-
if (handler) {
|
|
362
|
-
const notify = this.onUpdate(key, parent);
|
|
363
|
-
if (notify) {
|
|
364
|
-
parent.waiting.add(update => {
|
|
365
|
-
this.latest = update.filter(k => watch.has(k));
|
|
366
|
-
});
|
|
367
|
-
parent.waiting.add(notify);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
function subscriber(from) {
|
|
373
|
-
return REGISTER$1.get(from);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
const Oops$b = issues({
|
|
376
|
+
const Oops$8 = issues({
|
|
377
377
|
NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
|
|
378
378
|
});
|
|
379
379
|
class Model {
|
|
@@ -431,7 +431,7 @@ class Model {
|
|
|
431
431
|
return {
|
|
432
432
|
then: (callback) => {
|
|
433
433
|
if (!callback)
|
|
434
|
-
throw Oops$
|
|
434
|
+
throw Oops$8.NoChaining();
|
|
435
435
|
controller.waiting.add(callback);
|
|
436
436
|
}
|
|
437
437
|
};
|
|
@@ -464,11 +464,150 @@ class Model {
|
|
|
464
464
|
maybe.prototype instanceof this);
|
|
465
465
|
}
|
|
466
466
|
}
|
|
467
|
-
defineProperty(Model.prototype, "toString", {
|
|
468
|
-
value() {
|
|
469
|
-
return this.constructor.name;
|
|
467
|
+
defineProperty(Model.prototype, "toString", {
|
|
468
|
+
value() {
|
|
469
|
+
return this.constructor.name;
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
const FindInstruction = new WeakMap([
|
|
473
|
+
[Model, getParentForGetInstruction]
|
|
474
|
+
]);
|
|
475
|
+
|
|
476
|
+
const Oops$7 = issues({
|
|
477
|
+
PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or get) as computed source for ${model}.${property}. This is not allowed.`,
|
|
478
|
+
Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`
|
|
479
|
+
});
|
|
480
|
+
function get(arg0, arg1) {
|
|
481
|
+
return add(function get(key) {
|
|
482
|
+
const { subject, state } = this;
|
|
483
|
+
// Easy mistake, using a peer, will always be unresolved.
|
|
484
|
+
if (typeof arg0 == "symbol")
|
|
485
|
+
throw Oops$7.PeerNotAllowed(subject, key);
|
|
486
|
+
let source;
|
|
487
|
+
const sourceRequired = arg1 !== false;
|
|
488
|
+
if (arg0 instanceof Model) {
|
|
489
|
+
source = () => arg0;
|
|
490
|
+
if (typeof arg1 !== "function")
|
|
491
|
+
throw new Error(`Factory argument cannot be ${arg1}`);
|
|
492
|
+
}
|
|
493
|
+
else if (Model.isTypeof(arg0)) {
|
|
494
|
+
source = find(arg0, subject, sourceRequired);
|
|
495
|
+
}
|
|
496
|
+
else if (typeof arg0 == "function") {
|
|
497
|
+
arg1 = arg0.call(subject, key, subject);
|
|
498
|
+
source = () => subject;
|
|
499
|
+
}
|
|
500
|
+
if (typeof arg1 == "function")
|
|
501
|
+
return getComputed(key, this, source, arg1);
|
|
502
|
+
else {
|
|
503
|
+
const init = source((got) => {
|
|
504
|
+
state.set(key, got);
|
|
505
|
+
this.update(key);
|
|
506
|
+
});
|
|
507
|
+
// TODO: remove fixes suspense test
|
|
508
|
+
if (init || arg1 === false)
|
|
509
|
+
state.set(key, init);
|
|
510
|
+
return getRecursive(key, this);
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
function getComputed(key, parent, source, setter) {
|
|
515
|
+
const { state } = parent;
|
|
516
|
+
let sub;
|
|
517
|
+
let order = ORDER.get(parent);
|
|
518
|
+
let pending = KEYS.get(parent);
|
|
519
|
+
let instance;
|
|
520
|
+
if (!order)
|
|
521
|
+
ORDER.set(parent, order = new Map());
|
|
522
|
+
if (!pending)
|
|
523
|
+
KEYS.set(parent, pending = new Set());
|
|
524
|
+
const compute = (initial) => {
|
|
525
|
+
try {
|
|
526
|
+
return setter.call(sub.proxy, sub.proxy);
|
|
527
|
+
}
|
|
528
|
+
catch (err) {
|
|
529
|
+
Oops$7.Failed(instance, key, initial).warn();
|
|
530
|
+
throw err;
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
const create = () => {
|
|
534
|
+
// TODO: replace create with a cleanup function
|
|
535
|
+
const got = source(create);
|
|
536
|
+
if (!got)
|
|
537
|
+
throw suspend(parent, key);
|
|
538
|
+
instance = got;
|
|
539
|
+
sub = new Subscriber(control(instance), (_, control) => {
|
|
540
|
+
if (control !== parent)
|
|
541
|
+
refresh();
|
|
542
|
+
else
|
|
543
|
+
pending.add(refresh);
|
|
544
|
+
});
|
|
545
|
+
sub.watch.set(key, false);
|
|
546
|
+
try {
|
|
547
|
+
const value = compute(true);
|
|
548
|
+
state.set(key, value);
|
|
549
|
+
return value;
|
|
550
|
+
}
|
|
551
|
+
finally {
|
|
552
|
+
sub.commit();
|
|
553
|
+
order.set(refresh, order.size);
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
const refresh = () => {
|
|
557
|
+
let value;
|
|
558
|
+
try {
|
|
559
|
+
value = compute(false);
|
|
560
|
+
}
|
|
561
|
+
catch (e) {
|
|
562
|
+
console.error(e);
|
|
563
|
+
}
|
|
564
|
+
finally {
|
|
565
|
+
if (state.get(key) !== value) {
|
|
566
|
+
state.set(key, value);
|
|
567
|
+
parent.update(key);
|
|
568
|
+
return value;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
INFO.set(refresh, key);
|
|
573
|
+
return () => {
|
|
574
|
+
if (pending.has(refresh)) {
|
|
575
|
+
pending.delete(refresh);
|
|
576
|
+
refresh();
|
|
577
|
+
}
|
|
578
|
+
return sub ? state.get(key) : create();
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
const INFO = new WeakMap();
|
|
582
|
+
const KEYS = new WeakMap();
|
|
583
|
+
const ORDER = new WeakMap();
|
|
584
|
+
function flush(control) {
|
|
585
|
+
const pending = KEYS.get(control);
|
|
586
|
+
if (!pending || !pending.size)
|
|
587
|
+
return;
|
|
588
|
+
const priority = ORDER.get(control);
|
|
589
|
+
while (pending.size) {
|
|
590
|
+
let compute;
|
|
591
|
+
for (const item of pending)
|
|
592
|
+
if (!compute || priority.get(item) < priority.get(compute))
|
|
593
|
+
compute = item;
|
|
594
|
+
pending.delete(compute);
|
|
595
|
+
const key = INFO.get(compute);
|
|
596
|
+
if (!control.frame.has(key))
|
|
597
|
+
compute();
|
|
470
598
|
}
|
|
471
|
-
|
|
599
|
+
pending.clear();
|
|
600
|
+
}
|
|
601
|
+
// TODO: collapse this function when MVC is merged into Model.
|
|
602
|
+
function find(type, relativeTo, required) {
|
|
603
|
+
let T = type;
|
|
604
|
+
do {
|
|
605
|
+
T = getPrototypeOf(T);
|
|
606
|
+
const getter = FindInstruction.get(T);
|
|
607
|
+
if (getter)
|
|
608
|
+
return getter(type, relativeTo, required);
|
|
609
|
+
} while (T.prototype instanceof Model);
|
|
610
|
+
}
|
|
472
611
|
|
|
473
612
|
const REGISTER = new WeakMap();
|
|
474
613
|
const PENDING = new Map();
|
|
@@ -480,9 +619,6 @@ class Control {
|
|
|
480
619
|
this.followers = new Set();
|
|
481
620
|
REGISTER.set(subject, this);
|
|
482
621
|
}
|
|
483
|
-
subscribe(callback) {
|
|
484
|
-
return new Subscriber(this, callback);
|
|
485
|
-
}
|
|
486
622
|
watch(key) {
|
|
487
623
|
const { subject, state } = this;
|
|
488
624
|
const { value } = getOwnPropertyDescriptor(subject, key);
|
|
@@ -493,7 +629,7 @@ class Control {
|
|
|
493
629
|
if (instruction) {
|
|
494
630
|
delete subject[key];
|
|
495
631
|
PENDING.delete(value);
|
|
496
|
-
instruction
|
|
632
|
+
instruction(this, key);
|
|
497
633
|
}
|
|
498
634
|
return;
|
|
499
635
|
}
|
|
@@ -509,8 +645,8 @@ class Control {
|
|
|
509
645
|
const sub = subscriber(this);
|
|
510
646
|
const value = state.get(key);
|
|
511
647
|
if (sub) {
|
|
512
|
-
sub.
|
|
513
|
-
if (value === undefined && sub.
|
|
648
|
+
sub.follow(key);
|
|
649
|
+
if (value === undefined && sub.strict)
|
|
514
650
|
throw suspend(this, key);
|
|
515
651
|
}
|
|
516
652
|
return value;
|
|
@@ -571,6 +707,8 @@ class Control {
|
|
|
571
707
|
listeners.forEach(x => x(null, this));
|
|
572
708
|
}
|
|
573
709
|
}
|
|
710
|
+
Control.get = controller;
|
|
711
|
+
Control.for = control;
|
|
574
712
|
function controller(from) {
|
|
575
713
|
if ("is" in from)
|
|
576
714
|
from = from.is;
|
|
@@ -600,68 +738,6 @@ function control(subject, cb) {
|
|
|
600
738
|
return cb ? cb(control) : control;
|
|
601
739
|
}
|
|
602
740
|
|
|
603
|
-
const Parent = new WeakMap();
|
|
604
|
-
const Oops$a = issues({
|
|
605
|
-
BadAssignment: (parent, expected, got) => `Property ${parent} expected Model of type ${expected} but got ${got}.`
|
|
606
|
-
});
|
|
607
|
-
function getRecursive(key, from) {
|
|
608
|
-
const context = new WeakMap();
|
|
609
|
-
const { state } = from;
|
|
610
|
-
return (local) => {
|
|
611
|
-
if (!local)
|
|
612
|
-
return state.get(key);
|
|
613
|
-
if (!context.has(local)) {
|
|
614
|
-
let reset;
|
|
615
|
-
const init = () => {
|
|
616
|
-
if (reset)
|
|
617
|
-
reset();
|
|
618
|
-
const value = state.get(key);
|
|
619
|
-
if (value && controller(value)) {
|
|
620
|
-
const child = control(value).subscribe(local.onUpdate);
|
|
621
|
-
if (local.active)
|
|
622
|
-
child.commit();
|
|
623
|
-
local.dependant.add(child);
|
|
624
|
-
context.set(local, child.proxy);
|
|
625
|
-
reset = () => {
|
|
626
|
-
child.release();
|
|
627
|
-
local.dependant.delete(child);
|
|
628
|
-
context.set(local, undefined);
|
|
629
|
-
reset = undefined;
|
|
630
|
-
};
|
|
631
|
-
}
|
|
632
|
-
return true;
|
|
633
|
-
};
|
|
634
|
-
local.add(key, init);
|
|
635
|
-
init();
|
|
636
|
-
}
|
|
637
|
-
return context.get(local);
|
|
638
|
-
};
|
|
639
|
-
}
|
|
640
|
-
function setRecursive(controller, key, initial) {
|
|
641
|
-
const { state, subject } = controller;
|
|
642
|
-
const Type = initial.constructor;
|
|
643
|
-
let get;
|
|
644
|
-
const onUpdate = (next) => {
|
|
645
|
-
state.set(key, next);
|
|
646
|
-
if (!(next instanceof Type))
|
|
647
|
-
throw Oops$a.BadAssignment(`${subject}.${key}`, Type.name, String(next));
|
|
648
|
-
get = getRecursive(key, controller);
|
|
649
|
-
Parent.set(next, subject);
|
|
650
|
-
control(next);
|
|
651
|
-
return true;
|
|
652
|
-
};
|
|
653
|
-
onUpdate(initial);
|
|
654
|
-
defineProperty(subject, key, {
|
|
655
|
-
set: controller.ref(key, onUpdate),
|
|
656
|
-
get() {
|
|
657
|
-
const local = subscriber(this);
|
|
658
|
-
if (local)
|
|
659
|
-
local.add(key);
|
|
660
|
-
return get(local);
|
|
661
|
-
}
|
|
662
|
-
});
|
|
663
|
-
}
|
|
664
|
-
|
|
665
741
|
/**
|
|
666
742
|
* Run instruction as controller sets itself up.
|
|
667
743
|
* This will specialize the behavior of a given property.
|
|
@@ -669,64 +745,42 @@ function setRecursive(controller, key, initial) {
|
|
|
669
745
|
function add(instruction, label) {
|
|
670
746
|
const name = label || instruction.name || "pending";
|
|
671
747
|
const placeholder = Symbol(`${name} instruction`);
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
748
|
+
PENDING.set(placeholder, (control, key) => {
|
|
749
|
+
let output = instruction.call(control, key, control);
|
|
750
|
+
if (!output)
|
|
751
|
+
return undefined;
|
|
675
752
|
if (typeof output == "function")
|
|
676
753
|
output = { get: output };
|
|
677
|
-
if (typeof output != "object")
|
|
678
|
-
return;
|
|
679
|
-
if ("explicit" in output && output.explicit) {
|
|
680
|
-
defineProperty(subject, key, output);
|
|
681
|
-
return false;
|
|
682
|
-
}
|
|
683
|
-
let { get: onGet, set: onSet, suspend: shouldSuspend } = output;
|
|
684
754
|
if ("value" in output)
|
|
685
|
-
state.set(key, output.value);
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
if (destroy)
|
|
691
|
-
this.addListener((key) => {
|
|
692
|
-
if (key == null)
|
|
693
|
-
destroy();
|
|
694
|
-
});
|
|
695
|
-
}
|
|
696
|
-
const control = this;
|
|
697
|
-
defineProperty(subject, key, {
|
|
698
|
-
enumerable: output.enumerable,
|
|
699
|
-
set: onSet === false
|
|
755
|
+
control.state.set(key, output.value);
|
|
756
|
+
let { enumerable, get, set } = output;
|
|
757
|
+
defineProperty(control.subject, key, {
|
|
758
|
+
enumerable,
|
|
759
|
+
set: set === false
|
|
700
760
|
? undefined
|
|
701
|
-
:
|
|
761
|
+
: control.ref(key, set),
|
|
702
762
|
get() {
|
|
703
|
-
if (!state.has(key) && shouldSuspend)
|
|
704
|
-
throw suspend(control, key);
|
|
705
763
|
const local = subscriber(this);
|
|
706
|
-
const required = local && local.suspend;
|
|
707
764
|
if (local)
|
|
708
|
-
local.
|
|
765
|
+
local.follow(key);
|
|
709
766
|
try {
|
|
710
|
-
const value =
|
|
711
|
-
|
|
712
|
-
: state.get(key);
|
|
713
|
-
if (value === undefined && required === true)
|
|
767
|
+
const value = get ? get(local) : control.state.get(key);
|
|
768
|
+
if (value === undefined && local && local.strict === true)
|
|
714
769
|
throw suspend(control, key);
|
|
715
770
|
return value;
|
|
716
771
|
}
|
|
717
772
|
catch (err) {
|
|
718
|
-
if (err instanceof Promise &&
|
|
773
|
+
if (err instanceof Promise && local && local.strict === false)
|
|
719
774
|
return;
|
|
720
775
|
throw err;
|
|
721
776
|
}
|
|
722
777
|
}
|
|
723
778
|
});
|
|
724
|
-
}
|
|
725
|
-
PENDING.set(placeholder, setup);
|
|
779
|
+
});
|
|
726
780
|
return placeholder;
|
|
727
781
|
}
|
|
728
782
|
|
|
729
|
-
|
|
783
|
+
/******************************************************************************
|
|
730
784
|
Copyright (c) Microsoft Corporation.
|
|
731
785
|
|
|
732
786
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
@@ -751,7 +805,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
|
|
|
751
805
|
});
|
|
752
806
|
}
|
|
753
807
|
|
|
754
|
-
const Oops$
|
|
808
|
+
const Oops$6 = issues({
|
|
755
809
|
DuplicatePending: (key) => `Invoked action ${key} but one is already active.`
|
|
756
810
|
});
|
|
757
811
|
function run(task) {
|
|
@@ -759,7 +813,7 @@ function run(task) {
|
|
|
759
813
|
let pending = false;
|
|
760
814
|
const invoke = (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
761
815
|
if (pending)
|
|
762
|
-
return Promise.reject(Oops$
|
|
816
|
+
return Promise.reject(Oops$6.DuplicatePending(key));
|
|
763
817
|
pending = true;
|
|
764
818
|
this.update(key);
|
|
765
819
|
try {
|
|
@@ -781,106 +835,6 @@ function run(task) {
|
|
|
781
835
|
});
|
|
782
836
|
}
|
|
783
837
|
|
|
784
|
-
const Oops$8 = issues({
|
|
785
|
-
NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
|
|
786
|
-
});
|
|
787
|
-
function factoryMode(self, value, key, required) {
|
|
788
|
-
const { subject, state } = self;
|
|
789
|
-
let pending;
|
|
790
|
-
let error;
|
|
791
|
-
if (value instanceof Promise) {
|
|
792
|
-
state.set(key, undefined);
|
|
793
|
-
pending = value
|
|
794
|
-
.catch(err => error = err)
|
|
795
|
-
.then(val => {
|
|
796
|
-
state.set(key, val);
|
|
797
|
-
return val;
|
|
798
|
-
})
|
|
799
|
-
.finally(() => {
|
|
800
|
-
pending = undefined;
|
|
801
|
-
self.update(key);
|
|
802
|
-
});
|
|
803
|
-
}
|
|
804
|
-
else
|
|
805
|
-
state.set(key, value);
|
|
806
|
-
const suspend = () => {
|
|
807
|
-
if (required === false)
|
|
808
|
-
return undefined;
|
|
809
|
-
const issue = Oops$8.NotReady(subject, key);
|
|
810
|
-
assign(pending, {
|
|
811
|
-
message: issue.message,
|
|
812
|
-
stack: issue.stack
|
|
813
|
-
});
|
|
814
|
-
throw pending;
|
|
815
|
-
};
|
|
816
|
-
return () => {
|
|
817
|
-
if (error)
|
|
818
|
-
throw error;
|
|
819
|
-
if (pending)
|
|
820
|
-
return suspend();
|
|
821
|
-
if (state.has(key))
|
|
822
|
-
return state.get(key);
|
|
823
|
-
};
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
const Oops$7 = issues({
|
|
827
|
-
PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`,
|
|
828
|
-
ComputeFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`,
|
|
829
|
-
Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`
|
|
830
|
-
});
|
|
831
|
-
function get(arg0, arg1, arg2) {
|
|
832
|
-
return add(function get(key) {
|
|
833
|
-
const { subject } = this;
|
|
834
|
-
const computeRequired = arg1 === true || arg2 === true;
|
|
835
|
-
const factoryRequired = arg1 !== false && arg2 !== false;
|
|
836
|
-
let getter;
|
|
837
|
-
// Easy mistake, using a peer, will always be unresolved.
|
|
838
|
-
if (typeof arg0 == "symbol")
|
|
839
|
-
throw Oops$7.PeerNotAllowed(subject, key);
|
|
840
|
-
// if(typeof arg0 == "function" && /^[A-Z]/.test(arg0.name))
|
|
841
|
-
// throw Oops.BadSource(subject, key, arg0);
|
|
842
|
-
this.state.set(key, undefined);
|
|
843
|
-
const init = () => {
|
|
844
|
-
let source = this;
|
|
845
|
-
let setter;
|
|
846
|
-
if (typeof arg0 == "function") {
|
|
847
|
-
const result = mayRetry(arg0.bind(subject, key, subject));
|
|
848
|
-
if (typeof result == "function")
|
|
849
|
-
setter = result;
|
|
850
|
-
else {
|
|
851
|
-
getter = factoryMode(this, result, key, factoryRequired);
|
|
852
|
-
return;
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
else if (arg0 instanceof Promise) {
|
|
856
|
-
getter = factoryMode(this, arg0, key, factoryRequired);
|
|
857
|
-
return;
|
|
858
|
-
}
|
|
859
|
-
else if (typeof arg1 == "function") {
|
|
860
|
-
// replace source controller in-case it is different
|
|
861
|
-
source = control(arg0);
|
|
862
|
-
setter = arg1;
|
|
863
|
-
}
|
|
864
|
-
else
|
|
865
|
-
throw new Error(`Factory argument cannot be ${arg1}`);
|
|
866
|
-
getter = computeMode(this, source, setter, key, computeRequired);
|
|
867
|
-
};
|
|
868
|
-
if (typeof arg0 == "function" && computeRequired)
|
|
869
|
-
try {
|
|
870
|
-
init();
|
|
871
|
-
}
|
|
872
|
-
catch (err) {
|
|
873
|
-
Oops$7.ComputeFailed(subject, key).warn();
|
|
874
|
-
throw err;
|
|
875
|
-
}
|
|
876
|
-
return () => {
|
|
877
|
-
if (!getter)
|
|
878
|
-
init();
|
|
879
|
-
return getter();
|
|
880
|
-
};
|
|
881
|
-
});
|
|
882
|
-
}
|
|
883
|
-
|
|
884
838
|
function ref(arg, mapper) {
|
|
885
839
|
return add(function ref(key) {
|
|
886
840
|
let value = {};
|
|
@@ -900,7 +854,7 @@ function ref(arg, mapper) {
|
|
|
900
854
|
value: createRef(this, key)
|
|
901
855
|
});
|
|
902
856
|
});
|
|
903
|
-
|
|
857
|
+
defineProperty(this.subject, key, { value });
|
|
904
858
|
});
|
|
905
859
|
}
|
|
906
860
|
function createRef(src, key, cb) {
|
|
@@ -912,16 +866,78 @@ function createRef(src, key, cb) {
|
|
|
912
866
|
return refObjectFunction;
|
|
913
867
|
}
|
|
914
868
|
|
|
869
|
+
const Oops$5 = issues({
|
|
870
|
+
ComputeFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`,
|
|
871
|
+
NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
|
|
872
|
+
});
|
|
915
873
|
function set(value, argument) {
|
|
916
|
-
if (!(1 in arguments)) {
|
|
917
|
-
argument = value;
|
|
918
|
-
value = undefined;
|
|
919
|
-
}
|
|
920
874
|
return add(function set(key) {
|
|
875
|
+
const { state, subject } = this;
|
|
876
|
+
if (typeof value == "function" || value instanceof Promise) {
|
|
877
|
+
const required = argument !== false;
|
|
878
|
+
let getter;
|
|
879
|
+
const init = () => {
|
|
880
|
+
try {
|
|
881
|
+
if (typeof value == "function")
|
|
882
|
+
value = mayRetry(value.bind(subject, key, subject));
|
|
883
|
+
let pending;
|
|
884
|
+
let error;
|
|
885
|
+
if (value instanceof Promise) {
|
|
886
|
+
state.set(key, undefined);
|
|
887
|
+
pending = value
|
|
888
|
+
.catch(err => error = err)
|
|
889
|
+
.then(val => {
|
|
890
|
+
state.set(key, val);
|
|
891
|
+
return val;
|
|
892
|
+
})
|
|
893
|
+
.finally(() => {
|
|
894
|
+
pending = undefined;
|
|
895
|
+
this.update(key);
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
else
|
|
899
|
+
state.set(key, value);
|
|
900
|
+
const suspend = () => {
|
|
901
|
+
if (required === false)
|
|
902
|
+
return undefined;
|
|
903
|
+
const issue = Oops$5.NotReady(subject, key);
|
|
904
|
+
assign(pending, {
|
|
905
|
+
message: issue.message,
|
|
906
|
+
stack: issue.stack
|
|
907
|
+
});
|
|
908
|
+
throw pending;
|
|
909
|
+
};
|
|
910
|
+
getter = () => {
|
|
911
|
+
if (error)
|
|
912
|
+
throw error;
|
|
913
|
+
if (pending)
|
|
914
|
+
return suspend();
|
|
915
|
+
if (state.has(key))
|
|
916
|
+
return state.get(key);
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
catch (err) {
|
|
920
|
+
Oops$5.ComputeFailed(subject, key).warn();
|
|
921
|
+
throw err;
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
if (argument === true)
|
|
925
|
+
init();
|
|
926
|
+
return () => {
|
|
927
|
+
if (!getter)
|
|
928
|
+
init();
|
|
929
|
+
return getter();
|
|
930
|
+
};
|
|
931
|
+
}
|
|
921
932
|
if (value !== undefined)
|
|
922
|
-
|
|
933
|
+
state.set(key, value);
|
|
923
934
|
return {
|
|
924
|
-
|
|
935
|
+
get: () => {
|
|
936
|
+
if (state.has(key))
|
|
937
|
+
return state.get(key);
|
|
938
|
+
else
|
|
939
|
+
throw suspend(this, key);
|
|
940
|
+
},
|
|
925
941
|
set: typeof argument == "function"
|
|
926
942
|
? createValueEffect(argument)
|
|
927
943
|
: undefined
|
|
@@ -965,7 +981,7 @@ function keyed(control, property, initial) {
|
|
|
965
981
|
}
|
|
966
982
|
}
|
|
967
983
|
}
|
|
968
|
-
local.
|
|
984
|
+
local.follow(property, false);
|
|
969
985
|
local.dependant.add({
|
|
970
986
|
commit() {
|
|
971
987
|
if (using.size === 0)
|
|
@@ -981,6 +997,12 @@ function keyed(control, property, initial) {
|
|
|
981
997
|
context.set(local, proxy);
|
|
982
998
|
return proxy;
|
|
983
999
|
}
|
|
1000
|
+
control.addListener(key => {
|
|
1001
|
+
if (key === null) {
|
|
1002
|
+
update.clear();
|
|
1003
|
+
context.clear();
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
984
1006
|
return {
|
|
985
1007
|
value: initial,
|
|
986
1008
|
get(local) {
|
|
@@ -993,10 +1015,6 @@ function keyed(control, property, initial) {
|
|
|
993
1015
|
managed = init(next);
|
|
994
1016
|
context.clear();
|
|
995
1017
|
emit(ANY);
|
|
996
|
-
},
|
|
997
|
-
destroy() {
|
|
998
|
-
update.clear();
|
|
999
|
-
context.clear();
|
|
1000
1018
|
}
|
|
1001
1019
|
};
|
|
1002
1020
|
}
|
|
@@ -1075,11 +1093,11 @@ function createProxy(emit, watch) {
|
|
|
1075
1093
|
};
|
|
1076
1094
|
}
|
|
1077
1095
|
|
|
1078
|
-
const Oops$
|
|
1096
|
+
const Oops$4 = issues({
|
|
1079
1097
|
BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`,
|
|
1080
1098
|
NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
|
|
1081
1099
|
});
|
|
1082
|
-
function use
|
|
1100
|
+
function use(input, argument) {
|
|
1083
1101
|
return add(function use(key) {
|
|
1084
1102
|
const { state, subject } = this;
|
|
1085
1103
|
const required = argument !== false;
|
|
@@ -1091,7 +1109,7 @@ function use$1(input, argument) {
|
|
|
1091
1109
|
input = new input();
|
|
1092
1110
|
}
|
|
1093
1111
|
else if (input && typeof input !== "object")
|
|
1094
|
-
throw Oops$
|
|
1112
|
+
throw Oops$4.BadArgument(typeof input);
|
|
1095
1113
|
if (input instanceof Map || input instanceof Set)
|
|
1096
1114
|
return keyed(this, key, input);
|
|
1097
1115
|
const onUpdate = (next) => {
|
|
@@ -1108,7 +1126,7 @@ function use$1(input, argument) {
|
|
|
1108
1126
|
const suspend = () => {
|
|
1109
1127
|
if (required === false)
|
|
1110
1128
|
return;
|
|
1111
|
-
const issue = Oops$
|
|
1129
|
+
const issue = Oops$4.NotReady(subject, key);
|
|
1112
1130
|
assign(pending, {
|
|
1113
1131
|
message: issue.message,
|
|
1114
1132
|
stack: issue.stack
|
|
@@ -1153,33 +1171,11 @@ function use$1(input, argument) {
|
|
|
1153
1171
|
});
|
|
1154
1172
|
}
|
|
1155
1173
|
|
|
1156
|
-
|
|
1157
|
-
Required: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
|
|
1158
|
-
Unexpected: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
|
|
1159
|
-
});
|
|
1160
|
-
function has(Type, required) {
|
|
1161
|
-
return add(function parent() {
|
|
1162
|
-
const child = this.subject;
|
|
1163
|
-
const value = Parent.get(child);
|
|
1164
|
-
const expected = Type.name;
|
|
1165
|
-
if (!value) {
|
|
1166
|
-
if (required !== false)
|
|
1167
|
-
throw Oops$5.Required(expected, child);
|
|
1168
|
-
}
|
|
1169
|
-
else if (!(value instanceof Type))
|
|
1170
|
-
throw Oops$5.Unexpected(expected, child, value);
|
|
1171
|
-
return { value };
|
|
1172
|
-
});
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
const Oops$4 = issues({
|
|
1176
|
-
NotFound: (name) => `Couldn't find ${name} in context; did you forget to use a Provider?`
|
|
1177
|
-
});
|
|
1178
|
-
class Lookup {
|
|
1174
|
+
class Register {
|
|
1179
1175
|
constructor() {
|
|
1180
1176
|
this.table = new Map();
|
|
1181
1177
|
}
|
|
1182
|
-
|
|
1178
|
+
has(T) {
|
|
1183
1179
|
let key = this.table.get(T);
|
|
1184
1180
|
if (!key) {
|
|
1185
1181
|
key = Symbol(T.name);
|
|
@@ -1188,22 +1184,9 @@ class Lookup {
|
|
|
1188
1184
|
return key;
|
|
1189
1185
|
}
|
|
1190
1186
|
get(Type) {
|
|
1191
|
-
return this[this.
|
|
1192
|
-
}
|
|
1193
|
-
has(key, input, callback) {
|
|
1194
|
-
if (MVC.isTypeof(input) && input.global)
|
|
1195
|
-
return input.new();
|
|
1196
|
-
if (this.register.get(key) === input)
|
|
1197
|
-
return typeof input == "object"
|
|
1198
|
-
? input
|
|
1199
|
-
: this.get(input);
|
|
1200
|
-
const instance = this.add(input);
|
|
1201
|
-
this.register.set(key, input);
|
|
1202
|
-
if (callback)
|
|
1203
|
-
callback(instance);
|
|
1204
|
-
return instance;
|
|
1187
|
+
return this[this.has(Type)];
|
|
1205
1188
|
}
|
|
1206
|
-
add(input) {
|
|
1189
|
+
add(input, key) {
|
|
1207
1190
|
let writable = true;
|
|
1208
1191
|
let T;
|
|
1209
1192
|
let I;
|
|
@@ -1212,19 +1195,21 @@ class Lookup {
|
|
|
1212
1195
|
I = new input();
|
|
1213
1196
|
}
|
|
1214
1197
|
else {
|
|
1215
|
-
I = input;
|
|
1198
|
+
I = input.is;
|
|
1216
1199
|
T = I.constructor;
|
|
1217
1200
|
writable = false;
|
|
1218
1201
|
}
|
|
1219
1202
|
do {
|
|
1220
|
-
const key = this.
|
|
1203
|
+
const key = this.has(T);
|
|
1221
1204
|
defineProperty(this, key, {
|
|
1222
1205
|
configurable: true,
|
|
1223
|
-
value: this.hasOwnProperty(key) ? null : I,
|
|
1206
|
+
value: this.hasOwnProperty(key) && I !== this[key] ? null : I,
|
|
1224
1207
|
writable
|
|
1225
1208
|
});
|
|
1226
1209
|
T = getPrototypeOf(T);
|
|
1227
1210
|
} while (T !== Model);
|
|
1211
|
+
if (key !== undefined)
|
|
1212
|
+
this.register.set(key, input);
|
|
1228
1213
|
return I;
|
|
1229
1214
|
}
|
|
1230
1215
|
push() {
|
|
@@ -1238,52 +1223,77 @@ class Lookup {
|
|
|
1238
1223
|
const entry = getOwnPropertyDescriptor(this, key);
|
|
1239
1224
|
if (entry.writable && entry.value)
|
|
1240
1225
|
items.add(entry.value);
|
|
1226
|
+
delete this[key];
|
|
1241
1227
|
}
|
|
1242
1228
|
for (const model of items)
|
|
1243
1229
|
model.end();
|
|
1244
1230
|
}
|
|
1231
|
+
delete(instance) {
|
|
1232
|
+
for (const key of getOwnPropertySymbols(this)) {
|
|
1233
|
+
const entry = getOwnPropertyDescriptor(this, key);
|
|
1234
|
+
if (entry.value === instance)
|
|
1235
|
+
delete this[key];
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
const Global$1 = new Register();
|
|
1240
|
+
|
|
1241
|
+
const Oops$3 = issues({
|
|
1242
|
+
AlreadyExists: (name) => `Shared instance of ${name} already exists! Consider unmounting existing, or use ${name}.reset() to force-delete it.`,
|
|
1243
|
+
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.`,
|
|
1244
|
+
NotFound: (name) => `Couldn't find ${name} in context; did you forget to use a Provider?`,
|
|
1245
|
+
MultipleExist: (name) => `Did find ${name} in context, but multiple were defined by same Provider.`
|
|
1246
|
+
});
|
|
1247
|
+
const LookupContext = React.createContext(Global$1);
|
|
1248
|
+
function useLookup() {
|
|
1249
|
+
try {
|
|
1250
|
+
return React.useContext(LookupContext);
|
|
1251
|
+
}
|
|
1252
|
+
catch (err) {
|
|
1253
|
+
return Global$1;
|
|
1254
|
+
}
|
|
1245
1255
|
}
|
|
1246
|
-
const LookupContext = React.createContext(new Lookup());
|
|
1247
|
-
const useLookup = () => React.useContext(LookupContext);
|
|
1248
1256
|
function useContext(Type, required) {
|
|
1249
1257
|
const instance = useLookup().get(Type);
|
|
1258
|
+
if (instance === null)
|
|
1259
|
+
throw Oops$3.MultipleExist(Type.name);
|
|
1250
1260
|
if (!instance && required !== false)
|
|
1251
|
-
throw
|
|
1261
|
+
throw MVC.isTypeof(Type) && Type.global
|
|
1262
|
+
? Oops$3.DoesNotExist(Type.name)
|
|
1263
|
+
: Oops$3.NotFound(Type.name);
|
|
1252
1264
|
return instance;
|
|
1253
1265
|
}
|
|
1254
1266
|
|
|
1255
|
-
const Oops$
|
|
1267
|
+
const Oops$2 = issues({
|
|
1256
1268
|
NotAllowed: (parent, child) => `Global '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
|
|
1257
|
-
AmbientRequired: (requested, requester
|
|
1269
|
+
AmbientRequired: (requested, requester) => `Attempted to find an instance of ${requested} in context. It is required by ${requester}, but one could not be found.`
|
|
1258
1270
|
});
|
|
1259
1271
|
const Pending = new WeakMap();
|
|
1260
1272
|
const Applied = new WeakMap();
|
|
1261
|
-
function
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
throw Oops$3.NotAllowed(subject, type.name);
|
|
1271
|
-
getPending(subject).push(context => {
|
|
1272
|
-
let instance = context.get(type);
|
|
1273
|
-
if (typeof argument == "function") {
|
|
1274
|
-
if (argument(instance) === false)
|
|
1275
|
-
instance = undefined;
|
|
1273
|
+
function getContextForGetInstruction(type, relativeTo, required) {
|
|
1274
|
+
let item = getParent(type, relativeTo);
|
|
1275
|
+
return (refresh) => {
|
|
1276
|
+
if (item)
|
|
1277
|
+
return item;
|
|
1278
|
+
findRelative(relativeTo, type, got => {
|
|
1279
|
+
if (got) {
|
|
1280
|
+
item = got;
|
|
1281
|
+
refresh(got);
|
|
1276
1282
|
}
|
|
1277
|
-
else if (
|
|
1278
|
-
throw Oops$
|
|
1279
|
-
|
|
1280
|
-
|
|
1283
|
+
else if (required)
|
|
1284
|
+
throw Oops$2.AmbientRequired(type.name, relativeTo);
|
|
1285
|
+
});
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
function findRelative(from, type, callback) {
|
|
1289
|
+
if (MVC.isTypeof(type) && type.global)
|
|
1290
|
+
callback(Global$1.get(type));
|
|
1291
|
+
else if (from.constructor.global)
|
|
1292
|
+
throw Oops$2.NotAllowed(from, type.name);
|
|
1293
|
+
else
|
|
1294
|
+
getPending(from).push(context => {
|
|
1295
|
+
callback(context.get(type));
|
|
1281
1296
|
});
|
|
1282
|
-
return {
|
|
1283
|
-
recursive: true,
|
|
1284
|
-
suspend: true
|
|
1285
|
-
};
|
|
1286
|
-
});
|
|
1287
1297
|
}
|
|
1288
1298
|
function usePeerContext(subject) {
|
|
1289
1299
|
if (Applied.has(subject)) {
|
|
@@ -1307,7 +1317,7 @@ function getPending(subject) {
|
|
|
1307
1317
|
return pending;
|
|
1308
1318
|
}
|
|
1309
1319
|
|
|
1310
|
-
function
|
|
1320
|
+
function useModel(source, arg1, arg2) {
|
|
1311
1321
|
const instance = React.useMemo(() => {
|
|
1312
1322
|
const callback = arg2 || arg1;
|
|
1313
1323
|
const instance = Model.isTypeof(source)
|
|
@@ -1320,7 +1330,7 @@ function useNew(source, arg1, arg2) {
|
|
|
1320
1330
|
}, []);
|
|
1321
1331
|
usePeerContext(instance);
|
|
1322
1332
|
if (Array.isArray(arg1)) {
|
|
1323
|
-
const update = useState(0)[1];
|
|
1333
|
+
const update = React.useState(0)[1];
|
|
1324
1334
|
React.useLayoutEffect(() => {
|
|
1325
1335
|
if (arg1.length && instance instanceof Model)
|
|
1326
1336
|
instance.on(arg1, () => update(x => x + 1));
|
|
@@ -1334,19 +1344,10 @@ function useNew(source, arg1, arg2) {
|
|
|
1334
1344
|
const state = React.useState(0);
|
|
1335
1345
|
const local = React.useMemo(() => {
|
|
1336
1346
|
const refresh = state[1].bind(null, x => x + 1);
|
|
1337
|
-
return control(instance)
|
|
1347
|
+
return new Subscriber(control(instance), () => refresh);
|
|
1338
1348
|
}, []);
|
|
1339
|
-
if (typeof arg1 == "object")
|
|
1340
|
-
|
|
1341
|
-
let keys = arg2;
|
|
1342
|
-
local.active = false;
|
|
1343
|
-
if (!keys)
|
|
1344
|
-
keys = getOwnPropertyNames(subject);
|
|
1345
|
-
for (const key of keys)
|
|
1346
|
-
if (key in arg1)
|
|
1347
|
-
subject[key] = arg1[key];
|
|
1348
|
-
waiting.add(() => local.active = true);
|
|
1349
|
-
}
|
|
1349
|
+
if (typeof arg1 == "object")
|
|
1350
|
+
local.apply(arg1, arg2);
|
|
1350
1351
|
React.useLayoutEffect(() => {
|
|
1351
1352
|
local.commit();
|
|
1352
1353
|
return () => {
|
|
@@ -1358,19 +1359,22 @@ function useNew(source, arg1, arg2) {
|
|
|
1358
1359
|
return local.proxy;
|
|
1359
1360
|
}
|
|
1360
1361
|
|
|
1361
|
-
function
|
|
1362
|
+
function useTap(source, arg1, arg2) {
|
|
1363
|
+
const instance = typeof source == "object"
|
|
1364
|
+
? source
|
|
1365
|
+
: "get" in source
|
|
1366
|
+
? source.get()
|
|
1367
|
+
: source();
|
|
1368
|
+
const deps = [uid(instance)];
|
|
1362
1369
|
const state = React.useState(0);
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
const deps = [uid(source)];
|
|
1370
|
-
const local = use(refresh => {
|
|
1371
|
-
const sub = control(source).subscribe(() => update);
|
|
1370
|
+
const local = React.useMemo(() => {
|
|
1371
|
+
const refresh = state[1].bind(null, x => x + 1);
|
|
1372
|
+
const controller = control(instance);
|
|
1373
|
+
if (typeof arg1 != "function")
|
|
1374
|
+
return new Subscriber(controller, () => refresh, arg1);
|
|
1375
|
+
const sub = new Subscriber(controller, () => update);
|
|
1372
1376
|
const spy = sub.proxy;
|
|
1373
|
-
let make = () =>
|
|
1377
|
+
let make = () => arg1.call(spy, spy, forceUpdate);
|
|
1374
1378
|
function forceUpdate(passthru) {
|
|
1375
1379
|
if (typeof passthru == "function")
|
|
1376
1380
|
passthru = passthru();
|
|
@@ -1401,7 +1405,7 @@ function useCompute(source, compute, suspend) {
|
|
|
1401
1405
|
if (typeof value == "function") {
|
|
1402
1406
|
const get = value;
|
|
1403
1407
|
sub.watch.clear();
|
|
1404
|
-
|
|
1408
|
+
arg1 = () => get();
|
|
1405
1409
|
value = get();
|
|
1406
1410
|
}
|
|
1407
1411
|
if (value instanceof Promise) {
|
|
@@ -1420,7 +1424,7 @@ function useCompute(source, compute, suspend) {
|
|
|
1420
1424
|
get() {
|
|
1421
1425
|
if (value !== undefined)
|
|
1422
1426
|
return value;
|
|
1423
|
-
if (
|
|
1427
|
+
if (arg2)
|
|
1424
1428
|
throw new Promise(res => retry = res);
|
|
1425
1429
|
return null;
|
|
1426
1430
|
}
|
|
@@ -1429,7 +1433,10 @@ function useCompute(source, compute, suspend) {
|
|
|
1429
1433
|
}, deps);
|
|
1430
1434
|
if (!local)
|
|
1431
1435
|
return null;
|
|
1432
|
-
React.useLayoutEffect(() =>
|
|
1436
|
+
React.useLayoutEffect(() => {
|
|
1437
|
+
local.commit();
|
|
1438
|
+
return () => local.release();
|
|
1439
|
+
}, deps);
|
|
1433
1440
|
return local.proxy;
|
|
1434
1441
|
}
|
|
1435
1442
|
/** Values are not equal for purposes of a refresh. */
|
|
@@ -1438,58 +1445,35 @@ const notEqual = (a, b) => (b !== a && (!Array.isArray(a) ||
|
|
|
1438
1445
|
a.length !== b.length ||
|
|
1439
1446
|
a.some((x, i) => x !== b[i])));
|
|
1440
1447
|
|
|
1441
|
-
function useTap(source, arg1, arg2) {
|
|
1442
|
-
const instance = typeof source == "object"
|
|
1443
|
-
? source
|
|
1444
|
-
: "get" in source
|
|
1445
|
-
? source.get()
|
|
1446
|
-
: useContext(source);
|
|
1447
|
-
return typeof arg1 == "function"
|
|
1448
|
-
? useCompute(instance, arg1, arg2)
|
|
1449
|
-
: useSubscribe(instance, arg1);
|
|
1450
|
-
}
|
|
1451
|
-
function useSubscribe(source, expect) {
|
|
1452
|
-
const deps = [uid(source)];
|
|
1453
|
-
const local = use(refresh => (new Subscriber(control(source), () => refresh, expect)), deps);
|
|
1454
|
-
React.useLayoutEffect(() => local.commit(), deps);
|
|
1455
|
-
return local.proxy;
|
|
1456
|
-
}
|
|
1457
|
-
|
|
1458
|
-
const Global$1 = new WeakMap();
|
|
1459
|
-
const Oops$2 = issues({
|
|
1460
|
-
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.`,
|
|
1461
|
-
AlreadyExists: (name) => `Shared instance of ${name} already exists! Consider unmounting existing, or use ${name}.reset() to force-delete it.`
|
|
1462
|
-
});
|
|
1463
1448
|
class MVC extends Model {
|
|
1464
1449
|
end(force) {
|
|
1465
1450
|
const type = this.constructor;
|
|
1466
1451
|
if (type.keepAlive && !force)
|
|
1467
1452
|
return false;
|
|
1468
1453
|
super.end();
|
|
1469
|
-
Global$1.delete(
|
|
1454
|
+
Global$1.delete(this);
|
|
1470
1455
|
return true;
|
|
1471
1456
|
}
|
|
1472
1457
|
static new(...args) {
|
|
1473
|
-
|
|
1458
|
+
const exists = Global$1.get(this);
|
|
1459
|
+
if (exists)
|
|
1474
1460
|
if (this.keepAlive)
|
|
1475
|
-
return
|
|
1461
|
+
return exists;
|
|
1476
1462
|
else
|
|
1477
|
-
throw Oops$
|
|
1463
|
+
throw Oops$3.AlreadyExists(this.name);
|
|
1478
1464
|
const instance = super.new(...args);
|
|
1479
1465
|
if (this.global)
|
|
1480
|
-
Global$1.
|
|
1466
|
+
Global$1.add(instance);
|
|
1481
1467
|
return instance;
|
|
1482
1468
|
}
|
|
1483
1469
|
static get(arg1, arg2) {
|
|
1484
|
-
let instance;
|
|
1485
1470
|
const required = arg1 === undefined || arg1 === true;
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
instance = useContext(this, required);
|
|
1471
|
+
const instance = useContext(this, required);
|
|
1472
|
+
if (typeof arg1 === "function" && instance)
|
|
1473
|
+
callback(arg1);
|
|
1474
|
+
else if (arg2)
|
|
1475
|
+
callback(arg2);
|
|
1476
|
+
return instance;
|
|
1493
1477
|
function callback(effect) {
|
|
1494
1478
|
try {
|
|
1495
1479
|
React.useLayoutEffect(() => effect(instance), []);
|
|
@@ -1501,29 +1485,18 @@ class MVC extends Model {
|
|
|
1501
1485
|
effect(undefined);
|
|
1502
1486
|
}
|
|
1503
1487
|
}
|
|
1504
|
-
if (typeof arg1 === "string")
|
|
1505
|
-
return instance[arg1];
|
|
1506
|
-
if (typeof arg1 === "function" && instance)
|
|
1507
|
-
callback(arg1);
|
|
1508
|
-
else if (arg2)
|
|
1509
|
-
callback(arg2);
|
|
1510
|
-
return instance;
|
|
1511
1488
|
}
|
|
1512
1489
|
static tap(arg1, arg2) {
|
|
1513
|
-
|
|
1514
|
-
return typeof arg1 == "function"
|
|
1515
|
-
? useCompute(instance, arg1, arg2)
|
|
1516
|
-
: useSubscribe(instance, arg1);
|
|
1490
|
+
return useTap(this, arg1, arg2);
|
|
1517
1491
|
}
|
|
1518
1492
|
static use(a, b) {
|
|
1519
|
-
return
|
|
1493
|
+
return useModel(this, a, b);
|
|
1520
1494
|
}
|
|
1521
1495
|
static meta(arg1, arg2) {
|
|
1522
|
-
return
|
|
1523
|
-
? useCompute(this, arg1, arg2)
|
|
1524
|
-
: useSubscribe(this, arg1);
|
|
1496
|
+
return useTap(() => this, arg1, arg2);
|
|
1525
1497
|
}
|
|
1526
|
-
}
|
|
1498
|
+
}
|
|
1499
|
+
FindInstruction.set(MVC, getContextForGetInstruction);
|
|
1527
1500
|
|
|
1528
1501
|
const Oops$1 = issues({
|
|
1529
1502
|
BadProps: () => `Provider expects either a render function, 'get' or 'has' props.`
|
|
@@ -1557,8 +1530,17 @@ function useNewContext(include, assign) {
|
|
|
1557
1530
|
throw Oops.NoType();
|
|
1558
1531
|
return ambient.push();
|
|
1559
1532
|
}, []);
|
|
1560
|
-
function register(key,
|
|
1561
|
-
|
|
1533
|
+
function register(key, input) {
|
|
1534
|
+
let instance;
|
|
1535
|
+
if (MVC.isTypeof(input) && input.global)
|
|
1536
|
+
instance = input.new();
|
|
1537
|
+
else if (context.register.get(key) === input)
|
|
1538
|
+
instance = typeof input == "object"
|
|
1539
|
+
? input
|
|
1540
|
+
: context.get(input);
|
|
1541
|
+
else
|
|
1542
|
+
instance = context.add(input, key);
|
|
1543
|
+
init.add(instance);
|
|
1562
1544
|
if (assign)
|
|
1563
1545
|
assignWeak(instance, assign);
|
|
1564
1546
|
}
|
|
@@ -1568,6 +1550,7 @@ function useNewContext(include, assign) {
|
|
|
1568
1550
|
entries(include).forEach(e => register(...e));
|
|
1569
1551
|
for (const instance of init) {
|
|
1570
1552
|
control(instance).state.forEach(value => {
|
|
1553
|
+
// TODO: should this run repeatedly?
|
|
1571
1554
|
if (Parent.get(value) === instance) {
|
|
1572
1555
|
context.add(value);
|
|
1573
1556
|
init.add(value);
|
|
@@ -1575,6 +1558,8 @@ function useNewContext(include, assign) {
|
|
|
1575
1558
|
});
|
|
1576
1559
|
for (const apply of getPending(instance))
|
|
1577
1560
|
apply(context);
|
|
1561
|
+
// TODO: add test to validate this.
|
|
1562
|
+
init.delete(instance);
|
|
1578
1563
|
}
|
|
1579
1564
|
React.useLayoutEffect(() => () => context.pop(), []);
|
|
1580
1565
|
return context;
|
|
@@ -1621,4 +1606,4 @@ const Debug = {
|
|
|
1621
1606
|
UPDATE
|
|
1622
1607
|
};
|
|
1623
1608
|
|
|
1624
|
-
export { Consumer, Debug, Global, MVC, MVC as Model, Provider, add, MVC as default, get,
|
|
1609
|
+
export { Consumer, Debug, Global, MVC, MVC as Model, Provider, add, MVC as default, get, ref, run, set, use };
|