@expressive/mvc 0.0.3 → 0.1.1

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
@@ -8,6 +8,31 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
8
8
 
9
9
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
10
10
 
11
+ /*! *****************************************************************************
12
+ Copyright (c) Microsoft Corporation.
13
+
14
+ Permission to use, copy, modify, and/or distribute this software for any
15
+ purpose with or without fee is hereby granted.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
18
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
19
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
20
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
21
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
22
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
23
+ PERFORMANCE OF THIS SOFTWARE.
24
+ ***************************************************************************** */
25
+
26
+ function __awaiter(thisArg, _arguments, P, generator) {
27
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
28
+ return new (P || (P = Promise))(function (resolve, reject) {
29
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
30
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
31
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
32
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
33
+ });
34
+ }
35
+
11
36
  class Issue extends Error {
12
37
  constructor() {
13
38
  super(...arguments);
@@ -26,6 +51,50 @@ function issues(register) {
26
51
  }
27
52
 
28
53
  const Oops$c = issues({
54
+ ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
55
+ });
56
+ function suspend(source, key) {
57
+ const error = Oops$c.ValueNotReady(source.subject, key);
58
+ const promise = new Promise(resolve => {
59
+ const release = source.addListener(forKey => {
60
+ if (forKey == key)
61
+ return () => {
62
+ if (source.state[key] !== undefined) {
63
+ release();
64
+ resolve();
65
+ }
66
+ };
67
+ });
68
+ });
69
+ return Object.assign(promise, {
70
+ toString: () => String(error),
71
+ name: "Suspense",
72
+ message: error.message,
73
+ stack: error.stack
74
+ });
75
+ }
76
+ function mayRetry(fn) {
77
+ const retry = (err) => {
78
+ if (err instanceof Promise)
79
+ return err.then(compute);
80
+ else
81
+ throw err;
82
+ };
83
+ const compute = () => {
84
+ try {
85
+ const output = fn();
86
+ return output instanceof Promise
87
+ ? output.catch(retry)
88
+ : output;
89
+ }
90
+ catch (err) {
91
+ return retry(err);
92
+ }
93
+ };
94
+ return compute();
95
+ }
96
+
97
+ const Oops$b = issues({
29
98
  BadEffectCallback: () => `Callback for effect-callback may only return a function.`
30
99
  });
31
100
  const { assign, create, defineProperty, entries, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
@@ -51,7 +120,7 @@ function createEffect(callback) {
51
120
  if (unSet instanceof Promise)
52
121
  unSet = undefined;
53
122
  if (unSet && typeof unSet !== "function")
54
- throw Oops$c.BadEffectCallback();
123
+ throw Oops$b.BadEffectCallback();
55
124
  };
56
125
  }
57
126
  function createValueEffect(callback) {
@@ -67,28 +136,8 @@ function createValueEffect(callback) {
67
136
  if (typeof out == "function")
68
137
  unSet = out;
69
138
  else
70
- throw Oops$c.BadEffectCallback();
71
- };
72
- }
73
- function suspenseBoundary(fn) {
74
- const retry = (err) => {
75
- if (err instanceof Promise)
76
- return err.then(compute);
77
- else
78
- throw err;
79
- };
80
- const compute = () => {
81
- try {
82
- const output = fn();
83
- return output instanceof Promise
84
- ? output.catch(retry)
85
- : output;
86
- }
87
- catch (err) {
88
- return retry(err);
89
- }
139
+ throw Oops$b.BadEffectCallback();
90
140
  };
91
- return compute();
92
141
  }
93
142
 
94
143
  const UPDATE = new WeakMap();
@@ -107,9 +156,18 @@ const LOCAL = Symbol("LOCAL");
107
156
  const STATE = Symbol("STATE");
108
157
  class Model {
109
158
  constructor() {
110
- define(this, CONTROL, new Controller(this));
159
+ const control = new Controller(this);
160
+ define(this, CONTROL, control);
111
161
  define(this, "get", this);
112
- define(this, "set", this);
162
+ defineLazy(this, "set", () => {
163
+ const assign = (key, value) => {
164
+ control.update(key, value);
165
+ };
166
+ ready(control);
167
+ for (const key of getOwnPropertyNames(control.proxy))
168
+ define(assign, key, control.ref(key));
169
+ return assign;
170
+ });
113
171
  }
114
172
  get [STATE]() {
115
173
  return this[CONTROL].state;
@@ -150,18 +208,25 @@ class Model {
150
208
  effect(callback, select) {
151
209
  const effect = createEffect(callback);
152
210
  return control(this, control => {
211
+ let busy = false;
212
+ let inject = this.get;
213
+ const invoke = () => {
214
+ if (busy)
215
+ return;
216
+ const output = mayRetry(() => {
217
+ effect.call(inject, inject);
218
+ });
219
+ if (output instanceof Promise) {
220
+ output.finally(() => busy = false);
221
+ busy = true;
222
+ }
223
+ };
153
224
  if (!select) {
154
225
  const sub = new Subscriber(control, () => invoke);
155
- const invoke = () => {
156
- const x = sub.proxy;
157
- effect.call(x, x);
158
- };
226
+ inject = sub.proxy;
159
227
  invoke();
160
228
  return sub.commit();
161
229
  }
162
- const invoke = () => {
163
- effect.call(this.get, this.get);
164
- };
165
230
  invoke();
166
231
  if (!select.length) {
167
232
  control.onDestroy.add(invoke);
@@ -239,10 +304,7 @@ class Model {
239
304
  return (typeof maybe == "function" &&
240
305
  maybe.prototype instanceof this);
241
306
  }
242
- }
243
- defineLazy(Model, CONTROL, function () {
244
- return new Controller(this);
245
- });
307
+ }
246
308
 
247
309
  class Subscriber {
248
310
  constructor(target, onUpdate) {
@@ -290,30 +352,6 @@ class Subscriber {
290
352
  }
291
353
  }
292
354
 
293
- const Oops$b = issues({
294
- ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
295
- });
296
- function suspend(source, key) {
297
- const error = Oops$b.ValueNotReady(source.subject, key);
298
- const promise = new Promise(resolve => {
299
- const release = source.addListener(forKey => {
300
- if (forKey == key)
301
- return () => {
302
- if (source.state[key] !== undefined) {
303
- release();
304
- resolve();
305
- }
306
- };
307
- });
308
- });
309
- return Object.assign(promise, {
310
- toString: () => String(error),
311
- name: "Suspense",
312
- message: error.message,
313
- stack: error.stack
314
- });
315
- }
316
-
317
355
  const Oops$a = issues({
318
356
  BadComputedSource: (model, property, got) => `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
319
357
  PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`,
@@ -468,26 +506,28 @@ class Controller {
468
506
  this.proxy = Object.create(subject);
469
507
  }
470
508
  start() {
471
- for (const key in this.subject)
472
- this.manage(key);
509
+ READY.add(this);
510
+ for (const key in this.subject) {
511
+ const { value: entry } = getOwnPropertyDescriptor(this.subject, key);
512
+ if (typeof entry == "function")
513
+ continue;
514
+ if (typeof entry == "symbol") {
515
+ const instruction = PENDING.get(entry);
516
+ if (instruction)
517
+ instruction.call(this, key, this);
518
+ continue;
519
+ }
520
+ this.manage(key, entry);
521
+ }
473
522
  this.emit([]);
474
523
  }
475
524
  stop() {
476
525
  this.followers.clear();
477
526
  this.onDestroy.forEach(x => x());
478
527
  }
479
- manage(key) {
528
+ manage(key, value) {
480
529
  const { proxy, state, subject } = this;
481
- const { value: entry } = getOwnPropertyDescriptor(subject, key);
482
- if (typeof entry == "function")
483
- return;
484
- if (typeof entry == "symbol") {
485
- const instruction = PENDING.get(entry);
486
- if (instruction)
487
- instruction.call(this, key, this);
488
- return;
489
- }
490
- state[key] = entry;
530
+ state[key] = value;
491
531
  const set = this.ref(key);
492
532
  defineProperty(subject, key, {
493
533
  enumerable: false,
@@ -579,26 +619,23 @@ class Controller {
579
619
  }
580
620
  function control(subject, cb) {
581
621
  let control = subject[CONTROL];
582
- const ready = READY.has(control);
583
622
  if (!control) {
584
623
  control = new Controller(subject);
585
624
  define(subject, CONTROL, control);
586
625
  }
587
- if (!cb) {
588
- if (!ready) {
589
- READY.add(control);
590
- control.start();
591
- }
592
- return control;
593
- }
626
+ return ready(control, cb);
627
+ }
628
+ function ready(control, cb) {
629
+ const ready = READY.has(control);
594
630
  if (!ready) {
595
- let done;
596
- control.requestUpdate(() => {
597
- done = cb(control);
598
- });
599
- return () => done && done();
631
+ if (cb) {
632
+ let done;
633
+ control.requestUpdate(() => done = cb(control));
634
+ return () => done && done();
635
+ }
636
+ control.start();
600
637
  }
601
- return cb(control);
638
+ return cb ? cb(control) : control;
602
639
  }
603
640
 
604
641
  function apply(fn, label) {
@@ -691,18 +728,19 @@ const Oops$8 = issues({
691
728
  function act(task) {
692
729
  return apply(function act(key) {
693
730
  let pending = false;
694
- const invoke = (...args) => {
731
+ const invoke = (...args) => __awaiter(this, void 0, void 0, function* () {
695
732
  if (pending)
696
733
  return Promise.reject(Oops$8.DuplicateAction(key));
697
734
  pending = true;
698
735
  this.update(key);
699
- return new Promise(res => {
700
- res(task.apply(this.subject, args));
701
- }).finally(() => {
736
+ try {
737
+ return yield mayRetry(() => (task.apply(this.subject, args)));
738
+ }
739
+ finally {
702
740
  pending = false;
703
741
  this.update(key);
704
- });
705
- };
742
+ }
743
+ });
706
744
  this.state[key] = undefined;
707
745
  defineProperty(invoke, "active", {
708
746
  get: () => pending
@@ -832,7 +870,7 @@ function pendingFactory(parent, key, fn, required) {
832
870
  let pending;
833
871
  let error;
834
872
  const init = () => {
835
- const output = suspenseBoundary(() => {
873
+ const output = mayRetry(() => {
836
874
  return fn.call(subject, key, subject);
837
875
  });
838
876
  if (output instanceof Promise) {
@@ -1001,7 +1039,7 @@ const useLookup = () => React__default["default"].useContext(LookupContext);
1001
1039
  const Oops$4 = issues({
1002
1040
  NothingInContext: (name) => `Couldn't find ${name} in context; did you forget to use a Provider?`
1003
1041
  });
1004
- function useInContext(Type, arg) {
1042
+ function useLocal(Type, arg) {
1005
1043
  const instance = useLookup().get(Type);
1006
1044
  if (!instance && arg !== false)
1007
1045
  throw Oops$4.NothingInContext(Type.name);
@@ -1169,7 +1207,7 @@ function useTap(source, path, expect) {
1169
1207
  if ("get" in source)
1170
1208
  return () => source.get();
1171
1209
  if ("prototype" in source)
1172
- return () => useInContext(source);
1210
+ return () => useLocal(source);
1173
1211
  else
1174
1212
  return source;
1175
1213
  }, [])();
@@ -1204,7 +1242,7 @@ class MVC extends Model {
1204
1242
  return useModel(this, arg, callback);
1205
1243
  }
1206
1244
  static get(required) {
1207
- return useInContext(this, required);
1245
+ return useLocal(this, required);
1208
1246
  }
1209
1247
  static tap(key, expect) {
1210
1248
  return useTap(this, key, expect);
@@ -1258,7 +1296,7 @@ function Consumer(props) {
1258
1296
  return children(useTap(Control));
1259
1297
  const callback = has || get;
1260
1298
  if (typeof callback == "function")
1261
- callback(useInContext(Control, !!has));
1299
+ callback(useLocal(Control, !!has));
1262
1300
  else
1263
1301
  throw Oops$2.BadConsumerProps();
1264
1302
  return null;
@@ -1377,7 +1415,7 @@ exports.set = set;
1377
1415
  exports.tap = tap;
1378
1416
  exports.use = use$1;
1379
1417
  exports.useFrom = useFrom;
1380
- exports.useInContext = useInContext;
1418
+ exports.useLocal = useLocal;
1381
1419
  exports.useModel = useModel;
1382
1420
  exports.useTap = useTap;
1383
1421
  //# sourceMappingURL=index.js.map