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