@expressive/mvc 0.3.0 → 0.4.2
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 +670 -491
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +670 -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,362 @@ 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
|
+
let notify;
|
|
306
|
+
if (typeof handler == "function") {
|
|
307
|
+
const callback = handler();
|
|
308
|
+
if (typeof callback == "function")
|
|
309
|
+
notify = callback;
|
|
310
|
+
else if (callback === true)
|
|
311
|
+
notify = this.onUpdate(key, parent);
|
|
312
|
+
}
|
|
313
|
+
else
|
|
314
|
+
notify = this.onUpdate(key, parent);
|
|
315
|
+
const getWhy = () => {
|
|
316
|
+
const update = getUpdate(parent.subject);
|
|
317
|
+
const applicable = update.filter(k => using.has(k));
|
|
318
|
+
UPDATE.set(proxy, applicable);
|
|
319
|
+
};
|
|
320
|
+
if (notify) {
|
|
321
|
+
parent.waiting.add(getWhy);
|
|
322
|
+
parent.waiting.add(notify);
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
this.commit = () => {
|
|
326
|
+
this.active = true;
|
|
327
|
+
this.dependant.forEach(x => x.commit());
|
|
328
|
+
return this.release;
|
|
329
|
+
};
|
|
330
|
+
this.release = () => {
|
|
331
|
+
this.dependant.forEach(x => x.release());
|
|
332
|
+
release();
|
|
333
|
+
};
|
|
334
|
+
this.add = (key, value) => {
|
|
335
|
+
if (value)
|
|
336
|
+
using.set(key, value);
|
|
337
|
+
else if (!using.has(key))
|
|
338
|
+
using.set(key, true);
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const UPDATE = new WeakMap();
|
|
344
|
+
function getUpdate(subject) {
|
|
345
|
+
return UPDATE.get(subject);
|
|
346
|
+
}
|
|
511
347
|
class Controller {
|
|
512
348
|
constructor(subject) {
|
|
513
349
|
this.subject = subject;
|
|
514
|
-
this.state = {};
|
|
515
350
|
this.frame = new Set();
|
|
516
|
-
this.onDestroy = new Set();
|
|
517
351
|
this.waiting = new Set();
|
|
518
352
|
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
353
|
}
|
|
537
|
-
|
|
538
|
-
this
|
|
539
|
-
this.onDestroy.forEach(x => x());
|
|
354
|
+
subscribe(callback) {
|
|
355
|
+
return new Subscriber(this, callback);
|
|
540
356
|
}
|
|
541
|
-
|
|
542
|
-
const { state } = this;
|
|
543
|
-
const
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
get
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
357
|
+
add(key) {
|
|
358
|
+
const { subject, state } = this;
|
|
359
|
+
const { value } = getOwnPropertyDescriptor(subject, key);
|
|
360
|
+
if (typeof value == "function")
|
|
361
|
+
return;
|
|
362
|
+
if (typeof value == "symbol") {
|
|
363
|
+
const instruction = PENDING.get(value);
|
|
364
|
+
if (instruction)
|
|
365
|
+
instruction.call(this, key, this);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
state.set(key, value);
|
|
369
|
+
defineProperty(subject, key, {
|
|
551
370
|
enumerable: false,
|
|
371
|
+
set: this.ref(key),
|
|
552
372
|
get() {
|
|
553
373
|
const local = this[LOCAL];
|
|
554
|
-
if (local
|
|
555
|
-
local.
|
|
556
|
-
return state
|
|
557
|
-
}
|
|
558
|
-
set
|
|
374
|
+
if (local)
|
|
375
|
+
local.add(key);
|
|
376
|
+
return state.get(key);
|
|
377
|
+
}
|
|
559
378
|
});
|
|
560
379
|
}
|
|
380
|
+
addListener(listener) {
|
|
381
|
+
this.followers.add(listener);
|
|
382
|
+
return () => {
|
|
383
|
+
this.followers.delete(listener);
|
|
384
|
+
};
|
|
385
|
+
}
|
|
561
386
|
ref(key, handler) {
|
|
562
|
-
const {
|
|
387
|
+
const { subject, state } = this;
|
|
563
388
|
return (value) => {
|
|
564
|
-
if (state
|
|
389
|
+
if (state.get(key) == value)
|
|
565
390
|
return;
|
|
566
391
|
if (handler)
|
|
567
|
-
switch (handler.call(subject, value
|
|
392
|
+
switch (handler.call(subject, value)) {
|
|
568
393
|
case true:
|
|
569
394
|
this.update(key);
|
|
570
395
|
case false:
|
|
571
396
|
return;
|
|
572
397
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
}
|
|
576
|
-
addListener(listener) {
|
|
577
|
-
this.followers.add(listener);
|
|
578
|
-
return () => {
|
|
579
|
-
this.followers.delete(listener);
|
|
398
|
+
state.set(key, value);
|
|
399
|
+
this.update(key);
|
|
580
400
|
};
|
|
581
401
|
}
|
|
582
|
-
update(key
|
|
583
|
-
const { frame } = this;
|
|
584
|
-
if (
|
|
585
|
-
|
|
586
|
-
if (!frame.size)
|
|
402
|
+
update(key) {
|
|
403
|
+
const { followers, frame, subject, waiting } = this;
|
|
404
|
+
if (frame.has(key))
|
|
405
|
+
return;
|
|
406
|
+
else if (!frame.size)
|
|
587
407
|
setTimeout(() => {
|
|
588
|
-
flush(
|
|
589
|
-
|
|
408
|
+
flush(this);
|
|
409
|
+
const callback = Array.from(waiting);
|
|
410
|
+
const keys = Array.from(frame);
|
|
411
|
+
frame.clear();
|
|
412
|
+
waiting.clear();
|
|
413
|
+
UPDATE.set(subject, keys);
|
|
414
|
+
setTimeout(() => {
|
|
415
|
+
UPDATE.delete(subject);
|
|
416
|
+
}, 0);
|
|
417
|
+
for (const cb of callback)
|
|
418
|
+
try {
|
|
419
|
+
cb();
|
|
420
|
+
}
|
|
421
|
+
catch (err) {
|
|
422
|
+
console.error(err);
|
|
423
|
+
}
|
|
590
424
|
}, 0);
|
|
591
|
-
else if (frame.has(key))
|
|
592
|
-
return;
|
|
593
425
|
frame.add(key);
|
|
594
|
-
for (const callback of
|
|
426
|
+
for (const callback of followers) {
|
|
595
427
|
const event = callback(key, this);
|
|
596
428
|
if (typeof event == "function")
|
|
597
|
-
|
|
429
|
+
waiting.add(event);
|
|
598
430
|
}
|
|
599
431
|
}
|
|
600
|
-
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
432
|
+
clear() {
|
|
433
|
+
const listeners = [...this.followers];
|
|
434
|
+
this.followers.clear();
|
|
435
|
+
listeners.forEach(x => x(null, this));
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const Oops$9 = issues({
|
|
440
|
+
Timeout: (keys, timeout) => `No update for [${keys}] within ${timeout}.`,
|
|
441
|
+
StrictUpdate: () => `Strict update() did not find pending updates.`,
|
|
442
|
+
NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
|
|
443
|
+
});
|
|
444
|
+
const CONTROL = Symbol("CONTROL");
|
|
445
|
+
const WHY = Symbol("UPDATE");
|
|
446
|
+
const LOCAL = Symbol("LOCAL");
|
|
447
|
+
const STATE = Symbol("STATE");
|
|
448
|
+
class Model {
|
|
449
|
+
constructor() {
|
|
450
|
+
define(this, CONTROL, new Controller(this));
|
|
451
|
+
define(this, "get", this);
|
|
452
|
+
define(this, "is", this);
|
|
453
|
+
defineLazy(this, "set", () => {
|
|
454
|
+
const controller = ensure(this);
|
|
455
|
+
const assign = (key, value) => {
|
|
456
|
+
controller.state.set(key, value);
|
|
457
|
+
controller.update(key);
|
|
458
|
+
};
|
|
459
|
+
for (const key of controller.state.keys())
|
|
460
|
+
define(assign, key, controller.ref(key));
|
|
461
|
+
return assign;
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
get [STATE]() {
|
|
465
|
+
return this.export();
|
|
466
|
+
}
|
|
467
|
+
get [WHY]() {
|
|
468
|
+
return UPDATE.get(this);
|
|
469
|
+
}
|
|
470
|
+
on(select, handler, squash, once) {
|
|
471
|
+
return ensure(this, control => {
|
|
472
|
+
if (typeof select == "function")
|
|
473
|
+
return control.addListener(select);
|
|
474
|
+
const keys = typeof select == "string" ? [select] :
|
|
475
|
+
select.length ? select : [...control.state.keys()];
|
|
476
|
+
for (const key of keys)
|
|
477
|
+
try {
|
|
478
|
+
void this[key];
|
|
479
|
+
}
|
|
480
|
+
catch (e) { }
|
|
481
|
+
const callback = squash
|
|
482
|
+
? () => {
|
|
483
|
+
handler.call(this, getUpdate(this));
|
|
484
|
+
}
|
|
485
|
+
: () => {
|
|
486
|
+
getUpdate(this)
|
|
487
|
+
.filter(k => keys.includes(k))
|
|
488
|
+
.forEach(k => {
|
|
489
|
+
handler.call(this, control.state.get(k), k);
|
|
490
|
+
});
|
|
491
|
+
};
|
|
492
|
+
const onEvent = once
|
|
493
|
+
? () => {
|
|
494
|
+
remove();
|
|
495
|
+
callback();
|
|
496
|
+
}
|
|
497
|
+
: callback;
|
|
498
|
+
const remove = control.addListener(key => {
|
|
499
|
+
if (keys.includes(key))
|
|
500
|
+
return onEvent;
|
|
501
|
+
});
|
|
502
|
+
return remove;
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
once(select, argument, squash) {
|
|
506
|
+
if (typeof argument == "function")
|
|
507
|
+
return this.on(select, argument, squash, true);
|
|
508
|
+
if (typeof select == "string")
|
|
509
|
+
select = [select];
|
|
510
|
+
return new Promise((resolve, reject) => {
|
|
511
|
+
const invoke = (key) => {
|
|
512
|
+
if (!key) {
|
|
513
|
+
clear();
|
|
514
|
+
reject(Oops$9.Timeout(select, typeof argument == "number"
|
|
515
|
+
? `${argument}ms`
|
|
516
|
+
: `lifetime of ${this}`));
|
|
517
|
+
}
|
|
518
|
+
else if (select.includes(key)) {
|
|
519
|
+
clear();
|
|
520
|
+
return resolve;
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
const clear = ensure(this, x => {
|
|
524
|
+
for (const key of select)
|
|
525
|
+
try {
|
|
526
|
+
void this[key];
|
|
527
|
+
}
|
|
528
|
+
catch (e) { }
|
|
529
|
+
return x.addListener(invoke);
|
|
530
|
+
});
|
|
531
|
+
if (typeof argument == "number")
|
|
532
|
+
setTimeout(invoke, argument);
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
effect(callback, select) {
|
|
536
|
+
const effect = createEffect(callback);
|
|
537
|
+
return ensure(this, control => {
|
|
538
|
+
let busy = false;
|
|
539
|
+
let inject = this.is;
|
|
540
|
+
const invoke = () => {
|
|
541
|
+
if (busy)
|
|
542
|
+
return;
|
|
543
|
+
const output = mayRetry(() => {
|
|
544
|
+
effect.call(inject, inject);
|
|
545
|
+
});
|
|
546
|
+
if (output instanceof Promise) {
|
|
547
|
+
output.finally(() => busy = false);
|
|
548
|
+
busy = true;
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
if (!select) {
|
|
552
|
+
const sub = control.subscribe(() => invoke);
|
|
553
|
+
inject = sub.proxy;
|
|
554
|
+
invoke();
|
|
555
|
+
return sub.commit();
|
|
612
556
|
}
|
|
557
|
+
invoke();
|
|
558
|
+
return control.addListener(key => {
|
|
559
|
+
if (key === null) {
|
|
560
|
+
if (!select.length)
|
|
561
|
+
invoke();
|
|
562
|
+
}
|
|
563
|
+
else if (select.includes(key))
|
|
564
|
+
return invoke;
|
|
565
|
+
});
|
|
566
|
+
});
|
|
613
567
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
568
|
+
// TODO: account for exotic properties
|
|
569
|
+
import(source, select) {
|
|
570
|
+
const { subject } = ensure(this);
|
|
571
|
+
if (!select)
|
|
572
|
+
select = getOwnPropertyNames(subject);
|
|
573
|
+
for (const key of select)
|
|
574
|
+
if (key in source)
|
|
575
|
+
this[key] = source[key];
|
|
576
|
+
}
|
|
577
|
+
export(subset) {
|
|
578
|
+
const { state } = ensure(this);
|
|
579
|
+
const output = {};
|
|
580
|
+
const keys = subset || state.keys();
|
|
581
|
+
for (const key of keys)
|
|
582
|
+
output[key] = state.get(key);
|
|
583
|
+
return output;
|
|
584
|
+
}
|
|
585
|
+
update(arg, tag) {
|
|
586
|
+
const target = ensure(this);
|
|
587
|
+
const { frame, waiting } = target;
|
|
588
|
+
if (typeof arg == "string") {
|
|
589
|
+
target.update(arg);
|
|
590
|
+
if (1 in arguments && arg in this) {
|
|
591
|
+
const method = this[arg];
|
|
592
|
+
if (typeof method == "function")
|
|
593
|
+
if (typeof tag != "boolean")
|
|
594
|
+
method.call(this, tag);
|
|
595
|
+
else if (tag)
|
|
596
|
+
method.call(this);
|
|
597
|
+
}
|
|
598
|
+
arg = undefined;
|
|
619
599
|
}
|
|
620
600
|
if (!frame.size && arg === true)
|
|
621
601
|
return Promise.reject(Oops$9.StrictUpdate());
|
|
@@ -625,37 +605,51 @@ class Controller {
|
|
|
625
605
|
throw Oops$9.NoChaining();
|
|
626
606
|
if (frame.size || arg !== false)
|
|
627
607
|
waiting.add(() => {
|
|
628
|
-
callback(getUpdate(
|
|
608
|
+
callback(getUpdate(this));
|
|
629
609
|
});
|
|
630
610
|
else
|
|
631
611
|
callback(false);
|
|
632
612
|
}
|
|
633
613
|
};
|
|
634
614
|
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
define(subject, CONTROL, control);
|
|
615
|
+
/**
|
|
616
|
+
* Mark this instance for garbage-collection and send `willDestroy` event to all listeners.
|
|
617
|
+
*/
|
|
618
|
+
destroy() {
|
|
619
|
+
ensure(this).clear();
|
|
641
620
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
621
|
+
toString() {
|
|
622
|
+
return this.constructor.name;
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Creates a new instance of this controller.
|
|
626
|
+
*
|
|
627
|
+
* Beyond `new this(...)`, method will activate managed-state.
|
|
628
|
+
*
|
|
629
|
+
* @param args - arguments sent to constructor
|
|
630
|
+
*/
|
|
631
|
+
static create(...args) {
|
|
632
|
+
const instance = new this(...args);
|
|
633
|
+
ensure(instance);
|
|
634
|
+
return instance;
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Static equivalent of `x instanceof this`.
|
|
638
|
+
*
|
|
639
|
+
* Will determine if provided class is a subtype of this one.
|
|
640
|
+
*/
|
|
641
|
+
static isTypeof(maybe) {
|
|
642
|
+
return (typeof maybe == "function" &&
|
|
643
|
+
maybe.prototype instanceof this);
|
|
650
644
|
}
|
|
651
|
-
return cb ? cb(control) : control;
|
|
652
645
|
}
|
|
653
646
|
|
|
647
|
+
const PENDING = new Map();
|
|
654
648
|
function apply(fn, label) {
|
|
655
649
|
const name = label || fn.name || "pending";
|
|
656
650
|
const placeholder = Symbol(`${name} instruction`);
|
|
657
651
|
function setup(key) {
|
|
658
|
-
const {
|
|
652
|
+
const { subject, state } = this;
|
|
659
653
|
const control = this;
|
|
660
654
|
PENDING.delete(placeholder);
|
|
661
655
|
delete subject[key];
|
|
@@ -669,67 +663,56 @@ function apply(fn, label) {
|
|
|
669
663
|
return false;
|
|
670
664
|
}
|
|
671
665
|
if ("recursive" in output && output.recursive)
|
|
672
|
-
output = Object.assign(Object.assign({}, output), { get:
|
|
666
|
+
output = Object.assign(Object.assign({}, output), { get: getRecursive(key, this) });
|
|
673
667
|
const desc = output;
|
|
674
668
|
if ("value" in desc)
|
|
675
|
-
state
|
|
669
|
+
state.set(key, desc.value);
|
|
676
670
|
const { get: onGet, set: onSet, enumerable, suspense } = desc;
|
|
677
|
-
|
|
678
|
-
?
|
|
679
|
-
:
|
|
671
|
+
const set = onSet !== false
|
|
672
|
+
? this.ref(key, onSet)
|
|
673
|
+
: undefined;
|
|
680
674
|
function get() {
|
|
681
|
-
if (!(key
|
|
675
|
+
if (!state.has(key) && suspense)
|
|
682
676
|
throw suspend(control, key);
|
|
683
677
|
const local = this[LOCAL];
|
|
684
|
-
if (local
|
|
685
|
-
local.
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
return local
|
|
690
|
-
? onGet(value, local)
|
|
691
|
-
: onGet(value);
|
|
678
|
+
if (local)
|
|
679
|
+
local.add(key);
|
|
680
|
+
return onGet
|
|
681
|
+
? onGet(local)
|
|
682
|
+
: state.get(key);
|
|
692
683
|
}
|
|
693
|
-
|
|
694
|
-
defineProperty(x, key, { enumerable, get, set });
|
|
684
|
+
defineProperty(subject, key, { enumerable, get, set });
|
|
695
685
|
}
|
|
696
686
|
PENDING.set(placeholder, setup);
|
|
697
687
|
return placeholder;
|
|
698
688
|
}
|
|
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) {
|
|
689
|
+
function getRecursive(key, from) {
|
|
723
690
|
const context = new WeakMap();
|
|
724
|
-
|
|
691
|
+
const { state } = from;
|
|
692
|
+
return (local) => {
|
|
725
693
|
if (!local)
|
|
726
|
-
return
|
|
694
|
+
return state.get(key);
|
|
727
695
|
if (!context.has(local)) {
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
696
|
+
let child;
|
|
697
|
+
const init = () => {
|
|
698
|
+
if (child) {
|
|
699
|
+
child.release();
|
|
700
|
+
local.dependant.delete(child);
|
|
701
|
+
context.set(local, undefined);
|
|
702
|
+
child = undefined;
|
|
703
|
+
}
|
|
704
|
+
const value = state.get(key);
|
|
705
|
+
if (value && CONTROL in value) {
|
|
706
|
+
child = ensure(value).subscribe(local.onUpdate);
|
|
707
|
+
if (local.active)
|
|
708
|
+
child.commit();
|
|
709
|
+
local.dependant.add(child);
|
|
710
|
+
context.set(local, child.proxy);
|
|
711
|
+
}
|
|
712
|
+
return true;
|
|
713
|
+
};
|
|
714
|
+
init();
|
|
715
|
+
local.add(key, init);
|
|
733
716
|
}
|
|
734
717
|
return context.get(local);
|
|
735
718
|
};
|
|
@@ -754,7 +737,7 @@ function act(task) {
|
|
|
754
737
|
this.update(key);
|
|
755
738
|
}
|
|
756
739
|
});
|
|
757
|
-
this.state
|
|
740
|
+
this.state.set(key, undefined);
|
|
758
741
|
defineProperty(invoke, "active", {
|
|
759
742
|
get: () => pending
|
|
760
743
|
});
|
|
@@ -775,7 +758,7 @@ class Managed extends Array {
|
|
|
775
758
|
};
|
|
776
759
|
}
|
|
777
760
|
push(...items) {
|
|
778
|
-
const length = this
|
|
761
|
+
const { length } = this;
|
|
779
762
|
const end = super.push(...items);
|
|
780
763
|
this.update(length, end);
|
|
781
764
|
return end;
|
|
@@ -786,64 +769,65 @@ class Managed extends Array {
|
|
|
786
769
|
return end;
|
|
787
770
|
}
|
|
788
771
|
splice(start, deleteCount, ...rest) {
|
|
789
|
-
|
|
772
|
+
const end = deleteCount !== rest.length ? Infinity : deleteCount;
|
|
773
|
+
this.update(start, end);
|
|
790
774
|
return super.splice(start, deleteCount, ...rest);
|
|
791
775
|
}
|
|
792
776
|
}
|
|
793
777
|
function array() {
|
|
794
778
|
return apply(function array(key) {
|
|
779
|
+
const context = new WeakMap();
|
|
795
780
|
const array = new Managed(() => this.update(key));
|
|
796
|
-
const
|
|
797
|
-
const
|
|
798
|
-
const update = context.onUpdate(key, this);
|
|
781
|
+
const getLocal = (context) => {
|
|
782
|
+
const proxy = Object.create(array);
|
|
799
783
|
let rangeA = 0;
|
|
800
784
|
let rangeB = 0;
|
|
801
|
-
|
|
785
|
+
const onUpdate = () => {
|
|
802
786
|
const [lastA, lastB] = array.lastUpdate;
|
|
803
787
|
if (rangeA <= lastB && rangeB >= lastA)
|
|
804
|
-
return
|
|
805
|
-
}
|
|
806
|
-
|
|
788
|
+
return true;
|
|
789
|
+
};
|
|
790
|
+
const listen = (eventA, eventB) => {
|
|
807
791
|
if (context.active)
|
|
808
792
|
return;
|
|
809
|
-
context.
|
|
793
|
+
context.add(key, onUpdate);
|
|
810
794
|
rangeA = Math.min(rangeA, eventA);
|
|
811
795
|
rangeB = Math.max(rangeB, eventB);
|
|
812
|
-
}
|
|
813
|
-
|
|
796
|
+
};
|
|
797
|
+
proxy[Symbol.iterator] = () => {
|
|
814
798
|
listen(0, Infinity);
|
|
815
799
|
return array[Symbol.iterator]();
|
|
816
800
|
};
|
|
817
|
-
return
|
|
818
|
-
}
|
|
801
|
+
return proxy;
|
|
802
|
+
};
|
|
819
803
|
return {
|
|
820
|
-
|
|
804
|
+
value: array,
|
|
821
805
|
set: false,
|
|
822
|
-
|
|
806
|
+
get: (local) => {
|
|
807
|
+
if (!local)
|
|
808
|
+
return this.state.get(key);
|
|
809
|
+
if (context.has(local))
|
|
810
|
+
return context.get(local);
|
|
811
|
+
const output = getLocal(local);
|
|
812
|
+
context.set(local, output);
|
|
813
|
+
return output;
|
|
814
|
+
}
|
|
823
815
|
};
|
|
824
816
|
});
|
|
825
817
|
}
|
|
826
818
|
|
|
827
|
-
function ref(
|
|
819
|
+
function ref(callback) {
|
|
828
820
|
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]
|
|
821
|
+
const value = this.ref(key, callback && createValueEffect(callback));
|
|
822
|
+
defineProperty(value, "current", {
|
|
823
|
+
set: value,
|
|
824
|
+
get: () => this.state.get(key)
|
|
825
|
+
});
|
|
826
|
+
return {
|
|
827
|
+
value,
|
|
828
|
+
explicit: true
|
|
829
|
+
};
|
|
845
830
|
});
|
|
846
|
-
return refObjectFunction;
|
|
847
831
|
}
|
|
848
832
|
|
|
849
833
|
const Oops$7 = issues({
|
|
@@ -866,14 +850,18 @@ function put(factory, argument) {
|
|
|
866
850
|
if (output instanceof Promise) {
|
|
867
851
|
pending = output
|
|
868
852
|
.catch(err => error = err)
|
|
869
|
-
.then(val =>
|
|
853
|
+
.then(val => {
|
|
854
|
+
state.set(key, val);
|
|
855
|
+
return val;
|
|
856
|
+
})
|
|
870
857
|
.finally(() => {
|
|
871
858
|
pending = undefined;
|
|
872
859
|
this.update(key);
|
|
873
860
|
});
|
|
874
861
|
return;
|
|
875
862
|
}
|
|
876
|
-
|
|
863
|
+
state.set(key, output);
|
|
864
|
+
return output;
|
|
877
865
|
};
|
|
878
866
|
const suspend = () => {
|
|
879
867
|
if (required === false)
|
|
@@ -898,8 +886,8 @@ function put(factory, argument) {
|
|
|
898
886
|
return suspend();
|
|
899
887
|
if (error)
|
|
900
888
|
throw error;
|
|
901
|
-
if (key
|
|
902
|
-
return state
|
|
889
|
+
if (state.has(key))
|
|
890
|
+
return state.get(key);
|
|
903
891
|
let output = init();
|
|
904
892
|
return pending
|
|
905
893
|
? suspend()
|
|
@@ -926,7 +914,7 @@ function set(value, argument) {
|
|
|
926
914
|
}
|
|
927
915
|
return apply(function set(key) {
|
|
928
916
|
if (value !== undefined)
|
|
929
|
-
this.state
|
|
917
|
+
this.state.set(key, value);
|
|
930
918
|
return {
|
|
931
919
|
suspense: true,
|
|
932
920
|
set: typeof argument == "function"
|
|
@@ -956,35 +944,234 @@ function parent(Expects, required) {
|
|
|
956
944
|
});
|
|
957
945
|
}
|
|
958
946
|
|
|
947
|
+
const ANY$1 = Symbol("any");
|
|
948
|
+
function managedMap(control, property, initial) {
|
|
949
|
+
const lastUpdate = new Set();
|
|
950
|
+
const context = new WeakMap();
|
|
951
|
+
const assoc = new WeakMap();
|
|
952
|
+
let managed;
|
|
953
|
+
let value;
|
|
954
|
+
function reset() {
|
|
955
|
+
lastUpdate.clear();
|
|
956
|
+
}
|
|
957
|
+
function emit(key) {
|
|
958
|
+
lastUpdate.add(key);
|
|
959
|
+
control.update(property);
|
|
960
|
+
control.waiting.add(reset);
|
|
961
|
+
}
|
|
962
|
+
function setValue(next, initial) {
|
|
963
|
+
value = next;
|
|
964
|
+
managed = create(next);
|
|
965
|
+
control.state.set(property, next);
|
|
966
|
+
if (!initial)
|
|
967
|
+
emit(ANY$1);
|
|
968
|
+
const watch = (key, on) => {
|
|
969
|
+
const local = assoc.get(on);
|
|
970
|
+
if (local)
|
|
971
|
+
local(key);
|
|
972
|
+
};
|
|
973
|
+
managed.set = (k, v) => {
|
|
974
|
+
emit(k);
|
|
975
|
+
return value.set(k, v);
|
|
976
|
+
};
|
|
977
|
+
managed.delete = (k) => {
|
|
978
|
+
emit(k);
|
|
979
|
+
return value.delete(k);
|
|
980
|
+
};
|
|
981
|
+
managed.clear = () => {
|
|
982
|
+
emit(ANY$1);
|
|
983
|
+
value.clear();
|
|
984
|
+
};
|
|
985
|
+
managed.get = function (key) {
|
|
986
|
+
watch(key, this);
|
|
987
|
+
return value.get(key);
|
|
988
|
+
};
|
|
989
|
+
managed.has = function (key) {
|
|
990
|
+
watch(key, this);
|
|
991
|
+
return value.has(key);
|
|
992
|
+
};
|
|
993
|
+
managed.values = function () {
|
|
994
|
+
watch(ANY$1, this);
|
|
995
|
+
return value.values();
|
|
996
|
+
};
|
|
997
|
+
managed.keys = function () {
|
|
998
|
+
watch(ANY$1, this);
|
|
999
|
+
return value.keys();
|
|
1000
|
+
};
|
|
1001
|
+
managed.entries = function () {
|
|
1002
|
+
watch(ANY$1, this);
|
|
1003
|
+
return value.entries();
|
|
1004
|
+
};
|
|
1005
|
+
managed[Symbol.iterator] = function () {
|
|
1006
|
+
watch(ANY$1, this);
|
|
1007
|
+
return value[Symbol.iterator]();
|
|
1008
|
+
};
|
|
1009
|
+
defineProperty(managed, "size", {
|
|
1010
|
+
get() {
|
|
1011
|
+
watch(ANY$1, this);
|
|
1012
|
+
return value.size;
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
function getValue(local) {
|
|
1017
|
+
if (!local)
|
|
1018
|
+
return managed;
|
|
1019
|
+
if (context.has(local))
|
|
1020
|
+
return context.get(local);
|
|
1021
|
+
const refresh = local.onUpdate(property, control);
|
|
1022
|
+
const proxy = Object.create(managed);
|
|
1023
|
+
const using = new Set();
|
|
1024
|
+
const update = () => {
|
|
1025
|
+
if (using.has(ANY$1) || lastUpdate.has(ANY$1) && using.size)
|
|
1026
|
+
refresh();
|
|
1027
|
+
else
|
|
1028
|
+
for (const key of lastUpdate)
|
|
1029
|
+
if (using.has(key)) {
|
|
1030
|
+
refresh();
|
|
1031
|
+
break;
|
|
1032
|
+
}
|
|
1033
|
+
};
|
|
1034
|
+
context.set(local, proxy);
|
|
1035
|
+
assoc.set(proxy, (key) => {
|
|
1036
|
+
if (!local.active)
|
|
1037
|
+
using.add(key);
|
|
1038
|
+
});
|
|
1039
|
+
local.add(property, () => update);
|
|
1040
|
+
return proxy;
|
|
1041
|
+
}
|
|
1042
|
+
setValue(initial, true);
|
|
1043
|
+
return {
|
|
1044
|
+
set: setValue,
|
|
1045
|
+
get: getValue
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
const ANY = Symbol("any");
|
|
1050
|
+
function managedSet(control, property, initial) {
|
|
1051
|
+
const lastUpdate = new Set();
|
|
1052
|
+
const context = new WeakMap();
|
|
1053
|
+
const assoc = new WeakMap();
|
|
1054
|
+
let managed;
|
|
1055
|
+
let value;
|
|
1056
|
+
function reset() {
|
|
1057
|
+
lastUpdate.clear();
|
|
1058
|
+
}
|
|
1059
|
+
function emit(key) {
|
|
1060
|
+
lastUpdate.add(key);
|
|
1061
|
+
control.update(property);
|
|
1062
|
+
control.waiting.add(reset);
|
|
1063
|
+
}
|
|
1064
|
+
function setValue(next, initial) {
|
|
1065
|
+
value = next;
|
|
1066
|
+
managed = create(next);
|
|
1067
|
+
control.state.set(property, next);
|
|
1068
|
+
if (!initial)
|
|
1069
|
+
emit(ANY);
|
|
1070
|
+
const watch = (key, on) => {
|
|
1071
|
+
const local = assoc.get(on);
|
|
1072
|
+
if (local)
|
|
1073
|
+
local(key);
|
|
1074
|
+
};
|
|
1075
|
+
managed.add = (k) => {
|
|
1076
|
+
emit(k);
|
|
1077
|
+
return value.add(k);
|
|
1078
|
+
};
|
|
1079
|
+
managed.delete = (k) => {
|
|
1080
|
+
emit(k);
|
|
1081
|
+
return value.delete(k);
|
|
1082
|
+
};
|
|
1083
|
+
managed.clear = () => {
|
|
1084
|
+
emit(ANY);
|
|
1085
|
+
value.clear();
|
|
1086
|
+
};
|
|
1087
|
+
managed.has = function (key) {
|
|
1088
|
+
watch(key, this);
|
|
1089
|
+
return value.has(key);
|
|
1090
|
+
};
|
|
1091
|
+
managed.values = function () {
|
|
1092
|
+
watch(ANY, this);
|
|
1093
|
+
return value.values();
|
|
1094
|
+
};
|
|
1095
|
+
managed.keys = function () {
|
|
1096
|
+
watch(ANY, this);
|
|
1097
|
+
return value.keys();
|
|
1098
|
+
};
|
|
1099
|
+
managed.entries = function () {
|
|
1100
|
+
watch(ANY, this);
|
|
1101
|
+
return value.entries();
|
|
1102
|
+
};
|
|
1103
|
+
managed[Symbol.iterator] = function () {
|
|
1104
|
+
watch(ANY, this);
|
|
1105
|
+
return value[Symbol.iterator]();
|
|
1106
|
+
};
|
|
1107
|
+
defineProperty(managed, "size", {
|
|
1108
|
+
get() {
|
|
1109
|
+
watch(ANY, this);
|
|
1110
|
+
return value.size;
|
|
1111
|
+
}
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
function getValue(local) {
|
|
1115
|
+
if (!local)
|
|
1116
|
+
return managed;
|
|
1117
|
+
if (context.has(local))
|
|
1118
|
+
return context.get(local);
|
|
1119
|
+
const refresh = local.onUpdate(property, control);
|
|
1120
|
+
const proxy = Object.create(managed);
|
|
1121
|
+
const using = new Set();
|
|
1122
|
+
const update = () => {
|
|
1123
|
+
if (using.has(ANY) || lastUpdate.has(ANY) && using.size)
|
|
1124
|
+
refresh();
|
|
1125
|
+
else
|
|
1126
|
+
for (const key of lastUpdate)
|
|
1127
|
+
if (using.has(key)) {
|
|
1128
|
+
refresh();
|
|
1129
|
+
break;
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
context.set(local, proxy);
|
|
1133
|
+
assoc.set(proxy, (key) => {
|
|
1134
|
+
if (!local.active)
|
|
1135
|
+
using.add(key);
|
|
1136
|
+
});
|
|
1137
|
+
local.add(property, () => update);
|
|
1138
|
+
return proxy;
|
|
1139
|
+
}
|
|
1140
|
+
setValue(initial, true);
|
|
1141
|
+
return {
|
|
1142
|
+
set: setValue,
|
|
1143
|
+
get: getValue
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
|
|
959
1147
|
const Oops$5 = issues({
|
|
960
1148
|
BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`,
|
|
961
1149
|
});
|
|
962
1150
|
function use$1(input, argument) {
|
|
963
1151
|
return apply(function use(key) {
|
|
964
|
-
const { subject } = this;
|
|
965
|
-
|
|
966
|
-
|
|
1152
|
+
const { state, subject } = this;
|
|
1153
|
+
if (typeof input === "function")
|
|
1154
|
+
input = Model.isTypeof(input)
|
|
1155
|
+
? new input()
|
|
1156
|
+
: input();
|
|
1157
|
+
else if (input && typeof input !== "object")
|
|
1158
|
+
throw Oops$5.BadArgument(typeof input);
|
|
1159
|
+
if (input instanceof Map)
|
|
1160
|
+
return managedMap(this, key, input);
|
|
1161
|
+
if (input instanceof Set)
|
|
1162
|
+
return managedSet(this, key, input);
|
|
1163
|
+
function onUpdate(next) {
|
|
1164
|
+
state.set(key, next);
|
|
967
1165
|
if (next) {
|
|
968
1166
|
Parent.set(next, subject);
|
|
969
|
-
|
|
1167
|
+
ensure(next);
|
|
970
1168
|
}
|
|
971
1169
|
if (typeof argument == "function")
|
|
972
1170
|
argument(next);
|
|
973
1171
|
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
1172
|
}
|
|
1173
|
+
if (input)
|
|
1174
|
+
onUpdate(input);
|
|
988
1175
|
return {
|
|
989
1176
|
set: onUpdate,
|
|
990
1177
|
recursive: true
|
|
@@ -1096,7 +1283,8 @@ function tap(type, argument) {
|
|
|
1096
1283
|
}
|
|
1097
1284
|
else if (!instance && argument)
|
|
1098
1285
|
throw Oops$3.AmbientRequired(type.name, subject, key);
|
|
1099
|
-
this.
|
|
1286
|
+
this.state.set(key, instance);
|
|
1287
|
+
this.update(key);
|
|
1100
1288
|
});
|
|
1101
1289
|
return {
|
|
1102
1290
|
recursive: true,
|
|
@@ -1141,7 +1329,7 @@ function useModel(source, arg, arg2) {
|
|
|
1141
1329
|
new source() :
|
|
1142
1330
|
source() :
|
|
1143
1331
|
source;
|
|
1144
|
-
|
|
1332
|
+
ensure(instance);
|
|
1145
1333
|
if (typeof cb == "function")
|
|
1146
1334
|
cb(instance);
|
|
1147
1335
|
return instance;
|
|
@@ -1159,7 +1347,7 @@ function useModel(source, arg, arg2) {
|
|
|
1159
1347
|
return instance;
|
|
1160
1348
|
}
|
|
1161
1349
|
else {
|
|
1162
|
-
const local = use(refresh => (
|
|
1350
|
+
const local = use(refresh => (ensure(instance).subscribe(() => refresh)));
|
|
1163
1351
|
if (typeof arg == "object") {
|
|
1164
1352
|
local.active = false;
|
|
1165
1353
|
if (typeof arg2 !== "object")
|
|
@@ -1185,7 +1373,7 @@ function useModel(source, arg, arg2) {
|
|
|
1185
1373
|
|
|
1186
1374
|
function useFrom(target, compute, suspend) {
|
|
1187
1375
|
const local = use(refresh => {
|
|
1188
|
-
const sub =
|
|
1376
|
+
const sub = ensure(target).subscribe(() => update);
|
|
1189
1377
|
const spy = sub.proxy;
|
|
1190
1378
|
let value = compute.call(spy, spy);
|
|
1191
1379
|
let update;
|
|
@@ -1237,23 +1425,14 @@ function useTap(source, path, expect) {
|
|
|
1237
1425
|
}, [])();
|
|
1238
1426
|
if (typeof path == "function")
|
|
1239
1427
|
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
|
-
});
|
|
1428
|
+
const local = use(refresh => (ensure(instance).subscribe(() => refresh)));
|
|
1256
1429
|
React__default["default"].useLayoutEffect(local.commit, []);
|
|
1430
|
+
if (typeof path !== "undefined") {
|
|
1431
|
+
const value = local.proxy[path];
|
|
1432
|
+
if (value === undefined && expect)
|
|
1433
|
+
throw suspend(ensure(instance), path);
|
|
1434
|
+
return value;
|
|
1435
|
+
}
|
|
1257
1436
|
return local.proxy;
|
|
1258
1437
|
}
|
|
1259
1438
|
|
|
@@ -1274,7 +1453,7 @@ class MVC extends Model {
|
|
|
1274
1453
|
static new(arg) {
|
|
1275
1454
|
const instance = React__default["default"].useMemo(() => {
|
|
1276
1455
|
const instance = new this();
|
|
1277
|
-
|
|
1456
|
+
ensure(instance);
|
|
1278
1457
|
if (typeof arg == "function")
|
|
1279
1458
|
arg(instance);
|
|
1280
1459
|
else if (arg)
|
|
@@ -1359,7 +1538,7 @@ function useAppliedProps(within, props) {
|
|
|
1359
1538
|
const targets = within.local;
|
|
1360
1539
|
return function integrate(props) {
|
|
1361
1540
|
for (const [key, value] of entries(props))
|
|
1362
|
-
if (["of", "for", "children"].
|
|
1541
|
+
if (!["of", "for", "children"].includes(key))
|
|
1363
1542
|
for (const into of targets)
|
|
1364
1543
|
if (into && key in into)
|
|
1365
1544
|
into[key] = value;
|
|
@@ -1371,7 +1550,7 @@ function RenderFunction(props) {
|
|
|
1371
1550
|
const hook = use(refresh => {
|
|
1372
1551
|
const targets = props.context.local;
|
|
1373
1552
|
if (targets.length == 1)
|
|
1374
|
-
return
|
|
1553
|
+
return ensure(targets[0]).subscribe(() => refresh);
|
|
1375
1554
|
return {};
|
|
1376
1555
|
});
|
|
1377
1556
|
if (hook.commit)
|