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.
@@ -34,29 +34,6 @@ var __export = (target, all) => {
34
34
  __defProp(target, name, { get: all[name], enumerable: true });
35
35
  };
36
36
 
37
- // src/react/useSubject.ts
38
- var eq = (oldValue, newValue) => oldValue === newValue;
39
- var composeSubjectHook = (useState, useEffect) => {
40
- function useSubject(subject, initialValue, compareFn = eq) {
41
- const [state, setState2] = useState(initialValue);
42
- useEffect(() => {
43
- const subscription = subject.subscribe(({ newValue, oldValue }) => {
44
- if (!compareFn(oldValue, newValue)) {
45
- setState2(newValue);
46
- }
47
- });
48
- return () => {
49
- subscription.unsubscribe();
50
- };
51
- }, [state, compareFn, subject]);
52
- const updateState = (newValue) => {
53
- subject.next({ newValue, oldValue: state });
54
- };
55
- return [state, updateState];
56
- }
57
- return useSubject;
58
- };
59
-
60
37
  // src/internal/index.ts
61
38
  var internal_exports = {};
62
39
  __export(internal_exports, {
@@ -66,24 +43,34 @@ __export(internal_exports, {
66
43
  configure: () => configure,
67
44
  createStore: () => createStore,
68
45
  deposit: () => deposit,
46
+ evictDownStream: () => evictDownStream,
69
47
  finishAction: () => finishAction,
70
48
  finishTransaction: () => finishTransaction,
71
49
  getCachedState: () => getCachedState,
72
50
  getSelectorState: () => getSelectorState,
73
51
  getState__INTERNAL: () => getState__INTERNAL,
52
+ isAtomDefault: () => isAtomDefault,
74
53
  isDone: () => isDone,
54
+ isSelectorDefault: () => isSelectorDefault,
55
+ lookup: () => lookup,
56
+ lookupSelectorSources: () => lookupSelectorSources,
75
57
  markDone: () => markDone,
76
- propagateDown: () => propagateDown,
77
- recall: () => recall,
58
+ recallState: () => recallState,
59
+ registerSelector: () => registerSelector,
78
60
  setAtomState: () => setAtomState,
79
61
  setSelectorState: () => setSelectorState,
80
62
  setState__INTERNAL: () => setState__INTERNAL,
81
63
  startAction: () => startAction,
82
64
  startTransaction: () => startTransaction,
65
+ subscribeToRootAtoms: () => subscribeToRootAtoms,
66
+ traceAllSelectorAtoms: () => traceAllSelectorAtoms,
67
+ traceSelectorAtoms: () => traceSelectorAtoms,
68
+ updateSelectorAtoms: () => updateSelectorAtoms,
83
69
  withdraw: () => withdraw
84
70
  });
85
71
 
86
72
  // src/internal/get.ts
73
+ import { pipe as pipe6 } from "fp-ts/function";
87
74
  import HAMT2 from "hamt_plus";
88
75
 
89
76
  // src/internal/store.ts
@@ -479,7 +466,9 @@ var Join = class {
479
466
  var createStore = (name) => ({
480
467
  valueMap: HAMT.make(),
481
468
  selectorGraph: new Join({ relationType: `n:n` }),
469
+ selectorAtoms: new Join({ relationType: `n:n` }),
482
470
  atoms: HAMT.make(),
471
+ atomsAreDefault: HAMT.make(),
483
472
  selectors: HAMT.make(),
484
473
  readonlySelectors: HAMT.make(),
485
474
  operation: {
@@ -511,10 +500,25 @@ var clearStore = (store = IMPLICIT.STORE) => {
511
500
 
512
501
  // src/internal/get.ts
513
502
  var getCachedState = (state, store = IMPLICIT.STORE) => {
503
+ const path = [];
504
+ if (`default` in state) {
505
+ const atomKey = state.key;
506
+ store.selectorAtoms = pipe6(store.selectorAtoms, (oldValue) => {
507
+ let newValue = oldValue;
508
+ for (const selectorKey of path) {
509
+ newValue = newValue.set(selectorKey, atomKey);
510
+ }
511
+ return newValue;
512
+ });
513
+ }
514
514
  const value = HAMT2.get(state.key, store.valueMap);
515
515
  return value;
516
516
  };
517
517
  var getSelectorState = (selector) => selector.get();
518
+ function lookup(key, store) {
519
+ const type = HAMT2.has(key, store.atoms) ? `atom` : HAMT2.has(key, store.selectors) ? `selector` : `readonly_selector`;
520
+ return { key, type };
521
+ }
518
522
  function withdraw(token, store) {
519
523
  var _a, _b;
520
524
  return (_b = (_a = HAMT2.get(token.key, store.atoms)) != null ? _a : HAMT2.get(token.key, store.selectors)) != null ? _b : HAMT2.get(token.key, store.readonlySelectors);
@@ -529,14 +533,16 @@ function deposit(state) {
529
533
  return { key: state.key, type: `atom` };
530
534
  }
531
535
  var getState__INTERNAL = (state, store = IMPLICIT.STORE) => {
532
- var _a;
536
+ var _a, _b, _c;
533
537
  if (HAMT2.has(state.key, store.valueMap)) {
538
+ (_a = store.config.logger) == null ? void 0 : _a.info(`>> read "${state.key}"`);
534
539
  return getCachedState(state, store);
535
540
  }
536
541
  if (`get` in state) {
542
+ (_b = store.config.logger) == null ? void 0 : _b.info(`-> calc "${state.key}"`);
537
543
  return getSelectorState(state);
538
544
  }
539
- (_a = store.config.logger) == null ? void 0 : _a.error(
545
+ (_c = store.config.logger) == null ? void 0 : _c.error(
540
546
  `Attempted to get atom "${state.key}", which was never initialized in store "${store.config.name}".`
541
547
  );
542
548
  return state.default;
@@ -554,12 +560,12 @@ var startAction = (store) => {
554
560
  done: /* @__PURE__ */ new Set(),
555
561
  prev: store.valueMap
556
562
  };
557
- (_a = store.config.logger) == null ? void 0 : _a.info(`\u2610`, `operation start`);
563
+ (_a = store.config.logger) == null ? void 0 : _a.info(`\u2B55`, `operation start`);
558
564
  };
559
565
  var finishAction = (store) => {
560
566
  var _a;
561
567
  store.operation = { open: false };
562
- (_a = store.config.logger) == null ? void 0 : _a.info(`\u2611\uFE0F`, `operation done`);
568
+ (_a = store.config.logger) == null ? void 0 : _a.info(`\u{1F534}`, `operation done`);
563
569
  };
564
570
  var isDone = (key, store = IMPLICIT.STORE) => {
565
571
  var _a;
@@ -581,7 +587,7 @@ var markDone = (key, store = IMPLICIT.STORE) => {
581
587
  }
582
588
  store.operation.done.add(key);
583
589
  };
584
- var recall = (state, store = IMPLICIT.STORE) => {
590
+ var recallState = (state, store = IMPLICIT.STORE) => {
585
591
  var _a;
586
592
  if (!store.operation.open) {
587
593
  (_a = store.config.logger) == null ? void 0 : _a.warn(
@@ -593,87 +599,60 @@ var recall = (state, store = IMPLICIT.STORE) => {
593
599
  };
594
600
 
595
601
  // src/internal/set.ts
596
- var propagateDown = (state, store = IMPLICIT.STORE) => {
602
+ var evictDownStream = (state, store = IMPLICIT.STORE) => {
597
603
  var _a, _b;
598
- const relatedStateKeys = store.selectorGraph.getRelations(state.key);
604
+ const downstream = store.selectorAtoms.getRelations(state.key);
605
+ const downstreamKeys = downstream.map(({ id }) => id);
599
606
  (_a = store.config.logger) == null ? void 0 : _a.info(
600
- ` ||`,
601
- `bumping`,
602
- relatedStateKeys.length,
603
- `states:`,
604
- relatedStateKeys.map(({ id }) => id)
607
+ ` || ${downstreamKeys.length} downstream:`,
608
+ downstreamKeys
605
609
  );
606
610
  if (store.operation.open) {
607
- (_b = store.config.logger) == null ? void 0 : _b.info(` ||`, `done:`, store.operation.done);
611
+ (_b = store.config.logger) == null ? void 0 : _b.info(` ||`, [...store.operation.done], `already done`);
608
612
  }
609
- relatedStateKeys.forEach(({ id: stateKey }) => {
610
- var _a2, _b2, _c, _d, _e;
613
+ downstream.forEach(({ id: stateKey }) => {
614
+ var _a2, _b2, _c, _d;
611
615
  if (isDone(stateKey, store)) {
612
- (_a2 = store.config.logger) == null ? void 0 : _a2.info(` ||`, stateKey, `already done`);
616
+ (_a2 = store.config.logger) == null ? void 0 : _a2.info(` || ${stateKey} already done`);
613
617
  return;
614
618
  }
615
- (_b2 = store.config.logger) == null ? void 0 : _b2.info(`->`, `bumping`, stateKey);
616
- const state2 = (_c = HAMT4.get(stateKey, store.selectors)) != null ? _c : HAMT4.get(stateKey, store.readonlySelectors);
619
+ const state2 = (_b2 = HAMT4.get(stateKey, store.selectors)) != null ? _b2 : HAMT4.get(stateKey, store.readonlySelectors);
617
620
  if (!state2) {
618
- (_d = store.config.logger) == null ? void 0 : _d.info(
619
- ` ||`,
620
- stateKey,
621
- `is an atom - no need to propagate down`
621
+ (_c = store.config.logger) == null ? void 0 : _c.info(
622
+ ` || ${stateKey} is an atom, and can't be downstream`
622
623
  );
623
624
  return;
624
625
  }
625
626
  store.valueMap = HAMT4.remove(stateKey, store.valueMap);
626
- const newValue = getState__INTERNAL(state2, store);
627
- (_e = store.config.logger) == null ? void 0 : _e.info(` <-`, stateKey, `became`, newValue);
628
- const oldValue = recall(state2, store);
629
- state2.subject.next({ newValue, oldValue });
627
+ (_d = store.config.logger) == null ? void 0 : _d.info(` xx evicted "${stateKey}"`);
630
628
  markDone(stateKey, store);
631
- if (`set` in state2)
632
- propagateDown(state2, store);
633
629
  });
634
630
  };
635
631
  var setAtomState = (atom, next, store = IMPLICIT.STORE) => {
636
632
  var _a, _b;
637
633
  const oldValue = getState__INTERNAL(atom, store);
638
634
  const newValue = become(next)(oldValue);
639
- (_a = store.config.logger) == null ? void 0 : _a.info(
640
- `->`,
641
- `setting atom`,
642
- `"${atom.key}"`,
643
- `to`,
644
- newValue
645
- );
635
+ (_a = store.config.logger) == null ? void 0 : _a.info(`-> setting atom "${atom.key}" to`, newValue);
646
636
  store.valueMap = HAMT4.set(atom.key, newValue, store.valueMap);
637
+ if (isAtomDefault(atom.key)) {
638
+ store.atomsAreDefault = HAMT4.set(atom.key, false, store.atomsAreDefault);
639
+ }
647
640
  markDone(atom.key, store);
648
- atom.subject.next({ newValue, oldValue });
649
641
  (_b = store.config.logger) == null ? void 0 : _b.info(
650
- ` ||`,
651
- `propagating change made to`,
652
- `"${atom.key}"`
642
+ ` || evicting caches downstream from "${atom.key}"`
653
643
  );
654
- propagateDown(atom, store);
644
+ evictDownStream(atom, store);
645
+ atom.subject.next({ newValue, oldValue });
655
646
  };
656
647
  var setSelectorState = (selector, next, store = IMPLICIT.STORE) => {
657
648
  var _a, _b;
658
649
  const oldValue = getState__INTERNAL(selector, store);
659
650
  const newValue = become(next)(oldValue);
660
- (_a = store.config.logger) == null ? void 0 : _a.info(
661
- `->`,
662
- `setting selector`,
663
- `"${selector.key}"`,
664
- `to`,
665
- newValue
666
- );
667
- (_b = store.config.logger) == null ? void 0 : _b.info(
668
- ` ||`,
669
- `propagating change made to`,
670
- `"${selector.key}"`
671
- );
651
+ (_a = store.config.logger) == null ? void 0 : _a.info(`-> setting selector "${selector.key}" to`, newValue);
652
+ (_b = store.config.logger) == null ? void 0 : _b.info(` || propagating change made to "${selector.key}"`);
672
653
  selector.set(newValue);
673
- propagateDown(selector, store);
674
654
  };
675
- var setState__INTERNAL = (token, value, store = IMPLICIT.STORE) => {
676
- const state = withdraw(token, store);
655
+ var setState__INTERNAL = (state, value, store = IMPLICIT.STORE) => {
677
656
  if (`set` in state) {
678
657
  setSelectorState(state, value, store);
679
658
  } else {
@@ -681,6 +660,114 @@ var setState__INTERNAL = (token, value, store = IMPLICIT.STORE) => {
681
660
  }
682
661
  };
683
662
 
663
+ // src/internal/is-default.ts
664
+ import HAMT5 from "hamt_plus";
665
+ var isAtomDefault = (key, store = IMPLICIT.STORE) => {
666
+ return HAMT5.get(key, store.atomsAreDefault);
667
+ };
668
+ var isSelectorDefault = (key, store = IMPLICIT.STORE) => {
669
+ const roots = traceAllSelectorAtoms(key, store);
670
+ return roots.every((root) => isAtomDefault(root.key, store));
671
+ };
672
+
673
+ // src/internal/selector-internal.ts
674
+ var lookupSelectorSources = (key, store) => store.selectorGraph.getRelations(key).filter(({ source }) => source !== key).map(({ source }) => lookup(source, store));
675
+ var traceSelectorAtoms = (selectorKey, dependency, store) => {
676
+ const roots = [];
677
+ const sources = lookupSelectorSources(dependency.key, store);
678
+ let depth = 0;
679
+ while (sources.length > 0) {
680
+ const source = sources.shift();
681
+ ++depth;
682
+ if (depth > 999) {
683
+ throw new Error(
684
+ `Maximum selector dependency depth exceeded in selector "${selectorKey}".`
685
+ );
686
+ }
687
+ if (source.type !== `atom`) {
688
+ sources.push(...lookupSelectorSources(source.key, store));
689
+ } else {
690
+ roots.push(source);
691
+ }
692
+ }
693
+ return roots;
694
+ };
695
+ var traceAllSelectorAtoms = (selectorKey, store) => {
696
+ const sources = lookupSelectorSources(selectorKey, store);
697
+ return sources.flatMap(
698
+ (source) => source.type === `atom` ? source : traceSelectorAtoms(selectorKey, source, store)
699
+ );
700
+ };
701
+ var updateSelectorAtoms = (selectorKey, dependency, store) => {
702
+ var _a, _b;
703
+ if (dependency.type === `atom`) {
704
+ store.selectorAtoms = store.selectorAtoms.set(selectorKey, dependency.key);
705
+ (_a = store.config.logger) == null ? void 0 : _a.info(
706
+ ` || adding root for "${selectorKey}": ${dependency.key}`
707
+ );
708
+ return;
709
+ }
710
+ const roots = traceSelectorAtoms(selectorKey, dependency, store);
711
+ (_b = store.config.logger) == null ? void 0 : _b.info(` || adding roots for "${selectorKey}":`, roots);
712
+ for (const root of roots) {
713
+ store.selectorAtoms = store.selectorAtoms.set(selectorKey, root.key);
714
+ }
715
+ };
716
+ var registerSelector = (selectorKey, store = IMPLICIT.STORE) => ({
717
+ get: (dependency) => {
718
+ var _a, _b;
719
+ const alreadyRegistered = store.selectorGraph.getRelations(selectorKey).some(({ source }) => source === dependency.key);
720
+ const dependencyState = withdraw(dependency, store);
721
+ const dependencyValue = getState__INTERNAL(dependencyState, store);
722
+ if (alreadyRegistered) {
723
+ (_a = store.config.logger) == null ? void 0 : _a.info(
724
+ ` || ${selectorKey} <- ${dependency.key} =`,
725
+ dependencyValue
726
+ );
727
+ } else {
728
+ (_b = store.config.logger) == null ? void 0 : _b.info(
729
+ `\u{1F50C} registerSelector "${selectorKey}" <- "${dependency.key}" =`,
730
+ dependencyValue
731
+ );
732
+ store.selectorGraph = store.selectorGraph.set(
733
+ selectorKey,
734
+ dependency.key,
735
+ {
736
+ source: dependency.key
737
+ }
738
+ );
739
+ }
740
+ updateSelectorAtoms(selectorKey, dependency, store);
741
+ return dependencyValue;
742
+ },
743
+ set: (stateToken, newValue) => {
744
+ const state = withdraw(stateToken, store);
745
+ setState__INTERNAL(state, newValue, store);
746
+ }
747
+ });
748
+
749
+ // src/internal/subscribe-internal.ts
750
+ var subscribeToRootAtoms = (state, store = IMPLICIT.STORE) => {
751
+ const dependencySubscriptions = `default` in state ? null : traceAllSelectorAtoms(state.key, store).map((atomToken) => {
752
+ const atom = withdraw(atomToken, store);
753
+ return atom.subject.subscribe((atomChange) => {
754
+ var _a, _b;
755
+ (_a = store.config.logger) == null ? void 0 : _a.info(
756
+ `\u{1F4E2} atom changed: "${atomToken.key}" (`,
757
+ atomChange.oldValue,
758
+ `->`,
759
+ atomChange.newValue,
760
+ `) re-evaluating "${state.key}"`
761
+ );
762
+ const oldValue = recallState(state, store);
763
+ const newValue = getState__INTERNAL(state, store);
764
+ (_b = store.config.logger) == null ? void 0 : _b.info(` <- ${state.key} became`, newValue);
765
+ state.subject.next({ newValue, oldValue });
766
+ });
767
+ });
768
+ return dependencySubscriptions;
769
+ };
770
+
684
771
  // src/internal/transaction-internal.ts
685
772
  var finishTransaction = (store) => {
686
773
  var _a;
@@ -719,33 +806,75 @@ var abortTransaction = (store) => {
719
806
  };
720
807
 
721
808
  // src/index.ts
722
- var setState = (state, value, store = IMPLICIT.STORE) => {
809
+ var setState = (token, value, store = IMPLICIT.STORE) => {
723
810
  startAction(store);
811
+ const state = withdraw(token, store);
724
812
  setState__INTERNAL(state, value, store);
725
813
  finishAction(store);
726
814
  };
815
+ var subscribe = (token, observe, store = IMPLICIT.STORE) => {
816
+ var _a;
817
+ const state = withdraw(token, store);
818
+ const subscription = state.subject.subscribe(observe);
819
+ (_a = store.config.logger) == null ? void 0 : _a.info(`\u{1F440} subscribe to "${state.key}"`);
820
+ const dependencySubscriptions = subscribeToRootAtoms(state, store);
821
+ const unsubscribe = dependencySubscriptions === null ? () => {
822
+ var _a2;
823
+ (_a2 = store.config.logger) == null ? void 0 : _a2.info(`\u{1F648} unsubscribe from "${state.key}"`);
824
+ subscription.unsubscribe();
825
+ } : () => {
826
+ var _a2;
827
+ (_a2 = store.config.logger) == null ? void 0 : _a2.info(
828
+ `\u{1F648} unsubscribe from "${state.key}" and its dependencies`
829
+ );
830
+ subscription.unsubscribe();
831
+ for (const dependencySubscription of dependencySubscriptions) {
832
+ dependencySubscription.unsubscribe();
833
+ }
834
+ };
835
+ return unsubscribe;
836
+ };
727
837
 
728
838
  // src/react/index.ts
729
- var composeStoreHook = ({
839
+ var composeStoreHooks = ({
730
840
  useState,
731
841
  useEffect,
732
842
  store = internal_exports.IMPLICIT.STORE
733
843
  }) => {
734
- const useSubject = composeSubjectHook(useState, useEffect);
735
- function useStore(token) {
736
- const { type } = token;
844
+ function useI(token) {
845
+ const updateState = (next) => setState(token, next, store);
846
+ return updateState;
847
+ }
848
+ function useO(token) {
737
849
  const state = withdraw(token, store);
738
850
  const initialValue = internal_exports.getState__INTERNAL(state, store);
739
- const [value] = useSubject(state.subject, initialValue);
740
- if (type === `readonly_selector`) {
741
- return value;
851
+ const [current, dispatch] = useState(initialValue);
852
+ useEffect(() => {
853
+ const unsubscribe = subscribe(
854
+ token,
855
+ ({ newValue, oldValue }) => {
856
+ if (oldValue !== newValue) {
857
+ dispatch(newValue);
858
+ }
859
+ },
860
+ store
861
+ );
862
+ return unsubscribe;
863
+ }, [current, dispatch]);
864
+ return current;
865
+ }
866
+ function useIO(token) {
867
+ return [useO(token), useI(token)];
868
+ }
869
+ function useStore(token) {
870
+ if (token.type === `readonly_selector`) {
871
+ return useO(token);
742
872
  }
743
- const updateState = (next) => setState(token, next, store);
744
- return [value, updateState];
873
+ return useIO(token);
745
874
  }
746
- return { useStore };
875
+ return { useI, useO, useIO, useStore };
747
876
  };
748
877
  export {
749
- composeStoreHook
878
+ composeStoreHooks
750
879
  };
751
880
  //# sourceMappingURL=index.mjs.map