@expressive/mvc 0.2.6 → 0.4.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 +54 -74
- package/lib/index.esm.js +730 -556
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +730 -555
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -59,7 +59,7 @@ function suspend(source, key) {
|
|
|
59
59
|
const release = source.addListener(forKey => {
|
|
60
60
|
if (forKey == key)
|
|
61
61
|
return () => {
|
|
62
|
-
if (source.state
|
|
62
|
+
if (source.state.get(key) !== undefined) {
|
|
63
63
|
release();
|
|
64
64
|
resolve();
|
|
65
65
|
}
|
|
@@ -140,240 +140,37 @@ function createValueEffect(callback) {
|
|
|
140
140
|
};
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
UPDATE.delete(subject);
|
|
149
|
-
}, 0);
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
function getUpdate(subject) {
|
|
153
|
-
return UPDATE.get(subject);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const CONTROL = Symbol("CONTROL");
|
|
157
|
-
const WHY = Symbol("UPDATE");
|
|
158
|
-
const LOCAL = Symbol("LOCAL");
|
|
159
|
-
const STATE = Symbol("STATE");
|
|
160
|
-
class Model {
|
|
161
|
-
constructor() {
|
|
162
|
-
define(this, CONTROL, new Controller(this));
|
|
163
|
-
define(this, "get", this);
|
|
164
|
-
define(this, "is", this);
|
|
165
|
-
defineLazy(this, "set", () => {
|
|
166
|
-
const controller = control(this);
|
|
167
|
-
const assign = (key, value) => {
|
|
168
|
-
controller.update(key, value);
|
|
169
|
-
};
|
|
170
|
-
for (const key of getOwnPropertyNames(controller.proxy))
|
|
171
|
-
define(assign, key, controller.ref(key));
|
|
172
|
-
return assign;
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
get [STATE]() {
|
|
176
|
-
return this[CONTROL].state;
|
|
177
|
-
}
|
|
178
|
-
get [WHY]() {
|
|
179
|
-
return UPDATE.get(this);
|
|
180
|
-
}
|
|
181
|
-
on(select, handler, squash, once) {
|
|
182
|
-
return control(this, control => {
|
|
183
|
-
if (typeof select == "function")
|
|
184
|
-
return control.addListener(select);
|
|
185
|
-
const selection = typeof select == "string" ? [select] :
|
|
186
|
-
!select.length ? getOwnPropertyNames(control.state) :
|
|
187
|
-
select;
|
|
188
|
-
ensure(control, selection);
|
|
189
|
-
const callback = squash
|
|
190
|
-
? () => {
|
|
191
|
-
handler.call(this, getUpdate(this));
|
|
192
|
-
}
|
|
193
|
-
: () => {
|
|
194
|
-
getUpdate(this)
|
|
195
|
-
.filter(k => selection.includes(k))
|
|
196
|
-
.forEach(k => {
|
|
197
|
-
handler.call(this, control.state[k], k);
|
|
198
|
-
});
|
|
199
|
-
};
|
|
200
|
-
const onEvent = once
|
|
201
|
-
? () => {
|
|
202
|
-
remove();
|
|
203
|
-
callback();
|
|
204
|
-
}
|
|
205
|
-
: callback;
|
|
206
|
-
const remove = control.addListener(key => {
|
|
207
|
-
if (selection.includes(key))
|
|
208
|
-
return onEvent;
|
|
209
|
-
});
|
|
210
|
-
return remove;
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
once(select, callback, squash) {
|
|
214
|
-
if (callback)
|
|
215
|
-
return this.on(select, callback, squash, true);
|
|
216
|
-
return new Promise(resolve => {
|
|
217
|
-
this.on(select, resolve, true, true);
|
|
218
|
-
});
|
|
143
|
+
function ensure(subject, cb) {
|
|
144
|
+
let control = subject[CONTROL];
|
|
145
|
+
if (!control) {
|
|
146
|
+
control = new Controller(subject);
|
|
147
|
+
define(subject, CONTROL, control);
|
|
219
148
|
}
|
|
220
|
-
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
let
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
if (busy)
|
|
227
|
-
return;
|
|
228
|
-
const output = mayRetry(() => {
|
|
229
|
-
effect.call(inject, inject);
|
|
230
|
-
});
|
|
231
|
-
if (output instanceof Promise) {
|
|
232
|
-
output.finally(() => busy = false);
|
|
233
|
-
busy = true;
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
if (!select) {
|
|
237
|
-
const sub = new Subscriber(control, () => invoke);
|
|
238
|
-
inject = sub.proxy;
|
|
239
|
-
invoke();
|
|
240
|
-
return sub.commit();
|
|
241
|
-
}
|
|
242
|
-
invoke();
|
|
243
|
-
if (!select.length) {
|
|
244
|
-
control.onDestroy.add(invoke);
|
|
245
|
-
return () => {
|
|
246
|
-
control.onDestroy.delete(invoke);
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
return control.addListener(key => {
|
|
250
|
-
if (select.includes(key))
|
|
251
|
-
return invoke;
|
|
149
|
+
if (!control.state) {
|
|
150
|
+
const { waiting } = control;
|
|
151
|
+
if (cb) {
|
|
152
|
+
let done;
|
|
153
|
+
waiting.add(() => {
|
|
154
|
+
done = cb(control);
|
|
252
155
|
});
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
// TODO: account for exotic properties
|
|
256
|
-
import(source, select) {
|
|
257
|
-
const { subject } = control(this);
|
|
258
|
-
if (!select)
|
|
259
|
-
select = getOwnPropertyNames(subject);
|
|
260
|
-
for (const key of select)
|
|
261
|
-
if (key in source)
|
|
262
|
-
this[key] = source[key];
|
|
263
|
-
}
|
|
264
|
-
export(subset) {
|
|
265
|
-
const { state } = control(this);
|
|
266
|
-
const output = {};
|
|
267
|
-
if (!subset)
|
|
268
|
-
subset = getOwnPropertyNames(state);
|
|
269
|
-
for (const key of subset)
|
|
270
|
-
output[key] = state[key];
|
|
271
|
-
return output;
|
|
272
|
-
}
|
|
273
|
-
update(arg, tag) {
|
|
274
|
-
const target = control(this);
|
|
275
|
-
if (typeof arg == "string") {
|
|
276
|
-
target.update(arg);
|
|
277
|
-
if (1 in arguments && arg in this) {
|
|
278
|
-
const method = this[arg];
|
|
279
|
-
if (typeof method == "function")
|
|
280
|
-
if (typeof tag != "boolean")
|
|
281
|
-
method.call(this, tag);
|
|
282
|
-
else if (tag)
|
|
283
|
-
method.call(this);
|
|
284
|
-
}
|
|
285
|
-
arg = undefined;
|
|
156
|
+
return () => done && done();
|
|
286
157
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
295
|
-
toString() {
|
|
296
|
-
return this.constructor.name;
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* Creates a new instance of this controller.
|
|
300
|
-
*
|
|
301
|
-
* Beyond `new this(...)`, method will activate managed-state.
|
|
302
|
-
*
|
|
303
|
-
* @param args - arguments sent to constructor
|
|
304
|
-
*/
|
|
305
|
-
static create(...args) {
|
|
306
|
-
const instance = new this(...args);
|
|
307
|
-
control(instance);
|
|
308
|
-
return instance;
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* Static equivalent of `x instanceof this`.
|
|
312
|
-
*
|
|
313
|
-
* Will determine if provided class is a subtype of this one.
|
|
314
|
-
*/
|
|
315
|
-
static isTypeof(maybe) {
|
|
316
|
-
return (typeof maybe == "function" &&
|
|
317
|
-
maybe.prototype instanceof this);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
class Subscriber {
|
|
322
|
-
constructor(target, onUpdate) {
|
|
323
|
-
this.onUpdate = onUpdate;
|
|
324
|
-
this.active = false;
|
|
325
|
-
this.dependant = new Set();
|
|
326
|
-
this.watch = {};
|
|
327
|
-
const parent = target instanceof Controller
|
|
328
|
-
? target : control(target);
|
|
329
|
-
const subject = parent.subject;
|
|
330
|
-
const proxy = create(parent.proxy);
|
|
331
|
-
let reset;
|
|
332
|
-
define(proxy, LOCAL, this);
|
|
333
|
-
defineProperty(this, "proxy", {
|
|
334
|
-
configurable: true,
|
|
335
|
-
get() {
|
|
336
|
-
if (reset)
|
|
337
|
-
setTimeout(reset, 0);
|
|
338
|
-
return proxy;
|
|
339
|
-
}
|
|
340
|
-
});
|
|
341
|
-
const release = parent.addListener(key => {
|
|
342
|
-
const handler = this.watch[key];
|
|
343
|
-
if (!handler || !this.active)
|
|
344
|
-
return;
|
|
345
|
-
if (typeof handler == "function")
|
|
346
|
-
handler();
|
|
347
|
-
const notify = this.onUpdate(key, parent);
|
|
348
|
-
const getWhy = () => {
|
|
349
|
-
const update = getUpdate(subject);
|
|
350
|
-
const applicable = update.filter(k => k in this.watch);
|
|
351
|
-
reset = applyUpdate(proxy, applicable);
|
|
352
|
-
};
|
|
353
|
-
if (notify) {
|
|
354
|
-
parent.requestUpdate(getWhy);
|
|
355
|
-
parent.requestUpdate(notify);
|
|
356
|
-
}
|
|
357
|
-
});
|
|
358
|
-
this.commit = () => {
|
|
359
|
-
this.active = true;
|
|
360
|
-
this.dependant.forEach(x => x.commit());
|
|
361
|
-
return this.release;
|
|
362
|
-
};
|
|
363
|
-
this.release = () => {
|
|
364
|
-
this.dependant.forEach(x => x.release());
|
|
365
|
-
release();
|
|
366
|
-
};
|
|
158
|
+
control.state = new Map();
|
|
159
|
+
for (const key in subject)
|
|
160
|
+
control.add(key);
|
|
161
|
+
const callback = Array.from(waiting);
|
|
162
|
+
waiting.clear();
|
|
163
|
+
for (const cb of callback)
|
|
164
|
+
cb();
|
|
367
165
|
}
|
|
166
|
+
return cb ? cb(control) : control;
|
|
368
167
|
}
|
|
369
168
|
|
|
370
169
|
const Oops$a = issues({
|
|
371
170
|
BadSource: (model, property, got) => `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
|
|
372
171
|
PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`,
|
|
373
|
-
Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}]
|
|
374
|
-
Early: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
|
|
172
|
+
Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`
|
|
375
173
|
});
|
|
376
|
-
const INIT = new WeakSet();
|
|
377
174
|
const INFO = new WeakMap();
|
|
378
175
|
const USED = new WeakMap();
|
|
379
176
|
const KEYS = new WeakMap();
|
|
@@ -381,8 +178,8 @@ function from(source, arg1, arg2) {
|
|
|
381
178
|
return apply(function from(key) {
|
|
382
179
|
const parent = this;
|
|
383
180
|
const { subject, state } = this;
|
|
181
|
+
const required = arg2 === true || arg1 === true;
|
|
384
182
|
let getSource;
|
|
385
|
-
let required = arg2 === true || arg1 === true;
|
|
386
183
|
if (typeof arg1 == "boolean")
|
|
387
184
|
arg1 = undefined;
|
|
388
185
|
// Easy mistake, using a peer, will always be unresolved.
|
|
@@ -390,7 +187,7 @@ function from(source, arg1, arg2) {
|
|
|
390
187
|
throw Oops$a.PeerNotAllowed(subject, key);
|
|
391
188
|
// replace source controller in-case it is different
|
|
392
189
|
if (typeof source == "object")
|
|
393
|
-
getSource = () =>
|
|
190
|
+
getSource = () => ensure(source);
|
|
394
191
|
// specifically an arrow function (getter factory)
|
|
395
192
|
else if (!source.prototype) {
|
|
396
193
|
arg1 = source.call(subject, key);
|
|
@@ -399,21 +196,20 @@ function from(source, arg1, arg2) {
|
|
|
399
196
|
// Regular function is too ambiguous so not allowed.
|
|
400
197
|
else
|
|
401
198
|
throw Oops$a.BadSource(subject, key, source);
|
|
402
|
-
let sub;
|
|
403
199
|
const setter = arg1;
|
|
404
200
|
const info = { key, parent, priority: 1 };
|
|
201
|
+
let sub;
|
|
405
202
|
let register = USED.get(parent);
|
|
406
|
-
if (!register)
|
|
407
|
-
register = new Map();
|
|
408
|
-
|
|
409
|
-
}
|
|
203
|
+
if (!register)
|
|
204
|
+
USED.set(parent, register = new Map());
|
|
205
|
+
state.set(key, undefined);
|
|
410
206
|
register.set(key, info);
|
|
411
207
|
function compute(initial) {
|
|
412
208
|
try {
|
|
413
209
|
return setter.call(sub.proxy, sub.proxy);
|
|
414
210
|
}
|
|
415
211
|
catch (err) {
|
|
416
|
-
Oops$a.Failed(subject, key,
|
|
212
|
+
Oops$a.Failed(subject, key, initial).warn();
|
|
417
213
|
throw err;
|
|
418
214
|
}
|
|
419
215
|
}
|
|
@@ -426,16 +222,17 @@ function from(source, arg1, arg2) {
|
|
|
426
222
|
console.error(e);
|
|
427
223
|
}
|
|
428
224
|
finally {
|
|
429
|
-
if (state
|
|
430
|
-
parent.
|
|
225
|
+
if (state.get(key) !== value) {
|
|
226
|
+
parent.state.set(key, value);
|
|
227
|
+
parent.update(key);
|
|
431
228
|
return value;
|
|
432
229
|
}
|
|
433
230
|
}
|
|
434
231
|
}
|
|
435
232
|
function defer(_key, from) {
|
|
436
|
-
let pending = KEYS.get(from
|
|
233
|
+
let pending = KEYS.get(from);
|
|
437
234
|
if (!pending)
|
|
438
|
-
KEYS.set(from
|
|
235
|
+
KEYS.set(from, pending = []);
|
|
439
236
|
if (info.parent !== from)
|
|
440
237
|
update();
|
|
441
238
|
else {
|
|
@@ -443,179 +240,356 @@ function from(source, arg1, arg2) {
|
|
|
443
240
|
pending.splice(after + 1, 0, update);
|
|
444
241
|
}
|
|
445
242
|
}
|
|
446
|
-
function create(
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
value: undefined,
|
|
450
|
-
writable: true
|
|
451
|
-
});
|
|
243
|
+
function create() {
|
|
244
|
+
const control = getSource();
|
|
245
|
+
sub = control.subscribe(defer);
|
|
452
246
|
try {
|
|
453
|
-
|
|
247
|
+
const value = compute(true);
|
|
248
|
+
state.set(key, value);
|
|
249
|
+
return value;
|
|
454
250
|
}
|
|
455
251
|
catch (e) {
|
|
456
|
-
if (early)
|
|
457
|
-
Oops$a.Early(key).warn();
|
|
458
252
|
throw e;
|
|
459
253
|
}
|
|
460
254
|
finally {
|
|
461
255
|
sub.commit();
|
|
462
|
-
for (const key
|
|
256
|
+
for (const key of control.state.keys()) {
|
|
463
257
|
const peer = register.get(key);
|
|
464
258
|
if (peer && peer.priority >= info.priority)
|
|
465
259
|
info.priority = peer.priority + 1;
|
|
466
260
|
}
|
|
467
261
|
}
|
|
468
262
|
}
|
|
469
|
-
INIT.add(create);
|
|
470
263
|
INFO.set(update, info);
|
|
471
|
-
defineProperty(state, key, {
|
|
472
|
-
get: create,
|
|
473
|
-
configurable: true,
|
|
474
|
-
enumerable: true
|
|
475
|
-
});
|
|
476
264
|
return () => {
|
|
477
|
-
const value =
|
|
265
|
+
const value = sub ? state.get(key) : create();
|
|
478
266
|
if (value === undefined && required)
|
|
479
267
|
throw suspend(this, key);
|
|
480
268
|
return value;
|
|
481
269
|
};
|
|
482
270
|
});
|
|
483
271
|
}
|
|
484
|
-
function
|
|
485
|
-
|
|
486
|
-
const desc = getOwnPropertyDescriptor(on.state, key);
|
|
487
|
-
const getter = desc && desc.get;
|
|
488
|
-
if (INIT.has(getter))
|
|
489
|
-
getter(true);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
function flush(frame) {
|
|
493
|
-
const pending = KEYS.get(frame);
|
|
272
|
+
function flush(control) {
|
|
273
|
+
const pending = KEYS.get(control);
|
|
494
274
|
if (!pending)
|
|
495
275
|
return;
|
|
496
276
|
while (pending.length) {
|
|
497
277
|
const compute = pending.shift();
|
|
498
278
|
const { key } = INFO.get(compute);
|
|
499
|
-
if (!frame.has(key))
|
|
279
|
+
if (!control.frame.has(key))
|
|
500
280
|
compute();
|
|
501
281
|
}
|
|
502
|
-
KEYS.delete(
|
|
282
|
+
KEYS.delete(control);
|
|
503
283
|
}
|
|
504
284
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
const
|
|
285
|
+
class Subscriber {
|
|
286
|
+
constructor(parent, onUpdate) {
|
|
287
|
+
this.onUpdate = onUpdate;
|
|
288
|
+
this.active = false;
|
|
289
|
+
this.dependant = new Set();
|
|
290
|
+
const proxy = create(parent.subject);
|
|
291
|
+
const using = new Map();
|
|
292
|
+
define(proxy, LOCAL, this);
|
|
293
|
+
defineProperty(this, "proxy", {
|
|
294
|
+
configurable: true,
|
|
295
|
+
get() {
|
|
296
|
+
if (UPDATE.has(proxy))
|
|
297
|
+
setTimeout(() => UPDATE.delete(proxy), 0);
|
|
298
|
+
return proxy;
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
const release = parent.addListener(key => {
|
|
302
|
+
if (!this.active || !using.has(key))
|
|
303
|
+
return;
|
|
304
|
+
const handler = using.get(key);
|
|
305
|
+
if (typeof handler == "function")
|
|
306
|
+
if (handler() !== true)
|
|
307
|
+
return;
|
|
308
|
+
const notify = this.onUpdate(key, parent);
|
|
309
|
+
const getWhy = () => {
|
|
310
|
+
const update = getUpdate(parent.subject);
|
|
311
|
+
const applicable = update.filter(k => using.has(k));
|
|
312
|
+
UPDATE.set(proxy, applicable);
|
|
313
|
+
};
|
|
314
|
+
if (notify) {
|
|
315
|
+
parent.waiting.add(getWhy);
|
|
316
|
+
parent.waiting.add(notify);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
this.commit = () => {
|
|
320
|
+
this.active = true;
|
|
321
|
+
this.dependant.forEach(x => x.commit());
|
|
322
|
+
return this.release;
|
|
323
|
+
};
|
|
324
|
+
this.release = () => {
|
|
325
|
+
this.dependant.forEach(x => x.release());
|
|
326
|
+
release();
|
|
327
|
+
};
|
|
328
|
+
this.add = (key, value) => {
|
|
329
|
+
if (value)
|
|
330
|
+
using.set(key, value);
|
|
331
|
+
else if (!using.has(key))
|
|
332
|
+
using.set(key, true);
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const UPDATE = new WeakMap();
|
|
338
|
+
function getUpdate(subject) {
|
|
339
|
+
return UPDATE.get(subject);
|
|
340
|
+
}
|
|
511
341
|
class Controller {
|
|
512
342
|
constructor(subject) {
|
|
513
343
|
this.subject = subject;
|
|
514
|
-
this.state = {};
|
|
515
344
|
this.frame = new Set();
|
|
516
|
-
this.onDestroy = new Set();
|
|
517
345
|
this.waiting = new Set();
|
|
518
346
|
this.followers = new Set();
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
347
|
+
}
|
|
348
|
+
subscribe(callback) {
|
|
349
|
+
return new Subscriber(this, callback);
|
|
350
|
+
}
|
|
351
|
+
add(key) {
|
|
352
|
+
const { subject, state } = this;
|
|
353
|
+
const { value } = getOwnPropertyDescriptor(subject, key);
|
|
354
|
+
if (typeof value == "function")
|
|
355
|
+
return;
|
|
356
|
+
if (typeof value == "symbol") {
|
|
357
|
+
const instruction = PENDING.get(value);
|
|
358
|
+
if (instruction)
|
|
359
|
+
instruction.call(this, key, this);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
state.set(key, value);
|
|
363
|
+
defineProperty(subject, key, {
|
|
364
|
+
enumerable: false,
|
|
365
|
+
set: this.ref(key),
|
|
366
|
+
get() {
|
|
367
|
+
const local = this[LOCAL];
|
|
368
|
+
if (local)
|
|
369
|
+
local.add(key);
|
|
370
|
+
return state.get(key);
|
|
532
371
|
}
|
|
533
|
-
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
addListener(listener) {
|
|
375
|
+
this.followers.add(listener);
|
|
376
|
+
return () => {
|
|
377
|
+
this.followers.delete(listener);
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
ref(key, handler) {
|
|
381
|
+
const { subject, state } = this;
|
|
382
|
+
return (value) => {
|
|
383
|
+
if (state.get(key) == value)
|
|
384
|
+
return;
|
|
385
|
+
if (handler)
|
|
386
|
+
switch (handler.call(subject, value)) {
|
|
387
|
+
case true:
|
|
388
|
+
this.update(key);
|
|
389
|
+
case false:
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
state.set(key, value);
|
|
393
|
+
this.update(key);
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
update(key) {
|
|
397
|
+
const { followers, frame, subject, waiting } = this;
|
|
398
|
+
if (frame.has(key))
|
|
399
|
+
return;
|
|
400
|
+
else if (!frame.size)
|
|
401
|
+
setTimeout(() => {
|
|
402
|
+
flush(this);
|
|
403
|
+
const callback = Array.from(waiting);
|
|
404
|
+
const keys = Array.from(frame);
|
|
405
|
+
frame.clear();
|
|
406
|
+
waiting.clear();
|
|
407
|
+
UPDATE.set(subject, keys);
|
|
408
|
+
setTimeout(() => {
|
|
409
|
+
UPDATE.delete(subject);
|
|
410
|
+
}, 0);
|
|
411
|
+
for (const cb of callback)
|
|
412
|
+
try {
|
|
413
|
+
cb();
|
|
414
|
+
}
|
|
415
|
+
catch (err) {
|
|
416
|
+
console.error(err);
|
|
417
|
+
}
|
|
418
|
+
}, 0);
|
|
419
|
+
frame.add(key);
|
|
420
|
+
for (const callback of followers) {
|
|
421
|
+
const event = callback(key, this);
|
|
422
|
+
if (typeof event == "function")
|
|
423
|
+
waiting.add(event);
|
|
534
424
|
}
|
|
535
|
-
this.flush();
|
|
536
425
|
}
|
|
537
|
-
|
|
426
|
+
clear() {
|
|
427
|
+
const listeners = [...this.followers];
|
|
538
428
|
this.followers.clear();
|
|
539
|
-
|
|
429
|
+
listeners.forEach(x => x(null, this));
|
|
540
430
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const Oops$9 = issues({
|
|
434
|
+
Timeout: (keys, timeout) => `No update for [${keys}] within ${timeout}.`,
|
|
435
|
+
StrictUpdate: () => `Strict update() did not find pending updates.`,
|
|
436
|
+
NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
|
|
437
|
+
});
|
|
438
|
+
const CONTROL = Symbol("CONTROL");
|
|
439
|
+
const WHY = Symbol("UPDATE");
|
|
440
|
+
const LOCAL = Symbol("LOCAL");
|
|
441
|
+
const STATE = Symbol("STATE");
|
|
442
|
+
class Model {
|
|
443
|
+
constructor() {
|
|
444
|
+
define(this, CONTROL, new Controller(this));
|
|
445
|
+
define(this, "get", this);
|
|
446
|
+
define(this, "is", this);
|
|
447
|
+
defineLazy(this, "set", () => {
|
|
448
|
+
const controller = ensure(this);
|
|
449
|
+
const assign = (key, value) => {
|
|
450
|
+
controller.state.set(key, value);
|
|
451
|
+
controller.update(key);
|
|
452
|
+
};
|
|
453
|
+
for (const key of controller.state.keys())
|
|
454
|
+
define(assign, key, controller.ref(key));
|
|
455
|
+
return assign;
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
get [STATE]() {
|
|
459
|
+
return this.export();
|
|
460
|
+
}
|
|
461
|
+
get [WHY]() {
|
|
462
|
+
return UPDATE.get(this);
|
|
463
|
+
}
|
|
464
|
+
on(select, handler, squash, once) {
|
|
465
|
+
return ensure(this, control => {
|
|
466
|
+
if (typeof select == "function")
|
|
467
|
+
return control.addListener(select);
|
|
468
|
+
const keys = typeof select == "string" ? [select] :
|
|
469
|
+
select.length ? select : [...control.state.keys()];
|
|
470
|
+
for (const key of keys)
|
|
471
|
+
try {
|
|
472
|
+
void this[key];
|
|
473
|
+
}
|
|
474
|
+
catch (e) { }
|
|
475
|
+
const callback = squash
|
|
476
|
+
? () => {
|
|
477
|
+
handler.call(this, getUpdate(this));
|
|
478
|
+
}
|
|
479
|
+
: () => {
|
|
480
|
+
getUpdate(this)
|
|
481
|
+
.filter(k => keys.includes(k))
|
|
482
|
+
.forEach(k => {
|
|
483
|
+
handler.call(this, control.state.get(k), k);
|
|
484
|
+
});
|
|
485
|
+
};
|
|
486
|
+
const onEvent = once
|
|
487
|
+
? () => {
|
|
488
|
+
remove();
|
|
489
|
+
callback();
|
|
490
|
+
}
|
|
491
|
+
: callback;
|
|
492
|
+
const remove = control.addListener(key => {
|
|
493
|
+
if (keys.includes(key))
|
|
494
|
+
return onEvent;
|
|
495
|
+
});
|
|
496
|
+
return remove;
|
|
549
497
|
});
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
498
|
+
}
|
|
499
|
+
once(select, argument, squash) {
|
|
500
|
+
if (typeof argument == "function")
|
|
501
|
+
return this.on(select, argument, squash, true);
|
|
502
|
+
if (typeof select == "string")
|
|
503
|
+
select = [select];
|
|
504
|
+
return new Promise((resolve, reject) => {
|
|
505
|
+
const invoke = (key) => {
|
|
506
|
+
if (!key) {
|
|
507
|
+
clear();
|
|
508
|
+
reject(Oops$9.Timeout(select, typeof argument == "number"
|
|
509
|
+
? `${argument}ms`
|
|
510
|
+
: `lifetime of ${this}`));
|
|
511
|
+
}
|
|
512
|
+
else if (select.includes(key)) {
|
|
513
|
+
clear();
|
|
514
|
+
return resolve;
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
const clear = ensure(this, x => {
|
|
518
|
+
for (const key of select)
|
|
519
|
+
try {
|
|
520
|
+
void this[key];
|
|
521
|
+
}
|
|
522
|
+
catch (e) { }
|
|
523
|
+
return x.addListener(invoke);
|
|
524
|
+
});
|
|
525
|
+
if (typeof argument == "number")
|
|
526
|
+
setTimeout(invoke, argument);
|
|
559
527
|
});
|
|
560
528
|
}
|
|
561
|
-
|
|
562
|
-
const
|
|
563
|
-
return (
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
529
|
+
effect(callback, select) {
|
|
530
|
+
const effect = createEffect(callback);
|
|
531
|
+
return ensure(this, control => {
|
|
532
|
+
let busy = false;
|
|
533
|
+
let inject = this.is;
|
|
534
|
+
const invoke = () => {
|
|
535
|
+
if (busy)
|
|
536
|
+
return;
|
|
537
|
+
const output = mayRetry(() => {
|
|
538
|
+
effect.call(inject, inject);
|
|
539
|
+
});
|
|
540
|
+
if (output instanceof Promise) {
|
|
541
|
+
output.finally(() => busy = false);
|
|
542
|
+
busy = true;
|
|
572
543
|
}
|
|
573
|
-
|
|
574
|
-
|
|
544
|
+
};
|
|
545
|
+
if (!select) {
|
|
546
|
+
const sub = control.subscribe(() => invoke);
|
|
547
|
+
inject = sub.proxy;
|
|
548
|
+
invoke();
|
|
549
|
+
return sub.commit();
|
|
550
|
+
}
|
|
551
|
+
invoke();
|
|
552
|
+
return control.addListener(key => {
|
|
553
|
+
if (key === null) {
|
|
554
|
+
if (!select.length)
|
|
555
|
+
invoke();
|
|
556
|
+
}
|
|
557
|
+
else if (select.includes(key))
|
|
558
|
+
return invoke;
|
|
559
|
+
});
|
|
560
|
+
});
|
|
575
561
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
562
|
+
// TODO: account for exotic properties
|
|
563
|
+
import(source, select) {
|
|
564
|
+
const { subject } = ensure(this);
|
|
565
|
+
if (!select)
|
|
566
|
+
select = getOwnPropertyNames(subject);
|
|
567
|
+
for (const key of select)
|
|
568
|
+
if (key in source)
|
|
569
|
+
this[key] = source[key];
|
|
581
570
|
}
|
|
582
|
-
|
|
583
|
-
const {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
this.flush();
|
|
590
|
-
}, 0);
|
|
591
|
-
else if (frame.has(key))
|
|
592
|
-
return;
|
|
593
|
-
frame.add(key);
|
|
594
|
-
for (const callback of this.followers) {
|
|
595
|
-
const event = callback(key, this);
|
|
596
|
-
if (typeof event == "function")
|
|
597
|
-
this.waiting.add(event);
|
|
598
|
-
}
|
|
571
|
+
export(subset) {
|
|
572
|
+
const { state } = ensure(this);
|
|
573
|
+
const output = {};
|
|
574
|
+
const keys = subset || state.keys();
|
|
575
|
+
for (const key of keys)
|
|
576
|
+
output[key] = state.get(key);
|
|
577
|
+
return output;
|
|
599
578
|
}
|
|
600
|
-
|
|
601
|
-
const
|
|
602
|
-
const
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
579
|
+
update(arg, tag) {
|
|
580
|
+
const target = ensure(this);
|
|
581
|
+
const { frame, waiting } = target;
|
|
582
|
+
if (typeof arg == "string") {
|
|
583
|
+
target.update(arg);
|
|
584
|
+
if (1 in arguments && arg in this) {
|
|
585
|
+
const method = this[arg];
|
|
586
|
+
if (typeof method == "function")
|
|
587
|
+
if (typeof tag != "boolean")
|
|
588
|
+
method.call(this, tag);
|
|
589
|
+
else if (tag)
|
|
590
|
+
method.call(this);
|
|
612
591
|
}
|
|
613
|
-
|
|
614
|
-
requestUpdate(arg) {
|
|
615
|
-
const { frame, subject, waiting } = this;
|
|
616
|
-
if (typeof arg == "function") {
|
|
617
|
-
waiting.add(arg);
|
|
618
|
-
return;
|
|
592
|
+
arg = undefined;
|
|
619
593
|
}
|
|
620
594
|
if (!frame.size && arg === true)
|
|
621
595
|
return Promise.reject(Oops$9.StrictUpdate());
|
|
@@ -625,37 +599,51 @@ class Controller {
|
|
|
625
599
|
throw Oops$9.NoChaining();
|
|
626
600
|
if (frame.size || arg !== false)
|
|
627
601
|
waiting.add(() => {
|
|
628
|
-
callback(getUpdate(
|
|
602
|
+
callback(getUpdate(this));
|
|
629
603
|
});
|
|
630
604
|
else
|
|
631
605
|
callback(false);
|
|
632
606
|
}
|
|
633
607
|
};
|
|
634
608
|
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
define(subject, CONTROL, control);
|
|
609
|
+
/**
|
|
610
|
+
* Mark this instance for garbage-collection and send `willDestroy` event to all listeners.
|
|
611
|
+
*/
|
|
612
|
+
destroy() {
|
|
613
|
+
ensure(this).clear();
|
|
641
614
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
615
|
+
toString() {
|
|
616
|
+
return this.constructor.name;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Creates a new instance of this controller.
|
|
620
|
+
*
|
|
621
|
+
* Beyond `new this(...)`, method will activate managed-state.
|
|
622
|
+
*
|
|
623
|
+
* @param args - arguments sent to constructor
|
|
624
|
+
*/
|
|
625
|
+
static create(...args) {
|
|
626
|
+
const instance = new this(...args);
|
|
627
|
+
ensure(instance);
|
|
628
|
+
return instance;
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Static equivalent of `x instanceof this`.
|
|
632
|
+
*
|
|
633
|
+
* Will determine if provided class is a subtype of this one.
|
|
634
|
+
*/
|
|
635
|
+
static isTypeof(maybe) {
|
|
636
|
+
return (typeof maybe == "function" &&
|
|
637
|
+
maybe.prototype instanceof this);
|
|
650
638
|
}
|
|
651
|
-
return cb ? cb(control) : control;
|
|
652
639
|
}
|
|
653
640
|
|
|
641
|
+
const PENDING = new Map();
|
|
654
642
|
function apply(fn, label) {
|
|
655
643
|
const name = label || fn.name || "pending";
|
|
656
644
|
const placeholder = Symbol(`${name} instruction`);
|
|
657
645
|
function setup(key) {
|
|
658
|
-
const {
|
|
646
|
+
const { subject, state } = this;
|
|
659
647
|
const control = this;
|
|
660
648
|
PENDING.delete(placeholder);
|
|
661
649
|
delete subject[key];
|
|
@@ -669,67 +657,56 @@ function apply(fn, label) {
|
|
|
669
657
|
return false;
|
|
670
658
|
}
|
|
671
659
|
if ("recursive" in output && output.recursive)
|
|
672
|
-
output = Object.assign(Object.assign({}, output), { get:
|
|
660
|
+
output = Object.assign(Object.assign({}, output), { get: getRecursive(key, this) });
|
|
673
661
|
const desc = output;
|
|
674
662
|
if ("value" in desc)
|
|
675
|
-
state
|
|
663
|
+
state.set(key, desc.value);
|
|
676
664
|
const { get: onGet, set: onSet, enumerable, suspense } = desc;
|
|
677
|
-
|
|
678
|
-
?
|
|
679
|
-
:
|
|
665
|
+
const set = onSet !== false
|
|
666
|
+
? this.ref(key, onSet)
|
|
667
|
+
: undefined;
|
|
680
668
|
function get() {
|
|
681
|
-
if (!(key
|
|
669
|
+
if (!state.has(key) && suspense)
|
|
682
670
|
throw suspend(control, key);
|
|
683
671
|
const local = this[LOCAL];
|
|
684
|
-
if (local
|
|
685
|
-
local.
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
return local
|
|
690
|
-
? onGet(value, local)
|
|
691
|
-
: onGet(value);
|
|
672
|
+
if (local)
|
|
673
|
+
local.add(key);
|
|
674
|
+
return onGet
|
|
675
|
+
? onGet(local)
|
|
676
|
+
: state.get(key);
|
|
692
677
|
}
|
|
693
|
-
|
|
694
|
-
defineProperty(x, key, { enumerable, get, set });
|
|
678
|
+
defineProperty(subject, key, { enumerable, get, set });
|
|
695
679
|
}
|
|
696
680
|
PENDING.set(placeholder, setup);
|
|
697
681
|
return placeholder;
|
|
698
682
|
}
|
|
699
|
-
function
|
|
700
|
-
return forSubscriber((parent, context) => {
|
|
701
|
-
let child;
|
|
702
|
-
function init() {
|
|
703
|
-
if (child) {
|
|
704
|
-
child.release();
|
|
705
|
-
parent.dependant.delete(child);
|
|
706
|
-
context.set(parent, undefined);
|
|
707
|
-
child = undefined;
|
|
708
|
-
}
|
|
709
|
-
const value = source.state[key];
|
|
710
|
-
if (value && CONTROL in value) {
|
|
711
|
-
child = new Subscriber(value, parent.onUpdate);
|
|
712
|
-
if (parent.active)
|
|
713
|
-
child.commit();
|
|
714
|
-
parent.dependant.add(child);
|
|
715
|
-
context.set(parent, child.proxy);
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
init();
|
|
719
|
-
parent.watch[key] = init;
|
|
720
|
-
});
|
|
721
|
-
}
|
|
722
|
-
function forSubscriber(fn) {
|
|
683
|
+
function getRecursive(key, from) {
|
|
723
684
|
const context = new WeakMap();
|
|
724
|
-
|
|
685
|
+
const { state } = from;
|
|
686
|
+
return (local) => {
|
|
725
687
|
if (!local)
|
|
726
|
-
return
|
|
688
|
+
return state.get(key);
|
|
727
689
|
if (!context.has(local)) {
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
690
|
+
let child;
|
|
691
|
+
const init = () => {
|
|
692
|
+
if (child) {
|
|
693
|
+
child.release();
|
|
694
|
+
local.dependant.delete(child);
|
|
695
|
+
context.set(local, undefined);
|
|
696
|
+
child = undefined;
|
|
697
|
+
}
|
|
698
|
+
const value = state.get(key);
|
|
699
|
+
if (value && CONTROL in value) {
|
|
700
|
+
child = ensure(value).subscribe(local.onUpdate);
|
|
701
|
+
if (local.active)
|
|
702
|
+
child.commit();
|
|
703
|
+
local.dependant.add(child);
|
|
704
|
+
context.set(local, child.proxy);
|
|
705
|
+
}
|
|
706
|
+
return true;
|
|
707
|
+
};
|
|
708
|
+
init();
|
|
709
|
+
local.add(key, init);
|
|
733
710
|
}
|
|
734
711
|
return context.get(local);
|
|
735
712
|
};
|
|
@@ -754,7 +731,7 @@ function act(task) {
|
|
|
754
731
|
this.update(key);
|
|
755
732
|
}
|
|
756
733
|
});
|
|
757
|
-
this.state
|
|
734
|
+
this.state.set(key, undefined);
|
|
758
735
|
defineProperty(invoke, "active", {
|
|
759
736
|
get: () => pending
|
|
760
737
|
});
|
|
@@ -775,7 +752,7 @@ class Managed extends Array {
|
|
|
775
752
|
};
|
|
776
753
|
}
|
|
777
754
|
push(...items) {
|
|
778
|
-
const length = this
|
|
755
|
+
const { length } = this;
|
|
779
756
|
const end = super.push(...items);
|
|
780
757
|
this.update(length, end);
|
|
781
758
|
return end;
|
|
@@ -786,64 +763,65 @@ class Managed extends Array {
|
|
|
786
763
|
return end;
|
|
787
764
|
}
|
|
788
765
|
splice(start, deleteCount, ...rest) {
|
|
789
|
-
|
|
766
|
+
const end = deleteCount !== rest.length ? Infinity : deleteCount;
|
|
767
|
+
this.update(start, end);
|
|
790
768
|
return super.splice(start, deleteCount, ...rest);
|
|
791
769
|
}
|
|
792
770
|
}
|
|
793
771
|
function array() {
|
|
794
772
|
return apply(function array(key) {
|
|
773
|
+
const context = new WeakMap();
|
|
795
774
|
const array = new Managed(() => this.update(key));
|
|
796
|
-
const
|
|
797
|
-
const
|
|
798
|
-
const update = context.onUpdate(key, this);
|
|
775
|
+
const getLocal = (context) => {
|
|
776
|
+
const proxy = Object.create(array);
|
|
799
777
|
let rangeA = 0;
|
|
800
778
|
let rangeB = 0;
|
|
801
|
-
|
|
779
|
+
const onUpdate = () => {
|
|
802
780
|
const [lastA, lastB] = array.lastUpdate;
|
|
803
781
|
if (rangeA <= lastB && rangeB >= lastA)
|
|
804
|
-
return
|
|
805
|
-
}
|
|
806
|
-
|
|
782
|
+
return true;
|
|
783
|
+
};
|
|
784
|
+
const listen = (eventA, eventB) => {
|
|
807
785
|
if (context.active)
|
|
808
786
|
return;
|
|
809
|
-
context.
|
|
787
|
+
context.add(key, onUpdate);
|
|
810
788
|
rangeA = Math.min(rangeA, eventA);
|
|
811
789
|
rangeB = Math.max(rangeB, eventB);
|
|
812
|
-
}
|
|
813
|
-
|
|
790
|
+
};
|
|
791
|
+
proxy[Symbol.iterator] = () => {
|
|
814
792
|
listen(0, Infinity);
|
|
815
793
|
return array[Symbol.iterator]();
|
|
816
794
|
};
|
|
817
|
-
return
|
|
818
|
-
}
|
|
795
|
+
return proxy;
|
|
796
|
+
};
|
|
819
797
|
return {
|
|
820
|
-
|
|
798
|
+
value: array,
|
|
821
799
|
set: false,
|
|
822
|
-
|
|
800
|
+
get: (local) => {
|
|
801
|
+
if (!local)
|
|
802
|
+
return this.state.get(key);
|
|
803
|
+
if (context.has(local))
|
|
804
|
+
return context.get(local);
|
|
805
|
+
const output = getLocal(local);
|
|
806
|
+
context.set(local, output);
|
|
807
|
+
return output;
|
|
808
|
+
}
|
|
823
809
|
};
|
|
824
810
|
});
|
|
825
811
|
}
|
|
826
812
|
|
|
827
|
-
function ref(
|
|
813
|
+
function ref(callback) {
|
|
828
814
|
return apply(function ref(key) {
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
});
|
|
839
|
-
}
|
|
840
|
-
function createRef(key, cb) {
|
|
841
|
-
const refObjectFunction = this.ref(key, cb && createValueEffect(cb));
|
|
842
|
-
defineProperty(refObjectFunction, "current", {
|
|
843
|
-
set: refObjectFunction,
|
|
844
|
-
get: () => this.state[key]
|
|
815
|
+
const value = this.ref(key, callback && createValueEffect(callback));
|
|
816
|
+
defineProperty(value, "current", {
|
|
817
|
+
set: value,
|
|
818
|
+
get: () => this.state.get(key)
|
|
819
|
+
});
|
|
820
|
+
return {
|
|
821
|
+
value,
|
|
822
|
+
explicit: true
|
|
823
|
+
};
|
|
845
824
|
});
|
|
846
|
-
return refObjectFunction;
|
|
847
825
|
}
|
|
848
826
|
|
|
849
827
|
const Oops$7 = issues({
|
|
@@ -851,65 +829,64 @@ const Oops$7 = issues({
|
|
|
851
829
|
NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
|
|
852
830
|
Failed: (model, key) => `Generating initial value for ${model}.${key} failed.`
|
|
853
831
|
});
|
|
854
|
-
function
|
|
832
|
+
function put(factory, argument) {
|
|
855
833
|
return apply(function set(key) {
|
|
856
834
|
const { subject, state } = this;
|
|
857
835
|
const required = argument === true || argument === undefined;
|
|
858
836
|
let set;
|
|
859
837
|
let get;
|
|
860
|
-
let
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
this.update(key);
|
|
877
|
-
});
|
|
878
|
-
return;
|
|
879
|
-
}
|
|
880
|
-
return state[key] = output;
|
|
881
|
-
};
|
|
882
|
-
const suspend = () => {
|
|
883
|
-
if (required === false)
|
|
884
|
-
return undefined;
|
|
885
|
-
const issue = Oops$7.NotReady(subject, key);
|
|
886
|
-
Object.assign(pending, {
|
|
887
|
-
message: issue.message,
|
|
888
|
-
stack: issue.stack
|
|
838
|
+
let pending;
|
|
839
|
+
let error;
|
|
840
|
+
const init = () => {
|
|
841
|
+
const output = typeof factory == "function" ?
|
|
842
|
+
mayRetry(() => factory.call(subject, key, subject)) :
|
|
843
|
+
factory;
|
|
844
|
+
if (output instanceof Promise) {
|
|
845
|
+
pending = output
|
|
846
|
+
.catch(err => error = err)
|
|
847
|
+
.then(val => {
|
|
848
|
+
state.set(key, val);
|
|
849
|
+
return val;
|
|
850
|
+
})
|
|
851
|
+
.finally(() => {
|
|
852
|
+
pending = undefined;
|
|
853
|
+
this.update(key);
|
|
889
854
|
});
|
|
890
|
-
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
state.set(key, output);
|
|
858
|
+
return output;
|
|
859
|
+
};
|
|
860
|
+
const suspend = () => {
|
|
861
|
+
if (required === false)
|
|
862
|
+
return undefined;
|
|
863
|
+
const issue = Oops$7.NotReady(subject, key);
|
|
864
|
+
Object.assign(pending, {
|
|
865
|
+
message: issue.message,
|
|
866
|
+
stack: issue.stack
|
|
867
|
+
});
|
|
868
|
+
throw pending;
|
|
869
|
+
};
|
|
870
|
+
if (required)
|
|
871
|
+
try {
|
|
872
|
+
init();
|
|
873
|
+
}
|
|
874
|
+
catch (err) {
|
|
875
|
+
Oops$7.Failed(subject, key).warn();
|
|
876
|
+
throw err;
|
|
877
|
+
}
|
|
878
|
+
get = () => {
|
|
879
|
+
if (pending)
|
|
880
|
+
return suspend();
|
|
881
|
+
if (error)
|
|
882
|
+
throw error;
|
|
883
|
+
if (state.has(key))
|
|
884
|
+
return state.get(key);
|
|
885
|
+
let output = init();
|
|
886
|
+
return pending
|
|
887
|
+
? suspend()
|
|
888
|
+
: output;
|
|
889
|
+
};
|
|
913
890
|
if (typeof argument == "function")
|
|
914
891
|
set = createValueEffect(argument);
|
|
915
892
|
else
|
|
@@ -919,8 +896,24 @@ function set(factory, argument) {
|
|
|
919
896
|
};
|
|
920
897
|
return {
|
|
921
898
|
set,
|
|
922
|
-
get
|
|
923
|
-
|
|
899
|
+
get
|
|
900
|
+
};
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
function set(value, argument) {
|
|
905
|
+
if (!(1 in arguments)) {
|
|
906
|
+
argument = value;
|
|
907
|
+
value = undefined;
|
|
908
|
+
}
|
|
909
|
+
return apply(function set(key) {
|
|
910
|
+
if (value !== undefined)
|
|
911
|
+
this.state.set(key, value);
|
|
912
|
+
return {
|
|
913
|
+
suspense: true,
|
|
914
|
+
set: typeof argument == "function"
|
|
915
|
+
? createValueEffect(argument)
|
|
916
|
+
: undefined
|
|
924
917
|
};
|
|
925
918
|
});
|
|
926
919
|
}
|
|
@@ -945,35 +938,224 @@ function parent(Expects, required) {
|
|
|
945
938
|
});
|
|
946
939
|
}
|
|
947
940
|
|
|
941
|
+
const ANY$1 = Symbol("any");
|
|
942
|
+
function managedMap(control, property, initial) {
|
|
943
|
+
const lastUpdate = new Set();
|
|
944
|
+
const context = new WeakMap();
|
|
945
|
+
const assoc = new WeakMap();
|
|
946
|
+
let managed;
|
|
947
|
+
let value;
|
|
948
|
+
function reset() {
|
|
949
|
+
lastUpdate.clear();
|
|
950
|
+
}
|
|
951
|
+
function emit(key) {
|
|
952
|
+
lastUpdate.add(key);
|
|
953
|
+
control.update(property);
|
|
954
|
+
control.waiting.add(reset);
|
|
955
|
+
}
|
|
956
|
+
function setValue(next, initial) {
|
|
957
|
+
value = next;
|
|
958
|
+
managed = create(next);
|
|
959
|
+
control.state.set(property, next);
|
|
960
|
+
if (!initial)
|
|
961
|
+
emit(ANY$1);
|
|
962
|
+
const watch = (key, on) => {
|
|
963
|
+
const local = assoc.get(on);
|
|
964
|
+
if (local)
|
|
965
|
+
local(key);
|
|
966
|
+
};
|
|
967
|
+
managed.set = (k, v) => {
|
|
968
|
+
emit(k);
|
|
969
|
+
return value.set(k, v);
|
|
970
|
+
};
|
|
971
|
+
managed.delete = (k) => {
|
|
972
|
+
emit(k);
|
|
973
|
+
return value.delete(k);
|
|
974
|
+
};
|
|
975
|
+
managed.clear = () => {
|
|
976
|
+
emit(ANY$1);
|
|
977
|
+
value.clear();
|
|
978
|
+
};
|
|
979
|
+
managed.get = function (key) {
|
|
980
|
+
watch(key, this);
|
|
981
|
+
return value.get(key);
|
|
982
|
+
};
|
|
983
|
+
managed.has = function (key) {
|
|
984
|
+
watch(key, this);
|
|
985
|
+
return value.has(key);
|
|
986
|
+
};
|
|
987
|
+
managed.values = function () {
|
|
988
|
+
watch(ANY$1, this);
|
|
989
|
+
return value.values();
|
|
990
|
+
};
|
|
991
|
+
managed.keys = function () {
|
|
992
|
+
watch(ANY$1, this);
|
|
993
|
+
return value.keys();
|
|
994
|
+
};
|
|
995
|
+
managed.entries = function () {
|
|
996
|
+
watch(ANY$1, this);
|
|
997
|
+
return value.entries();
|
|
998
|
+
};
|
|
999
|
+
managed[Symbol.iterator] = function () {
|
|
1000
|
+
watch(ANY$1, this);
|
|
1001
|
+
return value[Symbol.iterator]();
|
|
1002
|
+
};
|
|
1003
|
+
defineProperty(managed, "size", {
|
|
1004
|
+
get() {
|
|
1005
|
+
watch(ANY$1, this);
|
|
1006
|
+
return value.size;
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
function getValue(local) {
|
|
1011
|
+
if (!local)
|
|
1012
|
+
return managed;
|
|
1013
|
+
if (context.has(local))
|
|
1014
|
+
return context.get(local);
|
|
1015
|
+
const proxy = Object.create(managed);
|
|
1016
|
+
const using = new Set();
|
|
1017
|
+
context.set(local, proxy);
|
|
1018
|
+
assoc.set(proxy, (key) => {
|
|
1019
|
+
if (!local.active)
|
|
1020
|
+
using.add(key);
|
|
1021
|
+
});
|
|
1022
|
+
local.add(property, () => {
|
|
1023
|
+
if (using.has(ANY$1) || lastUpdate.has(ANY$1) && using.size)
|
|
1024
|
+
return true;
|
|
1025
|
+
for (const key of lastUpdate)
|
|
1026
|
+
if (using.has(key))
|
|
1027
|
+
return true;
|
|
1028
|
+
});
|
|
1029
|
+
return proxy;
|
|
1030
|
+
}
|
|
1031
|
+
setValue(initial, true);
|
|
1032
|
+
return {
|
|
1033
|
+
set: setValue,
|
|
1034
|
+
get: getValue
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
const ANY = Symbol("any");
|
|
1039
|
+
function managedSet(control, property, initial) {
|
|
1040
|
+
const lastUpdate = new Set();
|
|
1041
|
+
const context = new WeakMap();
|
|
1042
|
+
const assoc = new WeakMap();
|
|
1043
|
+
let managed;
|
|
1044
|
+
let value;
|
|
1045
|
+
function reset() {
|
|
1046
|
+
lastUpdate.clear();
|
|
1047
|
+
}
|
|
1048
|
+
function emit(key) {
|
|
1049
|
+
lastUpdate.add(key);
|
|
1050
|
+
control.update(property);
|
|
1051
|
+
control.waiting.add(reset);
|
|
1052
|
+
}
|
|
1053
|
+
function setValue(next, initial) {
|
|
1054
|
+
value = next;
|
|
1055
|
+
managed = create(next);
|
|
1056
|
+
control.state.set(property, next);
|
|
1057
|
+
if (!initial)
|
|
1058
|
+
emit(ANY);
|
|
1059
|
+
const watch = (key, on) => {
|
|
1060
|
+
const local = assoc.get(on);
|
|
1061
|
+
if (local)
|
|
1062
|
+
local(key);
|
|
1063
|
+
};
|
|
1064
|
+
managed.add = (k) => {
|
|
1065
|
+
emit(k);
|
|
1066
|
+
return value.add(k);
|
|
1067
|
+
};
|
|
1068
|
+
managed.delete = (k) => {
|
|
1069
|
+
emit(k);
|
|
1070
|
+
return value.delete(k);
|
|
1071
|
+
};
|
|
1072
|
+
managed.clear = () => {
|
|
1073
|
+
emit(ANY);
|
|
1074
|
+
value.clear();
|
|
1075
|
+
};
|
|
1076
|
+
managed.has = function (key) {
|
|
1077
|
+
watch(key, this);
|
|
1078
|
+
return value.has(key);
|
|
1079
|
+
};
|
|
1080
|
+
managed.values = function () {
|
|
1081
|
+
watch(ANY, this);
|
|
1082
|
+
return value.values();
|
|
1083
|
+
};
|
|
1084
|
+
managed.keys = function () {
|
|
1085
|
+
watch(ANY, this);
|
|
1086
|
+
return value.keys();
|
|
1087
|
+
};
|
|
1088
|
+
managed.entries = function () {
|
|
1089
|
+
watch(ANY, this);
|
|
1090
|
+
return value.entries();
|
|
1091
|
+
};
|
|
1092
|
+
managed[Symbol.iterator] = function () {
|
|
1093
|
+
watch(ANY, this);
|
|
1094
|
+
return value[Symbol.iterator]();
|
|
1095
|
+
};
|
|
1096
|
+
defineProperty(managed, "size", {
|
|
1097
|
+
get() {
|
|
1098
|
+
watch(ANY, this);
|
|
1099
|
+
return value.size;
|
|
1100
|
+
}
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
function getValue(local) {
|
|
1104
|
+
if (!local)
|
|
1105
|
+
return managed;
|
|
1106
|
+
if (context.has(local))
|
|
1107
|
+
return context.get(local);
|
|
1108
|
+
const proxy = Object.create(managed);
|
|
1109
|
+
const using = new Set();
|
|
1110
|
+
context.set(local, proxy);
|
|
1111
|
+
assoc.set(proxy, (key) => {
|
|
1112
|
+
if (!local.active)
|
|
1113
|
+
using.add(key);
|
|
1114
|
+
});
|
|
1115
|
+
local.add(property, () => {
|
|
1116
|
+
if (using.has(ANY) || lastUpdate.has(ANY) && using.size)
|
|
1117
|
+
return true;
|
|
1118
|
+
for (const key of lastUpdate)
|
|
1119
|
+
if (using.has(key))
|
|
1120
|
+
return true;
|
|
1121
|
+
});
|
|
1122
|
+
return proxy;
|
|
1123
|
+
}
|
|
1124
|
+
setValue(initial, true);
|
|
1125
|
+
return {
|
|
1126
|
+
set: setValue,
|
|
1127
|
+
get: getValue
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
|
|
948
1131
|
const Oops$5 = issues({
|
|
949
1132
|
BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`,
|
|
950
1133
|
});
|
|
951
1134
|
function use$1(input, argument) {
|
|
952
1135
|
return apply(function use(key) {
|
|
953
|
-
const { subject } = this;
|
|
954
|
-
|
|
955
|
-
|
|
1136
|
+
const { state, subject } = this;
|
|
1137
|
+
if (typeof input === "function")
|
|
1138
|
+
input = Model.isTypeof(input)
|
|
1139
|
+
? new input()
|
|
1140
|
+
: input();
|
|
1141
|
+
else if (input && typeof input !== "object")
|
|
1142
|
+
throw Oops$5.BadArgument(typeof input);
|
|
1143
|
+
if (input instanceof Map)
|
|
1144
|
+
return managedMap(this, key, input);
|
|
1145
|
+
if (input instanceof Set)
|
|
1146
|
+
return managedSet(this, key, input);
|
|
1147
|
+
function onUpdate(next) {
|
|
1148
|
+
state.set(key, next);
|
|
956
1149
|
if (next) {
|
|
957
1150
|
Parent.set(next, subject);
|
|
958
|
-
|
|
1151
|
+
ensure(next);
|
|
959
1152
|
}
|
|
960
1153
|
if (typeof argument == "function")
|
|
961
1154
|
argument(next);
|
|
962
1155
|
return true;
|
|
963
|
-
};
|
|
964
|
-
if (input) {
|
|
965
|
-
const value = Model.isTypeof(input)
|
|
966
|
-
? new input() :
|
|
967
|
-
typeof input === "object"
|
|
968
|
-
? input :
|
|
969
|
-
typeof input === "function"
|
|
970
|
-
? input()
|
|
971
|
-
: null;
|
|
972
|
-
if (value === null)
|
|
973
|
-
throw Oops$5.BadArgument(typeof input);
|
|
974
|
-
if (value)
|
|
975
|
-
onUpdate(value);
|
|
976
1156
|
}
|
|
1157
|
+
if (input)
|
|
1158
|
+
onUpdate(input);
|
|
977
1159
|
return {
|
|
978
1160
|
set: onUpdate,
|
|
979
1161
|
recursive: true
|
|
@@ -1085,7 +1267,8 @@ function tap(type, argument) {
|
|
|
1085
1267
|
}
|
|
1086
1268
|
else if (!instance && argument)
|
|
1087
1269
|
throw Oops$3.AmbientRequired(type.name, subject, key);
|
|
1088
|
-
this.
|
|
1270
|
+
this.state.set(key, instance);
|
|
1271
|
+
this.update(key);
|
|
1089
1272
|
});
|
|
1090
1273
|
return {
|
|
1091
1274
|
recursive: true,
|
|
@@ -1130,7 +1313,7 @@ function useModel(source, arg, arg2) {
|
|
|
1130
1313
|
new source() :
|
|
1131
1314
|
source() :
|
|
1132
1315
|
source;
|
|
1133
|
-
|
|
1316
|
+
ensure(instance);
|
|
1134
1317
|
if (typeof cb == "function")
|
|
1135
1318
|
cb(instance);
|
|
1136
1319
|
return instance;
|
|
@@ -1148,7 +1331,7 @@ function useModel(source, arg, arg2) {
|
|
|
1148
1331
|
return instance;
|
|
1149
1332
|
}
|
|
1150
1333
|
else {
|
|
1151
|
-
const local = use(refresh => (
|
|
1334
|
+
const local = use(refresh => (ensure(instance).subscribe(() => refresh)));
|
|
1152
1335
|
if (typeof arg == "object") {
|
|
1153
1336
|
local.active = false;
|
|
1154
1337
|
if (typeof arg2 !== "object")
|
|
@@ -1174,7 +1357,7 @@ function useModel(source, arg, arg2) {
|
|
|
1174
1357
|
|
|
1175
1358
|
function useFrom(target, compute, suspend) {
|
|
1176
1359
|
const local = use(refresh => {
|
|
1177
|
-
const sub =
|
|
1360
|
+
const sub = ensure(target).subscribe(() => update);
|
|
1178
1361
|
const spy = sub.proxy;
|
|
1179
1362
|
let value = compute.call(spy, spy);
|
|
1180
1363
|
let update;
|
|
@@ -1226,23 +1409,14 @@ function useTap(source, path, expect) {
|
|
|
1226
1409
|
}, [])();
|
|
1227
1410
|
if (typeof path == "function")
|
|
1228
1411
|
return useFrom(instance, path, expect);
|
|
1229
|
-
const local = use(refresh =>
|
|
1230
|
-
const parent = control(instance);
|
|
1231
|
-
const sub = new Subscriber(parent, () => refresh);
|
|
1232
|
-
if (typeof path == "string") {
|
|
1233
|
-
const source = sub.proxy;
|
|
1234
|
-
defineProperty(sub, "proxy", {
|
|
1235
|
-
get() {
|
|
1236
|
-
const value = source[path];
|
|
1237
|
-
if (value === undefined && expect)
|
|
1238
|
-
throw suspend(parent, path);
|
|
1239
|
-
return value;
|
|
1240
|
-
}
|
|
1241
|
-
});
|
|
1242
|
-
}
|
|
1243
|
-
return sub;
|
|
1244
|
-
});
|
|
1412
|
+
const local = use(refresh => (ensure(instance).subscribe(() => refresh)));
|
|
1245
1413
|
React__default["default"].useLayoutEffect(local.commit, []);
|
|
1414
|
+
if (typeof path !== "undefined") {
|
|
1415
|
+
const value = local.proxy[path];
|
|
1416
|
+
if (value === undefined && expect)
|
|
1417
|
+
throw suspend(ensure(instance), path);
|
|
1418
|
+
return value;
|
|
1419
|
+
}
|
|
1246
1420
|
return local.proxy;
|
|
1247
1421
|
}
|
|
1248
1422
|
|
|
@@ -1263,7 +1437,7 @@ class MVC extends Model {
|
|
|
1263
1437
|
static new(arg) {
|
|
1264
1438
|
const instance = React__default["default"].useMemo(() => {
|
|
1265
1439
|
const instance = new this();
|
|
1266
|
-
|
|
1440
|
+
ensure(instance);
|
|
1267
1441
|
if (typeof arg == "function")
|
|
1268
1442
|
arg(instance);
|
|
1269
1443
|
else if (arg)
|
|
@@ -1348,7 +1522,7 @@ function useAppliedProps(within, props) {
|
|
|
1348
1522
|
const targets = within.local;
|
|
1349
1523
|
return function integrate(props) {
|
|
1350
1524
|
for (const [key, value] of entries(props))
|
|
1351
|
-
if (["of", "for", "children"].
|
|
1525
|
+
if (!["of", "for", "children"].includes(key))
|
|
1352
1526
|
for (const into of targets)
|
|
1353
1527
|
if (into && key in into)
|
|
1354
1528
|
into[key] = value;
|
|
@@ -1360,7 +1534,7 @@ function RenderFunction(props) {
|
|
|
1360
1534
|
const hook = use(refresh => {
|
|
1361
1535
|
const targets = props.context.local;
|
|
1362
1536
|
if (targets.length == 1)
|
|
1363
|
-
return
|
|
1537
|
+
return ensure(targets[0]).subscribe(() => refresh);
|
|
1364
1538
|
return {};
|
|
1365
1539
|
});
|
|
1366
1540
|
if (hook.commit)
|
|
@@ -1426,6 +1600,7 @@ exports.array = array;
|
|
|
1426
1600
|
exports["default"] = MVC;
|
|
1427
1601
|
exports.from = from;
|
|
1428
1602
|
exports.parent = parent;
|
|
1603
|
+
exports.put = put;
|
|
1429
1604
|
exports.ref = ref;
|
|
1430
1605
|
exports.set = set;
|
|
1431
1606
|
exports.tap = tap;
|