@expressive/mvc 1.0.0-beta.10 → 1.0.0-beta.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expressive/mvc",
3
- "version": "1.0.0-beta.10",
3
+ "version": "1.0.0-beta.14",
4
4
  "description": "Use classes to define state in React!",
5
5
  "author": {
6
6
  "name": "Gabe Klein",
@@ -27,7 +27,7 @@
27
27
  "watch": "rollup --config --sourcemap --watch",
28
28
  "prebuild": "rm -rf lib/",
29
29
  "publish:pre": "npm version prerelease --preid=beta",
30
- "preversion": "jest",
30
+ "preversion": "jest --coverage",
31
31
  "postversion": "npm run build; npm publish",
32
32
  "postpublish": "git push; git push --tags; rm -rf lib",
33
33
  "coverage": "jest --collectCoverage",
package/types/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export {
2
+ State,
2
3
  Model,
3
4
  Model as default,
4
5
  Singleton
@@ -5,104 +5,153 @@ import { Async, Class, EffectCallback, InstanceOf } from './types';
5
5
  * Run instruction as controller sets itself up.
6
6
  * This will specialize the behavior of a given property.
7
7
  **/
8
- export function set <T = any> (
9
- instruction: Model.Instruction<T>,
10
- name?: string
11
- ): T;
8
+ export function set <T = any> (instruction: Model.Instruction<T>, name?: string): T;
12
9
 
13
10
  /**
14
11
  * Creates a new child-instance of specified controller.
15
- *
12
+ *
16
13
  * @param Peer - Type of Model to create and apply to host property.
17
14
  * @param callback - Fired after controller is created and ready for use.
18
15
  */
19
- export function use <T extends typeof Model> (Peer: T, callback: (i?: InstanceOf<T>) => void): InstanceOf<T> | undefined;
20
- export function use <T extends typeof Model> (Peer: T, required: false): InstanceOf<T> | undefined;
21
- export function use <T extends typeof Model> (Peer: T, required?: true): InstanceOf<T>;
22
- export function use <T extends Model | undefined> (Peer: () => T, callback?: (i: T) => void): T;
23
- export function use <T extends Model> (): T | undefined;
16
+ export function use <T extends Class> (Peer: T, callback: (i?: InstanceOf<T>) => void): InstanceOf<T> | undefined;
17
+
18
+ /**
19
+ * Creates a new child-instance of specified controller.
20
+ *
21
+ * @param Peer - Type of Model to create and apply to host property.
22
+ * @param required - Property should be non-nullable, will throw if value is set to undefined.
23
+ */
24
+ export function use <T extends Class> (Peer: T, required: false): InstanceOf<T> | undefined;
25
+ export function use <T extends Class> (Peer: T, required?: true): InstanceOf<T>;
26
+
27
+ /**
28
+ * Creates a new child-instance of specified controller.
29
+ *
30
+ * @param factory - Generate model for use.
31
+ * @param callback - Fired after controller is created and ready for use.
32
+ */
33
+ export function use <T extends Model | undefined> (factory: () => T, callback?: (i: T) => void): T;
34
+
35
+ /**
36
+ * Create a child-instance relationship with provided model.
37
+ *
38
+ * Note: If `peer` is not already initialized before parent is
39
+ * (created with `new` as opposed to create method), that model will
40
+ * attach this via `parent()` instruction. It will not howerver, if
41
+ * already active.
42
+ *
43
+ * @param factory - Instance of model to attach.
44
+ * @param callback - Fired after controller is attached.
45
+ */
46
+ export function use <T extends Model> (peer: T, callback?: (i: T) => void): T;
47
+
48
+ /**
49
+ * Create a placeholder for specified Model type.
50
+ */
51
+ export function use <T extends Model> (): T | undefined;
52
+
53
+ /**
54
+ * Generate a child controller from specified object. Object's values are be trackable, as would be for a full-model.
55
+ *
56
+ * Note: Child will *not* be same object as one provided.
57
+ */
58
+ export function use <T extends BunchOf<any>> (object: T): T;
24
59
 
25
60
  /**
26
61
  * Generic instruction used by `use()` and `tap()` for recursive subscription.
27
- *
62
+ *
28
63
  * @param from - Instruction body is run upon parent create. Return function to fetch current value of field.
29
64
  */
30
65
  export function child <T extends Model> (from: (this: Model.Controller, key: string) => () => T | undefined): T;
31
66
 
32
- /**
33
- * Fetches and assigns the controller which spawned this host.
34
- * When parent assigns an instance via `use()` directive, it
35
- * will be made available to child upon this request.
36
- *
37
- * @param Expects - Type of controller compatible with this class.
38
- * @param required - Throw if controller is created independantly.
39
- */
40
- export function parent <T extends typeof Model> (Expects: T, required: true): InstanceOf<T>;
41
- export function parent <T extends typeof Model> (Expects: T, required?: false): InstanceOf<T> | undefined;
67
+ /**
68
+ * Fetches and assigns the controller which spawned this host.
69
+ * When parent assigns an instance via `use()` directive, it
70
+ * will be made available to child upon this request.
71
+ *
72
+ * @param Expects - Type of controller compatible with this class.
73
+ * @param required - Throw if controller is created independantly.
74
+ */
75
+ export function parent <T extends typeof Model> (Expects: T, required: true): InstanceOf<T>;
76
+ export function parent <T extends typeof Model> (Expects: T, required?: false): InstanceOf<T> | undefined;
77
+
78
+ /**
79
+ * Find and attach most applicable instance of Model via context.
80
+ *
81
+ * Host controller will search element-hierarchy relative to where it spawned.
82
+ *
83
+ * @param Type - Type of model to find from context
84
+ * @param callback -
85
+ * - Invoked after context is scanned, is passed result - either found or undefined.
86
+ * - If argument is inadequate, but required, your implemention should simply throw.
87
+ * - If inadequate and not required, conditionally return false.
88
+ */
89
+ export function tap <T extends typeof Model> (Type: T, callback?: (instance?: InstanceOf<T>) => void | true): InstanceOf<T>;
90
+ export function tap <T extends typeof Model> (Type: T, callback?: (instance?: InstanceOf<T>) => void | false): InstanceOf<T> | undefined;
42
91
 
43
- /**
44
- * Find and attach most applicable instance of Model via context.
45
- *
46
- * Expects a `<Provider>` of target controller to exist.
47
- * Host controller will search element-hierarchy relative to where it spawned.
48
- *
49
- * @param Type - Type of controller to attach to property.
50
- * @param required - Throw if instance of Type cannot be found.
51
- */
52
- export function tap <T extends Class> (Type: T, required?: boolean): InstanceOf<T>;
92
+ /**
93
+ * Find and attach most applicable instance of Model via context.
94
+ *
95
+ * Expects a `<Provider>` of target controller to exist.
96
+ * Host controller will search element-hierarchy relative to where it spawned.
97
+ *
98
+ * @param Type - Type of controller to attach to property.
99
+ * @param required - Throw if instance of Type cannot be found.
100
+ */
101
+ export function tap <T extends Class> (Type: T, required?: boolean): InstanceOf<T>;
53
102
 
54
- /**
55
- * Sets property to synchronously call effect upon update.
56
- *
57
- * @param starting - Beginning value of host property.
58
- * @param callback - Effect-callback fired upon set of host property.
59
- */
60
- export function on <T = any> (initial: undefined, callback: EffectCallback<T>): T | undefined;
61
- export function on <T = any> (initial: T, callback: EffectCallback<T>): T;
103
+ /**
104
+ * Sets property to synchronously call effect upon update.
105
+ *
106
+ * @param starting - Beginning value of host property.
107
+ * @param callback - Effect-callback fired upon set of host property.
108
+ */
109
+ export function on <T = any> (initial: undefined, callback: EffectCallback<T>): T | undefined;
110
+ export function on <T = any> (initial: T, callback: EffectCallback<T>): T;
62
111
 
63
- /**
64
- * Creates a ref-compatible property for use with components.
65
- * Will persist value, and updates to this are made part of controller event-stream.
66
- *
67
- * *Output is simultaneously a ref-function and ref-object, use as needed.*
68
- *
69
- * @param callback - Optional callback to synchronously fire when reference is first set or does update.
70
- */
71
- export function ref <T = HTMLElement> (callback?: EffectCallback<T>): Model.Ref<T>;
112
+ /**
113
+ * Creates a ref-compatible property for use with components.
114
+ * Will persist value, and updates to this are made part of controller event-stream.
115
+ *
116
+ * *Output is simultaneously a ref-function and ref-object, use as needed.*
117
+ *
118
+ * @param callback - Optional callback to synchronously fire when reference is first set or does update.
119
+ */
120
+ export function ref <T = HTMLElement> (callback?: EffectCallback<T>): Model.Ref<T>;
72
121
 
73
- /**
74
- * Sets an exotic method with managed ready-state. Property accepts an async function.
75
- *
76
- * When an act-method is invoked, its `active` property to true for duration of call.
77
- * This is emitted as an update to property, both when called and after returns (or throws).
78
- *
79
- * **Note:** Subsequent calls will immediately throw if one is still pending.
80
- *
81
- * @param action - Action to fire when resulting property is invoked.
82
- */
83
- export function act <T extends Async>(action: T): T & { active: boolean };
122
+ /**
123
+ * Sets an exotic method with managed ready-state. Property accepts an async function.
124
+ *
125
+ * When an act-method is invoked, its `active` property to true for duration of call.
126
+ * This is emitted as an update to property, both when called and after returns (or throws).
127
+ *
128
+ * **Note:** Subsequent calls will immediately throw if one is still pending.
129
+ *
130
+ * @param action - Action to fire when resulting property is invoked.
131
+ */
132
+ export function act <T extends Async> (action: T): T & { active: boolean };
84
133
 
85
- /**
86
- * Implement a computed value; output will be generated by provided function.
87
- *
88
- * *Alternative to using native getters; works exactly the same!*
89
- *
90
- * @param fn - Factory function to generate value and subscribe dependancies.
91
- */
92
- export function from <T, R>(fn: (this: T, on: T) => R): R;
134
+ /**
135
+ * Implement a computed value; output will be generated by provided function.
136
+ *
137
+ * *Alternative to using native getters; works exactly the same!*
138
+ *
139
+ * @param fn - Factory function to generate value and subscribe dependancies.
140
+ */
141
+ export function from <T, R> (fn: (this: T, on: T) => R): R;
93
142
 
94
- /**
95
- * Memoized value. Computes and stores a value returned by provided factory.
96
- * Equivalent to a getter, sans the automatic updates.
97
- *
98
- * @param compute - Factory for memoized value.
99
- * @param lazy - Wait until accessed to introduce value.
100
- */
101
- export function memo <T> (compute: () => T, lazy?: boolean): T;
143
+ /**
144
+ * Memoized value. Computes and stores a value returned by provided factory.
145
+ * Equivalent to a getter, sans the automatic updates.
146
+ *
147
+ * @param compute - Factory for memoized value.
148
+ * @param lazy - Wait until accessed to introduce value.
149
+ */
150
+ export function memo <T> (compute: () => T, lazy?: boolean): T;
102
151
 
103
- /**
104
- * Flag property as not to be tracked. Useful if changes often with no real-time impact.
105
- *
106
- * @param value - starting value of property.
107
- */
108
- export function lazy <T> (value?: T): T;
152
+ /**
153
+ * Flag property as not to be tracked. Useful if changes often with no real-time impact.
154
+ *
155
+ * @param value - starting value of property.
156
+ */
157
+ export function lazy <T> (value?: T): T;
package/types/model.d.ts CHANGED
@@ -1,6 +1,5 @@
1
- import Dispatch from './dispatch';
2
1
  import Lifecycle from './lifecycle';
3
- import { Selector } from './selector';
2
+ import { Select, Selector } from './selector';
4
3
  import { Class, InstanceOf, Key } from './types';
5
4
 
6
5
  interface PropertyDescriptor<T> {
@@ -12,6 +11,12 @@ interface PropertyDescriptor<T> {
12
11
  set?(v: T): void;
13
12
  }
14
13
 
14
+ type Argument<T> = T extends (arg: infer U) => any ? U : never;
15
+
16
+ type Thenable<T> = {
17
+ then(onFulfilled: (arg: T) => void): void;
18
+ }
19
+
15
20
  export namespace Model {
16
21
  /** Exotic value, actual value is contained. */
17
22
  interface Ref<T = any> {
@@ -25,7 +30,7 @@ export namespace Model {
25
30
  * Property initializer, will run upon instance creation.
26
31
  * Optional returned callback will run when once upon first access.
27
32
  */
28
- type Instruction<T> = (this: Controller, key: string) =>
33
+ type Instruction<T> = (this: Controller, key: string, thisArg: Controller) =>
29
34
  void | GetFunction<T> | PropertyDescriptor<T>;
30
35
 
31
36
  /** Shallow replacement given all entries of Model */
@@ -96,27 +101,26 @@ export namespace Model {
96
101
 
97
102
  type SelectField<T> = (arg: Omit<T, keyof Model>) => any;
98
103
 
104
+ type SelectFieldKey<T> = Select.Key<Fields<T>>;
105
+
106
+ type Typeof<T, ST, X extends keyof T = Exclude<keyof T, keyof Model>> = {
107
+ [Key in X]: T[Key] extends ST ? Key : never;
108
+ }[X];
109
+
110
+ type SelectTypeof<T, ST> = Select.Key<Typeof<T, ST>>;
111
+
99
112
  type HandleValue = (value: any) => boolean | void;
100
113
 
101
114
  export class Controller {
102
115
  state: BunchOf<any>;
103
116
  subject: {};
104
117
 
105
- /**
106
- * Place a given key's property under management.
107
- *
108
- * Note: If property is occupied by an instruction, it will be run.
109
- */
110
- add(key: string): void;
111
-
112
118
  /** Add property to managed state. */
113
119
  manage(key: string, initial: any, effect?: HandleValue): void;
114
120
 
115
121
  select(using?: Query): string[];
116
122
 
117
- sets(key: string, effect?: HandleValue): (value: any) => boolean | void;
118
-
119
- watch(target: string | Iterable<string> | Query, handler: Function, squash?: boolean, once?: boolean): Callback;
123
+ setter(key: string, effect?: HandleValue): (value: any) => boolean | void;
120
124
 
121
125
  addListener(batch: BunchOf<RequestCallback>): Callback;
122
126
 
@@ -125,17 +129,16 @@ export namespace Model {
125
129
  include(cb: RequestCallback): void;
126
130
  }
127
131
 
128
- type Listener = {
129
- commit(): void;
130
- release(): void;
131
- }
132
-
133
132
  export class Subscriber {
134
133
  proxy: any;
134
+ source: any;
135
+ parent: Controller;
135
136
  active: boolean;
136
137
  follows: BunchOf<Callback>;
137
- dependant: Set<Listener>;
138
- parent: Controller;
138
+ dependant: Set<{
139
+ commit(): void;
140
+ release(): void;
141
+ }>;
139
142
 
140
143
  follow(key: string, cb?: Callback | undefined): void;
141
144
  commit(): Callback;
@@ -148,9 +151,16 @@ declare const CONTROL: unique symbol;
148
151
  declare const LOCAL: unique symbol;
149
152
  declare const STATE: unique symbol;
150
153
 
151
- export interface Model extends Dispatch, Lifecycle {}
154
+ export abstract class State {
155
+ /** Controller for this instance. */
156
+ [CONTROL]: Model.Controller;
157
+
158
+ /** Current state of this instance. */
159
+ [STATE]?: Model.State<this>;
160
+
161
+ /** Current subscriber (if present) while used in a live context (e.g. hook or effect). */
162
+ [LOCAL]?: Model.Subscriber;
152
163
 
153
- export abstract class Model {
154
164
  /**
155
165
  * Circular reference to `this` controller.
156
166
  *
@@ -210,6 +220,36 @@ export abstract class Model {
210
220
  */
211
221
  set: this;
212
222
 
223
+ import <O extends Model.Data<this>> (via: O, select?: string[] | Model.SelectFields<this>): void;
224
+
225
+ export(): Model.State<this>;
226
+ export <P extends Model.Fields<this>> (select: P[]): Model.State<this, P>;
227
+ export <S extends Model.SelectFields<this>> (select: S): Model.State<this, Selector.From<S>>;
228
+
229
+ update(): PromiseLike<string[] | false>;
230
+ update(strict: true): Promise<string[]>;
231
+ update(strict: false): Promise<false>;
232
+ update(strict: boolean): Promise<string[] | false>;
233
+
234
+ update(keys: Model.Fields<this>): Thenable<string[]>;
235
+ update(keys: Model.SelectFieldKey<this>): Thenable<string[]>;
236
+
237
+ update(keys: Model.Fields<this>, callMethod: boolean): PromiseLike<string[]>;
238
+ update(keys: Model.SelectFields<this>, callMethod: boolean): PromiseLike<string[]>;
239
+
240
+ update<T>(keys: Model.Fields<this>, argument: T): PromiseLike<string[]>;
241
+ update<T>(keys: Model.SelectFields<this>, argument: T): PromiseLike<string[]>;
242
+
243
+ /*
244
+ Issue with self-reference, using fallback.
245
+
246
+ update(keys: Model.Typeof<this, () => void>, callMethod: boolean): Thenable<string[]>;
247
+ update(keys: Model.SelectTypeof<this, () => void>, callMethod: boolean): Thenable<string[]>;
248
+
249
+ update<T>(keys: Model.Typeof<this, (arg: T) => void>, argument: T): Thenable<string[]>;
250
+ update<T>(keys: Model.SelectTypeof<this, (arg: T) => void>, argument: T): Thenable<string[]>;
251
+ */
252
+
213
253
  /**
214
254
  * Mark this instance for garbage-collection and send `willDestroy` event to all listeners.
215
255
  *
@@ -239,33 +279,6 @@ export abstract class Model {
239
279
  tap <K extends Model.SelectField<this>> (key: K, expect?: boolean): ReturnType<K>;
240
280
  tap <K extends Model.SelectField<this>> (key: K, expect: true): Exclude<ReturnType<K>, undefined>;
241
281
 
242
- /**
243
- * **React Hook** - Attach to instance of this controller within ambient component.
244
- *
245
- * This method will fire lifecycle events on given controller.
246
- *
247
- * @param id - Argument passed to controller-lifecycle methods. Use to identify the consumer.
248
- */
249
- tag(id?: Key): this;
250
-
251
- /**
252
- * **React Hook** - Attach to instance of this controller within ambient component.
253
- *
254
- * This method will fire lifecycle events on given controller.
255
- *
256
- * @param idFactory - Will be invoked with fetched instance. Use this to register a tag as-needed.
257
- */
258
- tag(idFactory: (idFactory: this) => Key | void): this;
259
-
260
- /** Controller for this instance. */
261
- [CONTROL]: Model.Controller;
262
-
263
- /** Current state of this instance. */
264
- [STATE]?: Model.State<this>;
265
-
266
- /** Current subscriber (if present) while used in a live context (e.g. hook or effect). */
267
- [LOCAL]?: Model.Subscriber;
268
-
269
282
  /** Use symbol to access controller of a model. */
270
283
  static CONTROL: typeof CONTROL;
271
284
 
@@ -289,8 +302,11 @@ export abstract class Model {
289
302
  *
290
303
  * Differs from `use()` in lacking subscription and lifecycle events.
291
304
  * Much more efficient if you don't need hook-based features.
305
+ *
306
+ * @param args - Arguments passed to constructor of `this`
307
+ * @param callback - Run after creation of instance.
292
308
  */
293
- static new <T extends Class> (this: T, ...args: ConstructorParameters<T>): InstanceOf<T>;
309
+ static new <T extends Class> (this: T, args?: ConstructorParameters<T>, callback?: (instance: InstanceOf<T>) => void): InstanceOf<T>;
294
310
 
295
311
  /**
296
312
  * **React Hook** - Create and attach an instance of this controller a react component.
@@ -298,8 +314,9 @@ export abstract class Model {
298
314
  * Note: Model will be destroyed when ambient component unmounts!
299
315
  *
300
316
  * @param args - Arguments passed to constructor of `this`
317
+ * @param callback - Run after creation of instance.
301
318
  */
302
- static use <T extends Class> (this: T, ...args: ConstructorParameters<T>): InstanceOf<T>;
319
+ static use <T extends Class> (this: T, args?: ConstructorParameters<T>, callback?: (instance: InstanceOf<T>) => void): InstanceOf<T>;
303
320
 
304
321
  /**
305
322
  * **React Hook** - Similar to `use`, will instanciate a controller bound to ambient component.
@@ -307,7 +324,7 @@ export abstract class Model {
307
324
  *
308
325
  * @param data - Data to be applied to controller upon creation.
309
326
  */
310
- static uses <T extends Class, I extends InstanceOf<T>, D extends Partial<I>> (this: T, data: D): I;
327
+ static uses <T extends Class, I extends InstanceOf<T>, D extends Partial<I>> (this: T, data: D, only?: (keyof D)[]): I;
311
328
 
312
329
  /**
313
330
  * **React Hook** - Similar to `uses`, will instanciate a controller includive of given data.
@@ -316,7 +333,13 @@ export abstract class Model {
316
333
  *
317
334
  * @param data - Data to be observed by controller.
318
335
  */
319
- static using <T extends Class, I extends InstanceOf<T>, D extends Partial<I>> (this: T, data: D): I;
336
+ static using <T extends Class, I extends InstanceOf<T>, D extends Partial<I>> (this: T, data: D, only?: (keyof D)[]): I;
337
+
338
+ /**
339
+ * **React Hook** - Locate most relevant instance of this type in context.
340
+ */
341
+ static find <T extends Class>(this: T, strict: true): InstanceOf<T>;
342
+ static find <T extends Class>(this: T, strict?: boolean): InstanceOf<T> | undefined;
320
343
 
321
344
  /**
322
345
  * **React Hook** - Fetch most instance of this controller from context, if it exists.
@@ -362,6 +385,61 @@ export abstract class Model {
362
385
  static tap <T extends Class, I extends InstanceOf<T>, K extends Model.Fields<I>> (this: T, key: K, expect: true): Exclude<I[K], undefined>;
363
386
  static tap <T extends Class, I extends InstanceOf<T>, K extends Model.SelectField<I>> (this: T, key: K, expect: true): Exclude<ReturnType<K>, undefined>;
364
387
 
388
+ /**
389
+ * Static equivalent of `x instanceof this`.
390
+ *
391
+ * Will determine if provided class is a subtype of this one.
392
+ */
393
+ static isTypeof <T extends Class>(this: T, subject: any): subject is T;
394
+ }
395
+
396
+ export interface Model extends Lifecycle {}
397
+ export abstract class Model extends State {
398
+ // Keyed
399
+ on <S extends Model.SelectEvents<this>> (via: S, cb: Selector.Callback<S, this>, squash?: false, once?: boolean): Callback;
400
+ on <P extends Model.EventsCompat<this>> (key: P | P[], listener: UpdateCallback<this, P>, squash?: false, once?: boolean): Callback;
401
+ // Squash
402
+ on <S extends Model.SelectEvents<this>> (via: S, cb: (keys: Selector.From<S>[]) => void, squash: true, once?: boolean): Callback;
403
+ on <P extends Model.EventsCompat<this>> (key: P | P[], listener: (keys: P[]) => void, squash: true, once?: boolean): Callback;
404
+ // Unknown
405
+ on <S extends Model.SelectEvents<this>> (via: S, cb: unknown, squash: boolean, once?: boolean): Callback;
406
+ on <P extends Model.EventsCompat<this>> (key: P | P[], listener: unknown, squash: boolean, once?: boolean): Callback;
407
+
408
+ // Keyed
409
+ once <S extends Model.SelectEvents<this>> (via: S, cb: Selector.Callback<S, this>, squash?: false): Callback;
410
+ once <P extends Model.EventsCompat<this>> (key: P | P[], listener: UpdateCallback<this, P>, squash?: false): Callback;
411
+ // Squash
412
+ once <S extends Model.SelectEvents<this>> (via: S, cb: (keys: Selector.From<S>[]) => void, squash: true): Callback;
413
+ once <P extends Model.EventsCompat<this>> (key: P | P[], listener: (keys: P[]) => void, squash: true): Callback;
414
+ // Promise
415
+ once <S extends Model.SelectEvents<this>> (via: S): Promise<Selector.From<S>[]>;
416
+ once <P extends Model.EventsCompat<this>> (key: P | P[]): Promise<P[]>;
417
+ // Unknown
418
+ once <S extends Model.SelectEvents<this>> (via: S, cb: unknown, squash: boolean): Callback;
419
+ once <P extends Model.EventsCompat<this>> (key: P | P[], listener: unknown, squash: boolean): Callback;
420
+
421
+ effect(callback: (this: this, state: this) => void, select?: Model.SelectFields<this>): Callback;
422
+ effect(callback: (this: this, state: this) => void, select?: (keyof this)[]): Callback;
423
+
424
+ /**
425
+ * **React Hook** - Attach to instance of this controller within ambient component.
426
+ *
427
+ * This method will fire lifecycle events on given controller.
428
+ *
429
+ * @param id - Argument passed to controller-lifecycle methods. Use to identify the consumer.
430
+ */
431
+ tag(id?: Key): this;
432
+
433
+ /**
434
+ * **React Hook** - Attach to instance of this controller within ambient component.
435
+ *
436
+ * This method will fire lifecycle events on given controller.
437
+ *
438
+ * @param idFactory - Will be invoked with fetched instance. Use this to register a tag as-needed.
439
+ */
440
+ tag(idFactory: (idFactory: this) => Key | void): this;
441
+
442
+
365
443
  /**
366
444
  * **React Hook** - Attach to instance of this controller within ambient component.
367
445
  *
@@ -395,28 +473,17 @@ export abstract class Model {
395
473
  * Documentation TBD.
396
474
  */
397
475
  static meta <T extends Class, K extends Model.SelectField<T>> (this: T, key?: K): ReturnType<K>;
398
-
399
- /**
400
- * **React Hook** - Locate most relevant instance of this type in context.
401
- */
402
- static find <T extends Class>(this: T): InstanceOf<T>;
403
-
404
- /**
405
- * Static equivalent of `x instanceof this`.
406
- *
407
- * Will determine if provided class is a subtype of this one.
408
- */
409
- static isTypeof <T extends Class>(this: T, subject: any): subject is T;
410
-
411
- /**
412
- * Retreives parent inherited by this class, unless it is base `Model`.
413
- */
414
- static inherits: typeof Model | undefined;
415
476
  }
416
477
 
417
478
  export class Singleton extends Model {
418
- /** Current instance of this controller accessable anywhere. */
419
- static current?: Singleton;
479
+ /**
480
+ * Update the active instance of this class.
481
+ * Returns a thenable; resolves after successful update.
482
+ * If instance does not already exist, one will be created.
483
+ **/
484
+ static update<T extends Class>(
485
+ this: T, updates: Model.Data<InstanceOf<T>>
486
+ ): PromiseLike<string[] | false>;
420
487
 
421
488
  /** Destroy current instance of Singleton, if it exists. */
422
489
  static reset(): void;
package/types/selector.ts CHANGED
@@ -1,3 +1,7 @@
1
+ export type KeysOfType<T, ST> = {
2
+ [Key in keyof T]: T[Key] extends ST ? Key : never
3
+ }[keyof T];
4
+
1
5
  export namespace Selector {
2
6
  /**
3
7
  * Shallow-recursive clone of source object.
@@ -26,4 +30,18 @@ export namespace Selector {
26
30
 
27
31
  export type Gets<F extends Function<any>> =
28
32
  F extends (spy: any) => infer R ? Values<R> : never;
33
+
34
+ export type Callback<S extends Function<any>, T> =
35
+ (this: T, value: Gets<S>, key: From<S>) => void;
36
+ }
37
+
38
+ export namespace Select {
39
+ export type One<T> =
40
+ (arg: { [K in keyof T]: K }) => string & keyof T;
41
+
42
+ export type Key<T extends string | number | symbol> =
43
+ (arg: { [K in T]: K }) => string & T;
44
+
45
+ export type From<T extends One<any>> =
46
+ T extends (from: any) => infer R ? R : never;
29
47
  }
@@ -1,55 +0,0 @@
1
- import { Model } from './model';
2
- import { Selector } from './selector';
3
- import { Callback, EffectCallback, UpdateCallback } from './types';
4
-
5
- type CallbackFor<S extends Selector.Function<any>, T> =
6
- (this: T, value: Selector.Gets<S>, key: Selector.From<S>) => void;
7
-
8
- /**
9
- * Observable Instance
10
- *
11
- * Implements internal value tracking.
12
- * Able to be subscribed to, per-value to know when updated.
13
- */
14
- interface Dispatch {
15
- // Keyed
16
- on <S extends Model.SelectEvents<this>> (via: S, cb: CallbackFor<S, this>, squash?: false, once?: boolean): Callback;
17
- on <P extends Model.EventsCompat<this>> (key: P | P[], listener: UpdateCallback<this, P>, squash?: false, once?: boolean): Callback;
18
- // Squash
19
- on <S extends Model.SelectEvents<this>> (via: S, cb: (keys: Selector.From<S>[]) => void, squash: true, once?: boolean): Callback;
20
- on <P extends Model.EventsCompat<this>> (key: P | P[], listener: (keys: P[]) => void, squash: true, once?: boolean): Callback;
21
- // Unknown
22
- on <S extends Model.SelectEvents<this>> (via: S, cb: unknown, squash: boolean, once?: boolean): Callback;
23
- on <P extends Model.EventsCompat<this>> (key: P | P[], listener: unknown, squash: boolean, once?: boolean): Callback;
24
-
25
- // Keyed
26
- once <S extends Model.SelectEvents<this>> (via: S, cb: CallbackFor<S, this>, squash?: false): Callback;
27
- once <P extends Model.EventsCompat<this>> (key: P | P[], listener: UpdateCallback<this, P>, squash?: false): Callback;
28
- // Squash
29
- once <S extends Model.SelectEvents<this>> (via: S, cb: (keys: Selector.From<S>[]) => void, squash: true): Callback;
30
- once <P extends Model.EventsCompat<this>> (key: P | P[], listener: (keys: P[]) => void, squash: true): Callback;
31
- // Promise
32
- once <S extends Model.SelectEvents<this>> (via: S): Promise<Selector.From<S>[]>;
33
- once <P extends Model.EventsCompat<this>> (key: P | P[]): Promise<P[]>;
34
- // Unknown
35
- once <S extends Model.SelectEvents<this>> (via: S, cb: unknown, squash: boolean): Callback;
36
- once <P extends Model.EventsCompat<this>> (key: P | P[], listener: unknown, squash: boolean): Callback;
37
-
38
- effect(callback: EffectCallback<this>, select?: Model.SelectFields<this>): Callback;
39
- effect(callback: EffectCallback<this>, select?: (keyof this)[]): Callback;
40
-
41
- import <O extends Model.Data<this>> (via: O, select?: string[] | Model.SelectFields<this>): void;
42
-
43
- export(): Model.State<this>;
44
- export <P extends Model.Fields<this>> (select: P[]): Model.State<this, P>;
45
- export <S extends Model.SelectFields<this>> (select: S): Model.State<this, Selector.From<S>>;
46
-
47
- update(strict: true): Promise<string[]>;
48
- update(strict: false): Promise<false>;
49
- update(strict?: boolean): Promise<string[] | false>;
50
- update(keys: Model.Fields<this>[]): Promise<string[]>;
51
- update(keys: Model.Fields<this>): Promise<string[]>;
52
- update(keys: Model.SelectFields<this>): Promise<string[]>;
53
- }
54
-
55
- export = Dispatch;