@expressive/mvc 0.5.0 → 0.6.2

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
@@ -62,99 +62,74 @@ function mayRetry(fn) {
62
62
  }
63
63
 
64
64
  const Oops$b = issues({
65
- BadSource: (model, property, got) => `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
66
- PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`,
67
65
  Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`
68
66
  });
69
67
  const INFO = new WeakMap();
70
68
  const KEYS = new WeakMap();
71
69
  const ORDER = new WeakMap();
72
- function get(source, arg1, arg2) {
73
- return apply(function get(key) {
74
- const { subject, state } = this;
75
- const required = arg2 === true || arg1 === true;
76
- let getSource;
77
- if (typeof arg1 == "boolean")
78
- arg1 = undefined;
79
- // Easy mistake, using a peer, will always be unresolved.
80
- if (typeof source == "symbol")
81
- throw Oops$b.PeerNotAllowed(subject, key);
82
- // replace source controller in-case it is different
83
- if (typeof source == "object")
84
- getSource = () => ensure(source);
85
- // specifically an arrow function (getter factory)
86
- else if (!source.prototype) {
87
- arg1 = source.call(subject, key);
88
- getSource = () => this;
70
+ function computeMode(self, source, setter, key, required) {
71
+ const { subject, state } = self;
72
+ let sub;
73
+ let order = ORDER.get(self);
74
+ if (!order)
75
+ ORDER.set(self, order = []);
76
+ const compute = (initial) => {
77
+ try {
78
+ return setter.call(sub.proxy, sub.proxy);
89
79
  }
90
- // Regular function is too ambiguous so not allowed.
91
- else
92
- throw Oops$b.BadSource(subject, key, source);
93
- const setter = arg1;
94
- const info = { key, parent: this };
95
- let sub;
96
- let order = ORDER.get(this);
97
- if (!order)
98
- ORDER.set(this, order = []);
99
- const compute = (initial) => {
100
- try {
101
- return setter.call(sub.proxy, sub.proxy);
102
- }
103
- catch (err) {
104
- Oops$b.Failed(subject, key, initial).warn();
105
- throw err;
106
- }
107
- };
108
- const refresh = () => {
109
- let value;
110
- try {
111
- value = compute(false);
112
- }
113
- catch (e) {
114
- console.error(e);
115
- }
116
- finally {
117
- if (state.get(key) !== value) {
118
- state.set(key, value);
119
- this.update(key);
120
- return value;
121
- }
122
- }
123
- };
124
- const update = (_key, source) => {
125
- if (source !== this) {
126
- refresh();
127
- return;
128
- }
129
- let pending = KEYS.get(this);
130
- if (!pending)
131
- KEYS.set(this, pending = []);
132
- pending.push(refresh);
133
- };
134
- const create = () => {
135
- sub = getSource().subscribe(update);
136
- try {
137
- const value = compute(true);
80
+ catch (err) {
81
+ Oops$b.Failed(subject, key, initial).warn();
82
+ throw err;
83
+ }
84
+ };
85
+ const refresh = () => {
86
+ let value;
87
+ try {
88
+ value = compute(false);
89
+ }
90
+ catch (e) {
91
+ console.error(e);
92
+ }
93
+ finally {
94
+ if (state.get(key) !== value) {
138
95
  state.set(key, value);
96
+ self.update(key);
139
97
  return value;
140
98
  }
141
- catch (e) {
142
- throw e;
143
- }
144
- finally {
145
- sub.commit();
146
- order.push(refresh);
147
- }
148
- };
149
- state.set(key, undefined);
150
- INFO.set(refresh, info);
151
- return () => {
152
- const value = sub ? state.get(key) : create();
153
- if (value === undefined && required)
154
- throw suspend(this, key);
99
+ }
100
+ };
101
+ const update = (_key, source) => {
102
+ if (source !== self) {
103
+ refresh();
104
+ return;
105
+ }
106
+ let pending = KEYS.get(self);
107
+ if (!pending)
108
+ KEYS.set(self, pending = []);
109
+ pending.push(refresh);
110
+ };
111
+ const create = () => {
112
+ sub = source.subscribe(update);
113
+ try {
114
+ const value = compute(true);
115
+ state.set(key, value);
155
116
  return value;
156
- };
157
- });
117
+ }
118
+ catch (e) {
119
+ throw e;
120
+ }
121
+ finally {
122
+ sub.commit();
123
+ order.push(refresh);
124
+ }
125
+ };
126
+ INFO.set(refresh, key);
127
+ return () => {
128
+ const value = sub ? state.get(key) : create();
129
+ if (value === undefined && required)
130
+ throw suspend(self, key);
131
+ return value;
132
+ };
158
133
  }
159
134
  function flush(control) {
160
135
  const pending = KEYS.get(control);
@@ -165,7 +140,7 @@ function flush(control) {
165
140
  const compute = pending
166
141
  .sort((a, b) => list.indexOf(b) - list.indexOf(a))
167
142
  .pop();
168
- const { key } = INFO.get(compute);
143
+ const key = INFO.get(compute);
169
144
  if (!control.frame.has(key))
170
145
  compute();
171
146
  }
@@ -205,60 +180,6 @@ function createValueEffect(callback) {
205
180
  };
206
181
  }
207
182
 
208
- class Subscriber {
209
- constructor(parent, onUpdate) {
210
- this.onUpdate = onUpdate;
211
- this.active = false;
212
- this.dependant = new Set();
213
- const proxy = create(parent.subject);
214
- const using = new Map();
215
- defineProperty(proxy, LOCAL, { value: this });
216
- defineProperty(this, "proxy", {
217
- configurable: true,
218
- get() {
219
- if (UPDATE.has(proxy))
220
- setTimeout(() => UPDATE.delete(proxy), 0);
221
- return proxy;
222
- }
223
- });
224
- const release = parent.addListener(key => {
225
- if (!this.active)
226
- return;
227
- const handler = using.get(key);
228
- let notify;
229
- if (typeof handler == "function") {
230
- const callback = handler();
231
- if (callback === true)
232
- notify = this.onUpdate(key, parent);
233
- }
234
- else if (handler === true)
235
- notify = this.onUpdate(key, parent);
236
- if (!notify)
237
- return;
238
- const notate = () => {
239
- UPDATE.set(proxy, getUpdate(parent.subject).filter(k => using.has(k)));
240
- };
241
- parent.waiting.add(notate);
242
- parent.waiting.add(notify);
243
- });
244
- this.commit = () => {
245
- this.active = true;
246
- this.dependant.forEach(x => x.commit());
247
- return this.release;
248
- };
249
- this.release = () => {
250
- this.dependant.forEach(x => x.release());
251
- release();
252
- };
253
- this.add = (key, value) => {
254
- if (value !== undefined)
255
- using.set(key, value);
256
- else if (!using.has(key))
257
- using.set(key, true);
258
- };
259
- }
260
- }
261
-
262
183
  const Oops$9 = issues({
263
184
  Timeout: (keys, timeout) => `No update for [${keys}] within ${timeout}.`,
264
185
  StrictUpdate: () => `Strict update() did not find pending updates.`,
@@ -475,7 +396,63 @@ class Model {
475
396
  }
476
397
  }
477
398
 
399
+ class Subscriber {
400
+ constructor(parent, onUpdate) {
401
+ this.onUpdate = onUpdate;
402
+ this.active = false;
403
+ this.dependant = new Set();
404
+ const proxy = create(parent.subject);
405
+ const using = new Map();
406
+ const listen = this.add = (key, value) => {
407
+ if (value !== undefined)
408
+ using.set(key, value);
409
+ else if (!using.has(key))
410
+ using.set(key, true);
411
+ };
412
+ LISTEN.set(proxy, listen);
413
+ defineProperty(proxy, LOCAL, { value: this });
414
+ defineProperty(this, "proxy", {
415
+ configurable: true,
416
+ get() {
417
+ if (UPDATE.has(proxy))
418
+ setTimeout(() => UPDATE.delete(proxy), 0);
419
+ return proxy;
420
+ }
421
+ });
422
+ const release = parent.addListener(key => {
423
+ if (!this.active)
424
+ return;
425
+ const handler = using.get(key);
426
+ let notify;
427
+ if (typeof handler == "function") {
428
+ const callback = handler();
429
+ if (callback === true)
430
+ notify = this.onUpdate(key, parent);
431
+ }
432
+ else if (handler === true)
433
+ notify = this.onUpdate(key, parent);
434
+ if (!notify)
435
+ return;
436
+ const notate = () => {
437
+ UPDATE.set(proxy, getUpdate(parent.subject).filter(k => using.has(k)));
438
+ };
439
+ parent.waiting.add(notate);
440
+ parent.waiting.add(notify);
441
+ });
442
+ this.commit = () => {
443
+ this.active = true;
444
+ this.dependant.forEach(x => x.commit());
445
+ return this.release;
446
+ };
447
+ this.release = () => {
448
+ this.dependant.forEach(x => x.release());
449
+ release();
450
+ };
451
+ }
452
+ }
453
+
478
454
  const UPDATE = new WeakMap();
455
+ const LISTEN = new WeakMap();
479
456
  const CONTROL = Symbol("CONTROL");
480
457
  function getUpdate(subject) {
481
458
  return UPDATE.get(subject);
@@ -506,9 +483,9 @@ class Controller {
506
483
  enumerable: false,
507
484
  set: this.ref(key),
508
485
  get() {
509
- const local = this[LOCAL];
510
- if (local)
511
- local.add(key);
486
+ const listen = LISTEN.get(this);
487
+ if (listen)
488
+ listen(key);
512
489
  return state.get(key);
513
490
  }
514
491
  });
@@ -635,9 +612,10 @@ function apply(fn, label) {
635
612
  get() {
636
613
  if (!state.has(key) && suspense)
637
614
  throw suspend(control, key);
615
+ const listen = LISTEN.get(this);
638
616
  const local = this[LOCAL];
639
- if (local)
640
- local.add(key);
617
+ if (listen)
618
+ listen(key);
641
619
  return onGet
642
620
  ? onGet(local)
643
621
  : state.get(key);
@@ -735,91 +713,116 @@ function act(task) {
735
713
  });
736
714
  }
737
715
 
738
- function ref(callback) {
739
- return apply(function ref(key) {
740
- const value = this.ref(key, callback && createValueEffect(callback));
741
- defineProperty(value, "current", {
742
- set: value,
743
- get: () => this.state.get(key)
716
+ function factoryMode(self, output, key, required) {
717
+ const { subject, state } = self;
718
+ let pending;
719
+ let error;
720
+ if (output instanceof Promise) {
721
+ state.set(key, undefined);
722
+ pending = output
723
+ .catch(err => error = err)
724
+ .then(val => {
725
+ state.set(key, val);
726
+ return val;
727
+ })
728
+ .finally(() => {
729
+ pending = undefined;
730
+ self.update(key);
744
731
  });
745
- return {
746
- value,
747
- explicit: true
748
- };
749
- });
732
+ }
733
+ state.set(key, output);
734
+ const suspend = () => {
735
+ if (required === false)
736
+ return undefined;
737
+ const issue = Oops$7.NotReady(subject, key);
738
+ Object.assign(pending, {
739
+ message: issue.message,
740
+ stack: issue.stack
741
+ });
742
+ throw pending;
743
+ };
744
+ return () => {
745
+ if (error)
746
+ throw error;
747
+ if (pending)
748
+ return suspend();
749
+ if (state.has(key))
750
+ return state.get(key);
751
+ };
750
752
  }
751
753
 
752
754
  const Oops$7 = issues({
753
- NonOptional: (Parent, key) => `Property ${Parent}.${key} is marked as required.`,
755
+ // BadSource: (model, property, got) =>
756
+ // `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
757
+ PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`,
758
+ ComputeFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`,
754
759
  NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
755
- Failed: (model, key) => `Generating initial value for ${model}.${key} failed.`
760
+ FactoryFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`,
761
+ Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`
756
762
  });
757
- function put(factory, argument) {
758
- return apply(function set(key) {
759
- const { subject, state } = this;
760
- const required = argument === true ||
761
- argument === undefined;
762
- let pending;
763
- let error;
763
+ function get(arg0, arg1, arg2) {
764
+ return apply(function get(key) {
765
+ const { subject } = this;
766
+ const computeRequired = arg1 === true || arg2 === true;
767
+ const factoryRequired = arg1 !== false && arg2 !== false;
768
+ let getter;
769
+ // Easy mistake, using a peer, will always be unresolved.
770
+ if (typeof arg0 == "symbol")
771
+ throw Oops$7.PeerNotAllowed(subject, key);
772
+ // if(typeof arg0 == "function" && /^[A-Z]/.test(arg0.name))
773
+ // throw Oops.BadSource(subject, key, arg0);
774
+ this.state.set(key, undefined);
764
775
  const init = () => {
765
- const output = typeof factory == "function" ?
766
- mayRetry(() => factory.call(subject, key, subject)) :
767
- factory;
768
- if (output instanceof Promise) {
769
- pending = output
770
- .catch(err => error = err)
771
- .then(val => {
772
- state.set(key, val);
773
- return val;
774
- })
775
- .finally(() => {
776
- pending = undefined;
777
- this.update(key);
778
- });
776
+ let source = this;
777
+ let setter;
778
+ if (typeof arg0 == "function") {
779
+ const result = mayRetry(() => arg0.call(subject, key, subject));
780
+ if (typeof result == "function")
781
+ setter = result;
782
+ else {
783
+ getter = factoryMode(this, result, key, factoryRequired);
784
+ return;
785
+ }
786
+ }
787
+ else if (arg0 instanceof Promise) {
788
+ getter = factoryMode(this, arg0, key, factoryRequired);
779
789
  return;
780
790
  }
781
- state.set(key, output);
782
- return output;
791
+ else if (typeof arg1 == "function") {
792
+ // replace source controller in-case it is different
793
+ source = ensure(arg0);
794
+ setter = arg1;
795
+ }
796
+ else
797
+ throw new Error(`Factory argument cannot be ${arg1}`);
798
+ getter = computeMode(this, source, setter, key, computeRequired);
783
799
  };
784
- if (required)
800
+ if (typeof arg0 == "function" && factoryRequired)
785
801
  try {
786
802
  init();
787
803
  }
788
804
  catch (err) {
789
- Oops$7.Failed(subject, key).warn();
805
+ Oops$7.ComputeFailed(subject, key).warn();
790
806
  throw err;
791
807
  }
792
- const suspend = () => {
793
- if (required === false)
794
- return undefined;
795
- const issue = Oops$7.NotReady(subject, key);
796
- Object.assign(pending, {
797
- message: issue.message,
798
- stack: issue.stack
799
- });
800
- throw pending;
801
- };
802
- const get = () => {
803
- if (pending)
804
- return suspend();
805
- if (error)
806
- throw error;
807
- if (state.has(key))
808
- return state.get(key);
809
- const output = init();
810
- return pending
811
- ? suspend()
812
- : output;
808
+ return () => {
809
+ if (!getter)
810
+ init();
811
+ return getter();
813
812
  };
814
- const set = typeof argument == "function"
815
- ? createValueEffect(argument)
816
- : (value) => {
817
- if (value === undefined && required)
818
- throw Oops$7.NonOptional(subject, key);
819
- };
813
+ });
814
+ }
815
+
816
+ function ref(callback) {
817
+ return apply(function ref(key) {
818
+ const value = this.ref(key, callback && createValueEffect(callback));
819
+ defineProperty(value, "current", {
820
+ set: value,
821
+ get: () => this.state.get(key)
822
+ });
820
823
  return {
821
- set,
822
- get
824
+ value,
825
+ explicit: true
823
826
  };
824
827
  });
825
828
  }
@@ -987,31 +990,77 @@ function createProxy(emit, watch) {
987
990
  const Parent = new WeakMap();
988
991
  const Oops$6 = issues({
989
992
  BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`,
993
+ NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
990
994
  });
991
995
  function use$1(input, argument) {
992
996
  return apply(function use(key) {
993
997
  const { state, subject } = this;
998
+ const required = argument !== false;
999
+ let pending;
1000
+ let error;
1001
+ let get = (_local) => state.get(key);
994
1002
  if (typeof input === "function")
995
- input = "prototype" in input ? new input() : input();
1003
+ input = /^[A-Z]/.test(input.name)
1004
+ ? new input() : input;
996
1005
  else if (input && typeof input !== "object")
997
1006
  throw Oops$6.BadArgument(typeof input);
998
1007
  if (input instanceof Map || input instanceof Set)
999
1008
  return keyed(this, key, input);
1000
- function onUpdate(next) {
1009
+ const onUpdate = (next) => {
1001
1010
  state.set(key, next);
1002
1011
  if (next) {
1012
+ get = getRecursive(key, this);
1003
1013
  Parent.set(next, subject);
1004
1014
  ensure(next);
1005
1015
  }
1006
1016
  if (typeof argument == "function")
1007
1017
  argument(next);
1008
1018
  return true;
1009
- }
1010
- if (input)
1011
- onUpdate(input);
1019
+ };
1020
+ const suspend = () => {
1021
+ if (required === false)
1022
+ return;
1023
+ const issue = Oops$6.NotReady(subject, key);
1024
+ Object.assign(pending, {
1025
+ message: issue.message,
1026
+ stack: issue.stack
1027
+ });
1028
+ throw pending;
1029
+ };
1030
+ const initialize = () => {
1031
+ const output = typeof input == "function" ?
1032
+ mayRetry(() => input.call(subject, subject)) :
1033
+ input;
1034
+ if (output instanceof Promise) {
1035
+ pending = output
1036
+ .catch(err => error = err)
1037
+ .then(val => {
1038
+ onUpdate(val);
1039
+ return val;
1040
+ })
1041
+ .finally(() => {
1042
+ pending = undefined;
1043
+ this.update(key);
1044
+ });
1045
+ return;
1046
+ }
1047
+ onUpdate(output);
1048
+ };
1049
+ if (input !== undefined && required)
1050
+ initialize();
1012
1051
  return {
1013
1052
  set: onUpdate,
1014
- recursive: true
1053
+ get(local) {
1054
+ if (pending)
1055
+ return suspend();
1056
+ if (error)
1057
+ throw error;
1058
+ if (!state.has(key))
1059
+ initialize();
1060
+ if (pending)
1061
+ return suspend();
1062
+ return get(local);
1063
+ }
1015
1064
  };
1016
1065
  });
1017
1066
  }
@@ -1458,5 +1507,5 @@ class Global extends MVC {
1458
1507
  }
1459
1508
  }
1460
1509
 
1461
- export { CONTROL, Consumer, Global, LOCAL, MVC as Model, Provider, STATE, WHY, act, apply, MVC as default, get, has, put, ref, set, tap, use$1 as use, useFrom, useLocal, useModel, useTap };
1510
+ export { CONTROL, Consumer, Global, LOCAL, MVC as Model, Provider, STATE, WHY, act, apply, MVC as default, get, has, ref, set, tap, use$1 as use, useFrom, useLocal, useModel, useTap };
1462
1511
  //# sourceMappingURL=index.esm.js.map