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