@expressive/mvc 0.8.0 → 0.8.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.d.ts +21 -18
- package/lib/index.esm.js +82 -84
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +82 -84
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -25,7 +25,7 @@ function issues(register) {
|
|
|
25
25
|
return Library;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const Oops$
|
|
28
|
+
const Oops$d = issues({
|
|
29
29
|
BadCallback: () => `Callback for effect-callback may only return a function.`
|
|
30
30
|
});
|
|
31
31
|
const { assign, create, defineProperty, entries, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
|
|
@@ -38,7 +38,7 @@ function createEffect(callback) {
|
|
|
38
38
|
if (unSet instanceof Promise)
|
|
39
39
|
unSet = undefined;
|
|
40
40
|
if (unSet && typeof unSet !== "function")
|
|
41
|
-
throw Oops$
|
|
41
|
+
throw Oops$d.BadCallback();
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
function createValueEffect(callback) {
|
|
@@ -54,15 +54,15 @@ function createValueEffect(callback) {
|
|
|
54
54
|
if (typeof out == "function")
|
|
55
55
|
unSet = out;
|
|
56
56
|
else
|
|
57
|
-
throw Oops$
|
|
57
|
+
throw Oops$d.BadCallback();
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
const Oops$
|
|
61
|
+
const Oops$c = issues({
|
|
62
62
|
NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
|
|
63
63
|
});
|
|
64
64
|
function suspend(source, key) {
|
|
65
|
-
const error = Oops$
|
|
65
|
+
const error = Oops$c.NotReady(source.subject, key);
|
|
66
66
|
const promise = new Promise(resolve => {
|
|
67
67
|
const release = source.addListener(forKey => {
|
|
68
68
|
if (forKey == key)
|
|
@@ -102,7 +102,7 @@ function mayRetry(fn) {
|
|
|
102
102
|
return compute();
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
const Oops$
|
|
105
|
+
const Oops$b = issues({
|
|
106
106
|
Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`
|
|
107
107
|
});
|
|
108
108
|
const INFO = new WeakMap();
|
|
@@ -119,7 +119,7 @@ function computeMode(self, source, setter, key, required) {
|
|
|
119
119
|
return setter.call(sub.proxy, sub.proxy);
|
|
120
120
|
}
|
|
121
121
|
catch (err) {
|
|
122
|
-
Oops$
|
|
122
|
+
Oops$b.Failed(subject, key, initial).warn();
|
|
123
123
|
throw err;
|
|
124
124
|
}
|
|
125
125
|
};
|
|
@@ -195,20 +195,21 @@ class Subscriber {
|
|
|
195
195
|
this.onUpdate = onUpdate;
|
|
196
196
|
this.active = false;
|
|
197
197
|
this.dependant = new Set();
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
198
|
+
this.watch = new Map();
|
|
199
|
+
this.commit = () => {
|
|
200
|
+
const release = this.parent.addListener(this.event);
|
|
201
|
+
this.active = true;
|
|
202
|
+
this.dependant.forEach(x => x.commit());
|
|
203
|
+
return this.release = () => {
|
|
204
|
+
this.dependant.forEach(x => x.release());
|
|
205
|
+
release();
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
this.event = (key) => {
|
|
209
209
|
if (!this.active)
|
|
210
210
|
return;
|
|
211
|
-
const
|
|
211
|
+
const { parent, watch } = this;
|
|
212
|
+
const handler = watch.get(key);
|
|
212
213
|
let notify;
|
|
213
214
|
if (typeof handler == "function") {
|
|
214
215
|
const callback = handler();
|
|
@@ -219,49 +220,48 @@ class Subscriber {
|
|
|
219
220
|
notify = this.onUpdate(key, parent);
|
|
220
221
|
if (!notify)
|
|
221
222
|
return;
|
|
222
|
-
parent.waiting.add(() =>
|
|
223
|
+
parent.waiting.add(() => {
|
|
224
|
+
this.latest = parent.latest.filter(k => watch.has(k));
|
|
225
|
+
});
|
|
223
226
|
parent.waiting.add(notify);
|
|
224
|
-
});
|
|
225
|
-
this.add = (key, value) => {
|
|
226
|
-
if (value !== undefined)
|
|
227
|
-
using.set(key, value);
|
|
228
|
-
else if (!using.has(key))
|
|
229
|
-
using.set(key, true);
|
|
230
|
-
};
|
|
231
|
-
this.commit = () => {
|
|
232
|
-
this.active = true;
|
|
233
|
-
this.dependant.forEach(x => x.commit());
|
|
234
|
-
return this.release;
|
|
235
|
-
};
|
|
236
|
-
this.release = () => {
|
|
237
|
-
this.dependant.forEach(x => x.release());
|
|
238
|
-
release();
|
|
239
227
|
};
|
|
228
|
+
const proxy = create(parent.subject);
|
|
229
|
+
const reset = () => this.latest = undefined;
|
|
230
|
+
REGISTER$1.set(proxy, this);
|
|
231
|
+
defineProperty(this, "proxy", {
|
|
232
|
+
configurable: true,
|
|
233
|
+
get() {
|
|
234
|
+
setTimeout(reset, 0);
|
|
235
|
+
return proxy;
|
|
236
|
+
}
|
|
237
|
+
});
|
|
240
238
|
}
|
|
241
239
|
static get(from) {
|
|
242
240
|
return REGISTER$1.get(from);
|
|
243
241
|
}
|
|
242
|
+
get using() {
|
|
243
|
+
return Array.from(this.watch.keys());
|
|
244
|
+
}
|
|
245
|
+
assign(apply, keys) {
|
|
246
|
+
const { waiting, subject } = this.parent;
|
|
247
|
+
this.active = false;
|
|
248
|
+
if (typeof keys !== "object")
|
|
249
|
+
keys = getOwnPropertyNames(subject);
|
|
250
|
+
for (const key of keys)
|
|
251
|
+
if (key in apply)
|
|
252
|
+
subject[key] = apply[key];
|
|
253
|
+
waiting.add(() => this.active = true);
|
|
254
|
+
}
|
|
255
|
+
add(key, value) {
|
|
256
|
+
if (value !== undefined)
|
|
257
|
+
this.watch.set(key, value);
|
|
258
|
+
else if (!this.watch.has(key))
|
|
259
|
+
this.watch.set(key, true);
|
|
260
|
+
}
|
|
244
261
|
}
|
|
245
262
|
|
|
246
263
|
const REGISTER = new WeakMap();
|
|
247
|
-
const
|
|
248
|
-
function getUpdate(subject) {
|
|
249
|
-
return UPDATE$1.get(subject);
|
|
250
|
-
}
|
|
251
|
-
function setUpdate(subject, keys) {
|
|
252
|
-
UPDATE$1.set(subject, Array.from(keys));
|
|
253
|
-
setTimeout(() => UPDATE$1.delete(subject), 0);
|
|
254
|
-
}
|
|
255
|
-
/** Ensure a local update is dropped after use. */
|
|
256
|
-
function hasUpdate(proxy) {
|
|
257
|
-
if (UPDATE$1.has(proxy))
|
|
258
|
-
setTimeout(() => UPDATE$1.delete(proxy), 0);
|
|
259
|
-
}
|
|
260
|
-
/** Set local update for a subscribed context. */
|
|
261
|
-
function addUpdate(proxy, using) {
|
|
262
|
-
const parent = UPDATE$1.get(getPrototypeOf(proxy));
|
|
263
|
-
UPDATE$1.set(proxy, parent.filter(k => using.has(k)));
|
|
264
|
-
}
|
|
264
|
+
const PENDING = new Map();
|
|
265
265
|
class Control {
|
|
266
266
|
constructor(subject) {
|
|
267
267
|
this.subject = subject;
|
|
@@ -328,7 +328,8 @@ class Control {
|
|
|
328
328
|
else if (!frame.size)
|
|
329
329
|
setTimeout(() => {
|
|
330
330
|
flush(this);
|
|
331
|
-
|
|
331
|
+
this.latest = Array.from(frame);
|
|
332
|
+
setTimeout(() => this.latest = undefined, 0);
|
|
332
333
|
frame.clear();
|
|
333
334
|
for (const notify of waiting)
|
|
334
335
|
try {
|
|
@@ -378,13 +379,16 @@ class Control {
|
|
|
378
379
|
}
|
|
379
380
|
}
|
|
380
381
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
382
|
+
/**
|
|
383
|
+
* Run instruction as controller sets itself up.
|
|
384
|
+
* This will specialize the behavior of a given property.
|
|
385
|
+
*/
|
|
386
|
+
function add(instruction, label) {
|
|
387
|
+
const name = label || instruction.name || "pending";
|
|
384
388
|
const placeholder = Symbol(`${name} instruction`);
|
|
385
389
|
function setup(key) {
|
|
386
390
|
const { subject, state } = this;
|
|
387
|
-
let output =
|
|
391
|
+
let output = instruction.call(this, key, this);
|
|
388
392
|
if (typeof output == "function")
|
|
389
393
|
output = { get: output };
|
|
390
394
|
if (typeof output != "object")
|
|
@@ -485,7 +489,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
|
|
|
485
489
|
});
|
|
486
490
|
}
|
|
487
491
|
|
|
488
|
-
const Oops$
|
|
492
|
+
const Oops$a = issues({
|
|
489
493
|
DuplicatePending: (key) => `Invoked action ${key} but one is already active.`
|
|
490
494
|
});
|
|
491
495
|
function act(task) {
|
|
@@ -493,7 +497,7 @@ function act(task) {
|
|
|
493
497
|
let pending = false;
|
|
494
498
|
const invoke = (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
495
499
|
if (pending)
|
|
496
|
-
return Promise.reject(Oops$
|
|
500
|
+
return Promise.reject(Oops$a.DuplicatePending(key));
|
|
497
501
|
pending = true;
|
|
498
502
|
this.update(key);
|
|
499
503
|
try {
|
|
@@ -515,6 +519,9 @@ function act(task) {
|
|
|
515
519
|
});
|
|
516
520
|
}
|
|
517
521
|
|
|
522
|
+
const Oops$9 = issues({
|
|
523
|
+
NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
|
|
524
|
+
});
|
|
518
525
|
function factoryMode(self, output, key, required) {
|
|
519
526
|
const { subject, state } = self;
|
|
520
527
|
let pending;
|
|
@@ -536,7 +543,7 @@ function factoryMode(self, output, key, required) {
|
|
|
536
543
|
const suspend = () => {
|
|
537
544
|
if (required === false)
|
|
538
545
|
return undefined;
|
|
539
|
-
const issue = Oops$
|
|
546
|
+
const issue = Oops$9.NotReady(subject, key);
|
|
540
547
|
assign(pending, {
|
|
541
548
|
message: issue.message,
|
|
542
549
|
stack: issue.stack
|
|
@@ -554,12 +561,8 @@ function factoryMode(self, output, key, required) {
|
|
|
554
561
|
}
|
|
555
562
|
|
|
556
563
|
const Oops$8 = issues({
|
|
557
|
-
// BadSource: (model, property, got) =>
|
|
558
|
-
// `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
|
|
559
564
|
PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`,
|
|
560
565
|
ComputeFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`,
|
|
561
|
-
NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
|
|
562
|
-
FactoryFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`,
|
|
563
566
|
Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`
|
|
564
567
|
});
|
|
565
568
|
function get(arg0, arg1, arg2) {
|
|
@@ -937,7 +940,7 @@ class Model {
|
|
|
937
940
|
}
|
|
938
941
|
else if (keys.includes(key)) {
|
|
939
942
|
removeListener();
|
|
940
|
-
return () => resolve(single ? control.state.get(key) :
|
|
943
|
+
return () => resolve(single ? control.state.get(key) : control.latest);
|
|
941
944
|
}
|
|
942
945
|
});
|
|
943
946
|
});
|
|
@@ -957,7 +960,7 @@ class Model {
|
|
|
957
960
|
catch (e) { }
|
|
958
961
|
const callback = single
|
|
959
962
|
? () => { argument.call(this, control.state.get(select), select); }
|
|
960
|
-
: () => { argument.call(this,
|
|
963
|
+
: () => { argument.call(this, control.latest); };
|
|
961
964
|
const onEvent = once
|
|
962
965
|
? () => {
|
|
963
966
|
removeListener();
|
|
@@ -1042,7 +1045,7 @@ class Model {
|
|
|
1042
1045
|
throw Oops$5.NoChaining();
|
|
1043
1046
|
if (frame.size || arg1 !== false)
|
|
1044
1047
|
waiting.add(() => {
|
|
1045
|
-
callback(
|
|
1048
|
+
callback(control.latest);
|
|
1046
1049
|
});
|
|
1047
1050
|
else
|
|
1048
1051
|
callback(false);
|
|
@@ -1235,14 +1238,8 @@ function useModel(source, arg, arg2) {
|
|
|
1235
1238
|
return instance;
|
|
1236
1239
|
}
|
|
1237
1240
|
const local = use(refresh => (Control.for(instance).subscribe(() => refresh)));
|
|
1238
|
-
if (typeof arg == "object")
|
|
1239
|
-
local.
|
|
1240
|
-
if (typeof arg2 !== "object")
|
|
1241
|
-
arg2 = getOwnPropertyNames(instance);
|
|
1242
|
-
for (const key of arg2)
|
|
1243
|
-
if (key in arg)
|
|
1244
|
-
instance[key] = arg[key];
|
|
1245
|
-
}
|
|
1241
|
+
if (typeof arg == "object")
|
|
1242
|
+
local.assign(arg, arg2);
|
|
1246
1243
|
React__default["default"].useLayoutEffect(() => {
|
|
1247
1244
|
local.commit();
|
|
1248
1245
|
return () => {
|
|
@@ -1299,10 +1296,10 @@ function useTap(source, arg1, arg2) {
|
|
|
1299
1296
|
const instance = React.useMemo(() => {
|
|
1300
1297
|
if (typeof source == "object")
|
|
1301
1298
|
return () => source;
|
|
1302
|
-
if (MVC.isTypeof(source))
|
|
1303
|
-
return () => source.get(true);
|
|
1304
1299
|
if (Model.isTypeof(source))
|
|
1305
|
-
return
|
|
1300
|
+
return "get" in source
|
|
1301
|
+
? () => source.get(true)
|
|
1302
|
+
: () => useContext(source);
|
|
1306
1303
|
return source;
|
|
1307
1304
|
}, [])();
|
|
1308
1305
|
if (typeof arg1 == "function")
|
|
@@ -1332,10 +1329,10 @@ class MVC extends Model {
|
|
|
1332
1329
|
}
|
|
1333
1330
|
}
|
|
1334
1331
|
|
|
1335
|
-
const LOCAL =
|
|
1336
|
-
const STATE =
|
|
1337
|
-
const UPDATE =
|
|
1338
|
-
const CONTROL =
|
|
1332
|
+
const LOCAL = "$debug local";
|
|
1333
|
+
const STATE = "$debug state";
|
|
1334
|
+
const UPDATE = "$debug update";
|
|
1335
|
+
const CONTROL = "$debug controller";
|
|
1339
1336
|
Object.defineProperties(Model.prototype, {
|
|
1340
1337
|
[CONTROL]: {
|
|
1341
1338
|
get() {
|
|
@@ -1357,7 +1354,8 @@ Object.defineProperties(Model.prototype, {
|
|
|
1357
1354
|
},
|
|
1358
1355
|
[UPDATE]: {
|
|
1359
1356
|
get() {
|
|
1360
|
-
|
|
1357
|
+
const source = Subscriber.get(this) || Control.get(this);
|
|
1358
|
+
return source && source.latest;
|
|
1361
1359
|
}
|
|
1362
1360
|
}
|
|
1363
1361
|
});
|