@expressive/mvc 1.0.0-beta.19 → 1.0.0-beta.22
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.js +587 -515
- package/package.json +11 -6
- package/types/index.d.ts +0 -1
- package/types/instructions.d.ts +54 -14
- package/types/model.d.ts +56 -57
- package/types/types.d.ts +4 -1
package/lib/index.js
CHANGED
|
@@ -24,7 +24,7 @@ function issues(register) {
|
|
|
24
24
|
return Library;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
const Oops$
|
|
27
|
+
const Oops$9 = issues({
|
|
28
28
|
BadEffectCallback: () => `Callback for effect-callback may only return a function.`
|
|
29
29
|
});
|
|
30
30
|
const { assign, create, defineProperty, entries, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor, getOwnPropertyDescriptors, getOwnPropertySymbols, keys: keys$1, values } = Object;
|
|
@@ -53,7 +53,7 @@ function createEffect(callback) {
|
|
|
53
53
|
if (unSet instanceof Promise)
|
|
54
54
|
unSet = undefined;
|
|
55
55
|
if (unSet && typeof unSet !== "function")
|
|
56
|
-
throw Oops$
|
|
56
|
+
throw Oops$9.BadEffectCallback();
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
function createValueEffect(callback) {
|
|
@@ -69,7 +69,7 @@ function createValueEffect(callback) {
|
|
|
69
69
|
if (typeof out == "function")
|
|
70
70
|
unSet = out;
|
|
71
71
|
else
|
|
72
|
-
throw Oops$
|
|
72
|
+
throw Oops$9.BadEffectCallback();
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
75
|
function select(source, select) {
|
|
@@ -92,6 +92,179 @@ function selectRecursive(using, keys) {
|
|
|
92
92
|
return Array.from(found);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
class Subscriber {
|
|
96
|
+
constructor(parent, onUpdate) {
|
|
97
|
+
this.onUpdate = onUpdate;
|
|
98
|
+
this.active = false;
|
|
99
|
+
this.follows = {};
|
|
100
|
+
this.dependant = new Set();
|
|
101
|
+
if (!(parent instanceof Controller))
|
|
102
|
+
parent = manage(parent);
|
|
103
|
+
this.parent = parent;
|
|
104
|
+
this.source = parent.subject;
|
|
105
|
+
this.proxy = create(parent.subject);
|
|
106
|
+
define(this.proxy, LOCAL, this);
|
|
107
|
+
for (const key in parent.state)
|
|
108
|
+
this.spy(key);
|
|
109
|
+
}
|
|
110
|
+
spy(key) {
|
|
111
|
+
const { proxy, source } = this;
|
|
112
|
+
const { set } = getOwnPropertyDescriptor(source, key);
|
|
113
|
+
const intercept = () => {
|
|
114
|
+
this.follow(key);
|
|
115
|
+
delete proxy[key];
|
|
116
|
+
return proxy[key];
|
|
117
|
+
};
|
|
118
|
+
setAlias(intercept, `tap ${key}`);
|
|
119
|
+
defineProperty(proxy, key, {
|
|
120
|
+
get: intercept,
|
|
121
|
+
set,
|
|
122
|
+
configurable: true,
|
|
123
|
+
enumerable: true
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
follow(key, cb) {
|
|
127
|
+
this.follows[key] = cb || this.onUpdate;
|
|
128
|
+
}
|
|
129
|
+
commit() {
|
|
130
|
+
const { dependant, follows, parent } = this;
|
|
131
|
+
const onDone = parent.addListener(follows);
|
|
132
|
+
this.active = true;
|
|
133
|
+
dependant.forEach(x => x.commit());
|
|
134
|
+
return this.release = () => {
|
|
135
|
+
dependant.forEach(x => x.release());
|
|
136
|
+
onDone();
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const Oops$8 = issues({
|
|
142
|
+
ComputeFailed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
|
|
143
|
+
ComputedEarly: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
|
|
144
|
+
});
|
|
145
|
+
const ComputedInit = new WeakSet();
|
|
146
|
+
const ComputedInfo = new WeakMap();
|
|
147
|
+
const ComputedUsed = new WeakMap();
|
|
148
|
+
const ComputedKeys = new WeakMap();
|
|
149
|
+
function prepare(parent, key, source, setter, getter) {
|
|
150
|
+
let sub;
|
|
151
|
+
const { state, subject } = parent;
|
|
152
|
+
const info = { key, parent, priority: 1 };
|
|
153
|
+
const current = getter
|
|
154
|
+
? () => getter.call(subject, state[key], key)
|
|
155
|
+
: () => state[key];
|
|
156
|
+
const prioritize = register(parent, key, info);
|
|
157
|
+
function compute(initial) {
|
|
158
|
+
try {
|
|
159
|
+
return setter.call(sub.proxy, sub.proxy);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
Oops$8.ComputeFailed(subject, key, !!initial).warn();
|
|
163
|
+
throw err;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function update() {
|
|
167
|
+
let value;
|
|
168
|
+
try {
|
|
169
|
+
value = compute(false);
|
|
170
|
+
}
|
|
171
|
+
catch (e) {
|
|
172
|
+
console.error(e);
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
if (state[key] !== value) {
|
|
176
|
+
parent.update(key, value);
|
|
177
|
+
return value;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function create(early) {
|
|
182
|
+
sub = new Subscriber(source(), update);
|
|
183
|
+
defineProperty(state, key, {
|
|
184
|
+
value: undefined,
|
|
185
|
+
writable: true
|
|
186
|
+
});
|
|
187
|
+
defineProperty(subject, key, {
|
|
188
|
+
enumerable: true,
|
|
189
|
+
configurable: true,
|
|
190
|
+
get: current
|
|
191
|
+
});
|
|
192
|
+
try {
|
|
193
|
+
state[key] = compute(true);
|
|
194
|
+
}
|
|
195
|
+
catch (e) {
|
|
196
|
+
if (early)
|
|
197
|
+
Oops$8.ComputedEarly(key).warn();
|
|
198
|
+
throw e;
|
|
199
|
+
}
|
|
200
|
+
finally {
|
|
201
|
+
sub.commit();
|
|
202
|
+
for (const key in sub.follows)
|
|
203
|
+
prioritize(key);
|
|
204
|
+
}
|
|
205
|
+
return current();
|
|
206
|
+
}
|
|
207
|
+
setAlias(update, `try ${key}`);
|
|
208
|
+
setAlias(create, `new ${key}`);
|
|
209
|
+
setAlias(setter, `run ${key}`);
|
|
210
|
+
ComputedInit.add(create);
|
|
211
|
+
ComputedInfo.set(update, info);
|
|
212
|
+
for (const on of [state, subject])
|
|
213
|
+
defineProperty(on, key, {
|
|
214
|
+
get: create,
|
|
215
|
+
configurable: true,
|
|
216
|
+
enumerable: true
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
function register(on, key, info) {
|
|
220
|
+
let register = ComputedUsed.get(on);
|
|
221
|
+
if (!register)
|
|
222
|
+
ComputedUsed.set(on, register = new Map());
|
|
223
|
+
register.set(key, info);
|
|
224
|
+
return (key) => {
|
|
225
|
+
const peer = register.get(key);
|
|
226
|
+
if (peer && peer.priority >= info.priority)
|
|
227
|
+
info.priority = peer.priority + 1;
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
function ensure(on, keys) {
|
|
231
|
+
for (const key of keys) {
|
|
232
|
+
const desc = getOwnPropertyDescriptor(on.subject, key);
|
|
233
|
+
const getter = desc && desc.get;
|
|
234
|
+
if (ComputedInit.has(getter))
|
|
235
|
+
getter(true);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function capture(on, request) {
|
|
239
|
+
const compute = ComputedInfo.get(request);
|
|
240
|
+
const callback = request;
|
|
241
|
+
if (!compute)
|
|
242
|
+
return;
|
|
243
|
+
let pending = ComputedKeys.get(on);
|
|
244
|
+
if (!pending)
|
|
245
|
+
ComputedKeys.set(on, pending = []);
|
|
246
|
+
if (compute.parent !== on)
|
|
247
|
+
callback();
|
|
248
|
+
else {
|
|
249
|
+
const queue = pending.findIndex(peer => compute.priority > ComputedInfo.get(peer).priority);
|
|
250
|
+
pending.splice(queue + 1, 0, callback);
|
|
251
|
+
}
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
function flush(on) {
|
|
255
|
+
const handled = on.frame;
|
|
256
|
+
let pending = ComputedKeys.get(on);
|
|
257
|
+
if (pending) {
|
|
258
|
+
while (pending.length) {
|
|
259
|
+
const compute = pending.shift();
|
|
260
|
+
const { key } = ComputedInfo.get(compute);
|
|
261
|
+
if (!handled.has(key))
|
|
262
|
+
compute();
|
|
263
|
+
}
|
|
264
|
+
ComputedKeys.delete(on);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
95
268
|
const Lifecycle = {
|
|
96
269
|
WILL_RENDER: "willRender",
|
|
97
270
|
WILL_UPDATE: "willUpdate",
|
|
@@ -140,16 +313,27 @@ function eventsFor(prefix) {
|
|
|
140
313
|
}
|
|
141
314
|
|
|
142
315
|
const CONTROL = Symbol("control");
|
|
143
|
-
const CREATE = Symbol("start");
|
|
144
316
|
const LOCAL = Symbol("local");
|
|
145
317
|
const STATE = Symbol("state");
|
|
146
|
-
const Pending = new Map();
|
|
147
318
|
function manage(src) {
|
|
148
319
|
return src[CONTROL];
|
|
149
320
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
321
|
+
const Pending = new Map();
|
|
322
|
+
function apply(fn, label) {
|
|
323
|
+
const name = label || fn.name || "pending";
|
|
324
|
+
const placeholder = Symbol(`${name} instruction`);
|
|
325
|
+
function setup(key) {
|
|
326
|
+
let output = fn.call(this, key, this);
|
|
327
|
+
if (typeof output == "function") {
|
|
328
|
+
const getter = output;
|
|
329
|
+
output = Object.assign(Object.assign({}, getOwnPropertyDescriptor(this.subject, key)), { get() {
|
|
330
|
+
return getter(this[LOCAL]);
|
|
331
|
+
} });
|
|
332
|
+
}
|
|
333
|
+
if (output)
|
|
334
|
+
defineProperty(this.subject, key, output);
|
|
335
|
+
}
|
|
336
|
+
Pending.set(placeholder, setup);
|
|
153
337
|
return placeholder;
|
|
154
338
|
}
|
|
155
339
|
class Controller {
|
|
@@ -161,14 +345,10 @@ class Controller {
|
|
|
161
345
|
this.waiting = [];
|
|
162
346
|
}
|
|
163
347
|
static setup(onto) {
|
|
164
|
-
const create = onto[CREATE];
|
|
165
348
|
const control = new this(onto);
|
|
166
|
-
const after = create && create.call(onto, control);
|
|
167
349
|
defineLazy(onto, CONTROL, () => {
|
|
168
350
|
onto[STATE] = control.state;
|
|
169
351
|
control.start();
|
|
170
|
-
if (after)
|
|
171
|
-
after();
|
|
172
352
|
if (onto.didCreate)
|
|
173
353
|
onto.didCreate();
|
|
174
354
|
return control;
|
|
@@ -273,210 +453,78 @@ function keys(from, using) {
|
|
|
273
453
|
return from.select(using);
|
|
274
454
|
}
|
|
275
455
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
const { dependant, follows, parent } = this;
|
|
312
|
-
const onDone = parent.addListener(follows);
|
|
313
|
-
this.active = true;
|
|
314
|
-
dependant.forEach(x => x.commit());
|
|
315
|
-
return this.release = () => {
|
|
316
|
-
dependant.forEach(x => x.release());
|
|
317
|
-
onDone();
|
|
456
|
+
function child(from, name) {
|
|
457
|
+
return apply(function child(key) {
|
|
458
|
+
const proxyCache = new WeakMap();
|
|
459
|
+
const getCurrent = from.call(this, key);
|
|
460
|
+
function subscribe(sub) {
|
|
461
|
+
let child;
|
|
462
|
+
function start() {
|
|
463
|
+
const instance = getCurrent();
|
|
464
|
+
if (instance) {
|
|
465
|
+
child = new Subscriber(instance, sub.onUpdate);
|
|
466
|
+
if (sub.active)
|
|
467
|
+
child.commit();
|
|
468
|
+
sub.dependant.add(child);
|
|
469
|
+
proxyCache.set(sub, child.proxy);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
function refresh() {
|
|
473
|
+
if (child) {
|
|
474
|
+
child.release();
|
|
475
|
+
sub.dependant.delete(child);
|
|
476
|
+
proxyCache.set(sub, undefined);
|
|
477
|
+
child = undefined;
|
|
478
|
+
}
|
|
479
|
+
start();
|
|
480
|
+
sub.onUpdate();
|
|
481
|
+
}
|
|
482
|
+
start();
|
|
483
|
+
sub.follow(key, refresh);
|
|
484
|
+
}
|
|
485
|
+
return (local) => {
|
|
486
|
+
if (!local)
|
|
487
|
+
return getCurrent();
|
|
488
|
+
if (!proxyCache.has(local))
|
|
489
|
+
subscribe(local);
|
|
490
|
+
return proxyCache.get(local);
|
|
318
491
|
};
|
|
319
|
-
}
|
|
492
|
+
}, name || from.name);
|
|
320
493
|
}
|
|
321
494
|
|
|
322
495
|
const Oops$7 = issues({
|
|
323
|
-
|
|
324
|
-
ComputedEarly: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
|
|
496
|
+
HasPropertyUndefined: (control, property) => `${control}.${property} is marked as required for this render.`
|
|
325
497
|
});
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
if (!register)
|
|
333
|
-
ComputedUsed.set(on, register = new Map());
|
|
334
|
-
return register;
|
|
498
|
+
const useElementLifecycle = lifecycle("element");
|
|
499
|
+
function use$1(init) {
|
|
500
|
+
const [state, update] = react.useState(() => [
|
|
501
|
+
init(() => update(state.concat()))
|
|
502
|
+
]);
|
|
503
|
+
return state[0];
|
|
335
504
|
}
|
|
336
|
-
function
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
console.error(e);
|
|
360
|
-
}
|
|
361
|
-
finally {
|
|
362
|
-
if (state[key] !== value) {
|
|
363
|
-
control.update(key, value);
|
|
364
|
-
return value;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
function create(early) {
|
|
369
|
-
sub = new Subscriber(getSource(), update);
|
|
370
|
-
ComputedInfo.set(update, info);
|
|
371
|
-
defineProperty(state, key, {
|
|
372
|
-
value: undefined,
|
|
373
|
-
writable: true
|
|
374
|
-
});
|
|
375
|
-
defineProperty(subject, key, {
|
|
376
|
-
enumerable: true,
|
|
377
|
-
configurable: true,
|
|
378
|
-
get: () => state[key]
|
|
379
|
-
});
|
|
380
|
-
try {
|
|
381
|
-
return state[key] = compute(true);
|
|
382
|
-
}
|
|
383
|
-
catch (e) {
|
|
384
|
-
if (early)
|
|
385
|
-
Oops$7.ComputedEarly(key).warn();
|
|
386
|
-
throw e;
|
|
387
|
-
}
|
|
388
|
-
finally {
|
|
389
|
-
sub.commit();
|
|
390
|
-
for (const key in sub.follows) {
|
|
391
|
-
const peer = defined.get(key);
|
|
392
|
-
if (peer && peer.priority >= info.priority)
|
|
393
|
-
info.priority = peer.priority + 1;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
setAlias(update, `try ${key}`);
|
|
398
|
-
setAlias(create, `new ${key}`);
|
|
399
|
-
setAlias(getter, `run ${key}`);
|
|
400
|
-
ComputedInit.add(create);
|
|
401
|
-
for (const on of [state, subject])
|
|
402
|
-
defineProperty(on, key, {
|
|
403
|
-
get: create,
|
|
404
|
-
configurable: true,
|
|
405
|
-
enumerable: true
|
|
406
|
-
});
|
|
407
|
-
return info;
|
|
408
|
-
}
|
|
409
|
-
function ensure(on, keys) {
|
|
410
|
-
for (const key of keys) {
|
|
411
|
-
const desc = getOwnPropertyDescriptor(on.subject, key);
|
|
412
|
-
const getter = desc && desc.get;
|
|
413
|
-
if (ComputedInit.has(getter))
|
|
414
|
-
getter(true);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
function capture(on, request) {
|
|
418
|
-
const compute = ComputedInfo.get(request);
|
|
419
|
-
const callback = request;
|
|
420
|
-
if (!compute)
|
|
421
|
-
return;
|
|
422
|
-
let pending = ComputedKeys.get(on);
|
|
423
|
-
if (!pending)
|
|
424
|
-
ComputedKeys.set(on, pending = []);
|
|
425
|
-
if (compute.parent !== on)
|
|
426
|
-
callback();
|
|
427
|
-
else {
|
|
428
|
-
const queue = pending.findIndex(peer => compute.priority > ComputedInfo.get(peer).priority);
|
|
429
|
-
pending.splice(queue + 1, 0, callback);
|
|
430
|
-
}
|
|
431
|
-
return true;
|
|
432
|
-
}
|
|
433
|
-
function flush(on) {
|
|
434
|
-
const handled = on.frame;
|
|
435
|
-
let pending = ComputedKeys.get(on);
|
|
436
|
-
if (pending) {
|
|
437
|
-
while (pending.length) {
|
|
438
|
-
const compute = pending.shift();
|
|
439
|
-
const { key } = ComputedInfo.get(compute);
|
|
440
|
-
if (!handled.has(key))
|
|
441
|
-
compute();
|
|
442
|
-
}
|
|
443
|
-
ComputedKeys.delete(on);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
const Oops$6 = issues({
|
|
448
|
-
HasPropertyUndefined: (control, property) => `${control}.${property} is marked as required for this render.`
|
|
449
|
-
});
|
|
450
|
-
const useElementLifecycle = lifecycle("element");
|
|
451
|
-
function use$1(init) {
|
|
452
|
-
const [state, update] = react.useState(() => [
|
|
453
|
-
init(() => update(state.concat()))
|
|
454
|
-
]);
|
|
455
|
-
return state[0];
|
|
456
|
-
}
|
|
457
|
-
function useLazy(Type, args, callback) {
|
|
458
|
-
const instance = react.useMemo(() => {
|
|
459
|
-
const instance = Type.create(...args || []);
|
|
460
|
-
if (callback)
|
|
461
|
-
callback(instance);
|
|
462
|
-
return instance;
|
|
463
|
-
}, []);
|
|
464
|
-
react.useLayoutEffect(() => () => instance.destroy(), []);
|
|
465
|
-
return instance;
|
|
466
|
-
}
|
|
467
|
-
function useWatcher(target, focus, expected) {
|
|
468
|
-
const hook = use$1(refresh => {
|
|
469
|
-
const sub = new Subscriber(target, refresh);
|
|
470
|
-
if (focus) {
|
|
471
|
-
const src = sub.proxy;
|
|
472
|
-
const key = keys(sub.parent, focus)[0];
|
|
473
|
-
defineProperty(sub, "proxy", {
|
|
474
|
-
get() {
|
|
475
|
-
if (src[key] === undefined && expected)
|
|
476
|
-
throw Oops$6.HasPropertyUndefined(target, key);
|
|
477
|
-
return src[key];
|
|
478
|
-
}
|
|
479
|
-
});
|
|
505
|
+
function useLazy(Type, args, callback) {
|
|
506
|
+
const instance = react.useMemo(() => {
|
|
507
|
+
const instance = Type.create(...args || []);
|
|
508
|
+
if (callback)
|
|
509
|
+
callback(instance);
|
|
510
|
+
return instance;
|
|
511
|
+
}, []);
|
|
512
|
+
react.useLayoutEffect(() => () => instance.destroy(), []);
|
|
513
|
+
return instance;
|
|
514
|
+
}
|
|
515
|
+
function useWatcher(target, focus, expected) {
|
|
516
|
+
const hook = use$1(refresh => {
|
|
517
|
+
const sub = new Subscriber(target, refresh);
|
|
518
|
+
if (focus) {
|
|
519
|
+
const src = sub.proxy;
|
|
520
|
+
const key = keys(sub.parent, focus)[0];
|
|
521
|
+
defineProperty(sub, "proxy", {
|
|
522
|
+
get() {
|
|
523
|
+
if (src[key] === undefined && expected)
|
|
524
|
+
throw Oops$7.HasPropertyUndefined(target, key);
|
|
525
|
+
return src[key];
|
|
526
|
+
}
|
|
527
|
+
});
|
|
480
528
|
}
|
|
481
529
|
return sub;
|
|
482
530
|
});
|
|
@@ -506,6 +554,56 @@ function useModel(Type, args, callback) {
|
|
|
506
554
|
return hook.proxy;
|
|
507
555
|
}
|
|
508
556
|
|
|
557
|
+
const Oops$6 = issues({
|
|
558
|
+
CantAttachGlobal: (parent, child) => `Singleton '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
|
|
559
|
+
AmbientRequired: (requested, requester, key) => `Attempted to find an instance of ${requested} in context. It is required for [${requester}.${key}], but one could not be found.`
|
|
560
|
+
});
|
|
561
|
+
const PendingContext = new WeakMap();
|
|
562
|
+
const ContextWasUsed = new WeakMap();
|
|
563
|
+
function tap(type, argument) {
|
|
564
|
+
return child(function tap(key) {
|
|
565
|
+
return pendingAccess(this.subject, type, key, argument);
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
function pendingAccess(from, type, key, argument) {
|
|
569
|
+
if ("current" in type)
|
|
570
|
+
return () => type.current;
|
|
571
|
+
if ("current" in from.constructor)
|
|
572
|
+
throw Oops$6.CantAttachGlobal(from, type.name);
|
|
573
|
+
let instance;
|
|
574
|
+
getPending(from).push(context => {
|
|
575
|
+
instance = context.get(type);
|
|
576
|
+
if (typeof argument == "function") {
|
|
577
|
+
if (argument(instance) === false)
|
|
578
|
+
instance = undefined;
|
|
579
|
+
}
|
|
580
|
+
else if (!instance && argument)
|
|
581
|
+
throw Oops$6.AmbientRequired(type.name, from, key);
|
|
582
|
+
});
|
|
583
|
+
return () => instance;
|
|
584
|
+
}
|
|
585
|
+
function usePeerContext(subject) {
|
|
586
|
+
if (ContextWasUsed.has(subject)) {
|
|
587
|
+
if (ContextWasUsed.get(subject))
|
|
588
|
+
useLookup();
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
const pending = PendingContext.get(subject);
|
|
592
|
+
if (pending) {
|
|
593
|
+
const local = useLookup();
|
|
594
|
+
for (const init of pending)
|
|
595
|
+
init(local);
|
|
596
|
+
PendingContext.delete(subject);
|
|
597
|
+
}
|
|
598
|
+
ContextWasUsed.set(subject, !!pending);
|
|
599
|
+
}
|
|
600
|
+
function getPending(subject) {
|
|
601
|
+
let pending = PendingContext.get(subject);
|
|
602
|
+
if (!pending)
|
|
603
|
+
PendingContext.set(subject, pending = []);
|
|
604
|
+
return pending;
|
|
605
|
+
}
|
|
606
|
+
|
|
509
607
|
class Lookup {
|
|
510
608
|
constructor() {
|
|
511
609
|
this.table = new Map();
|
|
@@ -632,62 +730,73 @@ function Provider(props) {
|
|
|
632
730
|
}
|
|
633
731
|
|
|
634
732
|
const Oops$4 = issues({
|
|
635
|
-
CantAttachGlobal: (parent, child) => `Singleton '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
|
|
636
|
-
AmbientRequired: (requested, requester, key) => `Attempted to find an instance of ${requested} in context. It is required for [${requester}.${key}], but one could not be found.`
|
|
637
|
-
});
|
|
638
|
-
const PendingContext = new WeakMap();
|
|
639
|
-
const ContextWasUsed = new WeakMap();
|
|
640
|
-
const tap = (type, argument) => attach(function tap(key) {
|
|
641
|
-
return pendingAccess(this.subject, type, key, argument);
|
|
642
|
-
});
|
|
643
|
-
function pendingAccess(from, type, key, argument) {
|
|
644
|
-
if ("current" in type)
|
|
645
|
-
return () => type.current;
|
|
646
|
-
if ("current" in from.constructor)
|
|
647
|
-
throw Oops$4.CantAttachGlobal(from, type.name);
|
|
648
|
-
let instance;
|
|
649
|
-
getPending(from).push(context => {
|
|
650
|
-
instance = context.get(type);
|
|
651
|
-
if (typeof argument == "function") {
|
|
652
|
-
if (argument(instance) === false)
|
|
653
|
-
instance = undefined;
|
|
654
|
-
}
|
|
655
|
-
else if (!instance && argument)
|
|
656
|
-
throw Oops$4.AmbientRequired(type.name, from, key);
|
|
657
|
-
});
|
|
658
|
-
return () => instance;
|
|
659
|
-
}
|
|
660
|
-
function usePeerContext(subject) {
|
|
661
|
-
if (ContextWasUsed.has(subject)) {
|
|
662
|
-
if (ContextWasUsed.get(subject))
|
|
663
|
-
useLookup();
|
|
664
|
-
return;
|
|
665
|
-
}
|
|
666
|
-
const pending = PendingContext.get(subject);
|
|
667
|
-
if (pending) {
|
|
668
|
-
const local = useLookup();
|
|
669
|
-
for (const init of pending)
|
|
670
|
-
init(local);
|
|
671
|
-
PendingContext.delete(subject);
|
|
672
|
-
}
|
|
673
|
-
ContextWasUsed.set(subject, !!pending);
|
|
674
|
-
}
|
|
675
|
-
function getPending(subject) {
|
|
676
|
-
let pending = PendingContext.get(subject);
|
|
677
|
-
if (!pending)
|
|
678
|
-
PendingContext.set(subject, pending = []);
|
|
679
|
-
return pending;
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
const Oops$3 = issues({
|
|
683
733
|
StrictUpdate: (expected) => `Strict update() did ${expected ? "not " : ""}find pending updates.`,
|
|
684
734
|
NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
|
|
685
735
|
});
|
|
686
|
-
|
|
736
|
+
const useComponentLifecycle = lifecycle("component");
|
|
737
|
+
class Model {
|
|
687
738
|
constructor() {
|
|
688
|
-
Controller.setup(this);
|
|
739
|
+
const control = Controller.setup(this);
|
|
689
740
|
define(this, "get", this);
|
|
690
741
|
define(this, "set", this);
|
|
742
|
+
defer(control, "on");
|
|
743
|
+
defer(control, "effect");
|
|
744
|
+
control.include(() => {
|
|
745
|
+
delete this.on;
|
|
746
|
+
delete this.effect;
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
tap(path, expect) {
|
|
750
|
+
const proxy = useWatcher(this, path, expect);
|
|
751
|
+
this.update("willRender", true);
|
|
752
|
+
return proxy;
|
|
753
|
+
}
|
|
754
|
+
tag(id) {
|
|
755
|
+
return useSubscriber(this, id);
|
|
756
|
+
}
|
|
757
|
+
on(subset, handler, squash, once) {
|
|
758
|
+
const control = manage(this);
|
|
759
|
+
const set = keys(control, subset);
|
|
760
|
+
const batch = {};
|
|
761
|
+
const remove = control.addListener(batch);
|
|
762
|
+
const callback = squash
|
|
763
|
+
? handler.bind(this)
|
|
764
|
+
: (frame) => {
|
|
765
|
+
for (const key of frame)
|
|
766
|
+
if (set.includes(key))
|
|
767
|
+
handler.call(this, control.state[key], key);
|
|
768
|
+
};
|
|
769
|
+
const handle = once
|
|
770
|
+
? (k) => { remove(); callback(k); }
|
|
771
|
+
: callback;
|
|
772
|
+
for (const key of set)
|
|
773
|
+
batch[key] = handle;
|
|
774
|
+
ensure(control, set);
|
|
775
|
+
return remove;
|
|
776
|
+
}
|
|
777
|
+
once(select, callback, squash) {
|
|
778
|
+
if (callback)
|
|
779
|
+
return this.on(select, callback, squash, true);
|
|
780
|
+
else
|
|
781
|
+
return new Promise(resolve => {
|
|
782
|
+
this.on(select, resolve, true, true);
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
effect(callback, select) {
|
|
786
|
+
const control = manage(this);
|
|
787
|
+
let target = this;
|
|
788
|
+
const effect = createEffect(callback);
|
|
789
|
+
const invoke = () => effect.call(target, target);
|
|
790
|
+
if (select) {
|
|
791
|
+
invoke();
|
|
792
|
+
return this.on(select, invoke, true);
|
|
793
|
+
}
|
|
794
|
+
else {
|
|
795
|
+
const sub = new Subscriber(control, invoke);
|
|
796
|
+
target = sub.proxy;
|
|
797
|
+
invoke();
|
|
798
|
+
return sub.commit();
|
|
799
|
+
}
|
|
691
800
|
}
|
|
692
801
|
import(from, subset) {
|
|
693
802
|
for (const key of keys(manage(this), subset))
|
|
@@ -707,15 +816,18 @@ class State {
|
|
|
707
816
|
arg = select(this, arg);
|
|
708
817
|
if (typeof arg == "boolean") {
|
|
709
818
|
if (!control.pending === arg)
|
|
710
|
-
return Promise.reject(Oops$
|
|
819
|
+
return Promise.reject(Oops$4.StrictUpdate(arg));
|
|
711
820
|
}
|
|
712
821
|
else if (arg) {
|
|
713
822
|
control.update(arg);
|
|
714
|
-
if (1 in arguments && arg in this)
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
823
|
+
if (1 in arguments && arg in this) {
|
|
824
|
+
const method = this[arg];
|
|
825
|
+
if (typeof method == "function")
|
|
826
|
+
if (typeof tag != "boolean")
|
|
827
|
+
method.call(this, tag);
|
|
828
|
+
else if (tag)
|
|
829
|
+
method.call(this);
|
|
830
|
+
}
|
|
719
831
|
}
|
|
720
832
|
return {
|
|
721
833
|
then(callback) {
|
|
@@ -725,7 +837,7 @@ class State {
|
|
|
725
837
|
else
|
|
726
838
|
callback(false);
|
|
727
839
|
else
|
|
728
|
-
throw Oops$
|
|
840
|
+
throw Oops$4.NoChaining();
|
|
729
841
|
}
|
|
730
842
|
};
|
|
731
843
|
}
|
|
@@ -735,28 +847,14 @@ class State {
|
|
|
735
847
|
toString() {
|
|
736
848
|
return this.constructor.name;
|
|
737
849
|
}
|
|
738
|
-
tap(path, expect) {
|
|
739
|
-
return useWatcher(this, path, expect);
|
|
740
|
-
}
|
|
741
850
|
static create(...args) {
|
|
742
851
|
const instance = new this(...args);
|
|
743
852
|
manage(instance);
|
|
744
853
|
return instance;
|
|
745
854
|
}
|
|
746
855
|
static new(args, callback) {
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
static use(args, callback) {
|
|
750
|
-
return useModel(this, args, callback);
|
|
751
|
-
}
|
|
752
|
-
static uses(props, only) {
|
|
753
|
-
return this.use([], instance => {
|
|
754
|
-
instance.import(props, only);
|
|
755
|
-
});
|
|
756
|
-
}
|
|
757
|
-
static using(props, only) {
|
|
758
|
-
const instance = this.use([]);
|
|
759
|
-
instance.import(props, only);
|
|
856
|
+
const instance = useLazy(this, args, callback);
|
|
857
|
+
instance.update("willRender", true);
|
|
760
858
|
return instance;
|
|
761
859
|
}
|
|
762
860
|
static find(strict) {
|
|
@@ -771,74 +869,10 @@ class State {
|
|
|
771
869
|
instance);
|
|
772
870
|
}
|
|
773
871
|
static tap(key, expect) {
|
|
774
|
-
return
|
|
775
|
-
}
|
|
776
|
-
static isTypeof(maybe) {
|
|
777
|
-
return (typeof maybe == "function" &&
|
|
778
|
-
maybe.prototype instanceof this);
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
State.CONTROL = CONTROL;
|
|
782
|
-
State.STATE = STATE;
|
|
783
|
-
State.INIT = CREATE;
|
|
784
|
-
State.LOCAL = LOCAL;
|
|
785
|
-
|
|
786
|
-
const useComponentLifecycle = lifecycle("component");
|
|
787
|
-
class Model extends State {
|
|
788
|
-
[CREATE](using) {
|
|
789
|
-
defer(using, "on");
|
|
790
|
-
defer(using, "effect");
|
|
791
|
-
return () => {
|
|
792
|
-
delete this.on;
|
|
793
|
-
delete this.effect;
|
|
794
|
-
};
|
|
872
|
+
return this.find(true).tap(key, expect);
|
|
795
873
|
}
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
const set = keys(control, subset);
|
|
799
|
-
const batch = {};
|
|
800
|
-
const remove = control.addListener(batch);
|
|
801
|
-
const callback = squash
|
|
802
|
-
? handler.bind(this)
|
|
803
|
-
: (frame) => {
|
|
804
|
-
for (const key of frame)
|
|
805
|
-
if (set.includes(key))
|
|
806
|
-
handler.call(this, control.state[key], key);
|
|
807
|
-
};
|
|
808
|
-
const handle = once
|
|
809
|
-
? (k) => { remove(); callback(k); }
|
|
810
|
-
: callback;
|
|
811
|
-
for (const key of set)
|
|
812
|
-
batch[key] = handle;
|
|
813
|
-
ensure(control, set);
|
|
814
|
-
return remove;
|
|
815
|
-
}
|
|
816
|
-
once(select, callback, squash) {
|
|
817
|
-
if (callback)
|
|
818
|
-
return this.on(select, callback, squash, true);
|
|
819
|
-
else
|
|
820
|
-
return new Promise(resolve => {
|
|
821
|
-
this.on(select, resolve, true, true);
|
|
822
|
-
});
|
|
823
|
-
}
|
|
824
|
-
effect(callback, select) {
|
|
825
|
-
const control = manage(this);
|
|
826
|
-
let target = this;
|
|
827
|
-
const effect = createEffect(callback);
|
|
828
|
-
const invoke = () => effect.call(target, target);
|
|
829
|
-
if (select) {
|
|
830
|
-
invoke();
|
|
831
|
-
return this.on(select, invoke, true);
|
|
832
|
-
}
|
|
833
|
-
else {
|
|
834
|
-
const sub = new Subscriber(control, invoke);
|
|
835
|
-
target = sub.proxy;
|
|
836
|
-
invoke();
|
|
837
|
-
return sub.commit();
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
tag(id) {
|
|
841
|
-
return useSubscriber(this, id);
|
|
874
|
+
static tag(id) {
|
|
875
|
+
return this.find(true).tag(id);
|
|
842
876
|
}
|
|
843
877
|
static use(args, callback) {
|
|
844
878
|
const instance = useModel(this, args, callback);
|
|
@@ -846,13 +880,27 @@ class Model extends State {
|
|
|
846
880
|
usePeerContext(instance.get);
|
|
847
881
|
return instance;
|
|
848
882
|
}
|
|
849
|
-
static
|
|
850
|
-
return
|
|
883
|
+
static uses(props, only) {
|
|
884
|
+
return this.use([], instance => {
|
|
885
|
+
instance.import(props, only);
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
static using(props, only) {
|
|
889
|
+
const instance = this.use([]);
|
|
890
|
+
instance.import(props, only);
|
|
891
|
+
return instance;
|
|
851
892
|
}
|
|
852
893
|
static meta(path) {
|
|
853
894
|
return useWatcher(this, path);
|
|
854
895
|
}
|
|
896
|
+
static isTypeof(maybe) {
|
|
897
|
+
return (typeof maybe == "function" &&
|
|
898
|
+
maybe.prototype instanceof this);
|
|
899
|
+
}
|
|
855
900
|
}
|
|
901
|
+
Model.CONTROL = CONTROL;
|
|
902
|
+
Model.STATE = STATE;
|
|
903
|
+
Model.LOCAL = LOCAL;
|
|
856
904
|
defineLazy(Model, CONTROL, function () {
|
|
857
905
|
return new Controller(this).start();
|
|
858
906
|
});
|
|
@@ -868,194 +916,218 @@ function defer(on, method) {
|
|
|
868
916
|
};
|
|
869
917
|
}
|
|
870
918
|
|
|
919
|
+
const Parent = new WeakMap();
|
|
920
|
+
const Oops$3 = issues({
|
|
921
|
+
ParentRequired: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
|
|
922
|
+
UnexpectedParent: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
|
|
923
|
+
UndefinedNotAllowed: (key) => `Child property ${key} may not be undefined.`,
|
|
924
|
+
BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`
|
|
925
|
+
});
|
|
926
|
+
function bootstrap(object) {
|
|
927
|
+
const breakdown = getOwnPropertyDescriptors(object);
|
|
928
|
+
const control = new Model();
|
|
929
|
+
for (const key in breakdown)
|
|
930
|
+
defineProperty(control, key, breakdown[key]);
|
|
931
|
+
return control;
|
|
932
|
+
}
|
|
933
|
+
function use(input, argument) {
|
|
934
|
+
return child(function use(key) {
|
|
935
|
+
let instance;
|
|
936
|
+
const update = (next) => {
|
|
937
|
+
instance =
|
|
938
|
+
next instanceof Model ? next :
|
|
939
|
+
next && bootstrap(next);
|
|
940
|
+
if (instance) {
|
|
941
|
+
Parent.set(instance, this.subject);
|
|
942
|
+
manage(instance);
|
|
943
|
+
}
|
|
944
|
+
if (typeof argument == "function")
|
|
945
|
+
argument(instance);
|
|
946
|
+
else if (!instance && argument !== false)
|
|
947
|
+
throw Oops$3.UndefinedNotAllowed(key);
|
|
948
|
+
};
|
|
949
|
+
if (input) {
|
|
950
|
+
instance =
|
|
951
|
+
Model.isTypeof(input) ? new input() :
|
|
952
|
+
input instanceof Model ? input :
|
|
953
|
+
typeof input === "function" ? input() :
|
|
954
|
+
typeof input === "object" ? bootstrap(input) :
|
|
955
|
+
Oops$3.BadArgument(typeof input).throw();
|
|
956
|
+
if (instance)
|
|
957
|
+
update(instance);
|
|
958
|
+
}
|
|
959
|
+
else
|
|
960
|
+
argument = false;
|
|
961
|
+
this.manage(key, instance, update);
|
|
962
|
+
return () => instance;
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
function parent(Expects, required) {
|
|
966
|
+
return apply(function parent() {
|
|
967
|
+
const child = this.subject;
|
|
968
|
+
const expected = Expects.name;
|
|
969
|
+
const value = Parent.get(this.subject);
|
|
970
|
+
if (!value) {
|
|
971
|
+
if (required)
|
|
972
|
+
throw Oops$3.ParentRequired(expected, child);
|
|
973
|
+
}
|
|
974
|
+
else if (!(value instanceof Expects))
|
|
975
|
+
throw Oops$3.UnexpectedParent(expected, child, value);
|
|
976
|
+
return { value };
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
|
|
871
980
|
const Oops$2 = issues({
|
|
872
981
|
DuplicateAction: (key) => `Invoked action ${key} but one is already active.`,
|
|
873
982
|
BadComputedSource: (model, property, got) => `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
|
|
874
|
-
PeerNotAllowed: (model, property) =>
|
|
983
|
+
PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`
|
|
875
984
|
});
|
|
876
|
-
|
|
877
|
-
let output = fn.call(this, key, this);
|
|
878
|
-
if (typeof output == "function") {
|
|
879
|
-
const getter = output;
|
|
880
|
-
output = Object.assign(Object.assign({}, getOwnPropertyDescriptor(this.subject, key)), { get() {
|
|
881
|
-
return getter(this[LOCAL]);
|
|
882
|
-
} });
|
|
883
|
-
}
|
|
884
|
-
if (output)
|
|
885
|
-
defineProperty(this.subject, key, output);
|
|
886
|
-
}, name || fn.name || "pending");
|
|
887
|
-
const ref = (cb) => set(function ref(key) {
|
|
985
|
+
function createRef(key, cb) {
|
|
888
986
|
const refObjectFunction = this.setter(key, cb && createValueEffect(cb));
|
|
889
987
|
defineProperty(refObjectFunction, "current", {
|
|
890
988
|
set: refObjectFunction,
|
|
891
989
|
get: () => this.state[key]
|
|
892
990
|
});
|
|
893
|
-
return
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
define(source, key, get());
|
|
907
|
-
});
|
|
908
|
-
const lazy = (value) => set(function lazy(key) {
|
|
909
|
-
const source = this.subject;
|
|
910
|
-
source[key] = value;
|
|
911
|
-
defineProperty(this.state, key, {
|
|
912
|
-
get: () => source[key]
|
|
991
|
+
return refObjectFunction;
|
|
992
|
+
}
|
|
993
|
+
function ref(arg) {
|
|
994
|
+
return apply(function ref(key) {
|
|
995
|
+
let value = {};
|
|
996
|
+
if (typeof arg == "object") {
|
|
997
|
+
const source = manage(arg);
|
|
998
|
+
for (const key in source.state)
|
|
999
|
+
defineLazy(value, key, createRef.bind(source, key));
|
|
1000
|
+
}
|
|
1001
|
+
else
|
|
1002
|
+
value = createRef.call(this, key, arg);
|
|
1003
|
+
return { value };
|
|
913
1004
|
});
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
1005
|
+
}
|
|
1006
|
+
function on(initial, cb) {
|
|
1007
|
+
return apply(function on(key) {
|
|
1008
|
+
this.manage(key, initial, cb && createValueEffect(cb));
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
function memo(factory, defer) {
|
|
1012
|
+
return apply(function memo(key) {
|
|
1013
|
+
const source = this.subject;
|
|
1014
|
+
const get = () => factory.call(source);
|
|
1015
|
+
if (defer)
|
|
1016
|
+
defineLazy(source, key, get);
|
|
1017
|
+
else
|
|
1018
|
+
define(source, key, get());
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
function lazy(value) {
|
|
1022
|
+
return apply(function lazy(key) {
|
|
1023
|
+
const source = this.subject;
|
|
1024
|
+
source[key] = value;
|
|
1025
|
+
defineProperty(this.state, key, {
|
|
1026
|
+
get: () => source[key]
|
|
1027
|
+
});
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
function act(task) {
|
|
1031
|
+
return apply(function act(key) {
|
|
1032
|
+
let pending = false;
|
|
1033
|
+
const invoke = (...args) => {
|
|
1034
|
+
if (pending)
|
|
1035
|
+
return Promise.reject(Oops$2.DuplicateAction(key));
|
|
1036
|
+
pending = true;
|
|
926
1037
|
this.update(key);
|
|
1038
|
+
return new Promise(res => {
|
|
1039
|
+
res(task.apply(this.subject, args));
|
|
1040
|
+
}).finally(() => {
|
|
1041
|
+
pending = false;
|
|
1042
|
+
this.update(key);
|
|
1043
|
+
});
|
|
1044
|
+
};
|
|
1045
|
+
setAlias(invoke, `run ${key}`);
|
|
1046
|
+
defineProperty(invoke, "active", {
|
|
1047
|
+
get: () => pending
|
|
927
1048
|
});
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
1049
|
+
return {
|
|
1050
|
+
value: invoke,
|
|
1051
|
+
writable: false
|
|
1052
|
+
};
|
|
932
1053
|
});
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
};
|
|
937
|
-
});
|
|
938
|
-
function from(source, fn) {
|
|
939
|
-
return set(function from(key) {
|
|
1054
|
+
}
|
|
1055
|
+
function from(source, setter, getter) {
|
|
1056
|
+
return apply(function from(key) {
|
|
940
1057
|
const { subject } = this;
|
|
941
|
-
let
|
|
942
|
-
|
|
943
|
-
// Easy mistake, using a peer - will always be unresolved.
|
|
1058
|
+
let getSource;
|
|
1059
|
+
// Easy mistake, using a peer, will always be unresolved.
|
|
944
1060
|
if (typeof source == "symbol")
|
|
945
1061
|
throw Oops$2.PeerNotAllowed(subject, key);
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1062
|
+
// replace source controller in-case different
|
|
1063
|
+
if (typeof source == "object")
|
|
1064
|
+
getSource = () => manage(source);
|
|
1065
|
+
// specifically an arrow function (getter factory)
|
|
1066
|
+
else if (!source.prototype) {
|
|
1067
|
+
setter = source.call(subject, key);
|
|
1068
|
+
getSource = () => this;
|
|
950
1069
|
}
|
|
951
|
-
// is an arrow function (getter factory)
|
|
952
|
-
else if (!source.prototype)
|
|
953
|
-
getter = source(key);
|
|
954
1070
|
// is a peer Model (constructor)
|
|
955
|
-
else if (Model.isTypeof(source))
|
|
956
|
-
getter = fn;
|
|
1071
|
+
else if (Model.isTypeof(source))
|
|
957
1072
|
getSource = pendingAccess(subject, source, key, true);
|
|
958
|
-
|
|
959
|
-
// Vanilla function is to ambiguous so not allowed.
|
|
1073
|
+
// Regular function is to ambiguous so not allowed.
|
|
960
1074
|
else
|
|
961
1075
|
throw Oops$2.BadComputedSource(subject, key, source);
|
|
962
|
-
prepare(this, key,
|
|
1076
|
+
prepare(this, key, getSource, setter, getter);
|
|
963
1077
|
});
|
|
964
1078
|
}
|
|
965
1079
|
|
|
966
|
-
const attach = (from, name) => set(function attach(key) {
|
|
967
|
-
const proxyCache = new WeakMap();
|
|
968
|
-
const getCurrent = from.call(this, key);
|
|
969
|
-
function subscribe(sub) {
|
|
970
|
-
let child;
|
|
971
|
-
function start() {
|
|
972
|
-
const instance = getCurrent();
|
|
973
|
-
if (instance) {
|
|
974
|
-
child = new Subscriber(instance, sub.onUpdate);
|
|
975
|
-
if (sub.active)
|
|
976
|
-
child.commit();
|
|
977
|
-
sub.dependant.add(child);
|
|
978
|
-
proxyCache.set(sub, child.proxy);
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
function refresh() {
|
|
982
|
-
if (child) {
|
|
983
|
-
child.release();
|
|
984
|
-
sub.dependant.delete(child);
|
|
985
|
-
proxyCache.set(sub, undefined);
|
|
986
|
-
child = undefined;
|
|
987
|
-
}
|
|
988
|
-
start();
|
|
989
|
-
sub.onUpdate();
|
|
990
|
-
}
|
|
991
|
-
start();
|
|
992
|
-
sub.follow(key, refresh);
|
|
993
|
-
}
|
|
994
|
-
return (local) => {
|
|
995
|
-
if (!local)
|
|
996
|
-
return getCurrent();
|
|
997
|
-
if (!proxyCache.has(local))
|
|
998
|
-
subscribe(local);
|
|
999
|
-
return proxyCache.get(local);
|
|
1000
|
-
};
|
|
1001
|
-
}, name || from.name);
|
|
1002
|
-
|
|
1003
|
-
const Parent = new WeakMap();
|
|
1004
1080
|
const Oops$1 = issues({
|
|
1005
|
-
|
|
1006
|
-
UnexpectedParent: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
|
|
1007
|
-
UndefinedNotAllowed: (key) => `Child property ${key} may not be undefined.`,
|
|
1008
|
-
BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`
|
|
1081
|
+
ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
|
|
1009
1082
|
});
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1083
|
+
/**
|
|
1084
|
+
* Create suspense promise also interpretable as an error.
|
|
1085
|
+
* React will handle it but other contexts probably won't.
|
|
1086
|
+
*/
|
|
1087
|
+
function suspense(model, key) {
|
|
1088
|
+
const { message, stack } = Oops$1.ValueNotReady(model, key);
|
|
1089
|
+
const promise = new Promise(resolve => {
|
|
1090
|
+
const release = model.on(key, (value) => {
|
|
1091
|
+
if (value !== undefined)
|
|
1092
|
+
release(), resolve();
|
|
1093
|
+
});
|
|
1094
|
+
});
|
|
1095
|
+
return Object.assign(promise, { message, stack });
|
|
1016
1096
|
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
instance =
|
|
1021
|
-
next instanceof Model ? next :
|
|
1022
|
-
next && bootstrap(next);
|
|
1023
|
-
if (instance) {
|
|
1024
|
-
Parent.set(instance, this.subject);
|
|
1025
|
-
manage(instance);
|
|
1026
|
-
}
|
|
1027
|
-
if (typeof argument == "function")
|
|
1028
|
-
argument(instance);
|
|
1029
|
-
else if (!instance && argument !== false)
|
|
1030
|
-
throw Oops$1.UndefinedNotAllowed(key);
|
|
1031
|
-
};
|
|
1032
|
-
if (input) {
|
|
1033
|
-
instance =
|
|
1034
|
-
Model.isTypeof(input) ? new input() :
|
|
1035
|
-
input instanceof Model ? input :
|
|
1036
|
-
typeof input === "function" ? input() :
|
|
1037
|
-
typeof input === "object" ? bootstrap(input) :
|
|
1038
|
-
Oops$1.BadArgument(typeof input).throw();
|
|
1039
|
-
if (instance)
|
|
1040
|
-
update(instance);
|
|
1041
|
-
}
|
|
1097
|
+
function pending(source, compute) {
|
|
1098
|
+
if (typeof source == "function" && !source.prototype)
|
|
1099
|
+
return suspendForAsync(source);
|
|
1042
1100
|
else
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1101
|
+
return from(source, compute, function getter(value, key) {
|
|
1102
|
+
if (value === undefined)
|
|
1103
|
+
throw suspense(this, key);
|
|
1104
|
+
return value;
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
function suspendForAsync(source) {
|
|
1108
|
+
return apply(function suspense(key) {
|
|
1109
|
+
let waiting = true;
|
|
1110
|
+
let output;
|
|
1111
|
+
let error;
|
|
1112
|
+
return () => {
|
|
1113
|
+
if (waiting) {
|
|
1114
|
+
const issue = Oops$1.ValueNotReady(this.subject, key);
|
|
1115
|
+
const promise = source
|
|
1116
|
+
.call(this.subject, key)
|
|
1117
|
+
.catch(err => error = err)
|
|
1118
|
+
.then((value) => output = value)
|
|
1119
|
+
.finally(() => waiting = false);
|
|
1120
|
+
throw Object.assign(promise, {
|
|
1121
|
+
message: issue.message,
|
|
1122
|
+
stack: issue.stack
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
if (error)
|
|
1126
|
+
throw error;
|
|
1127
|
+
return output;
|
|
1128
|
+
};
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1059
1131
|
|
|
1060
1132
|
const Oops = issues({
|
|
1061
1133
|
GlobalDoesNotExist: (name) => `Tried to access singleton ${name}, but none exist! Did you forget to initialize?\nCall ${name}.create() before attempting to access, or consider using ${name}.use() instead.`,
|
|
@@ -1102,16 +1174,16 @@ exports.Consumer = Consumer;
|
|
|
1102
1174
|
exports.Model = Model;
|
|
1103
1175
|
exports.Provider = Provider;
|
|
1104
1176
|
exports.Singleton = Singleton;
|
|
1105
|
-
exports.State = State;
|
|
1106
1177
|
exports.act = act;
|
|
1107
|
-
exports.
|
|
1178
|
+
exports.apply = apply;
|
|
1179
|
+
exports.child = child;
|
|
1108
1180
|
exports["default"] = Model;
|
|
1109
1181
|
exports.from = from;
|
|
1110
1182
|
exports.lazy = lazy;
|
|
1111
1183
|
exports.memo = memo;
|
|
1112
1184
|
exports.on = on;
|
|
1113
1185
|
exports.parent = parent;
|
|
1186
|
+
exports.pending = pending;
|
|
1114
1187
|
exports.ref = ref;
|
|
1115
|
-
exports.set = set;
|
|
1116
1188
|
exports.tap = tap;
|
|
1117
1189
|
exports.use = use;
|