@expressive/mvc 1.0.0-beta.40 → 1.0.0-beta.43
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 +18 -2
- package/lib/index.esm.js +313 -294
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +313 -294
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
package/lib/index.esm.js
CHANGED
|
@@ -17,7 +17,7 @@ function issues(register) {
|
|
|
17
17
|
return Library;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
const Oops$
|
|
20
|
+
const Oops$c = issues({
|
|
21
21
|
BadEffectCallback: () => `Callback for effect-callback may only return a function.`
|
|
22
22
|
});
|
|
23
23
|
const { assign, create, defineProperty, entries, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
|
|
@@ -46,7 +46,7 @@ function createEffect(callback) {
|
|
|
46
46
|
if (unSet instanceof Promise)
|
|
47
47
|
unSet = undefined;
|
|
48
48
|
if (unSet && typeof unSet !== "function")
|
|
49
|
-
throw Oops$
|
|
49
|
+
throw Oops$c.BadEffectCallback();
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
52
|
function createValueEffect(callback) {
|
|
@@ -62,8 +62,28 @@ function createValueEffect(callback) {
|
|
|
62
62
|
if (typeof out == "function")
|
|
63
63
|
unSet = out;
|
|
64
64
|
else
|
|
65
|
-
throw Oops$
|
|
65
|
+
throw Oops$c.BadEffectCallback();
|
|
66
66
|
};
|
|
67
|
+
}
|
|
68
|
+
function suspenseBoundary(fn) {
|
|
69
|
+
const retry = (err) => {
|
|
70
|
+
if (err instanceof Promise)
|
|
71
|
+
return err.then(compute);
|
|
72
|
+
else
|
|
73
|
+
throw err;
|
|
74
|
+
};
|
|
75
|
+
const compute = () => {
|
|
76
|
+
try {
|
|
77
|
+
const output = fn();
|
|
78
|
+
return output instanceof Promise
|
|
79
|
+
? output.catch(retry)
|
|
80
|
+
: output;
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
return retry(err);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
return compute();
|
|
67
87
|
}
|
|
68
88
|
|
|
69
89
|
const UPDATE = new WeakMap();
|
|
@@ -76,7 +96,274 @@ function applyUpdate(subject, keys) {
|
|
|
76
96
|
};
|
|
77
97
|
}
|
|
78
98
|
|
|
79
|
-
|
|
99
|
+
class Subscriber {
|
|
100
|
+
constructor(target, onUpdate) {
|
|
101
|
+
this.onUpdate = onUpdate;
|
|
102
|
+
this.active = false;
|
|
103
|
+
this.dependant = new Set();
|
|
104
|
+
this.watch = {};
|
|
105
|
+
const parent = target instanceof Controller
|
|
106
|
+
? target : control(target);
|
|
107
|
+
const proxy = create(parent.proxy);
|
|
108
|
+
let reset;
|
|
109
|
+
define(proxy, LOCAL, this);
|
|
110
|
+
defineProperty(this, "proxy", {
|
|
111
|
+
configurable: true,
|
|
112
|
+
get() {
|
|
113
|
+
if (reset)
|
|
114
|
+
setTimeout(reset, 0);
|
|
115
|
+
return proxy;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
const release = parent.addListener(key => {
|
|
119
|
+
const handler = this.watch[key];
|
|
120
|
+
if (!handler || !this.active)
|
|
121
|
+
return;
|
|
122
|
+
if (typeof handler == "function")
|
|
123
|
+
handler();
|
|
124
|
+
const notify = this.onUpdate(key, parent);
|
|
125
|
+
const getWhy = (keys) => {
|
|
126
|
+
reset = applyUpdate(proxy, keys.filter(k => k in this.watch));
|
|
127
|
+
};
|
|
128
|
+
if (notify) {
|
|
129
|
+
parent.requestUpdate(getWhy);
|
|
130
|
+
parent.requestUpdate(notify);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
this.commit = () => {
|
|
134
|
+
this.active = true;
|
|
135
|
+
this.dependant.forEach(x => x.commit());
|
|
136
|
+
return this.release;
|
|
137
|
+
};
|
|
138
|
+
this.release = () => {
|
|
139
|
+
this.dependant.forEach(x => x.release());
|
|
140
|
+
release();
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const Oops$b = issues({
|
|
146
|
+
ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
|
|
147
|
+
FactoryFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`
|
|
148
|
+
});
|
|
149
|
+
/**
|
|
150
|
+
* Get value, suspend instead if undefined.
|
|
151
|
+
*
|
|
152
|
+
* Throws suspense promise also interpretable as an error.
|
|
153
|
+
* React could handle it but other contexts probably not.
|
|
154
|
+
*/
|
|
155
|
+
function pendingValue(via, key) {
|
|
156
|
+
const value = via.state[key];
|
|
157
|
+
if (value === undefined)
|
|
158
|
+
throw suspend(via, key);
|
|
159
|
+
return value;
|
|
160
|
+
}
|
|
161
|
+
function pendingFactory(parent, key, fn, immediate) {
|
|
162
|
+
const { subject, state } = parent;
|
|
163
|
+
let pending;
|
|
164
|
+
let error;
|
|
165
|
+
const init = () => {
|
|
166
|
+
const output = suspenseBoundary(() => {
|
|
167
|
+
return fn.call(subject, key, subject);
|
|
168
|
+
});
|
|
169
|
+
if (output instanceof Promise) {
|
|
170
|
+
pending = output
|
|
171
|
+
.catch(err => error = err)
|
|
172
|
+
.then(val => state[key] = val)
|
|
173
|
+
.finally(() => {
|
|
174
|
+
pending = undefined;
|
|
175
|
+
parent.update(key);
|
|
176
|
+
});
|
|
177
|
+
const issue = Oops$b.ValueNotReady(subject, key);
|
|
178
|
+
Object.assign(pending, {
|
|
179
|
+
message: issue.message,
|
|
180
|
+
stack: issue.stack
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
return state[key] = output;
|
|
184
|
+
};
|
|
185
|
+
if (immediate)
|
|
186
|
+
try {
|
|
187
|
+
init();
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
Oops$b.FactoryFailed(subject, key).warn();
|
|
191
|
+
throw err;
|
|
192
|
+
}
|
|
193
|
+
return () => {
|
|
194
|
+
if (pending)
|
|
195
|
+
throw pending;
|
|
196
|
+
if (error)
|
|
197
|
+
throw error;
|
|
198
|
+
if (key in state)
|
|
199
|
+
return state[key];
|
|
200
|
+
let output = init();
|
|
201
|
+
if (pending)
|
|
202
|
+
throw pending;
|
|
203
|
+
else
|
|
204
|
+
return output;
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function suspend(source, key) {
|
|
208
|
+
const error = Oops$b.ValueNotReady(source.subject, key);
|
|
209
|
+
const promise = new Promise(resolve => {
|
|
210
|
+
const release = source.addListener(forKey => {
|
|
211
|
+
if (forKey == key)
|
|
212
|
+
return () => {
|
|
213
|
+
if (source.state[key] !== undefined) {
|
|
214
|
+
release();
|
|
215
|
+
resolve();
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
return Object.assign(promise, {
|
|
221
|
+
toString: () => String(error),
|
|
222
|
+
name: "Suspense",
|
|
223
|
+
message: error.message,
|
|
224
|
+
stack: error.stack
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const Oops$a = issues({
|
|
229
|
+
BadComputedSource: (model, property, got) => `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
|
|
230
|
+
PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`,
|
|
231
|
+
ComputeFailed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
|
|
232
|
+
ComputedEarly: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
|
|
233
|
+
});
|
|
234
|
+
const INIT = new WeakSet();
|
|
235
|
+
const INFO = new WeakMap();
|
|
236
|
+
const USED = new WeakMap();
|
|
237
|
+
const KEYS = new WeakMap();
|
|
238
|
+
function from(source, arg1, arg2) {
|
|
239
|
+
return apply(function from(key) {
|
|
240
|
+
const parent = this;
|
|
241
|
+
const { subject, state } = this;
|
|
242
|
+
let getSource;
|
|
243
|
+
let getter;
|
|
244
|
+
if (arg2 === true || arg1 === true)
|
|
245
|
+
getter = pendingValue;
|
|
246
|
+
if (typeof arg1 == "boolean")
|
|
247
|
+
arg1 = undefined;
|
|
248
|
+
// Easy mistake, using a peer, will always be unresolved.
|
|
249
|
+
if (typeof source == "symbol")
|
|
250
|
+
throw Oops$a.PeerNotAllowed(subject, key);
|
|
251
|
+
// replace source controller in-case it is different
|
|
252
|
+
if (typeof source == "object")
|
|
253
|
+
getSource = () => control(source);
|
|
254
|
+
// specifically an arrow function (getter factory)
|
|
255
|
+
else if (!source.prototype) {
|
|
256
|
+
arg1 = source.call(subject, key);
|
|
257
|
+
getSource = () => this;
|
|
258
|
+
}
|
|
259
|
+
// Regular function is too ambiguous so not allowed.
|
|
260
|
+
else
|
|
261
|
+
throw Oops$a.BadComputedSource(subject, key, source);
|
|
262
|
+
let sub;
|
|
263
|
+
const setter = arg1;
|
|
264
|
+
const info = { key, parent, priority: 1 };
|
|
265
|
+
let register = USED.get(parent);
|
|
266
|
+
if (!register) {
|
|
267
|
+
register = new Map();
|
|
268
|
+
USED.set(parent, register);
|
|
269
|
+
}
|
|
270
|
+
register.set(key, info);
|
|
271
|
+
function compute(initial) {
|
|
272
|
+
try {
|
|
273
|
+
return setter.call(sub.proxy, sub.proxy);
|
|
274
|
+
}
|
|
275
|
+
catch (err) {
|
|
276
|
+
Oops$a.ComputeFailed(subject, key, !!initial).warn();
|
|
277
|
+
throw err;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function update() {
|
|
281
|
+
let value;
|
|
282
|
+
try {
|
|
283
|
+
value = compute(false);
|
|
284
|
+
}
|
|
285
|
+
catch (e) {
|
|
286
|
+
console.error(e);
|
|
287
|
+
}
|
|
288
|
+
finally {
|
|
289
|
+
if (state[key] !== value) {
|
|
290
|
+
parent.update(key, value);
|
|
291
|
+
return value;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function defer(_key, from) {
|
|
296
|
+
let pending = KEYS.get(from);
|
|
297
|
+
if (!pending)
|
|
298
|
+
KEYS.set(from, pending = []);
|
|
299
|
+
if (info.parent !== from)
|
|
300
|
+
update();
|
|
301
|
+
else {
|
|
302
|
+
const after = pending.findIndex(peer => (info.priority > INFO.get(peer).priority));
|
|
303
|
+
pending.splice(after + 1, 0, update);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function create(early) {
|
|
307
|
+
sub = new Subscriber(getSource(), defer);
|
|
308
|
+
defineProperty(state, key, {
|
|
309
|
+
value: undefined,
|
|
310
|
+
writable: true
|
|
311
|
+
});
|
|
312
|
+
try {
|
|
313
|
+
return state[key] = compute(true);
|
|
314
|
+
}
|
|
315
|
+
catch (e) {
|
|
316
|
+
if (early)
|
|
317
|
+
Oops$a.ComputedEarly(key).warn();
|
|
318
|
+
throw e;
|
|
319
|
+
}
|
|
320
|
+
finally {
|
|
321
|
+
sub.commit();
|
|
322
|
+
for (const key in sub.watch) {
|
|
323
|
+
const peer = register.get(key);
|
|
324
|
+
if (peer && peer.priority >= info.priority)
|
|
325
|
+
info.priority = peer.priority + 1;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
setAlias(update, `try ${key}`);
|
|
330
|
+
setAlias(create, `new ${key}`);
|
|
331
|
+
setAlias(setter, `run ${key}`);
|
|
332
|
+
INIT.add(create);
|
|
333
|
+
INFO.set(update, info);
|
|
334
|
+
defineProperty(state, key, {
|
|
335
|
+
get: create,
|
|
336
|
+
configurable: true,
|
|
337
|
+
enumerable: true
|
|
338
|
+
});
|
|
339
|
+
if (getter)
|
|
340
|
+
return () => getter(parent, key);
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
function ensure(on, keys) {
|
|
344
|
+
for (const key of keys) {
|
|
345
|
+
const desc = getOwnPropertyDescriptor(on.state, key);
|
|
346
|
+
const getter = desc && desc.get;
|
|
347
|
+
if (INIT.has(getter))
|
|
348
|
+
getter(true);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
function flush(on) {
|
|
352
|
+
const handled = on.frame;
|
|
353
|
+
const pending = KEYS.get(on);
|
|
354
|
+
if (!pending)
|
|
355
|
+
return;
|
|
356
|
+
while (pending.length) {
|
|
357
|
+
const why = [...handled];
|
|
358
|
+
const compute = pending.shift();
|
|
359
|
+
const { key } = INFO.get(compute);
|
|
360
|
+
if (!handled.has(key))
|
|
361
|
+
compute(why);
|
|
362
|
+
}
|
|
363
|
+
KEYS.delete(on);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const Oops$9 = issues({
|
|
80
367
|
StrictUpdate: (expected) => `Strict update() did ${expected ? "not " : ""}find pending updates.`,
|
|
81
368
|
NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
|
|
82
369
|
});
|
|
@@ -144,9 +431,7 @@ class Controller {
|
|
|
144
431
|
: () => state[key]
|
|
145
432
|
});
|
|
146
433
|
defineProperty(proxy, key, {
|
|
147
|
-
enumerable,
|
|
148
|
-
set,
|
|
149
|
-
get
|
|
434
|
+
enumerable, get, set
|
|
150
435
|
});
|
|
151
436
|
}
|
|
152
437
|
ref(key, handler) {
|
|
@@ -207,7 +492,7 @@ class Controller {
|
|
|
207
492
|
return;
|
|
208
493
|
}
|
|
209
494
|
if (typeof arg == "boolean" && arg !== this.frame.size > 0)
|
|
210
|
-
return Promise.reject(Oops$
|
|
495
|
+
return Promise.reject(Oops$9.StrictUpdate(arg));
|
|
211
496
|
return {
|
|
212
497
|
then: (callback) => {
|
|
213
498
|
if (callback)
|
|
@@ -216,7 +501,7 @@ class Controller {
|
|
|
216
501
|
else
|
|
217
502
|
callback(false);
|
|
218
503
|
else
|
|
219
|
-
throw Oops$
|
|
504
|
+
throw Oops$9.NoChaining();
|
|
220
505
|
}
|
|
221
506
|
};
|
|
222
507
|
}
|
|
@@ -245,164 +530,6 @@ function control(subject, cb) {
|
|
|
245
530
|
return cb(control);
|
|
246
531
|
}
|
|
247
532
|
|
|
248
|
-
class Subscriber {
|
|
249
|
-
constructor(target, onUpdate) {
|
|
250
|
-
this.onUpdate = onUpdate;
|
|
251
|
-
this.active = false;
|
|
252
|
-
this.dependant = new Set();
|
|
253
|
-
this.watch = {};
|
|
254
|
-
const parent = target instanceof Controller
|
|
255
|
-
? target : control(target);
|
|
256
|
-
const proxy = create(parent.proxy);
|
|
257
|
-
let reset;
|
|
258
|
-
define(proxy, LOCAL, this);
|
|
259
|
-
defineProperty(this, "proxy", {
|
|
260
|
-
configurable: true,
|
|
261
|
-
get() {
|
|
262
|
-
if (reset)
|
|
263
|
-
setTimeout(reset, 0);
|
|
264
|
-
return proxy;
|
|
265
|
-
}
|
|
266
|
-
});
|
|
267
|
-
const release = parent.addListener(key => {
|
|
268
|
-
const handler = this.watch[key];
|
|
269
|
-
if (!handler || !this.active)
|
|
270
|
-
return;
|
|
271
|
-
if (typeof handler == "function")
|
|
272
|
-
handler();
|
|
273
|
-
const notify = this.onUpdate(key, parent);
|
|
274
|
-
const getWhy = (keys) => {
|
|
275
|
-
reset = applyUpdate(proxy, keys.filter(k => k in this.watch));
|
|
276
|
-
};
|
|
277
|
-
if (notify) {
|
|
278
|
-
parent.requestUpdate(getWhy);
|
|
279
|
-
parent.requestUpdate(notify);
|
|
280
|
-
}
|
|
281
|
-
});
|
|
282
|
-
this.commit = () => {
|
|
283
|
-
this.active = true;
|
|
284
|
-
this.dependant.forEach(x => x.commit());
|
|
285
|
-
return this.release;
|
|
286
|
-
};
|
|
287
|
-
this.release = () => {
|
|
288
|
-
this.dependant.forEach(x => x.release());
|
|
289
|
-
release();
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
const Oops$b = issues({
|
|
295
|
-
ComputeFailed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
|
|
296
|
-
ComputedEarly: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
|
|
297
|
-
});
|
|
298
|
-
const INIT = new WeakSet();
|
|
299
|
-
const INFO = new WeakMap();
|
|
300
|
-
const USED = new WeakMap();
|
|
301
|
-
const KEYS = new WeakMap();
|
|
302
|
-
function prepare(parent, key, source, setter, getter) {
|
|
303
|
-
let sub;
|
|
304
|
-
const { state, subject } = parent;
|
|
305
|
-
const info = { key, parent, priority: 1 };
|
|
306
|
-
let register = USED.get(parent);
|
|
307
|
-
if (!register) {
|
|
308
|
-
register = new Map();
|
|
309
|
-
USED.set(parent, register);
|
|
310
|
-
}
|
|
311
|
-
register.set(key, info);
|
|
312
|
-
function compute(initial) {
|
|
313
|
-
try {
|
|
314
|
-
return setter.call(sub.proxy, sub.proxy);
|
|
315
|
-
}
|
|
316
|
-
catch (err) {
|
|
317
|
-
Oops$b.ComputeFailed(subject, key, !!initial).warn();
|
|
318
|
-
throw err;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
function update() {
|
|
322
|
-
let value;
|
|
323
|
-
try {
|
|
324
|
-
value = compute(false);
|
|
325
|
-
}
|
|
326
|
-
catch (e) {
|
|
327
|
-
console.error(e);
|
|
328
|
-
}
|
|
329
|
-
finally {
|
|
330
|
-
if (state[key] !== value) {
|
|
331
|
-
parent.update(key, value);
|
|
332
|
-
return value;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
function defer(_key, from) {
|
|
337
|
-
let pending = KEYS.get(from);
|
|
338
|
-
if (!pending)
|
|
339
|
-
KEYS.set(from, pending = []);
|
|
340
|
-
if (info.parent !== from)
|
|
341
|
-
update();
|
|
342
|
-
else {
|
|
343
|
-
const after = pending.findIndex(peer => (info.priority > INFO.get(peer).priority));
|
|
344
|
-
pending.splice(after + 1, 0, update);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
function create(early) {
|
|
348
|
-
sub = new Subscriber(source(), defer);
|
|
349
|
-
defineProperty(state, key, {
|
|
350
|
-
value: undefined,
|
|
351
|
-
writable: true
|
|
352
|
-
});
|
|
353
|
-
try {
|
|
354
|
-
return state[key] = compute(true);
|
|
355
|
-
}
|
|
356
|
-
catch (e) {
|
|
357
|
-
if (early)
|
|
358
|
-
Oops$b.ComputedEarly(key).warn();
|
|
359
|
-
throw e;
|
|
360
|
-
}
|
|
361
|
-
finally {
|
|
362
|
-
sub.commit();
|
|
363
|
-
for (const key in sub.watch) {
|
|
364
|
-
const peer = register.get(key);
|
|
365
|
-
if (peer && peer.priority >= info.priority)
|
|
366
|
-
info.priority = peer.priority + 1;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
setAlias(update, `try ${key}`);
|
|
371
|
-
setAlias(create, `new ${key}`);
|
|
372
|
-
setAlias(setter, `run ${key}`);
|
|
373
|
-
INIT.add(create);
|
|
374
|
-
INFO.set(update, info);
|
|
375
|
-
defineProperty(state, key, {
|
|
376
|
-
get: create,
|
|
377
|
-
configurable: true,
|
|
378
|
-
enumerable: true
|
|
379
|
-
});
|
|
380
|
-
if (getter)
|
|
381
|
-
return () => getter(parent, key);
|
|
382
|
-
}
|
|
383
|
-
function ensure(on, keys) {
|
|
384
|
-
for (const key of keys) {
|
|
385
|
-
const desc = getOwnPropertyDescriptor(on.state, key);
|
|
386
|
-
const getter = desc && desc.get;
|
|
387
|
-
if (INIT.has(getter))
|
|
388
|
-
getter(true);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
function flush(on) {
|
|
392
|
-
const handled = on.frame;
|
|
393
|
-
const pending = KEYS.get(on);
|
|
394
|
-
if (!pending)
|
|
395
|
-
return;
|
|
396
|
-
while (pending.length) {
|
|
397
|
-
const why = [...handled];
|
|
398
|
-
const compute = pending.shift();
|
|
399
|
-
const { key } = INFO.get(compute);
|
|
400
|
-
if (!handled.has(key))
|
|
401
|
-
compute(why);
|
|
402
|
-
}
|
|
403
|
-
KEYS.delete(on);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
533
|
const CONTROL = Symbol("CONTROL");
|
|
407
534
|
const WHY = Symbol("UPDATE");
|
|
408
535
|
const LOCAL = Symbol("LOCAL");
|
|
@@ -599,7 +726,7 @@ function recursive(source, key) {
|
|
|
599
726
|
};
|
|
600
727
|
}
|
|
601
728
|
|
|
602
|
-
const Oops$
|
|
729
|
+
const Oops$8 = issues({
|
|
603
730
|
DuplicateAction: (key) => `Invoked action ${key} but one is already active.`
|
|
604
731
|
});
|
|
605
732
|
function act(task) {
|
|
@@ -607,7 +734,7 @@ function act(task) {
|
|
|
607
734
|
let pending = false;
|
|
608
735
|
const invoke = (...args) => {
|
|
609
736
|
if (pending)
|
|
610
|
-
return Promise.reject(Oops$
|
|
737
|
+
return Promise.reject(Oops$8.DuplicateAction(key));
|
|
611
738
|
pending = true;
|
|
612
739
|
this.update(key);
|
|
613
740
|
return new Promise(res => {
|
|
@@ -629,107 +756,6 @@ function act(task) {
|
|
|
629
756
|
});
|
|
630
757
|
}
|
|
631
758
|
|
|
632
|
-
const Oops$9 = issues({
|
|
633
|
-
ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
|
|
634
|
-
});
|
|
635
|
-
/**
|
|
636
|
-
* Get value, suspend instead if undefined.
|
|
637
|
-
*
|
|
638
|
-
* Throws suspense promise also interpretable as an error.
|
|
639
|
-
* React could handle it but other contexts probably not.
|
|
640
|
-
*/
|
|
641
|
-
function pendingValue(via, key) {
|
|
642
|
-
const value = via.state[key];
|
|
643
|
-
if (value === undefined)
|
|
644
|
-
throw suspend(via, key);
|
|
645
|
-
return value;
|
|
646
|
-
}
|
|
647
|
-
function pendingFactory(via, key, fn) {
|
|
648
|
-
const { subject, state } = via;
|
|
649
|
-
let waiting;
|
|
650
|
-
let error;
|
|
651
|
-
return () => {
|
|
652
|
-
if (waiting)
|
|
653
|
-
throw waiting;
|
|
654
|
-
if (error)
|
|
655
|
-
throw error;
|
|
656
|
-
if (key in state)
|
|
657
|
-
return state[key];
|
|
658
|
-
let output;
|
|
659
|
-
try {
|
|
660
|
-
output = fn.call(subject, key, subject);
|
|
661
|
-
}
|
|
662
|
-
catch (err) {
|
|
663
|
-
throw error = err;
|
|
664
|
-
}
|
|
665
|
-
if (output instanceof Promise) {
|
|
666
|
-
const issue = Oops$9.ValueNotReady(subject, key);
|
|
667
|
-
output
|
|
668
|
-
.catch(err => error = err)
|
|
669
|
-
.then(out => state[key] = out)
|
|
670
|
-
.finally(() => waiting = undefined);
|
|
671
|
-
throw waiting = Object.assign(output, {
|
|
672
|
-
message: issue.message,
|
|
673
|
-
stack: issue.stack
|
|
674
|
-
});
|
|
675
|
-
}
|
|
676
|
-
else {
|
|
677
|
-
return state[key] = output;
|
|
678
|
-
}
|
|
679
|
-
};
|
|
680
|
-
}
|
|
681
|
-
function suspend(source, key) {
|
|
682
|
-
const error = Oops$9.ValueNotReady(source.subject, key);
|
|
683
|
-
const promise = new Promise(resolve => {
|
|
684
|
-
const release = source.addListener(forKey => {
|
|
685
|
-
if (forKey == key)
|
|
686
|
-
return () => {
|
|
687
|
-
if (source.state[key] !== undefined) {
|
|
688
|
-
release();
|
|
689
|
-
resolve();
|
|
690
|
-
}
|
|
691
|
-
};
|
|
692
|
-
});
|
|
693
|
-
});
|
|
694
|
-
return Object.assign(promise, {
|
|
695
|
-
toString: () => String(error),
|
|
696
|
-
name: "Suspense",
|
|
697
|
-
message: error.message,
|
|
698
|
-
stack: error.stack
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
const Oops$8 = issues({
|
|
703
|
-
BadComputedSource: (model, property, got) => `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
|
|
704
|
-
PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`
|
|
705
|
-
});
|
|
706
|
-
function from(source, setter, arg) {
|
|
707
|
-
return apply(function from(key) {
|
|
708
|
-
const { subject } = this;
|
|
709
|
-
let getSource;
|
|
710
|
-
let getter;
|
|
711
|
-
if (arg === true || setter === true)
|
|
712
|
-
getter = pendingValue;
|
|
713
|
-
if (typeof setter == "boolean")
|
|
714
|
-
setter = undefined;
|
|
715
|
-
// Easy mistake, using a peer, will always be unresolved.
|
|
716
|
-
if (typeof source == "symbol")
|
|
717
|
-
throw Oops$8.PeerNotAllowed(subject, key);
|
|
718
|
-
// replace source controller in-case it is different
|
|
719
|
-
if (typeof source == "object")
|
|
720
|
-
getSource = () => control(source);
|
|
721
|
-
// specifically an arrow function (getter factory)
|
|
722
|
-
else if (!source.prototype) {
|
|
723
|
-
setter = source.call(subject, key);
|
|
724
|
-
getSource = () => this;
|
|
725
|
-
}
|
|
726
|
-
// Regular function is too ambiguous so not allowed.
|
|
727
|
-
else
|
|
728
|
-
throw Oops$8.BadComputedSource(subject, key, source);
|
|
729
|
-
return prepare(this, key, getSource, setter, getter);
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
|
|
733
759
|
function ref(arg) {
|
|
734
760
|
return apply(function ref(key) {
|
|
735
761
|
let value = {};
|
|
@@ -754,8 +780,7 @@ function createRef(key, cb) {
|
|
|
754
780
|
|
|
755
781
|
const Oops$7 = issues({
|
|
756
782
|
NonOptional: (Parent, key) => `Property ${Parent}.${key} is marked as required.`,
|
|
757
|
-
BadFactory: () => `Set instruction can only accept a factory or undefined
|
|
758
|
-
FactoryFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`
|
|
783
|
+
BadFactory: () => `Set instruction can only accept a factory or undefined.`
|
|
759
784
|
});
|
|
760
785
|
function set(factory, argument) {
|
|
761
786
|
return apply(function set(key) {
|
|
@@ -767,19 +792,7 @@ function set(factory, argument) {
|
|
|
767
792
|
else if (typeof factory !== "function")
|
|
768
793
|
throw Oops$7.BadFactory();
|
|
769
794
|
else {
|
|
770
|
-
get = pendingFactory(this, key, factory);
|
|
771
|
-
if (required)
|
|
772
|
-
try {
|
|
773
|
-
get();
|
|
774
|
-
}
|
|
775
|
-
catch (err) {
|
|
776
|
-
if (err instanceof Promise)
|
|
777
|
-
;
|
|
778
|
-
else {
|
|
779
|
-
Oops$7.FactoryFailed(this.subject, key).warn();
|
|
780
|
-
throw err;
|
|
781
|
-
}
|
|
782
|
-
}
|
|
795
|
+
get = pendingFactory(this, key, factory, required);
|
|
783
796
|
}
|
|
784
797
|
if (typeof argument == "function")
|
|
785
798
|
set = createValueEffect(argument);
|
|
@@ -1120,15 +1133,21 @@ class MVC extends Model {
|
|
|
1120
1133
|
static tap(key, expect) {
|
|
1121
1134
|
return useTap(this, key, expect);
|
|
1122
1135
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1136
|
+
static new(arg) {
|
|
1137
|
+
const instance = React.useMemo(() => {
|
|
1138
|
+
const instance = new this();
|
|
1139
|
+
control(instance);
|
|
1140
|
+
if (typeof arg == "function")
|
|
1141
|
+
arg(instance);
|
|
1142
|
+
else if (arg)
|
|
1143
|
+
getOwnPropertyNames(instance).forEach(key => {
|
|
1144
|
+
if (key in arg)
|
|
1145
|
+
instance[key] = arg[key];
|
|
1146
|
+
});
|
|
1147
|
+
return instance;
|
|
1148
|
+
}, []);
|
|
1149
|
+
usePeerContext(instance.get);
|
|
1150
|
+
return instance;
|
|
1132
1151
|
}
|
|
1133
1152
|
static use(a, b) {
|
|
1134
1153
|
const instance = useModel(this, a, b);
|