@expressive/mvc 0.47.0 → 0.49.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
@@ -8,7 +8,7 @@ declare namespace Model {
8
8
  */
9
9
  type Callback<T extends Model = Model> = (this: T, thisArg: T) => MaybeAsync<(() => void) | Assign<T> | void>;
10
10
  /** Model constructor argument */
11
- type Argument<T extends Model = Model> = string | Assign<T> | Callback<T> | Promise<Assign<T> | (() => void) | void> | void;
11
+ type Argument<T extends Model = Model> = Assign<T> | Callback<T> | string | void;
12
12
  /** Model constructor arguments */
13
13
  type Args<T extends Model = any> = Argument<T>[];
14
14
  /** Subset of `keyof T` which are not methods or defined by base Model U. **/
@@ -198,7 +198,6 @@ declare class Model {
198
198
  * Register a callback to run when any instance of this Model is updated.
199
199
  */
200
200
  static on<T extends Model>(this: Model.Type<T>, listener: Model.OnEvent<T>): () => boolean;
201
- static at<T extends Model>(this: Model.Type<T>, from: Model, cb: (got: T | undefined) => void): void;
202
201
  }
203
202
 
204
203
  declare namespace get {
@@ -355,11 +354,24 @@ declare function has<T extends Model>(type: Model.Type<T>, one: true): T;
355
354
  declare function has<T extends Model>(type: Model.Type<T>, required: boolean): T | undefined;
356
355
  declare function has<T extends Model>(type: Model.Type<T>, arg?: has.Callback<T>): Set<T>;
357
356
 
357
+ type Effect<T extends {}> = (this: T, argument: T) => ((update: boolean | null) => void) | Promise<void> | null | void;
358
+ /**
359
+ * Create a side-effect which will update whenever values accessed change.
360
+ * Callback is called immediately and if ever values are stale.
361
+ *
362
+ * @param target - Instance of Model to observe.
363
+ * @param callback - Function to invoke when values change.
364
+ * @param requireValues - If `true` will throw if accessing a value which is `undefined`.
365
+ */
366
+ declare function effect<T extends {}>(target: T, callback: Effect<Required<T>>, requireValues: true): () => void;
367
+ declare function effect<T extends {}>(target: T, callback: Effect<T>, requireValues?: boolean): () => void;
368
+
358
369
  declare namespace Context {
359
- type Input = Model | Model.Type<Model> | Record<string | number, Model | Model.Type<Model>>;
370
+ type Input = Record<string | number, Model | Model.Type<Model>>;
360
371
  }
361
372
  declare class Context {
362
- static request<T extends Model>(type: Model.Type<T>, from: Model, callback: (got: T) => void): void;
373
+ static get<T extends Model>(on: Model, callback: ((got: Context) => void)): void;
374
+ static get<T extends Model>(on: Model): Context | undefined;
363
375
  id: string;
364
376
  protected downstream: WeakMap<Model.Type<Model>, symbol>;
365
377
  protected upstream: WeakMap<Model.Type<Model>, symbol>;
@@ -392,4 +404,4 @@ declare global {
392
404
  }
393
405
  }
394
406
 
395
- export { Context, Model, Model as default, get, has, ref, set, use };
407
+ export { Context, Model, Model as default, effect, get, has, ref, set, use };
package/dist/index.js CHANGED
@@ -23,6 +23,7 @@ __export(src_exports, {
23
23
  Context: () => Context,
24
24
  Model: () => Model,
25
25
  default: () => Model,
26
+ effect: () => effect,
26
27
  get: () => get,
27
28
  has: () => has,
28
29
  ref: () => ref,
@@ -35,6 +36,7 @@ module.exports = __toCommonJS(src_exports);
35
36
  var DISPATCH = /* @__PURE__ */ new Set();
36
37
  var OBSERVER = /* @__PURE__ */ new WeakMap();
37
38
  var LISTENER = /* @__PURE__ */ new WeakMap();
39
+ var REQUIRED = /* @__PURE__ */ new WeakSet();
38
40
  var onReady = () => null;
39
41
  function addListener(subject, callback, select) {
40
42
  let subs = LISTENER.get(subject);
@@ -59,11 +61,13 @@ function watch(from, key, value) {
59
61
  LISTENER.set(value = Object.create(value), nested);
60
62
  OBSERVER.set(value, observer);
61
63
  }
64
+ if (value === void 0 && REQUIRED.has(from))
65
+ throw new Error(`${from}.${key} is required in this context.`);
62
66
  }
63
67
  return value;
64
68
  }
65
69
  var PENDING = /* @__PURE__ */ new WeakMap();
66
- function event(source, key) {
70
+ function emit(source, key) {
67
71
  const subs = LISTENER.get(source);
68
72
  const ready = !subs.has(onReady);
69
73
  if (key === true && ready)
@@ -90,21 +94,21 @@ function event(source, key) {
90
94
  });
91
95
  PENDING.delete(source);
92
96
  }
93
- function queue(event2) {
97
+ function queue(event) {
94
98
  if (!DISPATCH.size)
95
99
  setTimeout(() => {
96
- DISPATCH.forEach((event3) => {
100
+ DISPATCH.forEach((event2) => {
97
101
  try {
98
- event3();
102
+ event2();
99
103
  } catch (err) {
100
104
  console.error(err);
101
105
  }
102
106
  });
103
107
  DISPATCH.clear();
104
108
  }, 0);
105
- DISPATCH.add(event2);
109
+ DISPATCH.add(event);
106
110
  }
107
- function effect(target, callback) {
111
+ function effect(target, callback, requireValues) {
108
112
  const listeners = LISTENER.get(target);
109
113
  let unset;
110
114
  let reset;
@@ -122,6 +126,8 @@ function effect(target, callback) {
122
126
  }
123
127
  return reset;
124
128
  });
129
+ if (requireValues)
130
+ REQUIRED.add(subscriber);
125
131
  try {
126
132
  const out = callback.call(subscriber, subscriber);
127
133
  unset = typeof out == "function" ? out : void 0;
@@ -150,7 +156,7 @@ function effect(target, callback) {
150
156
  }
151
157
 
152
158
  // src/model.ts
153
- var FLATTEN;
159
+ var EXPORT;
154
160
  var NAMES = /* @__PURE__ */ new WeakMap();
155
161
  var STATE = /* @__PURE__ */ new WeakMap();
156
162
  var NOTIFY = /* @__PURE__ */ new WeakMap();
@@ -235,16 +241,16 @@ var Model = class _Model {
235
241
  }
236
242
  if (arg1 === void 0) {
237
243
  const values = {};
238
- const current = !FLATTEN && (FLATTEN = /* @__PURE__ */ new Map([[self, values]]));
244
+ const current = !EXPORT && (EXPORT = /* @__PURE__ */ new Map([[self, values]]));
239
245
  for (let [key, value] of self) {
240
- if (FLATTEN.has(value))
241
- value = FLATTEN.get(value);
246
+ if (EXPORT.has(value))
247
+ value = EXPORT.get(value);
242
248
  else if (value && typeof value.get === "function")
243
- FLATTEN.set(value, value = value.get());
249
+ EXPORT.set(value, value = value.get());
244
250
  values[key] = value;
245
251
  }
246
252
  if (current)
247
- FLATTEN = void 0;
253
+ EXPORT = void 0;
248
254
  return Object.freeze(values);
249
255
  }
250
256
  if (typeof arg2 == "function") {
@@ -267,11 +273,11 @@ var Model = class _Model {
267
273
  return arg1.call(self, key, self);
268
274
  });
269
275
  if (arg1 === null)
270
- return event(this, null);
276
+ return emit(this, null);
271
277
  if (typeof arg1 == "object")
272
278
  assign(self, arg1);
273
279
  else if (arg1 == void 0)
274
- event(this, true);
280
+ emit(this, true);
275
281
  else if (1 in arguments)
276
282
  update(self, arg1, arg2, arg3);
277
283
  else
@@ -301,7 +307,7 @@ var Model = class _Model {
301
307
  */
302
308
  static new(...args) {
303
309
  const instance = new this(...args);
304
- event(instance, true);
310
+ emit(instance, true);
305
311
  return instance;
306
312
  }
307
313
  /**
@@ -323,9 +329,6 @@ var Model = class _Model {
323
329
  notify.add(listener);
324
330
  return () => notify.delete(listener);
325
331
  }
326
- static at(from, cb) {
327
- throw new Error(`Using context requires an adapter. If you are only testing, define \`get.context\` to simulate one.`);
328
- }
329
332
  };
330
333
  define(Model.prototype, "toString", {
331
334
  value() {
@@ -345,14 +348,14 @@ function fetch(subject, property, required) {
345
348
  return value;
346
349
  }
347
350
  const error = new Error(`${subject}.${property} is not yet available.`);
348
- const promise = new Promise((resolve, reject2) => {
351
+ const promise = new Promise((resolve, reject) => {
349
352
  addListener(subject, (key) => {
350
353
  if (key === property) {
351
354
  resolve(state[key]);
352
355
  return null;
353
356
  }
354
357
  if (key === null)
355
- reject2(new Error(`${subject} is destroyed.`));
358
+ reject(new Error(`${subject} is destroyed.`));
356
359
  });
357
360
  });
358
361
  throw Object.assign(promise, {
@@ -371,7 +374,7 @@ function update(subject, key, value, silent) {
371
374
  return true;
372
375
  if (value instanceof Model && !PARENT.has(value)) {
373
376
  PARENT.set(value, subject);
374
- event(value, true);
377
+ emit(value, true);
375
378
  }
376
379
  state[key] = value;
377
380
  if (silent !== true)
@@ -383,13 +386,13 @@ function push(subject, key, silent) {
383
386
  PENDING2.set(subject, pending = /* @__PURE__ */ new Set());
384
387
  if (!silent)
385
388
  queue(() => {
386
- event(subject, false);
389
+ emit(subject, false);
387
390
  PENDING2.delete(subject);
388
391
  });
389
392
  }
390
393
  pending.add(key);
391
394
  if (!silent)
392
- event(subject, key);
395
+ emit(subject, key);
393
396
  }
394
397
  var METHODS = new WeakMap([[Model, []]]);
395
398
  function assign(to, values) {
@@ -429,6 +432,132 @@ function uid() {
429
432
  return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
430
433
  }
431
434
 
435
+ // src/context.ts
436
+ var Register = /* @__PURE__ */ new WeakMap();
437
+ var Context = class _Context {
438
+ constructor(inputs) {
439
+ this.downstream = /* @__PURE__ */ new WeakMap();
440
+ this.upstream = /* @__PURE__ */ new WeakMap();
441
+ this.layer = /* @__PURE__ */ new Map();
442
+ if (inputs)
443
+ this.include(inputs);
444
+ }
445
+ static get(from, callback) {
446
+ const waiting = Register.get(from);
447
+ if (!callback)
448
+ return waiting instanceof _Context ? waiting : void 0;
449
+ if (waiting instanceof _Context)
450
+ callback(waiting);
451
+ else if (waiting)
452
+ waiting.push(callback);
453
+ else
454
+ Register.set(from, [callback]);
455
+ }
456
+ key(T, upstream) {
457
+ const table = upstream ? this.upstream : this.downstream;
458
+ let key = table.get(T);
459
+ if (!key) {
460
+ key = Symbol(T.name + (upstream ? " request" : ""));
461
+ table.set(T, key);
462
+ }
463
+ return key;
464
+ }
465
+ has(model) {
466
+ const key = this.key(model.constructor, true);
467
+ const result = this[key];
468
+ const waiting = Register.get(model);
469
+ if (waiting instanceof Array)
470
+ waiting.forEach((cb) => cb(this));
471
+ Register.set(model, this);
472
+ if (typeof result == "function")
473
+ return result(model);
474
+ }
475
+ get(Type) {
476
+ const result = this[this.key(Type)];
477
+ if (result === null)
478
+ throw new Error(`Did find ${Type} in context, but multiple were defined.`);
479
+ return result;
480
+ }
481
+ include(inputs, forEach) {
482
+ const init = /* @__PURE__ */ new Map();
483
+ Object.entries(inputs).forEach(([K, V]) => {
484
+ if (!(Model.is(V) || V instanceof Model))
485
+ throw new Error(`Context can only include Model or instance but got ${V}${K && " as " + K}.`);
486
+ });
487
+ for (const key in inputs) {
488
+ const input = inputs[key];
489
+ const exists = this.layer.get(key);
490
+ if (!exists) {
491
+ const instance = this.add(input);
492
+ this.layer.set(key, input);
493
+ init.set(instance, true);
494
+ } else if (exists !== input) {
495
+ this.pop();
496
+ this.id = uid();
497
+ this.include(inputs);
498
+ }
499
+ }
500
+ for (const [model, explicit] of init) {
501
+ model.set();
502
+ if (forEach)
503
+ forEach(model, explicit);
504
+ for (const [_key, value] of model)
505
+ if (PARENT.get(value) === model) {
506
+ this.add(value, true);
507
+ init.set(value, false);
508
+ }
509
+ }
510
+ }
511
+ put(T, I, implicit, writable) {
512
+ do {
513
+ const key = this.key(T, typeof I == "function");
514
+ const value = this.hasOwnProperty(key) ? null : I;
515
+ if (value || this[key] !== I && !implicit)
516
+ Object.defineProperty(this, key, {
517
+ configurable: true,
518
+ writable,
519
+ value
520
+ });
521
+ T = Object.getPrototypeOf(T);
522
+ } while (T !== Model);
523
+ }
524
+ add(input, implicit) {
525
+ let writable = true;
526
+ let T;
527
+ let I;
528
+ if (typeof input == "function") {
529
+ T = input;
530
+ I = new input();
531
+ } else {
532
+ I = input;
533
+ T = I.constructor;
534
+ writable = false;
535
+ }
536
+ this.has(I);
537
+ this.put(T, I, implicit, writable);
538
+ return I;
539
+ }
540
+ push(inputs) {
541
+ const next = Object.create(this);
542
+ next.layer = /* @__PURE__ */ new Map();
543
+ if (inputs)
544
+ next.include(inputs);
545
+ return next;
546
+ }
547
+ pop() {
548
+ const items = /* @__PURE__ */ new Set();
549
+ for (const key of Object.getOwnPropertySymbols(this)) {
550
+ const entry = Object.getOwnPropertyDescriptor(this, key);
551
+ if (entry.writable && entry.value)
552
+ items.add(entry.value);
553
+ delete this[key];
554
+ }
555
+ for (const model of items)
556
+ model.set(null);
557
+ this.layer.clear();
558
+ }
559
+ };
560
+
432
561
  // src/instruction/use.ts
433
562
  var INSTRUCT = /* @__PURE__ */ new Map();
434
563
  function use(model, argument) {
@@ -500,20 +629,21 @@ Model.on((_, subject) => {
500
629
  // src/instruction/get.ts
501
630
  var STALE = /* @__PURE__ */ new WeakSet();
502
631
  function get(arg0, arg1) {
503
- return use((key, subject) => {
632
+ return use((key, subject, state) => {
504
633
  let from = subject;
505
634
  if (typeof arg0 == "symbol")
506
635
  throw new Error(`Attempted to use an instruction result (probably use or get) as computed source for ${from}.${key}. This is not allowed.`);
507
636
  let source = (resolve) => resolve(from);
508
637
  if (arg0 instanceof Model) {
509
638
  from = arg0;
510
- } else if (arg0.prototype instanceof Model) {
639
+ } else if (Model.is(arg0)) {
511
640
  const hasParent = PARENT.get(from);
512
641
  if (!hasParent) {
513
642
  if (arg1 === true)
514
643
  throw new Error(`${from} may only exist as a child of type ${arg0}.`);
515
644
  source = (resolve) => {
516
- arg0.at(from, (got) => {
645
+ Context.get(from, (context) => {
646
+ const got = context.get(arg0);
517
647
  if (got)
518
648
  resolve(got);
519
649
  else if (arg1 !== false)
@@ -532,10 +662,8 @@ function get(arg0, arg1) {
532
662
  let connect2 = function(model) {
533
663
  reset = effect(model, (current) => {
534
664
  proxy = current;
535
- if (!reset)
536
- compute2(true);
537
- else if (STALE.delete(compute2))
538
- compute2();
665
+ if (!(key in state) || STALE.delete(compute2))
666
+ compute2(!reset);
539
667
  return (didUpdate) => {
540
668
  if (didUpdate) {
541
669
  STALE.add(compute2);
@@ -699,178 +827,49 @@ function attempt(fn) {
699
827
  return compute();
700
828
  }
701
829
 
702
- // src/context.ts
703
- var Expects = /* @__PURE__ */ new WeakMap();
704
- var Context = class {
705
- constructor(inputs) {
706
- this.downstream = /* @__PURE__ */ new WeakMap();
707
- this.upstream = /* @__PURE__ */ new WeakMap();
708
- this.layer = /* @__PURE__ */ new Map();
709
- if (inputs)
710
- this.include(inputs);
711
- }
712
- static request(type, from, callback) {
713
- let map = Expects.get(from);
714
- if (!map)
715
- Expects.set(from, map = /* @__PURE__ */ new Map());
716
- map.set(type, callback);
717
- }
718
- key(T, upstream) {
719
- const table = upstream ? this.upstream : this.downstream;
720
- let key = table.get(T);
721
- if (!key) {
722
- key = Symbol(T.name + (upstream ? " request" : ""));
723
- table.set(T, key);
724
- }
725
- return key;
726
- }
727
- has(model) {
728
- const key = this.key(model.constructor, true);
729
- const result = this[key];
730
- if (typeof result == "function")
731
- return result(model);
732
- }
733
- get(Type) {
734
- const result = this[this.key(Type)];
735
- if (result === null)
736
- throw new Error(`Did find ${Type} in context, but multiple were defined.`);
737
- return result;
738
- }
739
- include(inputs, forEach) {
740
- const init = /* @__PURE__ */ new Map();
741
- if (typeof inputs == "function" || inputs instanceof Model)
742
- inputs = { [0]: inputs };
743
- else if (!inputs)
744
- reject(inputs);
745
- Object.entries(inputs).forEach(([K, V]) => {
746
- if (!(Model.is(V) || V instanceof Model))
747
- reject(`${V} as ${K}`);
748
- });
749
- for (const key in inputs) {
750
- const input = inputs[key];
751
- const exists = this.layer.get(key);
752
- if (!exists) {
753
- const instance = this.add(input);
754
- this.layer.set(key, input);
755
- init.set(instance, true);
756
- } else if (exists !== input) {
757
- this.pop();
758
- this.id = uid();
759
- this.include(inputs);
760
- }
761
- }
762
- for (const [model, explicit] of init) {
763
- if (forEach)
764
- forEach(model, explicit);
765
- model.set();
766
- this.has(model);
767
- const expects = Expects.get(model);
768
- if (expects)
769
- for (let [T, callback] of expects)
770
- this.put(T, callback);
771
- for (const [_key, value] of model)
772
- if (PARENT.get(value) === model) {
773
- this.add(value, true);
774
- init.set(value, false);
775
- }
776
- }
777
- }
778
- put(T, I, implicit, writable) {
779
- do {
780
- const key = this.key(T, typeof I == "function");
781
- const value = this.hasOwnProperty(key) ? null : I;
782
- if (value || this[key] !== I && !implicit)
783
- Object.defineProperty(this, key, {
784
- configurable: true,
785
- writable,
786
- value
787
- });
788
- T = Object.getPrototypeOf(T);
789
- } while (T !== Model);
790
- }
791
- add(input, implicit) {
792
- let writable = true;
793
- let T;
794
- let I;
795
- if (typeof input == "function") {
796
- T = input;
797
- I = new input();
798
- } else {
799
- I = input;
800
- T = I.constructor;
801
- writable = false;
802
- }
803
- this.put(T, I, implicit, writable);
804
- return I;
805
- }
806
- push(inputs) {
807
- const next = Object.create(this);
808
- next.layer = /* @__PURE__ */ new Map();
809
- if (inputs)
810
- next.include(inputs);
811
- return next;
812
- }
813
- pop() {
814
- const items = /* @__PURE__ */ new Set();
815
- for (const key of Object.getOwnPropertySymbols(this)) {
816
- const entry = Object.getOwnPropertyDescriptor(this, key);
817
- if (entry.writable && entry.value)
818
- items.add(entry.value);
819
- delete this[key];
820
- }
821
- for (const model of items)
822
- model.set(null);
823
- this.layer.clear();
824
- }
825
- };
826
- function reject(argument) {
827
- throw new Error(`Context can only include Model or instance but got ${argument}.`);
828
- }
829
-
830
830
  // src/instruction/has.ts
831
831
  function has(type, argument) {
832
832
  return use((key, subject, state) => {
833
+ const expect = (callback) => Context.get(subject, (context) => context.put(type, callback));
833
834
  if (typeof argument == "boolean") {
834
- Context.request(type, subject, (model) => {
835
+ expect((model) => {
835
836
  subject.set(key, model);
836
837
  const remove = () => {
837
- drop();
838
838
  if (state[key] === model)
839
839
  delete state[key];
840
840
  };
841
- const drop = model.get(null, remove);
841
+ model.get(null, remove);
842
842
  return remove;
843
843
  });
844
844
  return { get: argument };
845
845
  }
846
- const children = /* @__PURE__ */ new Set();
847
- Context.request(type, subject, (model) => {
848
- if (children.has(model))
846
+ const value = /* @__PURE__ */ new Set();
847
+ expect((model) => {
848
+ if (value.has(model))
849
849
  return;
850
850
  const remove = typeof argument == "function" ? argument(model) : void 0;
851
851
  if (remove === false)
852
852
  return;
853
- children.add(model);
853
+ value.add(model);
854
854
  subject.set(key);
855
855
  const done = () => {
856
856
  drop();
857
857
  if (typeof remove == "function")
858
858
  remove();
859
- children.delete(model);
859
+ value.delete(model);
860
860
  subject.set(key);
861
861
  };
862
862
  const drop = model.get(null, done);
863
863
  return done;
864
864
  });
865
- return {
866
- value: children
867
- };
865
+ return { value };
868
866
  });
869
867
  }
870
868
  // Annotate the CommonJS export names for ESM import in node:
871
869
  0 && (module.exports = {
872
870
  Context,
873
871
  Model,
872
+ effect,
874
873
  get,
875
874
  has,
876
875
  ref,
package/dist/index.mjs CHANGED
@@ -2,6 +2,7 @@
2
2
  var DISPATCH = /* @__PURE__ */ new Set();
3
3
  var OBSERVER = /* @__PURE__ */ new WeakMap();
4
4
  var LISTENER = /* @__PURE__ */ new WeakMap();
5
+ var REQUIRED = /* @__PURE__ */ new WeakSet();
5
6
  var onReady = () => null;
6
7
  function addListener(subject, callback, select) {
7
8
  let subs = LISTENER.get(subject);
@@ -26,11 +27,13 @@ function watch(from, key, value) {
26
27
  LISTENER.set(value = Object.create(value), nested);
27
28
  OBSERVER.set(value, observer);
28
29
  }
30
+ if (value === void 0 && REQUIRED.has(from))
31
+ throw new Error(`${from}.${key} is required in this context.`);
29
32
  }
30
33
  return value;
31
34
  }
32
35
  var PENDING = /* @__PURE__ */ new WeakMap();
33
- function event(source, key) {
36
+ function emit(source, key) {
34
37
  const subs = LISTENER.get(source);
35
38
  const ready = !subs.has(onReady);
36
39
  if (key === true && ready)
@@ -57,21 +60,21 @@ function event(source, key) {
57
60
  });
58
61
  PENDING.delete(source);
59
62
  }
60
- function queue(event2) {
63
+ function queue(event) {
61
64
  if (!DISPATCH.size)
62
65
  setTimeout(() => {
63
- DISPATCH.forEach((event3) => {
66
+ DISPATCH.forEach((event2) => {
64
67
  try {
65
- event3();
68
+ event2();
66
69
  } catch (err) {
67
70
  console.error(err);
68
71
  }
69
72
  });
70
73
  DISPATCH.clear();
71
74
  }, 0);
72
- DISPATCH.add(event2);
75
+ DISPATCH.add(event);
73
76
  }
74
- function effect(target, callback) {
77
+ function effect(target, callback, requireValues) {
75
78
  const listeners = LISTENER.get(target);
76
79
  let unset;
77
80
  let reset;
@@ -89,6 +92,8 @@ function effect(target, callback) {
89
92
  }
90
93
  return reset;
91
94
  });
95
+ if (requireValues)
96
+ REQUIRED.add(subscriber);
92
97
  try {
93
98
  const out = callback.call(subscriber, subscriber);
94
99
  unset = typeof out == "function" ? out : void 0;
@@ -117,7 +122,7 @@ function effect(target, callback) {
117
122
  }
118
123
 
119
124
  // src/model.ts
120
- var FLATTEN;
125
+ var EXPORT;
121
126
  var NAMES = /* @__PURE__ */ new WeakMap();
122
127
  var STATE = /* @__PURE__ */ new WeakMap();
123
128
  var NOTIFY = /* @__PURE__ */ new WeakMap();
@@ -202,16 +207,16 @@ var Model = class _Model {
202
207
  }
203
208
  if (arg1 === void 0) {
204
209
  const values = {};
205
- const current = !FLATTEN && (FLATTEN = /* @__PURE__ */ new Map([[self, values]]));
210
+ const current = !EXPORT && (EXPORT = /* @__PURE__ */ new Map([[self, values]]));
206
211
  for (let [key, value] of self) {
207
- if (FLATTEN.has(value))
208
- value = FLATTEN.get(value);
212
+ if (EXPORT.has(value))
213
+ value = EXPORT.get(value);
209
214
  else if (value && typeof value.get === "function")
210
- FLATTEN.set(value, value = value.get());
215
+ EXPORT.set(value, value = value.get());
211
216
  values[key] = value;
212
217
  }
213
218
  if (current)
214
- FLATTEN = void 0;
219
+ EXPORT = void 0;
215
220
  return Object.freeze(values);
216
221
  }
217
222
  if (typeof arg2 == "function") {
@@ -234,11 +239,11 @@ var Model = class _Model {
234
239
  return arg1.call(self, key, self);
235
240
  });
236
241
  if (arg1 === null)
237
- return event(this, null);
242
+ return emit(this, null);
238
243
  if (typeof arg1 == "object")
239
244
  assign(self, arg1);
240
245
  else if (arg1 == void 0)
241
- event(this, true);
246
+ emit(this, true);
242
247
  else if (1 in arguments)
243
248
  update(self, arg1, arg2, arg3);
244
249
  else
@@ -268,7 +273,7 @@ var Model = class _Model {
268
273
  */
269
274
  static new(...args) {
270
275
  const instance = new this(...args);
271
- event(instance, true);
276
+ emit(instance, true);
272
277
  return instance;
273
278
  }
274
279
  /**
@@ -290,9 +295,6 @@ var Model = class _Model {
290
295
  notify.add(listener);
291
296
  return () => notify.delete(listener);
292
297
  }
293
- static at(from, cb) {
294
- throw new Error(`Using context requires an adapter. If you are only testing, define \`get.context\` to simulate one.`);
295
- }
296
298
  };
297
299
  define(Model.prototype, "toString", {
298
300
  value() {
@@ -312,14 +314,14 @@ function fetch(subject, property, required) {
312
314
  return value;
313
315
  }
314
316
  const error = new Error(`${subject}.${property} is not yet available.`);
315
- const promise = new Promise((resolve, reject2) => {
317
+ const promise = new Promise((resolve, reject) => {
316
318
  addListener(subject, (key) => {
317
319
  if (key === property) {
318
320
  resolve(state[key]);
319
321
  return null;
320
322
  }
321
323
  if (key === null)
322
- reject2(new Error(`${subject} is destroyed.`));
324
+ reject(new Error(`${subject} is destroyed.`));
323
325
  });
324
326
  });
325
327
  throw Object.assign(promise, {
@@ -338,7 +340,7 @@ function update(subject, key, value, silent) {
338
340
  return true;
339
341
  if (value instanceof Model && !PARENT.has(value)) {
340
342
  PARENT.set(value, subject);
341
- event(value, true);
343
+ emit(value, true);
342
344
  }
343
345
  state[key] = value;
344
346
  if (silent !== true)
@@ -350,13 +352,13 @@ function push(subject, key, silent) {
350
352
  PENDING2.set(subject, pending = /* @__PURE__ */ new Set());
351
353
  if (!silent)
352
354
  queue(() => {
353
- event(subject, false);
355
+ emit(subject, false);
354
356
  PENDING2.delete(subject);
355
357
  });
356
358
  }
357
359
  pending.add(key);
358
360
  if (!silent)
359
- event(subject, key);
361
+ emit(subject, key);
360
362
  }
361
363
  var METHODS = new WeakMap([[Model, []]]);
362
364
  function assign(to, values) {
@@ -396,6 +398,132 @@ function uid() {
396
398
  return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
397
399
  }
398
400
 
401
+ // src/context.ts
402
+ var Register = /* @__PURE__ */ new WeakMap();
403
+ var Context = class _Context {
404
+ constructor(inputs) {
405
+ this.downstream = /* @__PURE__ */ new WeakMap();
406
+ this.upstream = /* @__PURE__ */ new WeakMap();
407
+ this.layer = /* @__PURE__ */ new Map();
408
+ if (inputs)
409
+ this.include(inputs);
410
+ }
411
+ static get(from, callback) {
412
+ const waiting = Register.get(from);
413
+ if (!callback)
414
+ return waiting instanceof _Context ? waiting : void 0;
415
+ if (waiting instanceof _Context)
416
+ callback(waiting);
417
+ else if (waiting)
418
+ waiting.push(callback);
419
+ else
420
+ Register.set(from, [callback]);
421
+ }
422
+ key(T, upstream) {
423
+ const table = upstream ? this.upstream : this.downstream;
424
+ let key = table.get(T);
425
+ if (!key) {
426
+ key = Symbol(T.name + (upstream ? " request" : ""));
427
+ table.set(T, key);
428
+ }
429
+ return key;
430
+ }
431
+ has(model) {
432
+ const key = this.key(model.constructor, true);
433
+ const result = this[key];
434
+ const waiting = Register.get(model);
435
+ if (waiting instanceof Array)
436
+ waiting.forEach((cb) => cb(this));
437
+ Register.set(model, this);
438
+ if (typeof result == "function")
439
+ return result(model);
440
+ }
441
+ get(Type) {
442
+ const result = this[this.key(Type)];
443
+ if (result === null)
444
+ throw new Error(`Did find ${Type} in context, but multiple were defined.`);
445
+ return result;
446
+ }
447
+ include(inputs, forEach) {
448
+ const init = /* @__PURE__ */ new Map();
449
+ Object.entries(inputs).forEach(([K, V]) => {
450
+ if (!(Model.is(V) || V instanceof Model))
451
+ throw new Error(`Context can only include Model or instance but got ${V}${K && " as " + K}.`);
452
+ });
453
+ for (const key in inputs) {
454
+ const input = inputs[key];
455
+ const exists = this.layer.get(key);
456
+ if (!exists) {
457
+ const instance = this.add(input);
458
+ this.layer.set(key, input);
459
+ init.set(instance, true);
460
+ } else if (exists !== input) {
461
+ this.pop();
462
+ this.id = uid();
463
+ this.include(inputs);
464
+ }
465
+ }
466
+ for (const [model, explicit] of init) {
467
+ model.set();
468
+ if (forEach)
469
+ forEach(model, explicit);
470
+ for (const [_key, value] of model)
471
+ if (PARENT.get(value) === model) {
472
+ this.add(value, true);
473
+ init.set(value, false);
474
+ }
475
+ }
476
+ }
477
+ put(T, I, implicit, writable) {
478
+ do {
479
+ const key = this.key(T, typeof I == "function");
480
+ const value = this.hasOwnProperty(key) ? null : I;
481
+ if (value || this[key] !== I && !implicit)
482
+ Object.defineProperty(this, key, {
483
+ configurable: true,
484
+ writable,
485
+ value
486
+ });
487
+ T = Object.getPrototypeOf(T);
488
+ } while (T !== Model);
489
+ }
490
+ add(input, implicit) {
491
+ let writable = true;
492
+ let T;
493
+ let I;
494
+ if (typeof input == "function") {
495
+ T = input;
496
+ I = new input();
497
+ } else {
498
+ I = input;
499
+ T = I.constructor;
500
+ writable = false;
501
+ }
502
+ this.has(I);
503
+ this.put(T, I, implicit, writable);
504
+ return I;
505
+ }
506
+ push(inputs) {
507
+ const next = Object.create(this);
508
+ next.layer = /* @__PURE__ */ new Map();
509
+ if (inputs)
510
+ next.include(inputs);
511
+ return next;
512
+ }
513
+ pop() {
514
+ const items = /* @__PURE__ */ new Set();
515
+ for (const key of Object.getOwnPropertySymbols(this)) {
516
+ const entry = Object.getOwnPropertyDescriptor(this, key);
517
+ if (entry.writable && entry.value)
518
+ items.add(entry.value);
519
+ delete this[key];
520
+ }
521
+ for (const model of items)
522
+ model.set(null);
523
+ this.layer.clear();
524
+ }
525
+ };
526
+
399
527
  // src/instruction/use.ts
400
528
  var INSTRUCT = /* @__PURE__ */ new Map();
401
529
  function use(model, argument) {
@@ -467,20 +595,21 @@ Model.on((_, subject) => {
467
595
  // src/instruction/get.ts
468
596
  var STALE = /* @__PURE__ */ new WeakSet();
469
597
  function get(arg0, arg1) {
470
- return use((key, subject) => {
598
+ return use((key, subject, state) => {
471
599
  let from = subject;
472
600
  if (typeof arg0 == "symbol")
473
601
  throw new Error(`Attempted to use an instruction result (probably use or get) as computed source for ${from}.${key}. This is not allowed.`);
474
602
  let source = (resolve) => resolve(from);
475
603
  if (arg0 instanceof Model) {
476
604
  from = arg0;
477
- } else if (arg0.prototype instanceof Model) {
605
+ } else if (Model.is(arg0)) {
478
606
  const hasParent = PARENT.get(from);
479
607
  if (!hasParent) {
480
608
  if (arg1 === true)
481
609
  throw new Error(`${from} may only exist as a child of type ${arg0}.`);
482
610
  source = (resolve) => {
483
- arg0.at(from, (got) => {
611
+ Context.get(from, (context) => {
612
+ const got = context.get(arg0);
484
613
  if (got)
485
614
  resolve(got);
486
615
  else if (arg1 !== false)
@@ -499,10 +628,8 @@ function get(arg0, arg1) {
499
628
  let connect2 = function(model) {
500
629
  reset = effect(model, (current) => {
501
630
  proxy = current;
502
- if (!reset)
503
- compute2(true);
504
- else if (STALE.delete(compute2))
505
- compute2();
631
+ if (!(key in state) || STALE.delete(compute2))
632
+ compute2(!reset);
506
633
  return (didUpdate) => {
507
634
  if (didUpdate) {
508
635
  STALE.add(compute2);
@@ -666,178 +793,49 @@ function attempt(fn) {
666
793
  return compute();
667
794
  }
668
795
 
669
- // src/context.ts
670
- var Expects = /* @__PURE__ */ new WeakMap();
671
- var Context = class {
672
- constructor(inputs) {
673
- this.downstream = /* @__PURE__ */ new WeakMap();
674
- this.upstream = /* @__PURE__ */ new WeakMap();
675
- this.layer = /* @__PURE__ */ new Map();
676
- if (inputs)
677
- this.include(inputs);
678
- }
679
- static request(type, from, callback) {
680
- let map = Expects.get(from);
681
- if (!map)
682
- Expects.set(from, map = /* @__PURE__ */ new Map());
683
- map.set(type, callback);
684
- }
685
- key(T, upstream) {
686
- const table = upstream ? this.upstream : this.downstream;
687
- let key = table.get(T);
688
- if (!key) {
689
- key = Symbol(T.name + (upstream ? " request" : ""));
690
- table.set(T, key);
691
- }
692
- return key;
693
- }
694
- has(model) {
695
- const key = this.key(model.constructor, true);
696
- const result = this[key];
697
- if (typeof result == "function")
698
- return result(model);
699
- }
700
- get(Type) {
701
- const result = this[this.key(Type)];
702
- if (result === null)
703
- throw new Error(`Did find ${Type} in context, but multiple were defined.`);
704
- return result;
705
- }
706
- include(inputs, forEach) {
707
- const init = /* @__PURE__ */ new Map();
708
- if (typeof inputs == "function" || inputs instanceof Model)
709
- inputs = { [0]: inputs };
710
- else if (!inputs)
711
- reject(inputs);
712
- Object.entries(inputs).forEach(([K, V]) => {
713
- if (!(Model.is(V) || V instanceof Model))
714
- reject(`${V} as ${K}`);
715
- });
716
- for (const key in inputs) {
717
- const input = inputs[key];
718
- const exists = this.layer.get(key);
719
- if (!exists) {
720
- const instance = this.add(input);
721
- this.layer.set(key, input);
722
- init.set(instance, true);
723
- } else if (exists !== input) {
724
- this.pop();
725
- this.id = uid();
726
- this.include(inputs);
727
- }
728
- }
729
- for (const [model, explicit] of init) {
730
- if (forEach)
731
- forEach(model, explicit);
732
- model.set();
733
- this.has(model);
734
- const expects = Expects.get(model);
735
- if (expects)
736
- for (let [T, callback] of expects)
737
- this.put(T, callback);
738
- for (const [_key, value] of model)
739
- if (PARENT.get(value) === model) {
740
- this.add(value, true);
741
- init.set(value, false);
742
- }
743
- }
744
- }
745
- put(T, I, implicit, writable) {
746
- do {
747
- const key = this.key(T, typeof I == "function");
748
- const value = this.hasOwnProperty(key) ? null : I;
749
- if (value || this[key] !== I && !implicit)
750
- Object.defineProperty(this, key, {
751
- configurable: true,
752
- writable,
753
- value
754
- });
755
- T = Object.getPrototypeOf(T);
756
- } while (T !== Model);
757
- }
758
- add(input, implicit) {
759
- let writable = true;
760
- let T;
761
- let I;
762
- if (typeof input == "function") {
763
- T = input;
764
- I = new input();
765
- } else {
766
- I = input;
767
- T = I.constructor;
768
- writable = false;
769
- }
770
- this.put(T, I, implicit, writable);
771
- return I;
772
- }
773
- push(inputs) {
774
- const next = Object.create(this);
775
- next.layer = /* @__PURE__ */ new Map();
776
- if (inputs)
777
- next.include(inputs);
778
- return next;
779
- }
780
- pop() {
781
- const items = /* @__PURE__ */ new Set();
782
- for (const key of Object.getOwnPropertySymbols(this)) {
783
- const entry = Object.getOwnPropertyDescriptor(this, key);
784
- if (entry.writable && entry.value)
785
- items.add(entry.value);
786
- delete this[key];
787
- }
788
- for (const model of items)
789
- model.set(null);
790
- this.layer.clear();
791
- }
792
- };
793
- function reject(argument) {
794
- throw new Error(`Context can only include Model or instance but got ${argument}.`);
795
- }
796
-
797
796
  // src/instruction/has.ts
798
797
  function has(type, argument) {
799
798
  return use((key, subject, state) => {
799
+ const expect = (callback) => Context.get(subject, (context) => context.put(type, callback));
800
800
  if (typeof argument == "boolean") {
801
- Context.request(type, subject, (model) => {
801
+ expect((model) => {
802
802
  subject.set(key, model);
803
803
  const remove = () => {
804
- drop();
805
804
  if (state[key] === model)
806
805
  delete state[key];
807
806
  };
808
- const drop = model.get(null, remove);
807
+ model.get(null, remove);
809
808
  return remove;
810
809
  });
811
810
  return { get: argument };
812
811
  }
813
- const children = /* @__PURE__ */ new Set();
814
- Context.request(type, subject, (model) => {
815
- if (children.has(model))
812
+ const value = /* @__PURE__ */ new Set();
813
+ expect((model) => {
814
+ if (value.has(model))
816
815
  return;
817
816
  const remove = typeof argument == "function" ? argument(model) : void 0;
818
817
  if (remove === false)
819
818
  return;
820
- children.add(model);
819
+ value.add(model);
821
820
  subject.set(key);
822
821
  const done = () => {
823
822
  drop();
824
823
  if (typeof remove == "function")
825
824
  remove();
826
- children.delete(model);
825
+ value.delete(model);
827
826
  subject.set(key);
828
827
  };
829
828
  const drop = model.get(null, done);
830
829
  return done;
831
830
  });
832
- return {
833
- value: children
834
- };
831
+ return { value };
835
832
  });
836
833
  }
837
834
  export {
838
835
  Context,
839
836
  Model,
840
837
  Model as default,
838
+ effect,
841
839
  get,
842
840
  has,
843
841
  ref,
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.47.0",
32
- "gitHead": "7c0ddd5ea9b7e08bd5aa21aa8e1ab65ceef17fcf"
31
+ "version": "0.49.0",
32
+ "gitHead": "3a1423ef6d335d0e876904459d360a2e0ebdff7b"
33
33
  }