@expressive/mvc 0.35.2 → 0.35.5-alpha.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/dist/index.d.ts CHANGED
@@ -4,23 +4,23 @@ declare namespace Model {
4
4
  /** A type of Model which may be created without constructor arguments. */
5
5
  type New<T extends Model = Model> = (new () => T) & typeof Model;
6
6
  /** Subset of `keyof T` which are not methods or defined by base Model U. **/
7
- type Key<T> = Exclude<keyof T, keyof Model> & string;
8
- type Any<T> = Exclude<keyof T, keyof Model> | (string & {});
7
+ type Field<T> = Exclude<keyof T, keyof Model>;
8
+ type Key<T> = Field<T> | (string & {}) | number | symbol;
9
9
  /** Actual value stored in state. */
10
- type Value<R> = R extends Ref<infer T> ? T : R extends Model ? Export<R> : R;
11
- type ValueOf<T extends Model, K extends Any<T>> = K extends keyof T ? T[K] extends Ref<infer V> ? V : T[K] : unknown;
12
- type ValueCallback<T extends Model, K extends Any<T>> = (this: T, value: Model.ValueOf<T, K>, key: K, thisArg: K) => void;
10
+ type Actual<R> = R extends Ref<infer T> ? T : R extends Model ? State<R> : R;
11
+ type Value<T extends Model, K extends Key<T>> = K extends keyof T ? T[K] extends Ref<infer V> ? V : T[K] : unknown;
12
+ type OnUpdate<T extends Model, K extends Key<T>> = (this: T, value: Value<T, K>, key: K, thisArg: K) => void;
13
13
  /**
14
14
  * Values from current state of given controller.
15
15
  * Differs from `Values` as values here will drill
16
16
  * into "real" values held by exotics like ref.
17
17
  */
18
- type Export<T> = {
19
- [P in Key<T>]: Value<T[P]>;
18
+ type State<T> = {
19
+ [P in Field<T>]: Actual<T[P]>;
20
20
  };
21
21
  /** Object comperable to data found in T. */
22
22
  type Values<T> = {
23
- [P in Key<T>]?: Value<T[P]>;
23
+ [P in Field<T>]?: Actual<T[P]>;
24
24
  };
25
25
  /** Exotic value, where actual value is contained within. */
26
26
  type Ref<T = any> = {
@@ -44,7 +44,7 @@ declare namespace Model {
44
44
  * Property initializer, will run upon instance creation.
45
45
  * Optional returned callback will run when once upon first access.
46
46
  */
47
- type Instruction<T = any, M extends Model = any> = (this: M, key: Model.Key<M>, thisArg: M, state: Model.Export<M>) => Instruction.Descriptor<T> | Instruction.Getter<T> | void;
47
+ type Instruction<T = any, M extends Model = any> = (this: M, key: Model.Field<M> & string, thisArg: M, state: Model.State<M>) => Instruction.Descriptor<T> | Instruction.Getter<T> | void;
48
48
  }
49
49
  interface Model {
50
50
  /**
@@ -55,40 +55,44 @@ interface Model {
55
55
  }
56
56
  declare class Model {
57
57
  constructor(id?: string | number);
58
- /** Pull current values from state. Flattens all models and exotic values amongst properties. */
59
- get(): Model.Export<this>;
58
+ /** Pull current values from state. Flattens all models and exotic values recursively. */
59
+ get(): Model.State<this>;
60
60
  /** Run a function which will run automatically when accessed values change. */
61
61
  get(effect: Model.Effect<this>): () => void;
62
- get<T extends string>(key: T, required?: boolean): Model.ValueOf<this, T>;
63
- get<T extends string>(key: T, callback: Model.ValueCallback<this, T>): () => void;
62
+ /** Check if model is expired. */
63
+ get(event: null): boolean;
64
+ /** Callback when model is to be destroyed. */
65
+ get(event: null, callback: () => void): () => void;
66
+ get<T extends Model.Key<this>>(key: T, required?: boolean): Model.Value<this, T>;
67
+ get<T extends Model.Key<this>>(key: T, callback: Model.OnUpdate<this, T>): () => void;
64
68
  /**
65
69
  * Get update in progress.
66
70
  *
67
- * @returns a promise which resolves object with updated values. Is `undefined` if no update.
71
+ * @returns Promise which resolves object with updated values. Is `undefined` if there is no update.
68
72
  **/
69
73
  set(): Promise<Model.Values<this>> | undefined;
70
74
  /**
71
- * Call a function whenever an update occurs.
75
+ * Call a function when update occurs.
72
76
  *
73
- * @param callback
74
- * Note: This will be called for every assignment which changes the value.
75
- * To run logic on final value only, this callback may return a function.
76
- * Returning the same function for one or more will ensure it is only called once.
77
+ * Given function is called for every assignment (which changes value) or explicit `set`.
78
+ * To run logic on final value only, callback itself may return a function.
79
+ * Returning the same function for one or more events will ensure a side-effect is called only when settled.
77
80
  *
78
- * @returns a function to remove listener. Will return `true` if removed, `false` if inactive already.
81
+ * @param callback - Function to call when update occurs.
82
+ * @returns Function to remove listener. Will return `true` if removed, `false` if inactive already.
79
83
  */
80
84
  set(callback: Model.Event<this>): () => boolean;
81
85
  /**
82
- * Push an update without changing the value of associated property.
83
- *
84
- * This is useful where a property value internally has changed, but the object remains the same.
85
- * For example: An array which has pushed a new value, or a change to nested property.
86
+ * Declare an end to updates. This event is final and will freeze state.
86
87
  */
87
- set(key: string | symbol): void;
88
+ set(event: null): void;
88
89
  /**
89
- * Declare an end to updates. This event will freeze state.
90
+ * Push an update. This will not change the value of associated property.
91
+ *
92
+ * Useful where a property value internally has changed, but the object is the same.
93
+ * For example: An array has pushed a new value, or a nested property is updated.
90
94
  */
91
- set(key: null): void;
95
+ set(key: Model.Key<this>): void;
92
96
  /**
93
97
  * Update a property with value.
94
98
  *
@@ -96,13 +100,13 @@ declare class Model {
96
100
  * @param value - value to update property with (if the same as current, no update will occur)
97
101
  * @param silent - if true, will not notify listeners of an update
98
102
  */
99
- set<K extends string>(key: K, value?: Model.ValueOf<this, K>, silent?: boolean): void;
103
+ set<K extends string>(key: K, value?: Model.Value<this, K>, silent?: boolean): void;
100
104
  /** Iterate over managed properties in this instance of Model. */
101
105
  [Symbol.iterator](): Iterator<[string, unknown]>;
102
106
  /**
103
107
  * Creates a new instance of this controller.
104
108
  *
105
- * Beyond `new this(...)`, method will activate managed-state.
109
+ * **Important** - Use instead of `new this(...)` as this method also activates state.
106
110
  *
107
111
  * @param args - arguments sent to constructor
108
112
  */
@@ -172,10 +176,10 @@ declare namespace ref {
172
176
  }
173
177
  /** Object with references to all managed values of `T`. */
174
178
  type Proxy<T extends Model> = {
175
- [P in Model.Key<T>]-?: Model.Ref<T[P]>;
179
+ [P in Model.Field<T>]-?: Model.Ref<T[P]>;
176
180
  };
177
181
  type CustomProxy<T extends Model, R> = {
178
- [P in Model.Key<T>]-?: R;
182
+ [P in Model.Field<T>]-?: R;
179
183
  };
180
184
  }
181
185
  /**
@@ -195,7 +199,7 @@ declare function ref<T extends Model>(target: T): ref.Proxy<T>;
195
199
  * @param target - Source model from which to reference values.
196
200
  * @param mapper - Function producing the placeholder value for any given property.
197
201
  */
198
- declare function ref<O extends Model, R>(target: O, mapper: (key: Model.Key<O>) => R): ref.CustomProxy<O, R>;
202
+ declare function ref<O extends Model, R>(target: O, mapper: (key: Model.Field<O>) => R): ref.CustomProxy<O, R>;
199
203
  /**
200
204
  * Creates a ref-compatible property.
201
205
  * Will persist value, and updates to this are made part of controller event-stream.
package/dist/index.js CHANGED
@@ -38,13 +38,14 @@ var LISTENER = /* @__PURE__ */ new WeakMap();
38
38
  function onReady() {
39
39
  return null;
40
40
  }
41
- function addListener(to, fn) {
41
+ function addListener(to, fn, select) {
42
42
  let subs = LISTENER.get(to);
43
43
  if (!subs)
44
- LISTENER.set(to, subs = (/* @__PURE__ */ new Map()).set(onReady, void 0));
45
- if (!subs.has(onReady))
44
+ LISTENER.set(to, subs = (/* @__PURE__ */ new Map()).set(onReady, false));
45
+ const filter = 2 in arguments && /* @__PURE__ */ new Set([select]);
46
+ if (!filter && !subs.has(onReady))
46
47
  fn(true, to);
47
- subs.set(fn, void 0);
48
+ subs.set(fn, filter);
48
49
  return () => subs.delete(fn);
49
50
  }
50
51
  function watch(from, key, value) {
@@ -148,11 +149,7 @@ var Model = class _Model {
148
149
  const state = {};
149
150
  define(this, "is", { value: this });
150
151
  STATE.set(this, state);
151
- ID.set(this, `${Type}-${id ? String(id) : uid()}`);
152
- function onNull(key) {
153
- if (key === null)
154
- Object.freeze(state);
155
- }
152
+ ID.set(this, id ? String(id) : `${Type}-${uid()}`);
156
153
  while (true) {
157
154
  new Set(NOTIFY.get(Type)).forEach((x) => addListener(this, x));
158
155
  if (Type === _Model)
@@ -173,38 +170,49 @@ var Model = class _Model {
173
170
  });
174
171
  }
175
172
  }
176
- addListener(this, onNull);
173
+ addListener(this, () => {
174
+ for (const [_, value] of this)
175
+ if (value instanceof _Model && PARENT.get(value) === this)
176
+ value.set(null);
177
+ Object.freeze(state);
178
+ }, null);
177
179
  return null;
178
180
  });
179
181
  }
180
182
  get(arg1, arg2) {
181
183
  const self = this.is;
182
- if (typeof arg1 == "string")
183
- if (typeof arg2 == "function") {
184
- const state = STATE.get(self);
185
- if (arg1 in state)
186
- arg2.call(this, state[arg1], arg1, this);
187
- return addListener(self, (key) => {
188
- if (key === arg1)
189
- arg2.call(this, arg1 in state ? state[key] : key, key, this);
190
- });
191
- } else
192
- return fetch(self, arg1, arg2);
193
- if (arg1)
184
+ if (typeof arg1 == "function")
194
185
  return effect(self, arg1);
195
- const cache = /* @__PURE__ */ new WeakMap();
196
- function get2(value) {
197
- if (value instanceof _Model) {
198
- if (cache.has(value))
199
- return cache.get(value);
200
- const model = value;
201
- cache.set(value, value = {});
202
- for (const [key, val] of model)
203
- value[key] = get2(val);
204
- }
205
- return value;
186
+ if (arg1 === void 0) {
187
+ let get3 = function(value) {
188
+ if (value instanceof _Model) {
189
+ if (cache.has(value))
190
+ return cache.get(value);
191
+ const model = value;
192
+ cache.set(value, value = {});
193
+ for (const [key, val] of model)
194
+ value[key] = get3(val);
195
+ }
196
+ return value;
197
+ };
198
+ var get2 = get3;
199
+ const cache = /* @__PURE__ */ new WeakMap();
200
+ return get3(self);
206
201
  }
207
- return get2(self);
202
+ if (arg1 === null)
203
+ if (typeof arg2 == "function")
204
+ return addListener(self, arg2.bind(this, this), null);
205
+ else
206
+ return Object.isFrozen(STATE.get(self));
207
+ if (typeof arg2 == "function") {
208
+ const state = STATE.get(self);
209
+ if (arg1 in state)
210
+ arg2.call(this, state[arg1], arg1, this);
211
+ return addListener(self, () => {
212
+ arg2.call(this, arg1 in state ? state[arg1] : arg1, arg1, this);
213
+ }, arg1);
214
+ } else
215
+ return fetch(self, arg1, arg2);
208
216
  }
209
217
  set(arg1, arg2, arg3) {
210
218
  const self = this.is;
@@ -233,7 +241,7 @@ var Model = class _Model {
233
241
  /**
234
242
  * Creates a new instance of this controller.
235
243
  *
236
- * Beyond `new this(...)`, method will activate managed-state.
244
+ * **Important** - Use instead of `new this(...)` as this method also activates state.
237
245
  *
238
246
  * @param args - arguments sent to constructor
239
247
  */
@@ -662,6 +670,8 @@ function attempt(fn) {
662
670
 
663
671
  // src/instruction/use.ts
664
672
  function use(value, argument) {
673
+ if (typeof value === "function")
674
+ value = new value();
665
675
  return add((property, subject) => {
666
676
  function set2(next) {
667
677
  if (value instanceof Model && !(next instanceof value.constructor))
@@ -687,8 +697,6 @@ function use(value, argument) {
687
697
  if (typeof argument == "function")
688
698
  argument(next);
689
699
  }
690
- if (typeof value === "function")
691
- value = new value();
692
700
  const output = { set: set2, value };
693
701
  set2(value);
694
702
  return output;
package/dist/index.mjs CHANGED
@@ -5,13 +5,14 @@ var LISTENER = /* @__PURE__ */ new WeakMap();
5
5
  function onReady() {
6
6
  return null;
7
7
  }
8
- function addListener(to, fn) {
8
+ function addListener(to, fn, select) {
9
9
  let subs = LISTENER.get(to);
10
10
  if (!subs)
11
- LISTENER.set(to, subs = (/* @__PURE__ */ new Map()).set(onReady, void 0));
12
- if (!subs.has(onReady))
11
+ LISTENER.set(to, subs = (/* @__PURE__ */ new Map()).set(onReady, false));
12
+ const filter = 2 in arguments && /* @__PURE__ */ new Set([select]);
13
+ if (!filter && !subs.has(onReady))
13
14
  fn(true, to);
14
- subs.set(fn, void 0);
15
+ subs.set(fn, filter);
15
16
  return () => subs.delete(fn);
16
17
  }
17
18
  function watch(from, key, value) {
@@ -115,11 +116,7 @@ var Model = class _Model {
115
116
  const state = {};
116
117
  define(this, "is", { value: this });
117
118
  STATE.set(this, state);
118
- ID.set(this, `${Type}-${id ? String(id) : uid()}`);
119
- function onNull(key) {
120
- if (key === null)
121
- Object.freeze(state);
122
- }
119
+ ID.set(this, id ? String(id) : `${Type}-${uid()}`);
123
120
  while (true) {
124
121
  new Set(NOTIFY.get(Type)).forEach((x) => addListener(this, x));
125
122
  if (Type === _Model)
@@ -140,38 +137,49 @@ var Model = class _Model {
140
137
  });
141
138
  }
142
139
  }
143
- addListener(this, onNull);
140
+ addListener(this, () => {
141
+ for (const [_, value] of this)
142
+ if (value instanceof _Model && PARENT.get(value) === this)
143
+ value.set(null);
144
+ Object.freeze(state);
145
+ }, null);
144
146
  return null;
145
147
  });
146
148
  }
147
149
  get(arg1, arg2) {
148
150
  const self = this.is;
149
- if (typeof arg1 == "string")
150
- if (typeof arg2 == "function") {
151
- const state = STATE.get(self);
152
- if (arg1 in state)
153
- arg2.call(this, state[arg1], arg1, this);
154
- return addListener(self, (key) => {
155
- if (key === arg1)
156
- arg2.call(this, arg1 in state ? state[key] : key, key, this);
157
- });
158
- } else
159
- return fetch(self, arg1, arg2);
160
- if (arg1)
151
+ if (typeof arg1 == "function")
161
152
  return effect(self, arg1);
162
- const cache = /* @__PURE__ */ new WeakMap();
163
- function get2(value) {
164
- if (value instanceof _Model) {
165
- if (cache.has(value))
166
- return cache.get(value);
167
- const model = value;
168
- cache.set(value, value = {});
169
- for (const [key, val] of model)
170
- value[key] = get2(val);
171
- }
172
- return value;
153
+ if (arg1 === void 0) {
154
+ let get3 = function(value) {
155
+ if (value instanceof _Model) {
156
+ if (cache.has(value))
157
+ return cache.get(value);
158
+ const model = value;
159
+ cache.set(value, value = {});
160
+ for (const [key, val] of model)
161
+ value[key] = get3(val);
162
+ }
163
+ return value;
164
+ };
165
+ var get2 = get3;
166
+ const cache = /* @__PURE__ */ new WeakMap();
167
+ return get3(self);
173
168
  }
174
- return get2(self);
169
+ if (arg1 === null)
170
+ if (typeof arg2 == "function")
171
+ return addListener(self, arg2.bind(this, this), null);
172
+ else
173
+ return Object.isFrozen(STATE.get(self));
174
+ if (typeof arg2 == "function") {
175
+ const state = STATE.get(self);
176
+ if (arg1 in state)
177
+ arg2.call(this, state[arg1], arg1, this);
178
+ return addListener(self, () => {
179
+ arg2.call(this, arg1 in state ? state[arg1] : arg1, arg1, this);
180
+ }, arg1);
181
+ } else
182
+ return fetch(self, arg1, arg2);
175
183
  }
176
184
  set(arg1, arg2, arg3) {
177
185
  const self = this.is;
@@ -200,7 +208,7 @@ var Model = class _Model {
200
208
  /**
201
209
  * Creates a new instance of this controller.
202
210
  *
203
- * Beyond `new this(...)`, method will activate managed-state.
211
+ * **Important** - Use instead of `new this(...)` as this method also activates state.
204
212
  *
205
213
  * @param args - arguments sent to constructor
206
214
  */
@@ -629,6 +637,8 @@ function attempt(fn) {
629
637
 
630
638
  // src/instruction/use.ts
631
639
  function use(value, argument) {
640
+ if (typeof value === "function")
641
+ value = new value();
632
642
  return add((property, subject) => {
633
643
  function set2(next) {
634
644
  if (value instanceof Model && !(next instanceof value.constructor))
@@ -654,8 +664,6 @@ function use(value, argument) {
654
664
  if (typeof argument == "function")
655
665
  argument(next);
656
666
  }
657
- if (typeof value === "function")
658
- value = new value();
659
667
  const output = { set: set2, value };
660
668
  set2(value);
661
669
  return output;
package/package.json CHANGED
@@ -28,6 +28,6 @@
28
28
  "prebuild": "rm -rf dist",
29
29
  "preversion": "npm run test && npm run build"
30
30
  },
31
- "version": "0.35.2",
32
- "gitHead": "3ea5cde2819a66601be642a9befd8e4477caa0b8"
31
+ "version": "0.35.5-alpha.0",
32
+ "gitHead": "9626c3d85bd2fa7a8e5368c845ff8d889843be0b"
33
33
  }