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