@expressive/mvc 1.0.0-beta.17 → 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/lib/index.js +376 -314
- package/package.json +5 -10
- package/types/instructions.d.ts +35 -6
- package/types/model.d.ts +5 -3
- package/types/types.d.ts +4 -1
package/lib/index.js
CHANGED
|
@@ -24,7 +24,7 @@ function issues(register) {
|
|
|
24
24
|
return Library;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
const Oops$
|
|
27
|
+
const Oops$9 = issues({
|
|
28
28
|
BadEffectCallback: () => `Callback for effect-callback may only return a function.`
|
|
29
29
|
});
|
|
30
30
|
const { assign, create, defineProperty, entries, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor, getOwnPropertyDescriptors, getOwnPropertySymbols, keys: keys$1, values } = Object;
|
|
@@ -53,7 +53,7 @@ function createEffect(callback) {
|
|
|
53
53
|
if (unSet instanceof Promise)
|
|
54
54
|
unSet = undefined;
|
|
55
55
|
if (unSet && typeof unSet !== "function")
|
|
56
|
-
throw Oops$
|
|
56
|
+
throw Oops$9.BadEffectCallback();
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
function createValueEffect(callback) {
|
|
@@ -69,7 +69,7 @@ function createValueEffect(callback) {
|
|
|
69
69
|
if (typeof out == "function")
|
|
70
70
|
unSet = out;
|
|
71
71
|
else
|
|
72
|
-
throw Oops$
|
|
72
|
+
throw Oops$9.BadEffectCallback();
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
75
|
function select(source, select) {
|
|
@@ -92,6 +92,179 @@ function selectRecursive(using, keys) {
|
|
|
92
92
|
return Array.from(found);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
class Subscriber {
|
|
96
|
+
constructor(parent, onUpdate) {
|
|
97
|
+
this.onUpdate = onUpdate;
|
|
98
|
+
this.active = false;
|
|
99
|
+
this.follows = {};
|
|
100
|
+
this.dependant = new Set();
|
|
101
|
+
if (!(parent instanceof Controller))
|
|
102
|
+
parent = manage(parent);
|
|
103
|
+
this.parent = parent;
|
|
104
|
+
this.source = parent.subject;
|
|
105
|
+
this.proxy = create(parent.subject);
|
|
106
|
+
define(this.proxy, LOCAL, this);
|
|
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];
|
|
117
|
+
};
|
|
118
|
+
setAlias(intercept, `tap ${key}`);
|
|
119
|
+
defineProperty(proxy, key, {
|
|
120
|
+
get: intercept,
|
|
121
|
+
set,
|
|
122
|
+
configurable: true,
|
|
123
|
+
enumerable: true
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
follow(key, cb) {
|
|
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();
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
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
|
+
}
|
|
165
|
+
}
|
|
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;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
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);
|
|
194
|
+
}
|
|
195
|
+
catch (e) {
|
|
196
|
+
if (early)
|
|
197
|
+
Oops$8.ComputedEarly(key).warn();
|
|
198
|
+
throw e;
|
|
199
|
+
}
|
|
200
|
+
finally {
|
|
201
|
+
sub.commit();
|
|
202
|
+
for (const key in sub.follows)
|
|
203
|
+
prioritize(key);
|
|
204
|
+
}
|
|
205
|
+
return current();
|
|
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
|
+
});
|
|
218
|
+
}
|
|
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
|
+
};
|
|
229
|
+
}
|
|
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);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
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;
|
|
253
|
+
}
|
|
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
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
95
268
|
const Lifecycle = {
|
|
96
269
|
WILL_RENDER: "willRender",
|
|
97
270
|
WILL_UPDATE: "willUpdate",
|
|
@@ -147,9 +320,20 @@ const Pending = new Map();
|
|
|
147
320
|
function manage(src) {
|
|
148
321
|
return src[CONTROL];
|
|
149
322
|
}
|
|
150
|
-
function
|
|
151
|
-
const
|
|
152
|
-
|
|
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
|
+
});
|
|
153
337
|
return placeholder;
|
|
154
338
|
}
|
|
155
339
|
class Controller {
|
|
@@ -273,276 +457,6 @@ function keys(from, using) {
|
|
|
273
457
|
return from.select(using);
|
|
274
458
|
}
|
|
275
459
|
|
|
276
|
-
class Subscriber {
|
|
277
|
-
constructor(parent, onUpdate) {
|
|
278
|
-
this.onUpdate = onUpdate;
|
|
279
|
-
this.active = false;
|
|
280
|
-
this.follows = {};
|
|
281
|
-
this.dependant = new Set();
|
|
282
|
-
if (!(parent instanceof Controller))
|
|
283
|
-
parent = manage(parent);
|
|
284
|
-
this.parent = parent;
|
|
285
|
-
this.source = parent.subject;
|
|
286
|
-
this.proxy = create(parent.subject);
|
|
287
|
-
define(this.proxy, LOCAL, this);
|
|
288
|
-
for (const key in parent.state)
|
|
289
|
-
this.spy(key);
|
|
290
|
-
}
|
|
291
|
-
spy(key) {
|
|
292
|
-
const { proxy, source } = this;
|
|
293
|
-
const { set } = getOwnPropertyDescriptor(source, key);
|
|
294
|
-
const intercept = () => {
|
|
295
|
-
this.follow(key);
|
|
296
|
-
delete proxy[key];
|
|
297
|
-
return proxy[key];
|
|
298
|
-
};
|
|
299
|
-
setAlias(intercept, `tap ${key}`);
|
|
300
|
-
defineProperty(proxy, key, {
|
|
301
|
-
get: intercept,
|
|
302
|
-
set,
|
|
303
|
-
configurable: true,
|
|
304
|
-
enumerable: true
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
follow(key, cb) {
|
|
308
|
-
this.follows[key] = cb || this.onUpdate;
|
|
309
|
-
}
|
|
310
|
-
commit() {
|
|
311
|
-
const { dependant, follows, parent } = this;
|
|
312
|
-
const onDone = parent.addListener(follows);
|
|
313
|
-
this.active = true;
|
|
314
|
-
dependant.forEach(x => x.commit());
|
|
315
|
-
return this.release = () => {
|
|
316
|
-
dependant.forEach(x => x.release());
|
|
317
|
-
onDone();
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
const Oops$7 = issues({
|
|
323
|
-
ComputeFailed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
|
|
324
|
-
ComputedEarly: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
|
|
325
|
-
});
|
|
326
|
-
const ComputedInit = new WeakSet();
|
|
327
|
-
const ComputedInfo = new WeakMap();
|
|
328
|
-
const ComputedUsed = new WeakMap();
|
|
329
|
-
const ComputedKeys = new WeakMap();
|
|
330
|
-
function getRegister(on) {
|
|
331
|
-
let register = ComputedUsed.get(on);
|
|
332
|
-
if (!register)
|
|
333
|
-
ComputedUsed.set(on, register = new Map());
|
|
334
|
-
return register;
|
|
335
|
-
}
|
|
336
|
-
function prepareGetters(on) {
|
|
337
|
-
const defined = getRegister(on);
|
|
338
|
-
for (let scan = on.subject; scan.constructor !== Object; scan = getPrototypeOf(scan))
|
|
339
|
-
for (const key of getOwnPropertyNames(scan)) {
|
|
340
|
-
const { get, set } = getOwnPropertyDescriptor(scan, key);
|
|
341
|
-
if (get && !defined.has(key))
|
|
342
|
-
prepare(on, key, get, set);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
function prepare(on, key, getter, setter) {
|
|
346
|
-
const { state, subject } = on;
|
|
347
|
-
const defined = getRegister(on);
|
|
348
|
-
const info = {
|
|
349
|
-
key, parent: on, priority: 1
|
|
350
|
-
};
|
|
351
|
-
let sub;
|
|
352
|
-
defined.set(key, info);
|
|
353
|
-
function compute(initial) {
|
|
354
|
-
try {
|
|
355
|
-
return getter.call(sub.proxy, sub.proxy);
|
|
356
|
-
}
|
|
357
|
-
catch (err) {
|
|
358
|
-
Oops$7.ComputeFailed(subject, key, !!initial).warn();
|
|
359
|
-
throw err;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
function update() {
|
|
363
|
-
let value;
|
|
364
|
-
try {
|
|
365
|
-
value = compute(false);
|
|
366
|
-
}
|
|
367
|
-
catch (e) {
|
|
368
|
-
console.error(e);
|
|
369
|
-
}
|
|
370
|
-
finally {
|
|
371
|
-
if (state[key] !== value) {
|
|
372
|
-
on.update(key, value);
|
|
373
|
-
return value;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
function create(early) {
|
|
378
|
-
sub = new Subscriber(on, update);
|
|
379
|
-
ComputedInfo.set(update, info);
|
|
380
|
-
defineProperty(state, key, {
|
|
381
|
-
value: undefined,
|
|
382
|
-
writable: true
|
|
383
|
-
});
|
|
384
|
-
defineProperty(subject, key, {
|
|
385
|
-
enumerable: true,
|
|
386
|
-
configurable: true,
|
|
387
|
-
get: () => state[key],
|
|
388
|
-
set: setter
|
|
389
|
-
});
|
|
390
|
-
try {
|
|
391
|
-
return state[key] = compute(true);
|
|
392
|
-
}
|
|
393
|
-
catch (e) {
|
|
394
|
-
if (early)
|
|
395
|
-
Oops$7.ComputedEarly(key).warn();
|
|
396
|
-
throw e;
|
|
397
|
-
}
|
|
398
|
-
finally {
|
|
399
|
-
sub.commit();
|
|
400
|
-
for (const key in sub.follows) {
|
|
401
|
-
const peer = defined.get(key);
|
|
402
|
-
if (peer && peer.priority >= info.priority)
|
|
403
|
-
info.priority = peer.priority + 1;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
function revert(value) {
|
|
408
|
-
delete state[key];
|
|
409
|
-
defineProperty(subject, key, {
|
|
410
|
-
enumerable: true,
|
|
411
|
-
configurable: true,
|
|
412
|
-
value
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
setAlias(update, `try ${key}`);
|
|
416
|
-
setAlias(create, `new ${key}`);
|
|
417
|
-
setAlias(getter, `run ${key}`);
|
|
418
|
-
ComputedInit.add(create);
|
|
419
|
-
for (const on of [state, subject])
|
|
420
|
-
defineProperty(on, key, {
|
|
421
|
-
get: create,
|
|
422
|
-
set: setter || revert,
|
|
423
|
-
configurable: true,
|
|
424
|
-
enumerable: true
|
|
425
|
-
});
|
|
426
|
-
return info;
|
|
427
|
-
}
|
|
428
|
-
function ensure(on, keys) {
|
|
429
|
-
for (const key of keys) {
|
|
430
|
-
const desc = getOwnPropertyDescriptor(on.subject, key);
|
|
431
|
-
const getter = desc && desc.get;
|
|
432
|
-
if (ComputedInit.has(getter))
|
|
433
|
-
getter(true);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
function capture(on, request) {
|
|
437
|
-
const compute = ComputedInfo.get(request);
|
|
438
|
-
const callback = request;
|
|
439
|
-
if (!compute)
|
|
440
|
-
return;
|
|
441
|
-
let pending = ComputedKeys.get(on);
|
|
442
|
-
if (!pending)
|
|
443
|
-
ComputedKeys.set(on, pending = []);
|
|
444
|
-
if (compute.parent !== on)
|
|
445
|
-
callback();
|
|
446
|
-
else {
|
|
447
|
-
const queue = pending.findIndex(peer => compute.priority > ComputedInfo.get(peer).priority);
|
|
448
|
-
pending.splice(queue + 1, 0, callback);
|
|
449
|
-
}
|
|
450
|
-
return true;
|
|
451
|
-
}
|
|
452
|
-
function flush(on) {
|
|
453
|
-
const handled = on.frame;
|
|
454
|
-
let pending = ComputedKeys.get(on);
|
|
455
|
-
if (pending) {
|
|
456
|
-
while (pending.length) {
|
|
457
|
-
const compute = pending.shift();
|
|
458
|
-
const { key } = ComputedInfo.get(compute);
|
|
459
|
-
if (!handled.has(key))
|
|
460
|
-
compute();
|
|
461
|
-
}
|
|
462
|
-
ComputedKeys.delete(on);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
const Oops$6 = issues({
|
|
467
|
-
DuplicateAction: (key) => `Invoked action ${key} but one is already active.`,
|
|
468
|
-
SourceNotSupported: () => `Computed from-instruction does not support source other than 'this'.`
|
|
469
|
-
});
|
|
470
|
-
const set = (fn, name) => does(function set(key) {
|
|
471
|
-
let output = fn.call(this, key, this);
|
|
472
|
-
if (typeof output == "function") {
|
|
473
|
-
const getter = output;
|
|
474
|
-
output = Object.assign(Object.assign({}, getOwnPropertyDescriptor(this.subject, key)), { get() {
|
|
475
|
-
return getter(this[LOCAL]);
|
|
476
|
-
} });
|
|
477
|
-
}
|
|
478
|
-
if (output)
|
|
479
|
-
defineProperty(this.subject, key, output);
|
|
480
|
-
}, name || fn.name || "pending");
|
|
481
|
-
const ref = (cb) => set(function ref(key) {
|
|
482
|
-
const refObjectFunction = this.setter(key, cb && createValueEffect(cb));
|
|
483
|
-
defineProperty(refObjectFunction, "current", {
|
|
484
|
-
set: refObjectFunction,
|
|
485
|
-
get: () => this.state[key]
|
|
486
|
-
});
|
|
487
|
-
return {
|
|
488
|
-
value: refObjectFunction
|
|
489
|
-
};
|
|
490
|
-
});
|
|
491
|
-
const on = (initial, cb) => set(function on(key) {
|
|
492
|
-
this.manage(key, initial, cb && createValueEffect(cb));
|
|
493
|
-
});
|
|
494
|
-
const memo = (factory, defer) => set(function memo(key) {
|
|
495
|
-
const source = this.subject;
|
|
496
|
-
const get = () => factory.call(source);
|
|
497
|
-
if (defer)
|
|
498
|
-
defineLazy(source, key, get);
|
|
499
|
-
else
|
|
500
|
-
define(source, key, get());
|
|
501
|
-
});
|
|
502
|
-
const lazy = (value) => set(function lazy(key) {
|
|
503
|
-
const source = this.subject;
|
|
504
|
-
source[key] = value;
|
|
505
|
-
defineProperty(this.state, key, {
|
|
506
|
-
get: () => source[key]
|
|
507
|
-
});
|
|
508
|
-
});
|
|
509
|
-
const act = (task) => set(function act(key) {
|
|
510
|
-
let pending = false;
|
|
511
|
-
const invoke = (...args) => {
|
|
512
|
-
if (pending)
|
|
513
|
-
return Promise.reject(Oops$6.DuplicateAction(key));
|
|
514
|
-
pending = true;
|
|
515
|
-
this.update(key);
|
|
516
|
-
return new Promise(res => {
|
|
517
|
-
res(task.apply(this.subject, args));
|
|
518
|
-
}).finally(() => {
|
|
519
|
-
pending = false;
|
|
520
|
-
this.update(key);
|
|
521
|
-
});
|
|
522
|
-
};
|
|
523
|
-
setAlias(invoke, `run ${key}`);
|
|
524
|
-
defineProperty(invoke, "active", {
|
|
525
|
-
get: () => pending
|
|
526
|
-
});
|
|
527
|
-
return {
|
|
528
|
-
value: invoke,
|
|
529
|
-
writable: false
|
|
530
|
-
};
|
|
531
|
-
});
|
|
532
|
-
function from(source, fn) {
|
|
533
|
-
return set(function from(key) {
|
|
534
|
-
let getter;
|
|
535
|
-
if (typeof source == "function")
|
|
536
|
-
getter = source.call(this.subject, key);
|
|
537
|
-
else {
|
|
538
|
-
if (source !== this.subject)
|
|
539
|
-
throw Oops$6.SourceNotSupported();
|
|
540
|
-
getter = fn;
|
|
541
|
-
}
|
|
542
|
-
prepare(this, key, getter);
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
|
|
546
460
|
const attach = (from, name) => set(function attach(key) {
|
|
547
461
|
const proxyCache = new WeakMap();
|
|
548
462
|
const getCurrent = from.call(this, key);
|
|
@@ -580,7 +494,7 @@ const attach = (from, name) => set(function attach(key) {
|
|
|
580
494
|
};
|
|
581
495
|
}, name || from.name);
|
|
582
496
|
|
|
583
|
-
const Oops$
|
|
497
|
+
const Oops$7 = issues({
|
|
584
498
|
HasPropertyUndefined: (control, property) => `${control}.${property} is marked as required for this render.`
|
|
585
499
|
});
|
|
586
500
|
const useElementLifecycle = lifecycle("element");
|
|
@@ -609,7 +523,7 @@ function useWatcher(target, focus, expected) {
|
|
|
609
523
|
defineProperty(sub, "proxy", {
|
|
610
524
|
get() {
|
|
611
525
|
if (src[key] === undefined && expected)
|
|
612
|
-
throw Oops$
|
|
526
|
+
throw Oops$7.HasPropertyUndefined(target, key);
|
|
613
527
|
return src[key];
|
|
614
528
|
}
|
|
615
529
|
});
|
|
@@ -697,7 +611,7 @@ class Lookup {
|
|
|
697
611
|
}
|
|
698
612
|
}
|
|
699
613
|
|
|
700
|
-
const Oops$
|
|
614
|
+
const Oops$6 = issues({
|
|
701
615
|
NoProviderType: () => `Provider 'of' prop must be Model, typeof Model or a collection of them.`,
|
|
702
616
|
NothingInContext: (name) => `Couldn't find ${name} in context; did you forget to use a Provider?`,
|
|
703
617
|
BadConsumerProps: () => `Provider expects either a render function, 'get' or 'has' props.`
|
|
@@ -707,7 +621,7 @@ const useLookup = () => react.useContext(LookupContext);
|
|
|
707
621
|
function useFromContext(Type, strict) {
|
|
708
622
|
const instance = useLookup().get(Type);
|
|
709
623
|
if (!instance && strict)
|
|
710
|
-
throw Oops$
|
|
624
|
+
throw Oops$6.NothingInContext(Type.name);
|
|
711
625
|
return instance;
|
|
712
626
|
}
|
|
713
627
|
function Consumer(props) {
|
|
@@ -718,13 +632,13 @@ function Consumer(props) {
|
|
|
718
632
|
if (typeof callback == "function")
|
|
719
633
|
callback(Control.get(!!has));
|
|
720
634
|
else
|
|
721
|
-
throw Oops$
|
|
635
|
+
throw Oops$6.BadConsumerProps();
|
|
722
636
|
return null;
|
|
723
637
|
}
|
|
724
638
|
function useNewContext(from, inject) {
|
|
725
639
|
return react.useMemo(() => {
|
|
726
640
|
if (!inject)
|
|
727
|
-
throw Oops$
|
|
641
|
+
throw Oops$6.NoProviderType();
|
|
728
642
|
const context = from.push(inject);
|
|
729
643
|
for (const instance of context.local)
|
|
730
644
|
for (const apply of getPending(instance))
|
|
@@ -767,30 +681,32 @@ function Provider(props) {
|
|
|
767
681
|
: render);
|
|
768
682
|
}
|
|
769
683
|
|
|
770
|
-
const Oops$
|
|
684
|
+
const Oops$5 = issues({
|
|
771
685
|
CantAttachGlobal: (parent, child) => `Singleton '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
|
|
772
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.`
|
|
773
687
|
});
|
|
774
688
|
const PendingContext = new WeakMap();
|
|
775
689
|
const ContextWasUsed = new WeakMap();
|
|
776
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) {
|
|
777
694
|
if ("current" in type)
|
|
778
695
|
return () => type.current;
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
throw Oops$3.CantAttachGlobal(to, type.name);
|
|
696
|
+
if ("current" in from.constructor)
|
|
697
|
+
throw Oops$5.CantAttachGlobal(from, type.name);
|
|
782
698
|
let instance;
|
|
783
|
-
getPending(
|
|
699
|
+
getPending(from).push(context => {
|
|
784
700
|
instance = context.get(type);
|
|
785
701
|
if (typeof argument == "function") {
|
|
786
702
|
if (argument(instance) === false)
|
|
787
703
|
instance = undefined;
|
|
788
704
|
}
|
|
789
705
|
else if (!instance && argument)
|
|
790
|
-
throw Oops$
|
|
706
|
+
throw Oops$5.AmbientRequired(type.name, from, key);
|
|
791
707
|
});
|
|
792
708
|
return () => instance;
|
|
793
|
-
}
|
|
709
|
+
}
|
|
794
710
|
function usePeerContext(subject) {
|
|
795
711
|
if (ContextWasUsed.has(subject)) {
|
|
796
712
|
if (ContextWasUsed.get(subject))
|
|
@@ -813,17 +729,15 @@ function getPending(subject) {
|
|
|
813
729
|
return pending;
|
|
814
730
|
}
|
|
815
731
|
|
|
816
|
-
const Oops$
|
|
732
|
+
const Oops$4 = issues({
|
|
817
733
|
StrictUpdate: (expected) => `Strict update() did ${expected ? "not " : ""}find pending updates.`,
|
|
818
734
|
NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
|
|
819
735
|
});
|
|
820
736
|
class State {
|
|
821
|
-
constructor(
|
|
822
|
-
|
|
737
|
+
constructor() {
|
|
738
|
+
Controller.setup(this);
|
|
823
739
|
define(this, "get", this);
|
|
824
740
|
define(this, "set", this);
|
|
825
|
-
if (computeGetters !== false)
|
|
826
|
-
prepareGetters(control);
|
|
827
741
|
}
|
|
828
742
|
import(from, subset) {
|
|
829
743
|
for (const key of keys(manage(this), subset))
|
|
@@ -843,15 +757,18 @@ class State {
|
|
|
843
757
|
arg = select(this, arg);
|
|
844
758
|
if (typeof arg == "boolean") {
|
|
845
759
|
if (!control.pending === arg)
|
|
846
|
-
return Promise.reject(Oops$
|
|
760
|
+
return Promise.reject(Oops$4.StrictUpdate(arg));
|
|
847
761
|
}
|
|
848
762
|
else if (arg) {
|
|
849
763
|
control.update(arg);
|
|
850
|
-
if (1 in arguments && arg in this)
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
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
|
+
}
|
|
855
772
|
}
|
|
856
773
|
return {
|
|
857
774
|
then(callback) {
|
|
@@ -861,7 +778,7 @@ class State {
|
|
|
861
778
|
else
|
|
862
779
|
callback(false);
|
|
863
780
|
else
|
|
864
|
-
throw Oops$
|
|
781
|
+
throw Oops$4.NoChaining();
|
|
865
782
|
}
|
|
866
783
|
};
|
|
867
784
|
}
|
|
@@ -872,7 +789,9 @@ class State {
|
|
|
872
789
|
return this.constructor.name;
|
|
873
790
|
}
|
|
874
791
|
tap(path, expect) {
|
|
875
|
-
|
|
792
|
+
const proxy = useWatcher(this, path, expect);
|
|
793
|
+
this.update("willRender", true);
|
|
794
|
+
return proxy;
|
|
876
795
|
}
|
|
877
796
|
static create(...args) {
|
|
878
797
|
const instance = new this(...args);
|
|
@@ -880,7 +799,9 @@ class State {
|
|
|
880
799
|
return instance;
|
|
881
800
|
}
|
|
882
801
|
static new(args, callback) {
|
|
883
|
-
|
|
802
|
+
const instance = useLazy(this, args, callback);
|
|
803
|
+
instance.update("willRender", true);
|
|
804
|
+
return instance;
|
|
884
805
|
}
|
|
885
806
|
static use(args, callback) {
|
|
886
807
|
return useModel(this, args, callback);
|
|
@@ -907,7 +828,7 @@ class State {
|
|
|
907
828
|
instance);
|
|
908
829
|
}
|
|
909
830
|
static tap(key, expect) {
|
|
910
|
-
return
|
|
831
|
+
return this.find(true).tap(key, expect);
|
|
911
832
|
}
|
|
912
833
|
static isTypeof(maybe) {
|
|
913
834
|
return (typeof maybe == "function" &&
|
|
@@ -921,9 +842,6 @@ State.LOCAL = LOCAL;
|
|
|
921
842
|
|
|
922
843
|
const useComponentLifecycle = lifecycle("component");
|
|
923
844
|
class Model extends State {
|
|
924
|
-
constructor() {
|
|
925
|
-
super(false);
|
|
926
|
-
}
|
|
927
845
|
[CREATE](using) {
|
|
928
846
|
defer(using, "on");
|
|
929
847
|
defer(using, "effect");
|
|
@@ -986,7 +904,7 @@ class Model extends State {
|
|
|
986
904
|
return instance;
|
|
987
905
|
}
|
|
988
906
|
static tag(id) {
|
|
989
|
-
return
|
|
907
|
+
return this.find(true).tag(id);
|
|
990
908
|
}
|
|
991
909
|
static meta(path) {
|
|
992
910
|
return useWatcher(this, path);
|
|
@@ -1008,7 +926,7 @@ function defer(on, method) {
|
|
|
1008
926
|
}
|
|
1009
927
|
|
|
1010
928
|
const Parent = new WeakMap();
|
|
1011
|
-
const Oops$
|
|
929
|
+
const Oops$3 = issues({
|
|
1012
930
|
ParentRequired: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
|
|
1013
931
|
UnexpectedParent: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
|
|
1014
932
|
UndefinedNotAllowed: (key) => `Child property ${key} may not be undefined.`,
|
|
@@ -1034,7 +952,7 @@ const use = (input, argument) => attach(function use(key) {
|
|
|
1034
952
|
if (typeof argument == "function")
|
|
1035
953
|
argument(instance);
|
|
1036
954
|
else if (!instance && argument !== false)
|
|
1037
|
-
throw Oops$
|
|
955
|
+
throw Oops$3.UndefinedNotAllowed(key);
|
|
1038
956
|
};
|
|
1039
957
|
if (input) {
|
|
1040
958
|
instance =
|
|
@@ -1042,7 +960,7 @@ const use = (input, argument) => attach(function use(key) {
|
|
|
1042
960
|
input instanceof Model ? input :
|
|
1043
961
|
typeof input === "function" ? input() :
|
|
1044
962
|
typeof input === "object" ? bootstrap(input) :
|
|
1045
|
-
Oops$
|
|
963
|
+
Oops$3.BadArgument(typeof input).throw();
|
|
1046
964
|
if (instance)
|
|
1047
965
|
update(instance);
|
|
1048
966
|
}
|
|
@@ -1057,13 +975,156 @@ const parent = (Expects, required) => set(function parent() {
|
|
|
1057
975
|
const value = Parent.get(this.subject);
|
|
1058
976
|
if (!value) {
|
|
1059
977
|
if (required)
|
|
1060
|
-
throw Oops$
|
|
978
|
+
throw Oops$3.ParentRequired(expected, child);
|
|
1061
979
|
}
|
|
1062
980
|
else if (!(value instanceof Expects))
|
|
1063
|
-
throw Oops$
|
|
981
|
+
throw Oops$3.UnexpectedParent(expected, child, value);
|
|
1064
982
|
return { value };
|
|
1065
983
|
});
|
|
1066
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));
|
|
993
|
+
defineProperty(refObjectFunction, "current", {
|
|
994
|
+
set: refObjectFunction,
|
|
995
|
+
get: () => this.state[key]
|
|
996
|
+
});
|
|
997
|
+
return {
|
|
998
|
+
value: refObjectFunction
|
|
999
|
+
};
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
function on(initial, cb) {
|
|
1003
|
+
return set(function on(key) {
|
|
1004
|
+
this.manage(key, initial, cb && createValueEffect(cb));
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
function memo(factory, defer) {
|
|
1008
|
+
return set(function memo(key) {
|
|
1009
|
+
const source = this.subject;
|
|
1010
|
+
const get = () => factory.call(source);
|
|
1011
|
+
if (defer)
|
|
1012
|
+
defineLazy(source, key, get);
|
|
1013
|
+
else
|
|
1014
|
+
define(source, key, get());
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
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]
|
|
1023
|
+
});
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
function act(task) {
|
|
1027
|
+
return set(function act(key) {
|
|
1028
|
+
let pending = false;
|
|
1029
|
+
const invoke = (...args) => {
|
|
1030
|
+
if (pending)
|
|
1031
|
+
return Promise.reject(Oops$2.DuplicateAction(key));
|
|
1032
|
+
pending = true;
|
|
1033
|
+
this.update(key);
|
|
1034
|
+
return new Promise(res => {
|
|
1035
|
+
res(task.apply(this.subject, args));
|
|
1036
|
+
}).finally(() => {
|
|
1037
|
+
pending = false;
|
|
1038
|
+
this.update(key);
|
|
1039
|
+
});
|
|
1040
|
+
};
|
|
1041
|
+
setAlias(invoke, `run ${key}`);
|
|
1042
|
+
defineProperty(invoke, "active", {
|
|
1043
|
+
get: () => pending
|
|
1044
|
+
});
|
|
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();
|
|
1089
|
+
});
|
|
1090
|
+
});
|
|
1091
|
+
return Object.assign(promise, { message, stack });
|
|
1092
|
+
}
|
|
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
|
+
};
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1067
1128
|
const Oops = issues({
|
|
1068
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.`,
|
|
1069
1130
|
GlobalExists: (name) => `Shared instance of ${name} already exists! Consider unmounting existing, or use ${name}.reset() to force-delete it.`
|
|
@@ -1112,12 +1173,13 @@ exports.Singleton = Singleton;
|
|
|
1112
1173
|
exports.State = State;
|
|
1113
1174
|
exports.act = act;
|
|
1114
1175
|
exports.attach = attach;
|
|
1115
|
-
exports
|
|
1176
|
+
exports["default"] = Model;
|
|
1116
1177
|
exports.from = from;
|
|
1117
1178
|
exports.lazy = lazy;
|
|
1118
1179
|
exports.memo = memo;
|
|
1119
1180
|
exports.on = on;
|
|
1120
1181
|
exports.parent = parent;
|
|
1182
|
+
exports.pending = pending;
|
|
1121
1183
|
exports.ref = ref;
|
|
1122
1184
|
exports.set = set;
|
|
1123
1185
|
exports.tap = tap;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expressive/mvc",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.20",
|
|
4
4
|
"description": "Use classes to define state in React!",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Gabe Klein",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"keywords": [
|
|
16
16
|
"react",
|
|
17
|
-
"
|
|
17
|
+
"mvc"
|
|
18
18
|
],
|
|
19
19
|
"main": "lib/index.js",
|
|
20
20
|
"types": "types/index.d.ts",
|
|
@@ -24,14 +24,9 @@
|
|
|
24
24
|
],
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "rollup --config",
|
|
27
|
-
"watch": "
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"preversion": "jest --coverage",
|
|
31
|
-
"postversion": "npm run build; npm publish",
|
|
32
|
-
"postpublish": "git push; git push --tags; rm -rf lib",
|
|
33
|
-
"coverage": "jest --collectCoverage",
|
|
34
|
-
"test": "jest --watchAll"
|
|
27
|
+
"watch": "jest --watchAll",
|
|
28
|
+
"test": "jest --collectCoverage",
|
|
29
|
+
"prebuild": "rm -rf lib/"
|
|
35
30
|
},
|
|
36
31
|
"peerDependencies": {
|
|
37
32
|
"react": ">=17.0.2"
|
package/types/instructions.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Model
|
|
1
|
+
import Model from '.';
|
|
2
2
|
import { Async, Class, InstanceOf, InterceptCallback } from './types';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -143,16 +143,25 @@ export function act <T extends Async> (action: T): T & { active: boolean };
|
|
|
143
143
|
*
|
|
144
144
|
* @param fn - Factory function to generate a getter to subscribe dependancies.
|
|
145
145
|
*/
|
|
146
|
-
export function from <R> (fn: (property: string) => (this: any,
|
|
146
|
+
export function from <R> (fn: (property: string) => (this: any, state: any) => R): R;
|
|
147
147
|
|
|
148
148
|
/**
|
|
149
149
|
* Implement a computed value; output will be generated by provided function.
|
|
150
150
|
*
|
|
151
|
-
* @param source - Source
|
|
152
|
-
* **Note:** Currently will only support `this`.
|
|
151
|
+
* @param source - Source Model to find and subscribe to, for input.
|
|
153
152
|
*
|
|
154
153
|
* @param fn - Compute function. Bound to a subscriber-proxy of source, returns output value.
|
|
155
|
-
* Will update automatically as
|
|
154
|
+
* Will update automatically as input values change.
|
|
155
|
+
*/
|
|
156
|
+
export function from <T extends Class, R> (source: T, fn: (this: InstanceOf<T>, on: InstanceOf<T>) => R): R;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Implement a computed value; output will be generated by provided function.
|
|
160
|
+
*
|
|
161
|
+
* @param source - Source model from which computed value will be a subscriber.
|
|
162
|
+
*
|
|
163
|
+
* @param fn - Compute function. Bound to a subscriber-proxy of source, returns output value.
|
|
164
|
+
* Will update automatically as input values change.
|
|
156
165
|
*/
|
|
157
166
|
export function from <T, R> (source: T, fn: (this: T, on: T) => R): R;
|
|
158
167
|
|
|
@@ -170,4 +179,24 @@ export function memo <T> (compute: () => T, lazy?: boolean): T;
|
|
|
170
179
|
*
|
|
171
180
|
* @param value - starting value of property.
|
|
172
181
|
*/
|
|
173
|
-
export function lazy <T> (value?: T): T;
|
|
182
|
+
export function lazy <T> (value?: T): T;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Suspend during the execution of given function to obtain value.
|
|
186
|
+
*
|
|
187
|
+
* @param waitFor - Async function to suspend for.
|
|
188
|
+
*/
|
|
189
|
+
export function pending (waitFor: () => Promise<void>): undefined;
|
|
190
|
+
export function pending <T> (waitFor: () => Promise<T>): T;
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Suspend for a computed value; output will be generated by provided function.
|
|
194
|
+
* Will throw a promise if value is accessed while undefined, promise will
|
|
195
|
+
* resolve once value gains any value.
|
|
196
|
+
*
|
|
197
|
+
* @param source - Source model from which computed value will be a subscriber.
|
|
198
|
+
*
|
|
199
|
+
* @param fn - Compute function. Bound to a subscriber-proxy of source, returns output value.
|
|
200
|
+
* Will update automatically as input values change.
|
|
201
|
+
*/
|
|
202
|
+
export function pending <T, R> (source: T, fn: (this: T, on: T) => R): Exclude<R, undefined>;
|
package/types/model.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { Key } from 'react';
|
|
2
|
+
|
|
1
3
|
import Lifecycle from './lifecycle';
|
|
2
4
|
import { Select, Selector } from './selector';
|
|
3
|
-
import { Class, InstanceOf,
|
|
5
|
+
import { BunchOf, Callback, Class, InstanceOf, Query, RequestCallback, UpdateCallback } from './types';
|
|
4
6
|
|
|
5
7
|
interface PropertyDescriptor<T> {
|
|
6
8
|
configurable?: boolean;
|
|
@@ -428,7 +430,7 @@ export abstract class Model extends State {
|
|
|
428
430
|
*
|
|
429
431
|
* @param id - Argument passed to controller-lifecycle methods. Use to identify the consumer.
|
|
430
432
|
*/
|
|
431
|
-
tag(id?:
|
|
433
|
+
tag(id?: any): this;
|
|
432
434
|
|
|
433
435
|
/**
|
|
434
436
|
* **React Hook** - Attach to instance of this controller within ambient component.
|
|
@@ -437,7 +439,7 @@ export abstract class Model extends State {
|
|
|
437
439
|
*
|
|
438
440
|
* @param idFactory - Will be invoked with fetched instance. Use this to register a tag as-needed.
|
|
439
441
|
*/
|
|
440
|
-
tag(idFactory: (idFactory: this) =>
|
|
442
|
+
tag(idFactory: (idFactory: this) => any): this;
|
|
441
443
|
|
|
442
444
|
|
|
443
445
|
/**
|
package/types/types.d.ts
CHANGED
|
@@ -12,4 +12,7 @@ export type EffectCallback<T, A = T> = (this: T, argument: A) => Callback | Prom
|
|
|
12
12
|
export type UpdateCallback<T, P> = (this: T, value: IfApplicable<T, P>, changed: P) => void;
|
|
13
13
|
export type InterceptCallback<T> = (argument: T) => ((next: T) => void) | Promise<any> | void | boolean;
|
|
14
14
|
|
|
15
|
-
export type RefFunction = (e: HTMLElement | null) => void;
|
|
15
|
+
export type RefFunction = (e: HTMLElement | null) => void;
|
|
16
|
+
|
|
17
|
+
export type Recursive<T> = { [K in keyof T]: Recursive<Omit<T, K>> };
|
|
18
|
+
export type Query = (select: Recursive<{}>) => void;
|