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