@expressive/mvc 0.0.1
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/README.md +1666 -0
- package/lib/index.d.ts +600 -0
- package/lib/index.esm.js +1354 -0
- package/lib/index.esm.js.map +1 -0
- package/lib/index.js +1383 -0
- package/lib/index.js.map +1 -0
- package/package.json +53 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,1383 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var React = require('react');
|
|
6
|
+
|
|
7
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
8
|
+
|
|
9
|
+
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
|
|
10
|
+
|
|
11
|
+
class Issue extends Error {
|
|
12
|
+
constructor() {
|
|
13
|
+
super(...arguments);
|
|
14
|
+
// drop first line (error factory) in stack trace.
|
|
15
|
+
this.stack = this.stack.replace(/\n.+/, "");
|
|
16
|
+
}
|
|
17
|
+
warn() {
|
|
18
|
+
console.warn(this.message);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function issues(register) {
|
|
22
|
+
const Library = {};
|
|
23
|
+
for (const name in register)
|
|
24
|
+
Library[name] = (...args) => new Issue(register[name].apply(null, args));
|
|
25
|
+
return Library;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const Oops$c = issues({
|
|
29
|
+
BadEffectCallback: () => `Callback for effect-callback may only return a function.`
|
|
30
|
+
});
|
|
31
|
+
const { assign, create, defineProperty, entries, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
|
|
32
|
+
function define(target, key, value) {
|
|
33
|
+
defineProperty(target, key, { value });
|
|
34
|
+
}
|
|
35
|
+
function defineLazy(object, property, init) {
|
|
36
|
+
defineProperty(object, property, {
|
|
37
|
+
configurable: true,
|
|
38
|
+
get() {
|
|
39
|
+
const value = init.call(this);
|
|
40
|
+
defineProperty(this, property, { value });
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function createEffect(callback) {
|
|
46
|
+
let unSet;
|
|
47
|
+
return function (value) {
|
|
48
|
+
if (typeof unSet == "function")
|
|
49
|
+
unSet();
|
|
50
|
+
unSet = callback.call(this, value);
|
|
51
|
+
if (unSet instanceof Promise)
|
|
52
|
+
unSet = undefined;
|
|
53
|
+
if (unSet && typeof unSet !== "function")
|
|
54
|
+
throw Oops$c.BadEffectCallback();
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function createValueEffect(callback) {
|
|
58
|
+
let unSet;
|
|
59
|
+
return function (value) {
|
|
60
|
+
if (typeof unSet == "function")
|
|
61
|
+
unSet(value);
|
|
62
|
+
const out = callback.call(this, value, this);
|
|
63
|
+
if (typeof out == "boolean")
|
|
64
|
+
return out;
|
|
65
|
+
if (!out || out instanceof Promise)
|
|
66
|
+
return;
|
|
67
|
+
if (typeof out == "function")
|
|
68
|
+
unSet = out;
|
|
69
|
+
else
|
|
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
|
+
}
|
|
90
|
+
};
|
|
91
|
+
return compute();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const UPDATE = new WeakMap();
|
|
95
|
+
function applyUpdate(subject, keys) {
|
|
96
|
+
UPDATE.set(subject, keys);
|
|
97
|
+
return () => {
|
|
98
|
+
setTimeout(() => {
|
|
99
|
+
UPDATE.delete(subject);
|
|
100
|
+
}, 0);
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
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);
|
|
113
|
+
}
|
|
114
|
+
get [STATE]() {
|
|
115
|
+
return this[CONTROL].state;
|
|
116
|
+
}
|
|
117
|
+
get [WHY]() {
|
|
118
|
+
return UPDATE.get(this);
|
|
119
|
+
}
|
|
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;
|
|
141
|
+
});
|
|
142
|
+
}
|
|
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
|
+
});
|
|
149
|
+
}
|
|
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();
|
|
161
|
+
}
|
|
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
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return control.addListener(key => {
|
|
173
|
+
if (select.includes(key))
|
|
174
|
+
return invoke;
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
}
|
|
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];
|
|
186
|
+
}
|
|
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);
|
|
207
|
+
}
|
|
208
|
+
arg = undefined;
|
|
209
|
+
}
|
|
210
|
+
return target.requestUpdate(arg);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Mark this instance for garbage-collection and send `willDestroy` event to all listeners.
|
|
214
|
+
*/
|
|
215
|
+
destroy() {
|
|
216
|
+
control(this).stop();
|
|
217
|
+
}
|
|
218
|
+
toString() {
|
|
219
|
+
return this.constructor.name;
|
|
220
|
+
}
|
|
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;
|
|
232
|
+
}
|
|
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
|
+
});
|
|
246
|
+
|
|
247
|
+
class Subscriber {
|
|
248
|
+
constructor(target, onUpdate) {
|
|
249
|
+
this.onUpdate = onUpdate;
|
|
250
|
+
this.active = false;
|
|
251
|
+
this.dependant = new Set();
|
|
252
|
+
this.watch = {};
|
|
253
|
+
const parent = target instanceof Controller
|
|
254
|
+
? target : control(target);
|
|
255
|
+
const proxy = create(parent.proxy);
|
|
256
|
+
let reset;
|
|
257
|
+
define(proxy, LOCAL, this);
|
|
258
|
+
defineProperty(this, "proxy", {
|
|
259
|
+
configurable: true,
|
|
260
|
+
get() {
|
|
261
|
+
if (reset)
|
|
262
|
+
setTimeout(reset, 0);
|
|
263
|
+
return proxy;
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
const release = parent.addListener(key => {
|
|
267
|
+
const handler = this.watch[key];
|
|
268
|
+
if (!handler || !this.active)
|
|
269
|
+
return;
|
|
270
|
+
if (typeof handler == "function")
|
|
271
|
+
handler();
|
|
272
|
+
const notify = this.onUpdate(key, parent);
|
|
273
|
+
const getWhy = (keys) => {
|
|
274
|
+
reset = applyUpdate(proxy, keys.filter(k => k in this.watch));
|
|
275
|
+
};
|
|
276
|
+
if (notify) {
|
|
277
|
+
parent.requestUpdate(getWhy);
|
|
278
|
+
parent.requestUpdate(notify);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
this.commit = () => {
|
|
282
|
+
this.active = true;
|
|
283
|
+
this.dependant.forEach(x => x.commit());
|
|
284
|
+
return this.release;
|
|
285
|
+
};
|
|
286
|
+
this.release = () => {
|
|
287
|
+
this.dependant.forEach(x => x.release());
|
|
288
|
+
release();
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
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.`,
|
|
320
|
+
ComputeFailed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
|
|
321
|
+
ComputedEarly: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
|
|
322
|
+
});
|
|
323
|
+
const INIT = new WeakSet();
|
|
324
|
+
const INFO = new WeakMap();
|
|
325
|
+
const USED = new WeakMap();
|
|
326
|
+
const KEYS = new WeakMap();
|
|
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;
|
|
345
|
+
}
|
|
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);
|
|
356
|
+
}
|
|
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
|
+
}
|
|
366
|
+
}
|
|
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
|
+
}
|
|
381
|
+
}
|
|
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);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
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
|
+
}
|
|
415
|
+
}
|
|
416
|
+
INIT.add(create);
|
|
417
|
+
INFO.set(update, info);
|
|
418
|
+
defineProperty(state, key, {
|
|
419
|
+
get: create,
|
|
420
|
+
configurable: true,
|
|
421
|
+
enumerable: true
|
|
422
|
+
});
|
|
423
|
+
return () => {
|
|
424
|
+
const value = parent.state[key];
|
|
425
|
+
if (value === undefined && required)
|
|
426
|
+
throw suspend(this, key);
|
|
427
|
+
return value;
|
|
428
|
+
};
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
function ensure(on, keys) {
|
|
432
|
+
for (const key of keys) {
|
|
433
|
+
const desc = getOwnPropertyDescriptor(on.state, key);
|
|
434
|
+
const getter = desc && desc.get;
|
|
435
|
+
if (INIT.has(getter))
|
|
436
|
+
getter(true);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function flush(on) {
|
|
440
|
+
const handled = on.frame;
|
|
441
|
+
const pending = KEYS.get(on);
|
|
442
|
+
if (!pending)
|
|
443
|
+
return;
|
|
444
|
+
while (pending.length) {
|
|
445
|
+
const why = [...handled];
|
|
446
|
+
const compute = pending.shift();
|
|
447
|
+
const { key } = INFO.get(compute);
|
|
448
|
+
if (!handled.has(key))
|
|
449
|
+
compute(why);
|
|
450
|
+
}
|
|
451
|
+
KEYS.delete(on);
|
|
452
|
+
}
|
|
453
|
+
|
|
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);
|
|
469
|
+
}
|
|
470
|
+
start() {
|
|
471
|
+
for (const key in this.subject)
|
|
472
|
+
this.manage(key);
|
|
473
|
+
this.emit([]);
|
|
474
|
+
}
|
|
475
|
+
stop() {
|
|
476
|
+
this.followers.clear();
|
|
477
|
+
this.onDestroy.forEach(x => x());
|
|
478
|
+
}
|
|
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
|
|
496
|
+
});
|
|
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
|
|
506
|
+
});
|
|
507
|
+
}
|
|
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
|
+
};
|
|
522
|
+
}
|
|
523
|
+
addListener(listener) {
|
|
524
|
+
this.followers.add(listener);
|
|
525
|
+
return () => {
|
|
526
|
+
this.followers.delete(listener);
|
|
527
|
+
};
|
|
528
|
+
}
|
|
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);
|
|
536
|
+
}
|
|
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);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
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
|
+
};
|
|
578
|
+
}
|
|
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);
|
|
586
|
+
}
|
|
587
|
+
if (!cb) {
|
|
588
|
+
if (!ready) {
|
|
589
|
+
READY.add(control);
|
|
590
|
+
control.start();
|
|
591
|
+
}
|
|
592
|
+
return control;
|
|
593
|
+
}
|
|
594
|
+
if (!ready) {
|
|
595
|
+
let done;
|
|
596
|
+
control.requestUpdate(() => {
|
|
597
|
+
done = cb(control);
|
|
598
|
+
});
|
|
599
|
+
return () => done && done();
|
|
600
|
+
}
|
|
601
|
+
return cb(control);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function apply(fn, label) {
|
|
605
|
+
const name = label || fn.name || "pending";
|
|
606
|
+
const placeholder = Symbol(`${name} instruction`);
|
|
607
|
+
function setup(key) {
|
|
608
|
+
const { proxy, state, subject } = this;
|
|
609
|
+
const control = this;
|
|
610
|
+
PENDING.delete(placeholder);
|
|
611
|
+
delete subject[key];
|
|
612
|
+
let output = fn.call(this, key, this);
|
|
613
|
+
if (typeof output == "function")
|
|
614
|
+
output = { get: output };
|
|
615
|
+
if (typeof output != "object")
|
|
616
|
+
return;
|
|
617
|
+
if ("explicit" in output && output.explicit) {
|
|
618
|
+
defineProperty(subject, key, output);
|
|
619
|
+
return false;
|
|
620
|
+
}
|
|
621
|
+
if ("recursive" in output && output.recursive)
|
|
622
|
+
output = Object.assign(Object.assign({}, output), { get: recursive(this, key) });
|
|
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 });
|
|
645
|
+
}
|
|
646
|
+
PENDING.set(placeholder, setup);
|
|
647
|
+
return placeholder;
|
|
648
|
+
}
|
|
649
|
+
function recursive(source, key) {
|
|
650
|
+
return forSubscriber((parent, context) => {
|
|
651
|
+
let child;
|
|
652
|
+
function init() {
|
|
653
|
+
if (child) {
|
|
654
|
+
child.release();
|
|
655
|
+
parent.dependant.delete(child);
|
|
656
|
+
context.set(parent, undefined);
|
|
657
|
+
child = undefined;
|
|
658
|
+
}
|
|
659
|
+
const value = source.state[key];
|
|
660
|
+
if (value && CONTROL in value) {
|
|
661
|
+
child = new Subscriber(value, parent.onUpdate);
|
|
662
|
+
if (parent.active)
|
|
663
|
+
child.commit();
|
|
664
|
+
parent.dependant.add(child);
|
|
665
|
+
context.set(parent, child.proxy);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
init();
|
|
669
|
+
parent.watch[key] = init;
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
function forSubscriber(fn) {
|
|
673
|
+
const context = new WeakMap();
|
|
674
|
+
return (value, local) => {
|
|
675
|
+
if (!local)
|
|
676
|
+
return value;
|
|
677
|
+
if (!context.has(local)) {
|
|
678
|
+
const output = fn(local, context);
|
|
679
|
+
if (output) {
|
|
680
|
+
context.set(local, output);
|
|
681
|
+
return output;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return context.get(local);
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
const Oops$8 = issues({
|
|
689
|
+
DuplicateAction: (key) => `Invoked action ${key} but one is already active.`
|
|
690
|
+
});
|
|
691
|
+
function act(task) {
|
|
692
|
+
return apply(function act(key) {
|
|
693
|
+
let pending = false;
|
|
694
|
+
const invoke = (...args) => {
|
|
695
|
+
if (pending)
|
|
696
|
+
return Promise.reject(Oops$8.DuplicateAction(key));
|
|
697
|
+
pending = true;
|
|
698
|
+
this.update(key);
|
|
699
|
+
return new Promise(res => {
|
|
700
|
+
res(task.apply(this.subject, args));
|
|
701
|
+
}).finally(() => {
|
|
702
|
+
pending = false;
|
|
703
|
+
this.update(key);
|
|
704
|
+
});
|
|
705
|
+
};
|
|
706
|
+
this.state[key] = undefined;
|
|
707
|
+
defineProperty(invoke, "active", {
|
|
708
|
+
get: () => pending
|
|
709
|
+
});
|
|
710
|
+
return {
|
|
711
|
+
value: invoke,
|
|
712
|
+
set: false
|
|
713
|
+
};
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
|
|
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
|
+
}
|
|
741
|
+
}
|
|
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;
|
|
767
|
+
});
|
|
768
|
+
return {
|
|
769
|
+
get: access,
|
|
770
|
+
set: false,
|
|
771
|
+
value: array
|
|
772
|
+
};
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function ref(arg) {
|
|
777
|
+
return apply(function ref(key) {
|
|
778
|
+
let value = {};
|
|
779
|
+
if (typeof arg == "object") {
|
|
780
|
+
const source = control(arg);
|
|
781
|
+
for (const key in source.state)
|
|
782
|
+
defineLazy(value, key, createRef.bind(source, key));
|
|
783
|
+
}
|
|
784
|
+
else
|
|
785
|
+
value = createRef.call(this, key, arg);
|
|
786
|
+
return { value, explicit: true };
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
function createRef(key, cb) {
|
|
790
|
+
const refObjectFunction = this.ref(key, cb && createValueEffect(cb));
|
|
791
|
+
defineProperty(refObjectFunction, "current", {
|
|
792
|
+
set: refObjectFunction,
|
|
793
|
+
get: () => this.state[key]
|
|
794
|
+
});
|
|
795
|
+
return refObjectFunction;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
const Oops$7 = issues({
|
|
799
|
+
NonOptional: (Parent, key) => `Property ${Parent}.${key} is marked as required.`,
|
|
800
|
+
BadFactory: () => `Set instruction can only accept a factory or undefined.`,
|
|
801
|
+
ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
|
|
802
|
+
FactoryFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`
|
|
803
|
+
});
|
|
804
|
+
function set(factory, argument) {
|
|
805
|
+
return apply(function set(key) {
|
|
806
|
+
let set;
|
|
807
|
+
let get;
|
|
808
|
+
let suspense;
|
|
809
|
+
const required = argument === true || argument === undefined;
|
|
810
|
+
if (factory === undefined)
|
|
811
|
+
suspense = true;
|
|
812
|
+
else if (typeof factory === "function")
|
|
813
|
+
get = pendingFactory(this, key, factory, required);
|
|
814
|
+
else
|
|
815
|
+
throw Oops$7.BadFactory();
|
|
816
|
+
if (typeof argument == "function")
|
|
817
|
+
set = createValueEffect(argument);
|
|
818
|
+
else
|
|
819
|
+
set = (value) => {
|
|
820
|
+
if (value === undefined && required)
|
|
821
|
+
throw Oops$7.NonOptional(this.subject, key);
|
|
822
|
+
};
|
|
823
|
+
return {
|
|
824
|
+
set,
|
|
825
|
+
get,
|
|
826
|
+
suspense
|
|
827
|
+
};
|
|
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
|
+
};
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
const Parent = new WeakMap();
|
|
883
|
+
const Oops$6 = issues({
|
|
884
|
+
ParentRequired: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
|
|
885
|
+
UnexpectedParent: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
|
|
886
|
+
});
|
|
887
|
+
function parent(Expects, required) {
|
|
888
|
+
return apply(function parent() {
|
|
889
|
+
const child = this.subject;
|
|
890
|
+
const value = Parent.get(child);
|
|
891
|
+
const expected = Expects.name;
|
|
892
|
+
if (!value) {
|
|
893
|
+
if (required !== false)
|
|
894
|
+
throw Oops$6.ParentRequired(expected, child);
|
|
895
|
+
}
|
|
896
|
+
else if (!(value instanceof Expects))
|
|
897
|
+
throw Oops$6.UnexpectedParent(expected, child, value);
|
|
898
|
+
return { value };
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
const Oops$5 = issues({
|
|
903
|
+
BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`,
|
|
904
|
+
});
|
|
905
|
+
function use$1(input, argument) {
|
|
906
|
+
return apply(function use(key) {
|
|
907
|
+
const { subject } = this;
|
|
908
|
+
const onUpdate = (next) => {
|
|
909
|
+
this.state[key] = next;
|
|
910
|
+
if (next) {
|
|
911
|
+
Parent.set(next, subject);
|
|
912
|
+
control(next);
|
|
913
|
+
}
|
|
914
|
+
if (typeof argument == "function")
|
|
915
|
+
argument(next);
|
|
916
|
+
return true;
|
|
917
|
+
};
|
|
918
|
+
if (input) {
|
|
919
|
+
const value = Model.isTypeof(input)
|
|
920
|
+
? new input() :
|
|
921
|
+
typeof input === "object"
|
|
922
|
+
? input :
|
|
923
|
+
typeof input === "function"
|
|
924
|
+
? input()
|
|
925
|
+
: null;
|
|
926
|
+
if (value === null)
|
|
927
|
+
throw Oops$5.BadArgument(typeof input);
|
|
928
|
+
if (value)
|
|
929
|
+
onUpdate(value);
|
|
930
|
+
}
|
|
931
|
+
return {
|
|
932
|
+
set: onUpdate,
|
|
933
|
+
recursive: true
|
|
934
|
+
};
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
class Lookup {
|
|
939
|
+
constructor() {
|
|
940
|
+
this.table = new Map();
|
|
941
|
+
}
|
|
942
|
+
get local() {
|
|
943
|
+
return [
|
|
944
|
+
...new Set(getOwnPropertySymbols(this).map(symbol => this[symbol]))
|
|
945
|
+
];
|
|
946
|
+
}
|
|
947
|
+
key(T) {
|
|
948
|
+
let key = this.table.get(T);
|
|
949
|
+
if (!key) {
|
|
950
|
+
key = Symbol(T.name);
|
|
951
|
+
this.table.set(T, key);
|
|
952
|
+
}
|
|
953
|
+
return key;
|
|
954
|
+
}
|
|
955
|
+
get(T) {
|
|
956
|
+
return this[this.key(T)];
|
|
957
|
+
}
|
|
958
|
+
push(I) {
|
|
959
|
+
const next = create(this);
|
|
960
|
+
if (I instanceof Model || typeof I == "function")
|
|
961
|
+
next.register(I);
|
|
962
|
+
else
|
|
963
|
+
for (const i of values(I))
|
|
964
|
+
next.register(i);
|
|
965
|
+
return next;
|
|
966
|
+
}
|
|
967
|
+
register(I) {
|
|
968
|
+
let writable = true;
|
|
969
|
+
let T;
|
|
970
|
+
if (I instanceof Model) {
|
|
971
|
+
T = I.constructor;
|
|
972
|
+
writable = false;
|
|
973
|
+
}
|
|
974
|
+
else {
|
|
975
|
+
T = I;
|
|
976
|
+
I = I.create();
|
|
977
|
+
}
|
|
978
|
+
do {
|
|
979
|
+
const key = this.key(T);
|
|
980
|
+
const conflict = this.hasOwnProperty(key);
|
|
981
|
+
defineProperty(this, key, {
|
|
982
|
+
value: conflict ? null : I,
|
|
983
|
+
configurable: true,
|
|
984
|
+
writable
|
|
985
|
+
});
|
|
986
|
+
T = getPrototypeOf(T);
|
|
987
|
+
} while (T !== Model);
|
|
988
|
+
return I;
|
|
989
|
+
}
|
|
990
|
+
pop() {
|
|
991
|
+
for (const key of getOwnPropertySymbols(this)) {
|
|
992
|
+
const entry = getOwnPropertyDescriptor(this, key);
|
|
993
|
+
if (entry.writable && entry.value)
|
|
994
|
+
entry.value.destroy();
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
const LookupContext = React__default["default"].createContext(new Lookup());
|
|
1000
|
+
const useLookup = () => React__default["default"].useContext(LookupContext);
|
|
1001
|
+
const Oops$4 = issues({
|
|
1002
|
+
NothingInContext: (name) => `Couldn't find ${name} in context; did you forget to use a Provider?`
|
|
1003
|
+
});
|
|
1004
|
+
function useInContext(Type, arg) {
|
|
1005
|
+
const instance = useLookup().get(Type);
|
|
1006
|
+
if (!instance && arg !== false)
|
|
1007
|
+
throw Oops$4.NothingInContext(Type.name);
|
|
1008
|
+
return typeof arg == "string" ?
|
|
1009
|
+
instance[arg] :
|
|
1010
|
+
instance;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
const Oops$3 = issues({
|
|
1014
|
+
CantAttachGlobal: (parent, child) => `Global '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
|
|
1015
|
+
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.`
|
|
1016
|
+
});
|
|
1017
|
+
const PendingContext = new WeakMap();
|
|
1018
|
+
const ContextWasUsed = new WeakMap();
|
|
1019
|
+
function tap(type, argument) {
|
|
1020
|
+
return apply(function tap(key) {
|
|
1021
|
+
if ("set" in type)
|
|
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
|
+
});
|
|
1039
|
+
return {
|
|
1040
|
+
recursive: true,
|
|
1041
|
+
suspense: true
|
|
1042
|
+
};
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
function usePeerContext(subject) {
|
|
1046
|
+
if (ContextWasUsed.has(subject)) {
|
|
1047
|
+
if (ContextWasUsed.get(subject))
|
|
1048
|
+
useLookup();
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
const pending = PendingContext.get(subject);
|
|
1052
|
+
if (pending) {
|
|
1053
|
+
const local = useLookup();
|
|
1054
|
+
for (const init of pending)
|
|
1055
|
+
init(local);
|
|
1056
|
+
PendingContext.delete(subject);
|
|
1057
|
+
}
|
|
1058
|
+
ContextWasUsed.set(subject, !!pending);
|
|
1059
|
+
}
|
|
1060
|
+
function getPending(subject) {
|
|
1061
|
+
let pending = PendingContext.get(subject);
|
|
1062
|
+
if (!pending)
|
|
1063
|
+
PendingContext.set(subject, pending = []);
|
|
1064
|
+
return pending;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
function use(init) {
|
|
1068
|
+
const [state, update] = React__default["default"].useState(() => [
|
|
1069
|
+
init(() => update(state.concat()))
|
|
1070
|
+
]);
|
|
1071
|
+
return state[0];
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
function useModel(source, arg, arg2) {
|
|
1075
|
+
const instance = React__default["default"].useMemo(() => {
|
|
1076
|
+
const cb = arg2 || arg;
|
|
1077
|
+
const instance = typeof source == "function" ?
|
|
1078
|
+
source.prototype instanceof Model ?
|
|
1079
|
+
new source() :
|
|
1080
|
+
source() :
|
|
1081
|
+
source;
|
|
1082
|
+
control(instance);
|
|
1083
|
+
if (typeof cb == "function")
|
|
1084
|
+
cb(instance);
|
|
1085
|
+
return instance;
|
|
1086
|
+
}, []);
|
|
1087
|
+
if (Array.isArray(arg)) {
|
|
1088
|
+
const update = React.useState(0)[1];
|
|
1089
|
+
React__default["default"].useLayoutEffect(() => {
|
|
1090
|
+
if (arg.length && instance instanceof Model)
|
|
1091
|
+
instance.on(arg, () => update(x => x + 1), true);
|
|
1092
|
+
return () => {
|
|
1093
|
+
if (Model.isTypeof(source))
|
|
1094
|
+
instance.destroy();
|
|
1095
|
+
};
|
|
1096
|
+
}, []);
|
|
1097
|
+
return instance;
|
|
1098
|
+
}
|
|
1099
|
+
else {
|
|
1100
|
+
const local = use(refresh => (new Subscriber(instance, () => refresh)));
|
|
1101
|
+
if (typeof arg == "object") {
|
|
1102
|
+
local.active = false;
|
|
1103
|
+
if (typeof arg2 !== "object")
|
|
1104
|
+
arg2 = getOwnPropertyNames(instance);
|
|
1105
|
+
for (const key of arg2)
|
|
1106
|
+
if (key in arg)
|
|
1107
|
+
instance[key] = arg[key];
|
|
1108
|
+
React__default["default"].useLayoutEffect(() => {
|
|
1109
|
+
local.active = true;
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
React__default["default"].useLayoutEffect(() => {
|
|
1113
|
+
local.commit();
|
|
1114
|
+
return () => {
|
|
1115
|
+
local.release();
|
|
1116
|
+
if (Model.isTypeof(source))
|
|
1117
|
+
instance.destroy();
|
|
1118
|
+
};
|
|
1119
|
+
}, []);
|
|
1120
|
+
return local.proxy;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
function useFrom(target, compute, suspend) {
|
|
1125
|
+
const local = use(refresh => {
|
|
1126
|
+
const sub = new Subscriber(target, () => update);
|
|
1127
|
+
const spy = sub.proxy;
|
|
1128
|
+
let value = compute.call(spy, spy);
|
|
1129
|
+
let update;
|
|
1130
|
+
let retry;
|
|
1131
|
+
const set = (next) => {
|
|
1132
|
+
value = next;
|
|
1133
|
+
if (retry) {
|
|
1134
|
+
retry();
|
|
1135
|
+
retry = undefined;
|
|
1136
|
+
}
|
|
1137
|
+
else
|
|
1138
|
+
refresh();
|
|
1139
|
+
};
|
|
1140
|
+
if (value instanceof Promise) {
|
|
1141
|
+
value.then(set);
|
|
1142
|
+
value = undefined;
|
|
1143
|
+
}
|
|
1144
|
+
else {
|
|
1145
|
+
sub.commit();
|
|
1146
|
+
update = () => {
|
|
1147
|
+
const next = compute.call(spy, spy);
|
|
1148
|
+
if (value != next)
|
|
1149
|
+
set(next);
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
defineProperty(sub, "proxy", {
|
|
1153
|
+
get() {
|
|
1154
|
+
if (value === undefined && suspend)
|
|
1155
|
+
throw new Promise(res => retry = res);
|
|
1156
|
+
return value;
|
|
1157
|
+
}
|
|
1158
|
+
});
|
|
1159
|
+
return sub;
|
|
1160
|
+
});
|
|
1161
|
+
React__default["default"].useLayoutEffect(() => local.release, []);
|
|
1162
|
+
return local.proxy;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
function useTap(source, path, expect) {
|
|
1166
|
+
const instance = React.useMemo(() => {
|
|
1167
|
+
if (typeof source == "object")
|
|
1168
|
+
return () => source;
|
|
1169
|
+
if ("get" in source)
|
|
1170
|
+
return () => source.get();
|
|
1171
|
+
if ("prototype" in source)
|
|
1172
|
+
return () => useInContext(source);
|
|
1173
|
+
else
|
|
1174
|
+
return source;
|
|
1175
|
+
}, [])();
|
|
1176
|
+
if (typeof path == "function")
|
|
1177
|
+
return useFrom(instance, path, expect);
|
|
1178
|
+
const local = use(refresh => {
|
|
1179
|
+
const parent = control(instance);
|
|
1180
|
+
const sub = new Subscriber(parent, () => refresh);
|
|
1181
|
+
if (typeof path == "string") {
|
|
1182
|
+
const source = sub.proxy;
|
|
1183
|
+
defineProperty(sub, "proxy", {
|
|
1184
|
+
get() {
|
|
1185
|
+
const value = source[path];
|
|
1186
|
+
if (value === undefined && expect)
|
|
1187
|
+
throw suspend(parent, path);
|
|
1188
|
+
return value;
|
|
1189
|
+
}
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
return sub;
|
|
1193
|
+
});
|
|
1194
|
+
React__default["default"].useLayoutEffect(local.commit, []);
|
|
1195
|
+
return local.proxy;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
/* istanbul ignore file */
|
|
1199
|
+
class MVC extends Model {
|
|
1200
|
+
tap(path, expect) {
|
|
1201
|
+
return useTap(this, path, expect);
|
|
1202
|
+
}
|
|
1203
|
+
use(arg, callback) {
|
|
1204
|
+
return useModel(this, arg, callback);
|
|
1205
|
+
}
|
|
1206
|
+
static get(required) {
|
|
1207
|
+
return useInContext(this, required);
|
|
1208
|
+
}
|
|
1209
|
+
static tap(key, expect) {
|
|
1210
|
+
return useTap(this, key, expect);
|
|
1211
|
+
}
|
|
1212
|
+
static new(arg) {
|
|
1213
|
+
const instance = React__default["default"].useMemo(() => {
|
|
1214
|
+
const instance = new this();
|
|
1215
|
+
control(instance);
|
|
1216
|
+
if (typeof arg == "function")
|
|
1217
|
+
arg(instance);
|
|
1218
|
+
else if (arg)
|
|
1219
|
+
getOwnPropertyNames(instance).forEach(key => {
|
|
1220
|
+
if (key in arg)
|
|
1221
|
+
instance[key] = arg[key];
|
|
1222
|
+
});
|
|
1223
|
+
return instance;
|
|
1224
|
+
}, []);
|
|
1225
|
+
usePeerContext(instance.get);
|
|
1226
|
+
return instance;
|
|
1227
|
+
}
|
|
1228
|
+
static use(a, b) {
|
|
1229
|
+
const instance = useModel(this, a, b);
|
|
1230
|
+
usePeerContext(instance.get);
|
|
1231
|
+
return instance;
|
|
1232
|
+
}
|
|
1233
|
+
/**
|
|
1234
|
+
* @deprecated consider doing this manually - not worth minor efficiency gain over using.
|
|
1235
|
+
*/
|
|
1236
|
+
static uses(apply, keys) {
|
|
1237
|
+
return this.use(instance => {
|
|
1238
|
+
instance.import(apply, keys);
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* @deprecated is now replaced by overload of `use` method.
|
|
1243
|
+
*/
|
|
1244
|
+
static using(apply, keys) {
|
|
1245
|
+
return this.use(apply, keys);
|
|
1246
|
+
}
|
|
1247
|
+
static meta(path, expect) {
|
|
1248
|
+
return useTap(() => this, path, expect);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
const Oops$2 = issues({
|
|
1253
|
+
BadConsumerProps: () => `Provider expects either a render function, 'get' or 'has' props.`
|
|
1254
|
+
});
|
|
1255
|
+
function Consumer(props) {
|
|
1256
|
+
const { get, has, children, of: Control } = props;
|
|
1257
|
+
if (typeof children == "function")
|
|
1258
|
+
return children(useTap(Control));
|
|
1259
|
+
const callback = has || get;
|
|
1260
|
+
if (typeof callback == "function")
|
|
1261
|
+
callback(useInContext(Control, !!has));
|
|
1262
|
+
else
|
|
1263
|
+
throw Oops$2.BadConsumerProps();
|
|
1264
|
+
return null;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
const Oops$1 = issues({
|
|
1268
|
+
NoProviderType: () => `Provider 'of' prop must be Model, typeof Model or a collection of them.`
|
|
1269
|
+
});
|
|
1270
|
+
function Provider(props) {
|
|
1271
|
+
const context = useNewContext(props.of);
|
|
1272
|
+
const render = props.children;
|
|
1273
|
+
useAppliedProps(context, props);
|
|
1274
|
+
React__default["default"].useLayoutEffect(() => () => context.pop(), []);
|
|
1275
|
+
return React__default["default"].createElement(LookupContext.Provider, { value: context }, typeof render == "function"
|
|
1276
|
+
? React__default["default"].createElement(RenderFunction, { context, render })
|
|
1277
|
+
: render);
|
|
1278
|
+
}
|
|
1279
|
+
function useNewContext(inject) {
|
|
1280
|
+
const from = useLookup();
|
|
1281
|
+
return React__default["default"].useMemo(() => {
|
|
1282
|
+
if (!inject)
|
|
1283
|
+
throw Oops$1.NoProviderType();
|
|
1284
|
+
const context = from.push(inject);
|
|
1285
|
+
for (const instance of context.local)
|
|
1286
|
+
if (instance)
|
|
1287
|
+
for (const apply of getPending(instance))
|
|
1288
|
+
apply(context);
|
|
1289
|
+
return context;
|
|
1290
|
+
}, []);
|
|
1291
|
+
}
|
|
1292
|
+
function useAppliedProps(within, props) {
|
|
1293
|
+
const update = React__default["default"].useMemo(() => {
|
|
1294
|
+
const targets = within.local;
|
|
1295
|
+
return function integrate(props) {
|
|
1296
|
+
for (const [key, value] of entries(props))
|
|
1297
|
+
if (key != "of" && key != "children")
|
|
1298
|
+
for (const into of targets)
|
|
1299
|
+
if (into && key in into)
|
|
1300
|
+
into[key] = value;
|
|
1301
|
+
};
|
|
1302
|
+
}, []);
|
|
1303
|
+
update(props);
|
|
1304
|
+
}
|
|
1305
|
+
function RenderFunction(props) {
|
|
1306
|
+
const hook = use(refresh => {
|
|
1307
|
+
const targets = props.context.local;
|
|
1308
|
+
if (targets.length == 1)
|
|
1309
|
+
return new Subscriber(targets[0], () => refresh);
|
|
1310
|
+
return {};
|
|
1311
|
+
});
|
|
1312
|
+
if (hook.commit)
|
|
1313
|
+
React__default["default"].useLayoutEffect(hook.commit, []);
|
|
1314
|
+
return props.render(hook.proxy);
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
const Oops = issues({
|
|
1318
|
+
GlobalDoesNotExist: (name) => `Tried to access singleton ${name}, but none exist! Did you forget to initialize?\nCall ${name}.create() before attempting to access, or consider using ${name}.use() instead.`,
|
|
1319
|
+
GlobalExists: (name) => `Shared instance of ${name} already exists! Consider unmounting existing, or use ${name}.reset() to force-delete it.`
|
|
1320
|
+
});
|
|
1321
|
+
const Active = new WeakMap();
|
|
1322
|
+
class Global extends MVC {
|
|
1323
|
+
static create(...args) {
|
|
1324
|
+
const Type = this;
|
|
1325
|
+
if (Active.has(Type))
|
|
1326
|
+
throw Oops.GlobalExists(this.name);
|
|
1327
|
+
const instance = super.create(...args);
|
|
1328
|
+
Active.set(Type, instance);
|
|
1329
|
+
return instance;
|
|
1330
|
+
}
|
|
1331
|
+
/**
|
|
1332
|
+
* Update the active instance of this class.
|
|
1333
|
+
* Returns a thenable; resolves after successful update.
|
|
1334
|
+
* If instance does not already exist, one will be created.
|
|
1335
|
+
*/
|
|
1336
|
+
static set(updates) {
|
|
1337
|
+
const instance = Active.get(this) || this.create();
|
|
1338
|
+
instance.import(updates);
|
|
1339
|
+
return instance.update(false);
|
|
1340
|
+
}
|
|
1341
|
+
static get(arg) {
|
|
1342
|
+
const instance = Active.get(this);
|
|
1343
|
+
if (!instance && arg !== false)
|
|
1344
|
+
throw Oops.GlobalDoesNotExist(this.name);
|
|
1345
|
+
return (typeof arg == "string" ?
|
|
1346
|
+
instance[arg] :
|
|
1347
|
+
instance);
|
|
1348
|
+
}
|
|
1349
|
+
/** Destroy current instance of Global, if it exists. */
|
|
1350
|
+
static reset() {
|
|
1351
|
+
const current = Active.get(this);
|
|
1352
|
+
if (current)
|
|
1353
|
+
current.destroy();
|
|
1354
|
+
}
|
|
1355
|
+
destroy() {
|
|
1356
|
+
super.destroy();
|
|
1357
|
+
Active.delete(this.constructor);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
exports.CONTROL = CONTROL;
|
|
1362
|
+
exports.Consumer = Consumer;
|
|
1363
|
+
exports.Global = Global;
|
|
1364
|
+
exports.LOCAL = LOCAL;
|
|
1365
|
+
exports.Model = MVC;
|
|
1366
|
+
exports.Provider = Provider;
|
|
1367
|
+
exports.STATE = STATE;
|
|
1368
|
+
exports.WHY = WHY;
|
|
1369
|
+
exports.act = act;
|
|
1370
|
+
exports.apply = apply;
|
|
1371
|
+
exports.array = array;
|
|
1372
|
+
exports["default"] = MVC;
|
|
1373
|
+
exports.from = from;
|
|
1374
|
+
exports.parent = parent;
|
|
1375
|
+
exports.ref = ref;
|
|
1376
|
+
exports.set = set;
|
|
1377
|
+
exports.tap = tap;
|
|
1378
|
+
exports.use = use$1;
|
|
1379
|
+
exports.useFrom = useFrom;
|
|
1380
|
+
exports.useInContext = useInContext;
|
|
1381
|
+
exports.useModel = useModel;
|
|
1382
|
+
exports.useTap = useTap;
|
|
1383
|
+
//# sourceMappingURL=index.js.map
|