@expressive/mvc 1.0.0-beta.41 → 1.0.0-beta.44
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 -4
- package/lib/index.esm.js +359 -339
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +359 -339
- 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;
|
|
@@ -34,9 +34,6 @@ function defineLazy(object, property, init) {
|
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
|
-
function setAlias(func, displayName) {
|
|
38
|
-
assign(func, { displayName });
|
|
39
|
-
}
|
|
40
37
|
function createEffect(callback) {
|
|
41
38
|
let unSet;
|
|
42
39
|
return function (value) {
|
|
@@ -46,7 +43,7 @@ function createEffect(callback) {
|
|
|
46
43
|
if (unSet instanceof Promise)
|
|
47
44
|
unSet = undefined;
|
|
48
45
|
if (unSet && typeof unSet !== "function")
|
|
49
|
-
throw Oops$
|
|
46
|
+
throw Oops$c.BadEffectCallback();
|
|
50
47
|
};
|
|
51
48
|
}
|
|
52
49
|
function createValueEffect(callback) {
|
|
@@ -62,8 +59,28 @@ function createValueEffect(callback) {
|
|
|
62
59
|
if (typeof out == "function")
|
|
63
60
|
unSet = out;
|
|
64
61
|
else
|
|
65
|
-
throw Oops$
|
|
62
|
+
throw Oops$c.BadEffectCallback();
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function suspenseBoundary(fn) {
|
|
66
|
+
const retry = (err) => {
|
|
67
|
+
if (err instanceof Promise)
|
|
68
|
+
return err.then(compute);
|
|
69
|
+
else
|
|
70
|
+
throw err;
|
|
66
71
|
};
|
|
72
|
+
const compute = () => {
|
|
73
|
+
try {
|
|
74
|
+
const output = fn();
|
|
75
|
+
return output instanceof Promise
|
|
76
|
+
? output.catch(retry)
|
|
77
|
+
: output;
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
return retry(err);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
return compute();
|
|
67
84
|
}
|
|
68
85
|
|
|
69
86
|
const UPDATE = new WeakMap();
|
|
@@ -76,8 +93,215 @@ function applyUpdate(subject, keys) {
|
|
|
76
93
|
};
|
|
77
94
|
}
|
|
78
95
|
|
|
79
|
-
|
|
80
|
-
|
|
96
|
+
class Subscriber {
|
|
97
|
+
constructor(target, onUpdate) {
|
|
98
|
+
this.onUpdate = onUpdate;
|
|
99
|
+
this.active = false;
|
|
100
|
+
this.dependant = new Set();
|
|
101
|
+
this.watch = {};
|
|
102
|
+
const parent = target instanceof Controller
|
|
103
|
+
? target : control(target);
|
|
104
|
+
const proxy = create(parent.proxy);
|
|
105
|
+
let reset;
|
|
106
|
+
define(proxy, LOCAL, this);
|
|
107
|
+
defineProperty(this, "proxy", {
|
|
108
|
+
configurable: true,
|
|
109
|
+
get() {
|
|
110
|
+
if (reset)
|
|
111
|
+
setTimeout(reset, 0);
|
|
112
|
+
return proxy;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
const release = parent.addListener(key => {
|
|
116
|
+
const handler = this.watch[key];
|
|
117
|
+
if (!handler || !this.active)
|
|
118
|
+
return;
|
|
119
|
+
if (typeof handler == "function")
|
|
120
|
+
handler();
|
|
121
|
+
const notify = this.onUpdate(key, parent);
|
|
122
|
+
const getWhy = (keys) => {
|
|
123
|
+
reset = applyUpdate(proxy, keys.filter(k => k in this.watch));
|
|
124
|
+
};
|
|
125
|
+
if (notify) {
|
|
126
|
+
parent.requestUpdate(getWhy);
|
|
127
|
+
parent.requestUpdate(notify);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
this.commit = () => {
|
|
131
|
+
this.active = true;
|
|
132
|
+
this.dependant.forEach(x => x.commit());
|
|
133
|
+
return this.release;
|
|
134
|
+
};
|
|
135
|
+
this.release = () => {
|
|
136
|
+
this.dependant.forEach(x => x.release());
|
|
137
|
+
release();
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const Oops$b = issues({
|
|
143
|
+
ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
|
|
144
|
+
});
|
|
145
|
+
function suspend(source, key) {
|
|
146
|
+
const error = Oops$b.ValueNotReady(source.subject, key);
|
|
147
|
+
const promise = new Promise(resolve => {
|
|
148
|
+
const release = source.addListener(forKey => {
|
|
149
|
+
if (forKey == key)
|
|
150
|
+
return () => {
|
|
151
|
+
if (source.state[key] !== undefined) {
|
|
152
|
+
release();
|
|
153
|
+
resolve();
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
return Object.assign(promise, {
|
|
159
|
+
toString: () => String(error),
|
|
160
|
+
name: "Suspense",
|
|
161
|
+
message: error.message,
|
|
162
|
+
stack: error.stack
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const Oops$a = issues({
|
|
167
|
+
BadComputedSource: (model, property, got) => `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
|
|
168
|
+
PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`,
|
|
169
|
+
ComputeFailed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
|
|
170
|
+
ComputedEarly: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
|
|
171
|
+
});
|
|
172
|
+
const INIT = new WeakSet();
|
|
173
|
+
const INFO = new WeakMap();
|
|
174
|
+
const USED = new WeakMap();
|
|
175
|
+
const KEYS = new WeakMap();
|
|
176
|
+
function from(source, arg1, arg2) {
|
|
177
|
+
return apply(function from(key) {
|
|
178
|
+
const parent = this;
|
|
179
|
+
const { subject, state } = this;
|
|
180
|
+
let getSource;
|
|
181
|
+
let required = arg2 === true || arg1 === true;
|
|
182
|
+
if (typeof arg1 == "boolean")
|
|
183
|
+
arg1 = undefined;
|
|
184
|
+
// Easy mistake, using a peer, will always be unresolved.
|
|
185
|
+
if (typeof source == "symbol")
|
|
186
|
+
throw Oops$a.PeerNotAllowed(subject, key);
|
|
187
|
+
// replace source controller in-case it is different
|
|
188
|
+
if (typeof source == "object")
|
|
189
|
+
getSource = () => control(source);
|
|
190
|
+
// specifically an arrow function (getter factory)
|
|
191
|
+
else if (!source.prototype) {
|
|
192
|
+
arg1 = source.call(subject, key);
|
|
193
|
+
getSource = () => this;
|
|
194
|
+
}
|
|
195
|
+
// Regular function is too ambiguous so not allowed.
|
|
196
|
+
else
|
|
197
|
+
throw Oops$a.BadComputedSource(subject, key, source);
|
|
198
|
+
let sub;
|
|
199
|
+
const setter = arg1;
|
|
200
|
+
const info = { key, parent, priority: 1 };
|
|
201
|
+
let register = USED.get(parent);
|
|
202
|
+
if (!register) {
|
|
203
|
+
register = new Map();
|
|
204
|
+
USED.set(parent, register);
|
|
205
|
+
}
|
|
206
|
+
register.set(key, info);
|
|
207
|
+
function compute(initial) {
|
|
208
|
+
try {
|
|
209
|
+
return setter.call(sub.proxy, sub.proxy);
|
|
210
|
+
}
|
|
211
|
+
catch (err) {
|
|
212
|
+
Oops$a.ComputeFailed(subject, key, !!initial).warn();
|
|
213
|
+
throw err;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function update() {
|
|
217
|
+
let value;
|
|
218
|
+
try {
|
|
219
|
+
value = compute(false);
|
|
220
|
+
}
|
|
221
|
+
catch (e) {
|
|
222
|
+
console.error(e);
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
if (state[key] !== value) {
|
|
226
|
+
parent.update(key, value);
|
|
227
|
+
return value;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function defer(_key, from) {
|
|
232
|
+
let pending = KEYS.get(from);
|
|
233
|
+
if (!pending)
|
|
234
|
+
KEYS.set(from, pending = []);
|
|
235
|
+
if (info.parent !== from)
|
|
236
|
+
update();
|
|
237
|
+
else {
|
|
238
|
+
const after = pending.findIndex(peer => (info.priority > INFO.get(peer).priority));
|
|
239
|
+
pending.splice(after + 1, 0, update);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function create(early) {
|
|
243
|
+
sub = new Subscriber(getSource(), defer);
|
|
244
|
+
defineProperty(state, key, {
|
|
245
|
+
value: undefined,
|
|
246
|
+
writable: true
|
|
247
|
+
});
|
|
248
|
+
try {
|
|
249
|
+
return state[key] = compute(true);
|
|
250
|
+
}
|
|
251
|
+
catch (e) {
|
|
252
|
+
if (early)
|
|
253
|
+
Oops$a.ComputedEarly(key).warn();
|
|
254
|
+
throw e;
|
|
255
|
+
}
|
|
256
|
+
finally {
|
|
257
|
+
sub.commit();
|
|
258
|
+
for (const key in sub.watch) {
|
|
259
|
+
const peer = register.get(key);
|
|
260
|
+
if (peer && peer.priority >= info.priority)
|
|
261
|
+
info.priority = peer.priority + 1;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
INIT.add(create);
|
|
266
|
+
INFO.set(update, info);
|
|
267
|
+
defineProperty(state, key, {
|
|
268
|
+
get: create,
|
|
269
|
+
configurable: true,
|
|
270
|
+
enumerable: true
|
|
271
|
+
});
|
|
272
|
+
return () => {
|
|
273
|
+
const value = parent.state[key];
|
|
274
|
+
if (value === undefined && required)
|
|
275
|
+
throw suspend(this, key);
|
|
276
|
+
return value;
|
|
277
|
+
};
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
function ensure(on, keys) {
|
|
281
|
+
for (const key of keys) {
|
|
282
|
+
const desc = getOwnPropertyDescriptor(on.state, key);
|
|
283
|
+
const getter = desc && desc.get;
|
|
284
|
+
if (INIT.has(getter))
|
|
285
|
+
getter(true);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function flush(on) {
|
|
289
|
+
const handled = on.frame;
|
|
290
|
+
const pending = KEYS.get(on);
|
|
291
|
+
if (!pending)
|
|
292
|
+
return;
|
|
293
|
+
while (pending.length) {
|
|
294
|
+
const why = [...handled];
|
|
295
|
+
const compute = pending.shift();
|
|
296
|
+
const { key } = INFO.get(compute);
|
|
297
|
+
if (!handled.has(key))
|
|
298
|
+
compute(why);
|
|
299
|
+
}
|
|
300
|
+
KEYS.delete(on);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const Oops$9 = issues({
|
|
304
|
+
StrictUpdate: () => `Strict update() did not find pending updates.`,
|
|
81
305
|
NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
|
|
82
306
|
});
|
|
83
307
|
const READY = new WeakSet();
|
|
@@ -107,7 +331,9 @@ class Controller {
|
|
|
107
331
|
return;
|
|
108
332
|
let onGet;
|
|
109
333
|
let onSet;
|
|
334
|
+
let set;
|
|
110
335
|
let enumerable;
|
|
336
|
+
let suspense;
|
|
111
337
|
const instruction = Pending.get(entry);
|
|
112
338
|
if (instruction) {
|
|
113
339
|
Pending.delete(entry);
|
|
@@ -120,33 +346,35 @@ class Controller {
|
|
|
120
346
|
state[key] = desc.value;
|
|
121
347
|
onSet = desc.set;
|
|
122
348
|
onGet = desc.get;
|
|
349
|
+
suspense = desc.suspense;
|
|
123
350
|
enumerable = desc.enumerable;
|
|
124
351
|
}
|
|
125
352
|
}
|
|
126
353
|
else
|
|
127
354
|
state[key] = entry;
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
355
|
+
if (onSet !== false)
|
|
356
|
+
set = this.ref(key, onSet);
|
|
357
|
+
const get = (local) => {
|
|
358
|
+
if (!(key in state) && suspense)
|
|
359
|
+
throw suspend(this, key);
|
|
133
360
|
const value = state[key];
|
|
134
|
-
if (
|
|
135
|
-
local
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
361
|
+
if (onGet)
|
|
362
|
+
return local
|
|
363
|
+
? onGet(value, local)
|
|
364
|
+
: onGet(value);
|
|
365
|
+
return value;
|
|
366
|
+
};
|
|
139
367
|
defineProperty(subject, key, {
|
|
140
|
-
enumerable,
|
|
141
|
-
set,
|
|
142
|
-
get: onGet
|
|
143
|
-
? () => onGet(state[key])
|
|
144
|
-
: () => state[key]
|
|
368
|
+
enumerable, set, get
|
|
145
369
|
});
|
|
146
370
|
defineProperty(proxy, key, {
|
|
147
|
-
enumerable,
|
|
148
|
-
|
|
149
|
-
|
|
371
|
+
enumerable, set,
|
|
372
|
+
get() {
|
|
373
|
+
const local = this[LOCAL];
|
|
374
|
+
if (local && !local.watch[key])
|
|
375
|
+
local.watch[key] = true;
|
|
376
|
+
return get(local);
|
|
377
|
+
}
|
|
150
378
|
});
|
|
151
379
|
}
|
|
152
380
|
ref(key, handler) {
|
|
@@ -206,17 +434,17 @@ class Controller {
|
|
|
206
434
|
this.waiting.add(arg);
|
|
207
435
|
return;
|
|
208
436
|
}
|
|
209
|
-
if (
|
|
210
|
-
return Promise.reject(Oops$
|
|
437
|
+
if (arg === true && this.frame.size < 1)
|
|
438
|
+
return Promise.reject(Oops$9.StrictUpdate());
|
|
211
439
|
return {
|
|
212
440
|
then: (callback) => {
|
|
213
441
|
if (callback)
|
|
214
|
-
if (this.frame.size)
|
|
442
|
+
if (this.frame.size || arg !== false)
|
|
215
443
|
this.waiting.add(callback);
|
|
216
444
|
else
|
|
217
445
|
callback(false);
|
|
218
446
|
else
|
|
219
|
-
throw Oops$
|
|
447
|
+
throw Oops$9.NoChaining();
|
|
220
448
|
}
|
|
221
449
|
};
|
|
222
450
|
}
|
|
@@ -245,164 +473,6 @@ function control(subject, cb) {
|
|
|
245
473
|
return cb(control);
|
|
246
474
|
}
|
|
247
475
|
|
|
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
476
|
const CONTROL = Symbol("CONTROL");
|
|
407
477
|
const WHY = Symbol("UPDATE");
|
|
408
478
|
const LOCAL = Symbol("LOCAL");
|
|
@@ -552,17 +622,21 @@ function apply(fn, label) {
|
|
|
552
622
|
const placeholder = Symbol(`${name} instruction`);
|
|
553
623
|
function setup(key) {
|
|
554
624
|
let output = fn.call(this, key, this);
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
625
|
+
switch (typeof output) {
|
|
626
|
+
case "function":
|
|
627
|
+
return { get: output };
|
|
628
|
+
case "object": {
|
|
629
|
+
if ("explicit" in output && output.explicit) {
|
|
630
|
+
defineProperty(this.subject, key, output);
|
|
631
|
+
return false;
|
|
632
|
+
}
|
|
633
|
+
if ("recursive" in output && output.recursive)
|
|
634
|
+
return Object.assign(Object.assign({}, output), { get: recursive(this, key) });
|
|
635
|
+
}
|
|
636
|
+
case "boolean":
|
|
637
|
+
return output;
|
|
562
638
|
}
|
|
563
|
-
|
|
564
|
-
output = Object.assign(Object.assign({}, output), { get: recursive(this, key) });
|
|
565
|
-
return output;
|
|
639
|
+
return;
|
|
566
640
|
}
|
|
567
641
|
Pending.set(placeholder, setup);
|
|
568
642
|
return placeholder;
|
|
@@ -599,7 +673,7 @@ function recursive(source, key) {
|
|
|
599
673
|
};
|
|
600
674
|
}
|
|
601
675
|
|
|
602
|
-
const Oops$
|
|
676
|
+
const Oops$8 = issues({
|
|
603
677
|
DuplicateAction: (key) => `Invoked action ${key} but one is already active.`
|
|
604
678
|
});
|
|
605
679
|
function act(task) {
|
|
@@ -607,7 +681,7 @@ function act(task) {
|
|
|
607
681
|
let pending = false;
|
|
608
682
|
const invoke = (...args) => {
|
|
609
683
|
if (pending)
|
|
610
|
-
return Promise.reject(Oops$
|
|
684
|
+
return Promise.reject(Oops$8.DuplicateAction(key));
|
|
611
685
|
pending = true;
|
|
612
686
|
this.update(key);
|
|
613
687
|
return new Promise(res => {
|
|
@@ -618,7 +692,6 @@ function act(task) {
|
|
|
618
692
|
});
|
|
619
693
|
};
|
|
620
694
|
this.state[key] = undefined;
|
|
621
|
-
setAlias(invoke, `run ${key}`);
|
|
622
695
|
defineProperty(invoke, "active", {
|
|
623
696
|
get: () => pending
|
|
624
697
|
});
|
|
@@ -629,107 +702,6 @@ function act(task) {
|
|
|
629
702
|
});
|
|
630
703
|
}
|
|
631
704
|
|
|
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
705
|
function ref(arg) {
|
|
734
706
|
return apply(function ref(key) {
|
|
735
707
|
let value = {};
|
|
@@ -755,32 +727,21 @@ function createRef(key, cb) {
|
|
|
755
727
|
const Oops$7 = issues({
|
|
756
728
|
NonOptional: (Parent, key) => `Property ${Parent}.${key} is marked as required.`,
|
|
757
729
|
BadFactory: () => `Set instruction can only accept a factory or undefined.`,
|
|
730
|
+
ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
|
|
758
731
|
FactoryFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`
|
|
759
732
|
});
|
|
760
733
|
function set(factory, argument) {
|
|
761
734
|
return apply(function set(key) {
|
|
762
735
|
let set;
|
|
763
736
|
let get;
|
|
737
|
+
let suspense;
|
|
764
738
|
const required = argument === true || argument === undefined;
|
|
765
739
|
if (factory === undefined)
|
|
766
|
-
|
|
767
|
-
else if (typeof factory
|
|
740
|
+
suspense = true;
|
|
741
|
+
else if (typeof factory === "function")
|
|
742
|
+
get = pendingFactory(this, key, factory, required);
|
|
743
|
+
else
|
|
768
744
|
throw Oops$7.BadFactory();
|
|
769
|
-
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
|
-
}
|
|
783
|
-
}
|
|
784
745
|
if (typeof argument == "function")
|
|
785
746
|
set = createValueEffect(argument);
|
|
786
747
|
else
|
|
@@ -788,8 +749,63 @@ function set(factory, argument) {
|
|
|
788
749
|
if (value === undefined && required)
|
|
789
750
|
throw Oops$7.NonOptional(this.subject, key);
|
|
790
751
|
};
|
|
791
|
-
return {
|
|
752
|
+
return {
|
|
753
|
+
set,
|
|
754
|
+
get,
|
|
755
|
+
suspense
|
|
756
|
+
};
|
|
792
757
|
});
|
|
758
|
+
}
|
|
759
|
+
function pendingFactory(parent, key, fn, required) {
|
|
760
|
+
const { subject, state } = parent;
|
|
761
|
+
let pending;
|
|
762
|
+
let error;
|
|
763
|
+
const init = () => {
|
|
764
|
+
const output = suspenseBoundary(() => {
|
|
765
|
+
return fn.call(subject, key, subject);
|
|
766
|
+
});
|
|
767
|
+
if (output instanceof Promise) {
|
|
768
|
+
pending = output
|
|
769
|
+
.catch(err => error = err)
|
|
770
|
+
.then(val => state[key] = val)
|
|
771
|
+
.finally(() => {
|
|
772
|
+
pending = undefined;
|
|
773
|
+
parent.update(key);
|
|
774
|
+
});
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
return state[key] = output;
|
|
778
|
+
};
|
|
779
|
+
const suspend = () => {
|
|
780
|
+
if (required === false)
|
|
781
|
+
return undefined;
|
|
782
|
+
const issue = Oops$7.ValueNotReady(subject, key);
|
|
783
|
+
Object.assign(pending, {
|
|
784
|
+
message: issue.message,
|
|
785
|
+
stack: issue.stack
|
|
786
|
+
});
|
|
787
|
+
throw pending;
|
|
788
|
+
};
|
|
789
|
+
if (required)
|
|
790
|
+
try {
|
|
791
|
+
init();
|
|
792
|
+
}
|
|
793
|
+
catch (err) {
|
|
794
|
+
Oops$7.FactoryFailed(subject, key).warn();
|
|
795
|
+
throw err;
|
|
796
|
+
}
|
|
797
|
+
return () => {
|
|
798
|
+
if (pending)
|
|
799
|
+
return suspend();
|
|
800
|
+
if (error)
|
|
801
|
+
throw error;
|
|
802
|
+
if (key in state)
|
|
803
|
+
return state[key];
|
|
804
|
+
let output = init();
|
|
805
|
+
return pending
|
|
806
|
+
? suspend()
|
|
807
|
+
: output;
|
|
808
|
+
};
|
|
793
809
|
}
|
|
794
810
|
|
|
795
811
|
const Parent = new WeakMap();
|
|
@@ -932,24 +948,26 @@ const ContextWasUsed = new WeakMap();
|
|
|
932
948
|
function tap(type, argument) {
|
|
933
949
|
return apply(function tap(key) {
|
|
934
950
|
if ("set" in type)
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
+
return {
|
|
952
|
+
recursive: true,
|
|
953
|
+
value: type.get()
|
|
954
|
+
};
|
|
955
|
+
const { subject } = this;
|
|
956
|
+
if ("set" in subject.constructor)
|
|
957
|
+
throw Oops$3.CantAttachGlobal(subject, type.name);
|
|
958
|
+
getPending(subject).push(context => {
|
|
959
|
+
let instance = context.get(type);
|
|
960
|
+
if (typeof argument == "function") {
|
|
961
|
+
if (argument(instance) === false)
|
|
962
|
+
instance = undefined;
|
|
963
|
+
}
|
|
964
|
+
else if (!instance && argument)
|
|
965
|
+
throw Oops$3.AmbientRequired(type.name, subject, key);
|
|
966
|
+
this.update(key, instance);
|
|
967
|
+
});
|
|
951
968
|
return {
|
|
952
|
-
recursive: true
|
|
969
|
+
recursive: true,
|
|
970
|
+
suspense: true
|
|
953
971
|
};
|
|
954
972
|
});
|
|
955
973
|
}
|
|
@@ -1121,7 +1139,7 @@ class MVC extends Model {
|
|
|
1121
1139
|
return useTap(this, key, expect);
|
|
1122
1140
|
}
|
|
1123
1141
|
static new(arg) {
|
|
1124
|
-
|
|
1142
|
+
const instance = React.useMemo(() => {
|
|
1125
1143
|
const instance = new this();
|
|
1126
1144
|
control(instance);
|
|
1127
1145
|
if (typeof arg == "function")
|
|
@@ -1133,6 +1151,8 @@ class MVC extends Model {
|
|
|
1133
1151
|
});
|
|
1134
1152
|
return instance;
|
|
1135
1153
|
}, []);
|
|
1154
|
+
usePeerContext(instance.get);
|
|
1155
|
+
return instance;
|
|
1136
1156
|
}
|
|
1137
1157
|
static use(a, b) {
|
|
1138
1158
|
const instance = useModel(this, a, b);
|
|
@@ -1245,7 +1265,7 @@ class Global extends MVC {
|
|
|
1245
1265
|
static set(updates) {
|
|
1246
1266
|
const instance = Active.get(this) || this.create();
|
|
1247
1267
|
instance.import(updates);
|
|
1248
|
-
return instance.update();
|
|
1268
|
+
return instance.update(false);
|
|
1249
1269
|
}
|
|
1250
1270
|
static get(arg) {
|
|
1251
1271
|
const instance = Active.get(this);
|