@expressive/mvc 0.3.0 → 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 +18 -35
- package/lib/index.esm.js +642 -491
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +642 -491
- 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
|
-
this.proxy = Object.create(subject);
|
|
520
|
-
}
|
|
521
|
-
start() {
|
|
522
|
-
READY.add(this);
|
|
523
|
-
for (const key in this.subject) {
|
|
524
|
-
const { value } = getOwnPropertyDescriptor(this.subject, key);
|
|
525
|
-
if (typeof value == "function")
|
|
526
|
-
continue;
|
|
527
|
-
if (typeof value == "symbol") {
|
|
528
|
-
const instruction = PENDING.get(value);
|
|
529
|
-
if (instruction)
|
|
530
|
-
instruction.call(this, key, this);
|
|
531
|
-
continue;
|
|
532
|
-
}
|
|
533
|
-
this.manage(key, value);
|
|
534
|
-
}
|
|
535
|
-
this.flush();
|
|
536
347
|
}
|
|
537
|
-
|
|
538
|
-
this
|
|
539
|
-
this.onDestroy.forEach(x => x());
|
|
348
|
+
subscribe(callback) {
|
|
349
|
+
return new Subscriber(this, callback);
|
|
540
350
|
}
|
|
541
|
-
|
|
542
|
-
const { state } = this;
|
|
543
|
-
const
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
get
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
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, {
|
|
551
364
|
enumerable: false,
|
|
365
|
+
set: this.ref(key),
|
|
552
366
|
get() {
|
|
553
367
|
const local = this[LOCAL];
|
|
554
|
-
if (local
|
|
555
|
-
local.
|
|
556
|
-
return state
|
|
557
|
-
}
|
|
558
|
-
set
|
|
368
|
+
if (local)
|
|
369
|
+
local.add(key);
|
|
370
|
+
return state.get(key);
|
|
371
|
+
}
|
|
559
372
|
});
|
|
560
373
|
}
|
|
374
|
+
addListener(listener) {
|
|
375
|
+
this.followers.add(listener);
|
|
376
|
+
return () => {
|
|
377
|
+
this.followers.delete(listener);
|
|
378
|
+
};
|
|
379
|
+
}
|
|
561
380
|
ref(key, handler) {
|
|
562
|
-
const {
|
|
381
|
+
const { subject, state } = this;
|
|
563
382
|
return (value) => {
|
|
564
|
-
if (state
|
|
383
|
+
if (state.get(key) == value)
|
|
565
384
|
return;
|
|
566
385
|
if (handler)
|
|
567
|
-
switch (handler.call(subject, value
|
|
386
|
+
switch (handler.call(subject, value)) {
|
|
568
387
|
case true:
|
|
569
388
|
this.update(key);
|
|
570
389
|
case false:
|
|
571
390
|
return;
|
|
572
391
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
}
|
|
576
|
-
addListener(listener) {
|
|
577
|
-
this.followers.add(listener);
|
|
578
|
-
return () => {
|
|
579
|
-
this.followers.delete(listener);
|
|
392
|
+
state.set(key, value);
|
|
393
|
+
this.update(key);
|
|
580
394
|
};
|
|
581
395
|
}
|
|
582
|
-
update(key
|
|
583
|
-
const { frame } = this;
|
|
584
|
-
if (
|
|
585
|
-
|
|
586
|
-
if (!frame.size)
|
|
396
|
+
update(key) {
|
|
397
|
+
const { followers, frame, subject, waiting } = this;
|
|
398
|
+
if (frame.has(key))
|
|
399
|
+
return;
|
|
400
|
+
else if (!frame.size)
|
|
587
401
|
setTimeout(() => {
|
|
588
|
-
flush(
|
|
589
|
-
|
|
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
|
+
}
|
|
590
418
|
}, 0);
|
|
591
|
-
else if (frame.has(key))
|
|
592
|
-
return;
|
|
593
419
|
frame.add(key);
|
|
594
|
-
for (const callback of
|
|
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
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
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();
|
|
612
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
|
+
});
|
|
613
561
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
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;
|
|
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({
|
|
@@ -866,14 +844,18 @@ function put(factory, argument) {
|
|
|
866
844
|
if (output instanceof Promise) {
|
|
867
845
|
pending = output
|
|
868
846
|
.catch(err => error = err)
|
|
869
|
-
.then(val =>
|
|
847
|
+
.then(val => {
|
|
848
|
+
state.set(key, val);
|
|
849
|
+
return val;
|
|
850
|
+
})
|
|
870
851
|
.finally(() => {
|
|
871
852
|
pending = undefined;
|
|
872
853
|
this.update(key);
|
|
873
854
|
});
|
|
874
855
|
return;
|
|
875
856
|
}
|
|
876
|
-
|
|
857
|
+
state.set(key, output);
|
|
858
|
+
return output;
|
|
877
859
|
};
|
|
878
860
|
const suspend = () => {
|
|
879
861
|
if (required === false)
|
|
@@ -898,8 +880,8 @@ function put(factory, argument) {
|
|
|
898
880
|
return suspend();
|
|
899
881
|
if (error)
|
|
900
882
|
throw error;
|
|
901
|
-
if (key
|
|
902
|
-
return state
|
|
883
|
+
if (state.has(key))
|
|
884
|
+
return state.get(key);
|
|
903
885
|
let output = init();
|
|
904
886
|
return pending
|
|
905
887
|
? suspend()
|
|
@@ -926,7 +908,7 @@ function set(value, argument) {
|
|
|
926
908
|
}
|
|
927
909
|
return apply(function set(key) {
|
|
928
910
|
if (value !== undefined)
|
|
929
|
-
this.state
|
|
911
|
+
this.state.set(key, value);
|
|
930
912
|
return {
|
|
931
913
|
suspense: true,
|
|
932
914
|
set: typeof argument == "function"
|
|
@@ -956,35 +938,212 @@ function parent(Expects, required) {
|
|
|
956
938
|
});
|
|
957
939
|
}
|
|
958
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
|
+
|
|
959
1119
|
const Oops$5 = issues({
|
|
960
1120
|
BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`,
|
|
961
1121
|
});
|
|
962
1122
|
function use$1(input, argument) {
|
|
963
1123
|
return apply(function use(key) {
|
|
964
|
-
const { subject } = this;
|
|
965
|
-
|
|
966
|
-
|
|
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);
|
|
967
1137
|
if (next) {
|
|
968
1138
|
Parent.set(next, subject);
|
|
969
|
-
|
|
1139
|
+
ensure(next);
|
|
970
1140
|
}
|
|
971
1141
|
if (typeof argument == "function")
|
|
972
1142
|
argument(next);
|
|
973
1143
|
return true;
|
|
974
|
-
};
|
|
975
|
-
if (input) {
|
|
976
|
-
const value = Model.isTypeof(input)
|
|
977
|
-
? new input() :
|
|
978
|
-
typeof input === "object"
|
|
979
|
-
? input :
|
|
980
|
-
typeof input === "function"
|
|
981
|
-
? input()
|
|
982
|
-
: null;
|
|
983
|
-
if (value === null)
|
|
984
|
-
throw Oops$5.BadArgument(typeof input);
|
|
985
|
-
if (value)
|
|
986
|
-
onUpdate(value);
|
|
987
1144
|
}
|
|
1145
|
+
if (input)
|
|
1146
|
+
onUpdate(input);
|
|
988
1147
|
return {
|
|
989
1148
|
set: onUpdate,
|
|
990
1149
|
recursive: true
|
|
@@ -1096,7 +1255,8 @@ function tap(type, argument) {
|
|
|
1096
1255
|
}
|
|
1097
1256
|
else if (!instance && argument)
|
|
1098
1257
|
throw Oops$3.AmbientRequired(type.name, subject, key);
|
|
1099
|
-
this.
|
|
1258
|
+
this.state.set(key, instance);
|
|
1259
|
+
this.update(key);
|
|
1100
1260
|
});
|
|
1101
1261
|
return {
|
|
1102
1262
|
recursive: true,
|
|
@@ -1141,7 +1301,7 @@ function useModel(source, arg, arg2) {
|
|
|
1141
1301
|
new source() :
|
|
1142
1302
|
source() :
|
|
1143
1303
|
source;
|
|
1144
|
-
|
|
1304
|
+
ensure(instance);
|
|
1145
1305
|
if (typeof cb == "function")
|
|
1146
1306
|
cb(instance);
|
|
1147
1307
|
return instance;
|
|
@@ -1159,7 +1319,7 @@ function useModel(source, arg, arg2) {
|
|
|
1159
1319
|
return instance;
|
|
1160
1320
|
}
|
|
1161
1321
|
else {
|
|
1162
|
-
const local = use(refresh => (
|
|
1322
|
+
const local = use(refresh => (ensure(instance).subscribe(() => refresh)));
|
|
1163
1323
|
if (typeof arg == "object") {
|
|
1164
1324
|
local.active = false;
|
|
1165
1325
|
if (typeof arg2 !== "object")
|
|
@@ -1185,7 +1345,7 @@ function useModel(source, arg, arg2) {
|
|
|
1185
1345
|
|
|
1186
1346
|
function useFrom(target, compute, suspend) {
|
|
1187
1347
|
const local = use(refresh => {
|
|
1188
|
-
const sub =
|
|
1348
|
+
const sub = ensure(target).subscribe(() => update);
|
|
1189
1349
|
const spy = sub.proxy;
|
|
1190
1350
|
let value = compute.call(spy, spy);
|
|
1191
1351
|
let update;
|
|
@@ -1237,23 +1397,14 @@ function useTap(source, path, expect) {
|
|
|
1237
1397
|
}, [])();
|
|
1238
1398
|
if (typeof path == "function")
|
|
1239
1399
|
return useFrom(instance, path, expect);
|
|
1240
|
-
const local = use(refresh =>
|
|
1241
|
-
const parent = control(instance);
|
|
1242
|
-
const sub = new Subscriber(parent, () => refresh);
|
|
1243
|
-
if (typeof path == "string") {
|
|
1244
|
-
const source = sub.proxy;
|
|
1245
|
-
defineProperty(sub, "proxy", {
|
|
1246
|
-
get() {
|
|
1247
|
-
const value = source[path];
|
|
1248
|
-
if (value === undefined && expect)
|
|
1249
|
-
throw suspend(parent, path);
|
|
1250
|
-
return value;
|
|
1251
|
-
}
|
|
1252
|
-
});
|
|
1253
|
-
}
|
|
1254
|
-
return sub;
|
|
1255
|
-
});
|
|
1400
|
+
const local = use(refresh => (ensure(instance).subscribe(() => refresh)));
|
|
1256
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
|
+
}
|
|
1257
1408
|
return local.proxy;
|
|
1258
1409
|
}
|
|
1259
1410
|
|
|
@@ -1274,7 +1425,7 @@ class MVC extends Model {
|
|
|
1274
1425
|
static new(arg) {
|
|
1275
1426
|
const instance = React__default["default"].useMemo(() => {
|
|
1276
1427
|
const instance = new this();
|
|
1277
|
-
|
|
1428
|
+
ensure(instance);
|
|
1278
1429
|
if (typeof arg == "function")
|
|
1279
1430
|
arg(instance);
|
|
1280
1431
|
else if (arg)
|
|
@@ -1359,7 +1510,7 @@ function useAppliedProps(within, props) {
|
|
|
1359
1510
|
const targets = within.local;
|
|
1360
1511
|
return function integrate(props) {
|
|
1361
1512
|
for (const [key, value] of entries(props))
|
|
1362
|
-
if (["of", "for", "children"].
|
|
1513
|
+
if (!["of", "for", "children"].includes(key))
|
|
1363
1514
|
for (const into of targets)
|
|
1364
1515
|
if (into && key in into)
|
|
1365
1516
|
into[key] = value;
|
|
@@ -1371,7 +1522,7 @@ function RenderFunction(props) {
|
|
|
1371
1522
|
const hook = use(refresh => {
|
|
1372
1523
|
const targets = props.context.local;
|
|
1373
1524
|
if (targets.length == 1)
|
|
1374
|
-
return
|
|
1525
|
+
return ensure(targets[0]).subscribe(() => refresh);
|
|
1375
1526
|
return {};
|
|
1376
1527
|
});
|
|
1377
1528
|
if (hook.commit)
|