valdres 0.2.0-alpha.47 → 0.2.0-alpha.49

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
@@ -194,22 +194,22 @@ var initAtom = (atom, data) => {
194
194
  };
195
195
 
196
196
  // src/lib/getState.ts
197
- function getState(state, data) {
197
+ function getState(state, data, circularDependencySet) {
198
198
  if (data.values.has(state))
199
199
  return data.values.get(state);
200
200
  if (isAtom(state)) {
201
201
  if ("parent" in data)
202
- return getState(state, data.parent);
202
+ return getState(state, data.parent, circularDependencySet);
203
203
  return initAtom(state, data);
204
204
  }
205
205
  if (isSelector(state))
206
- return initSelector(state, data);
206
+ return initSelector(state, data, circularDependencySet);
207
207
  if (isAtomFamily(state)) {
208
208
  if ("parent" in data) {
209
209
  const closestData = findClosestStoreWithAtomInitialized(state.__keysAtom, data);
210
- return getState(state.__keysSelector, closestData);
210
+ return getState(state.__keysSelector, closestData, circularDependencySet);
211
211
  }
212
- return getState(state.__keysSelector, data);
212
+ return getState(state.__keysSelector, data, circularDependencySet);
213
213
  }
214
214
  if (isSelectorFamily(state)) {
215
215
  const array = Array.from(state.__valdresSelectorFamilyMap.keys());
@@ -236,6 +236,36 @@ class SuspendAndWaitForResolveError extends Error {
236
236
  this.promise = promise;
237
237
  }
238
238
  }
239
+ var generateSelectorTrace = (selectors) => {
240
+ const lastIndex = selectors.length - 1;
241
+ return selectors.map((selector, index) => {
242
+ const name = selector.name ?? "Anonymous Selector";
243
+ if (index === 0) {
244
+ return `[START] ${name}`;
245
+ } else if (index === lastIndex) {
246
+ return `[CRASH] ${name}`;
247
+ } else {
248
+ return ` ${" ".repeat(index)}${name}`;
249
+ }
250
+ }).join(`
251
+ `);
252
+ };
253
+
254
+ class CircularDependencyError extends Error {
255
+ selectors;
256
+ constructor() {
257
+ super();
258
+ this.selectors = [];
259
+ }
260
+ track(selector) {
261
+ this.selectors.push(selector);
262
+ }
263
+ get message() {
264
+ const firstSelectorName = this.selectors[0].name ?? "Anonymous Selector";
265
+ return `Circular dependency detected in '${firstSelectorName}'
266
+ ${generateSelectorTrace(this.selectors)}`;
267
+ }
268
+ }
239
269
  var getOrInitConsumersSet = (state, data) => {
240
270
  const set = data.stateConsumers.get(state);
241
271
  if (set)
@@ -244,12 +274,16 @@ var getOrInitConsumersSet = (state, data) => {
244
274
  data.stateConsumers.set(state, newSet);
245
275
  return newSet;
246
276
  };
247
- var evaluateSelector = (selector, data) => {
277
+ var evaluateSelector = (selector, data, circularDependencyMap = new WeakSet) => {
248
278
  const updatedDependencies = new Set;
279
+ if (circularDependencyMap.has(selector)) {
280
+ throw new CircularDependencyError;
281
+ }
282
+ circularDependencyMap.add(selector);
249
283
  let result;
250
284
  try {
251
285
  result = selector.get((state) => {
252
- const value = getState(state, data);
286
+ const value = getState(state, data, circularDependencyMap);
253
287
  updatedDependencies.add(state);
254
288
  if (isPromiseLike(value))
255
289
  throw new SuspendAndWaitForResolveError(value);
@@ -291,8 +325,15 @@ var handleSelectorResult = (value, selector, data) => {
291
325
  return value;
292
326
  }
293
327
  };
294
- var initSelector = (selector, data) => {
295
- const tmpValue = evaluateSelector(selector, data);
328
+ var initSelector = (selector, data, circularDependencySet = new WeakSet) => {
329
+ let tmpValue;
330
+ try {
331
+ tmpValue = evaluateSelector(selector, data, circularDependencySet);
332
+ } catch (e) {
333
+ if (e instanceof CircularDependencyError)
334
+ e.track(selector);
335
+ throw e;
336
+ }
296
337
  const value = handleSelectorResult(tmpValue, selector, data);
297
338
  if (data.expiredValues.has(selector)) {
298
339
  const expiredValue = data.expiredValues.get(selector);
@@ -566,6 +607,13 @@ var recursivlyResetTxnSelectorCache = (state, txnSubscribers, txnSelectorCache)
566
607
  }
567
608
  }
568
609
  };
610
+ var captureScopedTransaction = (scopedData) => {
611
+ let txn;
612
+ transaction((scopedTxn) => {
613
+ txn = scopedTxn;
614
+ }, scopedData, false);
615
+ return txn;
616
+ };
569
617
  var transaction = (callback, data, autoCommit = true) => {
570
618
  let txnAtomMap = new Map;
571
619
  let txnSelectorCache = new Map;
@@ -624,7 +672,7 @@ var transaction = (callback, data, autoCommit = true) => {
624
672
  dirtySelectors.clear();
625
673
  if (scopedTransactions) {
626
674
  for (const scopedTxn of Object.values(scopedTransactions)) {
627
- scopedTxn[3]();
675
+ scopedTxn.commit();
628
676
  }
629
677
  }
630
678
  };
@@ -636,19 +684,13 @@ var transaction = (callback, data, autoCommit = true) => {
636
684
  scope: (scopeId, callback2) => {
637
685
  if (scopeId in data.scopes) {
638
686
  const scopedData = data.scopes[scopeId];
639
- if (scopedTransactions?.[scopeId] === undefined) {
640
- scopedTransactions ||= {};
641
- transaction(({ set, get, reset, commit: commit2, scope }) => {
642
- scopedTransactions[scopeId] = [
643
- set,
644
- get,
645
- reset,
646
- commit2,
647
- scope
648
- ];
649
- }, scopedData, false);
687
+ if (scopedTransactions === undefined) {
688
+ scopedTransactions = {};
689
+ }
690
+ if (scopedTransactions[scopeId] === undefined) {
691
+ scopedTransactions[scopeId] = captureScopedTransaction(scopedData);
650
692
  }
651
- callback2(...scopedTransactions[scopeId]);
693
+ return callback2(scopedTransactions[scopeId]);
652
694
  } else {
653
695
  throw new Error(`Scope '${scopeId}' not found. Registered scopes: ${Object.keys(data.scopes).join(", ")}`);
654
696
  }
@@ -727,7 +769,7 @@ var store = (id) => {
727
769
  return storeFromStoreData(data);
728
770
  };
729
771
  // package.json
730
- var version = "0.2.0-alpha.46";
772
+ var version = "0.2.0-alpha.48";
731
773
 
732
774
  // src/globalStore.ts
733
775
  if (globalThis.__valdres__) {
@@ -2,6 +2,6 @@ import type { Atom } from "../types/Atom";
2
2
  import type { AtomFamily } from "../types/AtomFamily";
3
3
  import type { Selector } from "../types/Selector";
4
4
  import type { StoreData } from "../types/StoreData";
5
- export declare function getState<V, K>(atom: Atom<V>, data: StoreData): V;
6
- export declare function getState<V, K>(selector: Selector<V>, data: StoreData): V;
7
- export declare function getState<V, K>(family: AtomFamily<V, K>, data: StoreData): K[];
5
+ export declare function getState<V, K>(atom: Atom<V>, data: StoreData, circularDependencySet?: WeakSet<Selector>): V;
6
+ export declare function getState<V, K>(selector: Selector<V>, data: StoreData, circularDependencySet?: WeakSet<Selector>): V;
7
+ export declare function getState<V, K>(family: AtomFamily<V, K>, data: StoreData, circularDependencySet?: WeakSet<Selector>): K[];
@@ -1,4 +1,3 @@
1
1
  import type { StoreData } from "../types/StoreData";
2
2
  import type { Selector } from "../types/Selector";
3
- export declare const reEvaluateSelector: <V>(selector: Selector<V>, data: StoreData) => void;
4
- export declare const initSelector: <V>(selector: Selector<V>, data: StoreData) => V | Promise<V>;
3
+ export declare const initSelector: <V>(selector: Selector<V>, data: StoreData, circularDependencySet?: WeakSet<WeakKey>) => V | Promise<V>;
@@ -1,3 +1,3 @@
1
1
  import type { StoreData } from "../types/StoreData";
2
2
  import type { TransactionFn } from "../types/TransactionFn";
3
- export declare const transaction: (callback: TransactionFn, data: StoreData, autoCommit?: boolean) => void;
3
+ export declare const transaction: (callback: TransactionFn, data: StoreData, autoCommit?: boolean) => any;
@@ -1,2 +1,2 @@
1
1
  import type { TransactionInterface } from "./TransactionInterface";
2
- export type TransactionFn = (args: TransactionInterface) => void;
2
+ export type TransactionFn = (args: TransactionInterface) => any;
@@ -7,5 +7,5 @@ export type TransactionInterface = {
7
7
  get: GetValue;
8
8
  reset: ResetAtom;
9
9
  commit: () => void;
10
- scope: (scopeId: string, callback: TransactionFn) => void;
10
+ scope: <Callback extends TransactionFn>(scopeId: string, callback: Callback) => ReturnType<Callback>;
11
11
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "valdres",
3
- "version": "0.2.0-alpha.47",
3
+ "version": "0.2.0-alpha.49",
4
4
  "license": "MIT",
5
5
  "author": {
6
6
  "name": "Eigil Sagafos"
@@ -36,5 +36,5 @@
36
36
  "access": "public",
37
37
  "registry": "https://registry.npmjs.org/"
38
38
  },
39
- "gitHead": "c26d007ebb65bdcfb7fd81122cc51b4bcc8f4d22"
39
+ "gitHead": "1aa6c47faaaf0158c7ff7ed18e3b20648c183e24"
40
40
  }