@expressive/mvc 0.8.0 → 0.8.1

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
@@ -11,14 +11,19 @@ declare type Listener = {
11
11
  declare class Subscriber<T extends {} = any> {
12
12
  parent: Control<T>;
13
13
  onUpdate: Control.OnEvent;
14
+ static get<T extends {}>(from: T): Model.Subscriber<T> | undefined;
14
15
  proxy: T;
15
- commit: () => Callback;
16
16
  release: Callback;
17
+ latest?: Model.Event<T>[];
17
18
  active: boolean;
18
19
  dependant: Set<Listener>;
19
- add: (key: any, value?: boolean | Callback) => void;
20
- static get<T extends {}>(from: T): Subscriber<T> | undefined;
20
+ watch: Map<any, boolean | (() => true | void)>;
21
21
  constructor(parent: Control<T>, onUpdate: Control.OnEvent);
22
+ get using(): Model.Values<T>;
23
+ assign(apply: Model.Compat<T>, keys?: any): void;
24
+ add(key: any, value?: boolean | Callback): void;
25
+ commit: () => () => void;
26
+ private event;
22
27
  }
23
28
 
24
29
  declare namespace Model {
@@ -133,6 +138,7 @@ declare class Control<T extends {} = any> {
133
138
  frame: Set<string>;
134
139
  waiting: Set<Callback>;
135
140
  followers: Set<Model.Control.OnEvent<any>>;
141
+ latest?: Model.Event<T>[];
136
142
  constructor(subject: T);
137
143
  subscribe(callback: Control.OnEvent<T>): Model.Subscriber<T>;
138
144
  watch(key: keyof T & string): void;
@@ -179,7 +185,7 @@ declare namespace Instruction {
179
185
  * Run instruction as controller sets itself up.
180
186
  * This will specialize the behavior of a given property.
181
187
  */
182
- declare function add<T = any>(instruction: Instruction<T>, name?: string): T;
188
+ declare function add<T = any>(instruction: Instruction<any>, label?: string): T;
183
189
 
184
190
  declare type Async<T = any> = (...args: any[]) => Promise<T>;
185
191
  /**
@@ -419,15 +425,15 @@ declare class MVC extends Model {
419
425
  static meta<T, M extends Class>(this: M, from: (this: M, state: M) => T, expect?: boolean): T;
420
426
  }
421
427
 
422
- declare const LOCAL: unique symbol;
423
- declare const STATE: unique symbol;
424
- declare const UPDATE: unique symbol;
425
- declare const CONTROL: unique symbol;
428
+ declare const LOCAL = "$debug local";
429
+ declare const STATE = "$debug state";
430
+ declare const UPDATE = "$debug update";
431
+ declare const CONTROL = "$debug controller";
426
432
  declare const Debug: {
427
- readonly CONTROL: typeof CONTROL;
428
- readonly LOCAL: typeof LOCAL;
429
- readonly STATE: typeof STATE;
430
- readonly UPDATE: typeof UPDATE;
433
+ readonly CONTROL: "$debug controller";
434
+ readonly LOCAL: "$debug local";
435
+ readonly STATE: "$debug state";
436
+ readonly UPDATE: "$debug update";
431
437
  };
432
438
  declare type Debug<T extends {}> = T & {
433
439
  /** Controller for this instance. */
@@ -573,12 +579,9 @@ declare function tap<T extends Model>(Type: Model.Type<T>, required?: boolean):
573
579
  declare function useFrom<T extends {}, R>(source: (() => T) | T, compute: (this: T, from: T) => R, expect: true): Exclude<R, undefined>;
574
580
  declare function useFrom<T extends {}, R>(source: (() => T) | T, compute: (this: T, from: T) => R, expect?: boolean): R;
575
581
 
576
- declare function useModel<T extends Model>(source: Model.Type<T>, watch: Model.Field<T>[], callback?: (instance: T) => void): T;
577
- declare function useModel<T extends Model>(source: Model.Type<T>, callback?: (instance: T) => void): T;
578
- declare function useModel<T extends Model>(source: Model.Type<T>, apply: Model.Compat<T>, keys?: Model.Event<T>[]): T;
579
- declare function useModel<T extends Model>(source: () => T, watch?: Model.Field<T>[], callback?: (instance: T) => void): T;
580
- declare function useModel<T extends Model>(source: () => T, callback?: (instance: T) => void): T;
581
- declare function useModel<T extends Model>(source: () => T, apply: Model.Compat<T>, keys?: Model.Event<T>[]): T;
582
+ declare function useModel<T extends Model>(source: Model.Type<T> | (() => T), callback?: (instance: T) => void): T;
583
+ declare function useModel<T extends Model>(source: Model.Type<T> | (() => T), watch: Model.Field<T>[], callback?: (instance: T) => void): T;
584
+ declare function useModel<T extends Model>(source: Model.Type<T> | (() => T), apply: Model.Compat<T>, keys?: Model.Event<T>[]): T;
582
585
 
583
586
  declare namespace useTap {
584
587
  type Source<T extends Model> = T | Model.Type<T> | (() => T | Model.Type<T>);
package/lib/index.esm.js CHANGED
@@ -187,20 +187,21 @@ class Subscriber {
187
187
  this.onUpdate = onUpdate;
188
188
  this.active = false;
189
189
  this.dependant = new Set();
190
- const proxy = create(parent.subject);
191
- const using = new Map();
192
- REGISTER$1.set(proxy, this);
193
- defineProperty(this, "proxy", {
194
- configurable: true,
195
- get() {
196
- hasUpdate(proxy);
197
- return proxy;
198
- }
199
- });
200
- const release = parent.addListener(key => {
190
+ this.watch = new Map();
191
+ this.commit = () => {
192
+ const release = this.parent.addListener(this.event);
193
+ this.active = true;
194
+ this.dependant.forEach(x => x.commit());
195
+ return this.release = () => {
196
+ this.dependant.forEach(x => x.release());
197
+ release();
198
+ };
199
+ };
200
+ this.event = (key) => {
201
201
  if (!this.active)
202
202
  return;
203
- const handler = using.get(key);
203
+ const { parent, watch } = this;
204
+ const handler = watch.get(key);
204
205
  let notify;
205
206
  if (typeof handler == "function") {
206
207
  const callback = handler();
@@ -211,49 +212,47 @@ class Subscriber {
211
212
  notify = this.onUpdate(key, parent);
212
213
  if (!notify)
213
214
  return;
214
- parent.waiting.add(() => addUpdate(proxy, using));
215
+ parent.waiting.add(() => {
216
+ this.latest = parent.latest.filter(k => watch.has(k));
217
+ });
215
218
  parent.waiting.add(notify);
216
- });
217
- this.add = (key, value) => {
218
- if (value !== undefined)
219
- using.set(key, value);
220
- else if (!using.has(key))
221
- using.set(key, true);
222
- };
223
- this.commit = () => {
224
- this.active = true;
225
- this.dependant.forEach(x => x.commit());
226
- return this.release;
227
- };
228
- this.release = () => {
229
- this.dependant.forEach(x => x.release());
230
- release();
231
219
  };
220
+ const proxy = create(parent.subject);
221
+ const reset = () => this.latest = undefined;
222
+ REGISTER$1.set(proxy, this);
223
+ defineProperty(this, "proxy", {
224
+ configurable: true,
225
+ get() {
226
+ setTimeout(reset, 0);
227
+ return proxy;
228
+ }
229
+ });
232
230
  }
233
231
  static get(from) {
234
232
  return REGISTER$1.get(from);
235
233
  }
234
+ get using() {
235
+ return Array.from(this.watch.keys());
236
+ }
237
+ assign(apply, keys) {
238
+ const { waiting, subject } = this.parent;
239
+ this.active = false;
240
+ if (typeof keys !== "object")
241
+ keys = getOwnPropertyNames(subject);
242
+ for (const key of keys)
243
+ if (key in apply)
244
+ subject[key] = apply[key];
245
+ waiting.add(() => this.active = true);
246
+ }
247
+ add(key, value) {
248
+ if (value !== undefined)
249
+ this.watch.set(key, value);
250
+ else if (!this.watch.has(key))
251
+ this.watch.set(key, true);
252
+ }
236
253
  }
237
254
 
238
255
  const REGISTER = new WeakMap();
239
- const UPDATE$1 = new WeakMap();
240
- function getUpdate(subject) {
241
- return UPDATE$1.get(subject);
242
- }
243
- function setUpdate(subject, keys) {
244
- UPDATE$1.set(subject, Array.from(keys));
245
- setTimeout(() => UPDATE$1.delete(subject), 0);
246
- }
247
- /** Ensure a local update is dropped after use. */
248
- function hasUpdate(proxy) {
249
- if (UPDATE$1.has(proxy))
250
- setTimeout(() => UPDATE$1.delete(proxy), 0);
251
- }
252
- /** Set local update for a subscribed context. */
253
- function addUpdate(proxy, using) {
254
- const parent = UPDATE$1.get(getPrototypeOf(proxy));
255
- UPDATE$1.set(proxy, parent.filter(k => using.has(k)));
256
- }
257
256
  class Control {
258
257
  constructor(subject) {
259
258
  this.subject = subject;
@@ -320,7 +319,8 @@ class Control {
320
319
  else if (!frame.size)
321
320
  setTimeout(() => {
322
321
  flush(this);
323
- setUpdate(this.subject, frame);
322
+ this.latest = Array.from(frame);
323
+ setTimeout(() => this.latest = undefined, 0);
324
324
  frame.clear();
325
325
  for (const notify of waiting)
326
326
  try {
@@ -371,12 +371,16 @@ class Control {
371
371
  }
372
372
 
373
373
  const PENDING = new Map();
374
- function add(fn, label) {
375
- const name = label || fn.name || "pending";
374
+ /**
375
+ * Run instruction as controller sets itself up.
376
+ * This will specialize the behavior of a given property.
377
+ */
378
+ function add(instruction, label) {
379
+ const name = label || instruction.name || "pending";
376
380
  const placeholder = Symbol(`${name} instruction`);
377
381
  function setup(key) {
378
382
  const { subject, state } = this;
379
- let output = fn.call(this, key, this);
383
+ let output = instruction.call(this, key, this);
380
384
  if (typeof output == "function")
381
385
  output = { get: output };
382
386
  if (typeof output != "object")
@@ -929,7 +933,7 @@ class Model {
929
933
  }
930
934
  else if (keys.includes(key)) {
931
935
  removeListener();
932
- return () => resolve(single ? control.state.get(key) : getUpdate(this));
936
+ return () => resolve(single ? control.state.get(key) : control.latest);
933
937
  }
934
938
  });
935
939
  });
@@ -949,7 +953,7 @@ class Model {
949
953
  catch (e) { }
950
954
  const callback = single
951
955
  ? () => { argument.call(this, control.state.get(select), select); }
952
- : () => { argument.call(this, getUpdate(this)); };
956
+ : () => { argument.call(this, control.latest); };
953
957
  const onEvent = once
954
958
  ? () => {
955
959
  removeListener();
@@ -1034,7 +1038,7 @@ class Model {
1034
1038
  throw Oops$5.NoChaining();
1035
1039
  if (frame.size || arg1 !== false)
1036
1040
  waiting.add(() => {
1037
- callback(getUpdate(this));
1041
+ callback(control.latest);
1038
1042
  });
1039
1043
  else
1040
1044
  callback(false);
@@ -1227,14 +1231,8 @@ function useModel(source, arg, arg2) {
1227
1231
  return instance;
1228
1232
  }
1229
1233
  const local = use(refresh => (Control.for(instance).subscribe(() => refresh)));
1230
- if (typeof arg == "object") {
1231
- local.active = false;
1232
- if (typeof arg2 !== "object")
1233
- arg2 = getOwnPropertyNames(instance);
1234
- for (const key of arg2)
1235
- if (key in arg)
1236
- instance[key] = arg[key];
1237
- }
1234
+ if (typeof arg == "object")
1235
+ local.assign(arg, arg2);
1238
1236
  React.useLayoutEffect(() => {
1239
1237
  local.commit();
1240
1238
  return () => {
@@ -1324,10 +1322,10 @@ class MVC extends Model {
1324
1322
  }
1325
1323
  }
1326
1324
 
1327
- const LOCAL = Symbol("LOCAL");
1328
- const STATE = Symbol("STATE");
1329
- const UPDATE = Symbol("UPDATE");
1330
- const CONTROL = Symbol("CONTROL");
1325
+ const LOCAL = "$debug local";
1326
+ const STATE = "$debug state";
1327
+ const UPDATE = "$debug update";
1328
+ const CONTROL = "$debug controller";
1331
1329
  Object.defineProperties(Model.prototype, {
1332
1330
  [CONTROL]: {
1333
1331
  get() {
@@ -1349,7 +1347,8 @@ Object.defineProperties(Model.prototype, {
1349
1347
  },
1350
1348
  [UPDATE]: {
1351
1349
  get() {
1352
- return getUpdate(this);
1350
+ const source = Subscriber.get(this) || Control.get(this);
1351
+ return source && source.latest;
1353
1352
  }
1354
1353
  }
1355
1354
  });