valdres 0.2.0-pre.2 → 0.2.0-pre.21

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.js CHANGED
@@ -144,6 +144,18 @@ ${generateSelectorTrace(this.selectors)}`;
144
144
  }
145
145
  }
146
146
 
147
+ // src/errors/SelectorCircularDependencyError.ts
148
+ class SelectorCircularDependencyError extends SelectorEvaluationError {
149
+ constructor() {
150
+ super();
151
+ }
152
+ get message() {
153
+ const firstSelectorName = this.selectors[0].name ?? "Anonymous Selector";
154
+ return `Circular dependency detected in '${firstSelectorName}'
155
+ ${generateSelectorTrace(this.selectors)}`;
156
+ }
157
+ }
158
+
147
159
  // src/utils/isAtomFamily.ts
148
160
  var isAtomFamily = (state) => state && Object.hasOwn(state, "__valdresAtomFamilyMap");
149
161
 
@@ -187,7 +199,7 @@ var isProd = () => {
187
199
 
188
200
  // src/lib/setValueInData.ts
189
201
  var setValueInData = (atom, value, data) => {
190
- if (isProd()) {
202
+ if (atom.mutable || isProd()) {
191
203
  data.values.set(atom, value);
192
204
  return value;
193
205
  } else {
@@ -278,7 +290,7 @@ function getState(state, data, initializedAtomsSet, circularDependencySet) {
278
290
  const closestData = findClosestStoreWithAtomInitialized(state, data);
279
291
  return getState(state, closestData, initializedAtomsSet, circularDependencySet);
280
292
  }
281
- data.values.set(state, []);
293
+ data.values.set(state, renderAtomFamilyIndex(createAtomFamilyIndex()));
282
294
  initializedAtomsSet.add(state);
283
295
  return data.values.get(state);
284
296
  }
@@ -317,7 +329,9 @@ var getOrInitDependentsSet = (state, data) => {
317
329
  };
318
330
  var evaluateSelector = (selector, data, initializedAtomsSet, circularDependencySet = new WeakSet) => {
319
331
  const updatedDependencies = new Set;
320
- if (circularDependencySet.has(selector)) {}
332
+ if (circularDependencySet.has(selector)) {
333
+ throw new SelectorCircularDependencyError;
334
+ }
321
335
  circularDependencySet.add(selector);
322
336
  let result;
323
337
  try {
@@ -349,6 +363,7 @@ var evaluateSelector = (selector, data, initializedAtomsSet, circularDependencyS
349
363
  set.delete(selector);
350
364
  }
351
365
  data.stateDependencies.set(selector, updatedDependencies);
366
+ circularDependencySet.delete(selector);
352
367
  return result;
353
368
  };
354
369
  var handleSelectorResult = (value, selector, data) => {
@@ -422,30 +437,117 @@ var addSetToSet = (fromSet, toSet) => {
422
437
  }
423
438
  }
424
439
  };
425
- var findClosestStoreWithAtomInitialized2 = (atom, data) => {
426
- if ("parent" in data === false)
427
- return data;
428
- if (data.values.has(atom))
429
- return data;
430
- return findClosestStoreWithAtomInitialized2(atom, data.parent);
440
+ var getAtomFamilyRenderedMap = (index) => {
441
+ if (index.rendered)
442
+ return index.rendered;
443
+ const result = new Map(index.parentIndex ? getAtomFamilyRenderedMap(index.parentIndex) : undefined);
444
+ for (const [atom, timestamp] of index.created) {
445
+ result.set(atom, timestamp);
446
+ }
447
+ for (const [atom, timestamp] of index.deleted) {
448
+ result.delete(atom, timestamp);
449
+ }
450
+ index.rendered = result;
451
+ return result;
431
452
  };
432
- var findInClosestStore = (state, data) => {
433
- const store = findClosestStoreWithAtomInitialized2(state, data);
434
- return store.values.get(state);
453
+ var getSortedKeysByValues = (map) => {
454
+ return Array.from(map.entries()).sort((a, b) => a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0).map((entry) => entry[0]);
435
455
  };
436
- var addFamilyAtomsToSet = (family, familyAtoms, data) => {
437
- const currentAtoms = findInClosestStore(family, data) || [];
438
- const atomsToAdd = [];
439
- for (const familyAtom of familyAtoms) {
440
- if (!currentAtoms.includes(familyAtom)) {
441
- atomsToAdd.push(familyAtom);
442
- }
456
+ var renderAtomFamilyIndex = (index) => {
457
+ if (index.renderedArray) {
458
+ console.log("Using cached rendered array");
459
+ return index.renderedArray;
460
+ }
461
+ const renderedMap = getAtomFamilyRenderedMap(index);
462
+ const array = getSortedKeysByValues(renderedMap);
463
+ array.__index = index;
464
+ index.renderedArray = array;
465
+ return array;
466
+ };
467
+ var cloneAtomFamilyIndex = (index, parentIndexOverride) => {
468
+ return {
469
+ created: new Map(index.created),
470
+ deleted: new Map(index.deleted),
471
+ rendered: null,
472
+ renderedArray: null,
473
+ parentIndex: parentIndexOverride || index.parentIndex
474
+ };
475
+ };
476
+ var createAtomFamilyIndex = (parentIndex) => {
477
+ return {
478
+ created: new Map,
479
+ deleted: new Map,
480
+ rendered: null,
481
+ renderedArray: null,
482
+ parentIndex
483
+ };
484
+ };
485
+ var deleteFamilyAtomsFromSet = (family, familyAtoms, data, timestamp) => {
486
+ if (familyAtoms.size === 0)
487
+ return;
488
+ const index = findFamilyIndex(family, data);
489
+ for (const atom of familyAtoms) {
490
+ index.deleted.set(atom, timestamp);
491
+ }
492
+ index.rendered = null;
493
+ index.renderedArray = null;
494
+ data.values.set(family, renderAtomFamilyIndex(index));
495
+ recursivlyUpdateIndexes(data, family);
496
+ };
497
+ var initFamilyIndex = (family, data) => {
498
+ if (data.values.has(family))
499
+ return data.values.get(family).__index;
500
+ let parentIndex;
501
+ if ("parent" in data) {
502
+ parentIndex = initFamilyIndex(family, data.parent);
503
+ if (!parentIndex)
504
+ throw new Error("Parent index is missing");
505
+ }
506
+ const index = createAtomFamilyIndex(parentIndex);
507
+ data.values.set(family, renderAtomFamilyIndex(index));
508
+ return index;
509
+ };
510
+ var findFamilyIndex = (family, data) => {
511
+ if (!data.values.has(family)) {
512
+ initFamilyIndex(family, data);
443
513
  }
444
- if (atomsToAdd.length > 0) {
445
- data.values.set(family, [...currentAtoms, ...atomsToAdd]);
514
+ const value = data.values.get(family);
515
+ if (!value?.__index) {
516
+ console.log("value", value);
517
+ throw new Error("Family index is missing");
446
518
  }
519
+ return value.__index;
447
520
  };
448
- var propagateUpdatedAtoms = (atoms, data, subscriptions = new Set, families = new Map, isRecursive = false) => {
521
+ var recursivlyUpdateIndexes = (data, family) => {
522
+ Object.keys(data.scopes).forEach((scopeKey) => {
523
+ const scopedData = data.scopes[scopeKey];
524
+ if (scopeKey) {
525
+ if (scopedData.values.has(family)) {
526
+ const index = scopedData.values.get(family).__index;
527
+ index.rendered = null;
528
+ index.renderedArray = null;
529
+ scopedData.values.set(family, renderAtomFamilyIndex(index));
530
+ }
531
+ recursivlyUpdateIndexes(scopedData, family);
532
+ }
533
+ });
534
+ };
535
+ var addFamilyAtomsToSet = (family, familyAtoms, data, timestamp) => {
536
+ if (familyAtoms.size === 0)
537
+ return;
538
+ const index = findFamilyIndex(family, data);
539
+ if (!index)
540
+ throw new Error("index not found");
541
+ for (const atom of familyAtoms) {
542
+ index.created.set(atom, timestamp);
543
+ index.deleted.delete(atom);
544
+ }
545
+ index.rendered = null;
546
+ index.renderedArray = null;
547
+ data.values.set(family, renderAtomFamilyIndex(index));
548
+ recursivlyUpdateIndexes(data, family);
549
+ };
550
+ var propagateDeletedAtoms = (atoms, data, subscriptions = new Set, families = new Map, timestamp = performance.now()) => {
449
551
  const selectors = new Set;
450
552
  for (const atom of atoms) {
451
553
  addSetToSet(data.stateDependents.get(atom), selectors);
@@ -463,11 +565,50 @@ var propagateUpdatedAtoms = (atoms, data, subscriptions = new Set, families = ne
463
565
  addSetToSet(data.subscriptions.get(family), subscriptions);
464
566
  if (familyAtoms.size === 0)
465
567
  throw new Error("Should not be possible");
466
- addFamilyAtomsToSet(family, familyAtoms, data);
568
+ deleteFamilyAtomsFromSet(family, familyAtoms, data, timestamp);
569
+ }
570
+ }
571
+ propagateDirtySelectors(atoms, selectors, data, subscriptions, families);
572
+ };
573
+ var propagateUpdatedAtoms = (atoms, data, subscriptions = new Set, families = new Map, isRecursive = false, timestamp = performance.now(), selectorsOnly = false) => {
574
+ const selectors = new Set;
575
+ for (const atom of atoms) {
576
+ addSetToSet(data.stateDependents.get(atom), selectors);
577
+ if (!selectorsOnly) {
578
+ addSetToSet(data.subscriptions.get(atom), subscriptions);
579
+ if (isFamilyAtom(atom)) {
580
+ if (!families.has(atom.family)) {
581
+ families.set(atom.family, new Set);
582
+ }
583
+ families.get(atom.family).add(atom);
584
+ }
585
+ }
586
+ }
587
+ if (families.size > 0) {
588
+ for (const [family, familyAtoms] of families) {
589
+ addSetToSet(data.stateDependents.get(family), selectors);
590
+ addSetToSet(data.subscriptions.get(family), subscriptions);
591
+ if (familyAtoms.size === 0)
592
+ throw new Error("Should not be possible");
593
+ addFamilyAtomsToSet(family, familyAtoms, data, timestamp);
467
594
  }
468
595
  }
469
596
  if (!isRecursive) {
470
597
  propagateDirtySelectors(atoms, selectors, data, subscriptions, families);
598
+ if (data.scopes) {
599
+ for (const scopeId in data.scopes) {
600
+ const scope = data.scopes[scopeId];
601
+ const atomsToUpdateInScope = [];
602
+ for (const atom of atoms) {
603
+ if (!scope.values.has(atom)) {
604
+ atomsToUpdateInScope.push(atom);
605
+ }
606
+ }
607
+ if (atomsToUpdateInScope.length > 0) {
608
+ propagateUpdatedAtoms(atomsToUpdateInScope, scope, undefined, undefined, false, timestamp, true);
609
+ }
610
+ }
611
+ }
471
612
  }
472
613
  };
473
614
  var propagateDirtySelectors = (updatedAtoms, selectors, data, subscriptions, families) => {
@@ -496,18 +637,6 @@ var propagateDirtySelectors = (updatedAtoms, selectors, data, subscriptions, fam
496
637
  }
497
638
  }
498
639
  };
499
- var findAllDependents = (selector, data, depsRes = new Set, subsRes = new Set) => {
500
- const dependents = data.stateDependents.get(selector);
501
- const subscriptions = data.subscriptions.get(selector);
502
- addSetToSet(dependents, depsRes);
503
- addSetToSet(subscriptions, subsRes);
504
- if (dependents && dependents.size > 0) {
505
- for (const dependent of dependents) {
506
- findAllDependents(dependent, data, depsRes, subsRes);
507
- }
508
- }
509
- return [depsRes, subsRes];
510
- };
511
640
  var recursivlyHandleSelectorUpdates = (selectors, data, collectedSubscribers, updatedInitializedAtoms, seen = new Set) => {
512
641
  const selectorsForNextPass = new Set;
513
642
  for (const selector of selectors) {
@@ -523,23 +652,10 @@ var recursivlyHandleSelectorUpdates = (selectors, data, collectedSubscribers, up
523
652
  data.values.delete(selector);
524
653
  } else {
525
654
  const [wasValueUpdated, didEvalCrash, error] = reEvaluteSelector(selector, data, updatedInitializedAtoms);
526
- if (didEvalCrash) {
527
- const [deps, subs] = findAllDependents(selector, data);
528
- if (deps.size > 0) {
529
- for (const dep of deps) {
530
- data.expiredValues.set(dep, data.values.get(dep));
531
- data.values.delete(dep);
532
- }
533
- }
534
- if (subs.size > 0) {
535
- addSetToSet(subs, collectedSubscribers);
536
- }
537
- } else {
538
- if (!wasValueUpdated)
539
- continue;
540
- addSetToSet(data.stateDependents.get(selector), selectorsForNextPass);
541
- addSetToSet(subscribers, collectedSubscribers);
542
- }
655
+ if (!wasValueUpdated)
656
+ continue;
657
+ addSetToSet(data.stateDependents.get(selector), selectorsForNextPass);
658
+ addSetToSet(subscribers, collectedSubscribers);
543
659
  }
544
660
  }
545
661
  if (selectorsForNextPass.size > 0) {
@@ -575,16 +691,8 @@ function createStoreData(id, parent) {
575
691
 
576
692
  // src/lib/deleteFamilyAtom.ts
577
693
  var deleteFamilyAtom = (atom, data) => {
578
- const array = data.values.get(atom.family);
579
- const index = array.indexOf(atom);
580
- const newArray = [
581
- ...array.slice(0, index),
582
- ...array.slice(index + 1)
583
- ];
584
694
  data.values.delete(atom);
585
- propagateUpdatedAtoms([atom], data);
586
- setValueInData(atom.family, newArray, data);
587
- propagateUpdatedAtoms([atom.family], data);
695
+ propagateDeletedAtoms([atom], data);
588
696
  };
589
697
 
590
698
  // src/lib/resetAtom.ts
@@ -744,6 +852,8 @@ var setAtoms = (pairs, data, initializedAtomsSet) => {
744
852
  value = setValueInData(atom, value, data);
745
853
  if (atom.onSet)
746
854
  atom.onSet(value, data);
855
+ } else {
856
+ setValueInData(atom, value, data);
747
857
  }
748
858
  }
749
859
  const result = new Set([...updatedAtoms, ...initializedAtomsSet]);
@@ -753,174 +863,238 @@ var setAtoms = (pairs, data, initializedAtomsSet) => {
753
863
  };
754
864
 
755
865
  // src/lib/transaction.ts
756
- var findDependencies = (state, data, result = new Set) => {
757
- const dependents = data.stateDependents.get(state);
758
- if (dependents?.size) {
759
- for (const dependent of dependents) {
760
- if (!result.has(dependent)) {
761
- result.add(dependent);
762
- findDependencies(dependent, data, result);
763
- }
764
- }
765
- }
766
- return result;
767
- };
768
- var recursivlyResetTxnSelectorCache = (state, txnSubscribers, txnSelectorCache) => {
769
- for (const dep of txnSubscribers.get(state)) {
770
- txnSelectorCache.delete(dep);
771
- if (txnSubscribers.get(dep)?.size) {
772
- recursivlyResetTxnSelectorCache(dep, txnSubscribers, txnSelectorCache);
773
- }
774
- }
775
- };
776
- var captureScopedTransaction = (scopedData, parentGetInTxnOrData) => {
777
- let txn;
778
- transaction((scopedTxn) => {
779
- txn = scopedTxn;
780
- }, scopedData, false, parentGetInTxnOrData);
781
- return txn;
782
- };
783
866
  var deleteAtomFamilyAtoms = (set, data) => {
784
867
  set.forEach((atom) => {
785
868
  data.values.delete(atom);
786
869
  });
787
870
  };
788
- var transaction = (callback, data, autoCommit = true, parentGetInTxnOrData) => {
789
- const txnAtomMap = new Map;
790
- const txnAtomDeleteSet = new Set;
791
- const txnSelectorCache = new Map;
792
- const txnSubscribers = new Map;
793
- const dirtySelectors = new Set;
794
- let scopedTransactions;
795
- const getInTxnOrData = (state) => {
796
- if (txnAtomMap.has(state)) {
797
- return txnAtomMap.get(state);
871
+
872
+ class Transaction {
873
+ data;
874
+ parentTransaction;
875
+ dirty;
876
+ _scopedTransactions;
877
+ _initializedAtomsSet;
878
+ _deleteSet;
879
+ _selectorDependencies;
880
+ _selectorCache;
881
+ _atomMap;
882
+ constructor(data, parentTransaction, childTransaction) {
883
+ this.data = data;
884
+ this.parentTransaction = parentTransaction;
885
+ this.dirty = false;
886
+ if (childTransaction) {
887
+ this._scopedTransactions = new Map([
888
+ [childTransaction.data.id, childTransaction]
889
+ ]);
798
890
  }
799
- if (data.values.has(state)) {
800
- return data.values.get(state);
891
+ }
892
+ valueFromTxnOrData = (state) => {
893
+ if (this.atomMap.has(state)) {
894
+ return this.atomMap.get(state);
801
895
  }
802
- if (parentGetInTxnOrData) {
803
- return parentGetInTxnOrData(state);
896
+ if (this.data.values.has(state)) {
897
+ return this.data.values.get(state);
898
+ }
899
+ if (this.parentTransaction) {
900
+ return this.parentTransaction.valueFromTxnOrData(state);
804
901
  }
805
902
  };
806
- const initializedAtomsSet = new Set;
807
- const txnGet = (state) => {
808
- if (isAtom(state)) {
809
- const value = getInTxnOrData(state);
903
+ get = (state) => {
904
+ if (isAtom(state) || isAtomFamily(state)) {
905
+ const value = this.valueFromTxnOrData(state);
810
906
  if (value)
811
907
  return value;
812
- return getState(state, data, initializedAtomsSet);
908
+ return getState(state, this.data, this.initializedAtomsSet);
813
909
  } else if (isSelector(state)) {
814
- if (txnSelectorCache.has(state)) {
815
- return txnSelectorCache.get(state);
910
+ if (this.dirty) {
911
+ this.selectorCache.clear();
912
+ this.selectorDependencies.clear();
913
+ this.dirty = false;
914
+ } else if (this.selectorCache.has(state)) {
915
+ return this.selectorCache.get(state);
816
916
  }
817
- const deps = new Set;
818
917
  const res = state.get((s) => {
819
- deps.add(s);
820
- return txnGet(s);
821
- }, data.id);
822
- for (const dep of deps) {
823
- if (!txnSubscribers.has(dep)) {
824
- txnSubscribers.set(dep, new Set);
918
+ if (!this.selectorDependencies.has(s)) {
919
+ this.selectorDependencies.add(s);
825
920
  }
826
- txnSubscribers.get(dep).add(state);
827
- }
828
- txnSelectorCache.set(state, res);
921
+ return this.get(s);
922
+ }, this.data.id);
923
+ this.selectorCache.set(state, res);
829
924
  return res;
830
- } else if (isAtomFamily(state)) {
831
- const value = getInTxnOrData(state);
832
- if (value)
833
- return value;
834
- return getState(state, data, initializedAtomsSet);
835
925
  } else {
836
926
  throw new Error("Unsupported state");
837
927
  }
838
928
  };
839
- const txnSet = (atom, value) => {
929
+ set = (atom, value) => {
840
930
  if (!isAtom(atom))
841
931
  throw new Error("Not an atom");
842
932
  if (isFunction(value)) {
843
- const currentValue = txnGet(atom);
933
+ const currentValue = this.get(atom);
844
934
  value = value(currentValue);
845
935
  }
846
- for (const selector of findDependencies(atom, data)) {
847
- dirtySelectors.add(selector);
848
- txnSelectorCache.delete(selector);
849
- }
850
- if (txnSubscribers.get(atom)?.size) {
851
- recursivlyResetTxnSelectorCache(atom, txnSubscribers, txnSelectorCache);
852
- }
853
936
  if (isProd()) {
854
- txnAtomMap.set(atom, value);
937
+ this.atomMap.set(atom, value);
855
938
  } else {
856
- txnAtomMap.set(atom, deepFreeze(value));
939
+ this.atomMap.set(atom, deepFreeze(value));
857
940
  }
941
+ if (!this.dirty)
942
+ this.dirty = true;
858
943
  if (isFamilyAtom(atom)) {
859
- const currentFamilyList = txnGet(atom.family);
860
- if (!currentFamilyList.includes(atom)) {
861
- const newArr = [...currentFamilyList, atom];
862
- txnAtomMap.set(atom.family, newArr);
944
+ if (!this.atomMap.has(atom.family)) {
945
+ this.cloneFamilyIntoTxn(atom.family);
863
946
  }
947
+ const index = this.atomMap.get(atom.family).__index;
948
+ index.created.set(atom, performance.now());
949
+ index.deleted.delete(atom);
950
+ index.rendered = null;
951
+ index.renderedArray = null;
952
+ this.recursivlyUpdateAtomFamilyIndexes(atom.family);
864
953
  }
865
954
  return value;
866
955
  };
867
- const txnReset = (atom) => {
868
- const value = getAtomInitValue(atom, data, initializedAtomsSet);
869
- txnAtomMap.set(atom, value);
870
- return value;
956
+ batchSetFamilyAtoms = (family, pairs) => {
957
+ if (!this.atomMap.has(family)) {
958
+ this.cloneFamilyIntoTxn(family);
959
+ }
960
+ const index = this.atomMap.get(family).__index;
961
+ for (const [atom, value] of pairs) {
962
+ if (atom.family !== family) {
963
+ throw new Error("Atom does not belong to the provided family");
964
+ }
965
+ index.created.set(atom, performance.now());
966
+ if (index.deleted.has(atom))
967
+ index.deleted.delete(atom);
968
+ this.atomMap.set(atom, value);
969
+ }
970
+ index.rendered = null;
971
+ index.renderedArray = null;
972
+ this.recursivlyUpdateAtomFamilyIndexes(family);
871
973
  };
872
- const txnDel = (atom) => {
873
- const array = txnGet(atom.family);
874
- const index = array.indexOf(atom);
875
- const newArr = [
876
- ...array.slice(0, index),
877
- ...array.slice(index + 1)
878
- ];
879
- txnAtomMap.set(atom.family, newArr);
880
- if (data.values.has(atom)) {
881
- txnAtomDeleteSet.add(atom);
974
+ del = (atom) => {
975
+ if (!this.atomMap.has(atom.family)) {
976
+ this.cloneFamilyIntoTxn(atom.family);
977
+ }
978
+ const index = this.atomMap.get(atom.family).__index;
979
+ index.created.delete(atom);
980
+ index.deleted.set(atom, performance.now());
981
+ index.rendered = null;
982
+ index.renderedArray = null;
983
+ this.atomMap.set(atom.family, renderAtomFamilyIndex(index));
984
+ this.recursivlyUpdateAtomFamilyIndexes(atom.family);
985
+ if (this.data.values.has(atom)) {
986
+ this.deleteSet.add(atom);
987
+ }
988
+ if (this.atomMap.has(atom)) {
989
+ this.atomMap.delete(atom);
990
+ }
991
+ };
992
+ scope = (scopeId, callback) => {
993
+ if (scopeId in this.data.scopes) {
994
+ return this.scopedTransaction(scopeId).execute(callback, false);
995
+ } else {
996
+ throw new Error(`Scope '${scopeId}' not found. Registered scopes: ${Object.keys(this.data.scopes).join(", ")}`);
882
997
  }
883
- if (txnAtomMap.has(atom)) {
884
- txnAtomMap.delete(atom);
998
+ };
999
+ parentScope = (callback) => {
1000
+ if (!this.parentTransaction) {
1001
+ this.parentTransaction = new Transaction(this.data.parent, undefined, this);
885
1002
  }
1003
+ return this.parentTransaction.execute(callback, false);
1004
+ };
1005
+ reset = (atom) => {
1006
+ const value = getAtomInitValue(atom, this.data, this.initializedAtomsSet);
1007
+ this.atomMap.set(atom, value);
1008
+ return value;
1009
+ };
1010
+ execute = (callback, autoCommit = true) => {
1011
+ const result = callback(this);
1012
+ if (autoCommit)
1013
+ this.txnCommit();
1014
+ return result;
886
1015
  };
887
- const commit = () => {
888
- setAtoms(txnAtomMap, data, initializedAtomsSet);
889
- if (txnAtomDeleteSet.size) {
890
- deleteAtomFamilyAtoms(txnAtomDeleteSet, data);
1016
+ txnCommit = () => {
1017
+ if (this.parentTransaction) {
1018
+ this.parentTransaction.txnCommit();
1019
+ } else {
1020
+ this.commit();
891
1021
  }
892
- dirtySelectors.clear();
893
- if (scopedTransactions) {
894
- for (const scopedTxn of Object.values(scopedTransactions)) {
1022
+ };
1023
+ commit = () => {
1024
+ const initializedAtomsSet = new Set;
1025
+ setAtoms(this.atomMap, this.data, initializedAtomsSet);
1026
+ if (this.deleteSet?.size) {
1027
+ deleteAtomFamilyAtoms(this.deleteSet, this.data);
1028
+ }
1029
+ if (this._scopedTransactions) {
1030
+ for (const [, scopedTxn] of this._scopedTransactions) {
895
1031
  scopedTxn.commit();
896
1032
  }
897
1033
  }
898
1034
  };
899
- const result = callback({
900
- set: txnSet,
901
- get: txnGet,
902
- del: txnDel,
903
- reset: txnReset,
904
- commit,
905
- scope: (scopeId, callback2) => {
906
- if (scopeId in data.scopes) {
907
- const scopedData = data.scopes[scopeId];
908
- if (scopedTransactions === undefined) {
909
- scopedTransactions = {};
910
- }
911
- if (scopedTransactions[scopeId] === undefined) {
912
- scopedTransactions[scopeId] = captureScopedTransaction(scopedData, getInTxnOrData);
913
- }
914
- return callback2(scopedTransactions[scopeId]);
915
- } else {
916
- throw new Error(`Scope '${scopeId}' not found. Registered scopes: ${Object.keys(data.scopes).join(", ")}`);
1035
+ get atomMap() {
1036
+ if (!this._atomMap)
1037
+ this._atomMap = new Map;
1038
+ return this._atomMap;
1039
+ }
1040
+ get selectorCache() {
1041
+ if (!this._selectorCache)
1042
+ this._selectorCache = new Map;
1043
+ return this._selectorCache;
1044
+ }
1045
+ get selectorDependencies() {
1046
+ if (!this._selectorDependencies)
1047
+ this._selectorDependencies = new Set;
1048
+ return this._selectorDependencies;
1049
+ }
1050
+ get deleteSet() {
1051
+ if (!this._deleteSet)
1052
+ this._deleteSet = new Set;
1053
+ return this._deleteSet;
1054
+ }
1055
+ get initializedAtomsSet() {
1056
+ if (!this._initializedAtomsSet)
1057
+ this._initializedAtomsSet = new Set;
1058
+ return this._initializedAtomsSet;
1059
+ }
1060
+ scopedTransaction(scopeId) {
1061
+ if (!this._scopedTransactions)
1062
+ this._scopedTransactions = new Map;
1063
+ if (!this._scopedTransactions.has(scopeId)) {
1064
+ const scopedData = this.data.scopes[scopeId];
1065
+ const scopedTransaction = new Transaction(scopedData, this);
1066
+ this._scopedTransactions.set(scopeId, scopedTransaction);
1067
+ }
1068
+ return this._scopedTransactions.get(scopeId);
1069
+ }
1070
+ cloneFamilyIntoTxn(family, parentIndex, moveUpIfParent = true) {
1071
+ if (moveUpIfParent && this.parentTransaction)
1072
+ return this.parentTransaction.cloneFamilyIntoTxn(family, parentIndex, moveUpIfParent);
1073
+ const currentFamilyList = this.get(family);
1074
+ const clonedIndex = cloneAtomFamilyIndex(currentFamilyList.__index, parentIndex);
1075
+ if (this._scopedTransactions?.size) {
1076
+ for (const [, scopedTxn] of this._scopedTransactions) {
1077
+ scopedTxn.cloneFamilyIntoTxn(family, clonedIndex, false);
917
1078
  }
918
- },
919
- data
920
- });
921
- if (autoCommit)
922
- commit();
923
- return result;
1079
+ }
1080
+ this.atomMap.set(family, renderAtomFamilyIndex(clonedIndex));
1081
+ }
1082
+ recursivlyUpdateAtomFamilyIndexes(atomFamily) {
1083
+ const currentIndex = this.atomMap.get(atomFamily).__index;
1084
+ currentIndex.rendered = null;
1085
+ currentIndex.renderedArray = null;
1086
+ const updatedValue = renderAtomFamilyIndex(currentIndex);
1087
+ this.atomMap.set(atomFamily, updatedValue);
1088
+ if (this._scopedTransactions?.size) {
1089
+ for (const [, scopedTxn] of this._scopedTransactions) {
1090
+ scopedTxn.recursivlyUpdateAtomFamilyIndexes(atomFamily);
1091
+ }
1092
+ }
1093
+ }
1094
+ }
1095
+ var transaction = (callback, data) => {
1096
+ const txn = new Transaction(data);
1097
+ return txn.execute(callback);
924
1098
  };
925
1099
 
926
1100
  // src/lib/storeFromStoreData.ts
@@ -963,7 +1137,10 @@ function storeFromStoreData(data, detach) {
963
1137
  scopedStoreData.scopeConsumers.delete(detach2);
964
1138
  if (scopedStoreData.scopeConsumers.size === 0) {
965
1139
  delete data.scopes[scopeId];
1140
+ return true;
966
1141
  }
1142
+ console.warn(`Scope ${scopeId} still has ${scopedStoreData.scopeConsumers.size} consumers, will not detach`);
1143
+ return false;
967
1144
  };
968
1145
  scopedStoreData.scopeConsumers.add(detach2);
969
1146
  const newStore = storeFromStoreData(data.scopes[scopeId], detach2);
@@ -1326,9 +1503,9 @@ var isFamilySelector = (state) => isFamilyState(state) && isSelector(state);
1326
1503
 
1327
1504
  // src/index.ts
1328
1505
  if (globalThis.__valdres__) {
1329
- throw new Error(`Error! An instance of valdres is already loaded. Loaded: ${globalThis.__valdres__}. Attempted to load: ${"0.2.0-pre.2"}`);
1506
+ throw new Error(`Error! An instance of valdres is already loaded. Loaded: ${globalThis.__valdres__}. Attempted to load: ${"0.2.0-pre.21"}`);
1330
1507
  } else {
1331
- globalThis.__valdres__ = "0.2.0-pre.2";
1508
+ globalThis.__valdres__ = "0.2.0-pre.21";
1332
1509
  }
1333
1510
  export {
1334
1511
  store,
@@ -34,3 +34,4 @@ export type { Store } from "./types/Store";
34
34
  export type { StoreData } from "./types/StoreData";
35
35
  export type { TransactionFn } from "./types/TransactionFn";
36
36
  export type { TransactionInterface } from "./types/TransactionInterface";
37
+ export type { Transaction } from "./lib/transaction";
@@ -1,5 +1,5 @@
1
1
  import type { AtomFamily } from "./types/AtomFamily";
2
2
  import type { Selector } from "./types/Selector";
3
- export declare const index: <Term, Value extends unknown, FamilyArgs extends [any, ...any[]] = [any, ...any[]]>(family: AtomFamily<Value, FamilyArgs>, callback: (value: Value, term: Term) => boolean, options?: {
3
+ export declare const index: <Term, Value extends any, FamilyArgs extends [any, ...any[]] = [any, ...any[]]>(family: AtomFamily<Value, FamilyArgs>, callback: (value: Value, term: Term) => boolean, options?: {
4
4
  name?: string;
5
5
  }) => ((term: Term) => Selector<Term[]>);
@@ -1,4 +1,4 @@
1
1
  import type { AtomFamily } from "../types/AtomFamily";
2
2
  import type { AtomFamilyDefaultValue } from "../types/AtomFamilyDefaultValue";
3
3
  import type { AtomOptions } from "../types/AtomOptions";
4
- export declare const createAtomFamily: <Value extends unknown, Args extends [any, ...any[]] = [any, ...any[]]>(defaultValue: AtomFamilyDefaultValue<Value, Args>, options?: AtomOptions<Value>) => AtomFamily<Value, Args>;
4
+ export declare const createAtomFamily: <Value extends any, Args extends [any, ...any[]] = [any, ...any[]]>(defaultValue: AtomFamilyDefaultValue<Value, Args>, options?: AtomOptions<Value>) => AtomFamily<Value, Args>;
@@ -1,4 +1,4 @@
1
1
  import type { AtomFamily } from "../types/AtomFamily";
2
2
  import type { AtomFamilyDefaultValue } from "../types/AtomFamilyDefaultValue";
3
3
  import type { AtomOptions } from "../types/AtomOptions";
4
- export declare const createGlobalAtomFamily: <Value extends unknown, Args extends [any, ...any[]] = [any, ...any[]]>(defaultValue: AtomFamilyDefaultValue<Value, Args>, options: AtomOptions<Value>) => AtomFamily<Value, Args>;
4
+ export declare const createGlobalAtomFamily: <Value extends any, Args extends [any, ...any[]] = [any, ...any[]]>(defaultValue: AtomFamilyDefaultValue<Value, Args>, options: AtomOptions<Value>) => AtomFamily<Value, Args>;
@@ -1,8 +1,8 @@
1
1
  import type { Atom } from "../types/Atom";
2
2
  import type { AtomFamily } from "../types/AtomFamily";
3
+ import type { AtomFamilyAtom } from "../types/AtomFamilyAtom";
3
4
  import type { Selector } from "../types/Selector";
4
5
  import type { StoreData } from "../types/StoreData";
5
- import type { AtomFamilyAtom } from "../types/AtomFamilyAtom";
6
6
  export declare function getState<Value extends any, Args extends [any, ...any[]] = [any, ...any[]]>(atom: Atom<Value>, data: StoreData, initializedAtomsSet: Set<Atom>, circularDependencySet?: WeakSet<Selector>): Value;
7
7
  export declare function getState<Value extends any, Args extends [any, ...any[]] = [any, ...any[]]>(selector: Selector<Value>, data: StoreData, initializedAtomsSet: Set<Atom>, circularDependencySet?: WeakSet<Selector>): Value;
8
8
  export declare function getState<Value extends any, Args extends [any, ...any[]] = [any, ...any[]]>(family: AtomFamily<Value, Args>, data: StoreData, initializedAtomsSet: Set<Atom>, circularDependencySet?: WeakSet<Selector>): AtomFamilyAtom<Value, Args>[];
@@ -2,4 +2,4 @@ import type { Atom } from "../types/Atom";
2
2
  import type { AtomFamilyAtom } from "../types/AtomFamilyAtom";
3
3
  import type { StoreData } from "../types/StoreData";
4
4
  export declare const getAtomInitValue: <V = any>(atom: Atom<V>, data: StoreData, initializedAtomsSet: Set<Atom>) => any;
5
- export declare const initAtom: <Value extends unknown, Args extends [any, ...any[]] = [any, ...any[]]>(atom: Atom<Value> | AtomFamilyAtom<Value, Args>, data: StoreData, initializedAtomsSet: Set<Atom>) => void;
5
+ export declare const initAtom: <Value extends any, Args extends [any, ...any[]] = [any, ...any[]]>(atom: Atom<Value> | AtomFamilyAtom<Value, Args>, data: StoreData, initializedAtomsSet: Set<Atom>) => void;
@@ -1 +1 @@
1
- export declare const isFunction: (value: unknown) => value is Function;
1
+ export declare const isFunction: (value: any) => value is Function;
@@ -5,6 +5,21 @@ import type { Family } from "../types/Family";
5
5
  import type { Selector } from "../types/Selector";
6
6
  import type { StoreData } from "../types/StoreData";
7
7
  import type { Subscription } from "../types/Subscription";
8
- export declare const addFamilyAtomsToSet: (family: Family<any>, familyAtoms: Set<AtomFamilyAtom<any>>, data: StoreData) => void;
9
- export declare const propagateUpdatedAtoms: (atoms: (Atom<any> | AtomFamilyAtom<any, any> | AtomFamily<any, any>)[], data: StoreData, subscriptions?: Set<Subscription>, families?: Map<AtomFamily<any>, Set<AtomFamilyAtom<any>>>, isRecursive?: boolean) => void;
8
+ export declare const renderAtomFamilyIndex: (index: AtomFamilyIndex) => unknown[];
9
+ type AtomFamilyIndex = {
10
+ created: Map<AtomFamilyAtom<any, any>, Number>;
11
+ deleted: Map<AtomFamilyAtom<any, any>, Number>;
12
+ rendered: Map<AtomFamilyAtom<any, any>, Number> | null;
13
+ renderedArray: (AtomFamilyAtom<any, any>[] & {
14
+ __index: AtomFamilyIndex;
15
+ }) | null;
16
+ parentIndex?: AtomFamilyIndex;
17
+ };
18
+ export declare const cloneAtomFamilyIndex: (index: AtomFamilyIndex, parentIndexOverride?: AtomFamilyIndex) => AtomFamilyIndex;
19
+ export declare const createAtomFamilyIndex: (parentIndex?: AtomFamilyIndex) => AtomFamilyIndex;
20
+ export declare const deleteFamilyAtomsFromSet: (family: Family<any>, familyAtoms: Set<AtomFamilyAtom<any>>, data: StoreData, timestamp: number) => void;
21
+ export declare const addFamilyAtomsToSet: (family: Family<any>, familyAtoms: Set<AtomFamilyAtom<any>>, data: StoreData, timestamp: Number) => void;
22
+ export declare const propagateDeletedAtoms: (atoms: AtomFamilyAtom<any, any>[], data: StoreData, subscriptions?: Set<Subscription>, families?: Map<AtomFamily<any>, Set<AtomFamilyAtom<any>>>, timestamp?: number) => void;
23
+ export declare const propagateUpdatedAtoms: (atoms: (Atom<any> | AtomFamilyAtom<any, any> | AtomFamily<any, any>)[], data: StoreData, subscriptions?: Set<Subscription>, families?: Map<AtomFamily<any>, Set<AtomFamilyAtom<any>>>, isRecursive?: boolean, timestamp?: number, selectorsOnly?: boolean) => void;
10
24
  export declare const propagateDirtySelectors: (updatedAtoms: Atom[], selectors: Set<Selector>, data: StoreData, subscriptions: Set<Subscription>, families: Map<AtomFamily<any>, Set<AtomFamilyAtom<any>>>) => void;
25
+ export {};
@@ -1,8 +1,37 @@
1
1
  import type { Atom } from "../types/Atom";
2
+ import type { AtomFamilyAtom } from "../types/AtomFamilyAtom";
3
+ import type { GetValue } from "../types/GetValue";
2
4
  import type { StoreData } from "../types/StoreData";
3
5
  import type { TransactionFn } from "../types/TransactionFn";
4
- type GetAtomValue = {
5
- <V>(atom: Atom<V>): V;
6
- };
7
- export declare const transaction: (callback: TransactionFn, data: StoreData, autoCommit?: boolean, parentGetInTxnOrData?: GetAtomValue) => any;
8
- export {};
6
+ export declare class Transaction {
7
+ data: StoreData;
8
+ parentTransaction: Transaction | undefined;
9
+ dirty: boolean;
10
+ private _scopedTransactions;
11
+ private _initializedAtomsSet;
12
+ private _deleteSet;
13
+ private _selectorDependencies;
14
+ private _selectorCache;
15
+ private _atomMap;
16
+ constructor(data: StoreData, parentTransaction?: Transaction, childTransaction?: Transaction);
17
+ private valueFromTxnOrData;
18
+ get: GetValue;
19
+ set: <V>(atom: Atom<V>, value: V | ((currentValue: V) => V)) => V;
20
+ batchSetFamilyAtoms: (family: any, pairs: any) => void;
21
+ del: (atom: AtomFamilyAtom<any, any>) => void;
22
+ scope: (scopeId: string, callback: (txn: Transaction) => any) => any;
23
+ parentScope: (callback: (txn: Transaction) => any) => any;
24
+ reset: (atom: Atom) => any;
25
+ execute: (callback: TransactionFn, autoCommit?: boolean) => any;
26
+ private txnCommit;
27
+ commit: () => void;
28
+ private get atomMap();
29
+ private get selectorCache();
30
+ private get selectorDependencies();
31
+ private get deleteSet();
32
+ private get initializedAtomsSet();
33
+ private scopedTransaction;
34
+ private cloneFamilyIntoTxn;
35
+ private recursivlyUpdateAtomFamilyIndexes;
36
+ }
37
+ export declare const transaction: (callback: TransactionFn, data: StoreData) => any;
@@ -1,4 +1,4 @@
1
1
  import type { GetValue } from "./types/GetValue";
2
2
  import type { Selector } from "./types/Selector";
3
3
  import type { SelectorOptions } from "./types/SelectorOptions";
4
- export declare const selector: <Value extends unknown, FamilyArgs extends [any, ...any[]] = [any, ...any[]]>(get: (get: GetValue, storeId: string) => Value | Promise<Value>, options?: SelectorOptions<Value>) => Selector<Value, FamilyArgs>;
4
+ export declare const selector: <Value extends any, FamilyArgs extends [any, ...any[]] = [any, ...any[]]>(get: (get: GetValue, storeId: string) => Value | Promise<Value>, options?: SelectorOptions<Value>) => Selector<Value, FamilyArgs>;
@@ -1,4 +1,4 @@
1
1
  import type { GetValue } from "./types/GetValue";
2
2
  import type { SelectorFamily } from "./types/SelectorFamily";
3
3
  import type { SelectorOptions } from "./types/SelectorOptions";
4
- export declare const selectorFamily: <Value extends unknown, Args extends [any, ...any[]] = [any, ...any[]]>(callback: (...args: Args) => (get: GetValue) => Value | Promise<Value>, options?: SelectorOptions<Value>) => SelectorFamily<Value, Args>;
4
+ export declare const selectorFamily: <Value extends any, Args extends [any, ...any[]] = [any, ...any[]]>(callback: (...args: Args) => (get: GetValue) => Value | Promise<Value>, options?: SelectorOptions<Value>) => SelectorFamily<Value, Args>;
@@ -10,5 +10,6 @@ export type Atom<Value = unknown> = {
10
10
  onSet?: AtomOnSet<Value>;
11
11
  onMount?: () => void | (() => void);
12
12
  maxAge?: number;
13
+ mutable?: boolean;
13
14
  staleWhileRevalidate?: number;
14
15
  };
@@ -5,5 +5,6 @@ export type AtomFamily<Value extends any, Args extends [any, ...any[]] = [any, .
5
5
  release: (...args: Args) => void;
6
6
  equal: EqualFunc<Value>;
7
7
  name?: string;
8
+ mutable?: boolean;
8
9
  __valdresAtomFamilyMap: Map<Value, AtomFamilyAtom<Value, Args>>;
9
10
  };
@@ -8,6 +8,7 @@ export type AtomOptions<Value = unknown> = {
8
8
  onSet?: AtomOnSet<Value>;
9
9
  onMount?: () => () => void;
10
10
  maxAge?: number;
11
+ mutable?: boolean;
11
12
  staleWhileRevalidate?: number;
12
13
  equal?: EqualFunc<Value>;
13
14
  };
@@ -1,2 +1,2 @@
1
- import type { TransactionInterface } from "./TransactionInterface";
2
- export type TransactionFn = (args: TransactionInterface) => any;
1
+ import type { Transaction } from "../lib/transaction";
2
+ export type TransactionFn = (args: Transaction) => any;
@@ -1,2 +1,2 @@
1
1
  import type { AtomFamily } from "../types/AtomFamily";
2
- export declare const isAtomFamily: <Value extends unknown, Args extends [any, ...any[]] = [any, ...any[]]>(state: any) => state is AtomFamily<Value, Args>;
2
+ export declare const isAtomFamily: <Value extends any, Args extends [any, ...any[]] = [any, ...any[]]>(state: any) => state is AtomFamily<Value, Args>;
@@ -1,2 +1,2 @@
1
1
  import type { AtomFamilyAtom } from "../types/AtomFamilyAtom";
2
- export declare const isFamilyAtom: <Value extends unknown, Args extends [any, ...any[]] = [any, ...any[]]>(state: any) => state is AtomFamilyAtom<Value, Args>;
2
+ export declare const isFamilyAtom: <Value extends any, Args extends [any, ...any[]] = [any, ...any[]]>(state: any) => state is AtomFamilyAtom<Value, Args>;
@@ -1,2 +1,2 @@
1
1
  import type { AtomFamilySelector } from "../types/AtomFamilySelector";
2
- export declare const isFamilySelector: <Value extends unknown, Args extends [any, ...any[]] = [any, ...any[]]>(state: any) => state is AtomFamilySelector<Value, Args>;
2
+ export declare const isFamilySelector: <Value extends any, Args extends [any, ...any[]] = [any, ...any[]]>(state: any) => state is AtomFamilySelector<Value, Args>;
@@ -1,3 +1,3 @@
1
1
  import type { AtomFamilyAtom } from "../types/AtomFamilyAtom";
2
2
  import type { AtomFamilySelector } from "../types/AtomFamilySelector";
3
- export declare const isFamilyState: <Value extends unknown, Args extends [any, ...any[]] = [any, ...any[]]>(state: any) => state is AtomFamilyAtom<Value, Args> | AtomFamilySelector<Value, Args>;
3
+ export declare const isFamilyState: <Value extends any, Args extends [any, ...any[]] = [any, ...any[]]>(state: any) => state is AtomFamilyAtom<Value, Args> | AtomFamilySelector<Value, Args>;
@@ -1,2 +1,2 @@
1
1
  import type { SelectorFamily } from "../types/SelectorFamily";
2
- export declare const isSelectorFamily: <Value extends unknown, Args extends [any, ...any[]] = [any, ...any[]]>(state: any) => state is SelectorFamily<Value, Args>;
2
+ export declare const isSelectorFamily: <Value extends any, Args extends [any, ...any[]] = [any, ...any[]]>(state: any) => state is SelectorFamily<Value, Args>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "valdres",
3
- "version": "0.2.0-pre.2",
3
+ "version": "0.2.0-pre.21",
4
4
  "license": "MIT",
5
5
  "author": {
6
6
  "name": "Eigil Sagafos"