atom.io 0.1.0 → 0.2.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.
@@ -58,33 +58,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
58
58
  // src/react/index.ts
59
59
  var react_exports = {};
60
60
  __export(react_exports, {
61
- composeStoreHook: () => composeStoreHook
61
+ composeStoreHooks: () => composeStoreHooks
62
62
  });
63
63
  module.exports = __toCommonJS(react_exports);
64
64
 
65
- // src/react/useSubject.ts
66
- var eq = (oldValue, newValue) => oldValue === newValue;
67
- var composeSubjectHook = (useState, useEffect) => {
68
- function useSubject(subject, initialValue, compareFn = eq) {
69
- const [state, setState2] = useState(initialValue);
70
- useEffect(() => {
71
- const subscription = subject.subscribe(({ newValue, oldValue }) => {
72
- if (!compareFn(oldValue, newValue)) {
73
- setState2(newValue);
74
- }
75
- });
76
- return () => {
77
- subscription.unsubscribe();
78
- };
79
- }, [state, compareFn, subject]);
80
- const updateState = (newValue) => {
81
- subject.next({ newValue, oldValue: state });
82
- };
83
- return [state, updateState];
84
- }
85
- return useSubject;
86
- };
87
-
88
65
  // src/internal/index.ts
89
66
  var internal_exports = {};
90
67
  __export(internal_exports, {
@@ -94,24 +71,34 @@ __export(internal_exports, {
94
71
  configure: () => configure,
95
72
  createStore: () => createStore,
96
73
  deposit: () => deposit,
74
+ evictDownStream: () => evictDownStream,
97
75
  finishAction: () => finishAction,
98
76
  finishTransaction: () => finishTransaction,
99
77
  getCachedState: () => getCachedState,
100
78
  getSelectorState: () => getSelectorState,
101
79
  getState__INTERNAL: () => getState__INTERNAL,
80
+ isAtomDefault: () => isAtomDefault,
102
81
  isDone: () => isDone,
82
+ isSelectorDefault: () => isSelectorDefault,
83
+ lookup: () => lookup,
84
+ lookupSelectorSources: () => lookupSelectorSources,
103
85
  markDone: () => markDone,
104
- propagateDown: () => propagateDown,
105
- recall: () => recall,
86
+ recallState: () => recallState,
87
+ registerSelector: () => registerSelector,
106
88
  setAtomState: () => setAtomState,
107
89
  setSelectorState: () => setSelectorState,
108
90
  setState__INTERNAL: () => setState__INTERNAL,
109
91
  startAction: () => startAction,
110
92
  startTransaction: () => startTransaction,
93
+ subscribeToRootAtoms: () => subscribeToRootAtoms,
94
+ traceAllSelectorAtoms: () => traceAllSelectorAtoms,
95
+ traceSelectorAtoms: () => traceSelectorAtoms,
96
+ updateSelectorAtoms: () => updateSelectorAtoms,
111
97
  withdraw: () => withdraw
112
98
  });
113
99
 
114
100
  // src/internal/get.ts
101
+ var import_function7 = require("fp-ts/function");
115
102
  var import_hamt_plus2 = __toESM(require("hamt_plus"));
116
103
 
117
104
  // src/internal/store.ts
@@ -507,7 +494,9 @@ var Join = class {
507
494
  var createStore = (name) => ({
508
495
  valueMap: import_hamt_plus.default.make(),
509
496
  selectorGraph: new Join({ relationType: `n:n` }),
497
+ selectorAtoms: new Join({ relationType: `n:n` }),
510
498
  atoms: import_hamt_plus.default.make(),
499
+ atomsAreDefault: import_hamt_plus.default.make(),
511
500
  selectors: import_hamt_plus.default.make(),
512
501
  readonlySelectors: import_hamt_plus.default.make(),
513
502
  operation: {
@@ -539,10 +528,25 @@ var clearStore = (store = IMPLICIT.STORE) => {
539
528
 
540
529
  // src/internal/get.ts
541
530
  var getCachedState = (state, store = IMPLICIT.STORE) => {
531
+ const path = [];
532
+ if (`default` in state) {
533
+ const atomKey = state.key;
534
+ store.selectorAtoms = (0, import_function7.pipe)(store.selectorAtoms, (oldValue) => {
535
+ let newValue = oldValue;
536
+ for (const selectorKey of path) {
537
+ newValue = newValue.set(selectorKey, atomKey);
538
+ }
539
+ return newValue;
540
+ });
541
+ }
542
542
  const value = import_hamt_plus2.default.get(state.key, store.valueMap);
543
543
  return value;
544
544
  };
545
545
  var getSelectorState = (selector) => selector.get();
546
+ function lookup(key, store) {
547
+ const type = import_hamt_plus2.default.has(key, store.atoms) ? `atom` : import_hamt_plus2.default.has(key, store.selectors) ? `selector` : `readonly_selector`;
548
+ return { key, type };
549
+ }
546
550
  function withdraw(token, store) {
547
551
  var _a, _b;
548
552
  return (_b = (_a = import_hamt_plus2.default.get(token.key, store.atoms)) != null ? _a : import_hamt_plus2.default.get(token.key, store.selectors)) != null ? _b : import_hamt_plus2.default.get(token.key, store.readonlySelectors);
@@ -557,14 +561,16 @@ function deposit(state) {
557
561
  return { key: state.key, type: `atom` };
558
562
  }
559
563
  var getState__INTERNAL = (state, store = IMPLICIT.STORE) => {
560
- var _a;
564
+ var _a, _b, _c;
561
565
  if (import_hamt_plus2.default.has(state.key, store.valueMap)) {
566
+ (_a = store.config.logger) == null ? void 0 : _a.info(`>> read "${state.key}"`);
562
567
  return getCachedState(state, store);
563
568
  }
564
569
  if (`get` in state) {
570
+ (_b = store.config.logger) == null ? void 0 : _b.info(`-> calc "${state.key}"`);
565
571
  return getSelectorState(state);
566
572
  }
567
- (_a = store.config.logger) == null ? void 0 : _a.error(
573
+ (_c = store.config.logger) == null ? void 0 : _c.error(
568
574
  `Attempted to get atom "${state.key}", which was never initialized in store "${store.config.name}".`
569
575
  );
570
576
  return state.default;
@@ -582,12 +588,12 @@ var startAction = (store) => {
582
588
  done: /* @__PURE__ */ new Set(),
583
589
  prev: store.valueMap
584
590
  };
585
- (_a = store.config.logger) == null ? void 0 : _a.info(`\u2610`, `operation start`);
591
+ (_a = store.config.logger) == null ? void 0 : _a.info(`\u2B55`, `operation start`);
586
592
  };
587
593
  var finishAction = (store) => {
588
594
  var _a;
589
595
  store.operation = { open: false };
590
- (_a = store.config.logger) == null ? void 0 : _a.info(`\u2611\uFE0F`, `operation done`);
596
+ (_a = store.config.logger) == null ? void 0 : _a.info(`\u{1F534}`, `operation done`);
591
597
  };
592
598
  var isDone = (key, store = IMPLICIT.STORE) => {
593
599
  var _a;
@@ -609,7 +615,7 @@ var markDone = (key, store = IMPLICIT.STORE) => {
609
615
  }
610
616
  store.operation.done.add(key);
611
617
  };
612
- var recall = (state, store = IMPLICIT.STORE) => {
618
+ var recallState = (state, store = IMPLICIT.STORE) => {
613
619
  var _a;
614
620
  if (!store.operation.open) {
615
621
  (_a = store.config.logger) == null ? void 0 : _a.warn(
@@ -621,87 +627,60 @@ var recall = (state, store = IMPLICIT.STORE) => {
621
627
  };
622
628
 
623
629
  // src/internal/set.ts
624
- var propagateDown = (state, store = IMPLICIT.STORE) => {
630
+ var evictDownStream = (state, store = IMPLICIT.STORE) => {
625
631
  var _a, _b;
626
- const relatedStateKeys = store.selectorGraph.getRelations(state.key);
632
+ const downstream = store.selectorAtoms.getRelations(state.key);
633
+ const downstreamKeys = downstream.map(({ id }) => id);
627
634
  (_a = store.config.logger) == null ? void 0 : _a.info(
628
- ` ||`,
629
- `bumping`,
630
- relatedStateKeys.length,
631
- `states:`,
632
- relatedStateKeys.map(({ id }) => id)
635
+ ` || ${downstreamKeys.length} downstream:`,
636
+ downstreamKeys
633
637
  );
634
638
  if (store.operation.open) {
635
- (_b = store.config.logger) == null ? void 0 : _b.info(` ||`, `done:`, store.operation.done);
639
+ (_b = store.config.logger) == null ? void 0 : _b.info(` ||`, [...store.operation.done], `already done`);
636
640
  }
637
- relatedStateKeys.forEach(({ id: stateKey }) => {
638
- var _a2, _b2, _c, _d, _e;
641
+ downstream.forEach(({ id: stateKey }) => {
642
+ var _a2, _b2, _c, _d;
639
643
  if (isDone(stateKey, store)) {
640
- (_a2 = store.config.logger) == null ? void 0 : _a2.info(` ||`, stateKey, `already done`);
644
+ (_a2 = store.config.logger) == null ? void 0 : _a2.info(` || ${stateKey} already done`);
641
645
  return;
642
646
  }
643
- (_b2 = store.config.logger) == null ? void 0 : _b2.info(`->`, `bumping`, stateKey);
644
- const state2 = (_c = import_hamt_plus4.default.get(stateKey, store.selectors)) != null ? _c : import_hamt_plus4.default.get(stateKey, store.readonlySelectors);
647
+ const state2 = (_b2 = import_hamt_plus4.default.get(stateKey, store.selectors)) != null ? _b2 : import_hamt_plus4.default.get(stateKey, store.readonlySelectors);
645
648
  if (!state2) {
646
- (_d = store.config.logger) == null ? void 0 : _d.info(
647
- ` ||`,
648
- stateKey,
649
- `is an atom - no need to propagate down`
649
+ (_c = store.config.logger) == null ? void 0 : _c.info(
650
+ ` || ${stateKey} is an atom, and can't be downstream`
650
651
  );
651
652
  return;
652
653
  }
653
654
  store.valueMap = import_hamt_plus4.default.remove(stateKey, store.valueMap);
654
- const newValue = getState__INTERNAL(state2, store);
655
- (_e = store.config.logger) == null ? void 0 : _e.info(` <-`, stateKey, `became`, newValue);
656
- const oldValue = recall(state2, store);
657
- state2.subject.next({ newValue, oldValue });
655
+ (_d = store.config.logger) == null ? void 0 : _d.info(` xx evicted "${stateKey}"`);
658
656
  markDone(stateKey, store);
659
- if (`set` in state2)
660
- propagateDown(state2, store);
661
657
  });
662
658
  };
663
659
  var setAtomState = (atom, next, store = IMPLICIT.STORE) => {
664
660
  var _a, _b;
665
661
  const oldValue = getState__INTERNAL(atom, store);
666
662
  const newValue = become(next)(oldValue);
667
- (_a = store.config.logger) == null ? void 0 : _a.info(
668
- `->`,
669
- `setting atom`,
670
- `"${atom.key}"`,
671
- `to`,
672
- newValue
673
- );
663
+ (_a = store.config.logger) == null ? void 0 : _a.info(`-> setting atom "${atom.key}" to`, newValue);
674
664
  store.valueMap = import_hamt_plus4.default.set(atom.key, newValue, store.valueMap);
665
+ if (isAtomDefault(atom.key)) {
666
+ store.atomsAreDefault = import_hamt_plus4.default.set(atom.key, false, store.atomsAreDefault);
667
+ }
675
668
  markDone(atom.key, store);
676
- atom.subject.next({ newValue, oldValue });
677
669
  (_b = store.config.logger) == null ? void 0 : _b.info(
678
- ` ||`,
679
- `propagating change made to`,
680
- `"${atom.key}"`
670
+ ` || evicting caches downstream from "${atom.key}"`
681
671
  );
682
- propagateDown(atom, store);
672
+ evictDownStream(atom, store);
673
+ atom.subject.next({ newValue, oldValue });
683
674
  };
684
675
  var setSelectorState = (selector, next, store = IMPLICIT.STORE) => {
685
676
  var _a, _b;
686
677
  const oldValue = getState__INTERNAL(selector, store);
687
678
  const newValue = become(next)(oldValue);
688
- (_a = store.config.logger) == null ? void 0 : _a.info(
689
- `->`,
690
- `setting selector`,
691
- `"${selector.key}"`,
692
- `to`,
693
- newValue
694
- );
695
- (_b = store.config.logger) == null ? void 0 : _b.info(
696
- ` ||`,
697
- `propagating change made to`,
698
- `"${selector.key}"`
699
- );
679
+ (_a = store.config.logger) == null ? void 0 : _a.info(`-> setting selector "${selector.key}" to`, newValue);
680
+ (_b = store.config.logger) == null ? void 0 : _b.info(` || propagating change made to "${selector.key}"`);
700
681
  selector.set(newValue);
701
- propagateDown(selector, store);
702
682
  };
703
- var setState__INTERNAL = (token, value, store = IMPLICIT.STORE) => {
704
- const state = withdraw(token, store);
683
+ var setState__INTERNAL = (state, value, store = IMPLICIT.STORE) => {
705
684
  if (`set` in state) {
706
685
  setSelectorState(state, value, store);
707
686
  } else {
@@ -709,6 +688,114 @@ var setState__INTERNAL = (token, value, store = IMPLICIT.STORE) => {
709
688
  }
710
689
  };
711
690
 
691
+ // src/internal/is-default.ts
692
+ var import_hamt_plus5 = __toESM(require("hamt_plus"));
693
+ var isAtomDefault = (key, store = IMPLICIT.STORE) => {
694
+ return import_hamt_plus5.default.get(key, store.atomsAreDefault);
695
+ };
696
+ var isSelectorDefault = (key, store = IMPLICIT.STORE) => {
697
+ const roots = traceAllSelectorAtoms(key, store);
698
+ return roots.every((root) => isAtomDefault(root.key, store));
699
+ };
700
+
701
+ // src/internal/selector-internal.ts
702
+ var lookupSelectorSources = (key, store) => store.selectorGraph.getRelations(key).filter(({ source }) => source !== key).map(({ source }) => lookup(source, store));
703
+ var traceSelectorAtoms = (selectorKey, dependency, store) => {
704
+ const roots = [];
705
+ const sources = lookupSelectorSources(dependency.key, store);
706
+ let depth = 0;
707
+ while (sources.length > 0) {
708
+ const source = sources.shift();
709
+ ++depth;
710
+ if (depth > 999) {
711
+ throw new Error(
712
+ `Maximum selector dependency depth exceeded in selector "${selectorKey}".`
713
+ );
714
+ }
715
+ if (source.type !== `atom`) {
716
+ sources.push(...lookupSelectorSources(source.key, store));
717
+ } else {
718
+ roots.push(source);
719
+ }
720
+ }
721
+ return roots;
722
+ };
723
+ var traceAllSelectorAtoms = (selectorKey, store) => {
724
+ const sources = lookupSelectorSources(selectorKey, store);
725
+ return sources.flatMap(
726
+ (source) => source.type === `atom` ? source : traceSelectorAtoms(selectorKey, source, store)
727
+ );
728
+ };
729
+ var updateSelectorAtoms = (selectorKey, dependency, store) => {
730
+ var _a, _b;
731
+ if (dependency.type === `atom`) {
732
+ store.selectorAtoms = store.selectorAtoms.set(selectorKey, dependency.key);
733
+ (_a = store.config.logger) == null ? void 0 : _a.info(
734
+ ` || adding root for "${selectorKey}": ${dependency.key}`
735
+ );
736
+ return;
737
+ }
738
+ const roots = traceSelectorAtoms(selectorKey, dependency, store);
739
+ (_b = store.config.logger) == null ? void 0 : _b.info(` || adding roots for "${selectorKey}":`, roots);
740
+ for (const root of roots) {
741
+ store.selectorAtoms = store.selectorAtoms.set(selectorKey, root.key);
742
+ }
743
+ };
744
+ var registerSelector = (selectorKey, store = IMPLICIT.STORE) => ({
745
+ get: (dependency) => {
746
+ var _a, _b;
747
+ const alreadyRegistered = store.selectorGraph.getRelations(selectorKey).some(({ source }) => source === dependency.key);
748
+ const dependencyState = withdraw(dependency, store);
749
+ const dependencyValue = getState__INTERNAL(dependencyState, store);
750
+ if (alreadyRegistered) {
751
+ (_a = store.config.logger) == null ? void 0 : _a.info(
752
+ ` || ${selectorKey} <- ${dependency.key} =`,
753
+ dependencyValue
754
+ );
755
+ } else {
756
+ (_b = store.config.logger) == null ? void 0 : _b.info(
757
+ `\u{1F50C} registerSelector "${selectorKey}" <- "${dependency.key}" =`,
758
+ dependencyValue
759
+ );
760
+ store.selectorGraph = store.selectorGraph.set(
761
+ selectorKey,
762
+ dependency.key,
763
+ {
764
+ source: dependency.key
765
+ }
766
+ );
767
+ }
768
+ updateSelectorAtoms(selectorKey, dependency, store);
769
+ return dependencyValue;
770
+ },
771
+ set: (stateToken, newValue) => {
772
+ const state = withdraw(stateToken, store);
773
+ setState__INTERNAL(state, newValue, store);
774
+ }
775
+ });
776
+
777
+ // src/internal/subscribe-internal.ts
778
+ var subscribeToRootAtoms = (state, store = IMPLICIT.STORE) => {
779
+ const dependencySubscriptions = `default` in state ? null : traceAllSelectorAtoms(state.key, store).map((atomToken) => {
780
+ const atom = withdraw(atomToken, store);
781
+ return atom.subject.subscribe((atomChange) => {
782
+ var _a, _b;
783
+ (_a = store.config.logger) == null ? void 0 : _a.info(
784
+ `\u{1F4E2} atom changed: "${atomToken.key}" (`,
785
+ atomChange.oldValue,
786
+ `->`,
787
+ atomChange.newValue,
788
+ `) re-evaluating "${state.key}"`
789
+ );
790
+ const oldValue = recallState(state, store);
791
+ const newValue = getState__INTERNAL(state, store);
792
+ (_b = store.config.logger) == null ? void 0 : _b.info(` <- ${state.key} became`, newValue);
793
+ state.subject.next({ newValue, oldValue });
794
+ });
795
+ });
796
+ return dependencySubscriptions;
797
+ };
798
+
712
799
  // src/internal/transaction-internal.ts
713
800
  var finishTransaction = (store) => {
714
801
  var _a;
@@ -747,34 +834,76 @@ var abortTransaction = (store) => {
747
834
  };
748
835
 
749
836
  // src/index.ts
750
- var setState = (state, value, store = IMPLICIT.STORE) => {
837
+ var setState = (token, value, store = IMPLICIT.STORE) => {
751
838
  startAction(store);
839
+ const state = withdraw(token, store);
752
840
  setState__INTERNAL(state, value, store);
753
841
  finishAction(store);
754
842
  };
843
+ var subscribe = (token, observe, store = IMPLICIT.STORE) => {
844
+ var _a;
845
+ const state = withdraw(token, store);
846
+ const subscription = state.subject.subscribe(observe);
847
+ (_a = store.config.logger) == null ? void 0 : _a.info(`\u{1F440} subscribe to "${state.key}"`);
848
+ const dependencySubscriptions = subscribeToRootAtoms(state, store);
849
+ const unsubscribe = dependencySubscriptions === null ? () => {
850
+ var _a2;
851
+ (_a2 = store.config.logger) == null ? void 0 : _a2.info(`\u{1F648} unsubscribe from "${state.key}"`);
852
+ subscription.unsubscribe();
853
+ } : () => {
854
+ var _a2;
855
+ (_a2 = store.config.logger) == null ? void 0 : _a2.info(
856
+ `\u{1F648} unsubscribe from "${state.key}" and its dependencies`
857
+ );
858
+ subscription.unsubscribe();
859
+ for (const dependencySubscription of dependencySubscriptions) {
860
+ dependencySubscription.unsubscribe();
861
+ }
862
+ };
863
+ return unsubscribe;
864
+ };
755
865
 
756
866
  // src/react/index.ts
757
- var composeStoreHook = ({
867
+ var composeStoreHooks = ({
758
868
  useState,
759
869
  useEffect,
760
870
  store = internal_exports.IMPLICIT.STORE
761
871
  }) => {
762
- const useSubject = composeSubjectHook(useState, useEffect);
763
- function useStore(token) {
764
- const { type } = token;
872
+ function useI(token) {
873
+ const updateState = (next) => setState(token, next, store);
874
+ return updateState;
875
+ }
876
+ function useO(token) {
765
877
  const state = withdraw(token, store);
766
878
  const initialValue = internal_exports.getState__INTERNAL(state, store);
767
- const [value] = useSubject(state.subject, initialValue);
768
- if (type === `readonly_selector`) {
769
- return value;
879
+ const [current, dispatch] = useState(initialValue);
880
+ useEffect(() => {
881
+ const unsubscribe = subscribe(
882
+ token,
883
+ ({ newValue, oldValue }) => {
884
+ if (oldValue !== newValue) {
885
+ dispatch(newValue);
886
+ }
887
+ },
888
+ store
889
+ );
890
+ return unsubscribe;
891
+ }, [current, dispatch]);
892
+ return current;
893
+ }
894
+ function useIO(token) {
895
+ return [useO(token), useI(token)];
896
+ }
897
+ function useStore(token) {
898
+ if (token.type === `readonly_selector`) {
899
+ return useO(token);
770
900
  }
771
- const updateState = (next) => setState(token, next, store);
772
- return [value, updateState];
901
+ return useIO(token);
773
902
  }
774
- return { useStore };
903
+ return { useI, useO, useIO, useStore };
775
904
  };
776
905
  // Annotate the CommonJS export names for ESM import in node:
777
906
  0 && (module.exports = {
778
- composeStoreHook
907
+ composeStoreHooks
779
908
  });
780
909
  //# sourceMappingURL=index.js.map