@expressive/mvc 0.18.1 → 0.19.2

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 CHANGED
@@ -1,13 +1,43 @@
1
- declare const LOCAL = "$debug local";
2
- declare const STATE = "$debug state";
3
- declare const UPDATE = "$debug update";
4
- declare const CONTROL = "$debug controller";
1
+ declare namespace Issue {
2
+ type Variable = {} | string | number | boolean | null | undefined;
3
+ type Variables<T> = T extends (...args: infer T) => any ? T : never;
4
+ type Messages = {
5
+ [named: string]: (...args: Variable[]) => string;
6
+ };
7
+ type Output<M extends Messages> = {
8
+ readonly [P in keyof M]: (...args: Variables<M[P]>) => Issue;
9
+ };
10
+ }
11
+ declare class Issue extends Error {
12
+ stack: string;
13
+ warn(): void;
14
+ }
15
+ declare function issues<M extends Issue.Messages>(register: M): Issue.Output<M>;
16
+
17
+ declare const LOCAL = "__local";
18
+ declare const STATE = "__state";
19
+ declare const UPDATE = "__update";
20
+ declare const CONTROL = "__control";
5
21
  declare const Debug: {
6
- readonly CONTROL: "$debug controller";
7
- readonly LOCAL: "$debug local";
8
- readonly STATE: "$debug state";
9
- readonly UPDATE: "$debug update";
22
+ /** Use to access Model's local Subscriber (if exists). */
23
+ LOCAL: "__local";
24
+ /** Use to access snapshot of Model's current state. */
25
+ STATE: "__state";
26
+ /**
27
+ * Use to access snapshot of Model's latest update.
28
+ *
29
+ * Note: If used within a Subscriber, update will be narrowed to the specific keys
30
+ * which triggered a refresh (if one did).
31
+ */
32
+ UPDATE: "__update";
33
+ /** Use to access a Model's controller. */
34
+ CONTROL: "__control";
10
35
  };
36
+ /**
37
+ * Generic will declare debug properties on a given Model.
38
+ *
39
+ * These properties always exist at runtime, however are hidden unless you cast your Model as `Debug<T>`.
40
+ */
11
41
  type Debug<T extends Model> = T & {
12
42
  /** Controller for this instance. */
13
43
  [CONTROL]?: Control<T>;
@@ -28,24 +58,19 @@ type Callback = () => void;
28
58
  type Class = new (...args: any[]) => any;
29
59
  type InstanceOf<T> = T extends { prototype: infer U } ? U : never;
30
60
 
61
+ type MaybePromise<T> = T | Promise<T>;
62
+
31
63
  /** Type may not be undefined - instead will be null. */
32
64
  type NoVoid<T> = T extends undefined ? null : T;
33
65
 
34
- type OptionalValues<T extends {}> = {
35
- [P in Model.Key<T>]: T[P] | undefined;
36
- }
37
-
38
- type NonOptionalValues<T extends {}> = {
39
- [P in Model.Key<T>]: Exclude<T[P], undefined>;
40
- };
41
-
42
66
  declare namespace Model {
43
67
  export { Control };
44
68
  export { Subscriber };
45
69
  /** Including but not limited to T. */
46
70
  type Extends<T> = T | (string & Record<never, never>);
47
71
  /** Any typeof Model, using class constructor as the reference. */
48
- export type Type<T extends Model = Model> = (abstract new (...args: any[]) => T);
72
+ export type Type<T extends Model = Model> = abstract new () => T;
73
+ export type Class<T extends Model> = (abstract new () => T) & typeof Model;
49
74
  /** A typeof Model, specifically one which may be created without arguments. */
50
75
  export type New<T extends Model = Model> = (new () => T) & typeof Model;
51
76
  export type Effect<T> = (this: T, argument: T) => Callback | Promise<any> | void;
@@ -95,7 +120,7 @@ declare namespace Model {
95
120
  [P in Key<T>]: Value<T[P]>;
96
121
  };
97
122
  export type FindFunction = <T extends Model>(type: Model.Type<T>, relativeTo: Model, required: boolean) => (_refresh: (x: T) => void) => T | undefined;
98
- export type TapCallback<T extends Model, R> = (this: T, model: T, update: ForceUpdate) => R;
123
+ export type GetCallback<T extends Model, R> = (this: T, model: T, update: ForceUpdate) => R;
99
124
  type ForceUpdate = {
100
125
  /** Force an update in current component. */
101
126
  (): void;
@@ -119,7 +144,9 @@ declare class Model {
119
144
  * Use to obtain full reference from a destructure.
120
145
  */
121
146
  is: this;
122
- constructor();
147
+ /** Unique identifier for Model instance. */
148
+ id: string;
149
+ constructor(id?: string);
123
150
  on<P extends Model.Event<this>>(keys?: P | Iterable<P>, timeout?: number): Promise<P[]>;
124
151
  on<P extends Model.Event<this>>(keys: P | Iterable<P>, listener: (this: this, keys: Model.Event<this>[]) => void, once?: boolean): Callback;
125
152
  on(effect: Model.Effect<this>): Callback;
@@ -130,15 +157,15 @@ declare class Model {
130
157
  on(current: true): Promise<Model.Event<this>[]>;
131
158
  on(current: null): Promise<null>;
132
159
  get(): Model.Export<this>;
133
- get<P extends Model.Key<this>>(property: P): this[P];
134
- get<P extends Model.Key<this>>(property: P, timeout: number): Promise<this[P]>;
135
- get<P extends Model.Key<this>>(property: P, onChange: true): Promise<this[P]>;
136
- get<P extends Model.Key<this>>(property: P, onChange: boolean): this[P] | Promise<this[P]>;
137
- get<P extends Model.Key<this>>(property: P, listener: (this: this, value: this[P], key: P[]) => void): Callback;
160
+ get<P extends Model.Key<this>>(select: P): this[P];
138
161
  get<P extends Model.Key<this>>(select: Iterable<P>): Model.Get<this, P>;
162
+ get<P extends Model.Key<this>>(select: P, timeout: number): Promise<this[P]>;
139
163
  get<P extends Model.Key<this>>(select: Iterable<P>, timeout: number): Promise<Model.Get<this, P>>;
164
+ get<P extends Model.Key<this>>(select: P, onChange: true): Promise<this[P]>;
140
165
  get<P extends Model.Key<this>>(select: Iterable<P>, onChange: true): Promise<Model.Get<this, P>>;
141
- get<P extends Model.Key<this>>(select: Iterable<P>, onChange: boolean): Model.Get<this, P> | Promise<Model.Get<this, P>>;
166
+ get<P extends Model.Key<this>>(select: P, onChange: boolean): MaybePromise<this[P]>;
167
+ get<P extends Model.Key<this>>(select: Iterable<P>, onChange: boolean): MaybePromise<Model.Get<this, P>>;
168
+ get<P extends Model.Key<this>>(select: P, listener: (this: this, value: this[P], key: P[]) => void): Callback;
142
169
  get<P extends Model.Key<this>>(select: Iterable<P>, listener: (this: this, value: Model.Get<this, P>, key: P[]) => void): Callback;
143
170
  set(key: Model.Event<this>): PromiseLike<readonly Model.Event<this>[]>;
144
171
  set<K extends Model.Event<this>>(key: Model.Event<this>, value: Model.ValueOf<this, K>): PromiseLike<readonly Model.Event<this>[]>;
@@ -154,29 +181,22 @@ declare class Model {
154
181
  * @param args - arguments sent to constructor
155
182
  */
156
183
  static new<T extends Class>(this: T, ...args: ConstructorParameters<T>): InstanceOf<T>;
184
+ static find<T extends Model>(this: Model.Type<T>, required?: boolean): T;
185
+ static find<T extends Model>(this: Model.Type<T>, required: false): T | undefined;
186
+ static get<T extends Model>(this: Model.Type<T>): T;
187
+ static get<T extends Model>(this: Model.Type<T>, passive: true): T;
188
+ static get<T extends Model>(this: Model.Type<T>, required: false): T | undefined;
189
+ static get<T extends Model, R extends []>(this: Model.Type<T>, compute: Model.GetCallback<T, R | (() => R)>, expect?: boolean): R;
190
+ static get<T extends Model, R extends []>(this: Model.Type<T>, compute: Model.GetCallback<T, Promise<R> | (() => R) | null>, expect?: boolean): R | null;
191
+ static get<T extends Model, R extends []>(this: Model.Type<T>, compute: Model.GetCallback<T, Promise<R> | R>, expect: true): Exclude<R, undefined>;
192
+ static get<T extends Model, R>(this: Model.Type<T>, init: Model.GetCallback<T, () => R>): NoVoid<R>;
193
+ static get<T extends Model, R>(this: Model.Type<T>, init: Model.GetCallback<T, (() => R) | null>): NoVoid<R> | null;
194
+ static get<T extends Model, R>(this: Model.Type<T>, compute: Model.GetCallback<T, Promise<R> | R>, expect: true): Exclude<R, undefined>;
195
+ static get<T extends Model, R>(this: Model.Type<T>, compute: Model.GetCallback<T, Promise<R>>, expect?: boolean): NoVoid<R> | null;
196
+ static get<T extends Model, R>(this: Model.Type<T>, compute: Model.GetCallback<T, R>, expect?: boolean): NoVoid<R>;
157
197
  static use<I extends Model>(this: Model.Type<I>, watch: Model.Key<I>[], callback?: (instance: I) => void): I;
158
198
  static use<I extends Model>(this: Model.Type<I>, callback?: (instance: I) => void): I;
159
199
  static use<I extends Model>(this: Model.Type<I>, apply: Model.Compat<I>, keys?: Model.Event<I>[]): I;
160
- static get<T extends Model>(this: Model.Type<T>, required?: boolean): T;
161
- static get<T extends Model>(this: Model.Type<T>, required: false): T | undefined;
162
- static tap<T extends Model>(this: Model.Type<T>): T;
163
- static tap<T extends Model, R extends readonly unknown[] | []>(this: Model.Type<T>, compute: Model.TapCallback<T, R | (() => R)>, expect?: boolean): R;
164
- static tap<T extends Model, R extends readonly unknown[] | []>(this: Model.Type<T>, compute: Model.TapCallback<T, Promise<R> | (() => R) | null>, expect?: boolean): R | null;
165
- static tap<T extends Model, R extends readonly unknown[] | []>(this: Model.Type<T>, compute: Model.TapCallback<T, Promise<R> | R>, expect: true): Exclude<R, undefined>;
166
- static tap<T extends Model, R>(this: Model.Type<T>, init: Model.TapCallback<T, () => R>): NoVoid<R>;
167
- static tap<T extends Model, R>(this: Model.Type<T>, init: Model.TapCallback<T, (() => R) | null>): NoVoid<R> | null;
168
- static tap<T extends Model, R>(this: Model.Type<T>, compute: Model.TapCallback<T, Promise<R> | R>, expect: true): Exclude<R, undefined>;
169
- static tap<T extends Model, R>(this: Model.Type<T>, compute: Model.TapCallback<T, Promise<R>>, expect?: boolean): NoVoid<R> | null;
170
- static tap<T extends Model, R>(this: Model.Type<T>, compute: Model.TapCallback<T, R>, expect?: boolean): NoVoid<R>;
171
- static tap<T extends Model>(this: Model.Type<T>, expect: true): NonOptionalValues<T>;
172
- static tap<T extends Model>(this: Model.Type<T>, expect?: boolean): OptionalValues<T>;
173
- protected static meta<T extends Class>(this: T): T;
174
- protected static meta<T extends Class>(this: T, expect: true): NonOptionalValues<T>;
175
- protected static meta<T extends Class>(this: T, expect?: boolean): OptionalValues<T>;
176
- protected static meta<T, M extends Class>(this: M, from: (this: M, state: M) => Promise<T>, expect: true): Exclude<T, undefined>;
177
- protected static meta<T, M extends Class>(this: M, from: (this: M, state: M) => Promise<T>, expect?: boolean): T;
178
- protected static meta<T, M extends Class>(this: M, from: (this: M, state: M) => T, expect: true): Exclude<T, undefined>;
179
- protected static meta<T, M extends Class>(this: M, from: (this: M, state: M) => T, expect?: boolean): T;
180
200
  /**
181
201
  * Static equivalent of `x instanceof this`.
182
202
  *
@@ -272,7 +292,7 @@ declare namespace control {
272
292
  declare function control<T extends Model>(subject: T): Control<T>;
273
293
  declare function control<T extends Model>(subject: T, cb: control.OnReady<T>): Callback;
274
294
 
275
- declare function getParent<T extends Model>(from: Model, type?: Model.Type<T>): T;
295
+ declare function getParent<T extends Model>(from: Model, type?: Model.Class<T>): T;
276
296
 
277
297
  type Async<T = any> = (...args: any[]) => Promise<T>;
278
298
  /**
@@ -295,7 +315,7 @@ declare function run<S>(action: Async<S>): typeof action & {
295
315
  declare namespace get {
296
316
  type Function<T, S = any> = (this: S, on: S) => T;
297
317
  type Factory<R, T> = (this: T, property: string, on: T) => Function<R, T>;
298
- type FindFunction<T extends Model = Model> = (type: typeof Model, relativeTo: Model, required: boolean) => Source<T>;
318
+ type FindFunction<T extends Model = Model> = (type: Model.Class<T>, relativeTo: Model, required: boolean) => Source<T>;
299
319
  type Source<T extends Model = Model> = (callback: (x: T) => void) => void;
300
320
  /** Assign fetch algorithm for get instruction. */
301
321
  let using: (fn: FindFunction) => typeof get;
@@ -479,4 +499,4 @@ declare const Internal: {
479
499
  getParent: typeof getParent;
480
500
  };
481
501
 
482
- export { Control, Debug, Internal, Model, Register, Subscriber, add, Model as default, get, ref, run, set, use };
502
+ export { Control, Debug, Internal, Model, Register, Subscriber, add, Model as default, get, issues, ref, run, set, use };
package/lib/index.esm.js CHANGED
@@ -1,4 +1,8 @@
1
- const { assign, create, defineProperties, defineProperty, entries, getPrototypeOf, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
1
+ const { assign, create, defineProperties, defineProperty, entries, getPrototypeOf, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
2
+ /** Random alphanumberic of length 6; will always start with a letter. */
3
+ function random() {
4
+ return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
5
+ }
2
6
 
3
7
  class Issue extends Error {
4
8
  constructor() {
@@ -136,20 +140,17 @@ function createEffect(model, callback, keys) {
136
140
  return control(model, controller => {
137
141
  let unSet;
138
142
  let busy = false;
139
- function effect(value) {
140
- if (typeof unSet == "function")
141
- unSet();
142
- unSet = callback.call(this, value);
143
- if (unSet instanceof Promise)
144
- unSet = undefined;
145
- if (unSet && typeof unSet !== "function")
146
- throw Oops$7.BadCallback();
147
- }
148
143
  function invoke() {
149
144
  if (busy)
150
145
  return;
151
146
  const output = mayRetry(() => {
152
- effect.call(model, model);
147
+ if (typeof unSet == "function")
148
+ unSet();
149
+ unSet = callback.call(model, model);
150
+ if (unSet instanceof Promise)
151
+ unSet = undefined;
152
+ if (unSet && typeof unSet !== "function")
153
+ throw Oops$7.BadCallback();
153
154
  });
154
155
  if (output instanceof Promise) {
155
156
  output.finally(() => busy = false);
@@ -281,9 +282,10 @@ const Oops$5 = issues({
281
282
  NoAdapter: (method) => `Can't call Model.${method} without an adapter.`
282
283
  });
283
284
  class Model {
284
- constructor() {
285
+ constructor(id) {
285
286
  new Control(this);
286
287
  defineProperty(this, "is", { value: this });
288
+ defineProperty(this, "id", { value: id || random() });
287
289
  }
288
290
  on(arg1, arg2, arg3) {
289
291
  if (typeof arg1 == "function")
@@ -313,14 +315,7 @@ class Model {
313
315
  set(arg1, arg2) {
314
316
  const controller = control(this);
315
317
  const { state } = controller;
316
- if (typeof arg1 == "object")
317
- for (const key in arg1) {
318
- if (arg2 === true || (arg2 ? arg2.includes(key) : state.has(key))) {
319
- state.set(key, arg1[key]);
320
- controller.update(key);
321
- }
322
- }
323
- else if (typeof arg1 == "string") {
318
+ if (typeof arg1 == "string") {
324
319
  controller.update(arg1);
325
320
  if (1 in arguments) {
326
321
  if (state.has(arg1))
@@ -332,6 +327,12 @@ class Model {
332
327
  }
333
328
  }
334
329
  }
330
+ else if (typeof arg1 == "object")
331
+ for (const key in arg1)
332
+ if (arg2 === true || (arg2 ? arg2.includes(key) : state.has(key))) {
333
+ state.set(key, arg1[key]);
334
+ controller.update(key);
335
+ }
335
336
  return {
336
337
  then: (callback) => {
337
338
  if (!callback)
@@ -356,17 +357,14 @@ class Model {
356
357
  control(instance);
357
358
  return instance;
358
359
  }
359
- static use() {
360
- throw Oops$5.NoAdapter("use");
360
+ static find() {
361
+ throw Oops$5.NoAdapter("find");
361
362
  }
362
363
  static get() {
363
364
  throw Oops$5.NoAdapter("get");
364
365
  }
365
- static tap() {
366
- throw Oops$5.NoAdapter("tap");
367
- }
368
- static meta() {
369
- throw Oops$5.NoAdapter("meta");
366
+ static use() {
367
+ throw Oops$5.NoAdapter("use");
370
368
  }
371
369
  /**
372
370
  * Static equivalent of `x instanceof this`.
@@ -379,8 +377,14 @@ class Model {
379
377
  }
380
378
  }
381
379
  defineProperty(Model.prototype, "toString", {
380
+ configurable: true,
382
381
  value() {
383
- return this.constructor.name;
382
+ return `${this.constructor.name}-${this.id}`;
383
+ }
384
+ });
385
+ defineProperty(Model, "toString", {
386
+ value() {
387
+ return this.name;
384
388
  }
385
389
  });
386
390
 
@@ -418,7 +422,7 @@ function add(instruction, label) {
418
422
 
419
423
  const Oops$4 = issues({
420
424
  PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or get) as computed source for ${model}.${property}. This is not allowed.`,
421
- Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
425
+ Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} ${parent}.${property}.`,
422
426
  Required: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
423
427
  });
424
428
  function get(arg0, arg1) {
@@ -435,8 +439,8 @@ function get(arg0, arg1) {
435
439
  else if (typeof arg0 == "function")
436
440
  arg1 = arg0.call(subject, key, subject);
437
441
  return typeof arg1 == "function"
438
- ? getComputed(key, this, source, arg1)
439
- : getRecursive$1(key, this, source, arg1);
442
+ ? computed(this, key, source, arg1)
443
+ : recursive(this, key, source, arg1);
440
444
  });
441
445
  }
442
446
  function getParentForGetInstruction(type, relativeTo, required) {
@@ -445,54 +449,26 @@ function getParentForGetInstruction(type, relativeTo, required) {
445
449
  if (item)
446
450
  callback(item);
447
451
  else if (required)
448
- throw Oops$4.Required(type.name, relativeTo);
452
+ throw Oops$4.Required(type, relativeTo.constructor);
449
453
  };
450
454
  }
451
- function getRecursive$1(key, parent, source, required) {
452
- const context = new WeakMap();
453
- const { state } = parent;
455
+ function recursive(parent, key, source, required) {
456
+ const getter = getRecursive(key, parent);
454
457
  let waiting;
455
458
  source((got) => {
456
- state.set(key, got);
459
+ parent.state.set(key, got);
457
460
  if (waiting)
458
461
  parent.update(key);
459
462
  });
460
463
  waiting = true;
461
- function create(local) {
462
- let reset;
463
- local.follow(key, init);
464
- init();
465
- function init() {
466
- if (reset)
467
- reset();
468
- const value = state.get(key);
469
- if (value && controller(value)) {
470
- const child = new Subscriber(value, local.onUpdate);
471
- if (local.active)
472
- child.commit();
473
- local.dependant.add(child);
474
- context.set(local, child.proxy);
475
- reset = () => {
476
- child.release();
477
- local.dependant.delete(child);
478
- context.set(local, undefined);
479
- reset = undefined;
480
- };
481
- }
482
- }
483
- }
484
464
  return (local) => {
485
- const value = state.get(key);
465
+ const value = parent.state.get(key);
486
466
  if (!value && required !== false)
487
467
  parent.waitFor(key);
488
- if (!local)
489
- return value;
490
- if (!context.has(local))
491
- create(local);
492
- return context.get(local);
468
+ return local ? getter(local) : value;
493
469
  };
494
470
  }
495
- function getComputed(key, parent, source, setter) {
471
+ function computed(parent, key, source, setter) {
496
472
  const { state } = parent;
497
473
  let local;
498
474
  let active;
@@ -553,7 +529,7 @@ function getComputed(key, parent, source, setter) {
553
529
  const INFO = new WeakMap();
554
530
  const KEYS = new WeakMap();
555
531
  const ORDER = new WeakMap();
556
- function flush(control) {
532
+ function flushComputed(control) {
557
533
  const pending = KEYS.get(control);
558
534
  if (!pending || !pending.size)
559
535
  return;
@@ -647,7 +623,7 @@ class Control {
647
623
  return;
648
624
  else if (!frame.size)
649
625
  setTimeout(() => {
650
- flush(this);
626
+ flushComputed(this);
651
627
  const keys = this.latest = Array.from(frame);
652
628
  setTimeout(() => this.latest = undefined, 0);
653
629
  frame.clear();
@@ -706,21 +682,19 @@ function control(subject, cb) {
706
682
 
707
683
  const Parent = new WeakMap();
708
684
  const Oops$3 = issues({
709
- BadAssignment: (parent, expected, got) => `Property ${parent} expected Model of type ${expected} but got ${got}.`,
685
+ BadAssignment: (parent, expected, got) => `${parent} expected Model of type ${expected} but got ${got}.`,
710
686
  Unexpected: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
711
687
  });
712
688
  function getParent(from, type) {
713
689
  const value = Parent.get(from);
714
690
  if (value && type && !(value instanceof type))
715
- throw Oops$3.Unexpected(type.name, from, value);
691
+ throw Oops$3.Unexpected(type, from, value);
716
692
  return value;
717
693
  }
718
694
  function getRecursive(key, from) {
719
695
  const context = new WeakMap();
720
696
  const { state } = from;
721
697
  return (local) => {
722
- if (!local)
723
- return state.get(key);
724
698
  if (!context.has(local)) {
725
699
  let reset;
726
700
  const init = () => {
@@ -754,8 +728,9 @@ function setRecursive(controller, key, initial) {
754
728
  const onUpdate = (next) => {
755
729
  state.set(key, next);
756
730
  if (!(next instanceof Type))
757
- throw Oops$3.BadAssignment(`${subject}.${key}`, Type.name, String(next));
758
- get = getRecursive(key, controller);
731
+ throw Oops$3.BadAssignment(`${subject}.${key}`, Type, String(next));
732
+ const getter = getRecursive(key, controller);
733
+ get = (local) => local ? getter(local) : state.get(key);
759
734
  Parent.set(next, subject);
760
735
  control(next);
761
736
  return true;
@@ -1107,7 +1082,8 @@ function use(input, argument) {
1107
1082
  const onUpdate = (next) => {
1108
1083
  state.set(key, next);
1109
1084
  if (next) {
1110
- get = getRecursive(key, this);
1085
+ const getter = getRecursive(key, this);
1086
+ get = (local) => local ? getter(local) : state.get(key);
1111
1087
  Parent.set(next, subject);
1112
1088
  control(next);
1113
1089
  }
@@ -1163,10 +1139,10 @@ function use(input, argument) {
1163
1139
  });
1164
1140
  }
1165
1141
 
1166
- const LOCAL = "$debug local";
1167
- const STATE = "$debug state";
1168
- const UPDATE = "$debug update";
1169
- const CONTROL = "$debug controller";
1142
+ const LOCAL = "__local";
1143
+ const STATE = "__state";
1144
+ const UPDATE = "__update";
1145
+ const CONTROL = "__control";
1170
1146
  defineProperties(Model.prototype, {
1171
1147
  [CONTROL]: {
1172
1148
  get() {
@@ -1262,4 +1238,4 @@ const Internal = {
1262
1238
  getParent
1263
1239
  };
1264
1240
 
1265
- export { Control, Debug, Internal, Model, Register, Subscriber, add, Model as default, get, ref, run, set, use };
1241
+ export { Control, Debug, Internal, Model, Register, Subscriber, add, Model as default, get, issues, ref, run, set, use };
package/lib/index.js CHANGED
@@ -2,7 +2,11 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- const { assign, create, defineProperties, defineProperty, entries, getPrototypeOf, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
5
+ const { assign, create, defineProperties, defineProperty, entries, getPrototypeOf, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
6
+ /** Random alphanumberic of length 6; will always start with a letter. */
7
+ function random() {
8
+ return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
9
+ }
6
10
 
7
11
  class Issue extends Error {
8
12
  constructor() {
@@ -140,20 +144,17 @@ function createEffect(model, callback, keys) {
140
144
  return control(model, controller => {
141
145
  let unSet;
142
146
  let busy = false;
143
- function effect(value) {
144
- if (typeof unSet == "function")
145
- unSet();
146
- unSet = callback.call(this, value);
147
- if (unSet instanceof Promise)
148
- unSet = undefined;
149
- if (unSet && typeof unSet !== "function")
150
- throw Oops$7.BadCallback();
151
- }
152
147
  function invoke() {
153
148
  if (busy)
154
149
  return;
155
150
  const output = mayRetry(() => {
156
- effect.call(model, model);
151
+ if (typeof unSet == "function")
152
+ unSet();
153
+ unSet = callback.call(model, model);
154
+ if (unSet instanceof Promise)
155
+ unSet = undefined;
156
+ if (unSet && typeof unSet !== "function")
157
+ throw Oops$7.BadCallback();
157
158
  });
158
159
  if (output instanceof Promise) {
159
160
  output.finally(() => busy = false);
@@ -285,9 +286,10 @@ const Oops$5 = issues({
285
286
  NoAdapter: (method) => `Can't call Model.${method} without an adapter.`
286
287
  });
287
288
  class Model {
288
- constructor() {
289
+ constructor(id) {
289
290
  new Control(this);
290
291
  defineProperty(this, "is", { value: this });
292
+ defineProperty(this, "id", { value: id || random() });
291
293
  }
292
294
  on(arg1, arg2, arg3) {
293
295
  if (typeof arg1 == "function")
@@ -317,14 +319,7 @@ class Model {
317
319
  set(arg1, arg2) {
318
320
  const controller = control(this);
319
321
  const { state } = controller;
320
- if (typeof arg1 == "object")
321
- for (const key in arg1) {
322
- if (arg2 === true || (arg2 ? arg2.includes(key) : state.has(key))) {
323
- state.set(key, arg1[key]);
324
- controller.update(key);
325
- }
326
- }
327
- else if (typeof arg1 == "string") {
322
+ if (typeof arg1 == "string") {
328
323
  controller.update(arg1);
329
324
  if (1 in arguments) {
330
325
  if (state.has(arg1))
@@ -336,6 +331,12 @@ class Model {
336
331
  }
337
332
  }
338
333
  }
334
+ else if (typeof arg1 == "object")
335
+ for (const key in arg1)
336
+ if (arg2 === true || (arg2 ? arg2.includes(key) : state.has(key))) {
337
+ state.set(key, arg1[key]);
338
+ controller.update(key);
339
+ }
339
340
  return {
340
341
  then: (callback) => {
341
342
  if (!callback)
@@ -360,17 +361,14 @@ class Model {
360
361
  control(instance);
361
362
  return instance;
362
363
  }
363
- static use() {
364
- throw Oops$5.NoAdapter("use");
364
+ static find() {
365
+ throw Oops$5.NoAdapter("find");
365
366
  }
366
367
  static get() {
367
368
  throw Oops$5.NoAdapter("get");
368
369
  }
369
- static tap() {
370
- throw Oops$5.NoAdapter("tap");
371
- }
372
- static meta() {
373
- throw Oops$5.NoAdapter("meta");
370
+ static use() {
371
+ throw Oops$5.NoAdapter("use");
374
372
  }
375
373
  /**
376
374
  * Static equivalent of `x instanceof this`.
@@ -383,8 +381,14 @@ class Model {
383
381
  }
384
382
  }
385
383
  defineProperty(Model.prototype, "toString", {
384
+ configurable: true,
386
385
  value() {
387
- return this.constructor.name;
386
+ return `${this.constructor.name}-${this.id}`;
387
+ }
388
+ });
389
+ defineProperty(Model, "toString", {
390
+ value() {
391
+ return this.name;
388
392
  }
389
393
  });
390
394
 
@@ -422,7 +426,7 @@ function add(instruction, label) {
422
426
 
423
427
  const Oops$4 = issues({
424
428
  PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or get) as computed source for ${model}.${property}. This is not allowed.`,
425
- Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
429
+ Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} ${parent}.${property}.`,
426
430
  Required: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
427
431
  });
428
432
  function get(arg0, arg1) {
@@ -439,8 +443,8 @@ function get(arg0, arg1) {
439
443
  else if (typeof arg0 == "function")
440
444
  arg1 = arg0.call(subject, key, subject);
441
445
  return typeof arg1 == "function"
442
- ? getComputed(key, this, source, arg1)
443
- : getRecursive$1(key, this, source, arg1);
446
+ ? computed(this, key, source, arg1)
447
+ : recursive(this, key, source, arg1);
444
448
  });
445
449
  }
446
450
  function getParentForGetInstruction(type, relativeTo, required) {
@@ -449,54 +453,26 @@ function getParentForGetInstruction(type, relativeTo, required) {
449
453
  if (item)
450
454
  callback(item);
451
455
  else if (required)
452
- throw Oops$4.Required(type.name, relativeTo);
456
+ throw Oops$4.Required(type, relativeTo.constructor);
453
457
  };
454
458
  }
455
- function getRecursive$1(key, parent, source, required) {
456
- const context = new WeakMap();
457
- const { state } = parent;
459
+ function recursive(parent, key, source, required) {
460
+ const getter = getRecursive(key, parent);
458
461
  let waiting;
459
462
  source((got) => {
460
- state.set(key, got);
463
+ parent.state.set(key, got);
461
464
  if (waiting)
462
465
  parent.update(key);
463
466
  });
464
467
  waiting = true;
465
- function create(local) {
466
- let reset;
467
- local.follow(key, init);
468
- init();
469
- function init() {
470
- if (reset)
471
- reset();
472
- const value = state.get(key);
473
- if (value && controller(value)) {
474
- const child = new Subscriber(value, local.onUpdate);
475
- if (local.active)
476
- child.commit();
477
- local.dependant.add(child);
478
- context.set(local, child.proxy);
479
- reset = () => {
480
- child.release();
481
- local.dependant.delete(child);
482
- context.set(local, undefined);
483
- reset = undefined;
484
- };
485
- }
486
- }
487
- }
488
468
  return (local) => {
489
- const value = state.get(key);
469
+ const value = parent.state.get(key);
490
470
  if (!value && required !== false)
491
471
  parent.waitFor(key);
492
- if (!local)
493
- return value;
494
- if (!context.has(local))
495
- create(local);
496
- return context.get(local);
472
+ return local ? getter(local) : value;
497
473
  };
498
474
  }
499
- function getComputed(key, parent, source, setter) {
475
+ function computed(parent, key, source, setter) {
500
476
  const { state } = parent;
501
477
  let local;
502
478
  let active;
@@ -557,7 +533,7 @@ function getComputed(key, parent, source, setter) {
557
533
  const INFO = new WeakMap();
558
534
  const KEYS = new WeakMap();
559
535
  const ORDER = new WeakMap();
560
- function flush(control) {
536
+ function flushComputed(control) {
561
537
  const pending = KEYS.get(control);
562
538
  if (!pending || !pending.size)
563
539
  return;
@@ -651,7 +627,7 @@ class Control {
651
627
  return;
652
628
  else if (!frame.size)
653
629
  setTimeout(() => {
654
- flush(this);
630
+ flushComputed(this);
655
631
  const keys = this.latest = Array.from(frame);
656
632
  setTimeout(() => this.latest = undefined, 0);
657
633
  frame.clear();
@@ -710,21 +686,19 @@ function control(subject, cb) {
710
686
 
711
687
  const Parent = new WeakMap();
712
688
  const Oops$3 = issues({
713
- BadAssignment: (parent, expected, got) => `Property ${parent} expected Model of type ${expected} but got ${got}.`,
689
+ BadAssignment: (parent, expected, got) => `${parent} expected Model of type ${expected} but got ${got}.`,
714
690
  Unexpected: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
715
691
  });
716
692
  function getParent(from, type) {
717
693
  const value = Parent.get(from);
718
694
  if (value && type && !(value instanceof type))
719
- throw Oops$3.Unexpected(type.name, from, value);
695
+ throw Oops$3.Unexpected(type, from, value);
720
696
  return value;
721
697
  }
722
698
  function getRecursive(key, from) {
723
699
  const context = new WeakMap();
724
700
  const { state } = from;
725
701
  return (local) => {
726
- if (!local)
727
- return state.get(key);
728
702
  if (!context.has(local)) {
729
703
  let reset;
730
704
  const init = () => {
@@ -758,8 +732,9 @@ function setRecursive(controller, key, initial) {
758
732
  const onUpdate = (next) => {
759
733
  state.set(key, next);
760
734
  if (!(next instanceof Type))
761
- throw Oops$3.BadAssignment(`${subject}.${key}`, Type.name, String(next));
762
- get = getRecursive(key, controller);
735
+ throw Oops$3.BadAssignment(`${subject}.${key}`, Type, String(next));
736
+ const getter = getRecursive(key, controller);
737
+ get = (local) => local ? getter(local) : state.get(key);
763
738
  Parent.set(next, subject);
764
739
  control(next);
765
740
  return true;
@@ -1111,7 +1086,8 @@ function use(input, argument) {
1111
1086
  const onUpdate = (next) => {
1112
1087
  state.set(key, next);
1113
1088
  if (next) {
1114
- get = getRecursive(key, this);
1089
+ const getter = getRecursive(key, this);
1090
+ get = (local) => local ? getter(local) : state.get(key);
1115
1091
  Parent.set(next, subject);
1116
1092
  control(next);
1117
1093
  }
@@ -1167,10 +1143,10 @@ function use(input, argument) {
1167
1143
  });
1168
1144
  }
1169
1145
 
1170
- const LOCAL = "$debug local";
1171
- const STATE = "$debug state";
1172
- const UPDATE = "$debug update";
1173
- const CONTROL = "$debug controller";
1146
+ const LOCAL = "__local";
1147
+ const STATE = "__state";
1148
+ const UPDATE = "__update";
1149
+ const CONTROL = "__control";
1174
1150
  defineProperties(Model.prototype, {
1175
1151
  [CONTROL]: {
1176
1152
  get() {
@@ -1275,6 +1251,7 @@ exports.Subscriber = Subscriber;
1275
1251
  exports.add = add;
1276
1252
  exports["default"] = Model;
1277
1253
  exports.get = get;
1254
+ exports.issues = issues;
1278
1255
  exports.ref = ref;
1279
1256
  exports.run = run;
1280
1257
  exports.set = set;
package/package.json CHANGED
@@ -37,5 +37,5 @@
37
37
  "tslib": "^2.3.0",
38
38
  "typescript": "^4.9.5"
39
39
  },
40
- "version": "0.18.1"
40
+ "version": "0.19.2"
41
41
  }