@expressive/mvc 1.0.0-beta.36 → 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 +693 -757
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +700 -761
- 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,92 @@ 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
|
-
|
|
134
|
-
|
|
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;
|
|
135
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
|
+
});
|
|
136
149
|
}
|
|
137
|
-
|
|
150
|
+
ref(key, handler) {
|
|
138
151
|
const { state, subject } = this;
|
|
139
152
|
return (value) => {
|
|
140
153
|
if (state[key] == value)
|
|
@@ -155,6 +168,18 @@ class Controller {
|
|
|
155
168
|
this.followers.delete(listener);
|
|
156
169
|
};
|
|
157
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
|
+
}
|
|
158
183
|
update(key, value) {
|
|
159
184
|
if (1 in arguments)
|
|
160
185
|
this.state[key] = value;
|
|
@@ -164,19 +189,8 @@ class Controller {
|
|
|
164
189
|
setTimeout(() => {
|
|
165
190
|
flush(this);
|
|
166
191
|
const keys = Object.freeze([...this.frame]);
|
|
167
|
-
const handle = new Set(this.waiting);
|
|
168
|
-
this.waiting.clear();
|
|
169
192
|
this.frame.clear();
|
|
170
|
-
|
|
171
|
-
setTimeout(() => {
|
|
172
|
-
UPDATE.delete(this.subject);
|
|
173
|
-
}, 0);
|
|
174
|
-
handle.forEach(callback => {
|
|
175
|
-
try {
|
|
176
|
-
callback(keys);
|
|
177
|
-
}
|
|
178
|
-
catch (e) { }
|
|
179
|
-
});
|
|
193
|
+
this.emit(keys);
|
|
180
194
|
}, 0);
|
|
181
195
|
this.frame.add(key);
|
|
182
196
|
for (const callback of this.followers) {
|
|
@@ -191,7 +205,7 @@ class Controller {
|
|
|
191
205
|
return;
|
|
192
206
|
}
|
|
193
207
|
if (typeof arg == "boolean" && arg !== this.frame.size > 0)
|
|
194
|
-
return Promise.reject(Oops$
|
|
208
|
+
return Promise.reject(Oops$c.StrictUpdate(arg));
|
|
195
209
|
return {
|
|
196
210
|
then: (callback) => {
|
|
197
211
|
if (callback)
|
|
@@ -200,76 +214,83 @@ class Controller {
|
|
|
200
214
|
else
|
|
201
215
|
callback(false);
|
|
202
216
|
else
|
|
203
|
-
throw Oops$
|
|
217
|
+
throw Oops$c.NoChaining();
|
|
204
218
|
}
|
|
205
219
|
};
|
|
206
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);
|
|
240
|
+
});
|
|
241
|
+
return () => done && done();
|
|
242
|
+
}
|
|
243
|
+
return cb(control);
|
|
207
244
|
}
|
|
208
245
|
|
|
209
246
|
class Subscriber {
|
|
210
247
|
constructor(parent, onUpdate) {
|
|
211
248
|
this.onUpdate = onUpdate;
|
|
212
249
|
this.active = false;
|
|
213
|
-
this.watch = {};
|
|
214
250
|
this.dependant = new Set();
|
|
251
|
+
this.watch = {};
|
|
215
252
|
if (!(parent instanceof Controller))
|
|
216
|
-
parent =
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
const DEBUG = (keys) => {
|
|
221
|
-
UPDATE.set(proxy, keys.filter(k => k in this.watch));
|
|
222
|
-
};
|
|
253
|
+
parent = control(parent);
|
|
254
|
+
let reset;
|
|
255
|
+
const proxy = create(parent.proxy);
|
|
256
|
+
const controller = parent;
|
|
223
257
|
define(proxy, LOCAL, this);
|
|
224
258
|
defineProperty(this, "proxy", {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}, 0);
|
|
259
|
+
configurable: true,
|
|
260
|
+
get() {
|
|
261
|
+
if (reset)
|
|
262
|
+
setTimeout(reset, 0);
|
|
230
263
|
return proxy;
|
|
231
264
|
}
|
|
232
265
|
});
|
|
233
|
-
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
266
|
+
const onEvent = (key) => {
|
|
267
|
+
const handler = this.watch[key];
|
|
268
|
+
if (!handler)
|
|
269
|
+
return;
|
|
270
|
+
if (typeof handler == "function")
|
|
271
|
+
handler();
|
|
272
|
+
const notify = this.onUpdate(key, controller);
|
|
273
|
+
const getWhy = (keys) => {
|
|
274
|
+
reset = applyUpdate(proxy, keys.filter(k => k in this.watch));
|
|
239
275
|
};
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
enumerable: true
|
|
246
|
-
});
|
|
247
|
-
}
|
|
276
|
+
if (notify) {
|
|
277
|
+
controller.requestUpdate(getWhy);
|
|
278
|
+
controller.requestUpdate(notify);
|
|
279
|
+
}
|
|
280
|
+
};
|
|
248
281
|
this.commit = () => {
|
|
249
|
-
const
|
|
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
|
-
});
|
|
282
|
+
const release = controller.addListener(onEvent);
|
|
262
283
|
this.active = true;
|
|
263
284
|
this.dependant.forEach(x => x.commit());
|
|
264
285
|
return this.release = () => {
|
|
265
286
|
this.dependant.forEach(x => x.release());
|
|
266
|
-
|
|
287
|
+
release();
|
|
267
288
|
};
|
|
268
289
|
};
|
|
269
290
|
}
|
|
270
291
|
}
|
|
271
292
|
|
|
272
|
-
const Oops$
|
|
293
|
+
const Oops$b = issues({
|
|
273
294
|
ComputeFailed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
|
|
274
295
|
ComputedEarly: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
|
|
275
296
|
});
|
|
@@ -287,15 +308,12 @@ function prepare(parent, key, source, setter, getter) {
|
|
|
287
308
|
USED.set(parent, register);
|
|
288
309
|
}
|
|
289
310
|
register.set(key, info);
|
|
290
|
-
const current = getter
|
|
291
|
-
? () => getter(parent, key)
|
|
292
|
-
: () => state[key];
|
|
293
311
|
function compute(initial) {
|
|
294
312
|
try {
|
|
295
313
|
return setter.call(sub.proxy, sub.proxy);
|
|
296
314
|
}
|
|
297
315
|
catch (err) {
|
|
298
|
-
Oops$
|
|
316
|
+
Oops$b.ComputeFailed(subject, key, !!initial).warn();
|
|
299
317
|
throw err;
|
|
300
318
|
}
|
|
301
319
|
}
|
|
@@ -331,17 +349,12 @@ function prepare(parent, key, source, setter, getter) {
|
|
|
331
349
|
value: undefined,
|
|
332
350
|
writable: true
|
|
333
351
|
});
|
|
334
|
-
defineProperty(subject, key, {
|
|
335
|
-
enumerable: true,
|
|
336
|
-
configurable: true,
|
|
337
|
-
get: current
|
|
338
|
-
});
|
|
339
352
|
try {
|
|
340
|
-
state[key] = compute(true);
|
|
353
|
+
return state[key] = compute(true);
|
|
341
354
|
}
|
|
342
355
|
catch (e) {
|
|
343
356
|
if (early)
|
|
344
|
-
Oops$
|
|
357
|
+
Oops$b.ComputedEarly(key).warn();
|
|
345
358
|
throw e;
|
|
346
359
|
}
|
|
347
360
|
finally {
|
|
@@ -352,23 +365,24 @@ function prepare(parent, key, source, setter, getter) {
|
|
|
352
365
|
info.priority = peer.priority + 1;
|
|
353
366
|
}
|
|
354
367
|
}
|
|
355
|
-
return current();
|
|
356
368
|
}
|
|
357
369
|
setAlias(update, `try ${key}`);
|
|
358
370
|
setAlias(create, `new ${key}`);
|
|
359
371
|
setAlias(setter, `run ${key}`);
|
|
360
372
|
INIT.add(create);
|
|
361
373
|
INFO.set(update, info);
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
374
|
+
defineProperty(state, key, {
|
|
375
|
+
get: create,
|
|
376
|
+
configurable: true,
|
|
377
|
+
enumerable: true
|
|
378
|
+
});
|
|
379
|
+
return getter
|
|
380
|
+
? () => getter(parent, key)
|
|
381
|
+
: {};
|
|
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,302 @@ 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
555
|
if (!output)
|
|
543
556
|
return;
|
|
544
|
-
if (
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
(({ get, set } = output), desc = __rest(output, ["get", "set"]));
|
|
548
|
-
if ("value" in desc) {
|
|
549
|
-
state[key] = desc.value;
|
|
550
|
-
if (get || set)
|
|
551
|
-
delete desc.value;
|
|
552
|
-
else {
|
|
553
|
-
defineProperty(subject, key, desc);
|
|
554
|
-
return;
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
+
if ("explicit" in output) {
|
|
558
|
+
defineProperty(this.subject, key, output);
|
|
559
|
+
return;
|
|
557
560
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
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 };
|
|
565
|
+
else
|
|
566
|
+
return output;
|
|
563
567
|
}
|
|
564
568
|
Pending.set(placeholder, setup);
|
|
565
569
|
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
|
-
}
|
|
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;
|
|
590
581
|
}
|
|
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);
|
|
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);
|
|
603
589
|
}
|
|
604
590
|
};
|
|
605
|
-
|
|
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
|
+
};
|
|
606
601
|
}
|
|
607
602
|
|
|
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}.`,
|
|
603
|
+
const Oops$a = issues({
|
|
604
|
+
DuplicateAction: (key) => `Invoked action ${key} but one is already active.`
|
|
613
605
|
});
|
|
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);
|
|
606
|
+
function act(task) {
|
|
607
|
+
return apply(function act(key) {
|
|
608
|
+
let pending = false;
|
|
609
|
+
const invoke = (...args) => {
|
|
610
|
+
if (pending)
|
|
611
|
+
return Promise.reject(Oops$a.DuplicateAction(key));
|
|
612
|
+
pending = true;
|
|
613
|
+
this.update(key);
|
|
614
|
+
return new Promise(res => {
|
|
615
|
+
res(task.apply(this.subject, args));
|
|
616
|
+
}).finally(() => {
|
|
617
|
+
pending = false;
|
|
618
|
+
this.update(key);
|
|
619
|
+
});
|
|
636
620
|
};
|
|
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
|
-
}
|
|
621
|
+
this.state[key] = undefined;
|
|
622
|
+
setAlias(invoke, `run ${key}`);
|
|
623
|
+
defineProperty(invoke, "active", {
|
|
624
|
+
get: () => pending
|
|
625
|
+
});
|
|
652
626
|
return {
|
|
653
|
-
|
|
654
|
-
set:
|
|
627
|
+
value: invoke,
|
|
628
|
+
set: false
|
|
655
629
|
};
|
|
656
630
|
});
|
|
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
631
|
}
|
|
718
632
|
|
|
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;
|
|
633
|
+
const Oops$9 = issues({
|
|
634
|
+
ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
|
|
635
|
+
});
|
|
636
|
+
/**
|
|
637
|
+
* Get value, suspend instead if undefined.
|
|
638
|
+
*
|
|
639
|
+
* Throws suspense promise also interpretable as an error.
|
|
640
|
+
* React could handle it but other contexts probably not.
|
|
641
|
+
*/
|
|
642
|
+
function pendingValue(via, key) {
|
|
643
|
+
const value = via.state[key];
|
|
644
|
+
if (value === undefined)
|
|
645
|
+
throw suspend(via, key);
|
|
646
|
+
return value;
|
|
758
647
|
}
|
|
759
|
-
function
|
|
760
|
-
const
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
648
|
+
function pendingFactory(via, key, fn) {
|
|
649
|
+
const { subject, state } = via;
|
|
650
|
+
let waiting;
|
|
651
|
+
let error;
|
|
652
|
+
return () => {
|
|
653
|
+
if (waiting)
|
|
654
|
+
throw waiting;
|
|
655
|
+
if (error)
|
|
656
|
+
throw error;
|
|
657
|
+
if (key in state)
|
|
658
|
+
return state[key];
|
|
659
|
+
let output;
|
|
660
|
+
try {
|
|
661
|
+
output = fn.call(subject, key, subject);
|
|
772
662
|
}
|
|
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;
|
|
663
|
+
catch (err) {
|
|
664
|
+
throw error = err;
|
|
665
|
+
}
|
|
666
|
+
if (output instanceof Promise) {
|
|
667
|
+
const issue = Oops$9.ValueNotReady(subject, key);
|
|
668
|
+
output
|
|
669
|
+
.catch(err => error = err)
|
|
670
|
+
.then(out => state[key] = out)
|
|
671
|
+
.finally(() => waiting = undefined);
|
|
672
|
+
throw waiting = Object.assign(output, {
|
|
673
|
+
message: issue.message,
|
|
674
|
+
stack: issue.stack
|
|
675
|
+
});
|
|
797
676
|
}
|
|
798
677
|
else {
|
|
799
|
-
|
|
800
|
-
update = () => {
|
|
801
|
-
const next = compute.call(spy, spy);
|
|
802
|
-
if (value != next)
|
|
803
|
-
set(next);
|
|
804
|
-
};
|
|
678
|
+
return state[key] = output;
|
|
805
679
|
}
|
|
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;
|
|
680
|
+
};
|
|
818
681
|
}
|
|
819
|
-
function
|
|
820
|
-
const
|
|
821
|
-
|
|
822
|
-
const
|
|
823
|
-
|
|
824
|
-
if (key == Lifecycle.WILL_UNMOUNT)
|
|
682
|
+
function suspend(source, key) {
|
|
683
|
+
const error = Oops$9.ValueNotReady(source.subject, key);
|
|
684
|
+
const promise = new Promise(resolve => {
|
|
685
|
+
const release = source.addListener(forKey => {
|
|
686
|
+
if (forKey == key)
|
|
825
687
|
return () => {
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
688
|
+
if (source.state[key] !== undefined) {
|
|
689
|
+
release();
|
|
690
|
+
resolve();
|
|
691
|
+
}
|
|
692
|
+
};
|
|
829
693
|
});
|
|
830
|
-
if (callback)
|
|
831
|
-
callback(instance);
|
|
832
|
-
return sub;
|
|
833
694
|
});
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
695
|
+
return Object.assign(promise, {
|
|
696
|
+
toString: () => String(error),
|
|
697
|
+
name: "Suspense",
|
|
698
|
+
message: error.message,
|
|
699
|
+
stack: error.stack
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
|
|
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;
|
|
726
|
+
}
|
|
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);
|
|
731
|
+
});
|
|
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));
|
|
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();
|
|
770
|
+
else {
|
|
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);
|
|
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);
|
|
809
|
+
}
|
|
810
|
+
else if (!(value instanceof Expects))
|
|
811
|
+
throw Oops$6.UnexpectedParent(expected, child, value);
|
|
812
|
+
return { value };
|
|
813
|
+
});
|
|
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
|
+
};
|
|
849
|
+
});
|
|
837
850
|
}
|
|
838
851
|
|
|
839
852
|
class Lookup {
|
|
@@ -891,113 +904,56 @@ class Lookup {
|
|
|
891
904
|
pop() {
|
|
892
905
|
for (const key of getOwnPropertySymbols(this)) {
|
|
893
906
|
const entry = getOwnPropertyDescriptor(this, key);
|
|
894
|
-
if (entry.writable)
|
|
907
|
+
if (entry.writable && entry.value)
|
|
895
908
|
entry.value.destroy();
|
|
896
909
|
}
|
|
897
910
|
}
|
|
898
911
|
}
|
|
899
912
|
|
|
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
913
|
const LookupContext = React.createContext(new Lookup());
|
|
906
914
|
const useLookup = () => React.useContext(LookupContext);
|
|
907
|
-
|
|
915
|
+
const Oops$4 = issues({
|
|
916
|
+
NothingInContext: (name) => `Couldn't find ${name} in context; did you forget to use a Provider?`
|
|
917
|
+
});
|
|
918
|
+
function useInContext(Type, arg) {
|
|
908
919
|
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);
|
|
920
|
+
if (!instance && arg !== false)
|
|
921
|
+
throw Oops$4.NothingInContext(Type.name);
|
|
922
|
+
return typeof arg == "string" ?
|
|
923
|
+
instance[arg] :
|
|
924
|
+
instance;
|
|
969
925
|
}
|
|
970
926
|
|
|
971
|
-
const Oops$
|
|
972
|
-
CantAttachGlobal: (parent, child) => `
|
|
927
|
+
const Oops$3 = issues({
|
|
928
|
+
CantAttachGlobal: (parent, child) => `Global '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
|
|
973
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.`
|
|
974
930
|
});
|
|
975
931
|
const PendingContext = new WeakMap();
|
|
976
932
|
const ContextWasUsed = new WeakMap();
|
|
977
933
|
function tap(type, argument) {
|
|
978
|
-
return
|
|
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
|
+
}
|
|
979
952
|
return {
|
|
980
|
-
|
|
953
|
+
recursive: true
|
|
981
954
|
};
|
|
982
955
|
});
|
|
983
956
|
}
|
|
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
957
|
function usePeerContext(subject) {
|
|
1002
958
|
if (ContextWasUsed.has(subject)) {
|
|
1003
959
|
if (ContextWasUsed.get(subject))
|
|
@@ -1020,268 +976,242 @@ function getPending(subject) {
|
|
|
1020
976
|
return pending;
|
|
1021
977
|
}
|
|
1022
978
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
if (typeof
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
return new
|
|
1058
|
-
res(task.apply(this.subject, args));
|
|
1059
|
-
}).finally(() => {
|
|
1060
|
-
pending = false;
|
|
1061
|
-
this.update(key);
|
|
1062
|
-
});
|
|
1063
|
-
};
|
|
1064
|
-
this.state[key] = undefined;
|
|
1065
|
-
setAlias(invoke, `run ${key}`);
|
|
1066
|
-
defineProperty(invoke, "active", {
|
|
1067
|
-
get: () => pending
|
|
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;
|
|
1010
|
+
}
|
|
1011
|
+
else {
|
|
1012
|
+
const local = use(refresh => {
|
|
1013
|
+
return new Subscriber(instance, () => refresh);
|
|
1068
1014
|
});
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
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
|
-
}
|
|
1093
|
-
// is a peer Model (constructor)
|
|
1094
|
-
else if (Model.isTypeof(source))
|
|
1095
|
-
getSource = pendingAccess(subject, source, key, true);
|
|
1096
|
-
// Regular function is too ambiguous so not allowed.
|
|
1097
|
-
else
|
|
1098
|
-
throw Oops$3.BadComputedSource(subject, key, source);
|
|
1099
|
-
prepare(this, key, getSource, setter, getter);
|
|
1100
|
-
});
|
|
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;
|
|
1024
|
+
}
|
|
1101
1025
|
}
|
|
1102
1026
|
|
|
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
|
-
});
|
|
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;
|
|
1155
1046
|
}
|
|
1156
1047
|
else {
|
|
1157
|
-
|
|
1048
|
+
sub.commit();
|
|
1049
|
+
update = () => {
|
|
1050
|
+
const next = compute.call(spy, spy);
|
|
1051
|
+
if (value != next)
|
|
1052
|
+
set(next);
|
|
1053
|
+
};
|
|
1158
1054
|
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
if (forKey == key)
|
|
1166
|
-
return () => {
|
|
1167
|
-
if (source.state[key] !== undefined) {
|
|
1168
|
-
release();
|
|
1169
|
-
resolve();
|
|
1170
|
-
}
|
|
1171
|
-
};
|
|
1055
|
+
defineProperty(sub, "proxy", {
|
|
1056
|
+
get() {
|
|
1057
|
+
if (value === undefined && suspend)
|
|
1058
|
+
throw new Promise(res => retry = res);
|
|
1059
|
+
return value;
|
|
1060
|
+
}
|
|
1172
1061
|
});
|
|
1062
|
+
return sub;
|
|
1173
1063
|
});
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
name: "Suspense",
|
|
1177
|
-
message: error.message,
|
|
1178
|
-
stack: error.stack
|
|
1179
|
-
});
|
|
1064
|
+
React.useLayoutEffect(() => local.release, []);
|
|
1065
|
+
return local.proxy;
|
|
1180
1066
|
}
|
|
1181
1067
|
|
|
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
|
-
}
|
|
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;
|
|
1218
1092
|
}
|
|
1093
|
+
});
|
|
1219
1094
|
}
|
|
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 };
|
|
1095
|
+
return sub;
|
|
1228
1096
|
});
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
return set(initial && (() => initial), onUpdate);
|
|
1097
|
+
React.useLayoutEffect(() => local.commit(), []);
|
|
1098
|
+
return local.proxy;
|
|
1232
1099
|
}
|
|
1233
1100
|
|
|
1234
1101
|
class MVC extends Model {
|
|
1235
1102
|
tap(path, expect) {
|
|
1236
1103
|
return useTap(this, path, expect);
|
|
1237
1104
|
}
|
|
1238
|
-
|
|
1239
|
-
return
|
|
1105
|
+
use(arg, callback) {
|
|
1106
|
+
return useModel(this, arg, callback);
|
|
1240
1107
|
}
|
|
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);
|
|
1108
|
+
static get(required) {
|
|
1109
|
+
return useInContext(this, required);
|
|
1252
1110
|
}
|
|
1253
1111
|
static tap(key, expect) {
|
|
1254
|
-
return this
|
|
1112
|
+
return useTap(this, key, expect);
|
|
1255
1113
|
}
|
|
1256
|
-
|
|
1257
|
-
|
|
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);
|
|
1258
1123
|
}
|
|
1259
|
-
static use(callback) {
|
|
1260
|
-
|
|
1124
|
+
static use(arg, callback) {
|
|
1125
|
+
const instance = useModel(this, arg, callback);
|
|
1126
|
+
usePeerContext(instance.get);
|
|
1127
|
+
return instance;
|
|
1261
1128
|
}
|
|
1262
|
-
static uses(
|
|
1129
|
+
static uses(data, only) {
|
|
1263
1130
|
return this.use(instance => {
|
|
1264
|
-
instance.import(
|
|
1131
|
+
instance.import(data, only);
|
|
1265
1132
|
});
|
|
1266
1133
|
}
|
|
1267
|
-
static using(
|
|
1134
|
+
static using(data, only) {
|
|
1268
1135
|
const instance = this.use();
|
|
1269
|
-
instance.import(
|
|
1136
|
+
instance.import(data, only);
|
|
1270
1137
|
return instance;
|
|
1271
1138
|
}
|
|
1272
1139
|
static meta(path, expect) {
|
|
1273
|
-
return
|
|
1274
|
-
? useComputed(this, path, expect)
|
|
1275
|
-
: useActive(this, path, expect);
|
|
1140
|
+
return useTap(() => this, path, expect);
|
|
1276
1141
|
}
|
|
1277
1142
|
}
|
|
1278
1143
|
|
|
1144
|
+
const Oops$2 = issues({
|
|
1145
|
+
BadConsumerProps: () => `Provider expects either a render function, 'get' or 'has' props.`
|
|
1146
|
+
});
|
|
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;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
const Oops$1 = issues({
|
|
1160
|
+
NoProviderType: () => `Provider 'of' prop must be Model, typeof Model or a collection of them.`
|
|
1161
|
+
});
|
|
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);
|
|
1170
|
+
}
|
|
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;
|
|
1193
|
+
};
|
|
1194
|
+
}, []);
|
|
1195
|
+
update(props);
|
|
1196
|
+
}
|
|
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 {};
|
|
1203
|
+
});
|
|
1204
|
+
if (hook.commit)
|
|
1205
|
+
React.useLayoutEffect(() => hook.commit(), []);
|
|
1206
|
+
return props.render(hook.proxy);
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1279
1209
|
const Oops = issues({
|
|
1280
1210
|
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
1211
|
GlobalExists: (name) => `Shared instance of ${name} already exists! Consider unmounting existing, or use ${name}.reset() to force-delete it.`
|
|
1282
1212
|
});
|
|
1283
1213
|
const Active = new WeakMap();
|
|
1284
|
-
class
|
|
1214
|
+
class Global extends MVC {
|
|
1285
1215
|
static create(...args) {
|
|
1286
1216
|
const Type = this;
|
|
1287
1217
|
if (Active.has(Type))
|
|
@@ -1290,6 +1220,11 @@ class Singleton extends MVC {
|
|
|
1290
1220
|
Active.set(Type, instance);
|
|
1291
1221
|
return instance;
|
|
1292
1222
|
}
|
|
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
|
+
*/
|
|
1293
1228
|
static set(updates) {
|
|
1294
1229
|
const instance = Active.get(this) || this.create();
|
|
1295
1230
|
instance.import(updates);
|
|
@@ -1303,6 +1238,7 @@ class Singleton extends MVC {
|
|
|
1303
1238
|
instance[arg] :
|
|
1304
1239
|
instance);
|
|
1305
1240
|
}
|
|
1241
|
+
/** Destroy current instance of Global, if it exists. */
|
|
1306
1242
|
static reset() {
|
|
1307
1243
|
const current = Active.get(this);
|
|
1308
1244
|
if (current)
|
|
@@ -1314,5 +1250,5 @@ class Singleton extends MVC {
|
|
|
1314
1250
|
}
|
|
1315
1251
|
}
|
|
1316
1252
|
|
|
1317
|
-
export { Consumer, MVC as 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 };
|
|
1318
1254
|
//# sourceMappingURL=index.esm.js.map
|