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