@expressive/mvc 1.0.0-beta.31 → 1.0.0-beta.34

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$8 = issues({
23
+ const Oops$9 = 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$8.BadEffectCallback();
52
+ throw Oops$9.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$8.BadEffectCallback();
68
+ throw Oops$9.BadEffectCallback();
69
69
  };
70
70
  }
71
71
  function select(source, select) {
@@ -92,58 +92,51 @@ class Subscriber {
92
92
  constructor(parent, onUpdate) {
93
93
  this.onUpdate = onUpdate;
94
94
  this.active = false;
95
- this.handle = {};
95
+ this.using = {};
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
- };
116
97
  if (!(parent instanceof Controller))
117
98
  parent = manage(parent);
118
99
  this.parent = parent;
119
100
  this.source = parent.subject;
120
101
  this.proxy = create(parent.subject);
121
102
  define(this.proxy, LOCAL, this);
122
- defineLazy(this.proxy, UPDATE, this.debug);
103
+ defineLazy(this.proxy, UPDATE, () => {
104
+ const update = [];
105
+ this.notify = keys => {
106
+ update.splice(0, update.length, ...keys.filter(k => k in this.using));
107
+ };
108
+ return update;
109
+ });
123
110
  for (const key in parent.state)
124
111
  this.spy(key);
125
112
  }
126
113
  spy(key) {
127
- const { proxy, source } = this;
128
- const { set } = getOwnPropertyDescriptor(source, key);
129
- const intercept = () => {
130
- this.follow(key);
114
+ const { proxy } = this;
115
+ const existing = getOwnPropertyDescriptor(this.source, key);
116
+ const isUsing = () => {
117
+ this.using[key] = true;
131
118
  delete proxy[key];
132
119
  return proxy[key];
133
120
  };
134
- setAlias(intercept, `tap ${key}`);
121
+ setAlias(isUsing, `tap ${key}`);
135
122
  defineProperty(proxy, key, {
136
- get: intercept,
137
- set,
123
+ set: existing.set,
124
+ get: isUsing,
138
125
  configurable: true,
139
126
  enumerable: true
140
127
  });
141
128
  }
142
- follow(key, callback) {
143
- this.handle[key] = callback || true;
144
- }
145
129
  commit() {
146
- const onDone = this.parent.addListener(this.listen);
130
+ const onDone = this.parent.addListener((key, source) => {
131
+ const handler = this.using[key];
132
+ if (!handler)
133
+ return;
134
+ if (typeof handler == "function")
135
+ handler();
136
+ const notify = this.onUpdate(key, source);
137
+ source.onUpdate(notify);
138
+ source.onUpdate(this.notify);
139
+ });
147
140
  this.active = true;
148
141
  this.dependant.forEach(x => x.commit());
149
142
  return this.release = () => {
@@ -153,7 +146,7 @@ class Subscriber {
153
146
  }
154
147
  }
155
148
 
156
- const Oops$7 = issues({
149
+ const Oops$8 = issues({
157
150
  ComputeFailed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
158
151
  ComputedEarly: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
159
152
  });
@@ -172,14 +165,14 @@ function prepare(parent, key, source, setter, getter) {
172
165
  }
173
166
  register.set(key, info);
174
167
  const current = getter
175
- ? () => getter.call(subject, state[key], key)
168
+ ? () => getter(parent, key)
176
169
  : () => state[key];
177
170
  function compute(initial) {
178
171
  try {
179
172
  return setter.call(sub.proxy, sub.proxy);
180
173
  }
181
174
  catch (err) {
182
- Oops$7.ComputeFailed(subject, key, !!initial).warn();
175
+ Oops$8.ComputeFailed(subject, key, !!initial).warn();
183
176
  throw err;
184
177
  }
185
178
  }
@@ -225,12 +218,12 @@ function prepare(parent, key, source, setter, getter) {
225
218
  }
226
219
  catch (e) {
227
220
  if (early)
228
- Oops$7.ComputedEarly(key).warn();
221
+ Oops$8.ComputedEarly(key).warn();
229
222
  throw e;
230
223
  }
231
224
  finally {
232
225
  sub.commit();
233
- for (const key in sub.handle) {
226
+ for (const key in sub.using) {
234
227
  const peer = register.get(key);
235
228
  if (peer && peer.priority >= info.priority)
236
229
  info.priority = peer.priority + 1;
@@ -367,6 +360,10 @@ class Controller {
367
360
  get pending() {
368
361
  return this.frame.size > 0;
369
362
  }
363
+ onUpdate(cb) {
364
+ if (typeof cb == "function")
365
+ this.waiting.add(cb);
366
+ }
370
367
  start() {
371
368
  const { subject } = this;
372
369
  for (const key in subject) {
@@ -431,11 +428,8 @@ class Controller {
431
428
  if (!this.frame.size)
432
429
  setTimeout(() => this.emit(), 0);
433
430
  this.frame.add(key);
434
- for (const subscription of this.followers) {
435
- const notify = subscription(key, this);
436
- if (notify)
437
- this.waiting.add(notify);
438
- }
431
+ for (const callback of this.followers)
432
+ this.onUpdate(callback(key, this));
439
433
  }
440
434
  emit() {
441
435
  flush(this);
@@ -494,7 +488,7 @@ function child(source, name) {
494
488
  start();
495
489
  }
496
490
  start();
497
- sub.follow(key, restart);
491
+ sub.using[key] = restart;
498
492
  }
499
493
  return (local) => {
500
494
  if (!local)
@@ -506,6 +500,61 @@ function child(source, name) {
506
500
  }, name || source.name);
507
501
  }
508
502
 
503
+ const Oops$7 = issues({
504
+ NonOptional: (Parent, key) => `Property ${Parent}.${key} is marked as required.`,
505
+ BadFactory: () => `Set instruction can only accept a factory or undefined.`,
506
+ FactoryFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`
507
+ });
508
+ function lazy(value) {
509
+ return apply(function lazy(key) {
510
+ const source = this.subject;
511
+ source[key] = value;
512
+ defineProperty(this.state, key, {
513
+ get: () => source[key]
514
+ });
515
+ });
516
+ }
517
+ function set(factory, argument) {
518
+ return apply(function set(key) {
519
+ let onSet;
520
+ let onGet;
521
+ const required = argument === true || argument === undefined;
522
+ if (factory === undefined)
523
+ onGet = () => pendingValue(this, key);
524
+ else if (typeof factory !== "function")
525
+ throw Oops$7.BadFactory();
526
+ else {
527
+ onGet = pendingFactory(this, key, factory);
528
+ if (required)
529
+ try {
530
+ onGet();
531
+ }
532
+ catch (err) {
533
+ if (err instanceof Promise)
534
+ ;
535
+ else {
536
+ Oops$7.FactoryFailed(this.subject, key).warn();
537
+ throw err;
538
+ }
539
+ }
540
+ }
541
+ if (typeof argument == "function")
542
+ onSet = createValueEffect(argument);
543
+ else if (required)
544
+ onSet = (value) => {
545
+ if (value === undefined)
546
+ throw Oops$7.NonOptional(this.subject, key);
547
+ };
548
+ return {
549
+ set: this.setter(key, onSet),
550
+ get: onGet
551
+ };
552
+ });
553
+ }
554
+ function on(initial, onUpdate) {
555
+ return set(initial && (() => initial), onUpdate);
556
+ }
557
+
509
558
  const Oops$6 = issues({
510
559
  CantAttachGlobal: (parent, child) => `Singleton '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
511
560
  AmbientRequired: (requested, requester, key) => `Attempted to find an instance of ${requested} in context. It is required for [${requester}.${key}], but one could not be found.`
@@ -608,10 +657,13 @@ function act(task) {
608
657
  };
609
658
  });
610
659
  }
611
- function from(source, setter, getter) {
660
+ function from(source, setter, argument) {
612
661
  return apply(function from(key) {
613
- const { subject } = this;
614
662
  let getSource;
663
+ const { subject } = this;
664
+ const getter = argument === true
665
+ ? pendingValue
666
+ : argument || undefined;
615
667
  // Easy mistake, using a peer, will always be unresolved.
616
668
  if (typeof source == "symbol")
617
669
  throw Oops$5.PeerNotAllowed(subject, key);
@@ -626,7 +678,7 @@ function from(source, setter, getter) {
626
678
  // is a peer Model (constructor)
627
679
  else if (Model.isTypeof(source))
628
680
  getSource = pendingAccess(subject, source, key, true);
629
- // Regular function is to ambiguous so not allowed.
681
+ // Regular function is too ambiguous so not allowed.
630
682
  else
631
683
  throw Oops$5.BadComputedSource(subject, key, source);
632
684
  prepare(this, key, getSource, setter, getter);
@@ -636,12 +688,14 @@ function from(source, setter, getter) {
636
688
  const Oops$4 = issues({
637
689
  ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
638
690
  });
691
+ /* deprecated - ignore coverage */
692
+ /* istanbul ignore next */
639
693
  function pending(source, compute) {
640
- return (source === undefined ?
641
- suspendForValue() :
642
- typeof source == "function" && !source.prototype ?
643
- suspendForAsync(source) :
644
- suspendForComputed(source, compute));
694
+ if (source === undefined)
695
+ return set();
696
+ if (typeof source == "function" && !source.prototype)
697
+ return set(source);
698
+ return from(source, compute, true);
645
699
  }
646
700
  /**
647
701
  * Get value, suspend instead if undefined.
@@ -655,6 +709,40 @@ function pendingValue(via, key) {
655
709
  throw suspend(via, key);
656
710
  return value;
657
711
  }
712
+ function pendingFactory(via, key, fn) {
713
+ const { subject, state } = via;
714
+ let waiting;
715
+ let error;
716
+ return () => {
717
+ if (waiting)
718
+ throw waiting;
719
+ if (error)
720
+ throw error;
721
+ if (key in state)
722
+ return state[key];
723
+ let output;
724
+ try {
725
+ output = fn.call(subject, key, subject);
726
+ }
727
+ catch (err) {
728
+ throw error = err;
729
+ }
730
+ if (output instanceof Promise) {
731
+ const issue = Oops$4.ValueNotReady(subject, key);
732
+ output
733
+ .catch(err => error = err)
734
+ .then(out => state[key] = out)
735
+ .finally(() => waiting = undefined);
736
+ throw waiting = Object.assign(output, {
737
+ message: issue.message,
738
+ stack: issue.stack
739
+ });
740
+ }
741
+ else {
742
+ return state[key] = output;
743
+ }
744
+ };
745
+ }
658
746
  function suspend(source, key) {
659
747
  const error = Oops$4.ValueNotReady(source.subject, key);
660
748
  const promise = new Promise(resolve => {
@@ -674,45 +762,6 @@ function suspend(source, key) {
674
762
  message: error.message,
675
763
  stack: error.stack
676
764
  });
677
- }
678
- function suspendForComputed(source, compute) {
679
- function getter(_value, key) {
680
- return pendingValue(manage(this), key);
681
- }
682
- return from(source, compute, getter);
683
- }
684
- function suspendForValue() {
685
- return apply(function pending(key) {
686
- this.state[key] = undefined;
687
- return {
688
- set: this.setter(key),
689
- get: () => pendingValue(this, key)
690
- };
691
- });
692
- }
693
- function suspendForAsync(source) {
694
- return apply(function suspense(key) {
695
- const subject = this.subject;
696
- let waiting = true;
697
- let value;
698
- let error;
699
- return () => {
700
- if (error)
701
- throw error;
702
- if (!waiting)
703
- return value;
704
- const issue = Oops$4.ValueNotReady(subject, key);
705
- let suspend = source
706
- .call(subject, key)
707
- .catch(err => error = err)
708
- .then(out => value = out)
709
- .finally(() => waiting = false);
710
- throw Object.assign(suspend, {
711
- message: issue.message,
712
- stack: issue.stack
713
- });
714
- };
715
- });
716
765
  }
717
766
 
718
767
  function use$1(init) {
@@ -753,13 +802,11 @@ function useWatcher(target, focus, expected) {
753
802
  function useComputed(target, compute, expected) {
754
803
  const hook = use$1(refresh => {
755
804
  const sub = new Subscriber(target, () => update);
756
- const { proxy } = sub;
757
- let value;
805
+ const spy = sub.proxy;
806
+ let value = compute.call(spy, spy);
807
+ let update;
758
808
  let retry;
759
- const update = () => {
760
- const next = compute.call(proxy, proxy);
761
- if (value == next)
762
- return;
809
+ const set = (next) => {
763
810
  value = next;
764
811
  if (retry) {
765
812
  retry();
@@ -768,16 +815,26 @@ function useComputed(target, compute, expected) {
768
815
  else
769
816
  refresh();
770
817
  };
771
- const access = () => {
772
- if (value === undefined && expected)
773
- throw new Promise(res => {
774
- retry = res;
775
- });
776
- return value;
777
- };
778
- value = compute.call(proxy, proxy);
779
- sub.commit();
780
- defineProperty(sub, "proxy", { get: access });
818
+ if (value instanceof Promise) {
819
+ value.then(set);
820
+ value = undefined;
821
+ }
822
+ else {
823
+ sub.commit();
824
+ update = () => {
825
+ const next = compute.call(spy, spy);
826
+ if (value != next)
827
+ set(next);
828
+ };
829
+ }
830
+ const get = expected
831
+ ? () => {
832
+ if (value === undefined)
833
+ throw new Promise(res => retry = res);
834
+ return value;
835
+ }
836
+ : () => value;
837
+ defineProperty(sub, "proxy", { get });
781
838
  return sub;
782
839
  });
783
840
  useLayoutEffect(() => hook.release, []);
@@ -940,7 +997,7 @@ class Model {
940
997
  define(this, "set", this);
941
998
  defer(control, "on");
942
999
  defer(control, "effect");
943
- control.waiting.add(() => {
1000
+ control.onUpdate(() => {
944
1001
  delete this.on;
945
1002
  delete this.effect;
946
1003
  });
@@ -953,9 +1010,7 @@ class Model {
953
1010
  return proxy;
954
1011
  }
955
1012
  tag(id) {
956
- const hook = use$1(refresh => {
957
- return new Subscriber(this, () => refresh);
958
- });
1013
+ const hook = use$1(refresh => (new Subscriber(this, () => refresh)));
959
1014
  useElementLifecycle(hook, id || 0);
960
1015
  return hook.proxy;
961
1016
  }
@@ -989,10 +1044,9 @@ class Model {
989
1044
  once(select, callback, squash) {
990
1045
  if (callback)
991
1046
  return this.on(select, callback, squash, true);
992
- else
993
- return new Promise(resolve => {
994
- this.on(select, resolve, true, true);
995
- });
1047
+ return new Promise(resolve => {
1048
+ this.on(select, resolve, true, true);
1049
+ });
996
1050
  }
997
1051
  effect(callback, select) {
998
1052
  const control = manage(this);
@@ -1003,12 +1057,10 @@ class Model {
1003
1057
  invoke();
1004
1058
  return this.on(select, invoke, true);
1005
1059
  }
1006
- else {
1007
- const sub = new Subscriber(control, () => invoke);
1008
- target = sub.proxy;
1009
- invoke();
1010
- return sub.commit();
1011
- }
1060
+ const sub = new Subscriber(control, () => invoke);
1061
+ target = sub.proxy;
1062
+ invoke();
1063
+ return sub.commit();
1012
1064
  }
1013
1065
  import(from, subset) {
1014
1066
  for (const key of keys(manage(this), subset))
@@ -1204,31 +1256,6 @@ function parent(Expects, required) {
1204
1256
  });
1205
1257
  }
1206
1258
 
1207
- function on(initial, cb) {
1208
- return apply(function on(key) {
1209
- this.manage(key, initial, cb && createValueEffect(cb));
1210
- });
1211
- }
1212
- function memo(factory, defer) {
1213
- return apply(function memo(key) {
1214
- const source = this.subject;
1215
- const get = () => factory.call(source);
1216
- if (defer)
1217
- defineLazy(source, key, get);
1218
- else
1219
- define(source, key, get());
1220
- });
1221
- }
1222
- function lazy(value) {
1223
- return apply(function lazy(key) {
1224
- const source = this.subject;
1225
- source[key] = value;
1226
- defineProperty(this.state, key, {
1227
- get: () => source[key]
1228
- });
1229
- });
1230
- }
1231
-
1232
1259
  const Oops = issues({
1233
1260
  GlobalDoesNotExist: (name) => `Tried to access singleton ${name}, but none exist! Did you forget to initialize?\nCall ${name}.create() before attempting to access, or consider using ${name}.use() instead.`,
1234
1261
  GlobalExists: (name) => `Shared instance of ${name} already exists! Consider unmounting existing, or use ${name}.reset() to force-delete it.`
@@ -1270,5 +1297,5 @@ class Singleton extends Model {
1270
1297
  }
1271
1298
  }
1272
1299
 
1273
- export { Consumer, Model, Provider, Singleton, act, apply, child, Model as default, from, lazy, memo, on, parent, pending, ref, tap, use };
1300
+ export { Consumer, Model, Provider, Singleton, act, apply, child, Model as default, from, lazy, set as memo, on, parent, pending, ref, set, tap, use };
1274
1301
  //# sourceMappingURL=index.esm.js.map