@expressive/mvc 1.0.0-beta.10 → 1.0.0-beta.14
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.js +662 -597
- package/package.json +2 -2
- package/types/index.d.ts +1 -0
- package/types/instructions.d.ts +130 -81
- package/types/model.d.ts +139 -72
- package/types/selector.ts +18 -0
- package/types/dispatch.d.ts +0 -55
package/lib/index.js
CHANGED
|
@@ -13,6 +13,9 @@ class Issue extends Error {
|
|
|
13
13
|
warn() {
|
|
14
14
|
console.warn(this.message);
|
|
15
15
|
}
|
|
16
|
+
throw() {
|
|
17
|
+
throw this;
|
|
18
|
+
}
|
|
16
19
|
}
|
|
17
20
|
function issues(register) {
|
|
18
21
|
const Library = {};
|
|
@@ -41,9 +44,6 @@ function defineLazy(object, property, init) {
|
|
|
41
44
|
function setAlias(func, displayName) {
|
|
42
45
|
assign(func, { displayName });
|
|
43
46
|
}
|
|
44
|
-
function entriesIn(object) {
|
|
45
|
-
return entries(getOwnPropertyDescriptors(object));
|
|
46
|
-
}
|
|
47
47
|
function createEffect(callback) {
|
|
48
48
|
let unSet;
|
|
49
49
|
return function (value) {
|
|
@@ -55,6 +55,12 @@ function createEffect(callback) {
|
|
|
55
55
|
throw Oops$8.BadEffectCallback();
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
|
+
function select(source, select) {
|
|
59
|
+
const proxy = {};
|
|
60
|
+
for (const k in source)
|
|
61
|
+
proxy[k] = k;
|
|
62
|
+
return select(proxy);
|
|
63
|
+
}
|
|
58
64
|
function selectRecursive(using, keys) {
|
|
59
65
|
const found = new Set();
|
|
60
66
|
const spy = {};
|
|
@@ -85,16 +91,11 @@ function lifecycle(prefix) {
|
|
|
85
91
|
const type = eventsFor(prefix);
|
|
86
92
|
return function useLifecycle(sub, tag) {
|
|
87
93
|
const emit = react.useMemo(() => {
|
|
88
|
-
const
|
|
89
|
-
const { subject } = parent;
|
|
94
|
+
const subject = sub.source;
|
|
90
95
|
const id = typeof tag == "function" ? tag(subject) : tag;
|
|
91
96
|
return (name) => {
|
|
92
|
-
for (const key of [name, type(name)])
|
|
93
|
-
|
|
94
|
-
if (handler)
|
|
95
|
-
handler.call(subject, id);
|
|
96
|
-
parent.update(key);
|
|
97
|
-
}
|
|
97
|
+
for (const key of [name, type(name)])
|
|
98
|
+
subject.update(key, id);
|
|
98
99
|
};
|
|
99
100
|
}, [sub, tag]);
|
|
100
101
|
emit(Lifecycle.WILL_RENDER);
|
|
@@ -121,6 +122,19 @@ function eventsFor(prefix) {
|
|
|
121
122
|
return (e) => map.get(e);
|
|
122
123
|
}
|
|
123
124
|
|
|
125
|
+
const CONTROL = Symbol("control");
|
|
126
|
+
const CREATE = Symbol("start");
|
|
127
|
+
const LOCAL = Symbol("local");
|
|
128
|
+
const STATE = Symbol("state");
|
|
129
|
+
const Pending = new Map();
|
|
130
|
+
function manage(src) {
|
|
131
|
+
return src[CONTROL];
|
|
132
|
+
}
|
|
133
|
+
function does(instruction, label) {
|
|
134
|
+
const placeholder = Symbol(`${label} instruction`);
|
|
135
|
+
Pending.set(placeholder, instruction);
|
|
136
|
+
return placeholder;
|
|
137
|
+
}
|
|
124
138
|
class Controller {
|
|
125
139
|
constructor(subject) {
|
|
126
140
|
this.subject = subject;
|
|
@@ -128,14 +142,41 @@ class Controller {
|
|
|
128
142
|
this.frame = new Set();
|
|
129
143
|
this.handles = new Set();
|
|
130
144
|
this.waiting = [];
|
|
131
|
-
|
|
145
|
+
}
|
|
146
|
+
static setup(onto) {
|
|
147
|
+
const create = onto[CREATE];
|
|
148
|
+
const control = new this(onto);
|
|
149
|
+
const after = create && create.call(onto, control);
|
|
150
|
+
defineLazy(onto, CONTROL, () => {
|
|
151
|
+
onto[STATE] = control.state;
|
|
152
|
+
control.start();
|
|
153
|
+
if (after)
|
|
154
|
+
after();
|
|
155
|
+
if (onto.didCreate)
|
|
156
|
+
onto.didCreate();
|
|
157
|
+
return control;
|
|
158
|
+
});
|
|
159
|
+
return control;
|
|
132
160
|
}
|
|
133
161
|
get pending() {
|
|
134
162
|
return this.frame.size > 0;
|
|
135
163
|
}
|
|
136
164
|
start() {
|
|
137
|
-
|
|
138
|
-
|
|
165
|
+
const { subject } = this;
|
|
166
|
+
for (const key in subject) {
|
|
167
|
+
const desc = getOwnPropertyDescriptor(subject, key);
|
|
168
|
+
if (desc && "value" in desc) {
|
|
169
|
+
const { value } = desc;
|
|
170
|
+
const instruction = Pending.get(value);
|
|
171
|
+
if (instruction) {
|
|
172
|
+
Pending.delete(value);
|
|
173
|
+
delete subject[key];
|
|
174
|
+
instruction.call(this, key, this);
|
|
175
|
+
}
|
|
176
|
+
else if (typeof value !== "function" || /^[A-Z]/.test(key))
|
|
177
|
+
this.manage(key, value);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
139
180
|
this.emit();
|
|
140
181
|
return this;
|
|
141
182
|
}
|
|
@@ -145,16 +186,6 @@ class Controller {
|
|
|
145
186
|
? selectRecursive(using, keys.concat(lifecycleEvents))
|
|
146
187
|
: keys;
|
|
147
188
|
}
|
|
148
|
-
add(key) {
|
|
149
|
-
const desc = getOwnPropertyDescriptor(this.subject, key);
|
|
150
|
-
if (desc && "value" in desc) {
|
|
151
|
-
const { value } = desc;
|
|
152
|
-
if (apply(this, key, value))
|
|
153
|
-
return;
|
|
154
|
-
if (desc.enumerable && typeof value !== "function" || /^[A-Z]/.test(key))
|
|
155
|
-
this.manage(key, value);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
189
|
manage(key, initial, effect) {
|
|
159
190
|
const { state, subject } = this;
|
|
160
191
|
state[key] = initial;
|
|
@@ -162,10 +193,10 @@ class Controller {
|
|
|
162
193
|
enumerable: true,
|
|
163
194
|
configurable: true,
|
|
164
195
|
get: () => state[key],
|
|
165
|
-
set: this.
|
|
196
|
+
set: this.setter(key, effect)
|
|
166
197
|
});
|
|
167
198
|
}
|
|
168
|
-
|
|
199
|
+
setter(key, handler) {
|
|
169
200
|
const { state, subject } = this;
|
|
170
201
|
return (value) => {
|
|
171
202
|
if (state[key] == value)
|
|
@@ -180,25 +211,6 @@ class Controller {
|
|
|
180
211
|
this.update(key, value);
|
|
181
212
|
};
|
|
182
213
|
}
|
|
183
|
-
watch(subset, handler, squash, once) {
|
|
184
|
-
const set = keys(this, subset);
|
|
185
|
-
const batch = {};
|
|
186
|
-
const remove = this.addListener(batch);
|
|
187
|
-
const callback = squash
|
|
188
|
-
? handler.bind(this.subject)
|
|
189
|
-
: (frame) => {
|
|
190
|
-
for (const key of frame)
|
|
191
|
-
if (set.includes(key))
|
|
192
|
-
handler.call(this.subject, this.state[key], key);
|
|
193
|
-
};
|
|
194
|
-
const handle = once
|
|
195
|
-
? (k) => { remove(); callback(k); }
|
|
196
|
-
: callback;
|
|
197
|
-
for (const key of set)
|
|
198
|
-
batch[key] = handle;
|
|
199
|
-
ensure(this, set);
|
|
200
|
-
return remove;
|
|
201
|
-
}
|
|
202
214
|
addListener(batch) {
|
|
203
215
|
this.handles.add(batch);
|
|
204
216
|
return () => {
|
|
@@ -228,9 +240,9 @@ class Controller {
|
|
|
228
240
|
const keys = Array.from(this.frame);
|
|
229
241
|
const handle = new Set(this.waiting.splice(0));
|
|
230
242
|
this.frame.clear();
|
|
231
|
-
handle.forEach(
|
|
243
|
+
handle.forEach(callback => {
|
|
232
244
|
try {
|
|
233
|
-
|
|
245
|
+
callback(keys);
|
|
234
246
|
}
|
|
235
247
|
catch (e) { }
|
|
236
248
|
});
|
|
@@ -244,50 +256,6 @@ function keys(from, using) {
|
|
|
244
256
|
return from.select(using);
|
|
245
257
|
}
|
|
246
258
|
|
|
247
|
-
const Oops$7 = issues({
|
|
248
|
-
CantAttachGlobal: (parent, child) => `Singleton '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
|
|
249
|
-
AmbientRequired: (requested, requester, key) => `Attempted to find an instance of ${requested} in context. It is required for [${requester}.${key}], but one could not be found.`
|
|
250
|
-
});
|
|
251
|
-
const PendingContext = new WeakMap();
|
|
252
|
-
const ContextWasUsed = new WeakMap();
|
|
253
|
-
const tap = (type, required) => child(function tap(key) {
|
|
254
|
-
const to = this.subject;
|
|
255
|
-
let getInstance;
|
|
256
|
-
if ("current" in type)
|
|
257
|
-
getInstance = () => type.current;
|
|
258
|
-
else if ("current" in to.constructor)
|
|
259
|
-
throw Oops$7.CantAttachGlobal(to, type.name);
|
|
260
|
-
else
|
|
261
|
-
getPending(to).push(context => {
|
|
262
|
-
const remote = context.get(type);
|
|
263
|
-
if (!remote && required)
|
|
264
|
-
throw Oops$7.AmbientRequired(type.name, to, key);
|
|
265
|
-
getInstance = () => remote;
|
|
266
|
-
});
|
|
267
|
-
return () => getInstance();
|
|
268
|
-
});
|
|
269
|
-
function usePeerContext(subject) {
|
|
270
|
-
if (ContextWasUsed.has(subject)) {
|
|
271
|
-
if (ContextWasUsed.get(subject))
|
|
272
|
-
useLookup();
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
const pending = PendingContext.get(subject);
|
|
276
|
-
if (pending) {
|
|
277
|
-
const local = useLookup();
|
|
278
|
-
for (const init of pending)
|
|
279
|
-
init(local);
|
|
280
|
-
PendingContext.delete(subject);
|
|
281
|
-
}
|
|
282
|
-
ContextWasUsed.set(subject, !!pending);
|
|
283
|
-
}
|
|
284
|
-
function getPending(subject) {
|
|
285
|
-
let pending = PendingContext.get(subject);
|
|
286
|
-
if (!pending)
|
|
287
|
-
PendingContext.set(subject, pending = []);
|
|
288
|
-
return pending;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
259
|
class Subscriber {
|
|
292
260
|
constructor(parent, onUpdate) {
|
|
293
261
|
this.onUpdate = onUpdate;
|
|
@@ -297,14 +265,15 @@ class Subscriber {
|
|
|
297
265
|
if (!(parent instanceof Controller))
|
|
298
266
|
parent = manage(parent);
|
|
299
267
|
this.parent = parent;
|
|
268
|
+
this.source = parent.subject;
|
|
300
269
|
this.proxy = create(parent.subject);
|
|
301
270
|
define(this.proxy, LOCAL, this);
|
|
302
271
|
for (const key in parent.state)
|
|
303
272
|
this.spy(key);
|
|
304
273
|
}
|
|
305
274
|
spy(key) {
|
|
306
|
-
const { proxy } = this;
|
|
307
|
-
const { set } = getOwnPropertyDescriptor(
|
|
275
|
+
const { proxy, source } = this;
|
|
276
|
+
const { set } = getOwnPropertyDescriptor(source, key);
|
|
308
277
|
const intercept = () => {
|
|
309
278
|
this.follow(key);
|
|
310
279
|
delete proxy[key];
|
|
@@ -333,145 +302,401 @@ class Subscriber {
|
|
|
333
302
|
}
|
|
334
303
|
}
|
|
335
304
|
|
|
336
|
-
const Oops$
|
|
337
|
-
|
|
305
|
+
const Oops$7 = issues({
|
|
306
|
+
ComputeFailed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
|
|
307
|
+
ComputedEarly: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
|
|
338
308
|
});
|
|
339
|
-
const
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
const instance = react.useMemo(() => Type.create(...args), []);
|
|
349
|
-
react.useLayoutEffect(() => () => instance.destroy(), []);
|
|
350
|
-
return instance;
|
|
309
|
+
const ComputedInit = new WeakSet();
|
|
310
|
+
const ComputedInfo = new WeakMap();
|
|
311
|
+
const ComputedUsed = new WeakMap();
|
|
312
|
+
const ComputedKeys = new WeakMap();
|
|
313
|
+
function getRegister(on) {
|
|
314
|
+
let register = ComputedUsed.get(on);
|
|
315
|
+
if (!register)
|
|
316
|
+
ComputedUsed.set(on, register = new Map());
|
|
317
|
+
return register;
|
|
351
318
|
}
|
|
352
|
-
function
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
get() {
|
|
360
|
-
if (src[key] === undefined && expected)
|
|
361
|
-
throw Oops$6.HasPropertyUndefined(target, key);
|
|
362
|
-
return src[key];
|
|
363
|
-
}
|
|
364
|
-
});
|
|
319
|
+
function prepareGetters(on) {
|
|
320
|
+
const defined = getRegister(on);
|
|
321
|
+
for (let scan = on.subject; scan.constructor !== Object; scan = getPrototypeOf(scan))
|
|
322
|
+
for (const key of getOwnPropertyNames(scan)) {
|
|
323
|
+
const { get, set } = getOwnPropertyDescriptor(scan, key);
|
|
324
|
+
if (get && !defined.has(key))
|
|
325
|
+
prepare(on, key, get, set);
|
|
365
326
|
}
|
|
366
|
-
return sub;
|
|
367
|
-
});
|
|
368
|
-
react.useLayoutEffect(() => hook.commit(), []);
|
|
369
|
-
return hook.proxy;
|
|
370
327
|
}
|
|
371
|
-
function
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
});
|
|
387
|
-
usePeerContext(hook.parent.subject);
|
|
388
|
-
useComponentLifecycle(hook);
|
|
389
|
-
return hook.proxy;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
class Lookup {
|
|
393
|
-
constructor() {
|
|
394
|
-
this.table = new Map();
|
|
395
|
-
}
|
|
396
|
-
get local() {
|
|
397
|
-
return getOwnPropertySymbols(this).map((symbol) => this[symbol]);
|
|
398
|
-
}
|
|
399
|
-
key(T) {
|
|
400
|
-
let key = this.table.get(T);
|
|
401
|
-
if (!key) {
|
|
402
|
-
key = Symbol(T.name);
|
|
403
|
-
this.table.set(T, key);
|
|
328
|
+
function prepare(on, key, getter, setter) {
|
|
329
|
+
const { state, subject } = on;
|
|
330
|
+
const defined = getRegister(on);
|
|
331
|
+
const info = {
|
|
332
|
+
key, parent: on, priority: 1
|
|
333
|
+
};
|
|
334
|
+
let sub;
|
|
335
|
+
defined.set(key, info);
|
|
336
|
+
function compute(initial) {
|
|
337
|
+
try {
|
|
338
|
+
return getter.call(sub.proxy, sub.proxy);
|
|
339
|
+
}
|
|
340
|
+
catch (err) {
|
|
341
|
+
Oops$7.ComputeFailed(subject, key, !!initial).warn();
|
|
342
|
+
throw err;
|
|
404
343
|
}
|
|
405
|
-
return key;
|
|
406
|
-
}
|
|
407
|
-
get(T) {
|
|
408
|
-
return this[this.key(T)];
|
|
409
|
-
}
|
|
410
|
-
push(I) {
|
|
411
|
-
const next = create(this);
|
|
412
|
-
if (I instanceof Model || typeof I == "function")
|
|
413
|
-
next.register(I);
|
|
414
|
-
else
|
|
415
|
-
for (const i of values(I))
|
|
416
|
-
next.register(i);
|
|
417
|
-
return next;
|
|
418
344
|
}
|
|
419
|
-
|
|
420
|
-
let
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
T = I.constructor;
|
|
424
|
-
writable = false;
|
|
345
|
+
function update() {
|
|
346
|
+
let value;
|
|
347
|
+
try {
|
|
348
|
+
value = compute(false);
|
|
425
349
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
350
|
+
catch (e) {
|
|
351
|
+
console.error(e);
|
|
352
|
+
}
|
|
353
|
+
finally {
|
|
354
|
+
if (state[key] !== value) {
|
|
355
|
+
on.update(key, value);
|
|
356
|
+
return value;
|
|
357
|
+
}
|
|
429
358
|
}
|
|
430
|
-
do {
|
|
431
|
-
defineProperty(this, this.key(T), {
|
|
432
|
-
value: I,
|
|
433
|
-
writable
|
|
434
|
-
});
|
|
435
|
-
} while (T = T.inherits);
|
|
436
|
-
return I;
|
|
437
359
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
360
|
+
function create(early) {
|
|
361
|
+
sub = new Subscriber(on, update);
|
|
362
|
+
ComputedInfo.set(update, info);
|
|
363
|
+
defineProperty(state, key, {
|
|
364
|
+
value: undefined,
|
|
365
|
+
writable: true
|
|
366
|
+
});
|
|
367
|
+
defineProperty(subject, key, {
|
|
368
|
+
enumerable: true,
|
|
369
|
+
configurable: true,
|
|
370
|
+
get: () => state[key],
|
|
371
|
+
set: setter
|
|
372
|
+
});
|
|
373
|
+
try {
|
|
374
|
+
return state[key] = compute(true);
|
|
375
|
+
}
|
|
376
|
+
catch (e) {
|
|
377
|
+
if (early)
|
|
378
|
+
Oops$7.ComputedEarly(key).warn();
|
|
379
|
+
throw e;
|
|
380
|
+
}
|
|
381
|
+
finally {
|
|
382
|
+
sub.commit();
|
|
383
|
+
for (const key in sub.follows) {
|
|
384
|
+
const peer = defined.get(key);
|
|
385
|
+
if (peer && peer.priority >= info.priority)
|
|
386
|
+
info.priority = peer.priority + 1;
|
|
387
|
+
}
|
|
443
388
|
}
|
|
444
389
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
if (typeof callback == "function")
|
|
466
|
-
callback(Control.get(!!has));
|
|
467
|
-
else
|
|
468
|
-
throw Oops$5.BadConsumerProps();
|
|
469
|
-
return null;
|
|
390
|
+
function revert(value) {
|
|
391
|
+
delete state[key];
|
|
392
|
+
defineProperty(subject, key, {
|
|
393
|
+
enumerable: true,
|
|
394
|
+
configurable: true,
|
|
395
|
+
value
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
setAlias(update, `try ${key}`);
|
|
399
|
+
setAlias(create, `new ${key}`);
|
|
400
|
+
setAlias(getter, `run ${key}`);
|
|
401
|
+
ComputedInit.add(create);
|
|
402
|
+
for (const on of [state, subject])
|
|
403
|
+
defineProperty(on, key, {
|
|
404
|
+
get: create,
|
|
405
|
+
set: setter || revert,
|
|
406
|
+
configurable: true,
|
|
407
|
+
enumerable: true
|
|
408
|
+
});
|
|
409
|
+
return info;
|
|
470
410
|
}
|
|
471
|
-
function
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
411
|
+
function ensure(on, keys) {
|
|
412
|
+
for (const key of keys) {
|
|
413
|
+
const desc = getOwnPropertyDescriptor(on.subject, key);
|
|
414
|
+
const getter = desc && desc.get;
|
|
415
|
+
if (ComputedInit.has(getter))
|
|
416
|
+
getter(true);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
function capture(on, request) {
|
|
420
|
+
const compute = ComputedInfo.get(request);
|
|
421
|
+
const callback = request;
|
|
422
|
+
if (!compute)
|
|
423
|
+
return;
|
|
424
|
+
let pending = ComputedKeys.get(on);
|
|
425
|
+
if (!pending)
|
|
426
|
+
ComputedKeys.set(on, pending = []);
|
|
427
|
+
if (compute.parent !== on)
|
|
428
|
+
callback();
|
|
429
|
+
else {
|
|
430
|
+
const queue = pending.findIndex(peer => compute.priority > ComputedInfo.get(peer).priority);
|
|
431
|
+
pending.splice(queue + 1, 0, callback);
|
|
432
|
+
}
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
function flush(on) {
|
|
436
|
+
const handled = on.frame;
|
|
437
|
+
let pending = ComputedKeys.get(on);
|
|
438
|
+
if (pending) {
|
|
439
|
+
while (pending.length) {
|
|
440
|
+
const compute = pending.shift();
|
|
441
|
+
const { key } = ComputedInfo.get(compute);
|
|
442
|
+
if (!handled.has(key))
|
|
443
|
+
compute();
|
|
444
|
+
}
|
|
445
|
+
ComputedKeys.delete(on);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const Oops$6 = issues({
|
|
450
|
+
DuplicateAction: (key) => `Invoked action ${key} but one is already active.`
|
|
451
|
+
});
|
|
452
|
+
const set = (instruction, name) => does(function set(key) {
|
|
453
|
+
let output = instruction.call(this, key, this);
|
|
454
|
+
if (typeof output == "function") {
|
|
455
|
+
const getter = output;
|
|
456
|
+
output = Object.assign(Object.assign({}, getOwnPropertyDescriptor(this.subject, key)), { get() {
|
|
457
|
+
return getter(this[LOCAL]);
|
|
458
|
+
} });
|
|
459
|
+
}
|
|
460
|
+
if (output)
|
|
461
|
+
defineProperty(this.subject, key, output);
|
|
462
|
+
}, name || instruction.name || "pending");
|
|
463
|
+
const ref = (cb) => set(function ref(key) {
|
|
464
|
+
const refObjectFunction = this.setter(key, cb && createEffect(cb));
|
|
465
|
+
defineProperty(refObjectFunction, "current", {
|
|
466
|
+
set: refObjectFunction,
|
|
467
|
+
get: () => this.state[key]
|
|
468
|
+
});
|
|
469
|
+
return {
|
|
470
|
+
value: refObjectFunction
|
|
471
|
+
};
|
|
472
|
+
});
|
|
473
|
+
const on = (value, cb) => set(function on(key) {
|
|
474
|
+
this.manage(key, value, cb && createEffect(cb));
|
|
475
|
+
});
|
|
476
|
+
const memo = (factory, defer) => set(function memo(key) {
|
|
477
|
+
const source = this.subject;
|
|
478
|
+
const get = () => factory.call(source);
|
|
479
|
+
if (defer)
|
|
480
|
+
defineLazy(source, key, get);
|
|
481
|
+
else
|
|
482
|
+
define(source, key, get());
|
|
483
|
+
});
|
|
484
|
+
const lazy = (value) => set(function lazy(key) {
|
|
485
|
+
const source = this.subject;
|
|
486
|
+
source[key] = value;
|
|
487
|
+
defineProperty(this.state, key, {
|
|
488
|
+
get: () => source[key]
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
const act = (task) => set(function act(key) {
|
|
492
|
+
let pending = false;
|
|
493
|
+
const invoke = (...args) => {
|
|
494
|
+
if (pending)
|
|
495
|
+
return Promise.reject(Oops$6.DuplicateAction(key));
|
|
496
|
+
pending = true;
|
|
497
|
+
this.update(key);
|
|
498
|
+
return new Promise(res => {
|
|
499
|
+
res(task.apply(this.subject, args));
|
|
500
|
+
}).finally(() => {
|
|
501
|
+
pending = false;
|
|
502
|
+
this.update(key);
|
|
503
|
+
});
|
|
504
|
+
};
|
|
505
|
+
setAlias(invoke, `run ${key}`);
|
|
506
|
+
defineProperty(invoke, "active", {
|
|
507
|
+
get: () => pending
|
|
508
|
+
});
|
|
509
|
+
return {
|
|
510
|
+
value: invoke,
|
|
511
|
+
writable: false
|
|
512
|
+
};
|
|
513
|
+
});
|
|
514
|
+
const from = (getter) => set(function from(key) {
|
|
515
|
+
prepare(this, key, getter);
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
const attach = (from, name) => set(function attach(key) {
|
|
519
|
+
const proxyCache = new WeakMap();
|
|
520
|
+
const getCurrent = from.call(this, key);
|
|
521
|
+
function subscribe(sub) {
|
|
522
|
+
let child;
|
|
523
|
+
function start() {
|
|
524
|
+
const instance = getCurrent();
|
|
525
|
+
if (instance) {
|
|
526
|
+
child = new Subscriber(instance, sub.onUpdate);
|
|
527
|
+
if (sub.active)
|
|
528
|
+
child.commit();
|
|
529
|
+
sub.dependant.add(child);
|
|
530
|
+
proxyCache.set(sub, child.proxy);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
function refresh() {
|
|
534
|
+
if (child) {
|
|
535
|
+
child.release();
|
|
536
|
+
sub.dependant.delete(child);
|
|
537
|
+
proxyCache.set(sub, undefined);
|
|
538
|
+
child = undefined;
|
|
539
|
+
}
|
|
540
|
+
start();
|
|
541
|
+
sub.onUpdate();
|
|
542
|
+
}
|
|
543
|
+
start();
|
|
544
|
+
sub.follow(key, refresh);
|
|
545
|
+
}
|
|
546
|
+
return (local) => {
|
|
547
|
+
if (!local)
|
|
548
|
+
return getCurrent();
|
|
549
|
+
if (!proxyCache.has(local))
|
|
550
|
+
subscribe(local);
|
|
551
|
+
return proxyCache.get(local);
|
|
552
|
+
};
|
|
553
|
+
}, name || from.name);
|
|
554
|
+
|
|
555
|
+
const Oops$5 = issues({
|
|
556
|
+
HasPropertyUndefined: (control, property) => `${control}.${property} is marked as required for this render.`
|
|
557
|
+
});
|
|
558
|
+
const useElementLifecycle = lifecycle("element");
|
|
559
|
+
function use$1(init) {
|
|
560
|
+
const [state, update] = react.useState(() => [
|
|
561
|
+
init(() => update(state.concat()))
|
|
562
|
+
]);
|
|
563
|
+
return state[0];
|
|
564
|
+
}
|
|
565
|
+
function useLazy(Type, args, callback) {
|
|
566
|
+
const instance = react.useMemo(() => {
|
|
567
|
+
const instance = Type.create(...args || []);
|
|
568
|
+
if (callback)
|
|
569
|
+
callback(instance);
|
|
570
|
+
return instance;
|
|
571
|
+
}, []);
|
|
572
|
+
react.useLayoutEffect(() => () => instance.destroy(), []);
|
|
573
|
+
return instance;
|
|
574
|
+
}
|
|
575
|
+
function useWatcher(target, focus, expected) {
|
|
576
|
+
const hook = use$1(refresh => {
|
|
577
|
+
const sub = new Subscriber(target, refresh);
|
|
578
|
+
if (focus) {
|
|
579
|
+
const src = sub.proxy;
|
|
580
|
+
const key = keys(sub.parent, focus)[0];
|
|
581
|
+
defineProperty(sub, "proxy", {
|
|
582
|
+
get() {
|
|
583
|
+
if (src[key] === undefined && expected)
|
|
584
|
+
throw Oops$5.HasPropertyUndefined(target, key);
|
|
585
|
+
return src[key];
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
return sub;
|
|
590
|
+
});
|
|
591
|
+
react.useLayoutEffect(() => hook.commit(), []);
|
|
592
|
+
return hook.proxy;
|
|
593
|
+
}
|
|
594
|
+
function useSubscriber(target, tag) {
|
|
595
|
+
const hook = use$1(refresh => new Subscriber(target, refresh));
|
|
596
|
+
useElementLifecycle(hook, tag || 0);
|
|
597
|
+
return hook.proxy;
|
|
598
|
+
}
|
|
599
|
+
function useModel(Type, args, callback) {
|
|
600
|
+
const hook = use$1(refresh => {
|
|
601
|
+
const instance = new Type(...args || []);
|
|
602
|
+
const control = manage(instance);
|
|
603
|
+
const sub = new Subscriber(control, refresh);
|
|
604
|
+
const release = control.addListener({
|
|
605
|
+
[Lifecycle.WILL_UNMOUNT]() {
|
|
606
|
+
instance.destroy();
|
|
607
|
+
release();
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
if (callback)
|
|
611
|
+
callback(instance);
|
|
612
|
+
return sub;
|
|
613
|
+
});
|
|
614
|
+
return hook.proxy;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
class Lookup {
|
|
618
|
+
constructor() {
|
|
619
|
+
this.table = new Map();
|
|
620
|
+
}
|
|
621
|
+
get local() {
|
|
622
|
+
return getOwnPropertySymbols(this).map((symbol) => this[symbol]);
|
|
623
|
+
}
|
|
624
|
+
key(T) {
|
|
625
|
+
let key = this.table.get(T);
|
|
626
|
+
if (!key) {
|
|
627
|
+
key = Symbol(T.name);
|
|
628
|
+
this.table.set(T, key);
|
|
629
|
+
}
|
|
630
|
+
return key;
|
|
631
|
+
}
|
|
632
|
+
get(T) {
|
|
633
|
+
return this[this.key(T)];
|
|
634
|
+
}
|
|
635
|
+
push(I) {
|
|
636
|
+
const next = create(this);
|
|
637
|
+
if (I instanceof Model || typeof I == "function")
|
|
638
|
+
next.register(I);
|
|
639
|
+
else
|
|
640
|
+
for (const i of values(I))
|
|
641
|
+
next.register(i);
|
|
642
|
+
return next;
|
|
643
|
+
}
|
|
644
|
+
register(I) {
|
|
645
|
+
let writable = true;
|
|
646
|
+
let T;
|
|
647
|
+
if (I instanceof Model) {
|
|
648
|
+
T = I.constructor;
|
|
649
|
+
writable = false;
|
|
650
|
+
}
|
|
651
|
+
else {
|
|
652
|
+
T = I;
|
|
653
|
+
I = I.create();
|
|
654
|
+
}
|
|
655
|
+
do {
|
|
656
|
+
defineProperty(this, this.key(T), {
|
|
657
|
+
value: I, writable
|
|
658
|
+
});
|
|
659
|
+
T = getPrototypeOf(T);
|
|
660
|
+
} while (T !== Model);
|
|
661
|
+
return I;
|
|
662
|
+
}
|
|
663
|
+
pop() {
|
|
664
|
+
for (const key of getOwnPropertySymbols(this)) {
|
|
665
|
+
const entry = getOwnPropertyDescriptor(this, key);
|
|
666
|
+
if (entry.writable)
|
|
667
|
+
entry.value.destroy();
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const Oops$4 = issues({
|
|
673
|
+
NoProviderType: () => `Provider 'of' prop must be Model, typeof Model or a collection of them.`,
|
|
674
|
+
NothingInContext: (name) => `Couldn't find ${name} in context; did you forget to use a Provider?`,
|
|
675
|
+
BadConsumerProps: () => `Provider expects either a render function, 'get' or 'has' props.`
|
|
676
|
+
});
|
|
677
|
+
const LookupContext = react.createContext(new Lookup());
|
|
678
|
+
const useLookup = () => react.useContext(LookupContext);
|
|
679
|
+
function useFromContext(Type, strict) {
|
|
680
|
+
const instance = useLookup().get(Type);
|
|
681
|
+
if (!instance && strict)
|
|
682
|
+
throw Oops$4.NothingInContext(Type.name);
|
|
683
|
+
return instance;
|
|
684
|
+
}
|
|
685
|
+
function Consumer(props) {
|
|
686
|
+
const { get, has, children, of: Control } = props;
|
|
687
|
+
if (typeof children == "function")
|
|
688
|
+
return children(Control.tap());
|
|
689
|
+
const callback = has || get;
|
|
690
|
+
if (typeof callback == "function")
|
|
691
|
+
callback(Control.get(!!has));
|
|
692
|
+
else
|
|
693
|
+
throw Oops$4.BadConsumerProps();
|
|
694
|
+
return null;
|
|
695
|
+
}
|
|
696
|
+
function useNewContext(from, inject) {
|
|
697
|
+
return react.useMemo(() => {
|
|
698
|
+
if (!inject)
|
|
699
|
+
throw Oops$4.NoProviderType();
|
|
475
700
|
const context = from.push(inject);
|
|
476
701
|
for (const instance of context.local)
|
|
477
702
|
for (const apply of getPending(instance))
|
|
@@ -514,44 +739,194 @@ function Provider(props) {
|
|
|
514
739
|
: render);
|
|
515
740
|
}
|
|
516
741
|
|
|
517
|
-
const Oops$
|
|
518
|
-
|
|
742
|
+
const Oops$3 = issues({
|
|
743
|
+
CantAttachGlobal: (parent, child) => `Singleton '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
|
|
744
|
+
AmbientRequired: (requested, requester, key) => `Attempted to find an instance of ${requested} in context. It is required for [${requester}.${key}], but one could not be found.`
|
|
519
745
|
});
|
|
520
|
-
const
|
|
521
|
-
const
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
746
|
+
const PendingContext = new WeakMap();
|
|
747
|
+
const ContextWasUsed = new WeakMap();
|
|
748
|
+
const tap = (type, argument) => attach(function tap(key) {
|
|
749
|
+
if ("current" in type)
|
|
750
|
+
return () => type.current;
|
|
751
|
+
const to = this.subject;
|
|
752
|
+
if ("current" in to.constructor)
|
|
753
|
+
throw Oops$3.CantAttachGlobal(to, type.name);
|
|
754
|
+
let instance;
|
|
755
|
+
getPending(to).push(context => {
|
|
756
|
+
instance = context.get(type);
|
|
757
|
+
if (typeof argument == "function") {
|
|
758
|
+
if (argument(instance) === false)
|
|
759
|
+
instance = undefined;
|
|
760
|
+
}
|
|
761
|
+
else if (!instance && argument)
|
|
762
|
+
throw Oops$3.AmbientRequired(type.name, to, key);
|
|
763
|
+
});
|
|
764
|
+
return () => instance;
|
|
765
|
+
});
|
|
766
|
+
function usePeerContext(subject) {
|
|
767
|
+
if (ContextWasUsed.has(subject)) {
|
|
768
|
+
if (ContextWasUsed.get(subject))
|
|
769
|
+
useLookup();
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
const pending = PendingContext.get(subject);
|
|
773
|
+
if (pending) {
|
|
774
|
+
const local = useLookup();
|
|
775
|
+
for (const init of pending)
|
|
776
|
+
init(local);
|
|
777
|
+
PendingContext.delete(subject);
|
|
778
|
+
}
|
|
779
|
+
ContextWasUsed.set(subject, !!pending);
|
|
525
780
|
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
781
|
+
function getPending(subject) {
|
|
782
|
+
let pending = PendingContext.get(subject);
|
|
783
|
+
if (!pending)
|
|
784
|
+
PendingContext.set(subject, pending = []);
|
|
785
|
+
return pending;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
const Oops$2 = issues({
|
|
789
|
+
StrictUpdate: (expected) => `Strict update() did ${expected ? "not " : ""}find pending updates.`,
|
|
790
|
+
NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
|
|
791
|
+
});
|
|
792
|
+
class State {
|
|
793
|
+
constructor(computeGetters) {
|
|
794
|
+
const control = Controller.setup(this);
|
|
529
795
|
define(this, "get", this);
|
|
530
796
|
define(this, "set", this);
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
}
|
|
797
|
+
if (computeGetters !== false)
|
|
798
|
+
prepareGetters(control);
|
|
799
|
+
}
|
|
800
|
+
import(from, subset) {
|
|
801
|
+
for (const key of keys(manage(this), subset))
|
|
802
|
+
if (key in from)
|
|
803
|
+
this[key] = from[key];
|
|
804
|
+
}
|
|
805
|
+
export(subset) {
|
|
806
|
+
const control = manage(this);
|
|
807
|
+
const output = {};
|
|
808
|
+
for (const key of keys(control, subset))
|
|
809
|
+
output[key] = control.state[key];
|
|
810
|
+
return output;
|
|
811
|
+
}
|
|
812
|
+
update(arg, tag) {
|
|
813
|
+
const control = manage(this);
|
|
814
|
+
if (typeof arg == "function")
|
|
815
|
+
arg = select(this, arg);
|
|
816
|
+
if (typeof arg == "boolean") {
|
|
817
|
+
if (!control.pending === arg)
|
|
818
|
+
return Promise.reject(Oops$2.StrictUpdate(arg));
|
|
819
|
+
}
|
|
820
|
+
else if (arg) {
|
|
821
|
+
control.update(arg);
|
|
822
|
+
if (1 in arguments && arg in this)
|
|
823
|
+
if (typeof tag != "boolean")
|
|
824
|
+
this[arg](tag);
|
|
825
|
+
else if (tag)
|
|
826
|
+
this[arg]();
|
|
827
|
+
}
|
|
828
|
+
return {
|
|
829
|
+
then(callback) {
|
|
830
|
+
if (callback)
|
|
831
|
+
if (control.pending)
|
|
832
|
+
control.include(callback);
|
|
833
|
+
else
|
|
834
|
+
callback(false);
|
|
835
|
+
else
|
|
836
|
+
throw Oops$2.NoChaining();
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
destroy() {
|
|
841
|
+
this.update("willDestroy", true);
|
|
842
|
+
}
|
|
843
|
+
toString() {
|
|
844
|
+
return this.constructor.name;
|
|
542
845
|
}
|
|
543
846
|
tap(path, expect) {
|
|
544
847
|
return useWatcher(this, path, expect);
|
|
545
848
|
}
|
|
546
|
-
|
|
547
|
-
|
|
849
|
+
static create(...args) {
|
|
850
|
+
const instance = new this(...args);
|
|
851
|
+
manage(instance);
|
|
852
|
+
return instance;
|
|
853
|
+
}
|
|
854
|
+
static new(args, callback) {
|
|
855
|
+
return useLazy(this, args, callback);
|
|
856
|
+
}
|
|
857
|
+
static use(args, callback) {
|
|
858
|
+
return useModel(this, args, callback);
|
|
548
859
|
}
|
|
549
|
-
|
|
550
|
-
return
|
|
860
|
+
static uses(props, only) {
|
|
861
|
+
return this.use([], instance => {
|
|
862
|
+
instance.import(props, only);
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
static using(props, only) {
|
|
866
|
+
const instance = this.use([]);
|
|
867
|
+
instance.import(props, only);
|
|
868
|
+
return instance;
|
|
869
|
+
}
|
|
870
|
+
static find(strict) {
|
|
871
|
+
return useFromContext(this, strict);
|
|
872
|
+
}
|
|
873
|
+
static get(key) {
|
|
874
|
+
const instance = this.find(!!key);
|
|
875
|
+
return (typeof key == "function" ?
|
|
876
|
+
key(instance) :
|
|
877
|
+
typeof key == "string" ?
|
|
878
|
+
instance[key] :
|
|
879
|
+
instance);
|
|
880
|
+
}
|
|
881
|
+
static tap(key, expect) {
|
|
882
|
+
return useWatcher(this.find(true), key, expect);
|
|
883
|
+
}
|
|
884
|
+
static isTypeof(maybe) {
|
|
885
|
+
return (typeof maybe == "function" &&
|
|
886
|
+
maybe.prototype instanceof this);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
State.CONTROL = CONTROL;
|
|
890
|
+
State.STATE = STATE;
|
|
891
|
+
State.INIT = CREATE;
|
|
892
|
+
State.LOCAL = LOCAL;
|
|
893
|
+
|
|
894
|
+
const useComponentLifecycle = lifecycle("component");
|
|
895
|
+
class Model extends State {
|
|
896
|
+
constructor() {
|
|
897
|
+
super(false);
|
|
898
|
+
}
|
|
899
|
+
[CREATE](using) {
|
|
900
|
+
defer(using, "on");
|
|
901
|
+
defer(using, "effect");
|
|
902
|
+
return () => {
|
|
903
|
+
delete this.on;
|
|
904
|
+
delete this.effect;
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
on(subset, handler, squash, once) {
|
|
908
|
+
const control = manage(this);
|
|
909
|
+
const set = keys(control, subset);
|
|
910
|
+
const batch = {};
|
|
911
|
+
const remove = control.addListener(batch);
|
|
912
|
+
const callback = squash
|
|
913
|
+
? handler.bind(this)
|
|
914
|
+
: (frame) => {
|
|
915
|
+
for (const key of frame)
|
|
916
|
+
if (set.includes(key))
|
|
917
|
+
handler.call(this, control.state[key], key);
|
|
918
|
+
};
|
|
919
|
+
const handle = once
|
|
920
|
+
? (k) => { remove(); callback(k); }
|
|
921
|
+
: callback;
|
|
922
|
+
for (const key of set)
|
|
923
|
+
batch[key] = handle;
|
|
924
|
+
ensure(control, set);
|
|
925
|
+
return remove;
|
|
551
926
|
}
|
|
552
|
-
once(select,
|
|
553
|
-
if (
|
|
554
|
-
return this.on(select,
|
|
927
|
+
once(select, callback, squash) {
|
|
928
|
+
if (callback)
|
|
929
|
+
return this.on(select, callback, squash, true);
|
|
555
930
|
else
|
|
556
931
|
return new Promise(resolve => {
|
|
557
932
|
this.on(select, resolve, true, true);
|
|
@@ -573,92 +948,22 @@ class Model {
|
|
|
573
948
|
return sub.commit();
|
|
574
949
|
}
|
|
575
950
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
if (key in from)
|
|
579
|
-
this[key] = from[key];
|
|
580
|
-
}
|
|
581
|
-
export(subset) {
|
|
582
|
-
const control = manage(this);
|
|
583
|
-
const output = {};
|
|
584
|
-
for (const key of keys(control, subset))
|
|
585
|
-
output[key] = control.state[key];
|
|
586
|
-
return output;
|
|
587
|
-
}
|
|
588
|
-
update(arg) {
|
|
589
|
-
const control = manage(this);
|
|
590
|
-
if (typeof arg == "boolean") {
|
|
591
|
-
if (!control.pending === arg)
|
|
592
|
-
return Promise.reject(Oops$4.StrictUpdate(arg));
|
|
593
|
-
}
|
|
594
|
-
else if (arg)
|
|
595
|
-
for (const key of keys(control, arg))
|
|
596
|
-
control.update(key);
|
|
597
|
-
if (control.pending)
|
|
598
|
-
return new Promise(cb => control.include(cb));
|
|
599
|
-
return Promise.resolve(false);
|
|
600
|
-
}
|
|
601
|
-
destroy() {
|
|
602
|
-
if (this.willDestroy)
|
|
603
|
-
this.willDestroy();
|
|
604
|
-
}
|
|
605
|
-
toString() {
|
|
606
|
-
return this.constructor.name;
|
|
607
|
-
}
|
|
608
|
-
static create(...args) {
|
|
609
|
-
const instance = new this(...args);
|
|
610
|
-
manage(instance);
|
|
611
|
-
return instance;
|
|
612
|
-
}
|
|
613
|
-
static use(...args) {
|
|
614
|
-
return useModel(this, args);
|
|
615
|
-
}
|
|
616
|
-
static uses(props, only) {
|
|
617
|
-
return useModel(this, [], instance => {
|
|
618
|
-
instance.import(props, only);
|
|
619
|
-
});
|
|
951
|
+
tag(id) {
|
|
952
|
+
return useSubscriber(this, id);
|
|
620
953
|
}
|
|
621
|
-
static
|
|
622
|
-
const instance = useModel(this,
|
|
623
|
-
instance
|
|
954
|
+
static use(args, callback) {
|
|
955
|
+
const instance = useModel(this, args, callback);
|
|
956
|
+
useComponentLifecycle(instance[LOCAL]);
|
|
957
|
+
usePeerContext(instance.get);
|
|
624
958
|
return instance;
|
|
625
959
|
}
|
|
626
|
-
static new(...args) {
|
|
627
|
-
return useLazy(this, args);
|
|
628
|
-
}
|
|
629
|
-
static get(key) {
|
|
630
|
-
const instance = this.find(!!key);
|
|
631
|
-
return (typeof key == "function" ?
|
|
632
|
-
key(instance) :
|
|
633
|
-
typeof key == "string" ?
|
|
634
|
-
instance[key] :
|
|
635
|
-
instance);
|
|
636
|
-
}
|
|
637
|
-
static tap(key, expect) {
|
|
638
|
-
return useWatcher(this.find(true), key, expect);
|
|
639
|
-
}
|
|
640
960
|
static tag(id) {
|
|
641
961
|
return useSubscriber(this.find(true), id);
|
|
642
962
|
}
|
|
643
963
|
static meta(path) {
|
|
644
964
|
return useWatcher(this, path);
|
|
645
965
|
}
|
|
646
|
-
static find(strict) {
|
|
647
|
-
return useFromContext(this, strict);
|
|
648
|
-
}
|
|
649
|
-
static isTypeof(maybe) {
|
|
650
|
-
return (typeof maybe == "function" &&
|
|
651
|
-
maybe.prototype instanceof this);
|
|
652
|
-
}
|
|
653
|
-
static get inherits() {
|
|
654
|
-
const I = getPrototypeOf(this);
|
|
655
|
-
if (I !== Model)
|
|
656
|
-
return I;
|
|
657
|
-
}
|
|
658
966
|
}
|
|
659
|
-
Model.STATE = STATE;
|
|
660
|
-
Model.CONTROL = CONTROL;
|
|
661
|
-
Model.LOCAL = LOCAL;
|
|
662
967
|
defineLazy(Model, CONTROL, function () {
|
|
663
968
|
return new Controller(this).start();
|
|
664
969
|
});
|
|
@@ -674,239 +979,27 @@ function defer(on, method) {
|
|
|
674
979
|
};
|
|
675
980
|
}
|
|
676
981
|
|
|
677
|
-
const Oops$3 = issues({
|
|
678
|
-
ComputeFailed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
|
|
679
|
-
ComputedEarly: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
|
|
680
|
-
});
|
|
681
|
-
const ComputedInit = new WeakSet();
|
|
682
|
-
const ComputedInfo = new WeakMap();
|
|
683
|
-
const ComputedFor = new WeakMap();
|
|
684
|
-
const ComputedKeys = new WeakMap();
|
|
685
|
-
function bootstrap(on) {
|
|
686
|
-
const defined = new Map();
|
|
687
|
-
let scan = on.subject;
|
|
688
|
-
ComputedFor.set(on, defined);
|
|
689
|
-
while (scan !== Model && scan.constructor !== Model) {
|
|
690
|
-
for (let [key, { get, set }] of entriesIn(scan))
|
|
691
|
-
if (get && !defined.has(key))
|
|
692
|
-
defined.set(key, prepare(on, key, get, set));
|
|
693
|
-
scan = getPrototypeOf(scan);
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
function prepare(on, key, getter, setter) {
|
|
697
|
-
const { state, subject } = on;
|
|
698
|
-
const info = {
|
|
699
|
-
key, parent: on, priority: 1
|
|
700
|
-
};
|
|
701
|
-
let sub;
|
|
702
|
-
function compute(initial) {
|
|
703
|
-
try {
|
|
704
|
-
return getter.call(sub.proxy, sub.proxy);
|
|
705
|
-
}
|
|
706
|
-
catch (err) {
|
|
707
|
-
Oops$3.ComputeFailed(subject, key, !!initial).warn();
|
|
708
|
-
throw err;
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
function update() {
|
|
712
|
-
let value;
|
|
713
|
-
try {
|
|
714
|
-
value = compute(false);
|
|
715
|
-
}
|
|
716
|
-
catch (e) {
|
|
717
|
-
console.error(e);
|
|
718
|
-
}
|
|
719
|
-
finally {
|
|
720
|
-
if (state[key] !== value) {
|
|
721
|
-
on.update(key, value);
|
|
722
|
-
return value;
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
function create(early) {
|
|
727
|
-
sub = new Subscriber(on, update);
|
|
728
|
-
ComputedInfo.set(update, info);
|
|
729
|
-
defineProperty(state, key, {
|
|
730
|
-
value: undefined,
|
|
731
|
-
writable: true
|
|
732
|
-
});
|
|
733
|
-
defineProperty(subject, key, {
|
|
734
|
-
enumerable: true,
|
|
735
|
-
configurable: true,
|
|
736
|
-
get: () => state[key],
|
|
737
|
-
set: setter
|
|
738
|
-
});
|
|
739
|
-
try {
|
|
740
|
-
return state[key] = compute(true);
|
|
741
|
-
}
|
|
742
|
-
catch (e) {
|
|
743
|
-
if (early)
|
|
744
|
-
Oops$3.ComputedEarly(key).warn();
|
|
745
|
-
throw e;
|
|
746
|
-
}
|
|
747
|
-
finally {
|
|
748
|
-
let defined = ComputedFor.get(on);
|
|
749
|
-
sub.commit();
|
|
750
|
-
for (const key in sub.follows) {
|
|
751
|
-
const compute = defined.get(key);
|
|
752
|
-
if (compute && compute.priority >= info.priority)
|
|
753
|
-
info.priority = compute.priority + 1;
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
function revert(value) {
|
|
758
|
-
delete state[key];
|
|
759
|
-
defineProperty(subject, key, {
|
|
760
|
-
enumerable: true,
|
|
761
|
-
configurable: true,
|
|
762
|
-
value
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
|
-
setAlias(update, `try ${key}`);
|
|
766
|
-
setAlias(create, `new ${key}`);
|
|
767
|
-
setAlias(getter, `run ${key}`);
|
|
768
|
-
ComputedInit.add(create);
|
|
769
|
-
for (const on of [state, subject])
|
|
770
|
-
defineProperty(on, key, {
|
|
771
|
-
get: create,
|
|
772
|
-
set: setter || revert,
|
|
773
|
-
configurable: true,
|
|
774
|
-
enumerable: true
|
|
775
|
-
});
|
|
776
|
-
return info;
|
|
777
|
-
}
|
|
778
|
-
function ensure(on, keys) {
|
|
779
|
-
for (const key of keys) {
|
|
780
|
-
const desc = getOwnPropertyDescriptor(on.subject, key);
|
|
781
|
-
const getter = desc && desc.get;
|
|
782
|
-
if (ComputedInit.has(getter))
|
|
783
|
-
getter(true);
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
function capture(on, request) {
|
|
787
|
-
const compute = ComputedInfo.get(request);
|
|
788
|
-
if (!compute)
|
|
789
|
-
return;
|
|
790
|
-
let pending = ComputedKeys.get(on);
|
|
791
|
-
if (!pending)
|
|
792
|
-
ComputedKeys.set(on, pending = []);
|
|
793
|
-
if (compute.parent !== on)
|
|
794
|
-
request();
|
|
795
|
-
else {
|
|
796
|
-
const byPriorty = (sib) => compute.priority > ComputedInfo.get(sib).priority;
|
|
797
|
-
pending.splice(pending.findIndex(byPriorty) + 1, 0, request);
|
|
798
|
-
}
|
|
799
|
-
return true;
|
|
800
|
-
}
|
|
801
|
-
function flush(on) {
|
|
802
|
-
const handled = on.frame;
|
|
803
|
-
let pending = ComputedKeys.get(on);
|
|
804
|
-
if (pending) {
|
|
805
|
-
while (pending.length) {
|
|
806
|
-
const compute = pending.shift();
|
|
807
|
-
const { key } = ComputedInfo.get(compute);
|
|
808
|
-
if (!handled.has(key))
|
|
809
|
-
compute();
|
|
810
|
-
}
|
|
811
|
-
ComputedKeys.delete(on);
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
const Oops$2 = issues({
|
|
816
|
-
DuplicateAction: (key) => `Invoked action ${key} but one is already active.`
|
|
817
|
-
});
|
|
818
|
-
const Pending = new Map();
|
|
819
|
-
function apply(on, key, value) {
|
|
820
|
-
const instruction = Pending.get(value);
|
|
821
|
-
if (instruction) {
|
|
822
|
-
Pending.delete(value);
|
|
823
|
-
delete on.subject[key];
|
|
824
|
-
instruction.call(on, key);
|
|
825
|
-
return true;
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
function set(instruction, name = instruction.name || "pending") {
|
|
829
|
-
const placeholder = Symbol(`${name} instruction`);
|
|
830
|
-
function apply(key) {
|
|
831
|
-
let output = instruction.call(this, key);
|
|
832
|
-
if (typeof output == "function") {
|
|
833
|
-
const getter = output;
|
|
834
|
-
output = Object.assign(Object.assign({}, getOwnPropertyDescriptor(this.subject, key)), { get() {
|
|
835
|
-
return getter(this[LOCAL]);
|
|
836
|
-
} });
|
|
837
|
-
}
|
|
838
|
-
if (output)
|
|
839
|
-
defineProperty(this.subject, key, output);
|
|
840
|
-
}
|
|
841
|
-
Pending.set(placeholder, apply);
|
|
842
|
-
return placeholder;
|
|
843
|
-
}
|
|
844
|
-
const ref = (cb) => set(function ref(key) {
|
|
845
|
-
const refObjectFunction = this.sets(key, cb && createEffect(cb));
|
|
846
|
-
defineProperty(refObjectFunction, "current", {
|
|
847
|
-
set: refObjectFunction,
|
|
848
|
-
get: () => this.state[key]
|
|
849
|
-
});
|
|
850
|
-
return {
|
|
851
|
-
value: refObjectFunction
|
|
852
|
-
};
|
|
853
|
-
});
|
|
854
|
-
const on = (value, cb) => set(function on(key) {
|
|
855
|
-
this.manage(key, value, cb && createEffect(cb));
|
|
856
|
-
});
|
|
857
|
-
const memo = (factory, defer) => set(function memo(key) {
|
|
858
|
-
const source = this.subject;
|
|
859
|
-
const get = () => factory.call(source);
|
|
860
|
-
if (defer)
|
|
861
|
-
defineLazy(source, key, get);
|
|
862
|
-
else
|
|
863
|
-
define(source, key, get());
|
|
864
|
-
});
|
|
865
|
-
const lazy = (value) => set(function lazy(key) {
|
|
866
|
-
const source = this.subject;
|
|
867
|
-
source[key] = value;
|
|
868
|
-
defineProperty(this.state, key, {
|
|
869
|
-
get: () => source[key]
|
|
870
|
-
});
|
|
871
|
-
});
|
|
872
|
-
const act = (task) => set(function act(key) {
|
|
873
|
-
let pending = false;
|
|
874
|
-
const invoke = (...args) => {
|
|
875
|
-
if (pending)
|
|
876
|
-
return Promise.reject(Oops$2.DuplicateAction(key));
|
|
877
|
-
pending = true;
|
|
878
|
-
this.update(key);
|
|
879
|
-
return new Promise(res => {
|
|
880
|
-
res(task.apply(this.subject, args));
|
|
881
|
-
}).finally(() => {
|
|
882
|
-
pending = false;
|
|
883
|
-
this.update(key);
|
|
884
|
-
});
|
|
885
|
-
};
|
|
886
|
-
setAlias(invoke, `run ${key}`);
|
|
887
|
-
defineProperty(invoke, "active", {
|
|
888
|
-
get: () => pending
|
|
889
|
-
});
|
|
890
|
-
return {
|
|
891
|
-
value: invoke,
|
|
892
|
-
writable: false
|
|
893
|
-
};
|
|
894
|
-
});
|
|
895
|
-
const from = (getter) => set(function from(key) {
|
|
896
|
-
prepare(this, key, getter);
|
|
897
|
-
});
|
|
898
|
-
|
|
899
982
|
const Parent = new WeakMap();
|
|
900
983
|
const Oops$1 = issues({
|
|
901
984
|
ParentRequired: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
|
|
902
985
|
UnexpectedParent: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
|
|
903
|
-
UndefinedNotAllowed: (key) => `Child property ${key} may not be undefined
|
|
986
|
+
UndefinedNotAllowed: (key) => `Child property ${key} may not be undefined.`,
|
|
987
|
+
BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`
|
|
904
988
|
});
|
|
905
|
-
|
|
989
|
+
function bootstrap(object) {
|
|
990
|
+
const breakdown = getOwnPropertyDescriptors(object);
|
|
991
|
+
const control = new Model();
|
|
992
|
+
for (const key in breakdown)
|
|
993
|
+
defineProperty(control, key, breakdown[key]);
|
|
994
|
+
return control;
|
|
995
|
+
}
|
|
996
|
+
const use = (input, argument) => attach(function use(key) {
|
|
906
997
|
let instance;
|
|
907
998
|
const update = (next) => {
|
|
908
|
-
instance =
|
|
909
|
-
|
|
999
|
+
instance =
|
|
1000
|
+
next instanceof Model ? next :
|
|
1001
|
+
next && bootstrap(next);
|
|
1002
|
+
if (instance) {
|
|
910
1003
|
Parent.set(instance, this.subject);
|
|
911
1004
|
manage(instance);
|
|
912
1005
|
}
|
|
@@ -915,9 +1008,13 @@ const use = (Peer, argument) => child(function use(key) {
|
|
|
915
1008
|
else if (!instance && argument !== false)
|
|
916
1009
|
throw Oops$1.UndefinedNotAllowed(key);
|
|
917
1010
|
};
|
|
918
|
-
if (
|
|
919
|
-
instance =
|
|
920
|
-
? new
|
|
1011
|
+
if (input) {
|
|
1012
|
+
instance =
|
|
1013
|
+
Model.isTypeof(input) ? new input() :
|
|
1014
|
+
input instanceof Model ? input :
|
|
1015
|
+
typeof input === "function" ? input() :
|
|
1016
|
+
typeof input === "object" ? bootstrap(input) :
|
|
1017
|
+
Oops$1.BadArgument(typeof input).throw();
|
|
921
1018
|
if (instance)
|
|
922
1019
|
update(instance);
|
|
923
1020
|
}
|
|
@@ -926,45 +1023,6 @@ const use = (Peer, argument) => child(function use(key) {
|
|
|
926
1023
|
this.manage(key, instance, update);
|
|
927
1024
|
return () => instance;
|
|
928
1025
|
});
|
|
929
|
-
const child = (from, name) => {
|
|
930
|
-
function child(key) {
|
|
931
|
-
const proxyCache = new WeakMap();
|
|
932
|
-
const getCurrent = from.call(this, key);
|
|
933
|
-
function attach(sub) {
|
|
934
|
-
let child;
|
|
935
|
-
function create() {
|
|
936
|
-
const instance = getCurrent();
|
|
937
|
-
if (!instance)
|
|
938
|
-
return;
|
|
939
|
-
child = new Subscriber(instance, sub.onUpdate);
|
|
940
|
-
if (sub.active)
|
|
941
|
-
child.commit();
|
|
942
|
-
sub.dependant.add(child);
|
|
943
|
-
proxyCache.set(sub, child.proxy);
|
|
944
|
-
}
|
|
945
|
-
function refresh() {
|
|
946
|
-
if (child) {
|
|
947
|
-
child.release();
|
|
948
|
-
sub.dependant.delete(child);
|
|
949
|
-
proxyCache.set(sub, undefined);
|
|
950
|
-
child = undefined;
|
|
951
|
-
}
|
|
952
|
-
create();
|
|
953
|
-
sub.onUpdate();
|
|
954
|
-
}
|
|
955
|
-
create();
|
|
956
|
-
sub.follow(key, refresh);
|
|
957
|
-
}
|
|
958
|
-
return (local) => {
|
|
959
|
-
if (!local)
|
|
960
|
-
return getCurrent();
|
|
961
|
-
if (!proxyCache.has(local))
|
|
962
|
-
attach(local);
|
|
963
|
-
return proxyCache.get(local);
|
|
964
|
-
};
|
|
965
|
-
}
|
|
966
|
-
return set(child, name || from.name);
|
|
967
|
-
};
|
|
968
1026
|
const parent = (Expects, required) => set(function parent() {
|
|
969
1027
|
const child = this.subject;
|
|
970
1028
|
const expected = Expects.name;
|
|
@@ -995,10 +1053,16 @@ class Singleton extends Model {
|
|
|
995
1053
|
Active.set(Type, instance);
|
|
996
1054
|
return instance;
|
|
997
1055
|
}
|
|
998
|
-
static
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1056
|
+
static update(updates) {
|
|
1057
|
+
const instance = this.current || this.create();
|
|
1058
|
+
instance.import(updates);
|
|
1059
|
+
return instance.update();
|
|
1060
|
+
}
|
|
1061
|
+
static find(strict) {
|
|
1062
|
+
const instance = this.current;
|
|
1063
|
+
if (!instance && strict)
|
|
1064
|
+
throw Oops.GlobalDoesNotExist(this.name);
|
|
1065
|
+
return instance;
|
|
1002
1066
|
}
|
|
1003
1067
|
static reset() {
|
|
1004
1068
|
if (this.current)
|
|
@@ -1014,8 +1078,9 @@ exports.Consumer = Consumer;
|
|
|
1014
1078
|
exports.Model = Model;
|
|
1015
1079
|
exports.Provider = Provider;
|
|
1016
1080
|
exports.Singleton = Singleton;
|
|
1081
|
+
exports.State = State;
|
|
1017
1082
|
exports.act = act;
|
|
1018
|
-
exports.
|
|
1083
|
+
exports.attach = attach;
|
|
1019
1084
|
exports.default = Model;
|
|
1020
1085
|
exports.from = from;
|
|
1021
1086
|
exports.lazy = lazy;
|