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