@expressive/mvc 0.35.1 → 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
@@ -1,26 +1,26 @@
1
- type Predicate = (key: string) => boolean | void;
2
1
  declare namespace Model {
3
2
  /** Any type of Model, using own class constructor as its identifier. */
4
3
  type Type<T extends Model = Model> = abstract new (...args: any[]) => T;
5
4
  /** A type of Model which may be created without constructor arguments. */
6
5
  type New<T extends Model = Model> = (new () => T) & typeof Model;
7
6
  /** Subset of `keyof T` which are not methods or defined by base Model U. **/
8
- type Key<T> = Exclude<keyof T, keyof Model> & string;
9
- 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;
10
9
  /** Actual value stored in state. */
11
- type Value<R> = R extends Ref<infer T> ? T : R extends Model ? Export<R> : R;
12
- type ValueOf<T extends Model, K extends Any<T>> = K extends keyof T ? T[K] extends Ref<infer V> ? V : T[K] : unknown;
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> = {
@@ -29,7 +29,7 @@ declare namespace Model {
29
29
  };
30
30
  /** A callback function which is subscribed to parent and updates when values change. */
31
31
  type Effect<T> = (this: T, argument: T) => (() => void) | Promise<void> | null | void;
32
- type Event = (this: Model, key: string, value: unknown) => (() => void) | void;
32
+ type Event<T extends Model> = (this: T, key: unknown, source: T) => (() => void) | void | null;
33
33
  namespace Instruction {
34
34
  type Getter<T> = (source: Model) => T;
35
35
  type Setter<T> = (value: T, previous: T) => boolean | void | (() => T);
@@ -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,48 +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: true): Exclude<Model.ValueOf<this, T>, undefined>;
63
- get<T extends string>(key: T, required?: boolean): Model.ValueOf<this, T>;
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
- * Check if update is in progress.
66
- * Returns a promise which resolves updated keys. If no update, will resolve to `false`.
67
- **/
68
- set(): Promise<Model.Values<this> | false>;
69
- /**
70
- * Expect an update to state within a set time.
69
+ * Get update in progress.
71
70
  *
72
- * @param timeout - milliseconds to wait for update
73
- * @param predicate - optional function to filter for specific updates. If returns true, update is accepted.
74
- *
75
- * @returns a promise to resolve when next accepted update has occured.
76
- */
77
- set(timeout: number, predicate?: Predicate): Promise<Model.Values<this>>;
71
+ * @returns Promise which resolves object with updated values. Is `undefined` if there is no update.
72
+ **/
73
+ set(): Promise<Model.Values<this>> | undefined;
78
74
  /**
79
- * Call a function whenever an update occurs.
75
+ * Call a function when update occurs.
80
76
  *
81
- * @param callback
82
- * Note: This will be called for every assignment which changes the value.
83
- * To run logic on final value only, this callback may return a function.
84
- * Returning the same function for a key 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.
85
80
  *
86
- * @returns a function to remove listener. Returns true if listener isn't removed 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.
87
83
  */
88
- set(callback: Model.Event): () => boolean;
84
+ set(callback: Model.Event<this>): () => boolean;
89
85
  /**
90
- * Push an update without changing the value of associated property.
91
- *
92
- * This is useful where a property value internally has changed, but the object remains the same.
93
- * 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.
94
87
  */
95
- set(key: string): void;
88
+ set(event: null): void;
96
89
  /**
97
- * 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.
98
94
  */
99
- set(key: null): void;
95
+ set(key: Model.Key<this>): void;
100
96
  /**
101
97
  * Update a property with value.
102
98
  *
@@ -104,13 +100,13 @@ declare class Model {
104
100
  * @param value - value to update property with (if the same as current, no update will occur)
105
101
  * @param silent - if true, will not notify listeners of an update
106
102
  */
107
- 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;
108
104
  /** Iterate over managed properties in this instance of Model. */
109
105
  [Symbol.iterator](): Iterator<[string, unknown]>;
110
106
  /**
111
107
  * Creates a new instance of this controller.
112
108
  *
113
- * Beyond `new this(...)`, method will activate managed-state.
109
+ * **Important** - Use instead of `new this(...)` as this method also activates state.
114
110
  *
115
111
  * @param args - arguments sent to constructor
116
112
  */
@@ -122,9 +118,10 @@ declare class Model {
122
118
  * methods and properties of this class.
123
119
  */
124
120
  static is<T extends Model.Type>(this: T, maybe: any): maybe is T;
121
+ static on<T extends Model>(this: Model.Type<T>, event: Model.Event<T>): () => boolean;
125
122
  }
126
123
 
127
- declare function add<T = any, M extends Model = any>(instruction: Model.Instruction): T;
124
+ declare function add<T = any>(instruction: Model.Instruction): T;
128
125
 
129
126
  declare namespace get {
130
127
  type Function<T, S = any> = (this: S, on: S) => T;
@@ -179,10 +176,10 @@ declare namespace ref {
179
176
  }
180
177
  /** Object with references to all managed values of `T`. */
181
178
  type Proxy<T extends Model> = {
182
- [P in Model.Key<T>]-?: Model.Ref<T[P]>;
179
+ [P in Model.Field<T>]-?: Model.Ref<T[P]>;
183
180
  };
184
181
  type CustomProxy<T extends Model, R> = {
185
- [P in Model.Key<T>]-?: R;
182
+ [P in Model.Field<T>]-?: R;
186
183
  };
187
184
  }
188
185
  /**
@@ -202,7 +199,7 @@ declare function ref<T extends Model>(target: T): ref.Proxy<T>;
202
199
  * @param target - Source model from which to reference values.
203
200
  * @param mapper - Function producing the placeholder value for any given property.
204
201
  */
205
- 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>;
206
203
  /**
207
204
  * Creates a ref-compatible property.
208
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, null));
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, null);
48
+ subs.set(fn, filter);
48
49
  return () => subs.delete(fn);
49
50
  }
50
51
  function watch(from, key, value) {
@@ -138,28 +139,28 @@ function effect(target, callback) {
138
139
  // src/model.ts
139
140
  var ID = /* @__PURE__ */ new WeakMap();
140
141
  var PARENT = /* @__PURE__ */ new WeakMap();
141
- var INSTRUCT = /* @__PURE__ */ new Map();
142
- var PENDING = /* @__PURE__ */ new WeakMap();
143
142
  var STATE = /* @__PURE__ */ new WeakMap();
143
+ var NOTIFY = /* @__PURE__ */ new WeakMap();
144
+ var PENDING = /* @__PURE__ */ new WeakMap();
144
145
  var define = Object.defineProperty;
145
146
  var Model = class _Model {
146
147
  constructor(id) {
148
+ let Type = this.constructor;
147
149
  const state = {};
148
150
  define(this, "is", { value: this });
149
- ID.set(this, `${this.constructor}-${id ? String(id) : uid()}`);
150
151
  STATE.set(this, state);
151
- function onNull(key) {
152
- if (key === null)
153
- Object.freeze(state);
152
+ ID.set(this, id ? String(id) : `${Type}-${uid()}`);
153
+ while (true) {
154
+ new Set(NOTIFY.get(Type)).forEach((x) => addListener(this, x));
155
+ if (Type === _Model)
156
+ break;
157
+ Type = Object.getPrototypeOf(Type);
154
158
  }
155
159
  addListener(this, () => {
156
160
  for (const key in this) {
157
- const property = Object.getOwnPropertyDescriptor(this, key);
158
- const instruction = INSTRUCT.get(property.value);
159
- if (instruction)
160
- instruction(this, key, state);
161
- else {
162
- state[key] = property.value;
161
+ const desc = Object.getOwnPropertyDescriptor(this, key);
162
+ if ("value" in desc) {
163
+ state[key] = desc.value;
163
164
  define(this, key, {
164
165
  enumerable: true,
165
166
  set: (x) => update(this, key, x),
@@ -169,56 +170,68 @@ var Model = class _Model {
169
170
  });
170
171
  }
171
172
  }
172
- 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);
173
179
  return null;
174
180
  });
175
181
  }
176
182
  get(arg1, arg2) {
177
183
  const self = this.is;
178
- if (typeof arg1 == "string")
179
- return fetch(self, arg1, arg2);
180
- if (arg1)
184
+ if (typeof arg1 == "function")
181
185
  return effect(self, arg1);
182
- const cache = /* @__PURE__ */ new WeakMap();
183
- function get2(value) {
184
- if (value instanceof _Model) {
185
- if (cache.has(value))
186
- return cache.get(value);
187
- const model = value;
188
- cache.set(value, value = {});
189
- for (const [key, val] of model)
190
- value[key] = get2(val);
191
- }
192
- 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);
193
201
  }
194
- 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);
195
216
  }
196
217
  set(arg1, arg2, arg3) {
197
218
  const self = this.is;
198
219
  if (typeof arg1 == "function")
199
220
  return addListener(self, (key) => {
200
221
  if (typeof key == "string")
201
- return arg1.call(this, key, STATE.get(self)[key]);
222
+ return arg1.call(self, key, self);
202
223
  });
203
- if (typeof arg1 == "string" || arg1 === null)
224
+ if (arg1 !== void 0)
204
225
  return 1 in arguments ? update(self, arg1, arg2, arg3) : update(self, arg1);
205
- return new Promise((resolve, reject) => {
206
- if (typeof arg1 != "number" && !PENDING.has(self)) {
207
- resolve(false);
208
- return;
209
- }
226
+ if (!PENDING.has(self))
227
+ return;
228
+ return new Promise((resolve) => {
210
229
  const remove = addListener(this, (key) => {
211
- if (key === true || typeof arg2 == "function" && typeof key == "string" && arg2(key) !== true)
212
- return;
213
- if (timeout)
214
- clearTimeout(timeout);
215
- remove();
216
- return resolve.bind(null, PENDING.get(self));
230
+ if (key !== true) {
231
+ remove();
232
+ return resolve.bind(null, PENDING.get(self));
233
+ }
217
234
  });
218
- const timeout = typeof arg1 == "number" && setTimeout(() => {
219
- remove();
220
- reject(arg1);
221
- }, arg1);
222
235
  });
223
236
  }
224
237
  /** Iterate over managed properties in this instance of Model. */
@@ -228,7 +241,7 @@ var Model = class _Model {
228
241
  /**
229
242
  * Creates a new instance of this controller.
230
243
  *
231
- * Beyond `new this(...)`, method will activate managed-state.
244
+ * **Important** - Use instead of `new this(...)` as this method also activates state.
232
245
  *
233
246
  * @param args - arguments sent to constructor
234
247
  */
@@ -246,6 +259,13 @@ var Model = class _Model {
246
259
  static is(maybe) {
247
260
  return typeof maybe == "function" && maybe.prototype instanceof this;
248
261
  }
262
+ static on(event2) {
263
+ let notify = NOTIFY.get(this);
264
+ if (!notify)
265
+ NOTIFY.set(this, notify = /* @__PURE__ */ new Set());
266
+ notify.add(event2);
267
+ return () => notify.delete(event2);
268
+ }
249
269
  };
250
270
  define(Model.prototype, "toString", {
251
271
  value() {
@@ -285,15 +305,15 @@ function fetch(subject, property, required) {
285
305
  function update(subject, key, value, silent) {
286
306
  const state = STATE.get(subject);
287
307
  let pending = PENDING.get(subject);
288
- if (typeof key == "string") {
289
- if (2 in arguments) {
290
- const previous = state[key];
291
- if (value === previous)
292
- return true;
293
- state[key] = value;
294
- if (silent === true)
295
- return;
296
- }
308
+ if (typeof key == "string" && 2 in arguments) {
309
+ const previous = state[key];
310
+ if (value === previous)
311
+ return true;
312
+ state[key] = value;
313
+ if (silent === true)
314
+ return;
315
+ }
316
+ if (key !== true && key) {
297
317
  if (!pending) {
298
318
  PENDING.set(subject, pending = {});
299
319
  queue(() => {
@@ -301,7 +321,7 @@ function update(subject, key, value, silent) {
301
321
  PENDING.delete(subject);
302
322
  });
303
323
  }
304
- pending[key] = value;
324
+ pending[key] = key in state ? value : true;
305
325
  }
306
326
  event(subject, key);
307
327
  }
@@ -310,10 +330,20 @@ function uid() {
310
330
  }
311
331
 
312
332
  // src/instruction/add.ts
333
+ var INSTRUCT = /* @__PURE__ */ new Map();
313
334
  function add(instruction) {
314
- const placeholder = Symbol("instruction");
315
- INSTRUCT.set(placeholder, (subject, key, state) => {
316
- INSTRUCT.delete(placeholder);
335
+ const token = Symbol("instruction");
336
+ INSTRUCT.set(token, instruction);
337
+ return token;
338
+ }
339
+ Model.on((_, subject) => {
340
+ const state = STATE.get(subject);
341
+ for (const key in subject) {
342
+ const { value } = Object.getOwnPropertyDescriptor(subject, key);
343
+ const instruction = INSTRUCT.get(value);
344
+ if (!instruction)
345
+ continue;
346
+ INSTRUCT.delete(value);
317
347
  delete subject[key];
318
348
  const output = instruction.call(subject, key, subject, state);
319
349
  if (!output)
@@ -346,9 +376,9 @@ function add(instruction) {
346
376
  );
347
377
  }
348
378
  });
349
- });
350
- return placeholder;
351
- }
379
+ }
380
+ return null;
381
+ });
352
382
 
353
383
  // src/context.ts
354
384
  var Context = class {
@@ -416,8 +446,8 @@ var Context = class {
416
446
  if (value || I !== this[key] && !implicit)
417
447
  Object.defineProperty(this, key, {
418
448
  configurable: true,
419
- value,
420
- writable
449
+ writable,
450
+ value
421
451
  });
422
452
  T = Object.getPrototypeOf(T);
423
453
  } while (T !== Model);
@@ -524,24 +554,23 @@ function ref(arg, arg2) {
524
554
  return add((key, subject, state) => {
525
555
  let value = {};
526
556
  if (arg === subject)
527
- for (const key2 in state)
528
- if (typeof arg2 == "function")
529
- define2(value, key2, {
530
- configurable: true,
531
- get() {
532
- const out = arg2(key2);
533
- define2(value, key2, { value: out });
534
- return out;
535
- }
536
- });
537
- else {
538
- const ref2 = (value2) => subject.set(key2, value2);
539
- define2(ref2, "current", {
540
- get: () => state[key2],
541
- set: ref2
542
- });
543
- define2(value, key2, { value: ref2 });
544
- }
557
+ subject.get(() => {
558
+ for (const key2 in state)
559
+ if (typeof arg2 == "function")
560
+ define2(value, key2, {
561
+ configurable: true,
562
+ get() {
563
+ const out = arg2(key2);
564
+ define2(value, key2, { value: out });
565
+ return out;
566
+ }
567
+ });
568
+ else {
569
+ const set2 = (value2) => subject.set(key2, value2);
570
+ define2(set2, "current", { get: () => state[key2], set: set2 });
571
+ define2(value, key2, { value: set2 });
572
+ }
573
+ });
545
574
  else if (typeof arg == "object")
546
575
  throw new Error("ref instruction does not support object which is not 'this'");
547
576
  else {
@@ -641,6 +670,8 @@ function attempt(fn) {
641
670
 
642
671
  // src/instruction/use.ts
643
672
  function use(value, argument) {
673
+ if (typeof value === "function")
674
+ value = new value();
644
675
  return add((property, subject) => {
645
676
  function set2(next) {
646
677
  if (value instanceof Model && !(next instanceof value.constructor))
@@ -666,8 +697,6 @@ function use(value, argument) {
666
697
  if (typeof argument == "function")
667
698
  argument(next);
668
699
  }
669
- if (typeof value === "function")
670
- value = new value();
671
700
  const output = { set: set2, value };
672
701
  set2(value);
673
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, null));
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, null);
15
+ subs.set(fn, filter);
15
16
  return () => subs.delete(fn);
16
17
  }
17
18
  function watch(from, key, value) {
@@ -105,28 +106,28 @@ function effect(target, callback) {
105
106
  // src/model.ts
106
107
  var ID = /* @__PURE__ */ new WeakMap();
107
108
  var PARENT = /* @__PURE__ */ new WeakMap();
108
- var INSTRUCT = /* @__PURE__ */ new Map();
109
- var PENDING = /* @__PURE__ */ new WeakMap();
110
109
  var STATE = /* @__PURE__ */ new WeakMap();
110
+ var NOTIFY = /* @__PURE__ */ new WeakMap();
111
+ var PENDING = /* @__PURE__ */ new WeakMap();
111
112
  var define = Object.defineProperty;
112
113
  var Model = class _Model {
113
114
  constructor(id) {
115
+ let Type = this.constructor;
114
116
  const state = {};
115
117
  define(this, "is", { value: this });
116
- ID.set(this, `${this.constructor}-${id ? String(id) : uid()}`);
117
118
  STATE.set(this, state);
118
- function onNull(key) {
119
- if (key === null)
120
- Object.freeze(state);
119
+ ID.set(this, id ? String(id) : `${Type}-${uid()}`);
120
+ while (true) {
121
+ new Set(NOTIFY.get(Type)).forEach((x) => addListener(this, x));
122
+ if (Type === _Model)
123
+ break;
124
+ Type = Object.getPrototypeOf(Type);
121
125
  }
122
126
  addListener(this, () => {
123
127
  for (const key in this) {
124
- const property = Object.getOwnPropertyDescriptor(this, key);
125
- const instruction = INSTRUCT.get(property.value);
126
- if (instruction)
127
- instruction(this, key, state);
128
- else {
129
- state[key] = property.value;
128
+ const desc = Object.getOwnPropertyDescriptor(this, key);
129
+ if ("value" in desc) {
130
+ state[key] = desc.value;
130
131
  define(this, key, {
131
132
  enumerable: true,
132
133
  set: (x) => update(this, key, x),
@@ -136,56 +137,68 @@ var Model = class _Model {
136
137
  });
137
138
  }
138
139
  }
139
- 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);
140
146
  return null;
141
147
  });
142
148
  }
143
149
  get(arg1, arg2) {
144
150
  const self = this.is;
145
- if (typeof arg1 == "string")
146
- return fetch(self, arg1, arg2);
147
- if (arg1)
151
+ if (typeof arg1 == "function")
148
152
  return effect(self, arg1);
149
- const cache = /* @__PURE__ */ new WeakMap();
150
- function get2(value) {
151
- if (value instanceof _Model) {
152
- if (cache.has(value))
153
- return cache.get(value);
154
- const model = value;
155
- cache.set(value, value = {});
156
- for (const [key, val] of model)
157
- value[key] = get2(val);
158
- }
159
- 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);
160
168
  }
161
- 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);
162
183
  }
163
184
  set(arg1, arg2, arg3) {
164
185
  const self = this.is;
165
186
  if (typeof arg1 == "function")
166
187
  return addListener(self, (key) => {
167
188
  if (typeof key == "string")
168
- return arg1.call(this, key, STATE.get(self)[key]);
189
+ return arg1.call(self, key, self);
169
190
  });
170
- if (typeof arg1 == "string" || arg1 === null)
191
+ if (arg1 !== void 0)
171
192
  return 1 in arguments ? update(self, arg1, arg2, arg3) : update(self, arg1);
172
- return new Promise((resolve, reject) => {
173
- if (typeof arg1 != "number" && !PENDING.has(self)) {
174
- resolve(false);
175
- return;
176
- }
193
+ if (!PENDING.has(self))
194
+ return;
195
+ return new Promise((resolve) => {
177
196
  const remove = addListener(this, (key) => {
178
- if (key === true || typeof arg2 == "function" && typeof key == "string" && arg2(key) !== true)
179
- return;
180
- if (timeout)
181
- clearTimeout(timeout);
182
- remove();
183
- return resolve.bind(null, PENDING.get(self));
197
+ if (key !== true) {
198
+ remove();
199
+ return resolve.bind(null, PENDING.get(self));
200
+ }
184
201
  });
185
- const timeout = typeof arg1 == "number" && setTimeout(() => {
186
- remove();
187
- reject(arg1);
188
- }, arg1);
189
202
  });
190
203
  }
191
204
  /** Iterate over managed properties in this instance of Model. */
@@ -195,7 +208,7 @@ var Model = class _Model {
195
208
  /**
196
209
  * Creates a new instance of this controller.
197
210
  *
198
- * Beyond `new this(...)`, method will activate managed-state.
211
+ * **Important** - Use instead of `new this(...)` as this method also activates state.
199
212
  *
200
213
  * @param args - arguments sent to constructor
201
214
  */
@@ -213,6 +226,13 @@ var Model = class _Model {
213
226
  static is(maybe) {
214
227
  return typeof maybe == "function" && maybe.prototype instanceof this;
215
228
  }
229
+ static on(event2) {
230
+ let notify = NOTIFY.get(this);
231
+ if (!notify)
232
+ NOTIFY.set(this, notify = /* @__PURE__ */ new Set());
233
+ notify.add(event2);
234
+ return () => notify.delete(event2);
235
+ }
216
236
  };
217
237
  define(Model.prototype, "toString", {
218
238
  value() {
@@ -252,15 +272,15 @@ function fetch(subject, property, required) {
252
272
  function update(subject, key, value, silent) {
253
273
  const state = STATE.get(subject);
254
274
  let pending = PENDING.get(subject);
255
- if (typeof key == "string") {
256
- if (2 in arguments) {
257
- const previous = state[key];
258
- if (value === previous)
259
- return true;
260
- state[key] = value;
261
- if (silent === true)
262
- return;
263
- }
275
+ if (typeof key == "string" && 2 in arguments) {
276
+ const previous = state[key];
277
+ if (value === previous)
278
+ return true;
279
+ state[key] = value;
280
+ if (silent === true)
281
+ return;
282
+ }
283
+ if (key !== true && key) {
264
284
  if (!pending) {
265
285
  PENDING.set(subject, pending = {});
266
286
  queue(() => {
@@ -268,7 +288,7 @@ function update(subject, key, value, silent) {
268
288
  PENDING.delete(subject);
269
289
  });
270
290
  }
271
- pending[key] = value;
291
+ pending[key] = key in state ? value : true;
272
292
  }
273
293
  event(subject, key);
274
294
  }
@@ -277,10 +297,20 @@ function uid() {
277
297
  }
278
298
 
279
299
  // src/instruction/add.ts
300
+ var INSTRUCT = /* @__PURE__ */ new Map();
280
301
  function add(instruction) {
281
- const placeholder = Symbol("instruction");
282
- INSTRUCT.set(placeholder, (subject, key, state) => {
283
- INSTRUCT.delete(placeholder);
302
+ const token = Symbol("instruction");
303
+ INSTRUCT.set(token, instruction);
304
+ return token;
305
+ }
306
+ Model.on((_, subject) => {
307
+ const state = STATE.get(subject);
308
+ for (const key in subject) {
309
+ const { value } = Object.getOwnPropertyDescriptor(subject, key);
310
+ const instruction = INSTRUCT.get(value);
311
+ if (!instruction)
312
+ continue;
313
+ INSTRUCT.delete(value);
284
314
  delete subject[key];
285
315
  const output = instruction.call(subject, key, subject, state);
286
316
  if (!output)
@@ -313,9 +343,9 @@ function add(instruction) {
313
343
  );
314
344
  }
315
345
  });
316
- });
317
- return placeholder;
318
- }
346
+ }
347
+ return null;
348
+ });
319
349
 
320
350
  // src/context.ts
321
351
  var Context = class {
@@ -383,8 +413,8 @@ var Context = class {
383
413
  if (value || I !== this[key] && !implicit)
384
414
  Object.defineProperty(this, key, {
385
415
  configurable: true,
386
- value,
387
- writable
416
+ writable,
417
+ value
388
418
  });
389
419
  T = Object.getPrototypeOf(T);
390
420
  } while (T !== Model);
@@ -491,24 +521,23 @@ function ref(arg, arg2) {
491
521
  return add((key, subject, state) => {
492
522
  let value = {};
493
523
  if (arg === subject)
494
- for (const key2 in state)
495
- if (typeof arg2 == "function")
496
- define2(value, key2, {
497
- configurable: true,
498
- get() {
499
- const out = arg2(key2);
500
- define2(value, key2, { value: out });
501
- return out;
502
- }
503
- });
504
- else {
505
- const ref2 = (value2) => subject.set(key2, value2);
506
- define2(ref2, "current", {
507
- get: () => state[key2],
508
- set: ref2
509
- });
510
- define2(value, key2, { value: ref2 });
511
- }
524
+ subject.get(() => {
525
+ for (const key2 in state)
526
+ if (typeof arg2 == "function")
527
+ define2(value, key2, {
528
+ configurable: true,
529
+ get() {
530
+ const out = arg2(key2);
531
+ define2(value, key2, { value: out });
532
+ return out;
533
+ }
534
+ });
535
+ else {
536
+ const set2 = (value2) => subject.set(key2, value2);
537
+ define2(set2, "current", { get: () => state[key2], set: set2 });
538
+ define2(value, key2, { value: set2 });
539
+ }
540
+ });
512
541
  else if (typeof arg == "object")
513
542
  throw new Error("ref instruction does not support object which is not 'this'");
514
543
  else {
@@ -608,6 +637,8 @@ function attempt(fn) {
608
637
 
609
638
  // src/instruction/use.ts
610
639
  function use(value, argument) {
640
+ if (typeof value === "function")
641
+ value = new value();
611
642
  return add((property, subject) => {
612
643
  function set2(next) {
613
644
  if (value instanceof Model && !(next instanceof value.constructor))
@@ -633,8 +664,6 @@ function use(value, argument) {
633
664
  if (typeof argument == "function")
634
665
  argument(next);
635
666
  }
636
- if (typeof value === "function")
637
- value = new value();
638
667
  const output = { set: set2, value };
639
668
  set2(value);
640
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.1",
32
- "gitHead": "cca282d36102e4691e3d24a07f95e7652b27f5c3"
31
+ "version": "0.35.5-alpha.0",
32
+ "gitHead": "9626c3d85bd2fa7a8e5368c845ff8d889843be0b"
33
33
  }