@expressive/mvc 0.29.2 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.d.ts +26 -28
- package/lib/index.esm.js +19 -17
- package/lib/index.js +19 -17
- package/package.json +2 -2
package/lib/index.d.ts
CHANGED
|
@@ -58,18 +58,20 @@ declare global {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
type
|
|
62
|
-
type
|
|
61
|
+
type SelectOne<T, K extends Model.Key<T>> = K;
|
|
62
|
+
type SelectFew<T, K extends Model.Key<T>> = K[];
|
|
63
|
+
type Select<T, K extends Model.Key<T> = Model.Key<T>> = K | K[];
|
|
64
|
+
type Export<T, S> = S extends SelectOne<T, infer P> ? T[P] : S extends SelectFew<T, infer P> ? Model.Export<T, P> : never;
|
|
65
|
+
type OnUpdate<T, S> = (this: T, value: Export<T, S>, updated: Model.Event<T>[]) => void;
|
|
63
66
|
interface Observable {
|
|
64
67
|
get(): Model.Export<this>;
|
|
68
|
+
get<P extends Select<this>>(select: P): Export<this, P>;
|
|
69
|
+
get<P extends Select<this>>(select: P, onUpdate: OnUpdate<this, P>, once?: true): Callback;
|
|
70
|
+
get<P extends Select<this>>(select: P, onUpdate: OnUpdate<this, P>, initial?: false): Callback;
|
|
65
71
|
get(effect: Model.Effect<this>): Callback;
|
|
66
|
-
get<P extends Model.Key<this>>(select: P): this[P];
|
|
67
|
-
get<P extends Model.Key<this>>(select: P, onUpdate: OnValue<this, P>, once?: boolean): Callback;
|
|
68
|
-
get<P extends Model.Key<this>>(select: P[]): Model.Export<this, P>;
|
|
69
|
-
get<P extends Model.Key<this>>(select: P[], onUpdate: OnValues<this, P>, once?: boolean): Callback;
|
|
70
72
|
set(): Promise<Model.Event<this>[]>;
|
|
71
73
|
set(timeout: number): Promise<Model.Event<this>[] | false>;
|
|
72
|
-
set<T extends Model.Values<this>>(
|
|
74
|
+
set<T extends Model.Values<this>>(from: T, only?: (keyof T)[]): Promise<Model.Event<T>[] | false>;
|
|
73
75
|
set<K extends Model.Key<this>>(key: K, value: Model.Value<this[K]>): Promise<Model.Event<this>[] | false>;
|
|
74
76
|
set<K extends Model.Event<this>>(key: K): Promise<Model.Event<this>[] | false>;
|
|
75
77
|
}
|
|
@@ -82,34 +84,23 @@ declare namespace Model {
|
|
|
82
84
|
type Type<T extends Model = Model> = abstract new (...args: any[]) => T;
|
|
83
85
|
/** A typeof Model, specifically one which can be created without any arguments. */
|
|
84
86
|
type New<T extends Model = Model> = (new () => T) & typeof Model;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
current: T | null;
|
|
90
|
-
};
|
|
91
|
-
/**
|
|
92
|
-
* Subset of `keyof T` which are not methods or defined by base Model U.
|
|
93
|
-
**/
|
|
94
|
-
type Key<T, U = Model> = Extract<Exclude<keyof T, keyof U>, string>;
|
|
87
|
+
/** A callback function which is subscribed to parent and updates when values change. */
|
|
88
|
+
type Effect<T> = (this: T, argument: T) => Callback | Promise<void> | void;
|
|
89
|
+
/** Subset of `keyof T` which are not methods or defined by base Model U. **/
|
|
90
|
+
type Key<T, U = Observable> = Extract<Exclude<keyof T, keyof U>, string>;
|
|
95
91
|
/** Including but not limited to `keyof T` which are not methods or defined by base Model. */
|
|
96
92
|
type Event<T> = Key<T> | (string & {});
|
|
97
|
-
/** Actual value stored in state. */
|
|
98
|
-
type Value<R> = R extends Ref<infer T> ? T : R extends Observable ? Export<R> : R;
|
|
99
93
|
/** Object comperable to data found in T. */
|
|
100
94
|
type Values<T> = {
|
|
101
95
|
[P in Key<T>]?: Value<T[P]>;
|
|
102
96
|
};
|
|
103
97
|
/**
|
|
104
98
|
* Values from current state of given controller.
|
|
105
|
-
*
|
|
106
|
-
* Differs from `Entries` as values here will drill into "real" values held by exotics like ref.
|
|
99
|
+
* Differs from `Values` as values here will drill into "real" values held by exotics like ref.
|
|
107
100
|
*/
|
|
108
101
|
type Export<T, K extends Key<T> = Key<T>> = {
|
|
109
102
|
[P in K]: Value<T[P]>;
|
|
110
103
|
};
|
|
111
|
-
/** Promise thrown by something which is not yet ready. */
|
|
112
|
-
type Suspense = Promise<void> & Error;
|
|
113
104
|
/**
|
|
114
105
|
* Reference to `this` without a subscription.
|
|
115
106
|
* Use to obtain full reference from a destructure.
|
|
@@ -117,6 +108,13 @@ declare namespace Model {
|
|
|
117
108
|
type Focus<T> = T & {
|
|
118
109
|
is: T;
|
|
119
110
|
};
|
|
111
|
+
/** Exotic value, actual value is contained. */
|
|
112
|
+
type Ref<T = any> = {
|
|
113
|
+
(next: T): void;
|
|
114
|
+
current: T | null;
|
|
115
|
+
};
|
|
116
|
+
/** Actual value stored in state. */
|
|
117
|
+
type Value<R> = R extends Ref<infer T> ? T : R extends Observable ? Export<R> : R;
|
|
120
118
|
}
|
|
121
119
|
interface Model extends Observable {
|
|
122
120
|
}
|
|
@@ -344,9 +342,9 @@ declare class Control<T extends {} = any> {
|
|
|
344
342
|
static use: Control.UseHook;
|
|
345
343
|
static has: Control.GetContext;
|
|
346
344
|
static for: typeof control;
|
|
347
|
-
static
|
|
345
|
+
static does: typeof does;
|
|
348
346
|
static watch: typeof watch;
|
|
349
|
-
id
|
|
347
|
+
id: string | number | false;
|
|
350
348
|
subject: T;
|
|
351
349
|
state: {
|
|
352
350
|
[key: string]: any;
|
|
@@ -355,8 +353,8 @@ declare class Control<T extends {} = any> {
|
|
|
355
353
|
frame: Set<string>;
|
|
356
354
|
followers: Set<Observer<any>>;
|
|
357
355
|
observers: Map<string, Set<Observer<any>>>;
|
|
358
|
-
constructor(subject: T, id
|
|
359
|
-
add(key:
|
|
356
|
+
constructor(subject: T, id: string | number | false);
|
|
357
|
+
add(key: string): void;
|
|
360
358
|
watch(key: string, output: Control.PropertyDescriptor<any>): void;
|
|
361
359
|
ref<K extends Model.Key<T>>(key: K, callback?: (this: T, value: T[K]) => boolean | void): (value: any) => void;
|
|
362
360
|
update(key: string): void;
|
|
@@ -369,7 +367,7 @@ type Focus<T extends {}> = T & {
|
|
|
369
367
|
is: T;
|
|
370
368
|
};
|
|
371
369
|
declare function watch<T extends {}>(value: T, cb: Observer): Focus<T>;
|
|
372
|
-
declare function
|
|
370
|
+
declare function does<T = any>(instruction: Control.Instruction<T>): T;
|
|
373
371
|
|
|
374
372
|
declare const STATE = "__state";
|
|
375
373
|
declare const UPDATE = "__update";
|
package/lib/index.esm.js
CHANGED
|
@@ -292,6 +292,7 @@ function getMethod(argument, callback, once) {
|
|
|
292
292
|
if (typeof callback != "function")
|
|
293
293
|
return extract();
|
|
294
294
|
const select = typeof argument == "string" ? [argument] : argument;
|
|
295
|
+
const invoke = () => callback(extract(), self.latest || []);
|
|
295
296
|
if (select)
|
|
296
297
|
for (const key of select)
|
|
297
298
|
try {
|
|
@@ -302,11 +303,13 @@ function getMethod(argument, callback, once) {
|
|
|
302
303
|
catch (e) {
|
|
303
304
|
// TODO: should this be caught?
|
|
304
305
|
}
|
|
306
|
+
if (once === undefined)
|
|
307
|
+
invoke();
|
|
305
308
|
const remove = self.addListener(key => {
|
|
306
309
|
if (!select || select.includes(key)) {
|
|
307
310
|
if (once)
|
|
308
311
|
remove();
|
|
309
|
-
return
|
|
312
|
+
return invoke;
|
|
310
313
|
}
|
|
311
314
|
});
|
|
312
315
|
return remove;
|
|
@@ -351,7 +354,7 @@ function setMethod(arg1, arg2) {
|
|
|
351
354
|
|
|
352
355
|
class Model {
|
|
353
356
|
constructor(id) {
|
|
354
|
-
new Control(this, id);
|
|
357
|
+
new Control(this, id === undefined ? uid() : id);
|
|
355
358
|
}
|
|
356
359
|
/** Mark this instance for garbage collection. */
|
|
357
360
|
null() {
|
|
@@ -416,7 +419,11 @@ defineProperties(Model.prototype, {
|
|
|
416
419
|
throw new Error("Model.on() is deprecated. Use Model.get or Model.set instead.");
|
|
417
420
|
}
|
|
418
421
|
}
|
|
419
|
-
});
|
|
422
|
+
});
|
|
423
|
+
/** Random alphanumberic of length 6. Will always start with a letter. */
|
|
424
|
+
function uid() {
|
|
425
|
+
return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
|
|
426
|
+
}
|
|
420
427
|
|
|
421
428
|
const Oops$4 = issues({
|
|
422
429
|
BadAssignment: (parent, expected, got) => `${parent} expected Model of type ${expected} but got ${got}.`,
|
|
@@ -432,12 +439,11 @@ class Control {
|
|
|
432
439
|
this.frame = new Set();
|
|
433
440
|
this.followers = new Set();
|
|
434
441
|
this.observers = new Map([["", this.followers]]);
|
|
442
|
+
this.id = id;
|
|
435
443
|
this.subject = subject;
|
|
436
444
|
REGISTER.set(subject, this);
|
|
437
|
-
if (id !== false)
|
|
438
|
-
this.id = id === undefined ? uid() : id;
|
|
445
|
+
if (id !== false)
|
|
439
446
|
PENDING$1.set(this, new Set(Control.ready));
|
|
440
|
-
}
|
|
441
447
|
}
|
|
442
448
|
add(key) {
|
|
443
449
|
const { value } = getOwnPropertyDescriptor(this.subject, key);
|
|
@@ -534,7 +540,7 @@ Control.after = new Set();
|
|
|
534
540
|
Control.before = new Set();
|
|
535
541
|
Control.pending = new Set();
|
|
536
542
|
Control.for = control;
|
|
537
|
-
Control.
|
|
543
|
+
Control.does = does;
|
|
538
544
|
Control.watch = watch;
|
|
539
545
|
function control(subject, ready) {
|
|
540
546
|
const control = REGISTER.get(subject);
|
|
@@ -599,7 +605,7 @@ function watch(value, cb) {
|
|
|
599
605
|
OBSERVER.set(value, cb);
|
|
600
606
|
return value;
|
|
601
607
|
}
|
|
602
|
-
function
|
|
608
|
+
function does(instruction) {
|
|
603
609
|
const placeholder = Symbol("instruction");
|
|
604
610
|
INSTRUCT.set(placeholder, (onto, key) => {
|
|
605
611
|
delete onto.subject[key];
|
|
@@ -608,17 +614,13 @@ function apply(instruction) {
|
|
|
608
614
|
onto.watch(key, typeof output == "function" ? { get: output } : output);
|
|
609
615
|
});
|
|
610
616
|
return placeholder;
|
|
611
|
-
}
|
|
612
|
-
/** Random alphanumberic of length 6; will always start with a letter. */
|
|
613
|
-
function uid() {
|
|
614
|
-
return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
|
|
615
617
|
}
|
|
616
618
|
|
|
617
619
|
const Oops$3 = issues({
|
|
618
620
|
DuplicatePending: (key) => `Invoked action ${key} but one is already active.`
|
|
619
621
|
});
|
|
620
622
|
function run(task) {
|
|
621
|
-
return
|
|
623
|
+
return does((key, control) => {
|
|
622
624
|
let pending = false;
|
|
623
625
|
const invoke = (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
624
626
|
if (pending)
|
|
@@ -652,7 +654,7 @@ const Oops$2 = issues({
|
|
|
652
654
|
AmbientRequired: (requested, requester) => `Attempted to find an instance of ${requested} in context. It is required by ${requester}, but one could not be found.`
|
|
653
655
|
});
|
|
654
656
|
function get(arg0, arg1) {
|
|
655
|
-
return
|
|
657
|
+
return does((key, control) => {
|
|
656
658
|
let { subject } = control;
|
|
657
659
|
if (typeof arg0 == "symbol")
|
|
658
660
|
throw Oops$2.PeerNotAllowed(subject, key);
|
|
@@ -778,7 +780,7 @@ function flushComputed() {
|
|
|
778
780
|
Control.before.add(flushComputed);
|
|
779
781
|
|
|
780
782
|
function ref(arg, arg2) {
|
|
781
|
-
return
|
|
783
|
+
return does((key, source) => {
|
|
782
784
|
let value = {};
|
|
783
785
|
if (typeof arg == "object")
|
|
784
786
|
for (const key in control(arg, true).state)
|
|
@@ -813,7 +815,7 @@ const Oops$1 = issues({
|
|
|
813
815
|
NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
|
|
814
816
|
});
|
|
815
817
|
function set(value, argument) {
|
|
816
|
-
return
|
|
818
|
+
return does((key, control) => {
|
|
817
819
|
const { state, subject } = control;
|
|
818
820
|
if (typeof value == "function" || value instanceof Promise) {
|
|
819
821
|
const output = {};
|
|
@@ -883,7 +885,7 @@ function set(value, argument) {
|
|
|
883
885
|
}
|
|
884
886
|
|
|
885
887
|
function use(input, argument) {
|
|
886
|
-
return
|
|
888
|
+
return does((key, source) => {
|
|
887
889
|
const { state, subject } = source;
|
|
888
890
|
if (typeof input === "function")
|
|
889
891
|
input = new input();
|
package/lib/index.js
CHANGED
|
@@ -296,6 +296,7 @@ function getMethod(argument, callback, once) {
|
|
|
296
296
|
if (typeof callback != "function")
|
|
297
297
|
return extract();
|
|
298
298
|
const select = typeof argument == "string" ? [argument] : argument;
|
|
299
|
+
const invoke = () => callback(extract(), self.latest || []);
|
|
299
300
|
if (select)
|
|
300
301
|
for (const key of select)
|
|
301
302
|
try {
|
|
@@ -306,11 +307,13 @@ function getMethod(argument, callback, once) {
|
|
|
306
307
|
catch (e) {
|
|
307
308
|
// TODO: should this be caught?
|
|
308
309
|
}
|
|
310
|
+
if (once === undefined)
|
|
311
|
+
invoke();
|
|
309
312
|
const remove = self.addListener(key => {
|
|
310
313
|
if (!select || select.includes(key)) {
|
|
311
314
|
if (once)
|
|
312
315
|
remove();
|
|
313
|
-
return
|
|
316
|
+
return invoke;
|
|
314
317
|
}
|
|
315
318
|
});
|
|
316
319
|
return remove;
|
|
@@ -355,7 +358,7 @@ function setMethod(arg1, arg2) {
|
|
|
355
358
|
|
|
356
359
|
class Model {
|
|
357
360
|
constructor(id) {
|
|
358
|
-
new Control(this, id);
|
|
361
|
+
new Control(this, id === undefined ? uid() : id);
|
|
359
362
|
}
|
|
360
363
|
/** Mark this instance for garbage collection. */
|
|
361
364
|
null() {
|
|
@@ -420,7 +423,11 @@ defineProperties(Model.prototype, {
|
|
|
420
423
|
throw new Error("Model.on() is deprecated. Use Model.get or Model.set instead.");
|
|
421
424
|
}
|
|
422
425
|
}
|
|
423
|
-
});
|
|
426
|
+
});
|
|
427
|
+
/** Random alphanumberic of length 6. Will always start with a letter. */
|
|
428
|
+
function uid() {
|
|
429
|
+
return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
|
|
430
|
+
}
|
|
424
431
|
|
|
425
432
|
const Oops$4 = issues({
|
|
426
433
|
BadAssignment: (parent, expected, got) => `${parent} expected Model of type ${expected} but got ${got}.`,
|
|
@@ -436,12 +443,11 @@ class Control {
|
|
|
436
443
|
this.frame = new Set();
|
|
437
444
|
this.followers = new Set();
|
|
438
445
|
this.observers = new Map([["", this.followers]]);
|
|
446
|
+
this.id = id;
|
|
439
447
|
this.subject = subject;
|
|
440
448
|
REGISTER.set(subject, this);
|
|
441
|
-
if (id !== false)
|
|
442
|
-
this.id = id === undefined ? uid() : id;
|
|
449
|
+
if (id !== false)
|
|
443
450
|
PENDING$1.set(this, new Set(Control.ready));
|
|
444
|
-
}
|
|
445
451
|
}
|
|
446
452
|
add(key) {
|
|
447
453
|
const { value } = getOwnPropertyDescriptor(this.subject, key);
|
|
@@ -538,7 +544,7 @@ Control.after = new Set();
|
|
|
538
544
|
Control.before = new Set();
|
|
539
545
|
Control.pending = new Set();
|
|
540
546
|
Control.for = control;
|
|
541
|
-
Control.
|
|
547
|
+
Control.does = does;
|
|
542
548
|
Control.watch = watch;
|
|
543
549
|
function control(subject, ready) {
|
|
544
550
|
const control = REGISTER.get(subject);
|
|
@@ -603,7 +609,7 @@ function watch(value, cb) {
|
|
|
603
609
|
OBSERVER.set(value, cb);
|
|
604
610
|
return value;
|
|
605
611
|
}
|
|
606
|
-
function
|
|
612
|
+
function does(instruction) {
|
|
607
613
|
const placeholder = Symbol("instruction");
|
|
608
614
|
INSTRUCT.set(placeholder, (onto, key) => {
|
|
609
615
|
delete onto.subject[key];
|
|
@@ -612,17 +618,13 @@ function apply(instruction) {
|
|
|
612
618
|
onto.watch(key, typeof output == "function" ? { get: output } : output);
|
|
613
619
|
});
|
|
614
620
|
return placeholder;
|
|
615
|
-
}
|
|
616
|
-
/** Random alphanumberic of length 6; will always start with a letter. */
|
|
617
|
-
function uid() {
|
|
618
|
-
return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
|
|
619
621
|
}
|
|
620
622
|
|
|
621
623
|
const Oops$3 = issues({
|
|
622
624
|
DuplicatePending: (key) => `Invoked action ${key} but one is already active.`
|
|
623
625
|
});
|
|
624
626
|
function run(task) {
|
|
625
|
-
return
|
|
627
|
+
return does((key, control) => {
|
|
626
628
|
let pending = false;
|
|
627
629
|
const invoke = (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
628
630
|
if (pending)
|
|
@@ -656,7 +658,7 @@ const Oops$2 = issues({
|
|
|
656
658
|
AmbientRequired: (requested, requester) => `Attempted to find an instance of ${requested} in context. It is required by ${requester}, but one could not be found.`
|
|
657
659
|
});
|
|
658
660
|
function get(arg0, arg1) {
|
|
659
|
-
return
|
|
661
|
+
return does((key, control) => {
|
|
660
662
|
let { subject } = control;
|
|
661
663
|
if (typeof arg0 == "symbol")
|
|
662
664
|
throw Oops$2.PeerNotAllowed(subject, key);
|
|
@@ -782,7 +784,7 @@ function flushComputed() {
|
|
|
782
784
|
Control.before.add(flushComputed);
|
|
783
785
|
|
|
784
786
|
function ref(arg, arg2) {
|
|
785
|
-
return
|
|
787
|
+
return does((key, source) => {
|
|
786
788
|
let value = {};
|
|
787
789
|
if (typeof arg == "object")
|
|
788
790
|
for (const key in control(arg, true).state)
|
|
@@ -817,7 +819,7 @@ const Oops$1 = issues({
|
|
|
817
819
|
NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
|
|
818
820
|
});
|
|
819
821
|
function set(value, argument) {
|
|
820
|
-
return
|
|
822
|
+
return does((key, control) => {
|
|
821
823
|
const { state, subject } = control;
|
|
822
824
|
if (typeof value == "function" || value instanceof Promise) {
|
|
823
825
|
const output = {};
|
|
@@ -887,7 +889,7 @@ function set(value, argument) {
|
|
|
887
889
|
}
|
|
888
890
|
|
|
889
891
|
function use(input, argument) {
|
|
890
|
-
return
|
|
892
|
+
return does((key, source) => {
|
|
891
893
|
const { state, subject } = source;
|
|
892
894
|
if (typeof input === "function")
|
|
893
895
|
input = new input();
|
package/package.json
CHANGED