@expressive/mvc 0.8.2 → 0.9.0

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
@@ -25,44 +25,13 @@ function issues(register) {
25
25
  return Library;
26
26
  }
27
27
 
28
- const Oops$d = issues({
29
- BadCallback: () => `Callback for effect-callback may only return a function.`
30
- });
31
- const { assign, create, defineProperty, entries, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
32
- function createEffect(callback) {
33
- let unSet;
34
- return function (value) {
35
- if (typeof unSet == "function")
36
- unSet();
37
- unSet = callback.call(this, value);
38
- if (unSet instanceof Promise)
39
- unSet = undefined;
40
- if (unSet && typeof unSet !== "function")
41
- throw Oops$d.BadCallback();
42
- };
43
- }
44
- function createValueEffect(callback) {
45
- let unSet;
46
- return function (value) {
47
- if (typeof unSet == "function")
48
- unSet(value);
49
- const out = callback.call(this, value, this);
50
- if (typeof out == "boolean")
51
- return out;
52
- if (!out || out instanceof Promise)
53
- return;
54
- if (typeof out == "function")
55
- unSet = out;
56
- else
57
- throw Oops$d.BadCallback();
58
- };
59
- }
28
+ const { assign, create, defineProperties, defineProperty, entries, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
60
29
 
61
- const Oops$c = issues({
30
+ const Oops$d = issues({
62
31
  NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
63
32
  });
64
33
  function suspend(source, key) {
65
- const error = Oops$c.NotReady(source.subject, key);
34
+ const error = Oops$d.NotReady(source.subject, key);
66
35
  const promise = new Promise(resolve => {
67
36
  const release = source.addListener(forKey => {
68
37
  if (forKey == key)
@@ -102,7 +71,7 @@ function mayRetry(fn) {
102
71
  return compute();
103
72
  }
104
73
 
105
- const Oops$b = issues({
74
+ const Oops$c = issues({
106
75
  Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`
107
76
  });
108
77
  const INFO = new WeakMap();
@@ -119,7 +88,7 @@ function computeMode(self, source, setter, key, required) {
119
88
  return setter.call(sub.proxy, sub.proxy);
120
89
  }
121
90
  catch (err) {
122
- Oops$b.Failed(subject, key, initial).warn();
91
+ Oops$c.Failed(subject, key, initial).warn();
123
92
  throw err;
124
93
  }
125
94
  };
@@ -220,8 +189,8 @@ class Subscriber {
220
189
  notify = this.onUpdate(key, parent);
221
190
  if (!notify)
222
191
  return;
223
- parent.waiting.add(() => {
224
- this.latest = parent.latest.filter(k => watch.has(k));
192
+ parent.waiting.add(update => {
193
+ this.latest = update.filter(k => watch.has(k));
225
194
  });
226
195
  parent.waiting.add(notify);
227
196
  };
@@ -328,13 +297,13 @@ class Control {
328
297
  else if (!frame.size)
329
298
  setTimeout(() => {
330
299
  flush(this);
331
- this.latest = Array.from(frame);
300
+ const keys = this.latest = Array.from(frame);
332
301
  setTimeout(() => this.latest = undefined, 0);
333
302
  frame.clear();
334
303
  for (const notify of waiting)
335
304
  try {
336
305
  waiting.delete(notify);
337
- notify();
306
+ notify(keys);
338
307
  }
339
308
  catch (err) {
340
309
  console.error(err);
@@ -362,18 +331,21 @@ class Control {
362
331
  if (!control.state) {
363
332
  const { waiting } = control;
364
333
  if (cb) {
365
- let done;
334
+ let callback;
366
335
  waiting.add(() => {
367
- done = cb(control);
336
+ callback = cb(control);
368
337
  });
369
- return () => done && done();
338
+ return () => {
339
+ if (callback)
340
+ callback();
341
+ };
370
342
  }
371
343
  control.state = new Map();
372
344
  for (const key in subject)
373
345
  control.watch(key);
374
346
  control.waiting = new Set();
375
347
  for (const cb of waiting)
376
- cb();
348
+ cb([]);
377
349
  }
378
350
  return cb ? cb(control) : control;
379
351
  }
@@ -489,15 +461,15 @@ function __awaiter(thisArg, _arguments, P, generator) {
489
461
  });
490
462
  }
491
463
 
492
- const Oops$a = issues({
464
+ const Oops$b = issues({
493
465
  DuplicatePending: (key) => `Invoked action ${key} but one is already active.`
494
466
  });
495
- function act(task) {
496
- return add(function act(key) {
467
+ function run(task) {
468
+ return add(function run(key) {
497
469
  let pending = false;
498
470
  const invoke = (...args) => __awaiter(this, void 0, void 0, function* () {
499
471
  if (pending)
500
- return Promise.reject(Oops$a.DuplicatePending(key));
472
+ return Promise.reject(Oops$b.DuplicatePending(key));
501
473
  pending = true;
502
474
  this.update(key);
503
475
  try {
@@ -519,7 +491,7 @@ function act(task) {
519
491
  });
520
492
  }
521
493
 
522
- const Oops$9 = issues({
494
+ const Oops$a = issues({
523
495
  NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
524
496
  });
525
497
  function factoryMode(self, output, key, required) {
@@ -543,7 +515,7 @@ function factoryMode(self, output, key, required) {
543
515
  const suspend = () => {
544
516
  if (required === false)
545
517
  return undefined;
546
- const issue = Oops$9.NotReady(subject, key);
518
+ const issue = Oops$a.NotReady(subject, key);
547
519
  assign(pending, {
548
520
  message: issue.message,
549
521
  stack: issue.stack
@@ -560,7 +532,7 @@ function factoryMode(self, output, key, required) {
560
532
  };
561
533
  }
562
534
 
563
- const Oops$8 = issues({
535
+ const Oops$9 = issues({
564
536
  PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`,
565
537
  ComputeFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`,
566
538
  Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`
@@ -573,7 +545,7 @@ function get(arg0, arg1, arg2) {
573
545
  let getter;
574
546
  // Easy mistake, using a peer, will always be unresolved.
575
547
  if (typeof arg0 == "symbol")
576
- throw Oops$8.PeerNotAllowed(subject, key);
548
+ throw Oops$9.PeerNotAllowed(subject, key);
577
549
  // if(typeof arg0 == "function" && /^[A-Z]/.test(arg0.name))
578
550
  // throw Oops.BadSource(subject, key, arg0);
579
551
  this.state.set(key, undefined);
@@ -607,7 +579,7 @@ function get(arg0, arg1, arg2) {
607
579
  init();
608
580
  }
609
581
  catch (err) {
610
- Oops$8.ComputeFailed(subject, key).warn();
582
+ Oops$9.ComputeFailed(subject, key).warn();
611
583
  throw err;
612
584
  }
613
585
  return () => {
@@ -618,6 +590,67 @@ function get(arg0, arg1, arg2) {
618
590
  });
619
591
  }
620
592
 
593
+ const Oops$8 = issues({
594
+ BadCallback: () => `Callback for effect-callback may only return a function.`
595
+ });
596
+ function createEffect(model, callback, keys) {
597
+ return Control.for(model, control => {
598
+ let unSet;
599
+ let busy = false;
600
+ function effect(value) {
601
+ if (typeof unSet == "function")
602
+ unSet();
603
+ unSet = callback.call(this, value);
604
+ if (unSet instanceof Promise)
605
+ unSet = undefined;
606
+ if (unSet && typeof unSet !== "function")
607
+ throw Oops$8.BadCallback();
608
+ }
609
+ function invoke() {
610
+ if (busy)
611
+ return;
612
+ const output = mayRetry(() => {
613
+ effect.call(model, model);
614
+ });
615
+ if (output instanceof Promise) {
616
+ output.finally(() => busy = false);
617
+ busy = true;
618
+ }
619
+ }
620
+ if (Array.isArray(keys)) {
621
+ invoke();
622
+ return control.addListener(key => {
623
+ if (key === null) {
624
+ if (!keys.length)
625
+ invoke();
626
+ }
627
+ else if (keys.includes(key))
628
+ return invoke;
629
+ });
630
+ }
631
+ const sub = control.subscribe(() => invoke);
632
+ model = sub.proxy;
633
+ invoke();
634
+ return sub.commit();
635
+ });
636
+ }
637
+ function createValueEffect(callback) {
638
+ let unSet;
639
+ return function (value) {
640
+ if (typeof unSet == "function")
641
+ unSet(value);
642
+ const out = callback.call(this, value, this);
643
+ if (typeof out == "boolean")
644
+ return out;
645
+ if (!out || out instanceof Promise)
646
+ return;
647
+ if (typeof out == "function")
648
+ unSet = out;
649
+ else
650
+ throw Oops$8.BadCallback();
651
+ };
652
+ }
653
+
621
654
  function ref(arg, mapper) {
622
655
  return add(function ref(key) {
623
656
  let value = {};
@@ -911,8 +944,10 @@ function has(Type, required) {
911
944
  }
912
945
 
913
946
  const Oops$5 = issues({
914
- Timeout: (keys, timeout) => `No update for [${keys}] within ${timeout}.`,
915
- StrictUpdate: () => `Strict update() did not find pending updates.`,
947
+ Timeout: (keys, timeout) => `No update seen for [${keys}] within ${timeout}.`,
948
+ KeysExpected: (key) => `Key "${key}" was expected in current update.`,
949
+ StrictUpdate: () => `Expected update but none in progress.`,
950
+ StrictNoUpdate: () => `Update is not expected but one is in progress!`,
916
951
  NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
917
952
  });
918
953
  class Model {
@@ -920,91 +955,84 @@ class Model {
920
955
  new Control(this);
921
956
  defineProperty(this, "is", { value: this });
922
957
  }
923
- on(select, argument, once) {
924
- if (typeof select == "function")
925
- return Control.for(this, x => x.addListener(select));
926
- const single = typeof select == "string";
927
- let keys = single ? [select] : select;
928
- if (typeof argument !== "function")
929
- return new Promise((resolve, reject) => {
930
- const removeListener = Control.for(this, control => {
931
- for (const key of keys)
932
- try {
933
- void this[key];
934
- }
935
- catch (e) { }
936
- return control.addListener(key => {
937
- if (!key) {
938
- removeListener();
939
- reject(Oops$5.Timeout(select, `lifetime of ${this}`));
940
- }
941
- else if (keys.includes(key)) {
958
+ on(arg1, arg2, arg3) {
959
+ if (typeof arg1 == "function")
960
+ return createEffect(this.is, arg1, arg2);
961
+ const single = typeof arg1 == "string";
962
+ const keys = typeof arg1 == "string" ? [arg1] :
963
+ typeof arg1 == "object" ? arg1 :
964
+ undefined;
965
+ if (typeof arg2 == "function" && keys)
966
+ return Control.for(this, control => {
967
+ for (const key of keys)
968
+ try {
969
+ void this[key];
970
+ }
971
+ catch (e) { }
972
+ const invoke = single
973
+ ? () => arg2.call(this, control.state.get(arg1), arg1)
974
+ : (frame) => arg2.call(this, frame);
975
+ const removeListener = control.addListener(key => {
976
+ if (!key && !keys.length)
977
+ invoke([]);
978
+ if (keys.includes(key)) {
979
+ if (arg3)
942
980
  removeListener();
943
- return () => resolve(single ? control.state.get(key) : control.latest);
944
- }
945
- });
981
+ return invoke;
982
+ }
946
983
  });
947
- if (typeof argument == "number")
948
- setTimeout(() => {
949
- removeListener();
950
- reject(Oops$5.Timeout(select, `${argument}ms`));
951
- }, argument);
984
+ return removeListener;
952
985
  });
953
- return Control.for(this, control => {
954
- if (!keys.length)
955
- keys = [...control.state.keys()];
956
- for (const key of keys)
957
- try {
958
- void this[key];
986
+ return new Promise((resolve, reject) => {
987
+ const timeout = (message) => {
988
+ const k = keys ? keys.join(", ") : "any";
989
+ removeListener();
990
+ reject(Oops$5.Timeout(k, message));
991
+ };
992
+ const removeListener = Control.for(this, control => {
993
+ const pending = control.frame;
994
+ if (keys === null) {
995
+ if (pending.size)
996
+ reject(Oops$5.StrictNoUpdate());
997
+ else
998
+ resolve(null);
999
+ return;
959
1000
  }
960
- catch (e) { }
961
- const callback = single
962
- ? () => { argument.call(this, control.state.get(select), select); }
963
- : () => { argument.call(this, control.latest); };
964
- const onEvent = once
965
- ? () => {
966
- removeListener();
967
- callback();
1001
+ if (keys) {
1002
+ if (keys.length) {
1003
+ if (arg2 === 0 && single && !pending.has(keys[0])) {
1004
+ reject(Oops$5.KeysExpected(keys[0]));
1005
+ return;
1006
+ }
1007
+ else
1008
+ for (const key of keys)
1009
+ try {
1010
+ void this[key];
1011
+ }
1012
+ catch (e) { }
1013
+ }
968
1014
  }
969
- : callback;
970
- const removeListener = control.addListener(key => {
971
- if (keys.includes(key))
972
- return onEvent;
973
- });
974
- return removeListener;
975
- });
976
- }
977
- effect(callback, select) {
978
- const effect = createEffect(callback);
979
- return Control.for(this, control => {
980
- let busy = false;
981
- let inject = this.is;
982
- const invoke = () => {
983
- if (busy)
1015
+ else if (!pending.size && arg1 === true || arg2 === 0) {
1016
+ reject(Oops$5.StrictUpdate());
984
1017
  return;
985
- const output = mayRetry(() => {
986
- effect.call(inject, inject);
987
- });
988
- if (output instanceof Promise) {
989
- output.finally(() => busy = false);
990
- busy = true;
991
1018
  }
992
- };
993
- if (!select) {
994
- const sub = control.subscribe(() => invoke);
995
- inject = sub.proxy;
996
- invoke();
997
- return sub.commit();
998
- }
999
- invoke();
1000
- return control.addListener(key => {
1001
- if (key === null) {
1002
- if (!select.length)
1003
- invoke();
1004
- }
1005
- else if (select.includes(key))
1006
- return invoke;
1019
+ else
1020
+ control.waiting.add(resolve);
1021
+ return control.addListener(key => {
1022
+ if (!key) {
1023
+ if (keys && !keys.length)
1024
+ resolve([]);
1025
+ else
1026
+ timeout(`lifetime of ${this}`);
1027
+ }
1028
+ else if (!keys || keys.includes(key)) {
1029
+ removeListener();
1030
+ return keys => resolve(single ? control.state.get(key) : keys);
1031
+ }
1032
+ });
1007
1033
  });
1034
+ if (arg2 && typeof arg2 == "number")
1035
+ setTimeout(() => timeout(`${arg2}ms`), arg2);
1008
1036
  });
1009
1037
  }
1010
1038
  export(subset) {
@@ -1017,13 +1045,10 @@ class Model {
1017
1045
  }
1018
1046
  update(arg1, arg2) {
1019
1047
  const control = Control.for(this);
1020
- const { frame, state, waiting } = control;
1021
- if (arg1 === true && !frame.size)
1022
- return Promise.reject(Oops$5.StrictUpdate());
1023
1048
  if (typeof arg1 == "object") {
1024
1049
  for (const key in arg1)
1025
- if (arg2 === true || (arg2 ? arg2.includes(key) : state.has(key))) {
1026
- state.set(key, arg1[key]);
1050
+ if (arg2 === true || (arg2 ? arg2.includes(key) : control.state.has(key))) {
1051
+ control.state.set(key, arg1[key]);
1027
1052
  control.update(key);
1028
1053
  }
1029
1054
  }
@@ -1037,25 +1062,19 @@ class Model {
1037
1062
  else if (arg2)
1038
1063
  method.call(this);
1039
1064
  }
1040
- arg1 = undefined;
1041
1065
  }
1042
1066
  return {
1043
1067
  then: (callback) => {
1044
1068
  if (!callback)
1045
1069
  throw Oops$5.NoChaining();
1046
- if (frame.size || arg1 !== false)
1047
- waiting.add(() => {
1048
- callback(control.latest);
1049
- });
1050
- else
1051
- callback(false);
1070
+ control.waiting.add(callback);
1052
1071
  }
1053
1072
  };
1054
1073
  }
1055
1074
  /**
1056
- * Mark this instance for garbage-collection and send `willDestroy` event to all listeners.
1075
+ * Clean up side effects and mark this instance for garbage-collection.
1057
1076
  */
1058
- kill() {
1077
+ destroy() {
1059
1078
  Control.for(this).clear();
1060
1079
  }
1061
1080
  /**
@@ -1132,7 +1151,7 @@ class Lookup {
1132
1151
  items.add(entry.value);
1133
1152
  }
1134
1153
  for (const model of items)
1135
- model.kill();
1154
+ model.destroy();
1136
1155
  }
1137
1156
  }
1138
1157
  const LookupContext = React__default["default"].createContext(new Lookup());
@@ -1232,7 +1251,7 @@ function useModel(source, arg, arg2) {
1232
1251
  instance.on(arg, () => update(x => x + 1));
1233
1252
  return () => {
1234
1253
  if (Model.isTypeof(source))
1235
- instance.kill();
1254
+ instance.destroy();
1236
1255
  };
1237
1256
  }, []);
1238
1257
  return instance;
@@ -1245,15 +1264,15 @@ function useModel(source, arg, arg2) {
1245
1264
  return () => {
1246
1265
  local.release();
1247
1266
  if (Model.isTypeof(source))
1248
- instance.kill();
1267
+ instance.destroy();
1249
1268
  };
1250
1269
  }, []);
1251
1270
  return local.proxy;
1252
1271
  }
1253
1272
 
1254
- function useFrom(target, compute, suspend) {
1273
+ function useValue(source, compute, suspend) {
1255
1274
  const local = use(refresh => {
1256
- const sub = Control.for(target).subscribe(() => update);
1275
+ const sub = Control.for(source).subscribe(() => update);
1257
1276
  const spy = sub.proxy;
1258
1277
  let value = compute.call(spy, spy);
1259
1278
  let update;
@@ -1303,7 +1322,7 @@ function useTap(source, arg1, arg2) {
1303
1322
  return source;
1304
1323
  }, [])();
1305
1324
  if (typeof arg1 == "function")
1306
- return useFrom(instance, arg1, arg2);
1325
+ return useValue(instance, arg1, arg2);
1307
1326
  const local = use(refresh => (Control.for(instance).subscribe(() => refresh)));
1308
1327
  React__default["default"].useLayoutEffect(local.commit, []);
1309
1328
  if (arg1 === undefined)
@@ -1333,7 +1352,7 @@ const LOCAL = "$debug local";
1333
1352
  const STATE = "$debug state";
1334
1353
  const UPDATE = "$debug update";
1335
1354
  const CONTROL = "$debug controller";
1336
- Object.defineProperties(Model.prototype, {
1355
+ defineProperties(Model.prototype, {
1337
1356
  [CONTROL]: {
1338
1357
  get() {
1339
1358
  return Control.get(this);
@@ -1452,11 +1471,11 @@ class Global extends MVC {
1452
1471
  super();
1453
1472
  Active.set(this.constructor, this);
1454
1473
  }
1455
- kill(force) {
1474
+ destroy(force) {
1456
1475
  const type = this.constructor;
1457
1476
  if (type.keepAlive && !force)
1458
1477
  return false;
1459
- super.kill();
1478
+ Control.for(this).clear();
1460
1479
  Active.delete(type);
1461
1480
  return true;
1462
1481
  }
@@ -1490,7 +1509,7 @@ class Global extends MVC {
1490
1509
  static reset() {
1491
1510
  const current = Active.get(this);
1492
1511
  if (current)
1493
- current.kill(true);
1512
+ current.destroy(true);
1494
1513
  }
1495
1514
  }
1496
1515
  Global.keepAlive = true;
@@ -1501,17 +1520,17 @@ exports.Global = Global;
1501
1520
  exports.MVC = MVC;
1502
1521
  exports.Model = MVC;
1503
1522
  exports.Provider = Provider;
1504
- exports.act = act;
1505
1523
  exports.add = add;
1506
1524
  exports["default"] = MVC;
1507
1525
  exports.get = get;
1508
1526
  exports.has = has;
1509
1527
  exports.ref = ref;
1528
+ exports.run = run;
1510
1529
  exports.set = set;
1511
1530
  exports.tap = tap;
1512
1531
  exports.use = use$1;
1513
1532
  exports.useContext = useContext;
1514
- exports.useFrom = useFrom;
1515
1533
  exports.useModel = useModel;
1516
1534
  exports.useTap = useTap;
1535
+ exports.useValue = useValue;
1517
1536
  //# sourceMappingURL=index.js.map