@expressive/mvc 0.17.1 → 0.18.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.js CHANGED
@@ -2,11 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var React = require('react');
6
-
7
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
-
9
- var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
5
+ const { assign, create, defineProperties, defineProperty, entries, getPrototypeOf, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
10
6
 
11
7
  class Issue extends Error {
12
8
  constructor() {
@@ -25,31 +21,17 @@ function issues(register) {
25
21
  return Library;
26
22
  }
27
23
 
28
- const { assign, create, defineProperties, defineProperty, entries, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
29
- const UID_CACHE = new WeakMap();
30
- function assignWeak(into, from) {
31
- for (const K in from)
32
- if (K in into)
33
- into[K] = from[K];
34
- }
35
- function uid(object) {
36
- let uid = UID_CACHE.get(object);
37
- if (!uid)
38
- UID_CACHE.set(object, uid = Math.random());
39
- return uid;
40
- }
41
-
42
24
  const REGISTER$1 = new WeakMap();
43
25
  class Subscriber {
44
- constructor(parent, onUpdate, strict) {
45
- this.parent = parent;
26
+ constructor(parent, onUpdate) {
46
27
  this.onUpdate = onUpdate;
47
- this.strict = strict;
48
28
  this.active = false;
49
29
  this.dependant = new Set();
50
30
  this.watch = new Map();
51
- const proxy = create(parent.subject);
31
+ if (!(parent instanceof Control))
32
+ parent = Control.for(parent);
52
33
  const reset = () => this.latest = undefined;
34
+ const proxy = create(parent.subject);
53
35
  REGISTER$1.set(proxy, this);
54
36
  defineProperty(this, "proxy", {
55
37
  configurable: true,
@@ -58,6 +40,7 @@ class Subscriber {
58
40
  return proxy;
59
41
  }
60
42
  });
43
+ this.parent = parent;
61
44
  this.clear = parent.addListener(key => {
62
45
  if (this.active)
63
46
  this.notify(key);
@@ -66,15 +49,11 @@ class Subscriber {
66
49
  get using() {
67
50
  return Array.from(this.watch.keys());
68
51
  }
69
- apply(values, keys) {
70
- const { waiting, subject } = this.parent;
71
- this.active = false;
72
- if (!keys)
73
- keys = getOwnPropertyNames(subject);
74
- for (const key of keys)
75
- if (key in values)
76
- subject[key] = values[key];
77
- waiting.add(() => this.active = true);
52
+ get(key, using) {
53
+ this.follow(key);
54
+ return using
55
+ ? using(this)
56
+ : this.parent.state.get(key);
78
57
  }
79
58
  follow(key, value) {
80
59
  if (value !== undefined)
@@ -110,11 +89,11 @@ function subscriber(from) {
110
89
  return REGISTER$1.get(from);
111
90
  }
112
91
 
113
- const Oops$c = issues({
92
+ const Oops$8 = issues({
114
93
  NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
115
94
  });
116
95
  function suspend(source, key) {
117
- const error = Oops$c.NotReady(source.subject, key);
96
+ const error = Oops$8.NotReady(source.subject, key);
118
97
  const promise = new Promise(resolve => {
119
98
  const release = source.addListener(forKey => {
120
99
  if (forKey == key)
@@ -154,87 +133,7 @@ function mayRetry(fn) {
154
133
  return compute();
155
134
  }
156
135
 
157
- const Parent = new WeakMap();
158
- const Oops$b = issues({
159
- BadAssignment: (parent, expected, got) => `Property ${parent} expected Model of type ${expected} but got ${got}.`,
160
- Required: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
161
- Unexpected: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
162
- });
163
- function getParentForGetInstruction(type, relativeTo, required) {
164
- const item = getParent(type, relativeTo);
165
- return (_refresh) => {
166
- if (item)
167
- return item;
168
- if (required)
169
- throw Oops$b.Required(type.name, relativeTo);
170
- };
171
- }
172
- function getParent(type, from) {
173
- const value = Parent.get(from);
174
- if (value && !(value instanceof type))
175
- throw Oops$b.Unexpected(type.name, from, value);
176
- return value;
177
- }
178
- function getRecursive(key, from) {
179
- const context = new WeakMap();
180
- const { state } = from;
181
- return (local) => {
182
- if (!state.has(key))
183
- throw suspend(from, key);
184
- if (!local)
185
- return state.get(key);
186
- if (!context.has(local)) {
187
- let reset;
188
- const init = () => {
189
- if (reset)
190
- reset();
191
- const value = state.get(key);
192
- if (value && controller(value)) {
193
- const child = new Subscriber(control(value), local.onUpdate);
194
- if (local.active)
195
- child.commit();
196
- local.dependant.add(child);
197
- context.set(local, child.proxy);
198
- reset = () => {
199
- child.release();
200
- local.dependant.delete(child);
201
- context.set(local, undefined);
202
- reset = undefined;
203
- };
204
- }
205
- };
206
- local.follow(key, init);
207
- init();
208
- }
209
- return context.get(local);
210
- };
211
- }
212
- function setRecursive(controller, key, initial) {
213
- const { state, subject } = controller;
214
- const Type = initial.constructor;
215
- let get;
216
- const onUpdate = (next) => {
217
- state.set(key, next);
218
- if (!(next instanceof Type))
219
- throw Oops$b.BadAssignment(`${subject}.${key}`, Type.name, String(next));
220
- get = getRecursive(key, controller);
221
- Parent.set(next, subject);
222
- control(next);
223
- return true;
224
- };
225
- onUpdate(initial);
226
- defineProperty(subject, key, {
227
- set: controller.ref(key, onUpdate),
228
- get() {
229
- const local = subscriber(this);
230
- if (local)
231
- local.follow(key);
232
- return get(local);
233
- }
234
- });
235
- }
236
-
237
- const Oops$a = issues({
136
+ const Oops$7 = issues({
238
137
  BadCallback: () => `Callback for effect-callback may only return a function.`
239
138
  });
240
139
  function createEffect(model, callback, keys) {
@@ -248,7 +147,7 @@ function createEffect(model, callback, keys) {
248
147
  if (unSet instanceof Promise)
249
148
  unSet = undefined;
250
149
  if (unSet && typeof unSet !== "function")
251
- throw Oops$a.BadCallback();
150
+ throw Oops$7.BadCallback();
252
151
  }
253
152
  function invoke() {
254
153
  if (busy)
@@ -292,11 +191,11 @@ function createValueEffect(callback) {
292
191
  if (typeof out == "function")
293
192
  unSet = out;
294
193
  else
295
- throw Oops$a.BadCallback();
194
+ throw Oops$7.BadCallback();
296
195
  };
297
196
  }
298
197
 
299
- const Oops$9 = issues({
198
+ const Oops$6 = issues({
300
199
  Timeout: (keys, timeout) => `No update seen for [${keys}] within ${timeout}.`,
301
200
  KeysExpected: (key) => `Key "${key}" was expected in current update.`,
302
201
  StrictUpdate: () => `Expected update but none in progress.`,
@@ -332,13 +231,13 @@ function awaitUpdate(source, arg1, arg2) {
332
231
  const timeout = (message) => {
333
232
  const k = keys ? keys.join(", ") : "any";
334
233
  removeListener();
335
- reject(Oops$9.Timeout(k, message));
234
+ reject(Oops$6.Timeout(k, message));
336
235
  };
337
236
  const removeListener = control(source, controller => {
338
237
  const pending = controller.frame;
339
238
  if (keys === null) {
340
239
  if (pending.size)
341
- reject(Oops$9.StrictNoUpdate());
240
+ reject(Oops$6.StrictNoUpdate());
342
241
  else
343
242
  resolve(null);
344
243
  return;
@@ -346,7 +245,7 @@ function awaitUpdate(source, arg1, arg2) {
346
245
  if (keys) {
347
246
  if (keys.length) {
348
247
  if (arg2 === 0 && single && !pending.has(keys[0])) {
349
- reject(Oops$9.KeysExpected(keys[0]));
248
+ reject(Oops$6.KeysExpected(keys[0]));
350
249
  return;
351
250
  }
352
251
  else
@@ -358,7 +257,7 @@ function awaitUpdate(source, arg1, arg2) {
358
257
  }
359
258
  }
360
259
  else if (!pending.size && arg1 === true || arg2 === 0) {
361
- reject(Oops$9.StrictUpdate());
260
+ reject(Oops$6.StrictUpdate());
362
261
  return;
363
262
  }
364
263
  else
@@ -381,8 +280,9 @@ function awaitUpdate(source, arg1, arg2) {
381
280
  });
382
281
  }
383
282
 
384
- const Oops$8 = issues({
385
- NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
283
+ const Oops$5 = issues({
284
+ NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`,
285
+ NoAdapter: (method) => `Can't call Model.${method} without an adapter.`
386
286
  });
387
287
  class Model {
388
288
  constructor() {
@@ -399,14 +299,14 @@ class Model {
399
299
  }
400
300
  get(arg1, arg2) {
401
301
  const { state } = control(this);
402
- const extract = typeof arg1 == "string"
403
- ? () => state.get(arg1)
404
- : () => {
405
- const output = {};
406
- for (const key of arg1 || state.keys())
407
- output[key] = state.get(key);
408
- return output;
409
- };
302
+ const extract = () => {
303
+ if (typeof arg1 == "string")
304
+ return state.get(arg1);
305
+ const output = {};
306
+ for (const key of arg1 || state.keys())
307
+ output[key] = state.get(key);
308
+ return output;
309
+ };
410
310
  if (!arg1 || arg2 === undefined)
411
311
  return extract();
412
312
  if (typeof arg2 == "function")
@@ -439,15 +339,13 @@ class Model {
439
339
  return {
440
340
  then: (callback) => {
441
341
  if (!callback)
442
- throw Oops$8.NoChaining();
342
+ throw Oops$5.NoChaining();
443
343
  controller.waiting.add(callback);
444
344
  }
445
345
  };
446
346
  }
447
- /**
448
- * Clean up side effects and mark this instance for garbage-collection.
449
- */
450
- end() {
347
+ /** Mark this instance for garbage collection. */
348
+ gc() {
451
349
  control(this).clear();
452
350
  }
453
351
  /**
@@ -462,6 +360,18 @@ class Model {
462
360
  control(instance);
463
361
  return instance;
464
362
  }
363
+ static use() {
364
+ throw Oops$5.NoAdapter("use");
365
+ }
366
+ static get() {
367
+ throw Oops$5.NoAdapter("get");
368
+ }
369
+ static tap() {
370
+ throw Oops$5.NoAdapter("tap");
371
+ }
372
+ static meta() {
373
+ throw Oops$5.NoAdapter("meta");
374
+ }
465
375
  /**
466
376
  * Static equivalent of `x instanceof this`.
467
377
  *
@@ -476,114 +386,172 @@ defineProperty(Model.prototype, "toString", {
476
386
  value() {
477
387
  return this.constructor.name;
478
388
  }
479
- });
480
- const FindInstruction = new WeakMap([
481
- [Model, getParentForGetInstruction]
482
- ]);
389
+ });
483
390
 
484
- const Oops$7 = issues({
391
+ /**
392
+ * Run instruction as controller sets itself up.
393
+ * This will specialize the behavior of a given property.
394
+ */
395
+ function add(instruction, label) {
396
+ const name = label || instruction.name || "pending";
397
+ const placeholder = Symbol(`${name} instruction`);
398
+ PENDING.set(placeholder, (control, key) => {
399
+ let output = instruction.call(control, key, control);
400
+ if (!output)
401
+ return undefined;
402
+ if (typeof output == "function")
403
+ output = { get: output };
404
+ if ("value" in output)
405
+ control.state.set(key, output.value);
406
+ let { enumerable, get, set } = output;
407
+ defineProperty(control.subject, key, {
408
+ enumerable,
409
+ set: (set === false
410
+ ? undefined
411
+ : control.ref(key, set)),
412
+ get() {
413
+ const local = subscriber(this);
414
+ return (local ? local.get(key, get) :
415
+ get ? get(local) :
416
+ control.state.get(key));
417
+ }
418
+ });
419
+ });
420
+ return placeholder;
421
+ }
422
+
423
+ const Oops$4 = issues({
485
424
  PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or get) as computed source for ${model}.${property}. This is not allowed.`,
486
- Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`
425
+ Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
426
+ Required: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
487
427
  });
488
428
  function get(arg0, arg1) {
429
+ const fetch = this || getParentForGetInstruction;
489
430
  return add(function get(key) {
490
- const { subject, state } = this;
491
- // Easy mistake, using a peer, will always be unresolved.
431
+ let { subject } = this;
492
432
  if (typeof arg0 == "symbol")
493
- throw Oops$7.PeerNotAllowed(subject, key);
494
- let source;
495
- const sourceRequired = arg1 !== false;
496
- if (arg0 instanceof Model) {
497
- source = () => arg0;
498
- if (typeof arg1 !== "function")
499
- throw new Error(`Factory argument cannot be ${arg1}`);
500
- }
501
- else if (Model.isTypeof(arg0)) {
502
- source = find(arg0, subject, sourceRequired);
503
- }
504
- else if (typeof arg0 == "function") {
433
+ throw Oops$4.PeerNotAllowed(subject, key);
434
+ let source = cb => cb(subject);
435
+ if (arg0 instanceof Model)
436
+ subject = arg0;
437
+ else if (Model.isTypeof(arg0))
438
+ source = fetch(arg0, subject, arg1 !== false);
439
+ else if (typeof arg0 == "function")
505
440
  arg1 = arg0.call(subject, key, subject);
506
- source = () => subject;
507
- }
508
- if (typeof arg1 == "function")
509
- return getComputed(key, this, source, arg1);
510
- else {
511
- const init = source((got) => {
512
- state.set(key, got);
513
- this.update(key);
514
- });
515
- // TODO: remove fixes suspense test
516
- if (init || arg1 === false)
517
- state.set(key, init);
518
- return getRecursive(key, this);
519
- }
441
+ return typeof arg1 == "function"
442
+ ? getComputed(key, this, source, arg1)
443
+ : getRecursive$1(key, this, source, arg1);
444
+ });
445
+ }
446
+ function getParentForGetInstruction(type, relativeTo, required) {
447
+ const item = getParent(relativeTo, type);
448
+ return callback => {
449
+ if (item)
450
+ callback(item);
451
+ else if (required)
452
+ throw Oops$4.Required(type.name, relativeTo);
453
+ };
454
+ }
455
+ function getRecursive$1(key, parent, source, required) {
456
+ const context = new WeakMap();
457
+ const { state } = parent;
458
+ let waiting;
459
+ source((got) => {
460
+ state.set(key, got);
461
+ if (waiting)
462
+ parent.update(key);
520
463
  });
464
+ waiting = true;
465
+ function create(local) {
466
+ let reset;
467
+ local.follow(key, init);
468
+ init();
469
+ function init() {
470
+ if (reset)
471
+ reset();
472
+ const value = state.get(key);
473
+ if (value && controller(value)) {
474
+ const child = new Subscriber(value, local.onUpdate);
475
+ if (local.active)
476
+ child.commit();
477
+ local.dependant.add(child);
478
+ context.set(local, child.proxy);
479
+ reset = () => {
480
+ child.release();
481
+ local.dependant.delete(child);
482
+ context.set(local, undefined);
483
+ reset = undefined;
484
+ };
485
+ }
486
+ }
487
+ }
488
+ return (local) => {
489
+ const value = state.get(key);
490
+ if (!value && required !== false)
491
+ parent.waitFor(key);
492
+ if (!local)
493
+ return value;
494
+ if (!context.has(local))
495
+ create(local);
496
+ return context.get(local);
497
+ };
521
498
  }
522
499
  function getComputed(key, parent, source, setter) {
523
500
  const { state } = parent;
524
- let sub;
501
+ let local;
502
+ let active;
503
+ let isAsync;
525
504
  let order = ORDER.get(parent);
526
505
  let pending = KEYS.get(parent);
527
- let instance;
528
506
  if (!order)
529
507
  ORDER.set(parent, order = new Map());
530
508
  if (!pending)
531
509
  KEYS.set(parent, pending = new Set());
532
- const compute = (initial) => {
510
+ INFO.set(compute, key);
511
+ function compute(initial) {
512
+ let next;
533
513
  try {
534
- return setter.call(sub.proxy, sub.proxy);
514
+ next = setter.call(local.proxy, local.proxy);
535
515
  }
536
516
  catch (err) {
537
- Oops$7.Failed(instance, key, initial).warn();
538
- throw err;
517
+ Oops$4.Failed(parent.subject, key, initial).warn();
518
+ if (initial)
519
+ throw err;
520
+ console.error(err);
539
521
  }
540
- };
541
- const create = () => {
542
- // TODO: replace create with a cleanup function
543
- const got = source(create);
544
- if (!got)
545
- throw suspend(parent, key);
546
- instance = got;
547
- sub = new Subscriber(control(instance), (_, control) => {
522
+ if (next !== state.get(key)) {
523
+ state.set(key, next);
524
+ if (!initial || isAsync)
525
+ parent.update(key);
526
+ }
527
+ }
528
+ function connect(model) {
529
+ local = new Subscriber(model, (_, control) => {
548
530
  if (control !== parent)
549
- refresh();
531
+ compute();
550
532
  else
551
- pending.add(refresh);
533
+ pending.add(compute);
552
534
  });
553
- sub.watch.set(key, false);
535
+ local.watch.set(key, false);
554
536
  try {
555
- const value = compute(true);
556
- state.set(key, value);
557
- return value;
537
+ compute(true);
558
538
  }
559
539
  finally {
560
- sub.commit();
561
- order.set(refresh, order.size);
562
- }
563
- };
564
- const refresh = () => {
565
- let value;
566
- try {
567
- value = compute(false);
568
- }
569
- catch (e) {
570
- console.error(e);
540
+ local.commit();
541
+ order.set(compute, order.size);
571
542
  }
572
- finally {
573
- if (state.get(key) !== value) {
574
- state.set(key, value);
575
- parent.update(key);
576
- return value;
577
- }
578
- }
579
- };
580
- INFO.set(refresh, key);
543
+ }
581
544
  return () => {
582
- if (pending.has(refresh)) {
583
- pending.delete(refresh);
584
- refresh();
545
+ if (!active) {
546
+ active = true;
547
+ source(connect);
548
+ isAsync = true;
585
549
  }
586
- return sub ? state.get(key) : create();
550
+ if (!local)
551
+ parent.waitFor(key);
552
+ if (pending.delete(compute))
553
+ compute();
554
+ return state.get(key);
587
555
  };
588
556
  }
589
557
  const INFO = new WeakMap();
@@ -606,16 +574,10 @@ function flush(control) {
606
574
  }
607
575
  pending.clear();
608
576
  }
609
- // TODO: collapse this function when MVC is merged into Model.
610
- function find(type, relativeTo, required) {
611
- let T = type;
612
- do {
613
- T = getPrototypeOf(T);
614
- const getter = FindInstruction.get(T);
615
- if (getter)
616
- return getter(type, relativeTo, required);
617
- } while (T.prototype instanceof Model);
618
- }
577
+ function using(fn) {
578
+ return Object.assign(get.bind(fn), { using });
579
+ }
580
+ get.using = using;
619
581
 
620
582
  const REGISTER = new WeakMap();
621
583
  const PENDING = new Map();
@@ -650,17 +612,17 @@ class Control {
650
612
  enumerable: false,
651
613
  set: this.ref(key),
652
614
  get() {
653
- const sub = subscriber(this);
654
- const value = state.get(key);
655
- if (sub) {
656
- sub.follow(key);
657
- if (value === undefined && sub.strict)
658
- throw suspend(this, key);
659
- }
660
- return value;
615
+ const local = subscriber(this);
616
+ return local
617
+ ? local.get(key)
618
+ : state.get(key);
661
619
  }
662
620
  });
663
621
  }
622
+ /** Throw suspense which resolves when key is set. */
623
+ waitFor(key) {
624
+ throw suspend(this, key);
625
+ }
664
626
  addListener(listener) {
665
627
  this.followers.add(listener);
666
628
  return () => {
@@ -746,46 +708,72 @@ function control(subject, cb) {
746
708
  return cb ? cb(control) : control;
747
709
  }
748
710
 
749
- /**
750
- * Run instruction as controller sets itself up.
751
- * This will specialize the behavior of a given property.
752
- */
753
- function add(instruction, label) {
754
- const name = label || instruction.name || "pending";
755
- const placeholder = Symbol(`${name} instruction`);
756
- PENDING.set(placeholder, (control, key) => {
757
- let output = instruction.call(control, key, control);
758
- if (!output)
759
- return undefined;
760
- if (typeof output == "function")
761
- output = { get: output };
762
- if ("value" in output)
763
- control.state.set(key, output.value);
764
- let { enumerable, get, set } = output;
765
- defineProperty(control.subject, key, {
766
- enumerable,
767
- set: set === false
768
- ? undefined
769
- : control.ref(key, set),
770
- get() {
771
- const local = subscriber(this);
772
- if (local)
773
- local.follow(key);
774
- try {
775
- const value = get ? get(local) : control.state.get(key);
776
- if (value === undefined && local && local.strict === true)
777
- throw suspend(control, key);
778
- return value;
779
- }
780
- catch (err) {
781
- if (err instanceof Promise && local && local.strict === false)
782
- return;
783
- throw err;
711
+ const Parent = new WeakMap();
712
+ const Oops$3 = issues({
713
+ BadAssignment: (parent, expected, got) => `Property ${parent} expected Model of type ${expected} but got ${got}.`,
714
+ Unexpected: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
715
+ });
716
+ function getParent(from, type) {
717
+ const value = Parent.get(from);
718
+ if (value && type && !(value instanceof type))
719
+ throw Oops$3.Unexpected(type.name, from, value);
720
+ return value;
721
+ }
722
+ function getRecursive(key, from) {
723
+ const context = new WeakMap();
724
+ const { state } = from;
725
+ return (local) => {
726
+ if (!local)
727
+ return state.get(key);
728
+ if (!context.has(local)) {
729
+ let reset;
730
+ const init = () => {
731
+ if (reset)
732
+ reset();
733
+ const value = state.get(key);
734
+ if (value && controller(value)) {
735
+ const child = new Subscriber(value, local.onUpdate);
736
+ if (local.active)
737
+ child.commit();
738
+ local.dependant.add(child);
739
+ context.set(local, child.proxy);
740
+ reset = () => {
741
+ child.release();
742
+ local.dependant.delete(child);
743
+ context.set(local, undefined);
744
+ reset = undefined;
745
+ };
784
746
  }
785
- }
786
- });
747
+ };
748
+ local.follow(key, init);
749
+ init();
750
+ }
751
+ return context.get(local);
752
+ };
753
+ }
754
+ function setRecursive(controller, key, initial) {
755
+ const { state, subject } = controller;
756
+ const Type = initial.constructor;
757
+ let get;
758
+ const onUpdate = (next) => {
759
+ state.set(key, next);
760
+ if (!(next instanceof Type))
761
+ throw Oops$3.BadAssignment(`${subject}.${key}`, Type.name, String(next));
762
+ get = getRecursive(key, controller);
763
+ Parent.set(next, subject);
764
+ control(next);
765
+ return true;
766
+ };
767
+ onUpdate(initial);
768
+ defineProperty(subject, key, {
769
+ set: controller.ref(key, onUpdate),
770
+ get() {
771
+ const local = subscriber(this);
772
+ if (local)
773
+ local.follow(key);
774
+ return get(local);
775
+ }
787
776
  });
788
- return placeholder;
789
777
  }
790
778
 
791
779
  /******************************************************************************
@@ -813,7 +801,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
813
801
  });
814
802
  }
815
803
 
816
- const Oops$6 = issues({
804
+ const Oops$2 = issues({
817
805
  DuplicatePending: (key) => `Invoked action ${key} but one is already active.`
818
806
  });
819
807
  function run(task) {
@@ -821,7 +809,7 @@ function run(task) {
821
809
  let pending = false;
822
810
  const invoke = (...args) => __awaiter(this, void 0, void 0, function* () {
823
811
  if (pending)
824
- return Promise.reject(Oops$6.DuplicatePending(key));
812
+ return Promise.reject(Oops$2.DuplicatePending(key));
825
813
  pending = true;
826
814
  this.update(key);
827
815
  try {
@@ -874,7 +862,7 @@ function createRef(src, key, cb) {
874
862
  return refObjectFunction;
875
863
  }
876
864
 
877
- const Oops$5 = issues({
865
+ const Oops$1 = issues({
878
866
  ComputeFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`,
879
867
  NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
880
868
  });
@@ -908,7 +896,7 @@ function set(value, argument) {
908
896
  const suspend = () => {
909
897
  if (required === false)
910
898
  return undefined;
911
- const issue = Oops$5.NotReady(subject, key);
899
+ const issue = Oops$1.NotReady(subject, key);
912
900
  assign(pending, {
913
901
  message: issue.message,
914
902
  stack: issue.stack
@@ -925,7 +913,7 @@ function set(value, argument) {
925
913
  };
926
914
  }
927
915
  catch (err) {
928
- Oops$5.ComputeFailed(subject, key).warn();
916
+ Oops$1.ComputeFailed(subject, key).warn();
929
917
  throw err;
930
918
  }
931
919
  };
@@ -944,7 +932,7 @@ function set(value, argument) {
944
932
  if (state.has(key))
945
933
  return state.get(key);
946
934
  else
947
- throw suspend(this, key);
935
+ this.waitFor(key);
948
936
  },
949
937
  set: typeof argument == "function"
950
938
  ? createValueEffect(argument)
@@ -1101,7 +1089,7 @@ function createProxy(emit, watch) {
1101
1089
  };
1102
1090
  }
1103
1091
 
1104
- const Oops$4 = issues({
1092
+ const Oops = issues({
1105
1093
  BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`,
1106
1094
  NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
1107
1095
  });
@@ -1117,7 +1105,7 @@ function use(input, argument) {
1117
1105
  input = new input();
1118
1106
  }
1119
1107
  else if (input && typeof input !== "object")
1120
- throw Oops$4.BadArgument(typeof input);
1108
+ throw Oops.BadArgument(typeof input);
1121
1109
  if (input instanceof Map || input instanceof Set)
1122
1110
  return keyed(this, key, input);
1123
1111
  const onUpdate = (next) => {
@@ -1134,7 +1122,7 @@ function use(input, argument) {
1134
1122
  const suspend = () => {
1135
1123
  if (required === false)
1136
1124
  return;
1137
- const issue = Oops$4.NotReady(subject, key);
1125
+ const issue = Oops.NotReady(subject, key);
1138
1126
  assign(pending, {
1139
1127
  message: issue.message,
1140
1128
  stack: issue.stack
@@ -1179,6 +1167,42 @@ function use(input, argument) {
1179
1167
  });
1180
1168
  }
1181
1169
 
1170
+ const LOCAL = "$debug local";
1171
+ const STATE = "$debug state";
1172
+ const UPDATE = "$debug update";
1173
+ const CONTROL = "$debug controller";
1174
+ defineProperties(Model.prototype, {
1175
+ [CONTROL]: {
1176
+ get() {
1177
+ return controller(this);
1178
+ }
1179
+ },
1180
+ [LOCAL]: {
1181
+ get() {
1182
+ return subscriber(this);
1183
+ }
1184
+ },
1185
+ [STATE]: {
1186
+ get() {
1187
+ const { state } = controller(this);
1188
+ const output = {};
1189
+ state.forEach((value, key) => output[key] = value);
1190
+ return output;
1191
+ }
1192
+ },
1193
+ [UPDATE]: {
1194
+ get() {
1195
+ return (subscriber(this) || controller(this)).latest;
1196
+ }
1197
+ }
1198
+ });
1199
+ const Debug = {
1200
+ CONTROL,
1201
+ LOCAL,
1202
+ STATE,
1203
+ UPDATE
1204
+ };
1205
+
1182
1206
  class Register {
1183
1207
  constructor() {
1184
1208
  this.table = new Map();
@@ -1234,394 +1258,21 @@ class Register {
1234
1258
  delete this[key];
1235
1259
  }
1236
1260
  for (const model of items)
1237
- model.end();
1238
- }
1239
- delete(instance) {
1240
- for (const key of getOwnPropertySymbols(this)) {
1241
- const entry = getOwnPropertyDescriptor(this, key);
1242
- if (entry.value === instance)
1243
- delete this[key];
1244
- }
1245
- }
1246
- }
1247
- const Global$1 = new Register();
1248
-
1249
- const Oops$3 = issues({
1250
- AlreadyExists: (name) => `Shared instance of ${name} already exists! Consider unmounting existing, or use ${name}.reset() to force-delete it.`,
1251
- DoesNotExist: (name) => `Tried to access singleton ${name}, but none exist! Did you forget to initialize?\nCall ${name}.new() before attempting to access, or consider using ${name}.use() instead.`,
1252
- NotFound: (name) => `Couldn't find ${name} in context; did you forget to use a Provider?`,
1253
- MultipleExist: (name) => `Did find ${name} in context, but multiple were defined by same Provider.`
1254
- });
1255
- const LookupContext = React__default["default"].createContext(Global$1);
1256
- function useLookup() {
1257
- try {
1258
- return React__default["default"].useContext(LookupContext);
1259
- }
1260
- catch (err) {
1261
- return Global$1;
1261
+ model.gc();
1262
1262
  }
1263
- }
1264
- function useContext(Type, required) {
1265
- const instance = useLookup().get(Type);
1266
- if (instance === null)
1267
- throw Oops$3.MultipleExist(Type.name);
1268
- if (!instance && required !== false)
1269
- throw MVC.isTypeof(Type) && Type.global
1270
- ? Oops$3.DoesNotExist(Type.name)
1271
- : Oops$3.NotFound(Type.name);
1272
- return instance;
1273
1263
  }
1274
1264
 
1275
- const Oops$2 = issues({
1276
- NotAllowed: (parent, child) => `Global '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
1277
- AmbientRequired: (requested, requester) => `Attempted to find an instance of ${requested} in context. It is required by ${requester}, but one could not be found.`
1278
- });
1279
- const Pending = new WeakMap();
1280
- const Applied = new WeakMap();
1281
- function getContextForGetInstruction(type, relativeTo, required) {
1282
- let item = getParent(type, relativeTo);
1283
- return (refresh) => {
1284
- if (item)
1285
- return item;
1286
- findRelative(relativeTo, type, got => {
1287
- if (got) {
1288
- item = got;
1289
- refresh(got);
1290
- }
1291
- else if (required)
1292
- throw Oops$2.AmbientRequired(type.name, relativeTo);
1293
- });
1294
- };
1295
- }
1296
- function findRelative(from, type, callback) {
1297
- if (MVC.isTypeof(type) && type.global)
1298
- callback(Global$1.get(type));
1299
- else if (from.constructor.global)
1300
- throw Oops$2.NotAllowed(from, type.name);
1301
- else
1302
- getPending(from).push(context => {
1303
- callback(context.get(type));
1304
- });
1305
- }
1306
- function usePeerContext(subject) {
1307
- if (Applied.has(subject)) {
1308
- if (Applied.get(subject))
1309
- useLookup();
1310
- return;
1311
- }
1312
- const pending = Pending.get(subject);
1313
- if (pending) {
1314
- const local = useLookup();
1315
- for (const init of pending)
1316
- init(local);
1317
- Pending.delete(subject);
1318
- }
1319
- Applied.set(subject, !!pending);
1320
- }
1321
- function getPending(subject) {
1322
- let pending = Pending.get(subject);
1323
- if (!pending)
1324
- Pending.set(subject, pending = []);
1325
- return pending;
1326
- }
1327
-
1328
- function useModel(source, arg1, arg2) {
1329
- const instance = React__default["default"].useMemo(() => {
1330
- const callback = arg2 || arg1;
1331
- const instance = Model.isTypeof(source)
1332
- ? new source()
1333
- : source();
1334
- control(instance);
1335
- if (typeof callback == "function")
1336
- callback(instance);
1337
- return instance;
1338
- }, []);
1339
- usePeerContext(instance);
1340
- if (Array.isArray(arg1)) {
1341
- const update = React__default["default"].useState(0)[1];
1342
- React__default["default"].useLayoutEffect(() => {
1343
- if (arg1.length && instance instanceof Model)
1344
- instance.on(arg1, () => update(x => x + 1));
1345
- return () => {
1346
- if (Model.isTypeof(source))
1347
- instance.end();
1348
- };
1349
- }, []);
1350
- return instance;
1351
- }
1352
- const state = React__default["default"].useState(0);
1353
- const local = React__default["default"].useMemo(() => {
1354
- const refresh = state[1].bind(null, x => x + 1);
1355
- return new Subscriber(control(instance), () => refresh);
1356
- }, []);
1357
- if (typeof arg1 == "object")
1358
- local.apply(arg1, arg2);
1359
- React__default["default"].useLayoutEffect(() => {
1360
- local.commit();
1361
- return () => {
1362
- local.release();
1363
- if (Model.isTypeof(source))
1364
- instance.end();
1365
- };
1366
- }, []);
1367
- return local.proxy;
1368
- }
1369
-
1370
- function useTap(source, arg1, arg2) {
1371
- const instance = typeof source == "object"
1372
- ? source
1373
- : "get" in source
1374
- ? source.get()
1375
- : source();
1376
- const deps = [uid(instance)];
1377
- const state = React__default["default"].useState(0);
1378
- const local = React__default["default"].useMemo(() => {
1379
- const refresh = state[1].bind(null, x => x + 1);
1380
- const controller = control(instance);
1381
- if (typeof arg1 != "function")
1382
- return new Subscriber(controller, () => refresh, arg1);
1383
- const sub = new Subscriber(controller, () => update);
1384
- const spy = sub.proxy;
1385
- let make = () => arg1.call(spy, spy, forceUpdate);
1386
- function forceUpdate(passthru) {
1387
- if (typeof passthru == "function")
1388
- passthru = passthru();
1389
- if (make)
1390
- reassign(make());
1391
- else
1392
- refresh();
1393
- if (passthru)
1394
- return passthru.finally(refresh);
1395
- }
1396
- function reassign(next) {
1397
- value = next;
1398
- if (retry) {
1399
- retry();
1400
- retry = undefined;
1401
- }
1402
- else
1403
- refresh();
1404
- }
1405
- let retry;
1406
- let update;
1407
- let value = make();
1408
- if (value === null) {
1409
- sub.watch.clear();
1410
- make = undefined;
1411
- return;
1412
- }
1413
- if (typeof value == "function") {
1414
- const get = value;
1415
- sub.watch.clear();
1416
- arg1 = () => get();
1417
- value = get();
1418
- }
1419
- if (value instanceof Promise) {
1420
- value.then(reassign);
1421
- value = undefined;
1422
- }
1423
- else {
1424
- sub.commit();
1425
- update = () => {
1426
- const next = make();
1427
- if (notEqual(value, next))
1428
- reassign(next);
1429
- };
1430
- }
1431
- defineProperty(sub, "proxy", {
1432
- get() {
1433
- if (value !== undefined)
1434
- return value;
1435
- if (arg2)
1436
- throw new Promise(res => retry = res);
1437
- return null;
1438
- }
1439
- });
1440
- return sub;
1441
- }, deps);
1442
- if (!local)
1443
- return null;
1444
- React__default["default"].useLayoutEffect(() => {
1445
- local.commit();
1446
- return () => local.release();
1447
- }, deps);
1448
- return local.proxy;
1449
- }
1450
- /** Values are not equal for purposes of a refresh. */
1451
- const notEqual = (a, b) => (b !== a && (!Array.isArray(a) ||
1452
- !Array.isArray(b) ||
1453
- a.length !== b.length ||
1454
- a.some((x, i) => x !== b[i])));
1455
-
1456
- class MVC extends Model {
1457
- end(force) {
1458
- const type = this.constructor;
1459
- if (type.keepAlive && !force)
1460
- return false;
1461
- super.end();
1462
- Global$1.delete(this);
1463
- return true;
1464
- }
1465
- static new(...args) {
1466
- const exists = Global$1.get(this);
1467
- if (exists)
1468
- if (this.keepAlive)
1469
- return exists;
1470
- else
1471
- throw Oops$3.AlreadyExists(this.name);
1472
- const instance = super.new(...args);
1473
- if (this.global)
1474
- Global$1.add(instance);
1475
- return instance;
1476
- }
1477
- static get(arg1, arg2) {
1478
- const required = arg1 === undefined || arg1 === true;
1479
- const instance = useContext(this, required);
1480
- if (typeof arg1 === "function" && instance)
1481
- callback(arg1);
1482
- else if (arg2)
1483
- callback(arg2);
1484
- return instance;
1485
- function callback(effect) {
1486
- try {
1487
- React__default["default"].useLayoutEffect(() => effect(instance), []);
1488
- }
1489
- catch (err) {
1490
- if (instance)
1491
- instance.on(effect, []);
1492
- else
1493
- effect(undefined);
1494
- }
1495
- }
1496
- }
1497
- static tap(arg1, arg2) {
1498
- return useTap(this, arg1, arg2);
1499
- }
1500
- static use(a, b) {
1501
- return useModel(this, a, b);
1502
- }
1503
- static meta(arg1, arg2) {
1504
- return useTap(() => this, arg1, arg2);
1505
- }
1506
- }
1507
- FindInstruction.set(MVC, getContextForGetInstruction);
1508
-
1509
- const Oops$1 = issues({
1510
- BadProps: () => `Provider expects either a render function, 'get' or 'has' props.`
1511
- });
1512
- function Consumer(props) {
1513
- const { get, has, children, for: type } = props;
1514
- if (typeof children == "function")
1515
- return children(useTap(type));
1516
- const callback = has || get;
1517
- if (typeof callback == "function")
1518
- callback(useContext(type, !!has));
1519
- else
1520
- throw Oops$1.BadProps();
1521
- return null;
1522
- }
1523
-
1524
- const Oops = issues({
1525
- NoType: () => "Provider 'for' prop must be Model, typeof Model or a collection of them."
1526
- });
1527
- function Provider(props) {
1528
- const context = useNewContext(props.for, props.use);
1529
- return (React__default["default"].createElement(LookupContext.Provider, { value: context }, props.fallback === false
1530
- ? props.children
1531
- : (React__default["default"].createElement(React.Suspense, { fallback: props.fallback || null }, props.children))));
1532
- }
1533
- function useNewContext(include, assign) {
1534
- const init = new Set();
1535
- const ambient = useLookup();
1536
- const context = React__default["default"].useMemo(() => {
1537
- if (!include)
1538
- throw Oops.NoType();
1539
- return ambient.push();
1540
- }, []);
1541
- function register(key, input) {
1542
- let instance;
1543
- if (MVC.isTypeof(input) && input.global)
1544
- instance = input.new();
1545
- else if (context.register.get(key) === input)
1546
- instance = typeof input == "object"
1547
- ? input
1548
- : context.get(input);
1549
- else
1550
- instance = context.add(input, key);
1551
- init.add(instance);
1552
- if (assign)
1553
- assignWeak(instance, assign);
1554
- }
1555
- if (typeof include == "function" || include instanceof Model)
1556
- register(0, include);
1557
- else
1558
- entries(include).forEach(e => register(...e));
1559
- for (const instance of init) {
1560
- control(instance).state.forEach(value => {
1561
- // TODO: should this run repeatedly?
1562
- if (Parent.get(value) === instance) {
1563
- context.add(value);
1564
- init.add(value);
1565
- }
1566
- });
1567
- for (const apply of getPending(instance))
1568
- apply(context);
1569
- // TODO: add test to validate this.
1570
- init.delete(instance);
1571
- }
1572
- React__default["default"].useLayoutEffect(() => () => context.pop(), []);
1573
- return context;
1574
- }
1575
-
1576
- class Global extends MVC {
1577
- }
1578
- Global.global = true;
1579
- Global.keepAlive = true;
1580
-
1581
- const LOCAL = "$debug local";
1582
- const STATE = "$debug state";
1583
- const UPDATE = "$debug update";
1584
- const CONTROL = "$debug controller";
1585
- defineProperties(Model.prototype, {
1586
- [CONTROL]: {
1587
- get() {
1588
- return controller(this);
1589
- }
1590
- },
1591
- [LOCAL]: {
1592
- get() {
1593
- return subscriber(this);
1594
- }
1595
- },
1596
- [STATE]: {
1597
- get() {
1598
- const { state } = controller(this);
1599
- const output = {};
1600
- state.forEach((value, key) => output[key] = value);
1601
- return output;
1602
- }
1603
- },
1604
- [UPDATE]: {
1605
- get() {
1606
- return (subscriber(this) || controller(this)).latest;
1607
- }
1608
- }
1609
- });
1610
- const Debug = {
1611
- CONTROL,
1612
- LOCAL,
1613
- STATE,
1614
- UPDATE
1265
+ const Internal = {
1266
+ getParent
1615
1267
  };
1616
1268
 
1617
- exports.Consumer = Consumer;
1269
+ exports.Control = Control;
1618
1270
  exports.Debug = Debug;
1619
- exports.Global = Global;
1620
- exports.MVC = MVC;
1621
- exports.Model = MVC;
1622
- exports.Provider = Provider;
1271
+ exports.Internal = Internal;
1272
+ exports.Model = Model;
1273
+ exports.Register = Register;
1274
+ exports.Subscriber = Subscriber;
1623
1275
  exports.add = add;
1624
- exports["default"] = MVC;
1625
1276
  exports.get = get;
1626
1277
  exports.ref = ref;
1627
1278
  exports.run = run;