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