@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.d.ts +12 -6
- package/lib/index.esm.js +136 -98
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +136 -98
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
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$
|
|
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$
|
|
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
|
-
|
|
159
|
+
const control = new Controller(this);
|
|
160
|
+
define(this, CONTROL, control);
|
|
111
161
|
define(this, "get", this);
|
|
112
|
-
|
|
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
|
-
|
|
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
|
-
|
|
472
|
-
|
|
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
|
-
|
|
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
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
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
|
-
|
|
596
|
-
|
|
597
|
-
done = cb(control);
|
|
598
|
-
|
|
599
|
-
|
|
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
|
-
|
|
700
|
-
|
|
701
|
-
}
|
|
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 =
|
|
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
|
|
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 () =>
|
|
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
|
|
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(
|
|
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.
|
|
1418
|
+
exports.useLocal = useLocal;
|
|
1381
1419
|
exports.useModel = useModel;
|
|
1382
1420
|
exports.useTap = useTap;
|
|
1383
1421
|
//# sourceMappingURL=index.js.map
|