@expressive/mvc 1.0.0-beta.22 → 1.0.0-beta.26
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.esm.js +1220 -0
- package/lib/index.esm.js.map +1 -0
- package/lib/index.js +119 -68
- package/lib/index.js.map +1 -0
- package/package.json +9 -12
- package/types/context.d.ts +4 -6
- package/types/instructions.d.ts +66 -35
- package/types/model.d.ts +22 -7
- package/types/types.d.ts +2 -2
package/lib/index.esm.js
ADDED
|
@@ -0,0 +1,1220 @@
|
|
|
1
|
+
import { useMemo, useLayoutEffect, useState, createContext, useContext, createElement } from 'react';
|
|
2
|
+
|
|
3
|
+
class Issue extends Error {
|
|
4
|
+
constructor() {
|
|
5
|
+
super(...arguments);
|
|
6
|
+
// drop first line (error factory) in stack trace.
|
|
7
|
+
this.stack = this.stack.replace(/\n.+/, "");
|
|
8
|
+
}
|
|
9
|
+
warn() {
|
|
10
|
+
console.warn(this.message);
|
|
11
|
+
}
|
|
12
|
+
throw() {
|
|
13
|
+
throw this;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function issues(register) {
|
|
17
|
+
const Library = {};
|
|
18
|
+
for (const name in register)
|
|
19
|
+
Library[name] = (...args) => new Issue(register[name].apply(null, args));
|
|
20
|
+
return Library;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const Oops$9 = issues({
|
|
24
|
+
BadEffectCallback: () => `Callback for effect-callback may only return a function.`
|
|
25
|
+
});
|
|
26
|
+
const { assign, create, defineProperty, entries, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor, getOwnPropertyDescriptors, getOwnPropertySymbols, keys: keys$1, values } = Object;
|
|
27
|
+
function define(target, key, value) {
|
|
28
|
+
defineProperty(target, key, { value });
|
|
29
|
+
}
|
|
30
|
+
function defineLazy(object, property, init) {
|
|
31
|
+
defineProperty(object, property, {
|
|
32
|
+
configurable: true,
|
|
33
|
+
get() {
|
|
34
|
+
const value = init.call(this);
|
|
35
|
+
defineProperty(this, property, { value });
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function setAlias(func, displayName) {
|
|
41
|
+
assign(func, { displayName });
|
|
42
|
+
}
|
|
43
|
+
function createEffect(callback) {
|
|
44
|
+
let unSet;
|
|
45
|
+
return function (value) {
|
|
46
|
+
if (typeof unSet == "function")
|
|
47
|
+
unSet();
|
|
48
|
+
unSet = callback.call(this, value);
|
|
49
|
+
if (unSet instanceof Promise)
|
|
50
|
+
unSet = undefined;
|
|
51
|
+
if (unSet && typeof unSet !== "function")
|
|
52
|
+
throw Oops$9.BadEffectCallback();
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function createValueEffect(callback) {
|
|
56
|
+
let unSet;
|
|
57
|
+
return function (value) {
|
|
58
|
+
if (typeof unSet == "function")
|
|
59
|
+
unSet(value);
|
|
60
|
+
const out = callback.call(this, value);
|
|
61
|
+
if (typeof out == "boolean")
|
|
62
|
+
return out;
|
|
63
|
+
if (!out || out instanceof Promise)
|
|
64
|
+
return;
|
|
65
|
+
if (typeof out == "function")
|
|
66
|
+
unSet = out;
|
|
67
|
+
else
|
|
68
|
+
throw Oops$9.BadEffectCallback();
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function select(source, select) {
|
|
72
|
+
const proxy = {};
|
|
73
|
+
for (const k in source)
|
|
74
|
+
proxy[k] = k;
|
|
75
|
+
return select(proxy);
|
|
76
|
+
}
|
|
77
|
+
function selectRecursive(using, keys) {
|
|
78
|
+
const found = new Set();
|
|
79
|
+
const spy = {};
|
|
80
|
+
for (const key of new Set(keys))
|
|
81
|
+
defineProperty(spy, key, {
|
|
82
|
+
get() {
|
|
83
|
+
found.add(key);
|
|
84
|
+
return spy;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
using(spy);
|
|
88
|
+
return Array.from(found);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
class Subscriber {
|
|
92
|
+
constructor(parent, onUpdate) {
|
|
93
|
+
this.onUpdate = onUpdate;
|
|
94
|
+
this.active = false;
|
|
95
|
+
this.follows = {};
|
|
96
|
+
this.dependant = new Set();
|
|
97
|
+
if (!(parent instanceof Controller))
|
|
98
|
+
parent = manage(parent);
|
|
99
|
+
this.parent = parent;
|
|
100
|
+
this.source = parent.subject;
|
|
101
|
+
this.proxy = create(parent.subject);
|
|
102
|
+
define(this.proxy, LOCAL, this);
|
|
103
|
+
for (const key in parent.state)
|
|
104
|
+
this.spy(key);
|
|
105
|
+
}
|
|
106
|
+
spy(key) {
|
|
107
|
+
const { proxy, source } = this;
|
|
108
|
+
const { set } = getOwnPropertyDescriptor(source, key);
|
|
109
|
+
const intercept = () => {
|
|
110
|
+
this.follow(key);
|
|
111
|
+
delete proxy[key];
|
|
112
|
+
return proxy[key];
|
|
113
|
+
};
|
|
114
|
+
setAlias(intercept, `tap ${key}`);
|
|
115
|
+
defineProperty(proxy, key, {
|
|
116
|
+
get: intercept,
|
|
117
|
+
set,
|
|
118
|
+
configurable: true,
|
|
119
|
+
enumerable: true
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
follow(key, cb) {
|
|
123
|
+
this.follows[key] = cb || this.onUpdate;
|
|
124
|
+
}
|
|
125
|
+
commit() {
|
|
126
|
+
const { dependant, follows, parent } = this;
|
|
127
|
+
const onDone = parent.addListener(follows);
|
|
128
|
+
this.active = true;
|
|
129
|
+
dependant.forEach(x => x.commit());
|
|
130
|
+
return this.release = () => {
|
|
131
|
+
dependant.forEach(x => x.release());
|
|
132
|
+
onDone();
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const Oops$8 = issues({
|
|
138
|
+
ComputeFailed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
|
|
139
|
+
ComputedEarly: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
|
|
140
|
+
});
|
|
141
|
+
const ComputedInit = new WeakSet();
|
|
142
|
+
const ComputedInfo = new WeakMap();
|
|
143
|
+
const ComputedUsed = new WeakMap();
|
|
144
|
+
const ComputedKeys = new WeakMap();
|
|
145
|
+
function prepare(parent, key, source, setter, getter) {
|
|
146
|
+
let sub;
|
|
147
|
+
const { state, subject } = parent;
|
|
148
|
+
const info = { key, parent, priority: 1 };
|
|
149
|
+
const current = getter
|
|
150
|
+
? () => getter.call(subject, state[key], key)
|
|
151
|
+
: () => state[key];
|
|
152
|
+
const prioritize = register(parent, key, info);
|
|
153
|
+
function compute(initial) {
|
|
154
|
+
try {
|
|
155
|
+
return setter.call(sub.proxy, sub.proxy);
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
Oops$8.ComputeFailed(subject, key, !!initial).warn();
|
|
159
|
+
throw err;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function update() {
|
|
163
|
+
let value;
|
|
164
|
+
try {
|
|
165
|
+
value = compute(false);
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
console.error(e);
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
if (state[key] !== value) {
|
|
172
|
+
parent.update(key, value);
|
|
173
|
+
return value;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function create(early) {
|
|
178
|
+
sub = new Subscriber(source(), update);
|
|
179
|
+
defineProperty(state, key, {
|
|
180
|
+
value: undefined,
|
|
181
|
+
writable: true
|
|
182
|
+
});
|
|
183
|
+
defineProperty(subject, key, {
|
|
184
|
+
enumerable: true,
|
|
185
|
+
configurable: true,
|
|
186
|
+
get: current
|
|
187
|
+
});
|
|
188
|
+
try {
|
|
189
|
+
state[key] = compute(true);
|
|
190
|
+
}
|
|
191
|
+
catch (e) {
|
|
192
|
+
if (early)
|
|
193
|
+
Oops$8.ComputedEarly(key).warn();
|
|
194
|
+
throw e;
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
sub.commit();
|
|
198
|
+
for (const key in sub.follows)
|
|
199
|
+
prioritize(key);
|
|
200
|
+
}
|
|
201
|
+
return current();
|
|
202
|
+
}
|
|
203
|
+
setAlias(update, `try ${key}`);
|
|
204
|
+
setAlias(create, `new ${key}`);
|
|
205
|
+
setAlias(setter, `run ${key}`);
|
|
206
|
+
ComputedInit.add(create);
|
|
207
|
+
ComputedInfo.set(update, info);
|
|
208
|
+
for (const on of [state, subject])
|
|
209
|
+
defineProperty(on, key, {
|
|
210
|
+
get: create,
|
|
211
|
+
configurable: true,
|
|
212
|
+
enumerable: true
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
function register(on, key, info) {
|
|
216
|
+
let register = ComputedUsed.get(on);
|
|
217
|
+
if (!register)
|
|
218
|
+
ComputedUsed.set(on, register = new Map());
|
|
219
|
+
register.set(key, info);
|
|
220
|
+
return (key) => {
|
|
221
|
+
const peer = register.get(key);
|
|
222
|
+
if (peer && peer.priority >= info.priority)
|
|
223
|
+
info.priority = peer.priority + 1;
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function ensure(on, keys) {
|
|
227
|
+
for (const key of keys) {
|
|
228
|
+
const desc = getOwnPropertyDescriptor(on.subject, key);
|
|
229
|
+
const getter = desc && desc.get;
|
|
230
|
+
if (ComputedInit.has(getter))
|
|
231
|
+
getter(true);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function capture(on, request) {
|
|
235
|
+
const compute = ComputedInfo.get(request);
|
|
236
|
+
const callback = request;
|
|
237
|
+
if (!compute)
|
|
238
|
+
return;
|
|
239
|
+
let pending = ComputedKeys.get(on);
|
|
240
|
+
if (!pending)
|
|
241
|
+
ComputedKeys.set(on, pending = []);
|
|
242
|
+
if (compute.parent !== on)
|
|
243
|
+
callback();
|
|
244
|
+
else {
|
|
245
|
+
const queue = pending.findIndex(peer => compute.priority > ComputedInfo.get(peer).priority);
|
|
246
|
+
pending.splice(queue + 1, 0, callback);
|
|
247
|
+
}
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
function flush(on) {
|
|
251
|
+
const handled = on.frame;
|
|
252
|
+
let pending = ComputedKeys.get(on);
|
|
253
|
+
if (pending) {
|
|
254
|
+
while (pending.length) {
|
|
255
|
+
const compute = pending.shift();
|
|
256
|
+
const { key } = ComputedInfo.get(compute);
|
|
257
|
+
if (!handled.has(key))
|
|
258
|
+
compute();
|
|
259
|
+
}
|
|
260
|
+
ComputedKeys.delete(on);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const Lifecycle = {
|
|
265
|
+
WILL_RENDER: "willRender",
|
|
266
|
+
WILL_UPDATE: "willUpdate",
|
|
267
|
+
WILL_MOUNT: "willMount",
|
|
268
|
+
WILL_UNMOUNT: "willUnmount",
|
|
269
|
+
DID_MOUNT: "didMount"
|
|
270
|
+
};
|
|
271
|
+
const lifecycleEvents = [
|
|
272
|
+
"didCreate",
|
|
273
|
+
"willDestroy",
|
|
274
|
+
...values(Lifecycle)
|
|
275
|
+
];
|
|
276
|
+
function lifecycle(prefix) {
|
|
277
|
+
const type = eventsFor(prefix);
|
|
278
|
+
return function useLifecycle(sub, tag) {
|
|
279
|
+
const emit = useMemo(() => {
|
|
280
|
+
const subject = sub.source;
|
|
281
|
+
const id = typeof tag == "function" ? tag(subject) : tag;
|
|
282
|
+
return (name) => {
|
|
283
|
+
for (const key of [name, type(name)])
|
|
284
|
+
subject.update(key, id);
|
|
285
|
+
};
|
|
286
|
+
}, [sub, tag]);
|
|
287
|
+
emit(Lifecycle.WILL_RENDER);
|
|
288
|
+
emit(sub.active
|
|
289
|
+
? Lifecycle.WILL_UPDATE
|
|
290
|
+
: Lifecycle.WILL_MOUNT);
|
|
291
|
+
useLayoutEffect(() => {
|
|
292
|
+
sub.commit();
|
|
293
|
+
emit(Lifecycle.DID_MOUNT);
|
|
294
|
+
return () => {
|
|
295
|
+
emit(Lifecycle.WILL_UNMOUNT);
|
|
296
|
+
sub.release();
|
|
297
|
+
};
|
|
298
|
+
}, [sub]);
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function eventsFor(prefix) {
|
|
302
|
+
const map = new Map();
|
|
303
|
+
for (const name of values(Lifecycle)) {
|
|
304
|
+
const alias = prefix + name[0].toUpperCase() + name.slice(1);
|
|
305
|
+
lifecycleEvents.push(alias);
|
|
306
|
+
map.set(name, alias);
|
|
307
|
+
}
|
|
308
|
+
return (e) => map.get(e);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const CONTROL = Symbol("control");
|
|
312
|
+
const LOCAL = Symbol("local");
|
|
313
|
+
const STATE = Symbol("state");
|
|
314
|
+
function manage(src) {
|
|
315
|
+
return src[CONTROL];
|
|
316
|
+
}
|
|
317
|
+
const Pending = new Map();
|
|
318
|
+
function apply(fn, label) {
|
|
319
|
+
const name = label || fn.name || "pending";
|
|
320
|
+
const placeholder = Symbol(`${name} instruction`);
|
|
321
|
+
function setup(key) {
|
|
322
|
+
let output = fn.call(this, key, this);
|
|
323
|
+
if (typeof output == "function") {
|
|
324
|
+
const getter = output;
|
|
325
|
+
output = Object.assign(Object.assign({}, getOwnPropertyDescriptor(this.subject, key)), { get() {
|
|
326
|
+
return getter(this[LOCAL]);
|
|
327
|
+
} });
|
|
328
|
+
}
|
|
329
|
+
if (output)
|
|
330
|
+
defineProperty(this.subject, key, output);
|
|
331
|
+
}
|
|
332
|
+
Pending.set(placeholder, setup);
|
|
333
|
+
return placeholder;
|
|
334
|
+
}
|
|
335
|
+
class Controller {
|
|
336
|
+
constructor(subject) {
|
|
337
|
+
this.subject = subject;
|
|
338
|
+
this.state = {};
|
|
339
|
+
this.frame = new Set();
|
|
340
|
+
this.handles = new Set();
|
|
341
|
+
this.waiting = [];
|
|
342
|
+
}
|
|
343
|
+
static setup(onto) {
|
|
344
|
+
const control = new this(onto);
|
|
345
|
+
defineLazy(onto, CONTROL, () => {
|
|
346
|
+
onto[STATE] = control.state;
|
|
347
|
+
control.start();
|
|
348
|
+
if (onto.didCreate)
|
|
349
|
+
onto.didCreate();
|
|
350
|
+
return control;
|
|
351
|
+
});
|
|
352
|
+
return control;
|
|
353
|
+
}
|
|
354
|
+
get pending() {
|
|
355
|
+
return this.frame.size > 0;
|
|
356
|
+
}
|
|
357
|
+
start() {
|
|
358
|
+
const { subject } = this;
|
|
359
|
+
for (const key in subject) {
|
|
360
|
+
const desc = getOwnPropertyDescriptor(subject, key);
|
|
361
|
+
if (desc && "value" in desc) {
|
|
362
|
+
const { value } = desc;
|
|
363
|
+
const instruction = Pending.get(value);
|
|
364
|
+
if (instruction) {
|
|
365
|
+
Pending.delete(value);
|
|
366
|
+
delete subject[key];
|
|
367
|
+
instruction.call(this, key, this);
|
|
368
|
+
}
|
|
369
|
+
else if (typeof value !== "function" || /^[A-Z]/.test(key))
|
|
370
|
+
this.manage(key, value);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
this.emit();
|
|
374
|
+
return this;
|
|
375
|
+
}
|
|
376
|
+
select(using) {
|
|
377
|
+
const keys = getOwnPropertyNames(this.state);
|
|
378
|
+
return using
|
|
379
|
+
? selectRecursive(using, keys.concat(lifecycleEvents))
|
|
380
|
+
: keys;
|
|
381
|
+
}
|
|
382
|
+
manage(key, initial, effect) {
|
|
383
|
+
const { state, subject } = this;
|
|
384
|
+
state[key] = initial;
|
|
385
|
+
defineProperty(subject, key, {
|
|
386
|
+
enumerable: true,
|
|
387
|
+
configurable: true,
|
|
388
|
+
get: () => state[key],
|
|
389
|
+
set: this.setter(key, effect)
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
setter(key, handler) {
|
|
393
|
+
const { state, subject } = this;
|
|
394
|
+
return (value) => {
|
|
395
|
+
if (state[key] == value)
|
|
396
|
+
return;
|
|
397
|
+
if (handler)
|
|
398
|
+
switch (handler.call(subject, value)) {
|
|
399
|
+
case true:
|
|
400
|
+
this.update(key);
|
|
401
|
+
case false:
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
this.update(key, value);
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
addListener(batch) {
|
|
408
|
+
this.handles.add(batch);
|
|
409
|
+
return () => {
|
|
410
|
+
this.handles.delete(batch);
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
include(to) {
|
|
414
|
+
if (capture(this, to))
|
|
415
|
+
return;
|
|
416
|
+
else
|
|
417
|
+
this.waiting.push(to);
|
|
418
|
+
}
|
|
419
|
+
update(key, value) {
|
|
420
|
+
if (1 in arguments)
|
|
421
|
+
this.state[key] = value;
|
|
422
|
+
if (this.frame.has(key))
|
|
423
|
+
return;
|
|
424
|
+
if (!this.frame.size)
|
|
425
|
+
setTimeout(() => this.emit(), 0);
|
|
426
|
+
this.frame.add(key);
|
|
427
|
+
for (const subscription of this.handles)
|
|
428
|
+
if (key in subscription)
|
|
429
|
+
this.include(subscription[key]);
|
|
430
|
+
}
|
|
431
|
+
emit() {
|
|
432
|
+
flush(this);
|
|
433
|
+
const keys = Array.from(this.frame);
|
|
434
|
+
const handle = new Set(this.waiting.splice(0));
|
|
435
|
+
this.frame.clear();
|
|
436
|
+
handle.forEach(callback => {
|
|
437
|
+
try {
|
|
438
|
+
callback(keys);
|
|
439
|
+
}
|
|
440
|
+
catch (e) { }
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
function keys(from, using) {
|
|
445
|
+
if (typeof using == "string")
|
|
446
|
+
return [using];
|
|
447
|
+
if (typeof using == "object") {
|
|
448
|
+
using = Array.from(using);
|
|
449
|
+
if (using.length === 0)
|
|
450
|
+
using = undefined;
|
|
451
|
+
else
|
|
452
|
+
return using;
|
|
453
|
+
}
|
|
454
|
+
return from.select(using);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function child(from, name) {
|
|
458
|
+
return apply(function child(key) {
|
|
459
|
+
const proxyCache = new WeakMap();
|
|
460
|
+
const getCurrent = from.call(this, key);
|
|
461
|
+
function subscribe(sub) {
|
|
462
|
+
let child;
|
|
463
|
+
function start() {
|
|
464
|
+
const instance = getCurrent();
|
|
465
|
+
if (instance) {
|
|
466
|
+
child = new Subscriber(instance, sub.onUpdate);
|
|
467
|
+
if (sub.active)
|
|
468
|
+
child.commit();
|
|
469
|
+
sub.dependant.add(child);
|
|
470
|
+
proxyCache.set(sub, child.proxy);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
function refresh() {
|
|
474
|
+
if (child) {
|
|
475
|
+
child.release();
|
|
476
|
+
sub.dependant.delete(child);
|
|
477
|
+
proxyCache.set(sub, undefined);
|
|
478
|
+
child = undefined;
|
|
479
|
+
}
|
|
480
|
+
start();
|
|
481
|
+
sub.onUpdate();
|
|
482
|
+
}
|
|
483
|
+
start();
|
|
484
|
+
sub.follow(key, refresh);
|
|
485
|
+
}
|
|
486
|
+
return (local) => {
|
|
487
|
+
if (!local)
|
|
488
|
+
return getCurrent();
|
|
489
|
+
if (!proxyCache.has(local))
|
|
490
|
+
subscribe(local);
|
|
491
|
+
return proxyCache.get(local);
|
|
492
|
+
};
|
|
493
|
+
}, name || from.name);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const Oops$7 = issues({
|
|
497
|
+
HasPropertyUndefined: (control, property) => `${control}.${property} is marked as required for this render.`
|
|
498
|
+
});
|
|
499
|
+
function use$1(init) {
|
|
500
|
+
const [state, update] = useState(() => [
|
|
501
|
+
init(() => update(state.concat()))
|
|
502
|
+
]);
|
|
503
|
+
return state[0];
|
|
504
|
+
}
|
|
505
|
+
function useLazy(Type, callback) {
|
|
506
|
+
const instance = useMemo(() => {
|
|
507
|
+
const instance = Type.create();
|
|
508
|
+
if (callback)
|
|
509
|
+
callback(instance);
|
|
510
|
+
return instance;
|
|
511
|
+
}, []);
|
|
512
|
+
useLayoutEffect(() => () => instance.destroy(), []);
|
|
513
|
+
return instance;
|
|
514
|
+
}
|
|
515
|
+
function useWatcher(target, focus, expected) {
|
|
516
|
+
const hook = use$1(refresh => {
|
|
517
|
+
const sub = new Subscriber(target, refresh);
|
|
518
|
+
if (focus) {
|
|
519
|
+
const src = sub.proxy;
|
|
520
|
+
const key = keys(sub.parent, focus)[0];
|
|
521
|
+
defineProperty(sub, "proxy", {
|
|
522
|
+
get() {
|
|
523
|
+
if (src[key] === undefined && expected)
|
|
524
|
+
throw Oops$7.HasPropertyUndefined(target, key);
|
|
525
|
+
return src[key];
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
return sub;
|
|
530
|
+
});
|
|
531
|
+
useLayoutEffect(() => hook.commit(), []);
|
|
532
|
+
return hook.proxy;
|
|
533
|
+
}
|
|
534
|
+
function useModel(Type, callback) {
|
|
535
|
+
const hook = use$1(refresh => {
|
|
536
|
+
const instance = new Type();
|
|
537
|
+
const control = manage(instance);
|
|
538
|
+
const sub = new Subscriber(control, refresh);
|
|
539
|
+
const release = control.addListener({
|
|
540
|
+
[Lifecycle.WILL_UNMOUNT]() {
|
|
541
|
+
instance.destroy();
|
|
542
|
+
release();
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
if (callback)
|
|
546
|
+
callback(instance);
|
|
547
|
+
return sub;
|
|
548
|
+
});
|
|
549
|
+
return hook.proxy;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const Oops$6 = issues({
|
|
553
|
+
CantAttachGlobal: (parent, child) => `Singleton '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
|
|
554
|
+
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.`
|
|
555
|
+
});
|
|
556
|
+
const PendingContext = new WeakMap();
|
|
557
|
+
const ContextWasUsed = new WeakMap();
|
|
558
|
+
function tap(type, argument) {
|
|
559
|
+
return child(function tap(key) {
|
|
560
|
+
return pendingAccess(this.subject, type, key, argument);
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
function pendingAccess(from, type, key, argument) {
|
|
564
|
+
if ("current" in type)
|
|
565
|
+
return () => type.current;
|
|
566
|
+
if ("current" in from.constructor)
|
|
567
|
+
throw Oops$6.CantAttachGlobal(from, type.name);
|
|
568
|
+
let instance;
|
|
569
|
+
getPending(from).push(context => {
|
|
570
|
+
instance = context.get(type);
|
|
571
|
+
if (typeof argument == "function") {
|
|
572
|
+
if (argument(instance) === false)
|
|
573
|
+
instance = undefined;
|
|
574
|
+
}
|
|
575
|
+
else if (!instance && argument)
|
|
576
|
+
throw Oops$6.AmbientRequired(type.name, from, key);
|
|
577
|
+
});
|
|
578
|
+
return () => instance;
|
|
579
|
+
}
|
|
580
|
+
function usePeerContext(subject) {
|
|
581
|
+
if (ContextWasUsed.has(subject)) {
|
|
582
|
+
if (ContextWasUsed.get(subject))
|
|
583
|
+
useLookup();
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
const pending = PendingContext.get(subject);
|
|
587
|
+
if (pending) {
|
|
588
|
+
const local = useLookup();
|
|
589
|
+
for (const init of pending)
|
|
590
|
+
init(local);
|
|
591
|
+
PendingContext.delete(subject);
|
|
592
|
+
}
|
|
593
|
+
ContextWasUsed.set(subject, !!pending);
|
|
594
|
+
}
|
|
595
|
+
function getPending(subject) {
|
|
596
|
+
let pending = PendingContext.get(subject);
|
|
597
|
+
if (!pending)
|
|
598
|
+
PendingContext.set(subject, pending = []);
|
|
599
|
+
return pending;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
class Lookup {
|
|
603
|
+
constructor() {
|
|
604
|
+
this.table = new Map();
|
|
605
|
+
}
|
|
606
|
+
get local() {
|
|
607
|
+
return getOwnPropertySymbols(this).map((symbol) => this[symbol]);
|
|
608
|
+
}
|
|
609
|
+
key(T) {
|
|
610
|
+
let key = this.table.get(T);
|
|
611
|
+
if (!key) {
|
|
612
|
+
key = Symbol(T.name);
|
|
613
|
+
this.table.set(T, key);
|
|
614
|
+
}
|
|
615
|
+
return key;
|
|
616
|
+
}
|
|
617
|
+
get(T) {
|
|
618
|
+
return this[this.key(T)];
|
|
619
|
+
}
|
|
620
|
+
push(I) {
|
|
621
|
+
const next = create(this);
|
|
622
|
+
if (I instanceof Model || typeof I == "function")
|
|
623
|
+
next.register(I);
|
|
624
|
+
else
|
|
625
|
+
for (const i of values(I))
|
|
626
|
+
next.register(i);
|
|
627
|
+
return next;
|
|
628
|
+
}
|
|
629
|
+
register(I) {
|
|
630
|
+
let writable = true;
|
|
631
|
+
let T;
|
|
632
|
+
if (I instanceof Model) {
|
|
633
|
+
T = I.constructor;
|
|
634
|
+
writable = false;
|
|
635
|
+
}
|
|
636
|
+
else {
|
|
637
|
+
T = I;
|
|
638
|
+
I = I.create();
|
|
639
|
+
}
|
|
640
|
+
do {
|
|
641
|
+
defineProperty(this, this.key(T), {
|
|
642
|
+
value: I, writable
|
|
643
|
+
});
|
|
644
|
+
T = getPrototypeOf(T);
|
|
645
|
+
} while (T !== Model);
|
|
646
|
+
return I;
|
|
647
|
+
}
|
|
648
|
+
pop() {
|
|
649
|
+
for (const key of getOwnPropertySymbols(this)) {
|
|
650
|
+
const entry = getOwnPropertyDescriptor(this, key);
|
|
651
|
+
if (entry.writable)
|
|
652
|
+
entry.value.destroy();
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const Oops$5 = issues({
|
|
658
|
+
NoProviderType: () => `Provider 'of' prop must be Model, typeof Model or a collection of them.`,
|
|
659
|
+
NothingInContext: (name) => `Couldn't find ${name} in context; did you forget to use a Provider?`,
|
|
660
|
+
BadConsumerProps: () => `Provider expects either a render function, 'get' or 'has' props.`
|
|
661
|
+
});
|
|
662
|
+
const LookupContext = createContext(new Lookup());
|
|
663
|
+
const useLookup = () => useContext(LookupContext);
|
|
664
|
+
function useFromContext(Type, strict) {
|
|
665
|
+
const instance = useLookup().get(Type);
|
|
666
|
+
if (!instance && strict)
|
|
667
|
+
throw Oops$5.NothingInContext(Type.name);
|
|
668
|
+
return instance;
|
|
669
|
+
}
|
|
670
|
+
function Consumer(props) {
|
|
671
|
+
const { get, has, children, of: Control } = props;
|
|
672
|
+
if (typeof children == "function")
|
|
673
|
+
return children(Control.tap());
|
|
674
|
+
const callback = has || get;
|
|
675
|
+
if (typeof callback == "function")
|
|
676
|
+
callback(Control.get(!!has));
|
|
677
|
+
else
|
|
678
|
+
throw Oops$5.BadConsumerProps();
|
|
679
|
+
return null;
|
|
680
|
+
}
|
|
681
|
+
function useNewContext(from, inject) {
|
|
682
|
+
return useMemo(() => {
|
|
683
|
+
if (!inject)
|
|
684
|
+
throw Oops$5.NoProviderType();
|
|
685
|
+
const context = from.push(inject);
|
|
686
|
+
for (const instance of context.local)
|
|
687
|
+
for (const apply of getPending(instance))
|
|
688
|
+
apply(context);
|
|
689
|
+
return context;
|
|
690
|
+
}, []);
|
|
691
|
+
}
|
|
692
|
+
function useAppliedProps(within, props) {
|
|
693
|
+
const update = useMemo(() => {
|
|
694
|
+
const targets = within.local;
|
|
695
|
+
return function integrate(props) {
|
|
696
|
+
for (const [key, value] of entries(props))
|
|
697
|
+
if (key != "of" && key != "children")
|
|
698
|
+
for (const into of targets)
|
|
699
|
+
if (key in into)
|
|
700
|
+
into[key] = value;
|
|
701
|
+
};
|
|
702
|
+
}, []);
|
|
703
|
+
update(props);
|
|
704
|
+
}
|
|
705
|
+
function RenderFunction(props) {
|
|
706
|
+
const hook = use$1(refresh => {
|
|
707
|
+
const targets = props.context.local;
|
|
708
|
+
if (targets.length == 1)
|
|
709
|
+
return new Subscriber(targets[0], refresh);
|
|
710
|
+
return {};
|
|
711
|
+
});
|
|
712
|
+
if (hook.commit)
|
|
713
|
+
useLayoutEffect(() => hook.commit(), []);
|
|
714
|
+
return props.render(hook.proxy);
|
|
715
|
+
}
|
|
716
|
+
function Provider(props) {
|
|
717
|
+
const render = props.children;
|
|
718
|
+
const current = useLookup();
|
|
719
|
+
const context = useNewContext(current, props.of);
|
|
720
|
+
useAppliedProps(context, props);
|
|
721
|
+
useLayoutEffect(() => () => context.pop(), []);
|
|
722
|
+
return createElement(LookupContext.Provider, { value: context }, typeof render == "function"
|
|
723
|
+
? createElement(RenderFunction, { context, render })
|
|
724
|
+
: render);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
const Oops$4 = issues({
|
|
728
|
+
StrictUpdate: (expected) => `Strict update() did ${expected ? "not " : ""}find pending updates.`,
|
|
729
|
+
NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
|
|
730
|
+
});
|
|
731
|
+
const useElementLifecycle = lifecycle("element");
|
|
732
|
+
const useComponentLifecycle = lifecycle("component");
|
|
733
|
+
class Model {
|
|
734
|
+
constructor() {
|
|
735
|
+
const control = Controller.setup(this);
|
|
736
|
+
define(this, "get", this);
|
|
737
|
+
define(this, "set", this);
|
|
738
|
+
defer(control, "on");
|
|
739
|
+
defer(control, "effect");
|
|
740
|
+
control.include(() => {
|
|
741
|
+
delete this.on;
|
|
742
|
+
delete this.effect;
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
tap(path, expect) {
|
|
746
|
+
const proxy = useWatcher(this, path, expect);
|
|
747
|
+
this.update("willRender", true);
|
|
748
|
+
return proxy;
|
|
749
|
+
}
|
|
750
|
+
tag(id) {
|
|
751
|
+
const hook = use$1(refresh => {
|
|
752
|
+
return new Subscriber(this, refresh);
|
|
753
|
+
});
|
|
754
|
+
useElementLifecycle(hook, id || 0);
|
|
755
|
+
return hook.proxy;
|
|
756
|
+
}
|
|
757
|
+
use(callback) {
|
|
758
|
+
const hook = use$1(refresh => {
|
|
759
|
+
if (callback)
|
|
760
|
+
callback(this);
|
|
761
|
+
return new Subscriber(this, refresh);
|
|
762
|
+
});
|
|
763
|
+
useComponentLifecycle(hook);
|
|
764
|
+
return hook.proxy;
|
|
765
|
+
}
|
|
766
|
+
on(subset, handler, squash, once) {
|
|
767
|
+
const control = manage(this);
|
|
768
|
+
const set = keys(control, subset);
|
|
769
|
+
const batch = {};
|
|
770
|
+
const remove = control.addListener(batch);
|
|
771
|
+
const callback = squash
|
|
772
|
+
? handler.bind(this)
|
|
773
|
+
: (frame) => {
|
|
774
|
+
for (const key of frame)
|
|
775
|
+
if (set.includes(key))
|
|
776
|
+
handler.call(this, control.state[key], key);
|
|
777
|
+
};
|
|
778
|
+
const handle = once
|
|
779
|
+
? (k) => { remove(); callback(k); }
|
|
780
|
+
: callback;
|
|
781
|
+
for (const key of set)
|
|
782
|
+
batch[key] = handle;
|
|
783
|
+
ensure(control, set);
|
|
784
|
+
return remove;
|
|
785
|
+
}
|
|
786
|
+
once(select, callback, squash) {
|
|
787
|
+
if (callback)
|
|
788
|
+
return this.on(select, callback, squash, true);
|
|
789
|
+
else
|
|
790
|
+
return new Promise(resolve => {
|
|
791
|
+
this.on(select, resolve, true, true);
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
effect(callback, select) {
|
|
795
|
+
const control = manage(this);
|
|
796
|
+
let target = this;
|
|
797
|
+
const effect = createEffect(callback);
|
|
798
|
+
const invoke = () => effect.call(target, target);
|
|
799
|
+
if (select) {
|
|
800
|
+
invoke();
|
|
801
|
+
return this.on(select, invoke, true);
|
|
802
|
+
}
|
|
803
|
+
else {
|
|
804
|
+
const sub = new Subscriber(control, invoke);
|
|
805
|
+
target = sub.proxy;
|
|
806
|
+
invoke();
|
|
807
|
+
return sub.commit();
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
import(from, subset) {
|
|
811
|
+
for (const key of keys(manage(this), subset))
|
|
812
|
+
if (key in from)
|
|
813
|
+
this[key] = from[key];
|
|
814
|
+
}
|
|
815
|
+
export(subset) {
|
|
816
|
+
const control = manage(this);
|
|
817
|
+
const output = {};
|
|
818
|
+
for (const key of keys(control, subset))
|
|
819
|
+
output[key] = control.state[key];
|
|
820
|
+
return output;
|
|
821
|
+
}
|
|
822
|
+
update(arg, tag) {
|
|
823
|
+
const control = manage(this);
|
|
824
|
+
if (typeof arg == "function")
|
|
825
|
+
arg = select(this, arg);
|
|
826
|
+
if (typeof arg == "boolean") {
|
|
827
|
+
if (!control.pending === arg)
|
|
828
|
+
return Promise.reject(Oops$4.StrictUpdate(arg));
|
|
829
|
+
}
|
|
830
|
+
else if (arg) {
|
|
831
|
+
control.update(arg);
|
|
832
|
+
if (1 in arguments && arg in this) {
|
|
833
|
+
const method = this[arg];
|
|
834
|
+
if (typeof method == "function")
|
|
835
|
+
if (typeof tag != "boolean")
|
|
836
|
+
method.call(this, tag);
|
|
837
|
+
else if (tag)
|
|
838
|
+
method.call(this);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return {
|
|
842
|
+
then(callback) {
|
|
843
|
+
if (callback)
|
|
844
|
+
if (control.pending)
|
|
845
|
+
control.include(callback);
|
|
846
|
+
else
|
|
847
|
+
callback(false);
|
|
848
|
+
else
|
|
849
|
+
throw Oops$4.NoChaining();
|
|
850
|
+
}
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
destroy() {
|
|
854
|
+
this.update("willDestroy", true);
|
|
855
|
+
}
|
|
856
|
+
toString() {
|
|
857
|
+
return this.constructor.name;
|
|
858
|
+
}
|
|
859
|
+
static create(...args) {
|
|
860
|
+
const instance = new this(...args);
|
|
861
|
+
manage(instance);
|
|
862
|
+
return instance;
|
|
863
|
+
}
|
|
864
|
+
static new(callback) {
|
|
865
|
+
const instance = useLazy(this, callback);
|
|
866
|
+
instance.update("willRender", true);
|
|
867
|
+
return instance;
|
|
868
|
+
}
|
|
869
|
+
static find(strict) {
|
|
870
|
+
return useFromContext(this, strict);
|
|
871
|
+
}
|
|
872
|
+
static get(key) {
|
|
873
|
+
const instance = this.find(!!key);
|
|
874
|
+
return (typeof key == "function" ?
|
|
875
|
+
key(instance) :
|
|
876
|
+
typeof key == "string" ?
|
|
877
|
+
instance[key] :
|
|
878
|
+
instance);
|
|
879
|
+
}
|
|
880
|
+
static tap(key, expect) {
|
|
881
|
+
return this.find(true).tap(key, expect);
|
|
882
|
+
}
|
|
883
|
+
static tag(id) {
|
|
884
|
+
return this.find(true).tag(id);
|
|
885
|
+
}
|
|
886
|
+
static use(callback) {
|
|
887
|
+
const instance = useModel(this, callback);
|
|
888
|
+
useComponentLifecycle(instance[LOCAL]);
|
|
889
|
+
usePeerContext(instance.get);
|
|
890
|
+
return instance;
|
|
891
|
+
}
|
|
892
|
+
static uses(props, only) {
|
|
893
|
+
return this.use(instance => {
|
|
894
|
+
instance.import(props, only);
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
static using(props, only) {
|
|
898
|
+
const instance = this.use();
|
|
899
|
+
instance.import(props, only);
|
|
900
|
+
return instance;
|
|
901
|
+
}
|
|
902
|
+
static meta(path) {
|
|
903
|
+
return useWatcher(this, path);
|
|
904
|
+
}
|
|
905
|
+
static isTypeof(maybe) {
|
|
906
|
+
return (typeof maybe == "function" &&
|
|
907
|
+
maybe.prototype instanceof this);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
Model.CONTROL = CONTROL;
|
|
911
|
+
Model.STATE = STATE;
|
|
912
|
+
Model.LOCAL = LOCAL;
|
|
913
|
+
defineLazy(Model, CONTROL, function () {
|
|
914
|
+
return new Controller(this).start();
|
|
915
|
+
});
|
|
916
|
+
function defer(on, method) {
|
|
917
|
+
const { subject, waiting } = on;
|
|
918
|
+
const real = subject[method];
|
|
919
|
+
subject[method] = (...args) => {
|
|
920
|
+
let done;
|
|
921
|
+
waiting.push(() => {
|
|
922
|
+
done = real.apply(subject, args);
|
|
923
|
+
});
|
|
924
|
+
return () => done();
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
const Parent = new WeakMap();
|
|
929
|
+
const Oops$3 = issues({
|
|
930
|
+
ParentRequired: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
|
|
931
|
+
UnexpectedParent: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
|
|
932
|
+
UndefinedNotAllowed: (key) => `Child property ${key} may not be undefined.`,
|
|
933
|
+
BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`,
|
|
934
|
+
IsReadOnly: (owner, key) => `${owner}.${key} is set to read-only, but tried to reassign assign.`,
|
|
935
|
+
MustBeDefined: (owner, key) => `${owner}.${key} must have initial value, as a read-only property.`
|
|
936
|
+
});
|
|
937
|
+
function bootstrap(object) {
|
|
938
|
+
const breakdown = getOwnPropertyDescriptors(object);
|
|
939
|
+
const control = new Model();
|
|
940
|
+
for (const key in breakdown)
|
|
941
|
+
defineProperty(control, key, breakdown[key]);
|
|
942
|
+
return control;
|
|
943
|
+
}
|
|
944
|
+
function use(input, argument) {
|
|
945
|
+
return child(function use(key) {
|
|
946
|
+
const { subject } = this;
|
|
947
|
+
// `true` => readonly
|
|
948
|
+
// `false` => writable, may be undefined
|
|
949
|
+
// `undefined` => writable (default)
|
|
950
|
+
let mode = argument;
|
|
951
|
+
let current;
|
|
952
|
+
const onUpdate = (next, initial) => {
|
|
953
|
+
if (mode === true && !initial)
|
|
954
|
+
throw Oops$3.IsReadOnly(subject, key);
|
|
955
|
+
if (next) {
|
|
956
|
+
current = next instanceof Model
|
|
957
|
+
? next : bootstrap(next);
|
|
958
|
+
Parent.set(current, subject);
|
|
959
|
+
manage(current);
|
|
960
|
+
}
|
|
961
|
+
else if (mode === undefined)
|
|
962
|
+
throw Oops$3.UndefinedNotAllowed(key);
|
|
963
|
+
else
|
|
964
|
+
current = undefined;
|
|
965
|
+
if (typeof argument == "function")
|
|
966
|
+
return argument(current);
|
|
967
|
+
};
|
|
968
|
+
if (input) {
|
|
969
|
+
current =
|
|
970
|
+
Model.isTypeof(input) ? new input() :
|
|
971
|
+
input instanceof Model ? input :
|
|
972
|
+
typeof input === "function" ? input() :
|
|
973
|
+
typeof input === "object" ? bootstrap(input) :
|
|
974
|
+
Oops$3.BadArgument(typeof input).throw();
|
|
975
|
+
if (current)
|
|
976
|
+
mode = onUpdate(current, true) || mode;
|
|
977
|
+
else if (mode == true)
|
|
978
|
+
throw Oops$3.MustBeDefined(subject, key);
|
|
979
|
+
}
|
|
980
|
+
else
|
|
981
|
+
mode = false;
|
|
982
|
+
this.manage(key, current, onUpdate);
|
|
983
|
+
return () => current;
|
|
984
|
+
});
|
|
985
|
+
}
|
|
986
|
+
function parent(Expects, required) {
|
|
987
|
+
return apply(function parent() {
|
|
988
|
+
const child = this.subject;
|
|
989
|
+
const expected = Expects.name;
|
|
990
|
+
const value = Parent.get(this.subject);
|
|
991
|
+
if (!value) {
|
|
992
|
+
if (required)
|
|
993
|
+
throw Oops$3.ParentRequired(expected, child);
|
|
994
|
+
}
|
|
995
|
+
else if (!(value instanceof Expects))
|
|
996
|
+
throw Oops$3.UnexpectedParent(expected, child, value);
|
|
997
|
+
return { value };
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
const Oops$2 = issues({
|
|
1002
|
+
DuplicateAction: (key) => `Invoked action ${key} but one is already active.`,
|
|
1003
|
+
BadComputedSource: (model, property, got) => `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
|
|
1004
|
+
PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`
|
|
1005
|
+
});
|
|
1006
|
+
function createRef(key, cb) {
|
|
1007
|
+
const refObjectFunction = this.setter(key, cb && createValueEffect(cb));
|
|
1008
|
+
defineProperty(refObjectFunction, "current", {
|
|
1009
|
+
set: refObjectFunction,
|
|
1010
|
+
get: () => this.state[key]
|
|
1011
|
+
});
|
|
1012
|
+
return refObjectFunction;
|
|
1013
|
+
}
|
|
1014
|
+
function ref(arg) {
|
|
1015
|
+
return apply(function ref(key) {
|
|
1016
|
+
let value = {};
|
|
1017
|
+
if (typeof arg == "object") {
|
|
1018
|
+
const source = manage(arg);
|
|
1019
|
+
for (const key in source.state)
|
|
1020
|
+
defineLazy(value, key, createRef.bind(source, key));
|
|
1021
|
+
}
|
|
1022
|
+
else
|
|
1023
|
+
value = createRef.call(this, key, arg);
|
|
1024
|
+
return { value };
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
function on(initial, cb) {
|
|
1028
|
+
return apply(function on(key) {
|
|
1029
|
+
this.manage(key, initial, cb && createValueEffect(cb));
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
function memo(factory, defer) {
|
|
1033
|
+
return apply(function memo(key) {
|
|
1034
|
+
const source = this.subject;
|
|
1035
|
+
const get = () => factory.call(source);
|
|
1036
|
+
if (defer)
|
|
1037
|
+
defineLazy(source, key, get);
|
|
1038
|
+
else
|
|
1039
|
+
define(source, key, get());
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
function lazy(value) {
|
|
1043
|
+
return apply(function lazy(key) {
|
|
1044
|
+
const source = this.subject;
|
|
1045
|
+
source[key] = value;
|
|
1046
|
+
defineProperty(this.state, key, {
|
|
1047
|
+
get: () => source[key]
|
|
1048
|
+
});
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
function act(task) {
|
|
1052
|
+
return apply(function act(key) {
|
|
1053
|
+
let pending = false;
|
|
1054
|
+
const invoke = (...args) => {
|
|
1055
|
+
if (pending)
|
|
1056
|
+
return Promise.reject(Oops$2.DuplicateAction(key));
|
|
1057
|
+
pending = true;
|
|
1058
|
+
this.update(key);
|
|
1059
|
+
return new Promise(res => {
|
|
1060
|
+
res(task.apply(this.subject, args));
|
|
1061
|
+
}).finally(() => {
|
|
1062
|
+
pending = false;
|
|
1063
|
+
this.update(key);
|
|
1064
|
+
});
|
|
1065
|
+
};
|
|
1066
|
+
setAlias(invoke, `run ${key}`);
|
|
1067
|
+
defineProperty(invoke, "active", {
|
|
1068
|
+
get: () => pending
|
|
1069
|
+
});
|
|
1070
|
+
return {
|
|
1071
|
+
value: invoke,
|
|
1072
|
+
writable: false
|
|
1073
|
+
};
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
function from(source, setter, getter) {
|
|
1077
|
+
return apply(function from(key) {
|
|
1078
|
+
const { subject } = this;
|
|
1079
|
+
let getSource;
|
|
1080
|
+
// Easy mistake, using a peer, will always be unresolved.
|
|
1081
|
+
if (typeof source == "symbol")
|
|
1082
|
+
throw Oops$2.PeerNotAllowed(subject, key);
|
|
1083
|
+
// replace source controller in-case different
|
|
1084
|
+
if (typeof source == "object")
|
|
1085
|
+
getSource = () => manage(source);
|
|
1086
|
+
// specifically an arrow function (getter factory)
|
|
1087
|
+
else if (!source.prototype) {
|
|
1088
|
+
setter = source.call(subject, key);
|
|
1089
|
+
getSource = () => this;
|
|
1090
|
+
}
|
|
1091
|
+
// is a peer Model (constructor)
|
|
1092
|
+
else if (Model.isTypeof(source))
|
|
1093
|
+
getSource = pendingAccess(subject, source, key, true);
|
|
1094
|
+
// Regular function is to ambiguous so not allowed.
|
|
1095
|
+
else
|
|
1096
|
+
throw Oops$2.BadComputedSource(subject, key, source);
|
|
1097
|
+
prepare(this, key, getSource, setter, getter);
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
const Oops$1 = issues({
|
|
1102
|
+
ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
|
|
1103
|
+
});
|
|
1104
|
+
function pending(source, compute) {
|
|
1105
|
+
return (source === undefined ?
|
|
1106
|
+
suspendForValue() :
|
|
1107
|
+
typeof source == "function" && !source.prototype ?
|
|
1108
|
+
suspendForAsync(source) :
|
|
1109
|
+
suspendForComputed(source, compute));
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Get value, suspend instead if undefined.
|
|
1113
|
+
*
|
|
1114
|
+
* Throws suspense promise also interpretable as an error.
|
|
1115
|
+
* React could handle it but other contexts probably not.
|
|
1116
|
+
*/
|
|
1117
|
+
function pendingValue(via, key) {
|
|
1118
|
+
const value = via.state[key];
|
|
1119
|
+
if (value !== undefined)
|
|
1120
|
+
return value;
|
|
1121
|
+
const error = Oops$1.ValueNotReady(via.subject, key);
|
|
1122
|
+
const promise = new Promise(resolve => {
|
|
1123
|
+
const release = via.addListener({
|
|
1124
|
+
[key](value) {
|
|
1125
|
+
if (value !== undefined) {
|
|
1126
|
+
release();
|
|
1127
|
+
resolve();
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
});
|
|
1131
|
+
});
|
|
1132
|
+
throw Object.assign(promise, {
|
|
1133
|
+
toString: () => String(error),
|
|
1134
|
+
message: error.message,
|
|
1135
|
+
stack: error.stack
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
function suspendForComputed(source, compute) {
|
|
1139
|
+
function getter(_value, key) {
|
|
1140
|
+
return pendingValue(manage(this), key);
|
|
1141
|
+
}
|
|
1142
|
+
return from(source, compute, getter);
|
|
1143
|
+
}
|
|
1144
|
+
function suspendForValue() {
|
|
1145
|
+
return apply(function pending(key) {
|
|
1146
|
+
this.state[key] = undefined;
|
|
1147
|
+
return {
|
|
1148
|
+
set: this.setter(key),
|
|
1149
|
+
get: () => pendingValue(this, key)
|
|
1150
|
+
};
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
function suspendForAsync(source) {
|
|
1154
|
+
return apply(function suspense(key) {
|
|
1155
|
+
const subject = this.subject;
|
|
1156
|
+
let waiting = true;
|
|
1157
|
+
let value;
|
|
1158
|
+
let error;
|
|
1159
|
+
return () => {
|
|
1160
|
+
if (error)
|
|
1161
|
+
throw error;
|
|
1162
|
+
if (!waiting)
|
|
1163
|
+
return value;
|
|
1164
|
+
const issue = Oops$1.ValueNotReady(subject, key);
|
|
1165
|
+
let suspend = source
|
|
1166
|
+
.call(subject, key)
|
|
1167
|
+
.catch(err => error = err)
|
|
1168
|
+
.then(out => value = out)
|
|
1169
|
+
.finally(() => waiting = false);
|
|
1170
|
+
throw Object.assign(suspend, {
|
|
1171
|
+
message: issue.message,
|
|
1172
|
+
stack: issue.stack
|
|
1173
|
+
});
|
|
1174
|
+
};
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
const Oops = issues({
|
|
1179
|
+
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.`,
|
|
1180
|
+
GlobalExists: (name) => `Shared instance of ${name} already exists! Consider unmounting existing, or use ${name}.reset() to force-delete it.`
|
|
1181
|
+
});
|
|
1182
|
+
const Active = new WeakMap();
|
|
1183
|
+
class Singleton extends Model {
|
|
1184
|
+
static get current() {
|
|
1185
|
+
return Active.get(this);
|
|
1186
|
+
}
|
|
1187
|
+
static create(...args) {
|
|
1188
|
+
const Type = this;
|
|
1189
|
+
if (Active.has(Type))
|
|
1190
|
+
throw Oops.GlobalExists(this.name);
|
|
1191
|
+
let instance = super.create(...args);
|
|
1192
|
+
Active.set(Type, instance);
|
|
1193
|
+
return instance;
|
|
1194
|
+
}
|
|
1195
|
+
static update(updates) {
|
|
1196
|
+
const instance = this.current || this.create();
|
|
1197
|
+
instance.import(updates);
|
|
1198
|
+
return instance.update();
|
|
1199
|
+
}
|
|
1200
|
+
static is() {
|
|
1201
|
+
return this.find(true);
|
|
1202
|
+
}
|
|
1203
|
+
static find(strict) {
|
|
1204
|
+
const instance = this.current;
|
|
1205
|
+
if (!instance && strict)
|
|
1206
|
+
throw Oops.GlobalDoesNotExist(this.name);
|
|
1207
|
+
return instance;
|
|
1208
|
+
}
|
|
1209
|
+
static reset() {
|
|
1210
|
+
if (this.current)
|
|
1211
|
+
this.current.destroy();
|
|
1212
|
+
}
|
|
1213
|
+
destroy() {
|
|
1214
|
+
super.destroy();
|
|
1215
|
+
Active.delete(this.constructor);
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
export { Consumer, Model, Provider, Singleton, act, apply, child, Model as default, from, lazy, memo, on, parent, pending, ref, tap, use };
|
|
1220
|
+
//# sourceMappingURL=index.esm.js.map
|