@expressive/mvc 0.46.0 → 0.48.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,11 +198,8 @@ 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
- declare function add<T = any>(instruction: Model.Instruction): T;
205
-
206
203
  declare namespace get {
207
204
  type Function<T, S = any> = (on: S, key: string) => T;
208
205
  type Factory<T, S = any> = (this: S, key: string, thisArg: S) => T;
@@ -346,19 +343,9 @@ declare function set<T>(factory: set.Factory<T> | Promise<T>, required?: boolean
346
343
  */
347
344
  declare function set<T>(value: T | undefined, onUpdate: set.Callback<T>): T;
348
345
 
349
- /** Create a placeholder for specified Model type. */
350
- declare function use<T extends Model>(): T | undefined;
351
- /** Create a new child instance of model. */
346
+ declare function use<T = any>(instruction: Model.Instruction): T;
347
+ declare function use<T extends Model>(Type: Model.Type<T>, required: false): T | undefined;
352
348
  declare function use<T extends Model>(Type: Model.Type<T>, ready?: (i: T) => void): T;
353
- /**
354
- * Use existing model as a child of model assigned to.
355
- *
356
- * Note: If `peer` is not already initialized before parent is
357
- * (created with `new` as opposed to create method), that model will
358
- * attach this via `parent()` instruction. It will not, however, if
359
- * already active.
360
- **/
361
- declare function use<T extends Model>(model: T, ready?: (i: T) => void): T;
362
349
 
363
350
  declare namespace has {
364
351
  type Callback<T = any> = (model: T) => void | boolean | (() => void);
@@ -368,10 +355,11 @@ declare function has<T extends Model>(type: Model.Type<T>, required: boolean): T
368
355
  declare function has<T extends Model>(type: Model.Type<T>, arg?: has.Callback<T>): Set<T>;
369
356
 
370
357
  declare namespace Context {
371
- type Input = Model | Model.Type<Model> | Record<string | number, Model | Model.Type<Model>>;
358
+ type Input = Record<string | number, Model | Model.Type<Model>>;
372
359
  }
373
360
  declare class Context {
374
- static request<T extends Model>(type: Model.Type<T>, from: Model, callback: (got: T) => void): void;
361
+ static get<T extends Model>(on: Model, callback: ((got: Context) => void)): void;
362
+ static get<T extends Model>(on: Model): Context | undefined;
375
363
  id: string;
376
364
  protected downstream: WeakMap<Model.Type<Model>, symbol>;
377
365
  protected upstream: WeakMap<Model.Type<Model>, symbol>;
@@ -404,4 +392,4 @@ declare global {
404
392
  }
405
393
  }
406
394
 
407
- export { Context, Model, add, Model as default, get, has, ref, set, use };
395
+ export { Context, Model, Model as default, get, has, ref, set, use };
package/dist/index.js CHANGED
@@ -22,7 +22,6 @@ var src_exports = {};
22
22
  __export(src_exports, {
23
23
  Context: () => Context,
24
24
  Model: () => Model,
25
- add: () => add,
26
25
  default: () => Model,
27
26
  get: () => get,
28
27
  has: () => has,
@@ -151,7 +150,7 @@ function effect(target, callback) {
151
150
  }
152
151
 
153
152
  // src/model.ts
154
- var FLATTEN;
153
+ var EXPORT;
155
154
  var NAMES = /* @__PURE__ */ new WeakMap();
156
155
  var STATE = /* @__PURE__ */ new WeakMap();
157
156
  var NOTIFY = /* @__PURE__ */ new WeakMap();
@@ -197,7 +196,7 @@ var Model = class _Model {
197
196
  for (const key in this) {
198
197
  const desc = Object.getOwnPropertyDescriptor(this, key);
199
198
  if ("value" in desc) {
200
- state[key] = desc.value;
199
+ update(this, key, desc.value, true);
201
200
  define(this, key, {
202
201
  configurable: false,
203
202
  set: (x) => update(this, key, x),
@@ -207,6 +206,8 @@ var Model = class _Model {
207
206
  });
208
207
  }
209
208
  }
209
+ if (!PARENT.has(this))
210
+ PARENT.set(this, null);
210
211
  addListener(this, () => {
211
212
  if (!done)
212
213
  throw new Error(`Tried to destroy ${this} but not fully initialized.`);
@@ -234,16 +235,16 @@ var Model = class _Model {
234
235
  }
235
236
  if (arg1 === void 0) {
236
237
  const values = {};
237
- const current = !FLATTEN && (FLATTEN = /* @__PURE__ */ new Map([[self, values]]));
238
+ const current = !EXPORT && (EXPORT = /* @__PURE__ */ new Map([[self, values]]));
238
239
  for (let [key, value] of self) {
239
- if (FLATTEN.has(value))
240
- value = FLATTEN.get(value);
240
+ if (EXPORT.has(value))
241
+ value = EXPORT.get(value);
241
242
  else if (value && typeof value.get === "function")
242
- FLATTEN.set(value, value = value.get());
243
+ EXPORT.set(value, value = value.get());
243
244
  values[key] = value;
244
245
  }
245
246
  if (current)
246
- FLATTEN = void 0;
247
+ EXPORT = void 0;
247
248
  return Object.freeze(values);
248
249
  }
249
250
  if (typeof arg2 == "function") {
@@ -322,9 +323,6 @@ var Model = class _Model {
322
323
  notify.add(listener);
323
324
  return () => notify.delete(listener);
324
325
  }
325
- static at(from, cb) {
326
- throw new Error(`Using context requires an adapter. If you are only testing, define \`get.context\` to simulate one.`);
327
- }
328
326
  };
329
327
  define(Model.prototype, "toString", {
330
328
  value() {
@@ -344,14 +342,14 @@ function fetch(subject, property, required) {
344
342
  return value;
345
343
  }
346
344
  const error = new Error(`${subject}.${property} is not yet available.`);
347
- const promise = new Promise((resolve, reject2) => {
345
+ const promise = new Promise((resolve, reject) => {
348
346
  addListener(subject, (key) => {
349
347
  if (key === property) {
350
348
  resolve(state[key]);
351
349
  return null;
352
350
  }
353
351
  if (key === null)
354
- reject2(new Error(`${subject} is destroyed.`));
352
+ reject(new Error(`${subject} is destroyed.`));
355
353
  });
356
354
  });
357
355
  throw Object.assign(promise, {
@@ -368,6 +366,10 @@ function update(subject, key, value, silent) {
368
366
  const previous = state[key];
369
367
  if (value === previous)
370
368
  return true;
369
+ if (value instanceof Model && !PARENT.has(value)) {
370
+ PARENT.set(value, subject);
371
+ event(value, true);
372
+ }
371
373
  state[key] = value;
372
374
  if (silent !== true)
373
375
  push(subject, key);
@@ -424,26 +426,168 @@ function uid() {
424
426
  return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
425
427
  }
426
428
 
427
- // src/instruction/add.ts
429
+ // src/context.ts
430
+ var Register = /* @__PURE__ */ new WeakMap();
431
+ var Context = class _Context {
432
+ constructor(inputs) {
433
+ this.downstream = /* @__PURE__ */ new WeakMap();
434
+ this.upstream = /* @__PURE__ */ new WeakMap();
435
+ this.layer = /* @__PURE__ */ new Map();
436
+ if (inputs)
437
+ this.include(inputs);
438
+ }
439
+ static get(from, callback) {
440
+ const waiting = Register.get(from);
441
+ if (!callback)
442
+ return waiting instanceof _Context ? waiting : void 0;
443
+ if (waiting instanceof _Context)
444
+ callback(waiting);
445
+ else if (waiting)
446
+ waiting.push(callback);
447
+ else
448
+ Register.set(from, [callback]);
449
+ }
450
+ key(T, upstream) {
451
+ const table = upstream ? this.upstream : this.downstream;
452
+ let key = table.get(T);
453
+ if (!key) {
454
+ key = Symbol(T.name + (upstream ? " request" : ""));
455
+ table.set(T, key);
456
+ }
457
+ return key;
458
+ }
459
+ has(model) {
460
+ const key = this.key(model.constructor, true);
461
+ const result = this[key];
462
+ const waiting = Register.get(model);
463
+ if (waiting instanceof Array)
464
+ waiting.forEach((cb) => cb(this));
465
+ Register.set(model, this);
466
+ if (typeof result == "function")
467
+ return result(model);
468
+ }
469
+ get(Type) {
470
+ const result = this[this.key(Type)];
471
+ if (result === null)
472
+ throw new Error(`Did find ${Type} in context, but multiple were defined.`);
473
+ return result;
474
+ }
475
+ include(inputs, forEach) {
476
+ const init = /* @__PURE__ */ new Map();
477
+ Object.entries(inputs).forEach(([K, V]) => {
478
+ if (!(Model.is(V) || V instanceof Model))
479
+ throw new Error(`Context can only include Model or instance but got ${V}${K && " as " + K}.`);
480
+ });
481
+ for (const key in inputs) {
482
+ const input = inputs[key];
483
+ const exists = this.layer.get(key);
484
+ if (!exists) {
485
+ const instance = this.add(input);
486
+ this.layer.set(key, input);
487
+ init.set(instance, true);
488
+ } else if (exists !== input) {
489
+ this.pop();
490
+ this.id = uid();
491
+ this.include(inputs);
492
+ }
493
+ }
494
+ for (const [model, explicit] of init) {
495
+ model.set();
496
+ if (forEach)
497
+ forEach(model, explicit);
498
+ for (const [_key, value] of model)
499
+ if (PARENT.get(value) === model) {
500
+ this.add(value, true);
501
+ init.set(value, false);
502
+ }
503
+ }
504
+ }
505
+ put(T, I, implicit, writable) {
506
+ do {
507
+ const key = this.key(T, typeof I == "function");
508
+ const value = this.hasOwnProperty(key) ? null : I;
509
+ if (value || this[key] !== I && !implicit)
510
+ Object.defineProperty(this, key, {
511
+ configurable: true,
512
+ writable,
513
+ value
514
+ });
515
+ T = Object.getPrototypeOf(T);
516
+ } while (T !== Model);
517
+ }
518
+ add(input, implicit) {
519
+ let writable = true;
520
+ let T;
521
+ let I;
522
+ if (typeof input == "function") {
523
+ T = input;
524
+ I = new input();
525
+ } else {
526
+ I = input;
527
+ T = I.constructor;
528
+ writable = false;
529
+ }
530
+ this.has(I);
531
+ this.put(T, I, implicit, writable);
532
+ return I;
533
+ }
534
+ push(inputs) {
535
+ const next = Object.create(this);
536
+ next.layer = /* @__PURE__ */ new Map();
537
+ if (inputs)
538
+ next.include(inputs);
539
+ return next;
540
+ }
541
+ pop() {
542
+ const items = /* @__PURE__ */ new Set();
543
+ for (const key of Object.getOwnPropertySymbols(this)) {
544
+ const entry = Object.getOwnPropertyDescriptor(this, key);
545
+ if (entry.writable && entry.value)
546
+ items.add(entry.value);
547
+ delete this[key];
548
+ }
549
+ for (const model of items)
550
+ model.set(null);
551
+ this.layer.clear();
552
+ }
553
+ };
554
+
555
+ // src/instruction/use.ts
428
556
  var INSTRUCT = /* @__PURE__ */ new Map();
429
- function add(instruction) {
557
+ function use(model, argument) {
430
558
  const token = Symbol("instruction");
431
- INSTRUCT.set(token, instruction);
559
+ if (Model.is(model)) {
560
+ const type = model;
561
+ const value = new model();
562
+ model = (key, subject) => {
563
+ function set2(next) {
564
+ if (next ? !(next instanceof type) : argument !== false)
565
+ throw new Error(`${subject}.${key} expected Model of type ${type} but got ${next && next.constructor}.`);
566
+ update(subject, key, next);
567
+ if (next && typeof argument == "function")
568
+ argument(next);
569
+ return false;
570
+ }
571
+ set2(value);
572
+ return { set: set2, value };
573
+ };
574
+ }
575
+ INSTRUCT.set(token, model);
432
576
  return token;
433
577
  }
434
578
  Model.on((_, subject) => {
435
579
  const state = STATE.get(subject);
436
- Object.entries(
437
- Object.getOwnPropertyDescriptors(subject)
438
- ).forEach(([key, { value }]) => {
580
+ const props = Object.getOwnPropertyDescriptors(subject);
581
+ for (const key in props) {
582
+ const { value } = props[key];
439
583
  const instruction = INSTRUCT.get(value);
440
584
  if (!instruction)
441
- return;
585
+ continue;
442
586
  INSTRUCT.delete(value);
443
587
  delete subject[key];
444
588
  const output = instruction.call(subject, key, subject, state);
445
589
  if (!output)
446
- return;
590
+ continue;
447
591
  const desc = typeof output == "object" ? output : { get: output };
448
592
  const { enumerable = true } = desc;
449
593
  if ("value" in desc)
@@ -472,26 +616,28 @@ Model.on((_, subject) => {
472
616
  );
473
617
  }
474
618
  });
475
- });
619
+ }
476
620
  return null;
477
621
  });
478
622
 
479
623
  // src/instruction/get.ts
624
+ var STALE = /* @__PURE__ */ new WeakSet();
480
625
  function get(arg0, arg1) {
481
- return add((key, subject) => {
626
+ return use((key, subject, state) => {
482
627
  let from = subject;
483
628
  if (typeof arg0 == "symbol")
484
629
  throw new Error(`Attempted to use an instruction result (probably use or get) as computed source for ${from}.${key}. This is not allowed.`);
485
630
  let source = (resolve) => resolve(from);
486
- if (arg0 instanceof Model)
631
+ if (arg0 instanceof Model) {
487
632
  from = arg0;
488
- else if (Model.is(arg0)) {
633
+ } else if (Model.is(arg0)) {
489
634
  const hasParent = PARENT.get(from);
490
635
  if (!hasParent) {
491
636
  if (arg1 === true)
492
637
  throw new Error(`${from} may only exist as a child of type ${arg0}.`);
493
638
  source = (resolve) => {
494
- arg0.at(from, (got) => {
639
+ Context.get(from, (context) => {
640
+ const got = context.get(arg0);
495
641
  if (got)
496
642
  resolve(got);
497
643
  else if (arg1 !== false)
@@ -502,63 +648,59 @@ function get(arg0, arg1) {
502
648
  from = hasParent;
503
649
  else
504
650
  throw new Error(`New ${from} created as child of ${hasParent}, but must be instanceof ${arg0}.`);
505
- } else if (typeof arg0 == "function") {
651
+ } else {
506
652
  const fn = METHOD.get(arg0) || arg0;
507
653
  arg1 = (p, k) => fn.call(p, k, p);
508
654
  }
509
- if (typeof arg1 == "function")
510
- return compute(subject, key, source, arg1);
511
- source((got) => subject.set(key, got));
512
- return { get: arg1 };
513
- });
514
- }
515
- var STALE = /* @__PURE__ */ new WeakSet();
516
- function compute(subject, key, source, setter) {
517
- let reset;
518
- let isAsync;
519
- let proxy;
520
- function connect(model) {
521
- reset = effect(model, (current) => {
522
- proxy = current;
523
- if (!reset)
524
- compute2(true);
525
- else if (STALE.delete(compute2))
526
- compute2();
527
- return (didUpdate) => {
528
- if (didUpdate) {
529
- STALE.add(compute2);
530
- push(subject, key, true);
655
+ if (typeof arg1 == "function") {
656
+ let connect2 = function(model) {
657
+ reset = effect(model, (current) => {
658
+ proxy = current;
659
+ if (!(key in state) || STALE.delete(compute2))
660
+ compute2(!reset);
661
+ return (didUpdate) => {
662
+ if (didUpdate) {
663
+ STALE.add(compute2);
664
+ push(subject, key, true);
665
+ }
666
+ };
667
+ });
668
+ }, compute2 = function(initial) {
669
+ let next;
670
+ try {
671
+ next = getter.call(proxy, proxy, key);
672
+ } catch (err) {
673
+ console.warn(`An exception was thrown while ${initial ? "initializing" : "refreshing"} ${subject}.${key}.`);
674
+ if (initial)
675
+ throw err;
676
+ console.error(err);
531
677
  }
678
+ update(subject, key, next, !isAsync);
532
679
  };
533
- });
534
- }
535
- function compute2(initial) {
536
- let next;
537
- try {
538
- next = setter.call(proxy, proxy, key);
539
- } catch (err) {
540
- console.warn(`An exception was thrown while ${initial ? "initializing" : "refreshing"} ${subject}.${key}.`);
541
- if (initial)
542
- throw err;
543
- console.error(err);
544
- }
545
- update(subject, key, next, !isAsync);
546
- }
547
- return () => {
548
- if (!proxy) {
549
- source(connect);
550
- isAsync = true;
551
- }
552
- if (STALE.delete(compute2))
553
- compute2();
554
- return fetch(subject, key, !proxy);
555
- };
680
+ var connect = connect2, compute = compute2;
681
+ const getter = arg1;
682
+ let reset;
683
+ let isAsync;
684
+ let proxy;
685
+ return () => {
686
+ if (!proxy) {
687
+ source(connect2);
688
+ isAsync = true;
689
+ }
690
+ if (STALE.delete(compute2))
691
+ compute2();
692
+ return fetch(subject, key, !proxy);
693
+ };
694
+ } else
695
+ source((got) => subject.set(key, got));
696
+ return { get: arg1 };
697
+ });
556
698
  }
557
699
 
558
700
  // src/instruction/ref.ts
559
701
  var define2 = Object.defineProperty;
560
702
  function ref(arg, arg2) {
561
- return add((key, subject, state) => {
703
+ return use((key, subject, state) => {
562
704
  let value = {};
563
705
  if (arg === subject)
564
706
  subject.get(() => {
@@ -608,7 +750,7 @@ function ref(arg, arg2) {
608
750
 
609
751
  // src/instruction/set.ts
610
752
  function set(value, argument) {
611
- return add((key, subject) => {
753
+ return use((key, subject) => {
612
754
  const output = {};
613
755
  if (typeof value == "function" || value instanceof Promise) {
614
756
  let init2 = function() {
@@ -664,11 +806,11 @@ function set(value, argument) {
664
806
  function attempt(fn) {
665
807
  function retry(err) {
666
808
  if (err instanceof Promise)
667
- return err.then(compute2);
809
+ return err.then(compute);
668
810
  else
669
811
  throw err;
670
812
  }
671
- function compute2() {
813
+ function compute() {
672
814
  try {
673
815
  const output = fn();
674
816
  return output instanceof Promise ? output.catch(retry) : output;
@@ -676,202 +818,51 @@ function attempt(fn) {
676
818
  return retry(err);
677
819
  }
678
820
  }
679
- return compute2();
680
- }
681
-
682
- // src/instruction/use.ts
683
- function use(value, argument) {
684
- return add((property, subject) => {
685
- if (typeof value === "function")
686
- value = new value();
687
- function set2(next) {
688
- if (value instanceof Model && !(next instanceof value.constructor))
689
- throw new Error(`${subject}.${property} expected Model of type ${value.constructor} but got ${next}.`);
690
- if (next instanceof Model) {
691
- PARENT.set(next, subject);
692
- event(next, true);
693
- }
694
- if (typeof argument == "function")
695
- argument(next);
696
- }
697
- set2(value);
698
- return { set: set2, value };
699
- });
700
- }
701
-
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}.`);
821
+ return compute();
828
822
  }
829
823
 
830
824
  // src/instruction/has.ts
831
825
  function has(type, argument) {
832
- return add((key, subject, state) => {
826
+ return use((key, subject, state) => {
827
+ const expect = (callback) => Context.get(subject, (context) => context.put(type, callback));
833
828
  if (typeof argument == "boolean") {
834
- Context.request(type, subject, (model) => {
829
+ expect((model) => {
835
830
  subject.set(key, model);
836
831
  const remove = () => {
837
- drop();
838
832
  if (state[key] === model)
839
833
  delete state[key];
840
834
  };
841
- const drop = model.get(null, remove);
835
+ model.get(null, remove);
842
836
  return remove;
843
837
  });
844
838
  return { get: argument };
845
839
  }
846
- const children = /* @__PURE__ */ new Set();
847
- Context.request(type, subject, (model) => {
848
- if (children.has(model))
840
+ const value = /* @__PURE__ */ new Set();
841
+ expect((model) => {
842
+ if (value.has(model))
849
843
  return;
850
844
  const remove = typeof argument == "function" ? argument(model) : void 0;
851
845
  if (remove === false)
852
846
  return;
853
- children.add(model);
847
+ value.add(model);
854
848
  subject.set(key);
855
849
  const done = () => {
856
850
  drop();
857
851
  if (typeof remove == "function")
858
852
  remove();
859
- children.delete(model);
853
+ value.delete(model);
860
854
  subject.set(key);
861
855
  };
862
856
  const drop = model.get(null, done);
863
857
  return done;
864
858
  });
865
- return {
866
- value: children
867
- };
859
+ return { value };
868
860
  });
869
861
  }
870
862
  // Annotate the CommonJS export names for ESM import in node:
871
863
  0 && (module.exports = {
872
864
  Context,
873
865
  Model,
874
- add,
875
866
  get,
876
867
  has,
877
868
  ref,
package/dist/index.mjs CHANGED
@@ -117,7 +117,7 @@ function effect(target, callback) {
117
117
  }
118
118
 
119
119
  // src/model.ts
120
- var FLATTEN;
120
+ var EXPORT;
121
121
  var NAMES = /* @__PURE__ */ new WeakMap();
122
122
  var STATE = /* @__PURE__ */ new WeakMap();
123
123
  var NOTIFY = /* @__PURE__ */ new WeakMap();
@@ -163,7 +163,7 @@ var Model = class _Model {
163
163
  for (const key in this) {
164
164
  const desc = Object.getOwnPropertyDescriptor(this, key);
165
165
  if ("value" in desc) {
166
- state[key] = desc.value;
166
+ update(this, key, desc.value, true);
167
167
  define(this, key, {
168
168
  configurable: false,
169
169
  set: (x) => update(this, key, x),
@@ -173,6 +173,8 @@ var Model = class _Model {
173
173
  });
174
174
  }
175
175
  }
176
+ if (!PARENT.has(this))
177
+ PARENT.set(this, null);
176
178
  addListener(this, () => {
177
179
  if (!done)
178
180
  throw new Error(`Tried to destroy ${this} but not fully initialized.`);
@@ -200,16 +202,16 @@ var Model = class _Model {
200
202
  }
201
203
  if (arg1 === void 0) {
202
204
  const values = {};
203
- const current = !FLATTEN && (FLATTEN = /* @__PURE__ */ new Map([[self, values]]));
205
+ const current = !EXPORT && (EXPORT = /* @__PURE__ */ new Map([[self, values]]));
204
206
  for (let [key, value] of self) {
205
- if (FLATTEN.has(value))
206
- value = FLATTEN.get(value);
207
+ if (EXPORT.has(value))
208
+ value = EXPORT.get(value);
207
209
  else if (value && typeof value.get === "function")
208
- FLATTEN.set(value, value = value.get());
210
+ EXPORT.set(value, value = value.get());
209
211
  values[key] = value;
210
212
  }
211
213
  if (current)
212
- FLATTEN = void 0;
214
+ EXPORT = void 0;
213
215
  return Object.freeze(values);
214
216
  }
215
217
  if (typeof arg2 == "function") {
@@ -288,9 +290,6 @@ var Model = class _Model {
288
290
  notify.add(listener);
289
291
  return () => notify.delete(listener);
290
292
  }
291
- static at(from, cb) {
292
- throw new Error(`Using context requires an adapter. If you are only testing, define \`get.context\` to simulate one.`);
293
- }
294
293
  };
295
294
  define(Model.prototype, "toString", {
296
295
  value() {
@@ -310,14 +309,14 @@ function fetch(subject, property, required) {
310
309
  return value;
311
310
  }
312
311
  const error = new Error(`${subject}.${property} is not yet available.`);
313
- const promise = new Promise((resolve, reject2) => {
312
+ const promise = new Promise((resolve, reject) => {
314
313
  addListener(subject, (key) => {
315
314
  if (key === property) {
316
315
  resolve(state[key]);
317
316
  return null;
318
317
  }
319
318
  if (key === null)
320
- reject2(new Error(`${subject} is destroyed.`));
319
+ reject(new Error(`${subject} is destroyed.`));
321
320
  });
322
321
  });
323
322
  throw Object.assign(promise, {
@@ -334,6 +333,10 @@ function update(subject, key, value, silent) {
334
333
  const previous = state[key];
335
334
  if (value === previous)
336
335
  return true;
336
+ if (value instanceof Model && !PARENT.has(value)) {
337
+ PARENT.set(value, subject);
338
+ event(value, true);
339
+ }
337
340
  state[key] = value;
338
341
  if (silent !== true)
339
342
  push(subject, key);
@@ -390,26 +393,168 @@ function uid() {
390
393
  return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
391
394
  }
392
395
 
393
- // src/instruction/add.ts
396
+ // src/context.ts
397
+ var Register = /* @__PURE__ */ new WeakMap();
398
+ var Context = class _Context {
399
+ constructor(inputs) {
400
+ this.downstream = /* @__PURE__ */ new WeakMap();
401
+ this.upstream = /* @__PURE__ */ new WeakMap();
402
+ this.layer = /* @__PURE__ */ new Map();
403
+ if (inputs)
404
+ this.include(inputs);
405
+ }
406
+ static get(from, callback) {
407
+ const waiting = Register.get(from);
408
+ if (!callback)
409
+ return waiting instanceof _Context ? waiting : void 0;
410
+ if (waiting instanceof _Context)
411
+ callback(waiting);
412
+ else if (waiting)
413
+ waiting.push(callback);
414
+ else
415
+ Register.set(from, [callback]);
416
+ }
417
+ key(T, upstream) {
418
+ const table = upstream ? this.upstream : this.downstream;
419
+ let key = table.get(T);
420
+ if (!key) {
421
+ key = Symbol(T.name + (upstream ? " request" : ""));
422
+ table.set(T, key);
423
+ }
424
+ return key;
425
+ }
426
+ has(model) {
427
+ const key = this.key(model.constructor, true);
428
+ const result = this[key];
429
+ const waiting = Register.get(model);
430
+ if (waiting instanceof Array)
431
+ waiting.forEach((cb) => cb(this));
432
+ Register.set(model, this);
433
+ if (typeof result == "function")
434
+ return result(model);
435
+ }
436
+ get(Type) {
437
+ const result = this[this.key(Type)];
438
+ if (result === null)
439
+ throw new Error(`Did find ${Type} in context, but multiple were defined.`);
440
+ return result;
441
+ }
442
+ include(inputs, forEach) {
443
+ const init = /* @__PURE__ */ new Map();
444
+ Object.entries(inputs).forEach(([K, V]) => {
445
+ if (!(Model.is(V) || V instanceof Model))
446
+ throw new Error(`Context can only include Model or instance but got ${V}${K && " as " + K}.`);
447
+ });
448
+ for (const key in inputs) {
449
+ const input = inputs[key];
450
+ const exists = this.layer.get(key);
451
+ if (!exists) {
452
+ const instance = this.add(input);
453
+ this.layer.set(key, input);
454
+ init.set(instance, true);
455
+ } else if (exists !== input) {
456
+ this.pop();
457
+ this.id = uid();
458
+ this.include(inputs);
459
+ }
460
+ }
461
+ for (const [model, explicit] of init) {
462
+ model.set();
463
+ if (forEach)
464
+ forEach(model, explicit);
465
+ for (const [_key, value] of model)
466
+ if (PARENT.get(value) === model) {
467
+ this.add(value, true);
468
+ init.set(value, false);
469
+ }
470
+ }
471
+ }
472
+ put(T, I, implicit, writable) {
473
+ do {
474
+ const key = this.key(T, typeof I == "function");
475
+ const value = this.hasOwnProperty(key) ? null : I;
476
+ if (value || this[key] !== I && !implicit)
477
+ Object.defineProperty(this, key, {
478
+ configurable: true,
479
+ writable,
480
+ value
481
+ });
482
+ T = Object.getPrototypeOf(T);
483
+ } while (T !== Model);
484
+ }
485
+ add(input, implicit) {
486
+ let writable = true;
487
+ let T;
488
+ let I;
489
+ if (typeof input == "function") {
490
+ T = input;
491
+ I = new input();
492
+ } else {
493
+ I = input;
494
+ T = I.constructor;
495
+ writable = false;
496
+ }
497
+ this.has(I);
498
+ this.put(T, I, implicit, writable);
499
+ return I;
500
+ }
501
+ push(inputs) {
502
+ const next = Object.create(this);
503
+ next.layer = /* @__PURE__ */ new Map();
504
+ if (inputs)
505
+ next.include(inputs);
506
+ return next;
507
+ }
508
+ pop() {
509
+ const items = /* @__PURE__ */ new Set();
510
+ for (const key of Object.getOwnPropertySymbols(this)) {
511
+ const entry = Object.getOwnPropertyDescriptor(this, key);
512
+ if (entry.writable && entry.value)
513
+ items.add(entry.value);
514
+ delete this[key];
515
+ }
516
+ for (const model of items)
517
+ model.set(null);
518
+ this.layer.clear();
519
+ }
520
+ };
521
+
522
+ // src/instruction/use.ts
394
523
  var INSTRUCT = /* @__PURE__ */ new Map();
395
- function add(instruction) {
524
+ function use(model, argument) {
396
525
  const token = Symbol("instruction");
397
- INSTRUCT.set(token, instruction);
526
+ if (Model.is(model)) {
527
+ const type = model;
528
+ const value = new model();
529
+ model = (key, subject) => {
530
+ function set2(next) {
531
+ if (next ? !(next instanceof type) : argument !== false)
532
+ throw new Error(`${subject}.${key} expected Model of type ${type} but got ${next && next.constructor}.`);
533
+ update(subject, key, next);
534
+ if (next && typeof argument == "function")
535
+ argument(next);
536
+ return false;
537
+ }
538
+ set2(value);
539
+ return { set: set2, value };
540
+ };
541
+ }
542
+ INSTRUCT.set(token, model);
398
543
  return token;
399
544
  }
400
545
  Model.on((_, subject) => {
401
546
  const state = STATE.get(subject);
402
- Object.entries(
403
- Object.getOwnPropertyDescriptors(subject)
404
- ).forEach(([key, { value }]) => {
547
+ const props = Object.getOwnPropertyDescriptors(subject);
548
+ for (const key in props) {
549
+ const { value } = props[key];
405
550
  const instruction = INSTRUCT.get(value);
406
551
  if (!instruction)
407
- return;
552
+ continue;
408
553
  INSTRUCT.delete(value);
409
554
  delete subject[key];
410
555
  const output = instruction.call(subject, key, subject, state);
411
556
  if (!output)
412
- return;
557
+ continue;
413
558
  const desc = typeof output == "object" ? output : { get: output };
414
559
  const { enumerable = true } = desc;
415
560
  if ("value" in desc)
@@ -438,26 +583,28 @@ Model.on((_, subject) => {
438
583
  );
439
584
  }
440
585
  });
441
- });
586
+ }
442
587
  return null;
443
588
  });
444
589
 
445
590
  // src/instruction/get.ts
591
+ var STALE = /* @__PURE__ */ new WeakSet();
446
592
  function get(arg0, arg1) {
447
- return add((key, subject) => {
593
+ return use((key, subject, state) => {
448
594
  let from = subject;
449
595
  if (typeof arg0 == "symbol")
450
596
  throw new Error(`Attempted to use an instruction result (probably use or get) as computed source for ${from}.${key}. This is not allowed.`);
451
597
  let source = (resolve) => resolve(from);
452
- if (arg0 instanceof Model)
598
+ if (arg0 instanceof Model) {
453
599
  from = arg0;
454
- else if (Model.is(arg0)) {
600
+ } else if (Model.is(arg0)) {
455
601
  const hasParent = PARENT.get(from);
456
602
  if (!hasParent) {
457
603
  if (arg1 === true)
458
604
  throw new Error(`${from} may only exist as a child of type ${arg0}.`);
459
605
  source = (resolve) => {
460
- arg0.at(from, (got) => {
606
+ Context.get(from, (context) => {
607
+ const got = context.get(arg0);
461
608
  if (got)
462
609
  resolve(got);
463
610
  else if (arg1 !== false)
@@ -468,63 +615,59 @@ function get(arg0, arg1) {
468
615
  from = hasParent;
469
616
  else
470
617
  throw new Error(`New ${from} created as child of ${hasParent}, but must be instanceof ${arg0}.`);
471
- } else if (typeof arg0 == "function") {
618
+ } else {
472
619
  const fn = METHOD.get(arg0) || arg0;
473
620
  arg1 = (p, k) => fn.call(p, k, p);
474
621
  }
475
- if (typeof arg1 == "function")
476
- return compute(subject, key, source, arg1);
477
- source((got) => subject.set(key, got));
478
- return { get: arg1 };
479
- });
480
- }
481
- var STALE = /* @__PURE__ */ new WeakSet();
482
- function compute(subject, key, source, setter) {
483
- let reset;
484
- let isAsync;
485
- let proxy;
486
- function connect(model) {
487
- reset = effect(model, (current) => {
488
- proxy = current;
489
- if (!reset)
490
- compute2(true);
491
- else if (STALE.delete(compute2))
492
- compute2();
493
- return (didUpdate) => {
494
- if (didUpdate) {
495
- STALE.add(compute2);
496
- push(subject, key, true);
622
+ if (typeof arg1 == "function") {
623
+ let connect2 = function(model) {
624
+ reset = effect(model, (current) => {
625
+ proxy = current;
626
+ if (!(key in state) || STALE.delete(compute2))
627
+ compute2(!reset);
628
+ return (didUpdate) => {
629
+ if (didUpdate) {
630
+ STALE.add(compute2);
631
+ push(subject, key, true);
632
+ }
633
+ };
634
+ });
635
+ }, compute2 = function(initial) {
636
+ let next;
637
+ try {
638
+ next = getter.call(proxy, proxy, key);
639
+ } catch (err) {
640
+ console.warn(`An exception was thrown while ${initial ? "initializing" : "refreshing"} ${subject}.${key}.`);
641
+ if (initial)
642
+ throw err;
643
+ console.error(err);
497
644
  }
645
+ update(subject, key, next, !isAsync);
498
646
  };
499
- });
500
- }
501
- function compute2(initial) {
502
- let next;
503
- try {
504
- next = setter.call(proxy, proxy, key);
505
- } catch (err) {
506
- console.warn(`An exception was thrown while ${initial ? "initializing" : "refreshing"} ${subject}.${key}.`);
507
- if (initial)
508
- throw err;
509
- console.error(err);
510
- }
511
- update(subject, key, next, !isAsync);
512
- }
513
- return () => {
514
- if (!proxy) {
515
- source(connect);
516
- isAsync = true;
517
- }
518
- if (STALE.delete(compute2))
519
- compute2();
520
- return fetch(subject, key, !proxy);
521
- };
647
+ var connect = connect2, compute = compute2;
648
+ const getter = arg1;
649
+ let reset;
650
+ let isAsync;
651
+ let proxy;
652
+ return () => {
653
+ if (!proxy) {
654
+ source(connect2);
655
+ isAsync = true;
656
+ }
657
+ if (STALE.delete(compute2))
658
+ compute2();
659
+ return fetch(subject, key, !proxy);
660
+ };
661
+ } else
662
+ source((got) => subject.set(key, got));
663
+ return { get: arg1 };
664
+ });
522
665
  }
523
666
 
524
667
  // src/instruction/ref.ts
525
668
  var define2 = Object.defineProperty;
526
669
  function ref(arg, arg2) {
527
- return add((key, subject, state) => {
670
+ return use((key, subject, state) => {
528
671
  let value = {};
529
672
  if (arg === subject)
530
673
  subject.get(() => {
@@ -574,7 +717,7 @@ function ref(arg, arg2) {
574
717
 
575
718
  // src/instruction/set.ts
576
719
  function set(value, argument) {
577
- return add((key, subject) => {
720
+ return use((key, subject) => {
578
721
  const output = {};
579
722
  if (typeof value == "function" || value instanceof Promise) {
580
723
  let init2 = function() {
@@ -630,11 +773,11 @@ function set(value, argument) {
630
773
  function attempt(fn) {
631
774
  function retry(err) {
632
775
  if (err instanceof Promise)
633
- return err.then(compute2);
776
+ return err.then(compute);
634
777
  else
635
778
  throw err;
636
779
  }
637
- function compute2() {
780
+ function compute() {
638
781
  try {
639
782
  const output = fn();
640
783
  return output instanceof Promise ? output.catch(retry) : output;
@@ -642,201 +785,50 @@ function attempt(fn) {
642
785
  return retry(err);
643
786
  }
644
787
  }
645
- return compute2();
646
- }
647
-
648
- // src/instruction/use.ts
649
- function use(value, argument) {
650
- return add((property, subject) => {
651
- if (typeof value === "function")
652
- value = new value();
653
- function set2(next) {
654
- if (value instanceof Model && !(next instanceof value.constructor))
655
- throw new Error(`${subject}.${property} expected Model of type ${value.constructor} but got ${next}.`);
656
- if (next instanceof Model) {
657
- PARENT.set(next, subject);
658
- event(next, true);
659
- }
660
- if (typeof argument == "function")
661
- argument(next);
662
- }
663
- set2(value);
664
- return { set: set2, value };
665
- });
666
- }
667
-
668
- // src/context.ts
669
- var Expects = /* @__PURE__ */ new WeakMap();
670
- var Context = class {
671
- constructor(inputs) {
672
- this.downstream = /* @__PURE__ */ new WeakMap();
673
- this.upstream = /* @__PURE__ */ new WeakMap();
674
- this.layer = /* @__PURE__ */ new Map();
675
- if (inputs)
676
- this.include(inputs);
677
- }
678
- static request(type, from, callback) {
679
- let map = Expects.get(from);
680
- if (!map)
681
- Expects.set(from, map = /* @__PURE__ */ new Map());
682
- map.set(type, callback);
683
- }
684
- key(T, upstream) {
685
- const table = upstream ? this.upstream : this.downstream;
686
- let key = table.get(T);
687
- if (!key) {
688
- key = Symbol(T.name + (upstream ? " request" : ""));
689
- table.set(T, key);
690
- }
691
- return key;
692
- }
693
- has(model) {
694
- const key = this.key(model.constructor, true);
695
- const result = this[key];
696
- if (typeof result == "function")
697
- return result(model);
698
- }
699
- get(Type) {
700
- const result = this[this.key(Type)];
701
- if (result === null)
702
- throw new Error(`Did find ${Type} in context, but multiple were defined.`);
703
- return result;
704
- }
705
- include(inputs, forEach) {
706
- const init = /* @__PURE__ */ new Map();
707
- if (typeof inputs == "function" || inputs instanceof Model)
708
- inputs = { [0]: inputs };
709
- else if (!inputs)
710
- reject(inputs);
711
- Object.entries(inputs).forEach(([K, V]) => {
712
- if (!(Model.is(V) || V instanceof Model))
713
- reject(`${V} as ${K}`);
714
- });
715
- for (const key in inputs) {
716
- const input = inputs[key];
717
- const exists = this.layer.get(key);
718
- if (!exists) {
719
- const instance = this.add(input);
720
- this.layer.set(key, input);
721
- init.set(instance, true);
722
- } else if (exists !== input) {
723
- this.pop();
724
- this.id = uid();
725
- this.include(inputs);
726
- }
727
- }
728
- for (const [model, explicit] of init) {
729
- if (forEach)
730
- forEach(model, explicit);
731
- model.set();
732
- this.has(model);
733
- const expects = Expects.get(model);
734
- if (expects)
735
- for (let [T, callback] of expects)
736
- this.put(T, callback);
737
- for (const [_key, value] of model)
738
- if (PARENT.get(value) === model) {
739
- this.add(value, true);
740
- init.set(value, false);
741
- }
742
- }
743
- }
744
- put(T, I, implicit, writable) {
745
- do {
746
- const key = this.key(T, typeof I == "function");
747
- const value = this.hasOwnProperty(key) ? null : I;
748
- if (value || this[key] !== I && !implicit)
749
- Object.defineProperty(this, key, {
750
- configurable: true,
751
- writable,
752
- value
753
- });
754
- T = Object.getPrototypeOf(T);
755
- } while (T !== Model);
756
- }
757
- add(input, implicit) {
758
- let writable = true;
759
- let T;
760
- let I;
761
- if (typeof input == "function") {
762
- T = input;
763
- I = new input();
764
- } else {
765
- I = input;
766
- T = I.constructor;
767
- writable = false;
768
- }
769
- this.put(T, I, implicit, writable);
770
- return I;
771
- }
772
- push(inputs) {
773
- const next = Object.create(this);
774
- next.layer = /* @__PURE__ */ new Map();
775
- if (inputs)
776
- next.include(inputs);
777
- return next;
778
- }
779
- pop() {
780
- const items = /* @__PURE__ */ new Set();
781
- for (const key of Object.getOwnPropertySymbols(this)) {
782
- const entry = Object.getOwnPropertyDescriptor(this, key);
783
- if (entry.writable && entry.value)
784
- items.add(entry.value);
785
- delete this[key];
786
- }
787
- for (const model of items)
788
- model.set(null);
789
- this.layer.clear();
790
- }
791
- };
792
- function reject(argument) {
793
- throw new Error(`Context can only include Model or instance but got ${argument}.`);
788
+ return compute();
794
789
  }
795
790
 
796
791
  // src/instruction/has.ts
797
792
  function has(type, argument) {
798
- return add((key, subject, state) => {
793
+ return use((key, subject, state) => {
794
+ const expect = (callback) => Context.get(subject, (context) => context.put(type, callback));
799
795
  if (typeof argument == "boolean") {
800
- Context.request(type, subject, (model) => {
796
+ expect((model) => {
801
797
  subject.set(key, model);
802
798
  const remove = () => {
803
- drop();
804
799
  if (state[key] === model)
805
800
  delete state[key];
806
801
  };
807
- const drop = model.get(null, remove);
802
+ model.get(null, remove);
808
803
  return remove;
809
804
  });
810
805
  return { get: argument };
811
806
  }
812
- const children = /* @__PURE__ */ new Set();
813
- Context.request(type, subject, (model) => {
814
- if (children.has(model))
807
+ const value = /* @__PURE__ */ new Set();
808
+ expect((model) => {
809
+ if (value.has(model))
815
810
  return;
816
811
  const remove = typeof argument == "function" ? argument(model) : void 0;
817
812
  if (remove === false)
818
813
  return;
819
- children.add(model);
814
+ value.add(model);
820
815
  subject.set(key);
821
816
  const done = () => {
822
817
  drop();
823
818
  if (typeof remove == "function")
824
819
  remove();
825
- children.delete(model);
820
+ value.delete(model);
826
821
  subject.set(key);
827
822
  };
828
823
  const drop = model.get(null, done);
829
824
  return done;
830
825
  });
831
- return {
832
- value: children
833
- };
826
+ return { value };
834
827
  });
835
828
  }
836
829
  export {
837
830
  Context,
838
831
  Model,
839
- add,
840
832
  Model as default,
841
833
  get,
842
834
  has,
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.46.0",
32
- "gitHead": "22ffbbadad0ab45f73c71e178183941e78ed2a56"
31
+ "version": "0.48.0",
32
+ "gitHead": "1f5ddf82e800cf53bf867ac5e546ad3186ce8bf5"
33
33
  }