@expressive/mvc 1.0.0-beta.25 → 1.0.0-beta.29

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
@@ -20,7 +20,7 @@ function issues(register) {
20
20
  return Library;
21
21
  }
22
22
 
23
- const Oops$9 = issues({
23
+ const Oops$8 = issues({
24
24
  BadEffectCallback: () => `Callback for effect-callback may only return a function.`
25
25
  });
26
26
  const { assign, create, defineProperty, entries, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor, getOwnPropertyDescriptors, getOwnPropertySymbols, keys: keys$1, values } = Object;
@@ -49,7 +49,7 @@ function createEffect(callback) {
49
49
  if (unSet instanceof Promise)
50
50
  unSet = undefined;
51
51
  if (unSet && typeof unSet !== "function")
52
- throw Oops$9.BadEffectCallback();
52
+ throw Oops$8.BadEffectCallback();
53
53
  };
54
54
  }
55
55
  function createValueEffect(callback) {
@@ -65,7 +65,7 @@ function createValueEffect(callback) {
65
65
  if (typeof out == "function")
66
66
  unSet = out;
67
67
  else
68
- throw Oops$9.BadEffectCallback();
68
+ throw Oops$8.BadEffectCallback();
69
69
  };
70
70
  }
71
71
  function select(source, select) {
@@ -92,14 +92,34 @@ class Subscriber {
92
92
  constructor(parent, onUpdate) {
93
93
  this.onUpdate = onUpdate;
94
94
  this.active = false;
95
- this.follows = {};
95
+ this.handle = {};
96
96
  this.dependant = new Set();
97
+ this.debug = () => {
98
+ const update = [];
99
+ this.notify = keys => {
100
+ update.splice(0, update.length, ...keys.filter(k => k in this.handle));
101
+ };
102
+ return update;
103
+ };
104
+ this.listen = (key, from) => {
105
+ const handler = this.handle[key];
106
+ if (!handler)
107
+ return;
108
+ if (typeof handler == "function")
109
+ handler();
110
+ const notify = this.onUpdate(key, from);
111
+ if (notify)
112
+ from.waiting.add(notify);
113
+ if (this.notify)
114
+ from.waiting.add(this.notify);
115
+ };
97
116
  if (!(parent instanceof Controller))
98
117
  parent = manage(parent);
99
118
  this.parent = parent;
100
119
  this.source = parent.subject;
101
120
  this.proxy = create(parent.subject);
102
121
  define(this.proxy, LOCAL, this);
122
+ defineLazy(this.proxy, UPDATE, this.debug);
103
123
  for (const key in parent.state)
104
124
  this.spy(key);
105
125
  }
@@ -119,43 +139,47 @@ class Subscriber {
119
139
  enumerable: true
120
140
  });
121
141
  }
122
- follow(key, cb) {
123
- this.follows[key] = cb || this.onUpdate;
142
+ follow(key, callback) {
143
+ this.handle[key] = callback || true;
124
144
  }
125
145
  commit() {
126
- const { dependant, follows, parent } = this;
127
- const onDone = parent.addListener(follows);
146
+ const onDone = this.parent.addListener(this.listen);
128
147
  this.active = true;
129
- dependant.forEach(x => x.commit());
148
+ this.dependant.forEach(x => x.commit());
130
149
  return this.release = () => {
131
- dependant.forEach(x => x.release());
150
+ this.dependant.forEach(x => x.release());
132
151
  onDone();
133
152
  };
134
153
  }
135
154
  }
136
155
 
137
- const Oops$8 = issues({
156
+ const Oops$7 = issues({
138
157
  ComputeFailed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
139
158
  ComputedEarly: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
140
159
  });
141
- const ComputedInit = new WeakSet();
142
- const ComputedInfo = new WeakMap();
143
- const ComputedUsed = new WeakMap();
144
- const ComputedKeys = new WeakMap();
160
+ const INIT = new WeakSet();
161
+ const INFO = new WeakMap();
162
+ const USED = new WeakMap();
163
+ const KEYS = new WeakMap();
145
164
  function prepare(parent, key, source, setter, getter) {
146
165
  let sub;
147
166
  const { state, subject } = parent;
148
167
  const info = { key, parent, priority: 1 };
168
+ let register = USED.get(parent);
169
+ if (!register) {
170
+ register = new Map();
171
+ USED.set(parent, register);
172
+ }
173
+ register.set(key, info);
149
174
  const current = getter
150
175
  ? () => getter.call(subject, state[key], key)
151
176
  : () => state[key];
152
- const prioritize = register(parent, key, info);
153
177
  function compute(initial) {
154
178
  try {
155
179
  return setter.call(sub.proxy, sub.proxy);
156
180
  }
157
181
  catch (err) {
158
- Oops$8.ComputeFailed(subject, key, !!initial).warn();
182
+ Oops$7.ComputeFailed(subject, key, !!initial).warn();
159
183
  throw err;
160
184
  }
161
185
  }
@@ -174,8 +198,19 @@ function prepare(parent, key, source, setter, getter) {
174
198
  }
175
199
  }
176
200
  }
201
+ function defer(_key, from) {
202
+ let pending = KEYS.get(from);
203
+ if (!pending)
204
+ KEYS.set(from, pending = []);
205
+ if (info.parent !== from)
206
+ update();
207
+ else {
208
+ const after = pending.findIndex(peer => (info.priority > INFO.get(peer).priority));
209
+ pending.splice(after + 1, 0, update);
210
+ }
211
+ }
177
212
  function create(early) {
178
- sub = new Subscriber(source(), update);
213
+ sub = new Subscriber(source(), defer);
179
214
  defineProperty(state, key, {
180
215
  value: undefined,
181
216
  writable: true
@@ -190,21 +225,24 @@ function prepare(parent, key, source, setter, getter) {
190
225
  }
191
226
  catch (e) {
192
227
  if (early)
193
- Oops$8.ComputedEarly(key).warn();
228
+ Oops$7.ComputedEarly(key).warn();
194
229
  throw e;
195
230
  }
196
231
  finally {
197
232
  sub.commit();
198
- for (const key in sub.follows)
199
- prioritize(key);
233
+ for (const key in sub.handle) {
234
+ const peer = register.get(key);
235
+ if (peer && peer.priority >= info.priority)
236
+ info.priority = peer.priority + 1;
237
+ }
200
238
  }
201
239
  return current();
202
240
  }
203
241
  setAlias(update, `try ${key}`);
204
242
  setAlias(create, `new ${key}`);
205
243
  setAlias(setter, `run ${key}`);
206
- ComputedInit.add(create);
207
- ComputedInfo.set(update, info);
244
+ INIT.add(create);
245
+ INFO.set(update, info);
208
246
  for (const on of [state, subject])
209
247
  defineProperty(on, key, {
210
248
  get: create,
@@ -212,53 +250,27 @@ function prepare(parent, key, source, setter, getter) {
212
250
  enumerable: true
213
251
  });
214
252
  }
215
- function register(on, key, info) {
216
- let register = ComputedUsed.get(on);
217
- if (!register)
218
- ComputedUsed.set(on, register = new Map());
219
- register.set(key, info);
220
- return (key) => {
221
- const peer = register.get(key);
222
- if (peer && peer.priority >= info.priority)
223
- info.priority = peer.priority + 1;
224
- };
225
- }
226
253
  function ensure(on, keys) {
227
254
  for (const key of keys) {
228
255
  const desc = getOwnPropertyDescriptor(on.subject, key);
229
256
  const getter = desc && desc.get;
230
- if (ComputedInit.has(getter))
257
+ if (INIT.has(getter))
231
258
  getter(true);
232
259
  }
233
260
  }
234
- function capture(on, request) {
235
- const compute = ComputedInfo.get(request);
236
- const callback = request;
237
- if (!compute)
238
- return;
239
- let pending = ComputedKeys.get(on);
240
- if (!pending)
241
- ComputedKeys.set(on, pending = []);
242
- if (compute.parent !== on)
243
- callback();
244
- else {
245
- const queue = pending.findIndex(peer => compute.priority > ComputedInfo.get(peer).priority);
246
- pending.splice(queue + 1, 0, callback);
247
- }
248
- return true;
249
- }
250
261
  function flush(on) {
251
262
  const handled = on.frame;
252
- let pending = ComputedKeys.get(on);
253
- if (pending) {
254
- while (pending.length) {
255
- const compute = pending.shift();
256
- const { key } = ComputedInfo.get(compute);
257
- if (!handled.has(key))
258
- compute();
259
- }
260
- ComputedKeys.delete(on);
261
- }
263
+ const pending = KEYS.get(on);
264
+ if (!pending)
265
+ return;
266
+ while (pending.length) {
267
+ const why = [...handled];
268
+ const compute = pending.shift();
269
+ const { key } = INFO.get(compute);
270
+ if (!handled.has(key))
271
+ compute(why);
272
+ }
273
+ KEYS.delete(on);
262
274
  }
263
275
 
264
276
  const Lifecycle = {
@@ -309,6 +321,7 @@ function eventsFor(prefix) {
309
321
  }
310
322
 
311
323
  const CONTROL = Symbol("control");
324
+ const UPDATE = Symbol("update");
312
325
  const LOCAL = Symbol("local");
313
326
  const STATE = Symbol("state");
314
327
  function manage(src) {
@@ -337,8 +350,8 @@ class Controller {
337
350
  this.subject = subject;
338
351
  this.state = {};
339
352
  this.frame = new Set();
340
- this.handles = new Set();
341
- this.waiting = [];
353
+ this.waiting = new Set();
354
+ this.followers = new Set();
342
355
  }
343
356
  static setup(onto) {
344
357
  const control = new this(onto);
@@ -404,18 +417,12 @@ class Controller {
404
417
  this.update(key, value);
405
418
  };
406
419
  }
407
- addListener(batch) {
408
- this.handles.add(batch);
420
+ addListener(listener) {
421
+ this.followers.add(listener);
409
422
  return () => {
410
- this.handles.delete(batch);
423
+ this.followers.delete(listener);
411
424
  };
412
425
  }
413
- include(to) {
414
- if (capture(this, to))
415
- return;
416
- else
417
- this.waiting.push(to);
418
- }
419
426
  update(key, value) {
420
427
  if (1 in arguments)
421
428
  this.state[key] = value;
@@ -424,15 +431,22 @@ class Controller {
424
431
  if (!this.frame.size)
425
432
  setTimeout(() => this.emit(), 0);
426
433
  this.frame.add(key);
427
- for (const subscription of this.handles)
428
- if (key in subscription)
429
- this.include(subscription[key]);
434
+ for (const subscription of this.followers) {
435
+ const notify = subscription(key, this);
436
+ if (notify)
437
+ this.waiting.add(notify);
438
+ }
430
439
  }
431
440
  emit() {
432
441
  flush(this);
433
- const keys = Array.from(this.frame);
434
- const handle = new Set(this.waiting.splice(0));
442
+ const keys = Object.freeze([...this.frame]);
443
+ const handle = new Set(this.waiting);
444
+ this.waiting.clear();
435
445
  this.frame.clear();
446
+ defineProperty(this.subject, UPDATE, {
447
+ configurable: true,
448
+ value: keys
449
+ });
436
450
  handle.forEach(callback => {
437
451
  try {
438
452
  callback(keys);
@@ -444,104 +458,52 @@ class Controller {
444
458
  function keys(from, using) {
445
459
  if (typeof using == "string")
446
460
  return [using];
447
- if (typeof using == "object")
448
- return Array.from(using);
461
+ if (typeof using == "object") {
462
+ using = Array.from(using);
463
+ if (using.length === 0)
464
+ using = undefined;
465
+ else
466
+ return using;
467
+ }
449
468
  return from.select(using);
450
469
  }
451
470
 
452
- function child(from, name) {
471
+ function child(source, name) {
453
472
  return apply(function child(key) {
454
- const proxyCache = new WeakMap();
455
- const getCurrent = from.call(this, key);
473
+ const context = new WeakMap();
474
+ const current = source.call(this, key);
456
475
  function subscribe(sub) {
457
476
  let child;
458
477
  function start() {
459
- const instance = getCurrent();
478
+ const instance = current();
460
479
  if (instance) {
461
480
  child = new Subscriber(instance, sub.onUpdate);
462
481
  if (sub.active)
463
482
  child.commit();
464
483
  sub.dependant.add(child);
465
- proxyCache.set(sub, child.proxy);
484
+ context.set(sub, child.proxy);
466
485
  }
467
486
  }
468
- function refresh() {
487
+ function restart() {
469
488
  if (child) {
470
489
  child.release();
471
490
  sub.dependant.delete(child);
472
- proxyCache.set(sub, undefined);
491
+ context.set(sub, undefined);
473
492
  child = undefined;
474
493
  }
475
494
  start();
476
- sub.onUpdate();
477
495
  }
478
496
  start();
479
- sub.follow(key, refresh);
497
+ sub.follow(key, restart);
480
498
  }
481
499
  return (local) => {
482
500
  if (!local)
483
- return getCurrent();
484
- if (!proxyCache.has(local))
501
+ return current();
502
+ if (!context.has(local))
485
503
  subscribe(local);
486
- return proxyCache.get(local);
504
+ return context.get(local);
487
505
  };
488
- }, name || from.name);
489
- }
490
-
491
- const Oops$7 = issues({
492
- HasPropertyUndefined: (control, property) => `${control}.${property} is marked as required for this render.`
493
- });
494
- function use$1(init) {
495
- const [state, update] = useState(() => [
496
- init(() => update(state.concat()))
497
- ]);
498
- return state[0];
499
- }
500
- function useLazy(Type, callback) {
501
- const instance = useMemo(() => {
502
- const instance = Type.create();
503
- if (callback)
504
- callback(instance);
505
- return instance;
506
- }, []);
507
- useLayoutEffect(() => () => instance.destroy(), []);
508
- return instance;
509
- }
510
- function useWatcher(target, focus, expected) {
511
- const hook = use$1(refresh => {
512
- const sub = new Subscriber(target, refresh);
513
- if (focus) {
514
- const src = sub.proxy;
515
- const key = keys(sub.parent, focus)[0];
516
- defineProperty(sub, "proxy", {
517
- get() {
518
- if (src[key] === undefined && expected)
519
- throw Oops$7.HasPropertyUndefined(target, key);
520
- return src[key];
521
- }
522
- });
523
- }
524
- return sub;
525
- });
526
- useLayoutEffect(() => hook.commit(), []);
527
- return hook.proxy;
528
- }
529
- function useModel(Type, callback) {
530
- const hook = use$1(refresh => {
531
- const instance = new Type();
532
- const control = manage(instance);
533
- const sub = new Subscriber(control, refresh);
534
- const release = control.addListener({
535
- [Lifecycle.WILL_UNMOUNT]() {
536
- instance.destroy();
537
- release();
538
- }
539
- });
540
- if (callback)
541
- callback(instance);
542
- return sub;
543
- });
544
- return hook.proxy;
506
+ }, name || source.name);
545
507
  }
546
508
 
547
509
  const Oops$6 = issues({
@@ -594,6 +556,219 @@ function getPending(subject) {
594
556
  return pending;
595
557
  }
596
558
 
559
+ const Oops$5 = issues({
560
+ DuplicateAction: (key) => `Invoked action ${key} but one is already active.`,
561
+ BadComputedSource: (model, property, got) => `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
562
+ PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`
563
+ });
564
+ function createRef(key, cb) {
565
+ const refObjectFunction = this.setter(key, cb && createValueEffect(cb));
566
+ defineProperty(refObjectFunction, "current", {
567
+ set: refObjectFunction,
568
+ get: () => this.state[key]
569
+ });
570
+ return refObjectFunction;
571
+ }
572
+ function ref(arg) {
573
+ return apply(function ref(key) {
574
+ let value = {};
575
+ if (typeof arg == "object") {
576
+ const source = manage(arg);
577
+ for (const key in source.state)
578
+ defineLazy(value, key, createRef.bind(source, key));
579
+ }
580
+ else
581
+ value = createRef.call(this, key, arg);
582
+ return { value };
583
+ });
584
+ }
585
+ function act(task) {
586
+ return apply(function act(key) {
587
+ let pending = false;
588
+ const invoke = (...args) => {
589
+ if (pending)
590
+ return Promise.reject(Oops$5.DuplicateAction(key));
591
+ pending = true;
592
+ this.update(key);
593
+ return new Promise(res => {
594
+ res(task.apply(this.subject, args));
595
+ }).finally(() => {
596
+ pending = false;
597
+ this.update(key);
598
+ });
599
+ };
600
+ setAlias(invoke, `run ${key}`);
601
+ defineProperty(invoke, "active", {
602
+ get: () => pending
603
+ });
604
+ return {
605
+ value: invoke,
606
+ writable: false
607
+ };
608
+ });
609
+ }
610
+ function from(source, setter, getter) {
611
+ return apply(function from(key) {
612
+ const { subject } = this;
613
+ let getSource;
614
+ // Easy mistake, using a peer, will always be unresolved.
615
+ if (typeof source == "symbol")
616
+ throw Oops$5.PeerNotAllowed(subject, key);
617
+ // replace source controller in-case different
618
+ if (typeof source == "object")
619
+ getSource = () => manage(source);
620
+ // specifically an arrow function (getter factory)
621
+ else if (!source.prototype) {
622
+ setter = source.call(subject, key);
623
+ getSource = () => this;
624
+ }
625
+ // is a peer Model (constructor)
626
+ else if (Model.isTypeof(source))
627
+ getSource = pendingAccess(subject, source, key, true);
628
+ // Regular function is to ambiguous so not allowed.
629
+ else
630
+ throw Oops$5.BadComputedSource(subject, key, source);
631
+ prepare(this, key, getSource, setter, getter);
632
+ });
633
+ }
634
+
635
+ const Oops$4 = issues({
636
+ ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
637
+ });
638
+ function pending(source, compute) {
639
+ return (source === undefined ?
640
+ suspendForValue() :
641
+ typeof source == "function" && !source.prototype ?
642
+ suspendForAsync(source) :
643
+ suspendForComputed(source, compute));
644
+ }
645
+ /**
646
+ * Get value, suspend instead if undefined.
647
+ *
648
+ * Throws suspense promise also interpretable as an error.
649
+ * React could handle it but other contexts probably not.
650
+ */
651
+ function pendingValue(via, key) {
652
+ const value = via.state[key];
653
+ if (value === undefined)
654
+ throw suspend(via, key);
655
+ return value;
656
+ }
657
+ function suspend(source, key) {
658
+ const error = Oops$4.ValueNotReady(source.subject, key);
659
+ const promise = new Promise(resolve => {
660
+ const release = source.addListener(forKey => {
661
+ if (forKey == key)
662
+ return () => {
663
+ if (source.state[key] !== undefined) {
664
+ release();
665
+ resolve();
666
+ }
667
+ };
668
+ });
669
+ });
670
+ return Object.assign(promise, {
671
+ toString: () => String(error),
672
+ name: "Suspense",
673
+ message: error.message,
674
+ stack: error.stack
675
+ });
676
+ }
677
+ function suspendForComputed(source, compute) {
678
+ function getter(_value, key) {
679
+ return pendingValue(manage(this), key);
680
+ }
681
+ return from(source, compute, getter);
682
+ }
683
+ function suspendForValue() {
684
+ return apply(function pending(key) {
685
+ this.state[key] = undefined;
686
+ return {
687
+ set: this.setter(key),
688
+ get: () => pendingValue(this, key)
689
+ };
690
+ });
691
+ }
692
+ function suspendForAsync(source) {
693
+ return apply(function suspense(key) {
694
+ const subject = this.subject;
695
+ let waiting = true;
696
+ let value;
697
+ let error;
698
+ return () => {
699
+ if (error)
700
+ throw error;
701
+ if (!waiting)
702
+ return value;
703
+ const issue = Oops$4.ValueNotReady(subject, key);
704
+ let suspend = source
705
+ .call(subject, key)
706
+ .catch(err => error = err)
707
+ .then(out => value = out)
708
+ .finally(() => waiting = false);
709
+ throw Object.assign(suspend, {
710
+ message: issue.message,
711
+ stack: issue.stack
712
+ });
713
+ };
714
+ });
715
+ }
716
+
717
+ function use$1(init) {
718
+ const [state, update] = useState(() => [
719
+ init(() => update(state.concat()))
720
+ ]);
721
+ return state[0];
722
+ }
723
+ function useLazy(Type, callback) {
724
+ const instance = useMemo(() => {
725
+ const instance = Type.create();
726
+ if (callback)
727
+ callback(instance);
728
+ return instance;
729
+ }, []);
730
+ useLayoutEffect(() => () => instance.destroy(), []);
731
+ return instance;
732
+ }
733
+ function useWatcher(target, focus, expected) {
734
+ const hook = use$1(refresh => {
735
+ const sub = new Subscriber(target, () => refresh);
736
+ if (focus) {
737
+ const source = sub.proxy;
738
+ const key = keys(sub.parent, focus)[0];
739
+ defineProperty(sub, "proxy", {
740
+ get() {
741
+ const value = source[key];
742
+ if (value === undefined && expected)
743
+ throw suspend(sub.parent, key);
744
+ return value;
745
+ }
746
+ });
747
+ }
748
+ return sub;
749
+ });
750
+ useLayoutEffect(() => hook.commit(), []);
751
+ return hook.proxy;
752
+ }
753
+ function useModel(Type, callback) {
754
+ const hook = use$1(refresh => {
755
+ const instance = new Type();
756
+ const control = manage(instance);
757
+ const sub = new Subscriber(control, () => refresh);
758
+ const release = control.addListener((key) => {
759
+ if (key == Lifecycle.WILL_UNMOUNT)
760
+ return () => {
761
+ instance.destroy();
762
+ release();
763
+ };
764
+ });
765
+ if (callback)
766
+ callback(instance);
767
+ return sub;
768
+ });
769
+ return hook.proxy;
770
+ }
771
+
597
772
  class Lookup {
598
773
  constructor() {
599
774
  this.table = new Map();
@@ -649,7 +824,7 @@ class Lookup {
649
824
  }
650
825
  }
651
826
 
652
- const Oops$5 = issues({
827
+ const Oops$3 = issues({
653
828
  NoProviderType: () => `Provider 'of' prop must be Model, typeof Model or a collection of them.`,
654
829
  NothingInContext: (name) => `Couldn't find ${name} in context; did you forget to use a Provider?`,
655
830
  BadConsumerProps: () => `Provider expects either a render function, 'get' or 'has' props.`
@@ -659,7 +834,7 @@ const useLookup = () => useContext(LookupContext);
659
834
  function useFromContext(Type, strict) {
660
835
  const instance = useLookup().get(Type);
661
836
  if (!instance && strict)
662
- throw Oops$5.NothingInContext(Type.name);
837
+ throw Oops$3.NothingInContext(Type.name);
663
838
  return instance;
664
839
  }
665
840
  function Consumer(props) {
@@ -670,13 +845,13 @@ function Consumer(props) {
670
845
  if (typeof callback == "function")
671
846
  callback(Control.get(!!has));
672
847
  else
673
- throw Oops$5.BadConsumerProps();
848
+ throw Oops$3.BadConsumerProps();
674
849
  return null;
675
850
  }
676
851
  function useNewContext(from, inject) {
677
852
  return useMemo(() => {
678
853
  if (!inject)
679
- throw Oops$5.NoProviderType();
854
+ throw Oops$3.NoProviderType();
680
855
  const context = from.push(inject);
681
856
  for (const instance of context.local)
682
857
  for (const apply of getPending(instance))
@@ -701,7 +876,7 @@ function RenderFunction(props) {
701
876
  const hook = use$1(refresh => {
702
877
  const targets = props.context.local;
703
878
  if (targets.length == 1)
704
- return new Subscriber(targets[0], refresh);
879
+ return new Subscriber(targets[0], () => refresh);
705
880
  return {};
706
881
  });
707
882
  if (hook.commit)
@@ -719,7 +894,7 @@ function Provider(props) {
719
894
  : render);
720
895
  }
721
896
 
722
- const Oops$4 = issues({
897
+ const Oops$2 = issues({
723
898
  StrictUpdate: (expected) => `Strict update() did ${expected ? "not " : ""}find pending updates.`,
724
899
  NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
725
900
  });
@@ -732,7 +907,7 @@ class Model {
732
907
  define(this, "set", this);
733
908
  defer(control, "on");
734
909
  defer(control, "effect");
735
- control.include(() => {
910
+ control.waiting.add(() => {
736
911
  delete this.on;
737
912
  delete this.effect;
738
913
  });
@@ -744,7 +919,7 @@ class Model {
744
919
  }
745
920
  tag(id) {
746
921
  const hook = use$1(refresh => {
747
- return new Subscriber(this, refresh);
922
+ return new Subscriber(this, () => refresh);
748
923
  });
749
924
  useElementLifecycle(hook, id || 0);
750
925
  return hook.proxy;
@@ -753,29 +928,27 @@ class Model {
753
928
  const hook = use$1(refresh => {
754
929
  if (callback)
755
930
  callback(this);
756
- return new Subscriber(this, refresh);
931
+ return new Subscriber(this, () => refresh);
757
932
  });
758
933
  useComponentLifecycle(hook);
759
934
  return hook.proxy;
760
935
  }
761
936
  on(subset, handler, squash, once) {
762
937
  const control = manage(this);
763
- const set = keys(control, subset);
764
- const batch = {};
765
- const remove = control.addListener(batch);
938
+ const request = keys(control, subset);
766
939
  const callback = squash
767
940
  ? handler.bind(this)
768
- : (frame) => {
769
- for (const key of frame)
770
- if (set.includes(key))
771
- handler.call(this, control.state[key], key);
772
- };
773
- const handle = once
774
- ? (k) => { remove(); callback(k); }
941
+ : frame => frame
942
+ .filter(k => request.includes(k))
943
+ .forEach(k => handler.call(this, control.state[k], k));
944
+ const trigger = once
945
+ ? frame => { remove(); callback(frame); }
775
946
  : callback;
776
- for (const key of set)
777
- batch[key] = handle;
778
- ensure(control, set);
947
+ ensure(control, request);
948
+ const remove = control.addListener(key => {
949
+ if (request.includes(key))
950
+ return trigger;
951
+ });
779
952
  return remove;
780
953
  }
781
954
  once(select, callback, squash) {
@@ -796,7 +969,7 @@ class Model {
796
969
  return this.on(select, invoke, true);
797
970
  }
798
971
  else {
799
- const sub = new Subscriber(control, invoke);
972
+ const sub = new Subscriber(control, () => invoke);
800
973
  target = sub.proxy;
801
974
  invoke();
802
975
  return sub.commit();
@@ -820,7 +993,7 @@ class Model {
820
993
  arg = select(this, arg);
821
994
  if (typeof arg == "boolean") {
822
995
  if (!control.pending === arg)
823
- return Promise.reject(Oops$4.StrictUpdate(arg));
996
+ return Promise.reject(Oops$2.StrictUpdate(arg));
824
997
  }
825
998
  else if (arg) {
826
999
  control.update(arg);
@@ -837,11 +1010,11 @@ class Model {
837
1010
  then(callback) {
838
1011
  if (callback)
839
1012
  if (control.pending)
840
- control.include(callback);
1013
+ control.waiting.add(callback);
841
1014
  else
842
1015
  callback(false);
843
1016
  else
844
- throw Oops$4.NoChaining();
1017
+ throw Oops$2.NoChaining();
845
1018
  }
846
1019
  };
847
1020
  }
@@ -905,6 +1078,7 @@ class Model {
905
1078
  Model.CONTROL = CONTROL;
906
1079
  Model.STATE = STATE;
907
1080
  Model.LOCAL = LOCAL;
1081
+ Model.WHY = UPDATE;
908
1082
  defineLazy(Model, CONTROL, function () {
909
1083
  return new Controller(this).start();
910
1084
  });
@@ -913,7 +1087,7 @@ function defer(on, method) {
913
1087
  const real = subject[method];
914
1088
  subject[method] = (...args) => {
915
1089
  let done;
916
- waiting.push(() => {
1090
+ waiting.add(() => {
917
1091
  done = real.apply(subject, args);
918
1092
  });
919
1093
  return () => done();
@@ -921,7 +1095,7 @@ function defer(on, method) {
921
1095
  }
922
1096
 
923
1097
  const Parent = new WeakMap();
924
- const Oops$3 = issues({
1098
+ const Oops$1 = issues({
925
1099
  ParentRequired: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
926
1100
  UnexpectedParent: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
927
1101
  UndefinedNotAllowed: (key) => `Child property ${key} may not be undefined.`,
@@ -946,7 +1120,7 @@ function use(input, argument) {
946
1120
  let current;
947
1121
  const onUpdate = (next, initial) => {
948
1122
  if (mode === true && !initial)
949
- throw Oops$3.IsReadOnly(subject, key);
1123
+ throw Oops$1.IsReadOnly(subject, key);
950
1124
  if (next) {
951
1125
  current = next instanceof Model
952
1126
  ? next : bootstrap(next);
@@ -954,7 +1128,7 @@ function use(input, argument) {
954
1128
  manage(current);
955
1129
  }
956
1130
  else if (mode === undefined)
957
- throw Oops$3.UndefinedNotAllowed(key);
1131
+ throw Oops$1.UndefinedNotAllowed(key);
958
1132
  else
959
1133
  current = undefined;
960
1134
  if (typeof argument == "function")
@@ -966,11 +1140,11 @@ function use(input, argument) {
966
1140
  input instanceof Model ? input :
967
1141
  typeof input === "function" ? input() :
968
1142
  typeof input === "object" ? bootstrap(input) :
969
- Oops$3.BadArgument(typeof input).throw();
1143
+ Oops$1.BadArgument(typeof input).throw();
970
1144
  if (current)
971
1145
  mode = onUpdate(current, true) || mode;
972
1146
  else if (mode == true)
973
- throw Oops$3.MustBeDefined(subject, key);
1147
+ throw Oops$1.MustBeDefined(subject, key);
974
1148
  }
975
1149
  else
976
1150
  mode = false;
@@ -985,40 +1159,14 @@ function parent(Expects, required) {
985
1159
  const value = Parent.get(this.subject);
986
1160
  if (!value) {
987
1161
  if (required)
988
- throw Oops$3.ParentRequired(expected, child);
1162
+ throw Oops$1.ParentRequired(expected, child);
989
1163
  }
990
1164
  else if (!(value instanceof Expects))
991
- throw Oops$3.UnexpectedParent(expected, child, value);
1165
+ throw Oops$1.UnexpectedParent(expected, child, value);
992
1166
  return { value };
993
1167
  });
994
1168
  }
995
1169
 
996
- const Oops$2 = issues({
997
- DuplicateAction: (key) => `Invoked action ${key} but one is already active.`,
998
- BadComputedSource: (model, property, got) => `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
999
- PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`
1000
- });
1001
- function createRef(key, cb) {
1002
- const refObjectFunction = this.setter(key, cb && createValueEffect(cb));
1003
- defineProperty(refObjectFunction, "current", {
1004
- set: refObjectFunction,
1005
- get: () => this.state[key]
1006
- });
1007
- return refObjectFunction;
1008
- }
1009
- function ref(arg) {
1010
- return apply(function ref(key) {
1011
- let value = {};
1012
- if (typeof arg == "object") {
1013
- const source = manage(arg);
1014
- for (const key in source.state)
1015
- defineLazy(value, key, createRef.bind(source, key));
1016
- }
1017
- else
1018
- value = createRef.call(this, key, arg);
1019
- return { value };
1020
- });
1021
- }
1022
1170
  function on(initial, cb) {
1023
1171
  return apply(function on(key) {
1024
1172
  this.manage(key, initial, cb && createValueEffect(cb));
@@ -1042,132 +1190,6 @@ function lazy(value) {
1042
1190
  get: () => source[key]
1043
1191
  });
1044
1192
  });
1045
- }
1046
- function act(task) {
1047
- return apply(function act(key) {
1048
- let pending = false;
1049
- const invoke = (...args) => {
1050
- if (pending)
1051
- return Promise.reject(Oops$2.DuplicateAction(key));
1052
- pending = true;
1053
- this.update(key);
1054
- return new Promise(res => {
1055
- res(task.apply(this.subject, args));
1056
- }).finally(() => {
1057
- pending = false;
1058
- this.update(key);
1059
- });
1060
- };
1061
- setAlias(invoke, `run ${key}`);
1062
- defineProperty(invoke, "active", {
1063
- get: () => pending
1064
- });
1065
- return {
1066
- value: invoke,
1067
- writable: false
1068
- };
1069
- });
1070
- }
1071
- function from(source, setter, getter) {
1072
- return apply(function from(key) {
1073
- const { subject } = this;
1074
- let getSource;
1075
- // Easy mistake, using a peer, will always be unresolved.
1076
- if (typeof source == "symbol")
1077
- throw Oops$2.PeerNotAllowed(subject, key);
1078
- // replace source controller in-case different
1079
- if (typeof source == "object")
1080
- getSource = () => manage(source);
1081
- // specifically an arrow function (getter factory)
1082
- else if (!source.prototype) {
1083
- setter = source.call(subject, key);
1084
- getSource = () => this;
1085
- }
1086
- // is a peer Model (constructor)
1087
- else if (Model.isTypeof(source))
1088
- getSource = pendingAccess(subject, source, key, true);
1089
- // Regular function is to ambiguous so not allowed.
1090
- else
1091
- throw Oops$2.BadComputedSource(subject, key, source);
1092
- prepare(this, key, getSource, setter, getter);
1093
- });
1094
- }
1095
-
1096
- const Oops$1 = issues({
1097
- ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
1098
- });
1099
- function pending(source, compute) {
1100
- return (source === undefined ?
1101
- suspendForValue() :
1102
- typeof source == "function" && !source.prototype ?
1103
- suspendForAsync(source) :
1104
- suspendForComputed(source, compute));
1105
- }
1106
- /**
1107
- * Get value, suspend instead if undefined.
1108
- *
1109
- * Throws suspense promise also interpretable as an error.
1110
- * React could handle it but other contexts probably not.
1111
- */
1112
- function pendingValue(via, key) {
1113
- const value = via.state[key];
1114
- if (value !== undefined)
1115
- return value;
1116
- const error = Oops$1.ValueNotReady(via.subject, key);
1117
- const promise = new Promise(resolve => {
1118
- const release = via.addListener({
1119
- [key](value) {
1120
- if (value !== undefined) {
1121
- release();
1122
- resolve();
1123
- }
1124
- }
1125
- });
1126
- });
1127
- throw Object.assign(promise, {
1128
- toString: () => String(error),
1129
- message: error.message,
1130
- stack: error.stack
1131
- });
1132
- }
1133
- function suspendForComputed(source, compute) {
1134
- function getter(_value, key) {
1135
- return pendingValue(manage(this), key);
1136
- }
1137
- return from(source, compute, getter);
1138
- }
1139
- function suspendForValue() {
1140
- return apply(function pending(key) {
1141
- this.state[key] = undefined;
1142
- return {
1143
- set: this.setter(key),
1144
- get: () => pendingValue(this, key)
1145
- };
1146
- });
1147
- }
1148
- function suspendForAsync(source) {
1149
- return apply(function suspense(key) {
1150
- const subject = this.subject;
1151
- let waiting = true;
1152
- let value;
1153
- let error;
1154
- return () => {
1155
- if (error)
1156
- throw error;
1157
- if (!waiting)
1158
- return value;
1159
- const issue = Oops$1.ValueNotReady(subject, key);
1160
- let suspend = source
1161
- .call(subject, key)
1162
- .catch(err => error = err)
1163
- .then(out => value = out)
1164
- .finally(() => waiting = false);
1165
- throw Object.assign(suspend, {
1166
- message: issue.message,
1167
- stack: issue.stack
1168
- });
1169
- };
1170
- });
1171
1193
  }
1172
1194
 
1173
1195
  const Oops = issues({