@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.js CHANGED
@@ -24,7 +24,7 @@ function issues(register) {
24
24
  return Library;
25
25
  }
26
26
 
27
- const Oops$8 = issues({
27
+ const Oops$9 = issues({
28
28
  BadEffectCallback: () => `Callback for effect-callback may only return a function.`
29
29
  });
30
30
  const { assign, create, defineProperty, entries, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor, getOwnPropertyDescriptors, getOwnPropertySymbols, keys: keys$1, values } = Object;
@@ -53,7 +53,7 @@ function createEffect(callback) {
53
53
  if (unSet instanceof Promise)
54
54
  unSet = undefined;
55
55
  if (unSet && typeof unSet !== "function")
56
- throw Oops$8.BadEffectCallback();
56
+ throw Oops$9.BadEffectCallback();
57
57
  };
58
58
  }
59
59
  function createValueEffect(callback) {
@@ -69,7 +69,7 @@ function createValueEffect(callback) {
69
69
  if (typeof out == "function")
70
70
  unSet = out;
71
71
  else
72
- throw Oops$8.BadEffectCallback();
72
+ throw Oops$9.BadEffectCallback();
73
73
  };
74
74
  }
75
75
  function select(source, select) {
@@ -96,58 +96,51 @@ class Subscriber {
96
96
  constructor(parent, onUpdate) {
97
97
  this.onUpdate = onUpdate;
98
98
  this.active = false;
99
- this.handle = {};
99
+ this.using = {};
100
100
  this.dependant = new Set();
101
- this.debug = () => {
102
- const update = [];
103
- this.notify = keys => {
104
- update.splice(0, update.length, ...keys.filter(k => k in this.handle));
105
- };
106
- return update;
107
- };
108
- this.listen = (key, from) => {
109
- const handler = this.handle[key];
110
- if (!handler)
111
- return;
112
- if (typeof handler == "function")
113
- handler();
114
- const notify = this.onUpdate(key, from);
115
- if (notify)
116
- from.waiting.add(notify);
117
- if (this.notify)
118
- from.waiting.add(this.notify);
119
- };
120
101
  if (!(parent instanceof Controller))
121
102
  parent = manage(parent);
122
103
  this.parent = parent;
123
104
  this.source = parent.subject;
124
105
  this.proxy = create(parent.subject);
125
106
  define(this.proxy, LOCAL, this);
126
- defineLazy(this.proxy, UPDATE, this.debug);
107
+ defineLazy(this.proxy, UPDATE, () => {
108
+ const update = [];
109
+ this.notify = keys => {
110
+ update.splice(0, update.length, ...keys.filter(k => k in this.using));
111
+ };
112
+ return update;
113
+ });
127
114
  for (const key in parent.state)
128
115
  this.spy(key);
129
116
  }
130
117
  spy(key) {
131
- const { proxy, source } = this;
132
- const { set } = getOwnPropertyDescriptor(source, key);
133
- const intercept = () => {
134
- this.follow(key);
118
+ const { proxy } = this;
119
+ const existing = getOwnPropertyDescriptor(this.source, key);
120
+ const isUsing = () => {
121
+ this.using[key] = true;
135
122
  delete proxy[key];
136
123
  return proxy[key];
137
124
  };
138
- setAlias(intercept, `tap ${key}`);
125
+ setAlias(isUsing, `tap ${key}`);
139
126
  defineProperty(proxy, key, {
140
- get: intercept,
141
- set,
127
+ set: existing.set,
128
+ get: isUsing,
142
129
  configurable: true,
143
130
  enumerable: true
144
131
  });
145
132
  }
146
- follow(key, callback) {
147
- this.handle[key] = callback || true;
148
- }
149
133
  commit() {
150
- const onDone = this.parent.addListener(this.listen);
134
+ const onDone = this.parent.addListener((key, source) => {
135
+ const handler = this.using[key];
136
+ if (!handler)
137
+ return;
138
+ if (typeof handler == "function")
139
+ handler();
140
+ const notify = this.onUpdate(key, source);
141
+ source.onUpdate(notify);
142
+ source.onUpdate(this.notify);
143
+ });
151
144
  this.active = true;
152
145
  this.dependant.forEach(x => x.commit());
153
146
  return this.release = () => {
@@ -157,7 +150,7 @@ class Subscriber {
157
150
  }
158
151
  }
159
152
 
160
- const Oops$7 = issues({
153
+ const Oops$8 = issues({
161
154
  ComputeFailed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
162
155
  ComputedEarly: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
163
156
  });
@@ -176,14 +169,14 @@ function prepare(parent, key, source, setter, getter) {
176
169
  }
177
170
  register.set(key, info);
178
171
  const current = getter
179
- ? () => getter.call(subject, state[key], key)
172
+ ? () => getter(parent, key)
180
173
  : () => state[key];
181
174
  function compute(initial) {
182
175
  try {
183
176
  return setter.call(sub.proxy, sub.proxy);
184
177
  }
185
178
  catch (err) {
186
- Oops$7.ComputeFailed(subject, key, !!initial).warn();
179
+ Oops$8.ComputeFailed(subject, key, !!initial).warn();
187
180
  throw err;
188
181
  }
189
182
  }
@@ -229,12 +222,12 @@ function prepare(parent, key, source, setter, getter) {
229
222
  }
230
223
  catch (e) {
231
224
  if (early)
232
- Oops$7.ComputedEarly(key).warn();
225
+ Oops$8.ComputedEarly(key).warn();
233
226
  throw e;
234
227
  }
235
228
  finally {
236
229
  sub.commit();
237
- for (const key in sub.handle) {
230
+ for (const key in sub.using) {
238
231
  const peer = register.get(key);
239
232
  if (peer && peer.priority >= info.priority)
240
233
  info.priority = peer.priority + 1;
@@ -371,6 +364,10 @@ class Controller {
371
364
  get pending() {
372
365
  return this.frame.size > 0;
373
366
  }
367
+ onUpdate(cb) {
368
+ if (typeof cb == "function")
369
+ this.waiting.add(cb);
370
+ }
374
371
  start() {
375
372
  const { subject } = this;
376
373
  for (const key in subject) {
@@ -435,11 +432,8 @@ class Controller {
435
432
  if (!this.frame.size)
436
433
  setTimeout(() => this.emit(), 0);
437
434
  this.frame.add(key);
438
- for (const subscription of this.followers) {
439
- const notify = subscription(key, this);
440
- if (notify)
441
- this.waiting.add(notify);
442
- }
435
+ for (const callback of this.followers)
436
+ this.onUpdate(callback(key, this));
443
437
  }
444
438
  emit() {
445
439
  flush(this);
@@ -498,7 +492,7 @@ function child(source, name) {
498
492
  start();
499
493
  }
500
494
  start();
501
- sub.follow(key, restart);
495
+ sub.using[key] = restart;
502
496
  }
503
497
  return (local) => {
504
498
  if (!local)
@@ -510,6 +504,61 @@ function child(source, name) {
510
504
  }, name || source.name);
511
505
  }
512
506
 
507
+ const Oops$7 = issues({
508
+ NonOptional: (Parent, key) => `Property ${Parent}.${key} is marked as required.`,
509
+ BadFactory: () => `Set instruction can only accept a factory or undefined.`,
510
+ FactoryFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`
511
+ });
512
+ function lazy(value) {
513
+ return apply(function lazy(key) {
514
+ const source = this.subject;
515
+ source[key] = value;
516
+ defineProperty(this.state, key, {
517
+ get: () => source[key]
518
+ });
519
+ });
520
+ }
521
+ function set(factory, argument) {
522
+ return apply(function set(key) {
523
+ let onSet;
524
+ let onGet;
525
+ const required = argument === true || argument === undefined;
526
+ if (factory === undefined)
527
+ onGet = () => pendingValue(this, key);
528
+ else if (typeof factory !== "function")
529
+ throw Oops$7.BadFactory();
530
+ else {
531
+ onGet = pendingFactory(this, key, factory);
532
+ if (required)
533
+ try {
534
+ onGet();
535
+ }
536
+ catch (err) {
537
+ if (err instanceof Promise)
538
+ ;
539
+ else {
540
+ Oops$7.FactoryFailed(this.subject, key).warn();
541
+ throw err;
542
+ }
543
+ }
544
+ }
545
+ if (typeof argument == "function")
546
+ onSet = createValueEffect(argument);
547
+ else if (required)
548
+ onSet = (value) => {
549
+ if (value === undefined)
550
+ throw Oops$7.NonOptional(this.subject, key);
551
+ };
552
+ return {
553
+ set: this.setter(key, onSet),
554
+ get: onGet
555
+ };
556
+ });
557
+ }
558
+ function on(initial, onUpdate) {
559
+ return set(initial && (() => initial), onUpdate);
560
+ }
561
+
513
562
  const Oops$6 = issues({
514
563
  CantAttachGlobal: (parent, child) => `Singleton '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
515
564
  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.`
@@ -612,10 +661,13 @@ function act(task) {
612
661
  };
613
662
  });
614
663
  }
615
- function from(source, setter, getter) {
664
+ function from(source, setter, argument) {
616
665
  return apply(function from(key) {
617
- const { subject } = this;
618
666
  let getSource;
667
+ const { subject } = this;
668
+ const getter = argument === true
669
+ ? pendingValue
670
+ : argument || undefined;
619
671
  // Easy mistake, using a peer, will always be unresolved.
620
672
  if (typeof source == "symbol")
621
673
  throw Oops$5.PeerNotAllowed(subject, key);
@@ -630,7 +682,7 @@ function from(source, setter, getter) {
630
682
  // is a peer Model (constructor)
631
683
  else if (Model.isTypeof(source))
632
684
  getSource = pendingAccess(subject, source, key, true);
633
- // Regular function is to ambiguous so not allowed.
685
+ // Regular function is too ambiguous so not allowed.
634
686
  else
635
687
  throw Oops$5.BadComputedSource(subject, key, source);
636
688
  prepare(this, key, getSource, setter, getter);
@@ -640,12 +692,14 @@ function from(source, setter, getter) {
640
692
  const Oops$4 = issues({
641
693
  ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
642
694
  });
695
+ /* deprecated - ignore coverage */
696
+ /* istanbul ignore next */
643
697
  function pending(source, compute) {
644
- return (source === undefined ?
645
- suspendForValue() :
646
- typeof source == "function" && !source.prototype ?
647
- suspendForAsync(source) :
648
- suspendForComputed(source, compute));
698
+ if (source === undefined)
699
+ return set();
700
+ if (typeof source == "function" && !source.prototype)
701
+ return set(source);
702
+ return from(source, compute, true);
649
703
  }
650
704
  /**
651
705
  * Get value, suspend instead if undefined.
@@ -659,6 +713,40 @@ function pendingValue(via, key) {
659
713
  throw suspend(via, key);
660
714
  return value;
661
715
  }
716
+ function pendingFactory(via, key, fn) {
717
+ const { subject, state } = via;
718
+ let waiting;
719
+ let error;
720
+ return () => {
721
+ if (waiting)
722
+ throw waiting;
723
+ if (error)
724
+ throw error;
725
+ if (key in state)
726
+ return state[key];
727
+ let output;
728
+ try {
729
+ output = fn.call(subject, key, subject);
730
+ }
731
+ catch (err) {
732
+ throw error = err;
733
+ }
734
+ if (output instanceof Promise) {
735
+ const issue = Oops$4.ValueNotReady(subject, key);
736
+ output
737
+ .catch(err => error = err)
738
+ .then(out => state[key] = out)
739
+ .finally(() => waiting = undefined);
740
+ throw waiting = Object.assign(output, {
741
+ message: issue.message,
742
+ stack: issue.stack
743
+ });
744
+ }
745
+ else {
746
+ return state[key] = output;
747
+ }
748
+ };
749
+ }
662
750
  function suspend(source, key) {
663
751
  const error = Oops$4.ValueNotReady(source.subject, key);
664
752
  const promise = new Promise(resolve => {
@@ -678,45 +766,6 @@ function suspend(source, key) {
678
766
  message: error.message,
679
767
  stack: error.stack
680
768
  });
681
- }
682
- function suspendForComputed(source, compute) {
683
- function getter(_value, key) {
684
- return pendingValue(manage(this), key);
685
- }
686
- return from(source, compute, getter);
687
- }
688
- function suspendForValue() {
689
- return apply(function pending(key) {
690
- this.state[key] = undefined;
691
- return {
692
- set: this.setter(key),
693
- get: () => pendingValue(this, key)
694
- };
695
- });
696
- }
697
- function suspendForAsync(source) {
698
- return apply(function suspense(key) {
699
- const subject = this.subject;
700
- let waiting = true;
701
- let value;
702
- let error;
703
- return () => {
704
- if (error)
705
- throw error;
706
- if (!waiting)
707
- return value;
708
- const issue = Oops$4.ValueNotReady(subject, key);
709
- let suspend = source
710
- .call(subject, key)
711
- .catch(err => error = err)
712
- .then(out => value = out)
713
- .finally(() => waiting = false);
714
- throw Object.assign(suspend, {
715
- message: issue.message,
716
- stack: issue.stack
717
- });
718
- };
719
- });
720
769
  }
721
770
 
722
771
  function use$1(init) {
@@ -757,13 +806,11 @@ function useWatcher(target, focus, expected) {
757
806
  function useComputed(target, compute, expected) {
758
807
  const hook = use$1(refresh => {
759
808
  const sub = new Subscriber(target, () => update);
760
- const { proxy } = sub;
761
- let value;
809
+ const spy = sub.proxy;
810
+ let value = compute.call(spy, spy);
811
+ let update;
762
812
  let retry;
763
- const update = () => {
764
- const next = compute.call(proxy, proxy);
765
- if (value == next)
766
- return;
813
+ const set = (next) => {
767
814
  value = next;
768
815
  if (retry) {
769
816
  retry();
@@ -772,16 +819,26 @@ function useComputed(target, compute, expected) {
772
819
  else
773
820
  refresh();
774
821
  };
775
- const access = () => {
776
- if (value === undefined && expected)
777
- throw new Promise(res => {
778
- retry = res;
779
- });
780
- return value;
781
- };
782
- value = compute.call(proxy, proxy);
783
- sub.commit();
784
- defineProperty(sub, "proxy", { get: access });
822
+ if (value instanceof Promise) {
823
+ value.then(set);
824
+ value = undefined;
825
+ }
826
+ else {
827
+ sub.commit();
828
+ update = () => {
829
+ const next = compute.call(spy, spy);
830
+ if (value != next)
831
+ set(next);
832
+ };
833
+ }
834
+ const get = expected
835
+ ? () => {
836
+ if (value === undefined)
837
+ throw new Promise(res => retry = res);
838
+ return value;
839
+ }
840
+ : () => value;
841
+ defineProperty(sub, "proxy", { get });
785
842
  return sub;
786
843
  });
787
844
  react.useLayoutEffect(() => hook.release, []);
@@ -944,7 +1001,7 @@ class Model {
944
1001
  define(this, "set", this);
945
1002
  defer(control, "on");
946
1003
  defer(control, "effect");
947
- control.waiting.add(() => {
1004
+ control.onUpdate(() => {
948
1005
  delete this.on;
949
1006
  delete this.effect;
950
1007
  });
@@ -957,9 +1014,7 @@ class Model {
957
1014
  return proxy;
958
1015
  }
959
1016
  tag(id) {
960
- const hook = use$1(refresh => {
961
- return new Subscriber(this, () => refresh);
962
- });
1017
+ const hook = use$1(refresh => (new Subscriber(this, () => refresh)));
963
1018
  useElementLifecycle(hook, id || 0);
964
1019
  return hook.proxy;
965
1020
  }
@@ -993,10 +1048,9 @@ class Model {
993
1048
  once(select, callback, squash) {
994
1049
  if (callback)
995
1050
  return this.on(select, callback, squash, true);
996
- else
997
- return new Promise(resolve => {
998
- this.on(select, resolve, true, true);
999
- });
1051
+ return new Promise(resolve => {
1052
+ this.on(select, resolve, true, true);
1053
+ });
1000
1054
  }
1001
1055
  effect(callback, select) {
1002
1056
  const control = manage(this);
@@ -1007,12 +1061,10 @@ class Model {
1007
1061
  invoke();
1008
1062
  return this.on(select, invoke, true);
1009
1063
  }
1010
- else {
1011
- const sub = new Subscriber(control, () => invoke);
1012
- target = sub.proxy;
1013
- invoke();
1014
- return sub.commit();
1015
- }
1064
+ const sub = new Subscriber(control, () => invoke);
1065
+ target = sub.proxy;
1066
+ invoke();
1067
+ return sub.commit();
1016
1068
  }
1017
1069
  import(from, subset) {
1018
1070
  for (const key of keys(manage(this), subset))
@@ -1208,31 +1260,6 @@ function parent(Expects, required) {
1208
1260
  });
1209
1261
  }
1210
1262
 
1211
- function on(initial, cb) {
1212
- return apply(function on(key) {
1213
- this.manage(key, initial, cb && createValueEffect(cb));
1214
- });
1215
- }
1216
- function memo(factory, defer) {
1217
- return apply(function memo(key) {
1218
- const source = this.subject;
1219
- const get = () => factory.call(source);
1220
- if (defer)
1221
- defineLazy(source, key, get);
1222
- else
1223
- define(source, key, get());
1224
- });
1225
- }
1226
- function lazy(value) {
1227
- return apply(function lazy(key) {
1228
- const source = this.subject;
1229
- source[key] = value;
1230
- defineProperty(this.state, key, {
1231
- get: () => source[key]
1232
- });
1233
- });
1234
- }
1235
-
1236
1263
  const Oops = issues({
1237
1264
  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.`,
1238
1265
  GlobalExists: (name) => `Shared instance of ${name} already exists! Consider unmounting existing, or use ${name}.reset() to force-delete it.`
@@ -1284,11 +1311,12 @@ exports.child = child;
1284
1311
  exports["default"] = Model;
1285
1312
  exports.from = from;
1286
1313
  exports.lazy = lazy;
1287
- exports.memo = memo;
1314
+ exports.memo = set;
1288
1315
  exports.on = on;
1289
1316
  exports.parent = parent;
1290
1317
  exports.pending = pending;
1291
1318
  exports.ref = ref;
1319
+ exports.set = set;
1292
1320
  exports.tap = tap;
1293
1321
  exports.use = use;
1294
1322
  //# sourceMappingURL=index.js.map