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