@expressive/mvc 0.41.0 → 0.43.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
@@ -2,19 +2,30 @@ declare namespace Model {
2
2
  /** Any type of Model, using own class constructor as its identifier. */
3
3
  type Type<T extends Model = Model> = abstract new (...args: any[]) => T;
4
4
  /** A type of Model which may be created without constructor arguments. */
5
- type New<T extends Model = Model> = (new () => T) & typeof Model;
5
+ type New<T extends Model = Model> = (new (arg?: Argument) => T) & typeof Model;
6
+ /**
7
+ * Model constructor callback - is called when Model finishes intializing.
8
+ * Returned function will call when model is destroyed.
9
+ */
10
+ type Callback<T extends Model = Model> = (this: T, thisArg: T) => (() => void) | Assign<T> | void;
11
+ /** Model constructor argument */
12
+ type Argument<T extends Model = Model> = string | Callback<T> | Assign<T> | undefined;
6
13
  /** Subset of `keyof T` which are not methods or defined by base Model U. **/
7
14
  type Field<T> = Exclude<keyof T, keyof Model>;
8
15
  /** Any valid key for controller, including but not limited to Field<T>. */
9
- type Key<T> = Field<T> | (string & {}) | number | symbol;
16
+ type Event<T> = Field<T> | (string & {}) | number | symbol;
17
+ /** Object overlay which may be used to override values and methods while creating model. */
18
+ type Assign<T> = {
19
+ [K in Exclude<keyof T, keyof Model>]?: T[K] extends (...args: any[]) => infer R ? (this: T, ...args: Parameters<T[K]>) => ReturnType<T[K]> : T[K];
20
+ } & Record<string, unknown>;
10
21
  /** Export/Import compatible value for a given property in a Model. */
11
22
  type Export<R> = R extends {
12
23
  get(): infer T;
13
24
  } ? T : R;
14
25
  /** Value for a property managed by a controller. */
15
- type Value<T extends Model, K extends Key<T>> = K extends keyof T ? Export<T[K]> : unknown;
26
+ type Value<T extends Model, K extends Event<T>> = K extends keyof T ? Export<T[K]> : unknown;
16
27
  type OnEvent<T extends Model> = (this: T, key: unknown, source: T) => (() => void) | void | null;
17
- type OnUpdate<T extends Model, K extends Key<T>> = (this: T, value: Value<T, K>, key: K, thisArg: K) => void;
28
+ type OnUpdate<T extends Model, K extends Event<T>> = (this: T, value: Value<T, K>, key: K, thisArg: K) => void;
18
29
  /**
19
30
  * Values from current state of given controller.
20
31
  * Differs from `Values` as values here will drill
@@ -42,7 +53,7 @@ declare namespace Model {
42
53
  *
43
54
  * @returns A callback function which will be called when this effect is stale.
44
55
  */
45
- type Effect<T> = (this: T, current: T, update: Set<Model.Key<T>>) => EffectCallback | Promise<void> | null | void;
56
+ type Effect<T> = (this: T, current: T, update: Set<Event<T>>) => EffectCallback | Promise<void> | null | void;
46
57
  /**
47
58
  * A callback function returned by effect. Will be called when effect is stale.
48
59
  *
@@ -63,14 +74,7 @@ declare namespace Model {
63
74
  * Property initializer, will run upon instance creation.
64
75
  * Optional returned callback will run when once upon first access.
65
76
  */
66
- 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;
67
- /**
68
- * Model constructor callback - is called when Model finishes intializing.
69
- * Returned function will call when model is destroyed.
70
- */
71
- type Callback = (() => void | (() => void));
72
- /** Model constructor argument */
73
- type Argument = string | Callback;
77
+ type Instruction<T = any, M extends Model = any> = (this: M, key: Field<M> & string, thisArg: M, state: State<M>) => Instruction.Descriptor<T> | Instruction.Getter<T> | void;
74
78
  }
75
79
  interface Model {
76
80
  /**
@@ -99,14 +103,14 @@ declare class Model {
99
103
  * @param key - Property to get value of.
100
104
  * @param required - If true, will throw an error if property is not available.
101
105
  */
102
- get<T extends Model.Key<this>>(key: T, required?: boolean): Model.Value<this, T>;
106
+ get<T extends Model.Event<this>>(key: T, required?: boolean): Model.Value<this, T>;
103
107
  /**
104
108
  * Run a function when a property is updated.
105
109
  *
106
110
  * @param key - Property to watch for updates.
107
111
  * @param callback - Function to call when property is updated.
108
112
  */
109
- get<T extends Model.Key<this>>(key: T, callback: Model.OnUpdate<this, T>): () => void;
113
+ get<T extends Model.Event<this>>(key: T, callback: Model.OnUpdate<this, T>): () => void;
110
114
  /**
111
115
  * Check if model is expired.
112
116
  *
@@ -125,7 +129,7 @@ declare class Model {
125
129
  *
126
130
  * @returns Promise which resolves object with updated values. Is `undefined` if there is no update.
127
131
  **/
128
- set(): PromiseLike<Model.Key<this>[]> | undefined;
132
+ set(): PromiseLike<Model.Event<this>[]> | undefined;
129
133
  /**
130
134
  * Call a function when update occurs.
131
135
  *
@@ -158,7 +162,12 @@ declare class Model {
158
162
  * @param key - Property or event to dispatch.
159
163
  * @returns Promise which resolves an array of keys which were updated.
160
164
  */
161
- set(key: Model.Key<this>): PromiseLike<Model.Key<this>[]>;
165
+ set(key: Model.Event<this>): PromiseLike<Model.Event<this>[]>;
166
+ /**
167
+ * Update mulitple properties at once. Merges argument with current state.
168
+ * Properties which are not managed by this controller will be ignored.
169
+ */
170
+ set(assign: Model.Assign<this>): PromiseLike<Model.Event<this>[]> | undefined;
162
171
  /**
163
172
  * Update a property with value.
164
173
  *
@@ -166,7 +175,7 @@ declare class Model {
166
175
  * @param value - value to update property with (if the same as current, no update will occur)
167
176
  * @param silent - if true, will not notify listeners of an update
168
177
  */
169
- set<K extends string>(key: K, value?: Model.Value<this, K>, silent?: boolean): PromiseLike<Model.Key<this>[]> | undefined;
178
+ set<K extends string>(key: K, value?: Model.Value<this, K>, silent?: boolean): PromiseLike<Model.Event<this>[]> | undefined;
170
179
  /** Iterate over managed properties in this instance of Model. */
171
180
  [Symbol.iterator](): Iterator<[string, unknown]>;
172
181
  /**
@@ -176,7 +185,7 @@ declare class Model {
176
185
  *
177
186
  * @param args - arguments sent to constructor
178
187
  */
179
- static new<T extends typeof Model>(this: T, ...args: ConstructorParameters<T>): InstanceType<T>;
188
+ static new<T extends Model>(this: Model.New<T>, arg?: Model.Argument<T>): T;
180
189
  /**
181
190
  * Static equivalent of `x instanceof this`.
182
191
  * Determines if provided class is a subtype of this one.
package/dist/index.js CHANGED
@@ -149,7 +149,7 @@ function effect(target, callback) {
149
149
 
150
150
  // src/model.ts
151
151
  var FLATTEN;
152
- var REGISTER = /* @__PURE__ */ new WeakMap();
152
+ var NAMES = /* @__PURE__ */ new WeakMap();
153
153
  var STATE = /* @__PURE__ */ new WeakMap();
154
154
  var NOTIFY = /* @__PURE__ */ new WeakMap();
155
155
  var PENDING2 = /* @__PURE__ */ new WeakMap();
@@ -160,33 +160,20 @@ var Model = class _Model {
160
160
  constructor(arg) {
161
161
  const state = {};
162
162
  let Type = this.constructor;
163
+ bind(Type);
163
164
  define(this, "is", { value: this });
164
165
  STATE.set(this, state);
165
- REGISTER.set(this, typeof arg == "string" ? arg : `${Type}-${uid()}`);
166
+ NAMES.set(this, typeof arg == "string" ? arg : `${Type}-${uid()}`);
166
167
  while (true) {
167
168
  new Set(NOTIFY.get(Type)).forEach((x) => addListener(this, x));
168
169
  if (Type === _Model)
169
170
  break;
170
- const desc = Object.getOwnPropertyDescriptors(Type.prototype);
171
- for (const key in desc) {
172
- let { value } = desc[key];
173
- if (typeof value == "function" && key !== "constructor")
174
- Object.defineProperty(Type.prototype, key, {
175
- get() {
176
- const method = value.bind(this.is);
177
- METHOD.set(method, value);
178
- define(this.is, key, {
179
- value: method,
180
- configurable: false
181
- });
182
- return method;
183
- }
184
- });
185
- }
186
171
  Type = Object.getPrototypeOf(Type);
187
172
  }
188
173
  addListener(this, () => {
189
- let onDone;
174
+ const apply = typeof arg == "function" ? arg.call(this, this) : arg;
175
+ if (typeof apply == "object")
176
+ assign(this, apply);
190
177
  for (const key in this) {
191
178
  const desc = Object.getOwnPropertyDescriptor(this, key);
192
179
  if ("value" in desc) {
@@ -200,14 +187,12 @@ var Model = class _Model {
200
187
  });
201
188
  }
202
189
  }
203
- if (typeof arg == "function")
204
- onDone = arg();
205
190
  addListener(this, () => {
206
191
  for (const [_, value] of this)
207
192
  if (value instanceof _Model && PARENT.get(value) === this)
208
193
  value.set(null);
209
- if (typeof onDone == "function")
210
- onDone();
194
+ if (typeof apply == "function")
195
+ apply();
211
196
  Object.freeze(state);
212
197
  }, null);
213
198
  return null;
@@ -261,7 +246,9 @@ var Model = class _Model {
261
246
  });
262
247
  if (arg1 === null)
263
248
  return event(this, null);
264
- if (arg1 !== void 0)
249
+ if (typeof arg1 == "object")
250
+ assign(self, arg1);
251
+ else if (arg1 !== void 0)
265
252
  if (1 in arguments)
266
253
  update(self, arg1, arg2, arg3);
267
254
  else
@@ -289,8 +276,8 @@ var Model = class _Model {
289
276
  *
290
277
  * @param args - arguments sent to constructor
291
278
  */
292
- static new(...args) {
293
- const instance = new this(...args);
279
+ static new(arg) {
280
+ const instance = new this(arg);
294
281
  event(instance, true);
295
282
  return instance;
296
283
  }
@@ -319,7 +306,7 @@ var Model = class _Model {
319
306
  };
320
307
  define(Model.prototype, "toString", {
321
308
  value() {
322
- return REGISTER.get(this.is);
309
+ return NAMES.get(this.is);
323
310
  }
324
311
  });
325
312
  define(Model, "toString", {
@@ -377,6 +364,40 @@ function push(subject, key, silent) {
377
364
  if (!silent)
378
365
  event(subject, key);
379
366
  }
367
+ var METHODS = new WeakMap([[Model, []]]);
368
+ function assign(to, values) {
369
+ const methods = METHODS.get(to.constructor);
370
+ const applicable = new Set(Object.keys(to).concat(methods));
371
+ for (const key in values)
372
+ if (applicable.has(key))
373
+ to[key] = values[key];
374
+ }
375
+ function bind(type) {
376
+ let keys = METHODS.get(type);
377
+ if (!keys) {
378
+ METHODS.set(type, keys = bind2(Object.getPrototypeOf(type)).slice());
379
+ const desc = Object.getOwnPropertyDescriptors(type.prototype);
380
+ for (const key in desc)
381
+ if (key != "constructor" && "value" in desc[key]) {
382
+ let bind3 = function(method) {
383
+ const value2 = method.bind(this.is);
384
+ METHOD.set(value2, method);
385
+ define(this.is, key, { value: value2, writable: true });
386
+ return value2;
387
+ };
388
+ var bind2 = bind3;
389
+ let { value } = desc[key];
390
+ keys.push(key);
391
+ Object.defineProperty(type.prototype, key, {
392
+ set: bind3,
393
+ get() {
394
+ return this.hasOwnProperty(key) ? value : bind3.call(this, value);
395
+ }
396
+ });
397
+ }
398
+ }
399
+ return keys;
400
+ }
380
401
  function uid() {
381
402
  return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
382
403
  }
@@ -802,12 +823,12 @@ function has(type, argument) {
802
823
  return add((key, subject, state) => {
803
824
  if (typeof argument == "boolean") {
804
825
  Context.request(type, subject, (model) => {
826
+ subject.set(key, model);
805
827
  const remove = () => {
806
828
  drop();
807
829
  if (state[key] === model)
808
830
  delete state[key];
809
831
  };
810
- subject.set(key, model);
811
832
  const drop = model.get(null, remove);
812
833
  return remove;
813
834
  });
package/dist/index.mjs CHANGED
@@ -115,7 +115,7 @@ function effect(target, callback) {
115
115
 
116
116
  // src/model.ts
117
117
  var FLATTEN;
118
- var REGISTER = /* @__PURE__ */ new WeakMap();
118
+ var NAMES = /* @__PURE__ */ new WeakMap();
119
119
  var STATE = /* @__PURE__ */ new WeakMap();
120
120
  var NOTIFY = /* @__PURE__ */ new WeakMap();
121
121
  var PENDING2 = /* @__PURE__ */ new WeakMap();
@@ -126,33 +126,20 @@ var Model = class _Model {
126
126
  constructor(arg) {
127
127
  const state = {};
128
128
  let Type = this.constructor;
129
+ bind(Type);
129
130
  define(this, "is", { value: this });
130
131
  STATE.set(this, state);
131
- REGISTER.set(this, typeof arg == "string" ? arg : `${Type}-${uid()}`);
132
+ NAMES.set(this, typeof arg == "string" ? arg : `${Type}-${uid()}`);
132
133
  while (true) {
133
134
  new Set(NOTIFY.get(Type)).forEach((x) => addListener(this, x));
134
135
  if (Type === _Model)
135
136
  break;
136
- const desc = Object.getOwnPropertyDescriptors(Type.prototype);
137
- for (const key in desc) {
138
- let { value } = desc[key];
139
- if (typeof value == "function" && key !== "constructor")
140
- Object.defineProperty(Type.prototype, key, {
141
- get() {
142
- const method = value.bind(this.is);
143
- METHOD.set(method, value);
144
- define(this.is, key, {
145
- value: method,
146
- configurable: false
147
- });
148
- return method;
149
- }
150
- });
151
- }
152
137
  Type = Object.getPrototypeOf(Type);
153
138
  }
154
139
  addListener(this, () => {
155
- let onDone;
140
+ const apply = typeof arg == "function" ? arg.call(this, this) : arg;
141
+ if (typeof apply == "object")
142
+ assign(this, apply);
156
143
  for (const key in this) {
157
144
  const desc = Object.getOwnPropertyDescriptor(this, key);
158
145
  if ("value" in desc) {
@@ -166,14 +153,12 @@ var Model = class _Model {
166
153
  });
167
154
  }
168
155
  }
169
- if (typeof arg == "function")
170
- onDone = arg();
171
156
  addListener(this, () => {
172
157
  for (const [_, value] of this)
173
158
  if (value instanceof _Model && PARENT.get(value) === this)
174
159
  value.set(null);
175
- if (typeof onDone == "function")
176
- onDone();
160
+ if (typeof apply == "function")
161
+ apply();
177
162
  Object.freeze(state);
178
163
  }, null);
179
164
  return null;
@@ -227,7 +212,9 @@ var Model = class _Model {
227
212
  });
228
213
  if (arg1 === null)
229
214
  return event(this, null);
230
- if (arg1 !== void 0)
215
+ if (typeof arg1 == "object")
216
+ assign(self, arg1);
217
+ else if (arg1 !== void 0)
231
218
  if (1 in arguments)
232
219
  update(self, arg1, arg2, arg3);
233
220
  else
@@ -255,8 +242,8 @@ var Model = class _Model {
255
242
  *
256
243
  * @param args - arguments sent to constructor
257
244
  */
258
- static new(...args) {
259
- const instance = new this(...args);
245
+ static new(arg) {
246
+ const instance = new this(arg);
260
247
  event(instance, true);
261
248
  return instance;
262
249
  }
@@ -285,7 +272,7 @@ var Model = class _Model {
285
272
  };
286
273
  define(Model.prototype, "toString", {
287
274
  value() {
288
- return REGISTER.get(this.is);
275
+ return NAMES.get(this.is);
289
276
  }
290
277
  });
291
278
  define(Model, "toString", {
@@ -343,6 +330,40 @@ function push(subject, key, silent) {
343
330
  if (!silent)
344
331
  event(subject, key);
345
332
  }
333
+ var METHODS = new WeakMap([[Model, []]]);
334
+ function assign(to, values) {
335
+ const methods = METHODS.get(to.constructor);
336
+ const applicable = new Set(Object.keys(to).concat(methods));
337
+ for (const key in values)
338
+ if (applicable.has(key))
339
+ to[key] = values[key];
340
+ }
341
+ function bind(type) {
342
+ let keys = METHODS.get(type);
343
+ if (!keys) {
344
+ METHODS.set(type, keys = bind2(Object.getPrototypeOf(type)).slice());
345
+ const desc = Object.getOwnPropertyDescriptors(type.prototype);
346
+ for (const key in desc)
347
+ if (key != "constructor" && "value" in desc[key]) {
348
+ let bind3 = function(method) {
349
+ const value2 = method.bind(this.is);
350
+ METHOD.set(value2, method);
351
+ define(this.is, key, { value: value2, writable: true });
352
+ return value2;
353
+ };
354
+ var bind2 = bind3;
355
+ let { value } = desc[key];
356
+ keys.push(key);
357
+ Object.defineProperty(type.prototype, key, {
358
+ set: bind3,
359
+ get() {
360
+ return this.hasOwnProperty(key) ? value : bind3.call(this, value);
361
+ }
362
+ });
363
+ }
364
+ }
365
+ return keys;
366
+ }
346
367
  function uid() {
347
368
  return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
348
369
  }
@@ -768,12 +789,12 @@ function has(type, argument) {
768
789
  return add((key, subject, state) => {
769
790
  if (typeof argument == "boolean") {
770
791
  Context.request(type, subject, (model) => {
792
+ subject.set(key, model);
771
793
  const remove = () => {
772
794
  drop();
773
795
  if (state[key] === model)
774
796
  delete state[key];
775
797
  };
776
- subject.set(key, model);
777
798
  const drop = model.get(null, remove);
778
799
  return remove;
779
800
  });
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.41.0",
32
- "gitHead": "1c7d28301f4abc428c3bfdea891c34d31fc1e9d0"
31
+ "version": "0.43.0",
32
+ "gitHead": "ec66b5e7a3b7d2c525c2e9503c8cd4fd81e121a6"
33
33
  }