@expressive/mvc 1.0.0-beta.42 → 1.0.0-beta.45
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 +44 -37
- package/lib/index.esm.js +588 -507
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +588 -506
- 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;
|
|
@@ -42,9 +42,6 @@ function defineLazy(object, property, init) {
|
|
|
42
42
|
}
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
|
-
function setAlias(func, displayName) {
|
|
46
|
-
assign(func, { displayName });
|
|
47
|
-
}
|
|
48
45
|
function createEffect(callback) {
|
|
49
46
|
let unSet;
|
|
50
47
|
return function (value) {
|
|
@@ -54,7 +51,7 @@ function createEffect(callback) {
|
|
|
54
51
|
if (unSet instanceof Promise)
|
|
55
52
|
unSet = undefined;
|
|
56
53
|
if (unSet && typeof unSet !== "function")
|
|
57
|
-
throw Oops$
|
|
54
|
+
throw Oops$c.BadEffectCallback();
|
|
58
55
|
};
|
|
59
56
|
}
|
|
60
57
|
function createValueEffect(callback) {
|
|
@@ -70,8 +67,28 @@ function createValueEffect(callback) {
|
|
|
70
67
|
if (typeof out == "function")
|
|
71
68
|
unSet = out;
|
|
72
69
|
else
|
|
73
|
-
throw Oops$
|
|
70
|
+
throw Oops$c.BadEffectCallback();
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function suspenseBoundary(fn) {
|
|
74
|
+
const retry = (err) => {
|
|
75
|
+
if (err instanceof Promise)
|
|
76
|
+
return err.then(compute);
|
|
77
|
+
else
|
|
78
|
+
throw err;
|
|
79
|
+
};
|
|
80
|
+
const compute = () => {
|
|
81
|
+
try {
|
|
82
|
+
const output = fn();
|
|
83
|
+
return output instanceof Promise
|
|
84
|
+
? output.catch(retry)
|
|
85
|
+
: output;
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
return retry(err);
|
|
89
|
+
}
|
|
74
90
|
};
|
|
91
|
+
return compute();
|
|
75
92
|
}
|
|
76
93
|
|
|
77
94
|
const UPDATE = new WeakMap();
|
|
@@ -84,174 +101,148 @@ function applyUpdate(subject, keys) {
|
|
|
84
101
|
};
|
|
85
102
|
}
|
|
86
103
|
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
this
|
|
95
|
-
this
|
|
96
|
-
this.frame = new Set();
|
|
97
|
-
this.waiting = new Set();
|
|
98
|
-
this.onDestroy = new Set();
|
|
99
|
-
this.followers = new Set();
|
|
100
|
-
this.proxy = Object.create(subject);
|
|
104
|
+
const CONTROL = Symbol("CONTROL");
|
|
105
|
+
const WHY = Symbol("UPDATE");
|
|
106
|
+
const LOCAL = Symbol("LOCAL");
|
|
107
|
+
const STATE = Symbol("STATE");
|
|
108
|
+
class Model {
|
|
109
|
+
constructor() {
|
|
110
|
+
define(this, CONTROL, new Controller(this));
|
|
111
|
+
define(this, "get", this);
|
|
112
|
+
define(this, "set", this);
|
|
101
113
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
this.manage(key);
|
|
105
|
-
this.emit([]);
|
|
114
|
+
get [STATE]() {
|
|
115
|
+
return this[CONTROL].state;
|
|
106
116
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
this.onDestroy.forEach(x => x());
|
|
117
|
+
get [WHY]() {
|
|
118
|
+
return UPDATE.get(this);
|
|
110
119
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
else
|
|
135
|
-
state[key] = entry;
|
|
136
|
-
const set = onSet !== false
|
|
137
|
-
? this.ref(key, onSet)
|
|
138
|
-
: undefined;
|
|
139
|
-
function get() {
|
|
140
|
-
const local = this[LOCAL];
|
|
141
|
-
const value = state[key];
|
|
142
|
-
if (local && !local.watch[key])
|
|
143
|
-
local.watch[key] = true;
|
|
144
|
-
return onGet ? onGet(value, local) : value;
|
|
145
|
-
}
|
|
146
|
-
setAlias(get, `tap ${key}`);
|
|
147
|
-
defineProperty(subject, key, {
|
|
148
|
-
enumerable,
|
|
149
|
-
set,
|
|
150
|
-
get: onGet
|
|
151
|
-
? () => onGet(state[key])
|
|
152
|
-
: () => state[key]
|
|
153
|
-
});
|
|
154
|
-
defineProperty(proxy, key, {
|
|
155
|
-
enumerable,
|
|
156
|
-
set,
|
|
157
|
-
get
|
|
120
|
+
on(select, handler, squash, once) {
|
|
121
|
+
return control(this, control => {
|
|
122
|
+
if (typeof select == "function")
|
|
123
|
+
return control.addListener(select);
|
|
124
|
+
const keys = typeof select == "string" ? [select] :
|
|
125
|
+
!select.length ? getOwnPropertyNames(control.state) :
|
|
126
|
+
select;
|
|
127
|
+
ensure(control, keys);
|
|
128
|
+
const callback = squash
|
|
129
|
+
? handler.bind(this)
|
|
130
|
+
: frame => frame
|
|
131
|
+
.filter(k => keys.includes(k))
|
|
132
|
+
.forEach(k => handler.call(this, control.state[k], k));
|
|
133
|
+
const trigger = once
|
|
134
|
+
? frame => { remove(); callback(frame); }
|
|
135
|
+
: callback;
|
|
136
|
+
const remove = control.addListener(key => {
|
|
137
|
+
if (keys.includes(key))
|
|
138
|
+
return trigger;
|
|
139
|
+
});
|
|
140
|
+
return remove;
|
|
158
141
|
});
|
|
159
142
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
switch (handler.call(subject, value, state)) {
|
|
167
|
-
case true:
|
|
168
|
-
this.update(key);
|
|
169
|
-
case false:
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
this.update(key, value);
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
addListener(listener) {
|
|
176
|
-
this.followers.add(listener);
|
|
177
|
-
return () => {
|
|
178
|
-
this.followers.delete(listener);
|
|
179
|
-
};
|
|
143
|
+
once(select, callback, squash) {
|
|
144
|
+
if (callback)
|
|
145
|
+
return this.on(select, callback, squash, true);
|
|
146
|
+
return new Promise(resolve => {
|
|
147
|
+
this.on(select, resolve, true, true);
|
|
148
|
+
});
|
|
180
149
|
}
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
this
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
150
|
+
effect(callback, select) {
|
|
151
|
+
const effect = createEffect(callback);
|
|
152
|
+
return control(this, control => {
|
|
153
|
+
if (!select) {
|
|
154
|
+
const sub = new Subscriber(control, () => invoke);
|
|
155
|
+
const invoke = () => {
|
|
156
|
+
const x = sub.proxy;
|
|
157
|
+
effect.call(x, x);
|
|
158
|
+
};
|
|
159
|
+
invoke();
|
|
160
|
+
return sub.commit();
|
|
188
161
|
}
|
|
189
|
-
|
|
190
|
-
|
|
162
|
+
const invoke = () => {
|
|
163
|
+
effect.call(this.get, this.get);
|
|
164
|
+
};
|
|
165
|
+
invoke();
|
|
166
|
+
if (!select.length) {
|
|
167
|
+
control.onDestroy.add(invoke);
|
|
168
|
+
return () => {
|
|
169
|
+
control.onDestroy.delete(invoke);
|
|
170
|
+
};
|
|
191
171
|
}
|
|
172
|
+
return control.addListener(key => {
|
|
173
|
+
if (select.includes(key))
|
|
174
|
+
return invoke;
|
|
175
|
+
});
|
|
176
|
+
});
|
|
192
177
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const keys = Object.freeze([...this.frame]);
|
|
202
|
-
this.frame.clear();
|
|
203
|
-
this.emit(keys);
|
|
204
|
-
}, 0);
|
|
205
|
-
this.frame.add(key);
|
|
206
|
-
for (const callback of this.followers) {
|
|
207
|
-
const event = callback(key, this);
|
|
208
|
-
if (typeof event == "function")
|
|
209
|
-
this.waiting.add(event);
|
|
210
|
-
}
|
|
178
|
+
// TODO: account for exotic properties
|
|
179
|
+
import(source, select) {
|
|
180
|
+
const { subject } = control(this);
|
|
181
|
+
if (!select)
|
|
182
|
+
select = getOwnPropertyNames(subject);
|
|
183
|
+
for (const key of select)
|
|
184
|
+
if (key in source)
|
|
185
|
+
this[key] = source[key];
|
|
211
186
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
return
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
187
|
+
export(subset) {
|
|
188
|
+
const { state } = control(this);
|
|
189
|
+
const output = {};
|
|
190
|
+
if (!subset)
|
|
191
|
+
subset = getOwnPropertyNames(state);
|
|
192
|
+
for (const key of subset)
|
|
193
|
+
output[key] = state[key];
|
|
194
|
+
return output;
|
|
195
|
+
}
|
|
196
|
+
update(arg, tag) {
|
|
197
|
+
const target = control(this);
|
|
198
|
+
if (typeof arg == "string") {
|
|
199
|
+
target.update(arg);
|
|
200
|
+
if (1 in arguments && arg in this) {
|
|
201
|
+
const method = this[arg];
|
|
202
|
+
if (typeof method == "function")
|
|
203
|
+
if (typeof tag != "boolean")
|
|
204
|
+
method.call(this, tag);
|
|
205
|
+
else if (tag)
|
|
206
|
+
method.call(this);
|
|
228
207
|
}
|
|
229
|
-
|
|
208
|
+
arg = undefined;
|
|
209
|
+
}
|
|
210
|
+
return target.requestUpdate(arg);
|
|
230
211
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
control = new Controller(subject);
|
|
237
|
-
define(subject, CONTROL, control);
|
|
212
|
+
/**
|
|
213
|
+
* Mark this instance for garbage-collection and send `willDestroy` event to all listeners.
|
|
214
|
+
*/
|
|
215
|
+
destroy() {
|
|
216
|
+
control(this).stop();
|
|
238
217
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
READY.add(control);
|
|
242
|
-
control.start();
|
|
243
|
-
}
|
|
244
|
-
return control;
|
|
218
|
+
toString() {
|
|
219
|
+
return this.constructor.name;
|
|
245
220
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
221
|
+
/**
|
|
222
|
+
* Creates a new instance of this controller.
|
|
223
|
+
*
|
|
224
|
+
* Beyond `new this(...)`, method will activate managed-state.
|
|
225
|
+
*
|
|
226
|
+
* @param args - arguments sent to constructor
|
|
227
|
+
*/
|
|
228
|
+
static create(...args) {
|
|
229
|
+
const instance = new this(...args);
|
|
230
|
+
control(instance);
|
|
231
|
+
return instance;
|
|
252
232
|
}
|
|
253
|
-
|
|
254
|
-
|
|
233
|
+
/**
|
|
234
|
+
* Static equivalent of `x instanceof this`.
|
|
235
|
+
*
|
|
236
|
+
* Will determine if provided class is a subtype of this one.
|
|
237
|
+
*/
|
|
238
|
+
static isTypeof(maybe) {
|
|
239
|
+
return (typeof maybe == "function" &&
|
|
240
|
+
maybe.prototype instanceof this);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
defineLazy(Model, CONTROL, function () {
|
|
244
|
+
return new Controller(this);
|
|
245
|
+
});
|
|
255
246
|
|
|
256
247
|
class Subscriber {
|
|
257
248
|
constructor(target, onUpdate) {
|
|
@@ -300,6 +291,32 @@ class Subscriber {
|
|
|
300
291
|
}
|
|
301
292
|
|
|
302
293
|
const Oops$b = issues({
|
|
294
|
+
ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
|
|
295
|
+
});
|
|
296
|
+
function suspend(source, key) {
|
|
297
|
+
const error = Oops$b.ValueNotReady(source.subject, key);
|
|
298
|
+
const promise = new Promise(resolve => {
|
|
299
|
+
const release = source.addListener(forKey => {
|
|
300
|
+
if (forKey == key)
|
|
301
|
+
return () => {
|
|
302
|
+
if (source.state[key] !== undefined) {
|
|
303
|
+
release();
|
|
304
|
+
resolve();
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
return Object.assign(promise, {
|
|
310
|
+
toString: () => String(error),
|
|
311
|
+
name: "Suspense",
|
|
312
|
+
message: error.message,
|
|
313
|
+
stack: error.stack
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const Oops$a = issues({
|
|
318
|
+
BadComputedSource: (model, property, got) => `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
|
|
319
|
+
PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`,
|
|
303
320
|
ComputeFailed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
|
|
304
321
|
ComputedEarly: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
|
|
305
322
|
});
|
|
@@ -307,86 +324,109 @@ const INIT = new WeakSet();
|
|
|
307
324
|
const INFO = new WeakMap();
|
|
308
325
|
const USED = new WeakMap();
|
|
309
326
|
const KEYS = new WeakMap();
|
|
310
|
-
function
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
327
|
+
function from(source, arg1, arg2) {
|
|
328
|
+
return apply(function from(key) {
|
|
329
|
+
const parent = this;
|
|
330
|
+
const { subject, state } = this;
|
|
331
|
+
let getSource;
|
|
332
|
+
let required = arg2 === true || arg1 === true;
|
|
333
|
+
if (typeof arg1 == "boolean")
|
|
334
|
+
arg1 = undefined;
|
|
335
|
+
// Easy mistake, using a peer, will always be unresolved.
|
|
336
|
+
if (typeof source == "symbol")
|
|
337
|
+
throw Oops$a.PeerNotAllowed(subject, key);
|
|
338
|
+
// replace source controller in-case it is different
|
|
339
|
+
if (typeof source == "object")
|
|
340
|
+
getSource = () => control(source);
|
|
341
|
+
// specifically an arrow function (getter factory)
|
|
342
|
+
else if (!source.prototype) {
|
|
343
|
+
arg1 = source.call(subject, key);
|
|
344
|
+
getSource = () => this;
|
|
323
345
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
throw
|
|
346
|
+
// Regular function is too ambiguous so not allowed.
|
|
347
|
+
else
|
|
348
|
+
throw Oops$a.BadComputedSource(subject, key, source);
|
|
349
|
+
let sub;
|
|
350
|
+
const setter = arg1;
|
|
351
|
+
const info = { key, parent, priority: 1 };
|
|
352
|
+
let register = USED.get(parent);
|
|
353
|
+
if (!register) {
|
|
354
|
+
register = new Map();
|
|
355
|
+
USED.set(parent, register);
|
|
327
356
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
357
|
+
register.set(key, info);
|
|
358
|
+
function compute(initial) {
|
|
359
|
+
try {
|
|
360
|
+
return setter.call(sub.proxy, sub.proxy);
|
|
361
|
+
}
|
|
362
|
+
catch (err) {
|
|
363
|
+
Oops$a.ComputeFailed(subject, key, !!initial).warn();
|
|
364
|
+
throw err;
|
|
365
|
+
}
|
|
333
366
|
}
|
|
334
|
-
|
|
335
|
-
|
|
367
|
+
function update() {
|
|
368
|
+
let value;
|
|
369
|
+
try {
|
|
370
|
+
value = compute(false);
|
|
371
|
+
}
|
|
372
|
+
catch (e) {
|
|
373
|
+
console.error(e);
|
|
374
|
+
}
|
|
375
|
+
finally {
|
|
376
|
+
if (state[key] !== value) {
|
|
377
|
+
parent.update(key, value);
|
|
378
|
+
return value;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
336
381
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
382
|
+
function defer(_key, from) {
|
|
383
|
+
let pending = KEYS.get(from);
|
|
384
|
+
if (!pending)
|
|
385
|
+
KEYS.set(from, pending = []);
|
|
386
|
+
if (info.parent !== from)
|
|
387
|
+
update();
|
|
388
|
+
else {
|
|
389
|
+
const after = pending.findIndex(peer => (info.priority > INFO.get(peer).priority));
|
|
390
|
+
pending.splice(after + 1, 0, update);
|
|
341
391
|
}
|
|
342
392
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
393
|
+
function create(early) {
|
|
394
|
+
sub = new Subscriber(getSource(), defer);
|
|
395
|
+
defineProperty(state, key, {
|
|
396
|
+
value: undefined,
|
|
397
|
+
writable: true
|
|
398
|
+
});
|
|
399
|
+
try {
|
|
400
|
+
return state[key] = compute(true);
|
|
401
|
+
}
|
|
402
|
+
catch (e) {
|
|
403
|
+
if (early)
|
|
404
|
+
Oops$a.ComputedEarly(key).warn();
|
|
405
|
+
throw e;
|
|
406
|
+
}
|
|
407
|
+
finally {
|
|
408
|
+
sub.commit();
|
|
409
|
+
for (const key in sub.watch) {
|
|
410
|
+
const peer = register.get(key);
|
|
411
|
+
if (peer && peer.priority >= info.priority)
|
|
412
|
+
info.priority = peer.priority + 1;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
353
415
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
sub = new Subscriber(source(), defer);
|
|
416
|
+
INIT.add(create);
|
|
417
|
+
INFO.set(update, info);
|
|
357
418
|
defineProperty(state, key, {
|
|
358
|
-
|
|
359
|
-
|
|
419
|
+
get: create,
|
|
420
|
+
configurable: true,
|
|
421
|
+
enumerable: true
|
|
360
422
|
});
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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
|
|
423
|
+
return () => {
|
|
424
|
+
const value = parent.state[key];
|
|
425
|
+
if (value === undefined && required)
|
|
426
|
+
throw suspend(this, key);
|
|
427
|
+
return value;
|
|
428
|
+
};
|
|
387
429
|
});
|
|
388
|
-
if (getter)
|
|
389
|
-
return () => getter(parent, key);
|
|
390
430
|
}
|
|
391
431
|
function ensure(on, keys) {
|
|
392
432
|
for (const key of keys) {
|
|
@@ -411,175 +451,205 @@ function flush(on) {
|
|
|
411
451
|
KEYS.delete(on);
|
|
412
452
|
}
|
|
413
453
|
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
454
|
+
const Oops$9 = issues({
|
|
455
|
+
StrictUpdate: () => `Strict update() did not find pending updates.`,
|
|
456
|
+
NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
|
|
457
|
+
});
|
|
458
|
+
const PENDING = new Map();
|
|
459
|
+
const READY = new WeakSet();
|
|
460
|
+
class Controller {
|
|
461
|
+
constructor(subject) {
|
|
462
|
+
this.subject = subject;
|
|
463
|
+
this.state = {};
|
|
464
|
+
this.frame = new Set();
|
|
465
|
+
this.waiting = new Set();
|
|
466
|
+
this.onDestroy = new Set();
|
|
467
|
+
this.followers = new Set();
|
|
468
|
+
this.proxy = Object.create(subject);
|
|
426
469
|
}
|
|
427
|
-
|
|
428
|
-
|
|
470
|
+
start() {
|
|
471
|
+
for (const key in this.subject)
|
|
472
|
+
this.manage(key);
|
|
473
|
+
this.emit([]);
|
|
429
474
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
return control.addListener(select);
|
|
434
|
-
const keys = typeof select == "string" ? [select] :
|
|
435
|
-
!select.length ? getOwnPropertyNames(control.state) :
|
|
436
|
-
select;
|
|
437
|
-
ensure(control, keys);
|
|
438
|
-
const callback = squash
|
|
439
|
-
? handler.bind(this)
|
|
440
|
-
: frame => frame
|
|
441
|
-
.filter(k => keys.includes(k))
|
|
442
|
-
.forEach(k => handler.call(this, control.state[k], k));
|
|
443
|
-
const trigger = once
|
|
444
|
-
? frame => { remove(); callback(frame); }
|
|
445
|
-
: callback;
|
|
446
|
-
const remove = control.addListener(key => {
|
|
447
|
-
if (keys.includes(key))
|
|
448
|
-
return trigger;
|
|
449
|
-
});
|
|
450
|
-
return remove;
|
|
451
|
-
});
|
|
475
|
+
stop() {
|
|
476
|
+
this.followers.clear();
|
|
477
|
+
this.onDestroy.forEach(x => x());
|
|
452
478
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
479
|
+
manage(key) {
|
|
480
|
+
const { proxy, state, subject } = this;
|
|
481
|
+
const { value: entry } = getOwnPropertyDescriptor(subject, key);
|
|
482
|
+
if (typeof entry == "function")
|
|
483
|
+
return;
|
|
484
|
+
if (typeof entry == "symbol") {
|
|
485
|
+
const instruction = PENDING.get(entry);
|
|
486
|
+
if (instruction)
|
|
487
|
+
instruction.call(this, key, this);
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
state[key] = entry;
|
|
491
|
+
const set = this.ref(key);
|
|
492
|
+
defineProperty(subject, key, {
|
|
493
|
+
enumerable: false,
|
|
494
|
+
get: () => state[key],
|
|
495
|
+
set
|
|
458
496
|
});
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
};
|
|
469
|
-
invoke();
|
|
470
|
-
return sub.commit();
|
|
471
|
-
}
|
|
472
|
-
const invoke = () => {
|
|
473
|
-
effect.call(this.get, this.get);
|
|
474
|
-
};
|
|
475
|
-
invoke();
|
|
476
|
-
if (!select.length) {
|
|
477
|
-
control.onDestroy.add(invoke);
|
|
478
|
-
return () => {
|
|
479
|
-
control.onDestroy.delete(invoke);
|
|
480
|
-
};
|
|
481
|
-
}
|
|
482
|
-
return control.addListener(key => {
|
|
483
|
-
if (select.includes(key))
|
|
484
|
-
return invoke;
|
|
485
|
-
});
|
|
497
|
+
defineProperty(proxy, key, {
|
|
498
|
+
enumerable: false,
|
|
499
|
+
get() {
|
|
500
|
+
const local = this[LOCAL];
|
|
501
|
+
if (local && !local.watch[key])
|
|
502
|
+
local.watch[key] = true;
|
|
503
|
+
return state[key];
|
|
504
|
+
},
|
|
505
|
+
set
|
|
486
506
|
});
|
|
487
507
|
}
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
508
|
+
ref(key, handler) {
|
|
509
|
+
const { state, subject } = this;
|
|
510
|
+
return (value) => {
|
|
511
|
+
if (state[key] == value)
|
|
512
|
+
return;
|
|
513
|
+
if (handler)
|
|
514
|
+
switch (handler.call(subject, value, state)) {
|
|
515
|
+
case true:
|
|
516
|
+
this.update(key);
|
|
517
|
+
case false:
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
this.update(key, value);
|
|
521
|
+
};
|
|
496
522
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
for (const key of subset)
|
|
503
|
-
output[key] = state[key];
|
|
504
|
-
return output;
|
|
523
|
+
addListener(listener) {
|
|
524
|
+
this.followers.add(listener);
|
|
525
|
+
return () => {
|
|
526
|
+
this.followers.delete(listener);
|
|
527
|
+
};
|
|
505
528
|
}
|
|
506
|
-
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
if (typeof tag != "boolean")
|
|
514
|
-
method.call(this, tag);
|
|
515
|
-
else if (tag)
|
|
516
|
-
method.call(this);
|
|
529
|
+
emit(keys) {
|
|
530
|
+
const waiting = [...this.waiting];
|
|
531
|
+
this.waiting.clear();
|
|
532
|
+
applyUpdate(this.subject, keys)();
|
|
533
|
+
for (const callback of waiting)
|
|
534
|
+
try {
|
|
535
|
+
callback(keys);
|
|
517
536
|
}
|
|
518
|
-
|
|
537
|
+
catch (err) {
|
|
538
|
+
console.error(err);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
update(key, value) {
|
|
542
|
+
if (1 in arguments)
|
|
543
|
+
this.state[key] = value;
|
|
544
|
+
if (this.frame.has(key))
|
|
545
|
+
return;
|
|
546
|
+
if (!this.frame.size)
|
|
547
|
+
setTimeout(() => {
|
|
548
|
+
flush(this);
|
|
549
|
+
const keys = Object.freeze([...this.frame]);
|
|
550
|
+
this.frame.clear();
|
|
551
|
+
this.emit(keys);
|
|
552
|
+
}, 0);
|
|
553
|
+
this.frame.add(key);
|
|
554
|
+
for (const callback of this.followers) {
|
|
555
|
+
const event = callback(key, this);
|
|
556
|
+
if (typeof event == "function")
|
|
557
|
+
this.waiting.add(event);
|
|
519
558
|
}
|
|
520
|
-
return target.requestUpdate(arg);
|
|
521
559
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
560
|
+
requestUpdate(arg) {
|
|
561
|
+
if (typeof arg == "function") {
|
|
562
|
+
this.waiting.add(arg);
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
if (arg === true && this.frame.size < 1)
|
|
566
|
+
return Promise.reject(Oops$9.StrictUpdate());
|
|
567
|
+
return {
|
|
568
|
+
then: (callback) => {
|
|
569
|
+
if (callback)
|
|
570
|
+
if (this.frame.size || arg !== false)
|
|
571
|
+
this.waiting.add(callback);
|
|
572
|
+
else
|
|
573
|
+
callback(false);
|
|
574
|
+
else
|
|
575
|
+
throw Oops$9.NoChaining();
|
|
576
|
+
}
|
|
577
|
+
};
|
|
527
578
|
}
|
|
528
|
-
|
|
529
|
-
|
|
579
|
+
}
|
|
580
|
+
function control(subject, cb) {
|
|
581
|
+
let control = subject[CONTROL];
|
|
582
|
+
const ready = READY.has(control);
|
|
583
|
+
if (!control) {
|
|
584
|
+
control = new Controller(subject);
|
|
585
|
+
define(subject, CONTROL, control);
|
|
530
586
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
*/
|
|
538
|
-
static create(...args) {
|
|
539
|
-
const instance = new this(...args);
|
|
540
|
-
control(instance);
|
|
541
|
-
return instance;
|
|
587
|
+
if (!cb) {
|
|
588
|
+
if (!ready) {
|
|
589
|
+
READY.add(control);
|
|
590
|
+
control.start();
|
|
591
|
+
}
|
|
592
|
+
return control;
|
|
542
593
|
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
return (typeof maybe == "function" &&
|
|
550
|
-
maybe.prototype instanceof this);
|
|
594
|
+
if (!ready) {
|
|
595
|
+
let done;
|
|
596
|
+
control.requestUpdate(() => {
|
|
597
|
+
done = cb(control);
|
|
598
|
+
});
|
|
599
|
+
return () => done && done();
|
|
551
600
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
return new Controller(this);
|
|
555
|
-
});
|
|
601
|
+
return cb(control);
|
|
602
|
+
}
|
|
556
603
|
|
|
557
|
-
const Pending = new Map();
|
|
558
604
|
function apply(fn, label) {
|
|
559
605
|
const name = label || fn.name || "pending";
|
|
560
606
|
const placeholder = Symbol(`${name} instruction`);
|
|
561
607
|
function setup(key) {
|
|
608
|
+
const { proxy, state, subject } = this;
|
|
609
|
+
const control = this;
|
|
610
|
+
PENDING.delete(placeholder);
|
|
611
|
+
delete subject[key];
|
|
562
612
|
let output = fn.call(this, key, this);
|
|
563
613
|
if (typeof output == "function")
|
|
564
|
-
|
|
565
|
-
if (typeof output
|
|
566
|
-
return
|
|
614
|
+
output = { get: output };
|
|
615
|
+
if (typeof output != "object")
|
|
616
|
+
return;
|
|
567
617
|
if ("explicit" in output && output.explicit) {
|
|
568
|
-
defineProperty(
|
|
618
|
+
defineProperty(subject, key, output);
|
|
569
619
|
return false;
|
|
570
620
|
}
|
|
571
|
-
if ("recursive" in output)
|
|
621
|
+
if ("recursive" in output && output.recursive)
|
|
572
622
|
output = Object.assign(Object.assign({}, output), { get: recursive(this, key) });
|
|
573
|
-
|
|
623
|
+
const desc = output;
|
|
624
|
+
if ("value" in desc)
|
|
625
|
+
state[key] = desc.value;
|
|
626
|
+
const { get: onGet, set: onSet, enumerable, suspense } = desc;
|
|
627
|
+
let set = onSet === false
|
|
628
|
+
? undefined
|
|
629
|
+
: this.ref(key, onSet);
|
|
630
|
+
function get() {
|
|
631
|
+
if (!(key in state) && suspense)
|
|
632
|
+
throw suspend(control, key);
|
|
633
|
+
const local = this[LOCAL];
|
|
634
|
+
if (local && !(key in local.watch))
|
|
635
|
+
local.watch[key] = true;
|
|
636
|
+
const value = state[key];
|
|
637
|
+
if (!onGet)
|
|
638
|
+
return value;
|
|
639
|
+
return local
|
|
640
|
+
? onGet(value, local)
|
|
641
|
+
: onGet(value);
|
|
642
|
+
}
|
|
643
|
+
for (const x of [subject, proxy])
|
|
644
|
+
defineProperty(x, key, { enumerable, get, set });
|
|
574
645
|
}
|
|
575
|
-
|
|
646
|
+
PENDING.set(placeholder, setup);
|
|
576
647
|
return placeholder;
|
|
577
648
|
}
|
|
578
649
|
function recursive(source, key) {
|
|
579
|
-
|
|
580
|
-
const subscribe = (parent) => {
|
|
650
|
+
return forSubscriber((parent, context) => {
|
|
581
651
|
let child;
|
|
582
|
-
|
|
652
|
+
function init() {
|
|
583
653
|
if (child) {
|
|
584
654
|
child.release();
|
|
585
655
|
parent.dependant.delete(child);
|
|
@@ -594,20 +664,28 @@ function recursive(source, key) {
|
|
|
594
664
|
parent.dependant.add(child);
|
|
595
665
|
context.set(parent, child.proxy);
|
|
596
666
|
}
|
|
597
|
-
}
|
|
667
|
+
}
|
|
598
668
|
init();
|
|
599
669
|
parent.watch[key] = init;
|
|
600
|
-
};
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
function forSubscriber(fn) {
|
|
673
|
+
const context = new WeakMap();
|
|
601
674
|
return (value, local) => {
|
|
602
675
|
if (!local)
|
|
603
676
|
return value;
|
|
604
|
-
if (!context.has(local))
|
|
605
|
-
|
|
677
|
+
if (!context.has(local)) {
|
|
678
|
+
const output = fn(local, context);
|
|
679
|
+
if (output) {
|
|
680
|
+
context.set(local, output);
|
|
681
|
+
return output;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
606
684
|
return context.get(local);
|
|
607
685
|
};
|
|
608
686
|
}
|
|
609
687
|
|
|
610
|
-
const Oops$
|
|
688
|
+
const Oops$8 = issues({
|
|
611
689
|
DuplicateAction: (key) => `Invoked action ${key} but one is already active.`
|
|
612
690
|
});
|
|
613
691
|
function act(task) {
|
|
@@ -615,7 +693,7 @@ function act(task) {
|
|
|
615
693
|
let pending = false;
|
|
616
694
|
const invoke = (...args) => {
|
|
617
695
|
if (pending)
|
|
618
|
-
return Promise.reject(Oops$
|
|
696
|
+
return Promise.reject(Oops$8.DuplicateAction(key));
|
|
619
697
|
pending = true;
|
|
620
698
|
this.update(key);
|
|
621
699
|
return new Promise(res => {
|
|
@@ -626,7 +704,6 @@ function act(task) {
|
|
|
626
704
|
});
|
|
627
705
|
};
|
|
628
706
|
this.state[key] = undefined;
|
|
629
|
-
setAlias(invoke, `run ${key}`);
|
|
630
707
|
defineProperty(invoke, "active", {
|
|
631
708
|
get: () => pending
|
|
632
709
|
});
|
|
@@ -637,104 +714,62 @@ function act(task) {
|
|
|
637
714
|
});
|
|
638
715
|
}
|
|
639
716
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
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
|
-
};
|
|
717
|
+
class Managed extends Array {
|
|
718
|
+
constructor(onUpdate) {
|
|
719
|
+
super();
|
|
720
|
+
this.lastUpdate = [0, 0];
|
|
721
|
+
this.update = (from, to) => {
|
|
722
|
+
this.lastUpdate = [from, to];
|
|
723
|
+
onUpdate();
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
push(...items) {
|
|
727
|
+
const length = this.length;
|
|
728
|
+
const end = super.push(...items);
|
|
729
|
+
this.update(length, end);
|
|
730
|
+
return end;
|
|
731
|
+
}
|
|
732
|
+
unshift(...items) {
|
|
733
|
+
const end = super.push(...items);
|
|
734
|
+
this.update(0, end);
|
|
735
|
+
return end;
|
|
736
|
+
}
|
|
737
|
+
splice(start, deleteCount, ...rest) {
|
|
738
|
+
this.update(start, deleteCount !== rest.length ? Infinity : deleteCount);
|
|
739
|
+
return super.splice(start, deleteCount, ...rest);
|
|
740
|
+
}
|
|
688
741
|
}
|
|
689
|
-
function
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
const
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
742
|
+
function array() {
|
|
743
|
+
return apply(function array(key) {
|
|
744
|
+
const array = new Managed(() => this.update(key));
|
|
745
|
+
const access = forSubscriber(context => {
|
|
746
|
+
const local = Object.create(array);
|
|
747
|
+
const update = context.onUpdate(key, this);
|
|
748
|
+
let rangeA = 0;
|
|
749
|
+
let rangeB = 0;
|
|
750
|
+
function onUpdate() {
|
|
751
|
+
const [lastA, lastB] = array.lastUpdate;
|
|
752
|
+
if (rangeA <= lastB && rangeB >= lastA)
|
|
753
|
+
return update;
|
|
754
|
+
}
|
|
755
|
+
function listen(eventA, eventB) {
|
|
756
|
+
if (context.active)
|
|
757
|
+
return;
|
|
758
|
+
context.watch[key] = onUpdate;
|
|
759
|
+
rangeA = Math.min(rangeA, eventA);
|
|
760
|
+
rangeB = Math.max(rangeB, eventB);
|
|
761
|
+
}
|
|
762
|
+
local[Symbol.iterator] = () => {
|
|
763
|
+
listen(0, Infinity);
|
|
764
|
+
return array[Symbol.iterator]();
|
|
765
|
+
};
|
|
766
|
+
return local;
|
|
700
767
|
});
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
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);
|
|
768
|
+
return {
|
|
769
|
+
get: access,
|
|
770
|
+
set: false,
|
|
771
|
+
value: array
|
|
772
|
+
};
|
|
738
773
|
});
|
|
739
774
|
}
|
|
740
775
|
|
|
@@ -763,32 +798,21 @@ function createRef(key, cb) {
|
|
|
763
798
|
const Oops$7 = issues({
|
|
764
799
|
NonOptional: (Parent, key) => `Property ${Parent}.${key} is marked as required.`,
|
|
765
800
|
BadFactory: () => `Set instruction can only accept a factory or undefined.`,
|
|
801
|
+
ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
|
|
766
802
|
FactoryFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`
|
|
767
803
|
});
|
|
768
804
|
function set(factory, argument) {
|
|
769
805
|
return apply(function set(key) {
|
|
770
806
|
let set;
|
|
771
807
|
let get;
|
|
808
|
+
let suspense;
|
|
772
809
|
const required = argument === true || argument === undefined;
|
|
773
810
|
if (factory === undefined)
|
|
774
|
-
|
|
775
|
-
else if (typeof factory
|
|
811
|
+
suspense = true;
|
|
812
|
+
else if (typeof factory === "function")
|
|
813
|
+
get = pendingFactory(this, key, factory, required);
|
|
814
|
+
else
|
|
776
815
|
throw Oops$7.BadFactory();
|
|
777
|
-
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
|
-
}
|
|
791
|
-
}
|
|
792
816
|
if (typeof argument == "function")
|
|
793
817
|
set = createValueEffect(argument);
|
|
794
818
|
else
|
|
@@ -796,8 +820,63 @@ function set(factory, argument) {
|
|
|
796
820
|
if (value === undefined && required)
|
|
797
821
|
throw Oops$7.NonOptional(this.subject, key);
|
|
798
822
|
};
|
|
799
|
-
return {
|
|
823
|
+
return {
|
|
824
|
+
set,
|
|
825
|
+
get,
|
|
826
|
+
suspense
|
|
827
|
+
};
|
|
800
828
|
});
|
|
829
|
+
}
|
|
830
|
+
function pendingFactory(parent, key, fn, required) {
|
|
831
|
+
const { subject, state } = parent;
|
|
832
|
+
let pending;
|
|
833
|
+
let error;
|
|
834
|
+
const init = () => {
|
|
835
|
+
const output = suspenseBoundary(() => {
|
|
836
|
+
return fn.call(subject, key, subject);
|
|
837
|
+
});
|
|
838
|
+
if (output instanceof Promise) {
|
|
839
|
+
pending = output
|
|
840
|
+
.catch(err => error = err)
|
|
841
|
+
.then(val => state[key] = val)
|
|
842
|
+
.finally(() => {
|
|
843
|
+
pending = undefined;
|
|
844
|
+
parent.update(key);
|
|
845
|
+
});
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
return state[key] = output;
|
|
849
|
+
};
|
|
850
|
+
const suspend = () => {
|
|
851
|
+
if (required === false)
|
|
852
|
+
return undefined;
|
|
853
|
+
const issue = Oops$7.ValueNotReady(subject, key);
|
|
854
|
+
Object.assign(pending, {
|
|
855
|
+
message: issue.message,
|
|
856
|
+
stack: issue.stack
|
|
857
|
+
});
|
|
858
|
+
throw pending;
|
|
859
|
+
};
|
|
860
|
+
if (required)
|
|
861
|
+
try {
|
|
862
|
+
init();
|
|
863
|
+
}
|
|
864
|
+
catch (err) {
|
|
865
|
+
Oops$7.FactoryFailed(subject, key).warn();
|
|
866
|
+
throw err;
|
|
867
|
+
}
|
|
868
|
+
return () => {
|
|
869
|
+
if (pending)
|
|
870
|
+
return suspend();
|
|
871
|
+
if (error)
|
|
872
|
+
throw error;
|
|
873
|
+
if (key in state)
|
|
874
|
+
return state[key];
|
|
875
|
+
let output = init();
|
|
876
|
+
return pending
|
|
877
|
+
? suspend()
|
|
878
|
+
: output;
|
|
879
|
+
};
|
|
801
880
|
}
|
|
802
881
|
|
|
803
882
|
const Parent = new WeakMap();
|
|
@@ -940,24 +1019,26 @@ const ContextWasUsed = new WeakMap();
|
|
|
940
1019
|
function tap(type, argument) {
|
|
941
1020
|
return apply(function tap(key) {
|
|
942
1021
|
if ("set" in type)
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
1022
|
+
return {
|
|
1023
|
+
recursive: true,
|
|
1024
|
+
value: type.get()
|
|
1025
|
+
};
|
|
1026
|
+
const { subject } = this;
|
|
1027
|
+
if ("set" in subject.constructor)
|
|
1028
|
+
throw Oops$3.CantAttachGlobal(subject, type.name);
|
|
1029
|
+
getPending(subject).push(context => {
|
|
1030
|
+
let instance = context.get(type);
|
|
1031
|
+
if (typeof argument == "function") {
|
|
1032
|
+
if (argument(instance) === false)
|
|
1033
|
+
instance = undefined;
|
|
1034
|
+
}
|
|
1035
|
+
else if (!instance && argument)
|
|
1036
|
+
throw Oops$3.AmbientRequired(type.name, subject, key);
|
|
1037
|
+
this.update(key, instance);
|
|
1038
|
+
});
|
|
959
1039
|
return {
|
|
960
|
-
recursive: true
|
|
1040
|
+
recursive: true,
|
|
1041
|
+
suspense: true
|
|
961
1042
|
};
|
|
962
1043
|
});
|
|
963
1044
|
}
|
|
@@ -1255,7 +1336,7 @@ class Global extends MVC {
|
|
|
1255
1336
|
static set(updates) {
|
|
1256
1337
|
const instance = Active.get(this) || this.create();
|
|
1257
1338
|
instance.import(updates);
|
|
1258
|
-
return instance.update();
|
|
1339
|
+
return instance.update(false);
|
|
1259
1340
|
}
|
|
1260
1341
|
static get(arg) {
|
|
1261
1342
|
const instance = Active.get(this);
|
|
@@ -1287,6 +1368,7 @@ exports.STATE = STATE;
|
|
|
1287
1368
|
exports.WHY = WHY;
|
|
1288
1369
|
exports.act = act;
|
|
1289
1370
|
exports.apply = apply;
|
|
1371
|
+
exports.array = array;
|
|
1290
1372
|
exports["default"] = MVC;
|
|
1291
1373
|
exports.from = from;
|
|
1292
1374
|
exports.parent = parent;
|