From 6f26046a2dc3956f73bd9cf46299973b27d6231e Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Sun, 26 Apr 2026 13:12:36 +0200 Subject: [PATCH 01/17] feat(core): add evaluate util --- packages/store/src/evaluate.ts | 61 +++++++++ packages/store/tests/evaluate.spec.ts | 172 ++++++++++++++++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 packages/store/src/evaluate.ts create mode 100644 packages/store/tests/evaluate.spec.ts diff --git a/packages/store/src/evaluate.ts b/packages/store/src/evaluate.ts new file mode 100644 index 00000000..5974ce6d --- /dev/null +++ b/packages/store/src/evaluate.ts @@ -0,0 +1,61 @@ +export function evaluate(objA: T, objB: T) { + if (Object.is(objA, objB)) { + return true + } + + if ( + typeof objA !== 'object' || + objA === null || + typeof objB !== 'object' || + objB === null + ) { + return false + } + + if (objA instanceof Date && objB instanceof Date) { + return objA.getTime() === objB.getTime() + } + + if (objA instanceof File && objB instanceof File) { + return ( + objA.name === objB.name && + objA.size === objB.size && + objA.type === objB.type && + objA.lastModified === objB.lastModified + ) + } + + if (objA instanceof Map && objB instanceof Map) { + if (objA.size !== objB.size) return false + for (const [k, v] of objA) { + if (!objB.has(k) || !Object.is(v, objB.get(k))) return false + } + return true + } + + if (objA instanceof Set && objB instanceof Set) { + if (objA.size !== objB.size) return false + for (const v of objA) { + if (!objB.has(v)) return false + } + return true + } + + const keysA = Object.keys(objA as object) + const keysB = Object.keys(objB as object) + + if (keysA.length !== keysB.length) { + return false + } + + for (const key of keysA) { + if ( + !keysB.includes(key) || + !evaluate(objA[key as keyof T], objB[key as keyof T]) + ) { + return false + } + } + + return true +} diff --git a/packages/store/tests/evaluate.spec.ts b/packages/store/tests/evaluate.spec.ts new file mode 100644 index 00000000..05d0e99a --- /dev/null +++ b/packages/store/tests/evaluate.spec.ts @@ -0,0 +1,172 @@ +import { describe, expect, it } from 'vitest' +import { evaluate } from '../src/evaluate' + +describe('evaluate', () => { + it('should test equality between primitives', () => { + const numbersTrue = evaluate(1, 1) + expect(numbersTrue).toEqual(true) + + const stringFalse = evaluate('uh oh', '') + expect(stringFalse).toEqual(false) + + const boolTrue = evaluate(true, true) + expect(boolTrue).toEqual(true) + + const nullFalse = evaluate(null, {}) + expect(nullFalse).toEqual(false) + + const undefinedFalse = evaluate(undefined, null) + expect(undefinedFalse).toEqual(false) + }) + + it('should test equality between arrays', () => { + const arrayTrue = evaluate([], []) + expect(arrayTrue).toEqual(true) + + const arrayDeepSearchTrue = evaluate([[1]], [[1]]) + expect(arrayDeepSearchTrue).toEqual(true) + + const arrayFalse = evaluate([], ['']) + expect(arrayFalse).toEqual(false) + + const arrayDeepFalse = evaluate([[1]], []) + expect(arrayDeepFalse).toEqual(false) + + const arrayComplexFalse = evaluate([[{ test: 'true' }], null], [[1], {}]) + expect(arrayComplexFalse).toEqual(false) + + const arrayComplexTrue = evaluate( + [[{ test: 'true' }], null], + [[{ test: 'true' }], null], + ) + expect(arrayComplexTrue).toEqual(true) + }) + + it('should test equality between objects', () => { + const objTrue = evaluate({ test: 'same' }, { test: 'same' }) + expect(objTrue).toEqual(true) + + const objFalse = evaluate({ test: 'not' }, { test: 'same' }) + expect(objFalse).toEqual(false) + + const objDeepFalse = evaluate({ test: 'not' }, { test: { test: 'same' } }) + expect(objDeepFalse).toEqual(false) + + const objDeepArrFalse = evaluate({ test: [] }, { test: [[]] }) + expect(objDeepArrFalse).toEqual(false) + + const objNullFalse = evaluate({ test: '' }, null) + expect(objNullFalse).toEqual(false) + + const objComplexFalse = evaluate( + { test: { testTwo: '' }, arr: [[1]] }, + { test: { testTwo: false }, arr: [[1], [0]] }, + ) + expect(objComplexFalse).toEqual(false) + + const objComplexTrue = evaluate( + { test: { testTwo: '' }, arr: [[1]] }, + { test: { testTwo: '' }, arr: [[1]] }, + ) + expect(objComplexTrue).toEqual(true) + }) + + it('should test equality between Date objects', () => { + const date1 = new Date('2025-01-01T00:00:00.000Z') + const date2 = new Date('2025-01-01T00:00:00.000Z') + const date3 = new Date('2025-01-02T00:00:00.000Z') + + const dateTrue = evaluate(date1, date2) + expect(dateTrue).toEqual(true) + + const dateFalse = evaluate(date1, date3) + expect(dateFalse).toEqual(false) + + const dateObjectTrue = evaluate({ date: date1 }, { date: date2 }) + expect(dateObjectTrue).toEqual(true) + + const dateObjectFalse = evaluate({ date: date1 }, { date: date3 }) + expect(dateObjectFalse).toEqual(false) + }) + + it('should test equality between Map objects', () => { + const map1 = new Map([ + ['a', 1], + ['b', 2], + ]) + const map2 = new Map([ + ['a', 1], + ['b', 2], + ]) + expect(evaluate(map1, map2)).toEqual(true) + + const emptyMap1 = new Map() + const emptyMap2 = new Map() + expect(evaluate(emptyMap1, emptyMap2)).toEqual(true) + + const mapSmall = new Map([['a', 1]]) + const mapLarge = new Map([ + ['a', 1], + ['b', 2], + ]) + expect(evaluate(mapSmall, mapLarge)).toEqual(false) + + const mapA = new Map([ + ['a', 1], + ['b', 2], + ]) + const mapC = new Map([ + ['a', 1], + ['c', 2], + ]) + expect(evaluate(mapA, mapC)).toEqual(false) + + const mapVal1 = new Map([ + ['a', 1], + ['b', 2], + ]) + const mapVal2 = new Map([ + ['a', 1], + ['b', 3], + ]) + expect(evaluate(mapVal1, mapVal2)).toEqual(false) + }) + + it('should test equality between Set objects', () => { + const set1 = new Set([1, 2, 3]) + const set2 = new Set([1, 2, 3]) + expect(evaluate(set1, set2)).toEqual(true) + + const emptySet1 = new Set() + const emptySet2 = new Set() + expect(evaluate(emptySet1, emptySet2)).toEqual(true) + + const setSmall = new Set([1, 2]) + const setLarge = new Set([1, 2, 3]) + expect(evaluate(setSmall, setLarge)).toEqual(false) + + const setA = new Set([1, 2, 3]) + const setB = new Set([1, 2, 4]) + expect(evaluate(setA, setB)).toEqual(false) + }) + + it('should test equality between File objects', () => { + const file1 = new File(['hello'], 'hello.txt', { type: 'text/plain' }) + const file2 = new File(['hello'], 'hello.txt', { type: 'text/plain' }) + const fileDiffName = new File(['hello'], 'world.txt', { + type: 'text/plain', + }) + const fileDiffType = new File(['hello'], 'hello.txt', { type: 'text/html' }) + const fileDiffSize = new File(['hello world'], 'hello.txt', { + type: 'text/plain', + }) + + expect(evaluate(file1, file2)).toEqual(true) + expect(evaluate(file1, fileDiffName)).toEqual(false) + expect(evaluate(file1, fileDiffType)).toEqual(false) + expect(evaluate(file1, fileDiffSize)).toEqual(false) + + expect(evaluate({ file: file1 }, { file: file2 })).toEqual(true) + expect(evaluate({ file: file1 }, { file: fileDiffName })).toEqual(false) + }) +}) From 5c9612aee04b1c306599aa04de0a0ca8a94bb215 Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Mon, 27 Apr 2026 16:03:16 +0200 Subject: [PATCH 02/17] chore: pr comment --- packages/store/src/evaluate.ts | 6 +++++- packages/store/tests/evaluate.spec.ts | 13 ++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/store/src/evaluate.ts b/packages/store/src/evaluate.ts index 5974ce6d..fe79c299 100644 --- a/packages/store/src/evaluate.ts +++ b/packages/store/src/evaluate.ts @@ -16,7 +16,11 @@ export function evaluate(objA: T, objB: T) { return objA.getTime() === objB.getTime() } - if (objA instanceof File && objB instanceof File) { + if ( + typeof File !== 'undefined' && + objA instanceof File && + objB instanceof File + ) { return ( objA.name === objB.name && objA.size === objB.size && diff --git a/packages/store/tests/evaluate.spec.ts b/packages/store/tests/evaluate.spec.ts index 05d0e99a..b7ba7fde 100644 --- a/packages/store/tests/evaluate.spec.ts +++ b/packages/store/tests/evaluate.spec.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from 'vitest' +import { describe, expect, it, vi } from 'vitest' import { evaluate } from '../src/evaluate' describe('evaluate', () => { @@ -169,4 +169,15 @@ describe('evaluate', () => { expect(evaluate({ file: file1 }, { file: file2 })).toEqual(true) expect(evaluate({ file: file1 }, { file: fileDiffName })).toEqual(false) }) + + it('should not throw a runtime error when File is undefined in the environment', () => { + vi.stubGlobal('File', undefined) + + const file1 = { name: 'hello.txt', size: 5, type: 'text/plain', lastModified: 0 } + const file2 = { name: 'hello.txt', size: 5, type: 'text/plain', lastModified: 0 } + + expect(() => evaluate(file1, file2)).not.toThrow() + + vi.unstubAllGlobals() + }) }) From b436a80008ad860af7898f58bf93faeb1171c944 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 14:04:12 +0000 Subject: [PATCH 03/17] ci: apply automated fixes and generate docs --- packages/store/tests/evaluate.spec.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/store/tests/evaluate.spec.ts b/packages/store/tests/evaluate.spec.ts index b7ba7fde..7c700cea 100644 --- a/packages/store/tests/evaluate.spec.ts +++ b/packages/store/tests/evaluate.spec.ts @@ -173,11 +173,21 @@ describe('evaluate', () => { it('should not throw a runtime error when File is undefined in the environment', () => { vi.stubGlobal('File', undefined) - const file1 = { name: 'hello.txt', size: 5, type: 'text/plain', lastModified: 0 } - const file2 = { name: 'hello.txt', size: 5, type: 'text/plain', lastModified: 0 } + const file1 = { + name: 'hello.txt', + size: 5, + type: 'text/plain', + lastModified: 0, + } + const file2 = { + name: 'hello.txt', + size: 5, + type: 'text/plain', + lastModified: 0, + } expect(() => evaluate(file1, file2)).not.toThrow() - + vi.unstubAllGlobals() }) }) From 31a570c9eb9d634ebaec059efe846019d3222a5a Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Mon, 27 Apr 2026 17:56:11 +0200 Subject: [PATCH 04/17] chore: protect against mixed inputs --- packages/store/src/evaluate.ts | 6 +++++- packages/store/tests/evaluate.spec.ts | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/store/src/evaluate.ts b/packages/store/src/evaluate.ts index fe79c299..616c4700 100644 --- a/packages/store/src/evaluate.ts +++ b/packages/store/src/evaluate.ts @@ -1,4 +1,4 @@ -export function evaluate(objA: T, objB: T) { +export function evaluate(objA: T, objB: T, config?: {depth: 'shallow'| 'deep'}) { if (Object.is(objA, objB)) { return true } @@ -45,6 +45,10 @@ export function evaluate(objA: T, objB: T) { return true } + if (Object.getPrototypeOf(objA) !== Object.getPrototypeOf(objB)) { + return false + } + const keysA = Object.keys(objA as object) const keysB = Object.keys(objB as object) diff --git a/packages/store/tests/evaluate.spec.ts b/packages/store/tests/evaluate.spec.ts index 7c700cea..43066e5e 100644 --- a/packages/store/tests/evaluate.spec.ts +++ b/packages/store/tests/evaluate.spec.ts @@ -170,6 +170,15 @@ describe('evaluate', () => { expect(evaluate({ file: file1 }, { file: fileDiffName })).toEqual(false) }) + it('should return false for runtime cross-type comparisons', () => { + expect(evaluate(new Date(), new Map())).toEqual(false) + expect(evaluate(new Date(), new Set())).toEqual(false) + expect(evaluate(new Map(), new Set())).toEqual(false) + expect(evaluate(new Date(), {})).toEqual(false) + expect(evaluate(new Map(), {})).toEqual(false) + expect(evaluate(new Set(), {})).toEqual(false) + }) + it('should not throw a runtime error when File is undefined in the environment', () => { vi.stubGlobal('File', undefined) From 0fe90520d0810595cb5b68d4d54493ccc97e138a Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:57:37 +0000 Subject: [PATCH 05/17] ci: apply automated fixes and generate docs --- docs/reference/classes/ReadonlyStore.md | 124 ------------- docs/reference/classes/Store.md | 158 ---------------- docs/reference/functions/batch.md | 22 --- docs/reference/functions/createAsyncAtom.md | 32 ---- docs/reference/functions/createAtom.md | 62 ------- docs/reference/functions/createStore.md | 86 --------- docs/reference/functions/flush.md | 16 -- docs/reference/functions/shallow.md | 32 ---- docs/reference/functions/toObserver.md | 39 ---- docs/reference/index.md | 43 ----- docs/reference/interfaces/Atom.md | 62 ------- docs/reference/interfaces/AtomOptions.md | 38 ---- docs/reference/interfaces/BaseAtom.md | 55 ------ docs/reference/interfaces/InternalBaseAtom.md | 88 --------- .../interfaces/InternalReadonlyAtom.md | 172 ------------------ .../interfaces/InteropSubscribable.md | 38 ---- docs/reference/interfaces/Readable.md | 51 ------ docs/reference/interfaces/ReadonlyAtom.md | 60 ------ docs/reference/interfaces/Subscribable.md | 38 ---- docs/reference/interfaces/Subscription.md | 22 --- docs/reference/type-aliases/AnyAtom.md | 12 -- docs/reference/type-aliases/Observer.md | 72 -------- docs/reference/type-aliases/Selection.md | 18 -- docs/reference/type-aliases/StoreAction.md | 22 --- docs/reference/type-aliases/StoreActionMap.md | 12 -- .../type-aliases/StoreActionsFactory.md | 38 ---- packages/store/src/evaluate.ts | 6 +- 27 files changed, 5 insertions(+), 1413 deletions(-) delete mode 100644 docs/reference/classes/ReadonlyStore.md delete mode 100644 docs/reference/classes/Store.md delete mode 100644 docs/reference/functions/batch.md delete mode 100644 docs/reference/functions/createAsyncAtom.md delete mode 100644 docs/reference/functions/createAtom.md delete mode 100644 docs/reference/functions/createStore.md delete mode 100644 docs/reference/functions/flush.md delete mode 100644 docs/reference/functions/shallow.md delete mode 100644 docs/reference/functions/toObserver.md delete mode 100644 docs/reference/index.md delete mode 100644 docs/reference/interfaces/Atom.md delete mode 100644 docs/reference/interfaces/AtomOptions.md delete mode 100644 docs/reference/interfaces/BaseAtom.md delete mode 100644 docs/reference/interfaces/InternalBaseAtom.md delete mode 100644 docs/reference/interfaces/InternalReadonlyAtom.md delete mode 100644 docs/reference/interfaces/InteropSubscribable.md delete mode 100644 docs/reference/interfaces/Readable.md delete mode 100644 docs/reference/interfaces/ReadonlyAtom.md delete mode 100644 docs/reference/interfaces/Subscribable.md delete mode 100644 docs/reference/interfaces/Subscription.md delete mode 100644 docs/reference/type-aliases/AnyAtom.md delete mode 100644 docs/reference/type-aliases/Observer.md delete mode 100644 docs/reference/type-aliases/Selection.md delete mode 100644 docs/reference/type-aliases/StoreAction.md delete mode 100644 docs/reference/type-aliases/StoreActionMap.md delete mode 100644 docs/reference/type-aliases/StoreActionsFactory.md diff --git a/docs/reference/classes/ReadonlyStore.md b/docs/reference/classes/ReadonlyStore.md deleted file mode 100644 index e23ee15c..00000000 --- a/docs/reference/classes/ReadonlyStore.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -id: ReadonlyStore -title: ReadonlyStore ---- - -# Class: ReadonlyStore\ - -Defined in: [store.ts:59](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L59) - -## Type Parameters - -### T - -`T` - -## Implements - -- `Omit`\<[`Store`](Store.md)\<`T`\>, `"setState"` \| `"actions"`\> - -## Constructors - -### Constructor - -```ts -new ReadonlyStore(getValue): ReadonlyStore; -``` - -Defined in: [store.ts:64](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L64) - -#### Parameters - -##### getValue - -(`prev?`) => `T` - -#### Returns - -`ReadonlyStore`\<`T`\> - -### Constructor - -```ts -new ReadonlyStore(initialValue): ReadonlyStore; -``` - -Defined in: [store.ts:65](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L65) - -#### Parameters - -##### initialValue - -`T` - -#### Returns - -`ReadonlyStore`\<`T`\> - -## Accessors - -### state - -#### Get Signature - -```ts -get state(): T; -``` - -Defined in: [store.ts:73](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L73) - -##### Returns - -`T` - -#### Implementation of - -```ts -Omit.state -``` - -## Methods - -### get() - -```ts -get(): T; -``` - -Defined in: [store.ts:76](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L76) - -#### Returns - -`T` - -#### Implementation of - -```ts -Omit.get -``` - -*** - -### subscribe() - -```ts -subscribe(observerOrFn): Subscription; -``` - -Defined in: [store.ts:79](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L79) - -#### Parameters - -##### observerOrFn - -[`Observer`](../type-aliases/Observer.md)\<`T`\> | (`value`) => `void` - -#### Returns - -[`Subscription`](../interfaces/Subscription.md) - -#### Implementation of - -```ts -Omit.subscribe -``` diff --git a/docs/reference/classes/Store.md b/docs/reference/classes/Store.md deleted file mode 100644 index 346926a3..00000000 --- a/docs/reference/classes/Store.md +++ /dev/null @@ -1,158 +0,0 @@ ---- -id: Store -title: Store ---- - -# Class: Store\ - -Defined in: [store.ts:15](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L15) - -## Type Parameters - -### T - -`T` - -### TActions - -`TActions` *extends* [`StoreActionMap`](../type-aliases/StoreActionMap.md) = `never` - -## Constructors - -### Constructor - -```ts -new Store(getValue): Store; -``` - -Defined in: [store.ts:18](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L18) - -#### Parameters - -##### getValue - -(`prev?`) => `T` - -#### Returns - -`Store`\<`T`, `TActions`\> - -### Constructor - -```ts -new Store(initialValue): Store; -``` - -Defined in: [store.ts:19](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L19) - -#### Parameters - -##### initialValue - -`T` - -#### Returns - -`Store`\<`T`, `TActions`\> - -### Constructor - -```ts -new Store(initialValue, actionsFactory): Store; -``` - -Defined in: [store.ts:20](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L20) - -#### Parameters - -##### initialValue - -`NonFunction`\<`T`\> - -##### actionsFactory - -[`StoreActionsFactory`](../type-aliases/StoreActionsFactory.md)\<`T`, `TActions`\> - -#### Returns - -`Store`\<`T`, `TActions`\> - -## Properties - -### actions - -```ts -readonly actions: TActions; -``` - -Defined in: [store.ts:17](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L17) - -## Accessors - -### state - -#### Get Signature - -```ts -get state(): T; -``` - -Defined in: [store.ts:46](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L46) - -##### Returns - -`T` - -## Methods - -### get() - -```ts -get(): T; -``` - -Defined in: [store.ts:49](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L49) - -#### Returns - -`T` - -*** - -### setState() - -```ts -setState(updater): void; -``` - -Defined in: [store.ts:43](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L43) - -#### Parameters - -##### updater - -(`prev`) => `T` - -#### Returns - -`void` - -*** - -### subscribe() - -```ts -subscribe(observerOrFn): Subscription; -``` - -Defined in: [store.ts:52](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L52) - -#### Parameters - -##### observerOrFn - -[`Observer`](../type-aliases/Observer.md)\<`T`\> | (`value`) => `void` - -#### Returns - -[`Subscription`](../interfaces/Subscription.md) diff --git a/docs/reference/functions/batch.md b/docs/reference/functions/batch.md deleted file mode 100644 index b77cfeb9..00000000 --- a/docs/reference/functions/batch.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -id: batch -title: batch ---- - -# Function: batch() - -```ts -function batch(fn): void; -``` - -Defined in: [atom.ts:62](https://github.com/TanStack/store/blob/main/packages/store/src/atom.ts#L62) - -## Parameters - -### fn - -() => `void` - -## Returns - -`void` diff --git a/docs/reference/functions/createAsyncAtom.md b/docs/reference/functions/createAsyncAtom.md deleted file mode 100644 index 0e8eca80..00000000 --- a/docs/reference/functions/createAsyncAtom.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -id: createAsyncAtom -title: createAsyncAtom ---- - -# Function: createAsyncAtom() - -```ts -function createAsyncAtom(getValue, options?): ReadonlyAtom>; -``` - -Defined in: [atom.ts:100](https://github.com/TanStack/store/blob/main/packages/store/src/atom.ts#L100) - -## Type Parameters - -### T - -`T` - -## Parameters - -### getValue - -() => `Promise`\<`T`\> - -### options? - -[`AtomOptions`](../interfaces/AtomOptions.md)\<`AsyncAtomState`\<`T`, `unknown`\>\> - -## Returns - -[`ReadonlyAtom`](../interfaces/ReadonlyAtom.md)\<`AsyncAtomState`\<`T`, `unknown`\>\> diff --git a/docs/reference/functions/createAtom.md b/docs/reference/functions/createAtom.md deleted file mode 100644 index ad37a11c..00000000 --- a/docs/reference/functions/createAtom.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -id: createAtom -title: createAtom ---- - -# Function: createAtom() - -## Call Signature - -```ts -function createAtom(getValue, options?): ReadonlyAtom; -``` - -Defined in: [atom.ts:138](https://github.com/TanStack/store/blob/main/packages/store/src/atom.ts#L138) - -### Type Parameters - -#### T - -`T` - -### Parameters - -#### getValue - -(`prev?`) => `T` - -#### options? - -[`AtomOptions`](../interfaces/AtomOptions.md)\<`T`\> - -### Returns - -[`ReadonlyAtom`](../interfaces/ReadonlyAtom.md)\<`T`\> - -## Call Signature - -```ts -function createAtom(initialValue, options?): Atom; -``` - -Defined in: [atom.ts:142](https://github.com/TanStack/store/blob/main/packages/store/src/atom.ts#L142) - -### Type Parameters - -#### T - -`T` - -### Parameters - -#### initialValue - -`T` - -#### options? - -[`AtomOptions`](../interfaces/AtomOptions.md)\<`T`\> - -### Returns - -[`Atom`](../interfaces/Atom.md)\<`T`\> diff --git a/docs/reference/functions/createStore.md b/docs/reference/functions/createStore.md deleted file mode 100644 index 8c211275..00000000 --- a/docs/reference/functions/createStore.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -id: createStore -title: createStore ---- - -# Function: createStore() - -## Call Signature - -```ts -function createStore(getValue): ReadonlyStore; -``` - -Defined in: [store.ts:86](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L86) - -### Type Parameters - -#### T - -`T` - -### Parameters - -#### getValue - -(`prev?`) => `T` - -### Returns - -[`ReadonlyStore`](../classes/ReadonlyStore.md)\<`T`\> - -## Call Signature - -```ts -function createStore(initialValue): Store; -``` - -Defined in: [store.ts:89](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L89) - -### Type Parameters - -#### T - -`T` - -### Parameters - -#### initialValue - -`T` - -### Returns - -[`Store`](../classes/Store.md)\<`T`\> - -## Call Signature - -```ts -function createStore(initialValue, actions): Store; -``` - -Defined in: [store.ts:90](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L90) - -### Type Parameters - -#### T - -`T` - -#### TActions - -`TActions` *extends* [`StoreActionMap`](../type-aliases/StoreActionMap.md) - -### Parameters - -#### initialValue - -`NonFunction`\<`T`\> - -#### actions - -[`StoreActionsFactory`](../type-aliases/StoreActionsFactory.md)\<`T`, `TActions`\> - -### Returns - -[`Store`](../classes/Store.md)\<`T`, `TActions`\> diff --git a/docs/reference/functions/flush.md b/docs/reference/functions/flush.md deleted file mode 100644 index af4df285..00000000 --- a/docs/reference/functions/flush.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -id: flush -title: flush ---- - -# Function: flush() - -```ts -function flush(): void; -``` - -Defined in: [atom.ts:81](https://github.com/TanStack/store/blob/main/packages/store/src/atom.ts#L81) - -## Returns - -`void` diff --git a/docs/reference/functions/shallow.md b/docs/reference/functions/shallow.md deleted file mode 100644 index fb3b313a..00000000 --- a/docs/reference/functions/shallow.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -id: shallow -title: shallow ---- - -# Function: shallow() - -```ts -function shallow(objA, objB): boolean; -``` - -Defined in: [shallow.ts:1](https://github.com/TanStack/store/blob/main/packages/store/src/shallow.ts#L1) - -## Type Parameters - -### T - -`T` - -## Parameters - -### objA - -`T` - -### objB - -`T` - -## Returns - -`boolean` diff --git a/docs/reference/functions/toObserver.md b/docs/reference/functions/toObserver.md deleted file mode 100644 index 2f7a17ee..00000000 --- a/docs/reference/functions/toObserver.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -id: toObserver -title: toObserver ---- - -# Function: toObserver() - -```ts -function toObserver( - nextHandler?, - errorHandler?, -completionHandler?): Observer; -``` - -Defined in: [atom.ts:12](https://github.com/TanStack/store/blob/main/packages/store/src/atom.ts#L12) - -## Type Parameters - -### T - -`T` - -## Parameters - -### nextHandler? - -[`Observer`](../type-aliases/Observer.md)\<`T`\> | (`value`) => `void` - -### errorHandler? - -(`error`) => `void` - -### completionHandler? - -() => `void` - -## Returns - -[`Observer`](../type-aliases/Observer.md)\<`T`\> diff --git a/docs/reference/index.md b/docs/reference/index.md deleted file mode 100644 index afc8e5dd..00000000 --- a/docs/reference/index.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -id: "@tanstack/store" -title: "@tanstack/store" ---- - -# @tanstack/store - -## Classes - -- [ReadonlyStore](classes/ReadonlyStore.md) -- [Store](classes/Store.md) - -## Interfaces - -- [Atom](interfaces/Atom.md) -- [AtomOptions](interfaces/AtomOptions.md) -- [BaseAtom](interfaces/BaseAtom.md) -- [InternalBaseAtom](interfaces/InternalBaseAtom.md) -- [InternalReadonlyAtom](interfaces/InternalReadonlyAtom.md) -- [InteropSubscribable](interfaces/InteropSubscribable.md) -- [Readable](interfaces/Readable.md) -- [ReadonlyAtom](interfaces/ReadonlyAtom.md) -- [Subscribable](interfaces/Subscribable.md) -- [Subscription](interfaces/Subscription.md) - -## Type Aliases - -- [AnyAtom](type-aliases/AnyAtom.md) -- [Observer](type-aliases/Observer.md) -- [Selection](type-aliases/Selection.md) -- [StoreAction](type-aliases/StoreAction.md) -- [StoreActionMap](type-aliases/StoreActionMap.md) -- [StoreActionsFactory](type-aliases/StoreActionsFactory.md) - -## Functions - -- [batch](functions/batch.md) -- [createAsyncAtom](functions/createAsyncAtom.md) -- [createAtom](functions/createAtom.md) -- [createStore](functions/createStore.md) -- [flush](functions/flush.md) -- [shallow](functions/shallow.md) -- [toObserver](functions/toObserver.md) diff --git a/docs/reference/interfaces/Atom.md b/docs/reference/interfaces/Atom.md deleted file mode 100644 index 63124fd2..00000000 --- a/docs/reference/interfaces/Atom.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -id: Atom -title: Atom ---- - -# Interface: Atom\ - -Defined in: [types.ts:42](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L42) - -## Extends - -- [`BaseAtom`](BaseAtom.md)\<`T`\> - -## Type Parameters - -### T - -`T` - -## Properties - -### get() - -```ts -get: () => T; -``` - -Defined in: [types.ts:30](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L30) - -#### Returns - -`T` - -#### Inherited from - -[`BaseAtom`](BaseAtom.md).[`get`](BaseAtom.md#get) - -*** - -### set - -```ts -set: (fn) => void & (value) => void; -``` - -Defined in: [types.ts:44](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L44) - -Sets the value of the atom using a function. - -*** - -### subscribe - -```ts -subscribe: (observer) => Subscription & (next, error?, complete?) => Subscription; -``` - -Defined in: [types.ts:21](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L21) - -#### Inherited from - -[`BaseAtom`](BaseAtom.md).[`subscribe`](BaseAtom.md#subscribe) diff --git a/docs/reference/interfaces/AtomOptions.md b/docs/reference/interfaces/AtomOptions.md deleted file mode 100644 index c05fee8d..00000000 --- a/docs/reference/interfaces/AtomOptions.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -id: AtomOptions -title: AtomOptions ---- - -# Interface: AtomOptions\ - -Defined in: [types.ts:47](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L47) - -## Type Parameters - -### T - -`T` - -## Properties - -### compare()? - -```ts -optional compare: (prev, next) => boolean; -``` - -Defined in: [types.ts:48](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L48) - -#### Parameters - -##### prev - -`T` - -##### next - -`T` - -#### Returns - -`boolean` diff --git a/docs/reference/interfaces/BaseAtom.md b/docs/reference/interfaces/BaseAtom.md deleted file mode 100644 index 57de2ed9..00000000 --- a/docs/reference/interfaces/BaseAtom.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -id: BaseAtom -title: BaseAtom ---- - -# Interface: BaseAtom\ - -Defined in: [types.ts:33](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L33) - -## Extends - -- [`Subscribable`](Subscribable.md)\<`T`\>.[`Readable`](Readable.md)\<`T`\> - -## Extended by - -- [`Atom`](Atom.md) -- [`ReadonlyAtom`](ReadonlyAtom.md) - -## Type Parameters - -### T - -`T` - -## Properties - -### get() - -```ts -get: () => T; -``` - -Defined in: [types.ts:30](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L30) - -#### Returns - -`T` - -#### Inherited from - -[`Readable`](Readable.md).[`get`](Readable.md#get) - -*** - -### subscribe - -```ts -subscribe: (observer) => Subscription & (next, error?, complete?) => Subscription; -``` - -Defined in: [types.ts:21](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L21) - -#### Inherited from - -[`Subscribable`](Subscribable.md).[`subscribe`](Subscribable.md#subscribe) diff --git a/docs/reference/interfaces/InternalBaseAtom.md b/docs/reference/interfaces/InternalBaseAtom.md deleted file mode 100644 index b2716e81..00000000 --- a/docs/reference/interfaces/InternalBaseAtom.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -id: InternalBaseAtom -title: InternalBaseAtom ---- - -# Interface: InternalBaseAtom\ - -Defined in: [types.ts:35](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L35) - -## Extends - -- [`Subscribable`](Subscribable.md)\<`T`\>.[`Readable`](Readable.md)\<`T`\> - -## Extended by - -- [`InternalReadonlyAtom`](InternalReadonlyAtom.md) - -## Type Parameters - -### T - -`T` - -## Properties - -### \_snapshot - -```ts -_snapshot: T; -``` - -Defined in: [types.ts:37](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L37) - -**`Internal`** - -*** - -### \_update() - -```ts -_update: (getValue?) => boolean; -``` - -Defined in: [types.ts:39](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L39) - -**`Internal`** - -#### Parameters - -##### getValue? - -`T` | (`snapshot`) => `T` - -#### Returns - -`boolean` - -*** - -### get() - -```ts -get: () => T; -``` - -Defined in: [types.ts:30](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L30) - -#### Returns - -`T` - -#### Inherited from - -[`Readable`](Readable.md).[`get`](Readable.md#get) - -*** - -### subscribe - -```ts -subscribe: (observer) => Subscription & (next, error?, complete?) => Subscription; -``` - -Defined in: [types.ts:21](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L21) - -#### Inherited from - -[`Subscribable`](Subscribable.md).[`subscribe`](Subscribable.md#subscribe) diff --git a/docs/reference/interfaces/InternalReadonlyAtom.md b/docs/reference/interfaces/InternalReadonlyAtom.md deleted file mode 100644 index c4b92f33..00000000 --- a/docs/reference/interfaces/InternalReadonlyAtom.md +++ /dev/null @@ -1,172 +0,0 @@ ---- -id: InternalReadonlyAtom -title: InternalReadonlyAtom ---- - -# Interface: InternalReadonlyAtom\ - -Defined in: [types.ts:53](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L53) - -## Extends - -- [`InternalBaseAtom`](InternalBaseAtom.md)\<`T`\>.`ReactiveNode` - -## Type Parameters - -### T - -`T` - -## Properties - -### \_snapshot - -```ts -_snapshot: T; -``` - -Defined in: [types.ts:37](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L37) - -**`Internal`** - -#### Inherited from - -[`InternalBaseAtom`](InternalBaseAtom.md).[`_snapshot`](InternalBaseAtom.md#_snapshot) - -*** - -### \_update() - -```ts -_update: (getValue?) => boolean; -``` - -Defined in: [types.ts:39](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L39) - -**`Internal`** - -#### Parameters - -##### getValue? - -`T` | (`snapshot`) => `T` - -#### Returns - -`boolean` - -#### Inherited from - -[`InternalBaseAtom`](InternalBaseAtom.md).[`_update`](InternalBaseAtom.md#_update) - -*** - -### deps? - -```ts -optional deps: Link; -``` - -Defined in: [alien.ts:6](https://github.com/TanStack/store/blob/main/packages/store/src/alien.ts#L6) - -#### Inherited from - -```ts -ReactiveNode.deps -``` - -*** - -### depsTail? - -```ts -optional depsTail: Link; -``` - -Defined in: [alien.ts:7](https://github.com/TanStack/store/blob/main/packages/store/src/alien.ts#L7) - -#### Inherited from - -```ts -ReactiveNode.depsTail -``` - -*** - -### flags - -```ts -flags: ReactiveFlags; -``` - -Defined in: [alien.ts:10](https://github.com/TanStack/store/blob/main/packages/store/src/alien.ts#L10) - -#### Inherited from - -```ts -ReactiveNode.flags -``` - -*** - -### get() - -```ts -get: () => T; -``` - -Defined in: [types.ts:30](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L30) - -#### Returns - -`T` - -#### Inherited from - -[`InternalBaseAtom`](InternalBaseAtom.md).[`get`](InternalBaseAtom.md#get) - -*** - -### subs? - -```ts -optional subs: Link; -``` - -Defined in: [alien.ts:8](https://github.com/TanStack/store/blob/main/packages/store/src/alien.ts#L8) - -#### Inherited from - -```ts -ReactiveNode.subs -``` - -*** - -### subscribe - -```ts -subscribe: (observer) => Subscription & (next, error?, complete?) => Subscription; -``` - -Defined in: [types.ts:21](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L21) - -#### Inherited from - -[`InternalBaseAtom`](InternalBaseAtom.md).[`subscribe`](InternalBaseAtom.md#subscribe) - -*** - -### subsTail? - -```ts -optional subsTail: Link; -``` - -Defined in: [alien.ts:9](https://github.com/TanStack/store/blob/main/packages/store/src/alien.ts#L9) - -#### Inherited from - -```ts -ReactiveNode.subsTail -``` diff --git a/docs/reference/interfaces/InteropSubscribable.md b/docs/reference/interfaces/InteropSubscribable.md deleted file mode 100644 index 45c4cea3..00000000 --- a/docs/reference/interfaces/InteropSubscribable.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -id: InteropSubscribable -title: InteropSubscribable ---- - -# Interface: InteropSubscribable\ - -Defined in: [types.ts:5](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L5) - -## Extended by - -- [`Subscribable`](Subscribable.md) - -## Type Parameters - -### T - -`T` - -## Properties - -### subscribe() - -```ts -subscribe: (observer) => Subscription; -``` - -Defined in: [types.ts:6](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L6) - -#### Parameters - -##### observer - -[`Observer`](../type-aliases/Observer.md)\<`T`\> - -#### Returns - -[`Subscription`](Subscription.md) diff --git a/docs/reference/interfaces/Readable.md b/docs/reference/interfaces/Readable.md deleted file mode 100644 index 96f344b1..00000000 --- a/docs/reference/interfaces/Readable.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -id: Readable -title: Readable ---- - -# Interface: Readable\ - -Defined in: [types.ts:29](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L29) - -## Extends - -- [`Subscribable`](Subscribable.md)\<`T`\> - -## Extended by - -- [`BaseAtom`](BaseAtom.md) -- [`InternalBaseAtom`](InternalBaseAtom.md) - -## Type Parameters - -### T - -`T` - -## Properties - -### get() - -```ts -get: () => T; -``` - -Defined in: [types.ts:30](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L30) - -#### Returns - -`T` - -*** - -### subscribe - -```ts -subscribe: (observer) => Subscription & (next, error?, complete?) => Subscription; -``` - -Defined in: [types.ts:21](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L21) - -#### Inherited from - -[`Subscribable`](Subscribable.md).[`subscribe`](Subscribable.md#subscribe) diff --git a/docs/reference/interfaces/ReadonlyAtom.md b/docs/reference/interfaces/ReadonlyAtom.md deleted file mode 100644 index 2aeaa860..00000000 --- a/docs/reference/interfaces/ReadonlyAtom.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -id: ReadonlyAtom -title: ReadonlyAtom ---- - -# Interface: ReadonlyAtom\ - -Defined in: [types.ts:67](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L67) - -An atom that is read-only and cannot be set. - -## Example - -```ts -const atom = createAtom(() => 42); -// @ts-expect-error - Cannot set a readonly atom -atom.set(43); -``` - -## Extends - -- [`BaseAtom`](BaseAtom.md)\<`T`\> - -## Type Parameters - -### T - -`T` - -## Properties - -### get() - -```ts -get: () => T; -``` - -Defined in: [types.ts:30](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L30) - -#### Returns - -`T` - -#### Inherited from - -[`BaseAtom`](BaseAtom.md).[`get`](BaseAtom.md#get) - -*** - -### subscribe - -```ts -subscribe: (observer) => Subscription & (next, error?, complete?) => Subscription; -``` - -Defined in: [types.ts:21](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L21) - -#### Inherited from - -[`BaseAtom`](BaseAtom.md).[`subscribe`](BaseAtom.md#subscribe) diff --git a/docs/reference/interfaces/Subscribable.md b/docs/reference/interfaces/Subscribable.md deleted file mode 100644 index 0857be0c..00000000 --- a/docs/reference/interfaces/Subscribable.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -id: Subscribable -title: Subscribable ---- - -# Interface: Subscribable\ - -Defined in: [types.ts:20](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L20) - -## Extends - -- [`InteropSubscribable`](InteropSubscribable.md)\<`T`\> - -## Extended by - -- [`Readable`](Readable.md) -- [`BaseAtom`](BaseAtom.md) -- [`InternalBaseAtom`](InternalBaseAtom.md) - -## Type Parameters - -### T - -`T` - -## Properties - -### subscribe - -```ts -subscribe: (observer) => Subscription & (next, error?, complete?) => Subscription; -``` - -Defined in: [types.ts:21](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L21) - -#### Overrides - -[`InteropSubscribable`](InteropSubscribable.md).[`subscribe`](InteropSubscribable.md#subscribe) diff --git a/docs/reference/interfaces/Subscription.md b/docs/reference/interfaces/Subscription.md deleted file mode 100644 index 5ddc5fba..00000000 --- a/docs/reference/interfaces/Subscription.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -id: Subscription -title: Subscription ---- - -# Interface: Subscription - -Defined in: [types.ts:16](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L16) - -## Properties - -### unsubscribe() - -```ts -unsubscribe: () => void; -``` - -Defined in: [types.ts:17](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L17) - -#### Returns - -`void` diff --git a/docs/reference/type-aliases/AnyAtom.md b/docs/reference/type-aliases/AnyAtom.md deleted file mode 100644 index d41e125c..00000000 --- a/docs/reference/type-aliases/AnyAtom.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: AnyAtom -title: AnyAtom ---- - -# Type Alias: AnyAtom - -```ts -type AnyAtom = BaseAtom; -``` - -Defined in: [types.ts:51](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L51) diff --git a/docs/reference/type-aliases/Observer.md b/docs/reference/type-aliases/Observer.md deleted file mode 100644 index 47cd75dd..00000000 --- a/docs/reference/type-aliases/Observer.md +++ /dev/null @@ -1,72 +0,0 @@ ---- -id: Observer -title: Observer ---- - -# Type Alias: Observer\ - -```ts -type Observer = object; -``` - -Defined in: [types.ts:10](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L10) - -## Type Parameters - -### T - -`T` - -## Properties - -### complete()? - -```ts -optional complete: () => void; -``` - -Defined in: [types.ts:13](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L13) - -#### Returns - -`void` - -*** - -### error()? - -```ts -optional error: (err) => void; -``` - -Defined in: [types.ts:12](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L12) - -#### Parameters - -##### err - -`unknown` - -#### Returns - -`void` - -*** - -### next()? - -```ts -optional next: (value) => void; -``` - -Defined in: [types.ts:11](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L11) - -#### Parameters - -##### value - -`T` - -#### Returns - -`void` diff --git a/docs/reference/type-aliases/Selection.md b/docs/reference/type-aliases/Selection.md deleted file mode 100644 index 484ad187..00000000 --- a/docs/reference/type-aliases/Selection.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -id: Selection -title: Selection ---- - -# Type Alias: Selection\ - -```ts -type Selection = Readable; -``` - -Defined in: [types.ts:3](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L3) - -## Type Parameters - -### TSelected - -`TSelected` diff --git a/docs/reference/type-aliases/StoreAction.md b/docs/reference/type-aliases/StoreAction.md deleted file mode 100644 index 1e839025..00000000 --- a/docs/reference/type-aliases/StoreAction.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -id: StoreAction -title: StoreAction ---- - -# Type Alias: StoreAction() - -```ts -type StoreAction = (...args) => any; -``` - -Defined in: [store.ts:4](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L4) - -## Parameters - -### args - -...`any`[] - -## Returns - -`any` diff --git a/docs/reference/type-aliases/StoreActionMap.md b/docs/reference/type-aliases/StoreActionMap.md deleted file mode 100644 index 5191c5f5..00000000 --- a/docs/reference/type-aliases/StoreActionMap.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: StoreActionMap -title: StoreActionMap ---- - -# Type Alias: StoreActionMap - -```ts -type StoreActionMap = Record; -``` - -Defined in: [store.ts:6](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L6) diff --git a/docs/reference/type-aliases/StoreActionsFactory.md b/docs/reference/type-aliases/StoreActionsFactory.md deleted file mode 100644 index b7caaee8..00000000 --- a/docs/reference/type-aliases/StoreActionsFactory.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -id: StoreActionsFactory -title: StoreActionsFactory ---- - -# Type Alias: StoreActionsFactory()\ - -```ts -type StoreActionsFactory = (store) => TActions; -``` - -Defined in: [store.ts:8](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L8) - -## Type Parameters - -### T - -`T` - -### TActions - -`TActions` *extends* [`StoreActionMap`](StoreActionMap.md) - -## Parameters - -### store - -#### get - -[`Store`](../classes/Store.md)\<`T`\>\[`"get"`\] - -#### setState - -[`Store`](../classes/Store.md)\<`T`\>\[`"setState"`\] - -## Returns - -`TActions` diff --git a/packages/store/src/evaluate.ts b/packages/store/src/evaluate.ts index 616c4700..d296eb8f 100644 --- a/packages/store/src/evaluate.ts +++ b/packages/store/src/evaluate.ts @@ -1,4 +1,8 @@ -export function evaluate(objA: T, objB: T, config?: {depth: 'shallow'| 'deep'}) { +export function evaluate( + objA: T, + objB: T, + config?: { depth: 'shallow' | 'deep' }, +) { if (Object.is(objA, objB)) { return true } From 50628e1f88c725ecece5bc04342608c32d7b2ba3 Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Tue, 28 Apr 2026 12:10:18 +0200 Subject: [PATCH 06/17] chore: split mode --- packages/store/src/evaluate.ts | 55 ++- packages/store/tests/evaluate.spec.ts | 513 ++++++++++++++++++-------- 2 files changed, 410 insertions(+), 158 deletions(-) diff --git a/packages/store/src/evaluate.ts b/packages/store/src/evaluate.ts index d296eb8f..da729b41 100644 --- a/packages/store/src/evaluate.ts +++ b/packages/store/src/evaluate.ts @@ -1,7 +1,7 @@ export function evaluate( objA: T, objB: T, - config?: { depth: 'shallow' | 'deep' }, + config: { mode: 'shallow' | 'deep' } = { mode: 'shallow' }, ) { if (Object.is(objA, objB)) { return true @@ -35,17 +35,37 @@ export function evaluate( if (objA instanceof Map && objB instanceof Map) { if (objA.size !== objB.size) return false - for (const [k, v] of objA) { - if (!objB.has(k) || !Object.is(v, objB.get(k))) return false + + if (config.mode === 'deep') { + for (const [k, v] of objA) { + if (!objB.has(k) || !evaluate(v, objB.get(k), config)) return false + } + } + + if (config.mode === 'shallow') { + for (const [k, v] of objA) { + if (!objB.has(k) || !Object.is(v, objB.get(k))) return false + } } + return true } if (objA instanceof Set && objB instanceof Set) { if (objA.size !== objB.size) return false - for (const v of objA) { - if (!objB.has(v)) return false + + if (config.mode === 'deep') { + for (const v of objA) { + if (![...objB].some((bv) => evaluate(v, bv, config))) return false + } } + + if (config.mode === 'shallow') { + for (const v of objA) { + if (!objB.has(v)) return false + } + } + return true } @@ -60,12 +80,25 @@ export function evaluate( return false } - for (const key of keysA) { - if ( - !keysB.includes(key) || - !evaluate(objA[key as keyof T], objB[key as keyof T]) - ) { - return false + if (config.mode === 'deep') { + for (const key of keysA) { + if ( + !keysB.includes(key) || + !evaluate(objA[key as keyof T], objB[key as keyof T], config) + ) { + return false + } + } + } + + if (config.mode === 'shallow') { + for (const key of keysA) { + if ( + !keysB.includes(key) || + !Object.is(objA[key as keyof T], objB[key as keyof T]) + ) { + return false + } } } diff --git a/packages/store/tests/evaluate.spec.ts b/packages/store/tests/evaluate.spec.ts index 43066e5e..2ab402a0 100644 --- a/packages/store/tests/evaluate.spec.ts +++ b/packages/store/tests/evaluate.spec.ts @@ -19,184 +19,403 @@ describe('evaluate', () => { expect(undefinedFalse).toEqual(false) }) - it('should test equality between arrays', () => { - const arrayTrue = evaluate([], []) - expect(arrayTrue).toEqual(true) - - const arrayDeepSearchTrue = evaluate([[1]], [[1]]) - expect(arrayDeepSearchTrue).toEqual(true) + it('should return false for runtime cross-type comparisons', () => { + expect(evaluate(new Date(), new Map())).toEqual(false) + expect(evaluate(new Date(), new Set())).toEqual(false) + expect(evaluate(new Map(), new Set())).toEqual(false) + expect(evaluate(new Date(), {})).toEqual(false) + expect(evaluate(new Map(), {})).toEqual(false) + expect(evaluate(new Set(), {})).toEqual(false) + }) - const arrayFalse = evaluate([], ['']) - expect(arrayFalse).toEqual(false) + it('should not throw a runtime error when File is undefined in the environment', () => { + vi.stubGlobal('File', undefined) - const arrayDeepFalse = evaluate([[1]], []) - expect(arrayDeepFalse).toEqual(false) + const file1 = { + name: 'hello.txt', + size: 5, + type: 'text/plain', + lastModified: 0, + } + const file2 = { + name: 'hello.txt', + size: 5, + type: 'text/plain', + lastModified: 0, + } - const arrayComplexFalse = evaluate([[{ test: 'true' }], null], [[1], {}]) - expect(arrayComplexFalse).toEqual(false) + expect(() => evaluate(file1, file2)).not.toThrow() - const arrayComplexTrue = evaluate( - [[{ test: 'true' }], null], - [[{ test: 'true' }], null], - ) - expect(arrayComplexTrue).toEqual(true) + vi.unstubAllGlobals() }) - it('should test equality between objects', () => { - const objTrue = evaluate({ test: 'same' }, { test: 'same' }) - expect(objTrue).toEqual(true) + describe('shallow', () => { + it('should test equality between arrays', () => { + expect(evaluate([], [])).toEqual(true) - const objFalse = evaluate({ test: 'not' }, { test: 'same' }) - expect(objFalse).toEqual(false) + const arrayFalse = evaluate([], ['']) + expect(arrayFalse).toEqual(false) - const objDeepFalse = evaluate({ test: 'not' }, { test: { test: 'same' } }) - expect(objDeepFalse).toEqual(false) + const arrayDeepFalse = evaluate([[1]], []) + expect(arrayDeepFalse).toEqual(false) - const objDeepArrFalse = evaluate({ test: [] }, { test: [[]] }) - expect(objDeepArrFalse).toEqual(false) + const arrayNestedFalse = evaluate([[1]], [[1]]) + expect(arrayNestedFalse).toEqual(false) - const objNullFalse = evaluate({ test: '' }, null) - expect(objNullFalse).toEqual(false) + const arrayComplexFalse = evaluate([[{ test: 'true' }], null], [[1], {}]) + expect(arrayComplexFalse).toEqual(false) - const objComplexFalse = evaluate( - { test: { testTwo: '' }, arr: [[1]] }, - { test: { testTwo: false }, arr: [[1], [0]] }, - ) - expect(objComplexFalse).toEqual(false) - - const objComplexTrue = evaluate( - { test: { testTwo: '' }, arr: [[1]] }, - { test: { testTwo: '' }, arr: [[1]] }, - ) - expect(objComplexTrue).toEqual(true) - }) + const arrayComplexFalse2 = evaluate( + [[{ test: 'true' }], null], + [[{ test: 'true' }], null], + ) + expect(arrayComplexFalse2).toEqual(false) + }) - it('should test equality between Date objects', () => { - const date1 = new Date('2025-01-01T00:00:00.000Z') - const date2 = new Date('2025-01-01T00:00:00.000Z') - const date3 = new Date('2025-01-02T00:00:00.000Z') + it('should test equality between objects', () => { + const objTrue = evaluate({ test: 'same' }, { test: 'same' }) + expect(objTrue).toEqual(true) - const dateTrue = evaluate(date1, date2) - expect(dateTrue).toEqual(true) + const objFalse = evaluate({ test: 'not' }, { test: 'same' }) + expect(objFalse).toEqual(false) - const dateFalse = evaluate(date1, date3) - expect(dateFalse).toEqual(false) + const objDeepFalse = evaluate({ test: 'not' }, { test: { test: 'same' } }) + expect(objDeepFalse).toEqual(false) - const dateObjectTrue = evaluate({ date: date1 }, { date: date2 }) - expect(dateObjectTrue).toEqual(true) + const objDeepArrFalse = evaluate({ test: [] }, { test: [[]] }) + expect(objDeepArrFalse).toEqual(false) - const dateObjectFalse = evaluate({ date: date1 }, { date: date3 }) - expect(dateObjectFalse).toEqual(false) - }) + const objNullFalse = evaluate({ test: '' }, null) + expect(objNullFalse).toEqual(false) - it('should test equality between Map objects', () => { - const map1 = new Map([ - ['a', 1], - ['b', 2], - ]) - const map2 = new Map([ - ['a', 1], - ['b', 2], - ]) - expect(evaluate(map1, map2)).toEqual(true) - - const emptyMap1 = new Map() - const emptyMap2 = new Map() - expect(evaluate(emptyMap1, emptyMap2)).toEqual(true) - - const mapSmall = new Map([['a', 1]]) - const mapLarge = new Map([ - ['a', 1], - ['b', 2], - ]) - expect(evaluate(mapSmall, mapLarge)).toEqual(false) - - const mapA = new Map([ - ['a', 1], - ['b', 2], - ]) - const mapC = new Map([ - ['a', 1], - ['c', 2], - ]) - expect(evaluate(mapA, mapC)).toEqual(false) - - const mapVal1 = new Map([ - ['a', 1], - ['b', 2], - ]) - const mapVal2 = new Map([ - ['a', 1], - ['b', 3], - ]) - expect(evaluate(mapVal1, mapVal2)).toEqual(false) - }) + const objComplexFalse = evaluate( + { test: { testTwo: '' }, arr: [[1]] }, + { test: { testTwo: false }, arr: [[1], [0]] }, + ) + expect(objComplexFalse).toEqual(false) - it('should test equality between Set objects', () => { - const set1 = new Set([1, 2, 3]) - const set2 = new Set([1, 2, 3]) - expect(evaluate(set1, set2)).toEqual(true) + const objComplexShallowFalse = evaluate( + { test: { testTwo: '' }, arr: [[1]] }, + { test: { testTwo: '' }, arr: [[1]] }, + ) + expect(objComplexShallowFalse).toEqual(false) + }) - const emptySet1 = new Set() - const emptySet2 = new Set() - expect(evaluate(emptySet1, emptySet2)).toEqual(true) + it('should test equality between Date objects', () => { + const date1 = new Date('2025-01-01T00:00:00.000Z') + const date2 = new Date('2025-01-01T00:00:00.000Z') + const date3 = new Date('2025-01-02T00:00:00.000Z') - const setSmall = new Set([1, 2]) - const setLarge = new Set([1, 2, 3]) - expect(evaluate(setSmall, setLarge)).toEqual(false) + expect(evaluate(date1, date2)).toEqual(true) + expect(evaluate(date1, date3)).toEqual(false) - const setA = new Set([1, 2, 3]) - const setB = new Set([1, 2, 4]) - expect(evaluate(setA, setB)).toEqual(false) - }) + const dateObjectShallowFalse = evaluate({ date: date1 }, { date: date2 }) + expect(dateObjectShallowFalse).toEqual(false) - it('should test equality between File objects', () => { - const file1 = new File(['hello'], 'hello.txt', { type: 'text/plain' }) - const file2 = new File(['hello'], 'hello.txt', { type: 'text/plain' }) - const fileDiffName = new File(['hello'], 'world.txt', { - type: 'text/plain', + const dateObjectFalse = evaluate({ date: date1 }, { date: date3 }) + expect(dateObjectFalse).toEqual(false) }) - const fileDiffType = new File(['hello'], 'hello.txt', { type: 'text/html' }) - const fileDiffSize = new File(['hello world'], 'hello.txt', { - type: 'text/plain', + + it('should test equality between Map objects', () => { + expect( + evaluate( + new Map([ + ['a', 1], + ['b', 2], + ]), + new Map([ + ['a', 1], + ['b', 2], + ]), + ), + ).toEqual(true) + expect(evaluate(new Map(), new Map())).toEqual(true) + expect( + evaluate( + new Map([['a', 1]]), + new Map([ + ['a', 1], + ['b', 2], + ]), + ), + ).toEqual(false) + expect( + evaluate( + new Map([ + ['a', 1], + ['b', 2], + ]), + new Map([ + ['a', 1], + ['c', 2], + ]), + ), + ).toEqual(false) + expect( + evaluate( + new Map([ + ['a', 1], + ['b', 2], + ]), + new Map([ + ['a', 1], + ['b', 3], + ]), + ), + ).toEqual(false) + + const obj = { x: 1 } + expect(evaluate(new Map([['a', obj]]), new Map([['a', obj]]))).toEqual( + true, + ) + expect( + evaluate(new Map([['a', { x: 1 }]]), new Map([['a', { x: 1 }]])), + ).toEqual(false) }) - expect(evaluate(file1, file2)).toEqual(true) - expect(evaluate(file1, fileDiffName)).toEqual(false) - expect(evaluate(file1, fileDiffType)).toEqual(false) - expect(evaluate(file1, fileDiffSize)).toEqual(false) + it('should test equality between Set objects', () => { + expect(evaluate(new Set([1, 2, 3]), new Set([1, 2, 3]))).toEqual(true) + expect(evaluate(new Set(), new Set())).toEqual(true) + expect(evaluate(new Set([1, 2]), new Set([1, 2, 3]))).toEqual(false) + expect(evaluate(new Set([1, 2, 3]), new Set([1, 2, 4]))).toEqual(false) - expect(evaluate({ file: file1 }, { file: file2 })).toEqual(true) - expect(evaluate({ file: file1 }, { file: fileDiffName })).toEqual(false) - }) + const obj = { x: 1 } + expect(evaluate(new Set([obj]), new Set([obj]))).toEqual(true) + expect(evaluate(new Set([{ x: 1 }]), new Set([{ x: 1 }]))).toEqual(false) + }) - it('should return false for runtime cross-type comparisons', () => { - expect(evaluate(new Date(), new Map())).toEqual(false) - expect(evaluate(new Date(), new Set())).toEqual(false) - expect(evaluate(new Map(), new Set())).toEqual(false) - expect(evaluate(new Date(), {})).toEqual(false) - expect(evaluate(new Map(), {})).toEqual(false) - expect(evaluate(new Set(), {})).toEqual(false) + it('should test equality between File objects', () => { + const file1 = new File(['hello'], 'hello.txt', { type: 'text/plain' }) + const file2 = new File(['hello'], 'hello.txt', { type: 'text/plain' }) + const fileDiffName = new File(['hello'], 'world.txt', { + type: 'text/plain', + }) + const fileDiffType = new File(['hello'], 'hello.txt', { + type: 'text/html', + }) + const fileDiffSize = new File(['hello world'], 'hello.txt', { + type: 'text/plain', + }) + + expect(evaluate(file1, file2)).toEqual(true) + expect(evaluate(file1, fileDiffName)).toEqual(false) + expect(evaluate(file1, fileDiffType)).toEqual(false) + expect(evaluate(file1, fileDiffSize)).toEqual(false) + + expect(evaluate({ file: file1 }, { file: file2 })).toEqual(false) + expect(evaluate({ file: file1 }, { file: fileDiffName })).toEqual(false) + }) }) - it('should not throw a runtime error when File is undefined in the environment', () => { - vi.stubGlobal('File', undefined) + describe('deep', () => { + it('should test equality between arrays', () => { + expect(evaluate([], [], { mode: 'deep' })).toEqual(true) + + const arrayFalse = evaluate([], [''], { mode: 'deep' }) + expect(arrayFalse).toEqual(false) + + const arrayDeepFalse = evaluate([[1]], [], { mode: 'deep' }) + expect(arrayDeepFalse).toEqual(false) + + const arrayDeepSearchTrue = evaluate([[1]], [[1]], { mode: 'deep' }) + expect(arrayDeepSearchTrue).toEqual(true) + + const arrayComplexFalse = evaluate( + [[{ test: 'true' }], null], + [[1], {}], + { + mode: 'deep', + }, + ) + expect(arrayComplexFalse).toEqual(false) + + const arrayComplexTrue = evaluate( + [[{ test: 'true' }], null], + [[{ test: 'true' }], null], + { mode: 'deep' }, + ) + expect(arrayComplexTrue).toEqual(true) + }) - const file1 = { - name: 'hello.txt', - size: 5, - type: 'text/plain', - lastModified: 0, - } - const file2 = { - name: 'hello.txt', - size: 5, - type: 'text/plain', - lastModified: 0, - } + it('should test equality between objects', () => { + const objTrue = evaluate( + { test: 'same' }, + { test: 'same' }, + { mode: 'deep' }, + ) + expect(objTrue).toEqual(true) + + const objFalse = evaluate( + { test: 'not' }, + { test: 'same' }, + { mode: 'deep' }, + ) + expect(objFalse).toEqual(false) + + const objDeepFalse = evaluate( + { test: 'not' }, + { test: { test: 'same' } }, + { mode: 'deep' }, + ) + expect(objDeepFalse).toEqual(false) + + const objDeepArrFalse = evaluate( + { test: [] }, + { test: [[]] }, + { mode: 'deep' }, + ) + expect(objDeepArrFalse).toEqual(false) + + const objNullFalse = evaluate({ test: '' }, null, { mode: 'deep' }) + expect(objNullFalse).toEqual(false) + + const objComplexFalse = evaluate( + { test: { testTwo: '' }, arr: [[1]] }, + { test: { testTwo: false }, arr: [[1], [0]] }, + { mode: 'deep' }, + ) + expect(objComplexFalse).toEqual(false) + + const objComplexTrue = evaluate( + { test: { testTwo: '' }, arr: [[1]] }, + { test: { testTwo: '' }, arr: [[1]] }, + { mode: 'deep' }, + ) + expect(objComplexTrue).toEqual(true) + }) - expect(() => evaluate(file1, file2)).not.toThrow() + it('should test equality between Date objects', () => { + const date1 = new Date('2025-01-01T00:00:00.000Z') + const date2 = new Date('2025-01-01T00:00:00.000Z') + const date3 = new Date('2025-01-02T00:00:00.000Z') + + expect(evaluate(date1, date2, { mode: 'deep' })).toEqual(true) + expect(evaluate(date1, date3, { mode: 'deep' })).toEqual(false) + + const dateObjectTrue = evaluate( + { date: date1 }, + { date: date2 }, + { mode: 'deep' }, + ) + expect(dateObjectTrue).toEqual(true) + + const dateObjectFalse = evaluate( + { date: date1 }, + { date: date3 }, + { mode: 'deep' }, + ) + expect(dateObjectFalse).toEqual(false) + }) - vi.unstubAllGlobals() + it('should test equality between Map objects', () => { + expect( + evaluate( + new Map([ + ['a', 1], + ['b', 2], + ]), + new Map([ + ['a', 1], + ['b', 2], + ]), + { mode: 'deep' }, + ), + ).toEqual(true) + expect(evaluate(new Map(), new Map(), { mode: 'deep' })).toEqual(true) + expect( + evaluate( + new Map([['a', 1]]), + new Map([ + ['a', 1], + ['b', 2], + ]), + { mode: 'deep' }, + ), + ).toEqual(false) + expect( + evaluate( + new Map([ + ['a', 1], + ['b', 2], + ]), + new Map([ + ['a', 1], + ['b', 3], + ]), + { mode: 'deep' }, + ), + ).toEqual(false) + + expect( + evaluate(new Map([['a', { x: 1 }]]), new Map([['a', { x: 1 }]]), { + mode: 'deep', + }), + ).toEqual(true) + expect( + evaluate(new Map([['a', { x: 1 }]]), new Map([['a', { x: 2 }]]), { + mode: 'deep', + }), + ).toEqual(false) + expect( + evaluate( + new Map([['a', { nested: { x: 1 } }]]), + new Map([['a', { nested: { x: 1 } }]]), + { mode: 'deep' }, + ), + ).toEqual(true) + }) + + it('should test equality between Set objects', () => { + expect( + evaluate(new Set([1, 2, 3]), new Set([1, 2, 3]), { mode: 'deep' }), + ).toEqual(true) + expect(evaluate(new Set(), new Set(), { mode: 'deep' })).toEqual(true) + expect( + evaluate(new Set([1, 2]), new Set([1, 2, 3]), { mode: 'deep' }), + ).toEqual(false) + expect( + evaluate(new Set([1, 2, 3]), new Set([1, 2, 4]), { mode: 'deep' }), + ).toEqual(false) + + expect( + evaluate(new Set([{ x: 1 }]), new Set([{ x: 1 }]), { mode: 'deep' }), + ).toEqual(true) + expect( + evaluate(new Set([{ x: 1 }]), new Set([{ x: 2 }]), { mode: 'deep' }), + ).toEqual(false) + expect( + evaluate( + new Set([{ nested: { x: 1 } }]), + new Set([{ nested: { x: 1 } }]), + { mode: 'deep' }, + ), + ).toEqual(true) + }) + + it('should test equality between File objects', () => { + const file1 = new File(['hello'], 'hello.txt', { type: 'text/plain' }) + const file2 = new File(['hello'], 'hello.txt', { type: 'text/plain' }) + const fileDiffName = new File(['hello'], 'world.txt', { + type: 'text/plain', + }) + const fileDiffType = new File(['hello'], 'hello.txt', { + type: 'text/html', + }) + const fileDiffSize = new File(['hello world'], 'hello.txt', { + type: 'text/plain', + }) + + expect(evaluate(file1, file2, { mode: 'deep' })).toEqual(true) + expect(evaluate(file1, fileDiffName, { mode: 'deep' })).toEqual(false) + expect(evaluate(file1, fileDiffType, { mode: 'deep' })).toEqual(false) + expect(evaluate(file1, fileDiffSize, { mode: 'deep' })).toEqual(false) + + expect( + evaluate({ file: file1 }, { file: file2 }, { mode: 'deep' }), + ).toEqual(true) + expect( + evaluate({ file: file1 }, { file: fileDiffName }, { mode: 'deep' }), + ).toEqual(false) + }) }) }) From 194eab9eac5ff1ec389eaae719d86b7c2b86ecd5 Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Tue, 28 Apr 2026 12:19:35 +0200 Subject: [PATCH 07/17] fix: circular references --- packages/store/src/evaluate.ts | 37 ++++++++++++++++++++++----- packages/store/tests/evaluate.spec.ts | 15 +++++++++++ 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/packages/store/src/evaluate.ts b/packages/store/src/evaluate.ts index da729b41..d7ce522c 100644 --- a/packages/store/src/evaluate.ts +++ b/packages/store/src/evaluate.ts @@ -2,7 +2,16 @@ export function evaluate( objA: T, objB: T, config: { mode: 'shallow' | 'deep' } = { mode: 'shallow' }, -) { +): boolean { + return _evaluate(objA, objB, config, new WeakMap()) +} + +function _evaluate( + objA: T, + objB: T, + config: { mode: 'shallow' | 'deep' }, + seen: WeakMap>, +): boolean { if (Object.is(objA, objB)) { return true } @@ -16,6 +25,20 @@ export function evaluate( return false } + // guards against circular references + if (config.mode === 'deep') { + let seenB = seen.get(objA as object) + + if (seenB?.has(objB as object)) return true + + if (!seenB) { + seenB = new WeakSet() + seen.set(objA as object, seenB) + } + + seenB.add(objB as object) + } + if (objA instanceof Date && objB instanceof Date) { return objA.getTime() === objB.getTime() } @@ -38,7 +61,8 @@ export function evaluate( if (config.mode === 'deep') { for (const [k, v] of objA) { - if (!objB.has(k) || !evaluate(v, objB.get(k), config)) return false + if (!objB.has(k) || !_evaluate(v, objB.get(k), config, seen)) + return false } } @@ -56,7 +80,8 @@ export function evaluate( if (config.mode === 'deep') { for (const v of objA) { - if (![...objB].some((bv) => evaluate(v, bv, config))) return false + if (![...objB].some((bv) => _evaluate(v, bv, config, seen))) + return false } } @@ -83,8 +108,8 @@ export function evaluate( if (config.mode === 'deep') { for (const key of keysA) { if ( - !keysB.includes(key) || - !evaluate(objA[key as keyof T], objB[key as keyof T], config) + !Object.prototype.hasOwnProperty.call(objB, key) || + !_evaluate(objA[key as keyof T], objB[key as keyof T], config, seen) ) { return false } @@ -94,7 +119,7 @@ export function evaluate( if (config.mode === 'shallow') { for (const key of keysA) { if ( - !keysB.includes(key) || + !Object.prototype.hasOwnProperty.call(objB, key) || !Object.is(objA[key as keyof T], objB[key as keyof T]) ) { return false diff --git a/packages/store/tests/evaluate.spec.ts b/packages/store/tests/evaluate.spec.ts index 2ab402a0..271eb1c4 100644 --- a/packages/store/tests/evaluate.spec.ts +++ b/packages/store/tests/evaluate.spec.ts @@ -417,5 +417,20 @@ describe('evaluate', () => { evaluate({ file: file1 }, { file: fileDiffName }, { mode: 'deep' }), ).toEqual(false) }) + + it('should handle circular references', () => { + const a: any = { x: 1 } + a.self = a + + const b: any = { x: 1 } + b.self = b + + expect(() => evaluate(a, b, { mode: 'deep' })).not.toThrow() + expect(evaluate(a, b, { mode: 'deep' })).toEqual(true) + + const c: any = { x: 2 } + c.self = c + expect(evaluate(a, c, { mode: 'deep' })).toEqual(false) + }) }) }) From 2c9d2218c038ac1a1ae1b65ebd2922c99fde7adc Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 10:21:56 +0000 Subject: [PATCH 08/17] ci: apply automated fixes and generate docs --- docs/reference/classes/ReadonlyStore.md | 124 +++++++++++++ docs/reference/classes/Store.md | 158 ++++++++++++++++ docs/reference/functions/batch.md | 22 +++ docs/reference/functions/createAsyncAtom.md | 32 ++++ docs/reference/functions/createAtom.md | 62 +++++++ docs/reference/functions/createStore.md | 86 +++++++++ docs/reference/functions/flush.md | 16 ++ docs/reference/functions/shallow.md | 32 ++++ docs/reference/functions/toObserver.md | 39 ++++ docs/reference/index.md | 43 +++++ docs/reference/interfaces/Atom.md | 62 +++++++ docs/reference/interfaces/AtomOptions.md | 38 ++++ docs/reference/interfaces/BaseAtom.md | 55 ++++++ docs/reference/interfaces/InternalBaseAtom.md | 88 +++++++++ .../interfaces/InternalReadonlyAtom.md | 172 ++++++++++++++++++ .../interfaces/InteropSubscribable.md | 38 ++++ docs/reference/interfaces/Readable.md | 51 ++++++ docs/reference/interfaces/ReadonlyAtom.md | 60 ++++++ docs/reference/interfaces/Subscribable.md | 38 ++++ docs/reference/interfaces/Subscription.md | 22 +++ docs/reference/type-aliases/AnyAtom.md | 12 ++ docs/reference/type-aliases/Observer.md | 72 ++++++++ docs/reference/type-aliases/Selection.md | 18 ++ docs/reference/type-aliases/StoreAction.md | 22 +++ docs/reference/type-aliases/StoreActionMap.md | 12 ++ .../type-aliases/StoreActionsFactory.md | 38 ++++ 26 files changed, 1412 insertions(+) create mode 100644 docs/reference/classes/ReadonlyStore.md create mode 100644 docs/reference/classes/Store.md create mode 100644 docs/reference/functions/batch.md create mode 100644 docs/reference/functions/createAsyncAtom.md create mode 100644 docs/reference/functions/createAtom.md create mode 100644 docs/reference/functions/createStore.md create mode 100644 docs/reference/functions/flush.md create mode 100644 docs/reference/functions/shallow.md create mode 100644 docs/reference/functions/toObserver.md create mode 100644 docs/reference/index.md create mode 100644 docs/reference/interfaces/Atom.md create mode 100644 docs/reference/interfaces/AtomOptions.md create mode 100644 docs/reference/interfaces/BaseAtom.md create mode 100644 docs/reference/interfaces/InternalBaseAtom.md create mode 100644 docs/reference/interfaces/InternalReadonlyAtom.md create mode 100644 docs/reference/interfaces/InteropSubscribable.md create mode 100644 docs/reference/interfaces/Readable.md create mode 100644 docs/reference/interfaces/ReadonlyAtom.md create mode 100644 docs/reference/interfaces/Subscribable.md create mode 100644 docs/reference/interfaces/Subscription.md create mode 100644 docs/reference/type-aliases/AnyAtom.md create mode 100644 docs/reference/type-aliases/Observer.md create mode 100644 docs/reference/type-aliases/Selection.md create mode 100644 docs/reference/type-aliases/StoreAction.md create mode 100644 docs/reference/type-aliases/StoreActionMap.md create mode 100644 docs/reference/type-aliases/StoreActionsFactory.md diff --git a/docs/reference/classes/ReadonlyStore.md b/docs/reference/classes/ReadonlyStore.md new file mode 100644 index 00000000..e23ee15c --- /dev/null +++ b/docs/reference/classes/ReadonlyStore.md @@ -0,0 +1,124 @@ +--- +id: ReadonlyStore +title: ReadonlyStore +--- + +# Class: ReadonlyStore\ + +Defined in: [store.ts:59](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L59) + +## Type Parameters + +### T + +`T` + +## Implements + +- `Omit`\<[`Store`](Store.md)\<`T`\>, `"setState"` \| `"actions"`\> + +## Constructors + +### Constructor + +```ts +new ReadonlyStore(getValue): ReadonlyStore; +``` + +Defined in: [store.ts:64](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L64) + +#### Parameters + +##### getValue + +(`prev?`) => `T` + +#### Returns + +`ReadonlyStore`\<`T`\> + +### Constructor + +```ts +new ReadonlyStore(initialValue): ReadonlyStore; +``` + +Defined in: [store.ts:65](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L65) + +#### Parameters + +##### initialValue + +`T` + +#### Returns + +`ReadonlyStore`\<`T`\> + +## Accessors + +### state + +#### Get Signature + +```ts +get state(): T; +``` + +Defined in: [store.ts:73](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L73) + +##### Returns + +`T` + +#### Implementation of + +```ts +Omit.state +``` + +## Methods + +### get() + +```ts +get(): T; +``` + +Defined in: [store.ts:76](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L76) + +#### Returns + +`T` + +#### Implementation of + +```ts +Omit.get +``` + +*** + +### subscribe() + +```ts +subscribe(observerOrFn): Subscription; +``` + +Defined in: [store.ts:79](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L79) + +#### Parameters + +##### observerOrFn + +[`Observer`](../type-aliases/Observer.md)\<`T`\> | (`value`) => `void` + +#### Returns + +[`Subscription`](../interfaces/Subscription.md) + +#### Implementation of + +```ts +Omit.subscribe +``` diff --git a/docs/reference/classes/Store.md b/docs/reference/classes/Store.md new file mode 100644 index 00000000..346926a3 --- /dev/null +++ b/docs/reference/classes/Store.md @@ -0,0 +1,158 @@ +--- +id: Store +title: Store +--- + +# Class: Store\ + +Defined in: [store.ts:15](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L15) + +## Type Parameters + +### T + +`T` + +### TActions + +`TActions` *extends* [`StoreActionMap`](../type-aliases/StoreActionMap.md) = `never` + +## Constructors + +### Constructor + +```ts +new Store(getValue): Store; +``` + +Defined in: [store.ts:18](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L18) + +#### Parameters + +##### getValue + +(`prev?`) => `T` + +#### Returns + +`Store`\<`T`, `TActions`\> + +### Constructor + +```ts +new Store(initialValue): Store; +``` + +Defined in: [store.ts:19](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L19) + +#### Parameters + +##### initialValue + +`T` + +#### Returns + +`Store`\<`T`, `TActions`\> + +### Constructor + +```ts +new Store(initialValue, actionsFactory): Store; +``` + +Defined in: [store.ts:20](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L20) + +#### Parameters + +##### initialValue + +`NonFunction`\<`T`\> + +##### actionsFactory + +[`StoreActionsFactory`](../type-aliases/StoreActionsFactory.md)\<`T`, `TActions`\> + +#### Returns + +`Store`\<`T`, `TActions`\> + +## Properties + +### actions + +```ts +readonly actions: TActions; +``` + +Defined in: [store.ts:17](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L17) + +## Accessors + +### state + +#### Get Signature + +```ts +get state(): T; +``` + +Defined in: [store.ts:46](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L46) + +##### Returns + +`T` + +## Methods + +### get() + +```ts +get(): T; +``` + +Defined in: [store.ts:49](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L49) + +#### Returns + +`T` + +*** + +### setState() + +```ts +setState(updater): void; +``` + +Defined in: [store.ts:43](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L43) + +#### Parameters + +##### updater + +(`prev`) => `T` + +#### Returns + +`void` + +*** + +### subscribe() + +```ts +subscribe(observerOrFn): Subscription; +``` + +Defined in: [store.ts:52](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L52) + +#### Parameters + +##### observerOrFn + +[`Observer`](../type-aliases/Observer.md)\<`T`\> | (`value`) => `void` + +#### Returns + +[`Subscription`](../interfaces/Subscription.md) diff --git a/docs/reference/functions/batch.md b/docs/reference/functions/batch.md new file mode 100644 index 00000000..b77cfeb9 --- /dev/null +++ b/docs/reference/functions/batch.md @@ -0,0 +1,22 @@ +--- +id: batch +title: batch +--- + +# Function: batch() + +```ts +function batch(fn): void; +``` + +Defined in: [atom.ts:62](https://github.com/TanStack/store/blob/main/packages/store/src/atom.ts#L62) + +## Parameters + +### fn + +() => `void` + +## Returns + +`void` diff --git a/docs/reference/functions/createAsyncAtom.md b/docs/reference/functions/createAsyncAtom.md new file mode 100644 index 00000000..0e8eca80 --- /dev/null +++ b/docs/reference/functions/createAsyncAtom.md @@ -0,0 +1,32 @@ +--- +id: createAsyncAtom +title: createAsyncAtom +--- + +# Function: createAsyncAtom() + +```ts +function createAsyncAtom(getValue, options?): ReadonlyAtom>; +``` + +Defined in: [atom.ts:100](https://github.com/TanStack/store/blob/main/packages/store/src/atom.ts#L100) + +## Type Parameters + +### T + +`T` + +## Parameters + +### getValue + +() => `Promise`\<`T`\> + +### options? + +[`AtomOptions`](../interfaces/AtomOptions.md)\<`AsyncAtomState`\<`T`, `unknown`\>\> + +## Returns + +[`ReadonlyAtom`](../interfaces/ReadonlyAtom.md)\<`AsyncAtomState`\<`T`, `unknown`\>\> diff --git a/docs/reference/functions/createAtom.md b/docs/reference/functions/createAtom.md new file mode 100644 index 00000000..ad37a11c --- /dev/null +++ b/docs/reference/functions/createAtom.md @@ -0,0 +1,62 @@ +--- +id: createAtom +title: createAtom +--- + +# Function: createAtom() + +## Call Signature + +```ts +function createAtom(getValue, options?): ReadonlyAtom; +``` + +Defined in: [atom.ts:138](https://github.com/TanStack/store/blob/main/packages/store/src/atom.ts#L138) + +### Type Parameters + +#### T + +`T` + +### Parameters + +#### getValue + +(`prev?`) => `T` + +#### options? + +[`AtomOptions`](../interfaces/AtomOptions.md)\<`T`\> + +### Returns + +[`ReadonlyAtom`](../interfaces/ReadonlyAtom.md)\<`T`\> + +## Call Signature + +```ts +function createAtom(initialValue, options?): Atom; +``` + +Defined in: [atom.ts:142](https://github.com/TanStack/store/blob/main/packages/store/src/atom.ts#L142) + +### Type Parameters + +#### T + +`T` + +### Parameters + +#### initialValue + +`T` + +#### options? + +[`AtomOptions`](../interfaces/AtomOptions.md)\<`T`\> + +### Returns + +[`Atom`](../interfaces/Atom.md)\<`T`\> diff --git a/docs/reference/functions/createStore.md b/docs/reference/functions/createStore.md new file mode 100644 index 00000000..8c211275 --- /dev/null +++ b/docs/reference/functions/createStore.md @@ -0,0 +1,86 @@ +--- +id: createStore +title: createStore +--- + +# Function: createStore() + +## Call Signature + +```ts +function createStore(getValue): ReadonlyStore; +``` + +Defined in: [store.ts:86](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L86) + +### Type Parameters + +#### T + +`T` + +### Parameters + +#### getValue + +(`prev?`) => `T` + +### Returns + +[`ReadonlyStore`](../classes/ReadonlyStore.md)\<`T`\> + +## Call Signature + +```ts +function createStore(initialValue): Store; +``` + +Defined in: [store.ts:89](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L89) + +### Type Parameters + +#### T + +`T` + +### Parameters + +#### initialValue + +`T` + +### Returns + +[`Store`](../classes/Store.md)\<`T`\> + +## Call Signature + +```ts +function createStore(initialValue, actions): Store; +``` + +Defined in: [store.ts:90](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L90) + +### Type Parameters + +#### T + +`T` + +#### TActions + +`TActions` *extends* [`StoreActionMap`](../type-aliases/StoreActionMap.md) + +### Parameters + +#### initialValue + +`NonFunction`\<`T`\> + +#### actions + +[`StoreActionsFactory`](../type-aliases/StoreActionsFactory.md)\<`T`, `TActions`\> + +### Returns + +[`Store`](../classes/Store.md)\<`T`, `TActions`\> diff --git a/docs/reference/functions/flush.md b/docs/reference/functions/flush.md new file mode 100644 index 00000000..af4df285 --- /dev/null +++ b/docs/reference/functions/flush.md @@ -0,0 +1,16 @@ +--- +id: flush +title: flush +--- + +# Function: flush() + +```ts +function flush(): void; +``` + +Defined in: [atom.ts:81](https://github.com/TanStack/store/blob/main/packages/store/src/atom.ts#L81) + +## Returns + +`void` diff --git a/docs/reference/functions/shallow.md b/docs/reference/functions/shallow.md new file mode 100644 index 00000000..fb3b313a --- /dev/null +++ b/docs/reference/functions/shallow.md @@ -0,0 +1,32 @@ +--- +id: shallow +title: shallow +--- + +# Function: shallow() + +```ts +function shallow(objA, objB): boolean; +``` + +Defined in: [shallow.ts:1](https://github.com/TanStack/store/blob/main/packages/store/src/shallow.ts#L1) + +## Type Parameters + +### T + +`T` + +## Parameters + +### objA + +`T` + +### objB + +`T` + +## Returns + +`boolean` diff --git a/docs/reference/functions/toObserver.md b/docs/reference/functions/toObserver.md new file mode 100644 index 00000000..2f7a17ee --- /dev/null +++ b/docs/reference/functions/toObserver.md @@ -0,0 +1,39 @@ +--- +id: toObserver +title: toObserver +--- + +# Function: toObserver() + +```ts +function toObserver( + nextHandler?, + errorHandler?, +completionHandler?): Observer; +``` + +Defined in: [atom.ts:12](https://github.com/TanStack/store/blob/main/packages/store/src/atom.ts#L12) + +## Type Parameters + +### T + +`T` + +## Parameters + +### nextHandler? + +[`Observer`](../type-aliases/Observer.md)\<`T`\> | (`value`) => `void` + +### errorHandler? + +(`error`) => `void` + +### completionHandler? + +() => `void` + +## Returns + +[`Observer`](../type-aliases/Observer.md)\<`T`\> diff --git a/docs/reference/index.md b/docs/reference/index.md new file mode 100644 index 00000000..afc8e5dd --- /dev/null +++ b/docs/reference/index.md @@ -0,0 +1,43 @@ +--- +id: "@tanstack/store" +title: "@tanstack/store" +--- + +# @tanstack/store + +## Classes + +- [ReadonlyStore](classes/ReadonlyStore.md) +- [Store](classes/Store.md) + +## Interfaces + +- [Atom](interfaces/Atom.md) +- [AtomOptions](interfaces/AtomOptions.md) +- [BaseAtom](interfaces/BaseAtom.md) +- [InternalBaseAtom](interfaces/InternalBaseAtom.md) +- [InternalReadonlyAtom](interfaces/InternalReadonlyAtom.md) +- [InteropSubscribable](interfaces/InteropSubscribable.md) +- [Readable](interfaces/Readable.md) +- [ReadonlyAtom](interfaces/ReadonlyAtom.md) +- [Subscribable](interfaces/Subscribable.md) +- [Subscription](interfaces/Subscription.md) + +## Type Aliases + +- [AnyAtom](type-aliases/AnyAtom.md) +- [Observer](type-aliases/Observer.md) +- [Selection](type-aliases/Selection.md) +- [StoreAction](type-aliases/StoreAction.md) +- [StoreActionMap](type-aliases/StoreActionMap.md) +- [StoreActionsFactory](type-aliases/StoreActionsFactory.md) + +## Functions + +- [batch](functions/batch.md) +- [createAsyncAtom](functions/createAsyncAtom.md) +- [createAtom](functions/createAtom.md) +- [createStore](functions/createStore.md) +- [flush](functions/flush.md) +- [shallow](functions/shallow.md) +- [toObserver](functions/toObserver.md) diff --git a/docs/reference/interfaces/Atom.md b/docs/reference/interfaces/Atom.md new file mode 100644 index 00000000..63124fd2 --- /dev/null +++ b/docs/reference/interfaces/Atom.md @@ -0,0 +1,62 @@ +--- +id: Atom +title: Atom +--- + +# Interface: Atom\ + +Defined in: [types.ts:42](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L42) + +## Extends + +- [`BaseAtom`](BaseAtom.md)\<`T`\> + +## Type Parameters + +### T + +`T` + +## Properties + +### get() + +```ts +get: () => T; +``` + +Defined in: [types.ts:30](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L30) + +#### Returns + +`T` + +#### Inherited from + +[`BaseAtom`](BaseAtom.md).[`get`](BaseAtom.md#get) + +*** + +### set + +```ts +set: (fn) => void & (value) => void; +``` + +Defined in: [types.ts:44](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L44) + +Sets the value of the atom using a function. + +*** + +### subscribe + +```ts +subscribe: (observer) => Subscription & (next, error?, complete?) => Subscription; +``` + +Defined in: [types.ts:21](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L21) + +#### Inherited from + +[`BaseAtom`](BaseAtom.md).[`subscribe`](BaseAtom.md#subscribe) diff --git a/docs/reference/interfaces/AtomOptions.md b/docs/reference/interfaces/AtomOptions.md new file mode 100644 index 00000000..c05fee8d --- /dev/null +++ b/docs/reference/interfaces/AtomOptions.md @@ -0,0 +1,38 @@ +--- +id: AtomOptions +title: AtomOptions +--- + +# Interface: AtomOptions\ + +Defined in: [types.ts:47](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L47) + +## Type Parameters + +### T + +`T` + +## Properties + +### compare()? + +```ts +optional compare: (prev, next) => boolean; +``` + +Defined in: [types.ts:48](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L48) + +#### Parameters + +##### prev + +`T` + +##### next + +`T` + +#### Returns + +`boolean` diff --git a/docs/reference/interfaces/BaseAtom.md b/docs/reference/interfaces/BaseAtom.md new file mode 100644 index 00000000..57de2ed9 --- /dev/null +++ b/docs/reference/interfaces/BaseAtom.md @@ -0,0 +1,55 @@ +--- +id: BaseAtom +title: BaseAtom +--- + +# Interface: BaseAtom\ + +Defined in: [types.ts:33](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L33) + +## Extends + +- [`Subscribable`](Subscribable.md)\<`T`\>.[`Readable`](Readable.md)\<`T`\> + +## Extended by + +- [`Atom`](Atom.md) +- [`ReadonlyAtom`](ReadonlyAtom.md) + +## Type Parameters + +### T + +`T` + +## Properties + +### get() + +```ts +get: () => T; +``` + +Defined in: [types.ts:30](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L30) + +#### Returns + +`T` + +#### Inherited from + +[`Readable`](Readable.md).[`get`](Readable.md#get) + +*** + +### subscribe + +```ts +subscribe: (observer) => Subscription & (next, error?, complete?) => Subscription; +``` + +Defined in: [types.ts:21](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L21) + +#### Inherited from + +[`Subscribable`](Subscribable.md).[`subscribe`](Subscribable.md#subscribe) diff --git a/docs/reference/interfaces/InternalBaseAtom.md b/docs/reference/interfaces/InternalBaseAtom.md new file mode 100644 index 00000000..b2716e81 --- /dev/null +++ b/docs/reference/interfaces/InternalBaseAtom.md @@ -0,0 +1,88 @@ +--- +id: InternalBaseAtom +title: InternalBaseAtom +--- + +# Interface: InternalBaseAtom\ + +Defined in: [types.ts:35](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L35) + +## Extends + +- [`Subscribable`](Subscribable.md)\<`T`\>.[`Readable`](Readable.md)\<`T`\> + +## Extended by + +- [`InternalReadonlyAtom`](InternalReadonlyAtom.md) + +## Type Parameters + +### T + +`T` + +## Properties + +### \_snapshot + +```ts +_snapshot: T; +``` + +Defined in: [types.ts:37](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L37) + +**`Internal`** + +*** + +### \_update() + +```ts +_update: (getValue?) => boolean; +``` + +Defined in: [types.ts:39](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L39) + +**`Internal`** + +#### Parameters + +##### getValue? + +`T` | (`snapshot`) => `T` + +#### Returns + +`boolean` + +*** + +### get() + +```ts +get: () => T; +``` + +Defined in: [types.ts:30](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L30) + +#### Returns + +`T` + +#### Inherited from + +[`Readable`](Readable.md).[`get`](Readable.md#get) + +*** + +### subscribe + +```ts +subscribe: (observer) => Subscription & (next, error?, complete?) => Subscription; +``` + +Defined in: [types.ts:21](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L21) + +#### Inherited from + +[`Subscribable`](Subscribable.md).[`subscribe`](Subscribable.md#subscribe) diff --git a/docs/reference/interfaces/InternalReadonlyAtom.md b/docs/reference/interfaces/InternalReadonlyAtom.md new file mode 100644 index 00000000..c4b92f33 --- /dev/null +++ b/docs/reference/interfaces/InternalReadonlyAtom.md @@ -0,0 +1,172 @@ +--- +id: InternalReadonlyAtom +title: InternalReadonlyAtom +--- + +# Interface: InternalReadonlyAtom\ + +Defined in: [types.ts:53](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L53) + +## Extends + +- [`InternalBaseAtom`](InternalBaseAtom.md)\<`T`\>.`ReactiveNode` + +## Type Parameters + +### T + +`T` + +## Properties + +### \_snapshot + +```ts +_snapshot: T; +``` + +Defined in: [types.ts:37](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L37) + +**`Internal`** + +#### Inherited from + +[`InternalBaseAtom`](InternalBaseAtom.md).[`_snapshot`](InternalBaseAtom.md#_snapshot) + +*** + +### \_update() + +```ts +_update: (getValue?) => boolean; +``` + +Defined in: [types.ts:39](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L39) + +**`Internal`** + +#### Parameters + +##### getValue? + +`T` | (`snapshot`) => `T` + +#### Returns + +`boolean` + +#### Inherited from + +[`InternalBaseAtom`](InternalBaseAtom.md).[`_update`](InternalBaseAtom.md#_update) + +*** + +### deps? + +```ts +optional deps: Link; +``` + +Defined in: [alien.ts:6](https://github.com/TanStack/store/blob/main/packages/store/src/alien.ts#L6) + +#### Inherited from + +```ts +ReactiveNode.deps +``` + +*** + +### depsTail? + +```ts +optional depsTail: Link; +``` + +Defined in: [alien.ts:7](https://github.com/TanStack/store/blob/main/packages/store/src/alien.ts#L7) + +#### Inherited from + +```ts +ReactiveNode.depsTail +``` + +*** + +### flags + +```ts +flags: ReactiveFlags; +``` + +Defined in: [alien.ts:10](https://github.com/TanStack/store/blob/main/packages/store/src/alien.ts#L10) + +#### Inherited from + +```ts +ReactiveNode.flags +``` + +*** + +### get() + +```ts +get: () => T; +``` + +Defined in: [types.ts:30](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L30) + +#### Returns + +`T` + +#### Inherited from + +[`InternalBaseAtom`](InternalBaseAtom.md).[`get`](InternalBaseAtom.md#get) + +*** + +### subs? + +```ts +optional subs: Link; +``` + +Defined in: [alien.ts:8](https://github.com/TanStack/store/blob/main/packages/store/src/alien.ts#L8) + +#### Inherited from + +```ts +ReactiveNode.subs +``` + +*** + +### subscribe + +```ts +subscribe: (observer) => Subscription & (next, error?, complete?) => Subscription; +``` + +Defined in: [types.ts:21](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L21) + +#### Inherited from + +[`InternalBaseAtom`](InternalBaseAtom.md).[`subscribe`](InternalBaseAtom.md#subscribe) + +*** + +### subsTail? + +```ts +optional subsTail: Link; +``` + +Defined in: [alien.ts:9](https://github.com/TanStack/store/blob/main/packages/store/src/alien.ts#L9) + +#### Inherited from + +```ts +ReactiveNode.subsTail +``` diff --git a/docs/reference/interfaces/InteropSubscribable.md b/docs/reference/interfaces/InteropSubscribable.md new file mode 100644 index 00000000..45c4cea3 --- /dev/null +++ b/docs/reference/interfaces/InteropSubscribable.md @@ -0,0 +1,38 @@ +--- +id: InteropSubscribable +title: InteropSubscribable +--- + +# Interface: InteropSubscribable\ + +Defined in: [types.ts:5](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L5) + +## Extended by + +- [`Subscribable`](Subscribable.md) + +## Type Parameters + +### T + +`T` + +## Properties + +### subscribe() + +```ts +subscribe: (observer) => Subscription; +``` + +Defined in: [types.ts:6](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L6) + +#### Parameters + +##### observer + +[`Observer`](../type-aliases/Observer.md)\<`T`\> + +#### Returns + +[`Subscription`](Subscription.md) diff --git a/docs/reference/interfaces/Readable.md b/docs/reference/interfaces/Readable.md new file mode 100644 index 00000000..96f344b1 --- /dev/null +++ b/docs/reference/interfaces/Readable.md @@ -0,0 +1,51 @@ +--- +id: Readable +title: Readable +--- + +# Interface: Readable\ + +Defined in: [types.ts:29](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L29) + +## Extends + +- [`Subscribable`](Subscribable.md)\<`T`\> + +## Extended by + +- [`BaseAtom`](BaseAtom.md) +- [`InternalBaseAtom`](InternalBaseAtom.md) + +## Type Parameters + +### T + +`T` + +## Properties + +### get() + +```ts +get: () => T; +``` + +Defined in: [types.ts:30](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L30) + +#### Returns + +`T` + +*** + +### subscribe + +```ts +subscribe: (observer) => Subscription & (next, error?, complete?) => Subscription; +``` + +Defined in: [types.ts:21](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L21) + +#### Inherited from + +[`Subscribable`](Subscribable.md).[`subscribe`](Subscribable.md#subscribe) diff --git a/docs/reference/interfaces/ReadonlyAtom.md b/docs/reference/interfaces/ReadonlyAtom.md new file mode 100644 index 00000000..2aeaa860 --- /dev/null +++ b/docs/reference/interfaces/ReadonlyAtom.md @@ -0,0 +1,60 @@ +--- +id: ReadonlyAtom +title: ReadonlyAtom +--- + +# Interface: ReadonlyAtom\ + +Defined in: [types.ts:67](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L67) + +An atom that is read-only and cannot be set. + +## Example + +```ts +const atom = createAtom(() => 42); +// @ts-expect-error - Cannot set a readonly atom +atom.set(43); +``` + +## Extends + +- [`BaseAtom`](BaseAtom.md)\<`T`\> + +## Type Parameters + +### T + +`T` + +## Properties + +### get() + +```ts +get: () => T; +``` + +Defined in: [types.ts:30](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L30) + +#### Returns + +`T` + +#### Inherited from + +[`BaseAtom`](BaseAtom.md).[`get`](BaseAtom.md#get) + +*** + +### subscribe + +```ts +subscribe: (observer) => Subscription & (next, error?, complete?) => Subscription; +``` + +Defined in: [types.ts:21](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L21) + +#### Inherited from + +[`BaseAtom`](BaseAtom.md).[`subscribe`](BaseAtom.md#subscribe) diff --git a/docs/reference/interfaces/Subscribable.md b/docs/reference/interfaces/Subscribable.md new file mode 100644 index 00000000..0857be0c --- /dev/null +++ b/docs/reference/interfaces/Subscribable.md @@ -0,0 +1,38 @@ +--- +id: Subscribable +title: Subscribable +--- + +# Interface: Subscribable\ + +Defined in: [types.ts:20](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L20) + +## Extends + +- [`InteropSubscribable`](InteropSubscribable.md)\<`T`\> + +## Extended by + +- [`Readable`](Readable.md) +- [`BaseAtom`](BaseAtom.md) +- [`InternalBaseAtom`](InternalBaseAtom.md) + +## Type Parameters + +### T + +`T` + +## Properties + +### subscribe + +```ts +subscribe: (observer) => Subscription & (next, error?, complete?) => Subscription; +``` + +Defined in: [types.ts:21](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L21) + +#### Overrides + +[`InteropSubscribable`](InteropSubscribable.md).[`subscribe`](InteropSubscribable.md#subscribe) diff --git a/docs/reference/interfaces/Subscription.md b/docs/reference/interfaces/Subscription.md new file mode 100644 index 00000000..5ddc5fba --- /dev/null +++ b/docs/reference/interfaces/Subscription.md @@ -0,0 +1,22 @@ +--- +id: Subscription +title: Subscription +--- + +# Interface: Subscription + +Defined in: [types.ts:16](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L16) + +## Properties + +### unsubscribe() + +```ts +unsubscribe: () => void; +``` + +Defined in: [types.ts:17](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L17) + +#### Returns + +`void` diff --git a/docs/reference/type-aliases/AnyAtom.md b/docs/reference/type-aliases/AnyAtom.md new file mode 100644 index 00000000..d41e125c --- /dev/null +++ b/docs/reference/type-aliases/AnyAtom.md @@ -0,0 +1,12 @@ +--- +id: AnyAtom +title: AnyAtom +--- + +# Type Alias: AnyAtom + +```ts +type AnyAtom = BaseAtom; +``` + +Defined in: [types.ts:51](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L51) diff --git a/docs/reference/type-aliases/Observer.md b/docs/reference/type-aliases/Observer.md new file mode 100644 index 00000000..47cd75dd --- /dev/null +++ b/docs/reference/type-aliases/Observer.md @@ -0,0 +1,72 @@ +--- +id: Observer +title: Observer +--- + +# Type Alias: Observer\ + +```ts +type Observer = object; +``` + +Defined in: [types.ts:10](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L10) + +## Type Parameters + +### T + +`T` + +## Properties + +### complete()? + +```ts +optional complete: () => void; +``` + +Defined in: [types.ts:13](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L13) + +#### Returns + +`void` + +*** + +### error()? + +```ts +optional error: (err) => void; +``` + +Defined in: [types.ts:12](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L12) + +#### Parameters + +##### err + +`unknown` + +#### Returns + +`void` + +*** + +### next()? + +```ts +optional next: (value) => void; +``` + +Defined in: [types.ts:11](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L11) + +#### Parameters + +##### value + +`T` + +#### Returns + +`void` diff --git a/docs/reference/type-aliases/Selection.md b/docs/reference/type-aliases/Selection.md new file mode 100644 index 00000000..484ad187 --- /dev/null +++ b/docs/reference/type-aliases/Selection.md @@ -0,0 +1,18 @@ +--- +id: Selection +title: Selection +--- + +# Type Alias: Selection\ + +```ts +type Selection = Readable; +``` + +Defined in: [types.ts:3](https://github.com/TanStack/store/blob/main/packages/store/src/types.ts#L3) + +## Type Parameters + +### TSelected + +`TSelected` diff --git a/docs/reference/type-aliases/StoreAction.md b/docs/reference/type-aliases/StoreAction.md new file mode 100644 index 00000000..1e839025 --- /dev/null +++ b/docs/reference/type-aliases/StoreAction.md @@ -0,0 +1,22 @@ +--- +id: StoreAction +title: StoreAction +--- + +# Type Alias: StoreAction() + +```ts +type StoreAction = (...args) => any; +``` + +Defined in: [store.ts:4](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L4) + +## Parameters + +### args + +...`any`[] + +## Returns + +`any` diff --git a/docs/reference/type-aliases/StoreActionMap.md b/docs/reference/type-aliases/StoreActionMap.md new file mode 100644 index 00000000..5191c5f5 --- /dev/null +++ b/docs/reference/type-aliases/StoreActionMap.md @@ -0,0 +1,12 @@ +--- +id: StoreActionMap +title: StoreActionMap +--- + +# Type Alias: StoreActionMap + +```ts +type StoreActionMap = Record; +``` + +Defined in: [store.ts:6](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L6) diff --git a/docs/reference/type-aliases/StoreActionsFactory.md b/docs/reference/type-aliases/StoreActionsFactory.md new file mode 100644 index 00000000..b7caaee8 --- /dev/null +++ b/docs/reference/type-aliases/StoreActionsFactory.md @@ -0,0 +1,38 @@ +--- +id: StoreActionsFactory +title: StoreActionsFactory +--- + +# Type Alias: StoreActionsFactory()\ + +```ts +type StoreActionsFactory = (store) => TActions; +``` + +Defined in: [store.ts:8](https://github.com/TanStack/store/blob/main/packages/store/src/store.ts#L8) + +## Type Parameters + +### T + +`T` + +### TActions + +`TActions` *extends* [`StoreActionMap`](StoreActionMap.md) + +## Parameters + +### store + +#### get + +[`Store`](../classes/Store.md)\<`T`\>\[`"get"`\] + +#### setState + +[`Store`](../classes/Store.md)\<`T`\>\[`"setState"`\] + +## Returns + +`TActions` From a5d59a13b881f7cd2a20534d45a475ac01c90902 Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Tue, 28 Apr 2026 12:33:40 +0200 Subject: [PATCH 09/17] fix: symbol keys eval --- packages/store/src/evaluate.ts | 11 +++++++++-- packages/store/tests/evaluate.spec.ts | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/packages/store/src/evaluate.ts b/packages/store/src/evaluate.ts index d7ce522c..2843312e 100644 --- a/packages/store/src/evaluate.ts +++ b/packages/store/src/evaluate.ts @@ -94,12 +94,13 @@ function _evaluate( return true } + // guards against runtime cross type evaluation if (Object.getPrototypeOf(objA) !== Object.getPrototypeOf(objB)) { return false } - const keysA = Object.keys(objA as object) - const keysB = Object.keys(objB as object) + const keysA = getOwnKeys(objA as object) + const keysB = getOwnKeys(objB as object) if (keysA.length !== keysB.length) { return false @@ -129,3 +130,9 @@ function _evaluate( return true } + +function getOwnKeys(obj: object): Array { + return (Object.keys(obj) as Array).concat( + Object.getOwnPropertySymbols(obj), + ) +} diff --git a/packages/store/tests/evaluate.spec.ts b/packages/store/tests/evaluate.spec.ts index 271eb1c4..7cb2b983 100644 --- a/packages/store/tests/evaluate.spec.ts +++ b/packages/store/tests/evaluate.spec.ts @@ -205,6 +205,15 @@ describe('evaluate', () => { expect(evaluate({ file: file1 }, { file: file2 })).toEqual(false) expect(evaluate({ file: file1 }, { file: fileDiffName })).toEqual(false) }) + + it('should test equality between objects with Symbol keys', () => { + const sym = Symbol('id') + + expect(evaluate({ [sym]: 1, name: 'foo' }, { [sym]: 1, name: 'foo' })).toEqual(true) + expect(evaluate({ [sym]: 1, name: 'foo' }, { [sym]: 2, name: 'foo' })).toEqual(false) + expect(evaluate({ [sym]: 1 } as any, {} as any)).toEqual(false) + expect(evaluate({} as any, { [sym]: 1 } as any)).toEqual(false) + }) }) describe('deep', () => { @@ -418,6 +427,15 @@ describe('evaluate', () => { ).toEqual(false) }) + it('should test equality between objects with Symbol keys', () => { + const sym = Symbol('id') + + expect(evaluate({ [sym]: 1, name: 'foo' }, { [sym]: 1, name: 'foo' }, { mode: 'deep' })).toEqual(true) + expect(evaluate({ [sym]: 1, name: 'foo' }, { [sym]: 2, name: 'foo' }, { mode: 'deep' })).toEqual(false) + expect(evaluate({ [sym]: { nested: true } } as any, { [sym]: { nested: true } } as any, { mode: 'deep' })).toEqual(true) + expect(evaluate({ [sym]: { nested: true } } as any, { [sym]: { nested: false } } as any, { mode: 'deep' })).toEqual(false) + }) + it('should handle circular references', () => { const a: any = { x: 1 } a.self = a From 14efb5ec35bded00e0652670c493bede643ba5e5 Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Tue, 28 Apr 2026 12:53:48 +0200 Subject: [PATCH 10/17] fix: unstub cleanup --- packages/store/tests/evaluate.spec.ts | 48 ++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/packages/store/tests/evaluate.spec.ts b/packages/store/tests/evaluate.spec.ts index 7cb2b983..8a94c444 100644 --- a/packages/store/tests/evaluate.spec.ts +++ b/packages/store/tests/evaluate.spec.ts @@ -44,9 +44,11 @@ describe('evaluate', () => { lastModified: 0, } - expect(() => evaluate(file1, file2)).not.toThrow() - - vi.unstubAllGlobals() + try { + expect(() => evaluate(file1, file2)).not.toThrow() + } finally { + vi.unstubAllGlobals() + } }) describe('shallow', () => { @@ -209,8 +211,12 @@ describe('evaluate', () => { it('should test equality between objects with Symbol keys', () => { const sym = Symbol('id') - expect(evaluate({ [sym]: 1, name: 'foo' }, { [sym]: 1, name: 'foo' })).toEqual(true) - expect(evaluate({ [sym]: 1, name: 'foo' }, { [sym]: 2, name: 'foo' })).toEqual(false) + expect( + evaluate({ [sym]: 1, name: 'foo' }, { [sym]: 1, name: 'foo' }), + ).toEqual(true) + expect( + evaluate({ [sym]: 1, name: 'foo' }, { [sym]: 2, name: 'foo' }), + ).toEqual(false) expect(evaluate({ [sym]: 1 } as any, {} as any)).toEqual(false) expect(evaluate({} as any, { [sym]: 1 } as any)).toEqual(false) }) @@ -430,10 +436,34 @@ describe('evaluate', () => { it('should test equality between objects with Symbol keys', () => { const sym = Symbol('id') - expect(evaluate({ [sym]: 1, name: 'foo' }, { [sym]: 1, name: 'foo' }, { mode: 'deep' })).toEqual(true) - expect(evaluate({ [sym]: 1, name: 'foo' }, { [sym]: 2, name: 'foo' }, { mode: 'deep' })).toEqual(false) - expect(evaluate({ [sym]: { nested: true } } as any, { [sym]: { nested: true } } as any, { mode: 'deep' })).toEqual(true) - expect(evaluate({ [sym]: { nested: true } } as any, { [sym]: { nested: false } } as any, { mode: 'deep' })).toEqual(false) + expect( + evaluate( + { [sym]: 1, name: 'foo' }, + { [sym]: 1, name: 'foo' }, + { mode: 'deep' }, + ), + ).toEqual(true) + expect( + evaluate( + { [sym]: 1, name: 'foo' }, + { [sym]: 2, name: 'foo' }, + { mode: 'deep' }, + ), + ).toEqual(false) + expect( + evaluate( + { [sym]: { nested: true } } as any, + { [sym]: { nested: true } } as any, + { mode: 'deep' }, + ), + ).toEqual(true) + expect( + evaluate( + { [sym]: { nested: true } } as any, + { [sym]: { nested: false } } as any, + { mode: 'deep' }, + ), + ).toEqual(false) }) it('should handle circular references', () => { From 4c5120f7811f7462546ae48b63052b38599f0f91 Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Tue, 28 Apr 2026 13:11:33 +0200 Subject: [PATCH 11/17] chore: update tests --- packages/solid-store/tests/index.test.tsx | 34 +++++++++++------------ packages/store/src/index.ts | 1 + packages/svelte-store/tests/index.test.ts | 22 +++++++-------- packages/vue-store/tests/index.test.tsx | 34 +++++++++++------------ 4 files changed, 46 insertions(+), 45 deletions(-) diff --git a/packages/solid-store/tests/index.test.tsx b/packages/solid-store/tests/index.test.tsx index de9895d0..d0f3b7f6 100644 --- a/packages/solid-store/tests/index.test.tsx +++ b/packages/solid-store/tests/index.test.tsx @@ -4,7 +4,7 @@ import { createAtom, createStore } from '@tanstack/store' import { _useStore, createStoreContext, - shallow, + evaluate, useAtom, useSelector, useStore, @@ -263,101 +263,101 @@ describe('shallow', () => { test('should return true for shallowly equal objects', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 1, b: 'hello' } - expect(shallow(objA, objB)).toBe(true) + expect(evaluate(objA, objB)).toBe(true) }) test('should return false for objects with different values', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 2, b: 'world' } - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return false for objects with different keys', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 1, c: 'world' } // @ts-expect-error - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return false for objects with different structures', () => { const objA = { a: 1, b: 'hello' } const objB = [1, 'hello'] // @ts-expect-error - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return false for one object being null', () => { const objA = { a: 1, b: 'hello' } const objB = null - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return false for one object being undefined', () => { const objA = { a: 1, b: 'hello' } const objB = undefined - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return true for two null objects', () => { const objA = null const objB = null - expect(shallow(objA, objB)).toBe(true) + expect(evaluate(objA, objB)).toBe(true) }) test('should return false for objects with different types', () => { const objA = { a: 1, b: 'hello' } const objB = { a: '1', b: 'hello' } // @ts-expect-error - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return true for shallow equal objects with symbol keys', () => { const sym = Symbol.for('key') const objA = { [sym]: 1 } const objB = { [sym]: 1 } - expect(shallow(objA, objB)).toBe(true) + expect(evaluate(objA, objB)).toBe(true) }) test('should return false for shallow different values for symbol keys', () => { const sym = Symbol.for('key') const objA = { [sym]: 1 } const objB = { [sym]: 2 } - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return true for shallowly equal maps', () => { const objA = new Map([['1', 'hello']]) const objB = new Map([['1', 'hello']]) - expect(shallow(objA, objB)).toBe(true) + expect(evaluate(objA, objB)).toBe(true) }) test('should return false for maps with different values', () => { const objA = new Map([['1', 'hello']]) const objB = new Map([['1', 'world']]) - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return true for shallowly equal sets', () => { const objA = new Set([1]) const objB = new Set([1]) - expect(shallow(objA, objB)).toBe(true) + expect(evaluate(objA, objB)).toBe(true) }) test('should return false for sets with different values', () => { const objA = new Set([1]) const objB = new Set([2]) - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return false for dates with different values', () => { const objA = new Date('2025-04-10T14:48:00') const objB = new Date('2025-04-10T14:58:00') - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return true for equal dates', () => { const objA = new Date('2025-02-10') const objB = new Date('2025-02-10') - expect(shallow(objA, objB)).toBe(true) + expect(evaluate(objA, objB)).toBe(true) }) }) diff --git a/packages/store/src/index.ts b/packages/store/src/index.ts index 71215a5e..b1ecad9a 100644 --- a/packages/store/src/index.ts +++ b/packages/store/src/index.ts @@ -2,3 +2,4 @@ export * from './types' export * from './atom' export * from './store' export * from './shallow' +export * from './evaluate' diff --git a/packages/svelte-store/tests/index.test.ts b/packages/svelte-store/tests/index.test.ts index dc304f01..65a38ba0 100644 --- a/packages/svelte-store/tests/index.test.ts +++ b/packages/svelte-store/tests/index.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it, test } from 'vitest' import { render, waitFor } from '@testing-library/svelte' import { userEvent } from '@testing-library/user-event' -import { shallow } from '../src/index.svelte.js' +import { evaluate } from '../src/index.svelte.js' import TestBaseStore from './BaseStore.test.svelte' import TestRerender from './Render.test.svelte' import TestValue from './Value.test.svelte' @@ -44,63 +44,63 @@ describe('shallow', () => { test('should return true for shallowly equal objects', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 1, b: 'hello' } - expect(shallow(objA, objB)).toBe(true) + expect(evaluate(objA, objB)).toBe(true) }) test('should return false for objects with different values', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 2, b: 'world' } - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return false for objects with different keys', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 1, c: 'world' } // @ts-expect-error testing invalid input - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return false for objects with different structures', () => { const objA = { a: 1, b: 'hello' } const objB = [1, 'hello'] // @ts-expect-error testing invalid input - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return false for one object being null', () => { const objA = { a: 1, b: 'hello' } const objB = null - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return false for one object being undefined', () => { const objA = { a: 1, b: 'hello' } const objB = undefined - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return true for two null objects', () => { const objA = null const objB = null - expect(shallow(objA, objB)).toBe(true) + expect(evaluate(objA, objB)).toBe(true) }) test('should return false for objects with different types', () => { const objA = { a: 1, b: 'hello' } const objB = { a: '1', b: 'hello' } // @ts-expect-error testing invalid input - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return false for dates with different values', () => { const objA = new Date('2025-04-10T14:48:00') const objB = new Date('2025-04-10T14:58:00') - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return true for equal dates', () => { const objA = new Date('2025-02-10') const objB = new Date('2025-02-10') - expect(shallow(objA, objB)).toBe(true) + expect(evaluate(objA, objB)).toBe(true) }) }) diff --git a/packages/vue-store/tests/index.test.tsx b/packages/vue-store/tests/index.test.tsx index f9a2d0ce..1009c277 100644 --- a/packages/vue-store/tests/index.test.tsx +++ b/packages/vue-store/tests/index.test.tsx @@ -5,7 +5,7 @@ import { createAtom, createStore } from '@tanstack/store' import { userEvent } from '@testing-library/user-event' import { _useStore, - shallow, + evaluate, useAtom, useSelector, useStore, @@ -378,101 +378,101 @@ describe('shallow', () => { test('should return true for shallowly equal objects', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 1, b: 'hello' } - expect(shallow(objA, objB)).toBe(true) + expect(evaluate(objA, objB)).toBe(true) }) test('should return false for objects with different values', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 2, b: 'world' } - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return false for objects with different keys', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 1, c: 'world' } // @ts-expect-error - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return false for objects with different structures', () => { const objA = { a: 1, b: 'hello' } const objB = [1, 'hello'] // @ts-expect-error - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return false for one object being null', () => { const objA = { a: 1, b: 'hello' } const objB = null - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return false for one object being undefined', () => { const objA = { a: 1, b: 'hello' } const objB = undefined - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return true for two null objects', () => { const objA = null const objB = null - expect(shallow(objA, objB)).toBe(true) + expect(evaluate(objA, objB)).toBe(true) }) test('should return false for objects with different types', () => { const objA = { a: 1, b: 'hello' } const objB = { a: '1', b: 'hello' } // @ts-expect-error - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return true for shallow equal objects with symbol keys', () => { const sym = Symbol.for('key') const objA = { [sym]: 1 } const objB = { [sym]: 1 } - expect(shallow(objA, objB)).toBe(true) + expect(evaluate(objA, objB)).toBe(true) }) test('should return false for shallow different values for symbol keys', () => { const sym = Symbol.for('key') const objA = { [sym]: 1 } const objB = { [sym]: 2 } - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return true for shallowly equal maps', () => { const objA = new Map([['1', 'hello']]) const objB = new Map([['1', 'hello']]) - expect(shallow(objA, objB)).toBe(true) + expect(evaluate(objA, objB)).toBe(true) }) test('should return false for maps with different values', () => { const objA = new Map([['1', 'hello']]) const objB = new Map([['1', 'world']]) - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return true for shallowly equal sets', () => { const objA = new Set([1]) const objB = new Set([1]) - expect(shallow(objA, objB)).toBe(true) + expect(evaluate(objA, objB)).toBe(true) }) test('should return false for sets with different values', () => { const objA = new Set([1]) const objB = new Set([2]) - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return false for dates with different values', () => { const objA = new Date('2025-04-10T14:48:00') const objB = new Date('2025-04-10T14:58:00') - expect(shallow(objA, objB)).toBe(false) + expect(evaluate(objA, objB)).toBe(false) }) test('should return true for equal dates', () => { const objA = new Date('2025-02-10') const objB = new Date('2025-02-10') - expect(shallow(objA, objB)).toBe(true) + expect(evaluate(objA, objB)).toBe(true) }) }) From 06f8762698cc050dd8e9551ffbd2ff51d0df30a7 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 11:12:36 +0000 Subject: [PATCH 12/17] ci: apply automated fixes and generate docs --- docs/reference/functions/evaluate.md | 41 ++++++++++++++++++++++++++++ docs/reference/index.md | 1 + 2 files changed, 42 insertions(+) create mode 100644 docs/reference/functions/evaluate.md diff --git a/docs/reference/functions/evaluate.md b/docs/reference/functions/evaluate.md new file mode 100644 index 00000000..3766a2ae --- /dev/null +++ b/docs/reference/functions/evaluate.md @@ -0,0 +1,41 @@ +--- +id: evaluate +title: evaluate +--- + +# Function: evaluate() + +```ts +function evaluate( + objA, + objB, + config): boolean; +``` + +Defined in: [evaluate.ts:1](https://github.com/TanStack/store/blob/main/packages/store/src/evaluate.ts#L1) + +## Type Parameters + +### T + +`T` + +## Parameters + +### objA + +`T` + +### objB + +`T` + +### config + +#### mode + +`"shallow"` \| `"deep"` + +## Returns + +`boolean` diff --git a/docs/reference/index.md b/docs/reference/index.md index afc8e5dd..5b6d355f 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -38,6 +38,7 @@ title: "@tanstack/store" - [createAsyncAtom](functions/createAsyncAtom.md) - [createAtom](functions/createAtom.md) - [createStore](functions/createStore.md) +- [evaluate](functions/evaluate.md) - [flush](functions/flush.md) - [shallow](functions/shallow.md) - [toObserver](functions/toObserver.md) From 6352e9c0edf87e70822c7fd7e12eae434f8cf111 Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Tue, 28 Apr 2026 13:25:33 +0200 Subject: [PATCH 13/17] fix: file test --- packages/store/tests/evaluate.spec.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/store/tests/evaluate.spec.ts b/packages/store/tests/evaluate.spec.ts index 8a94c444..78719e11 100644 --- a/packages/store/tests/evaluate.spec.ts +++ b/packages/store/tests/evaluate.spec.ts @@ -187,16 +187,19 @@ describe('evaluate', () => { }) it('should test equality between File objects', () => { - const file1 = new File(['hello'], 'hello.txt', { type: 'text/plain' }) - const file2 = new File(['hello'], 'hello.txt', { type: 'text/plain' }) + const file1 = new File(['hello'], 'hello.txt', { type: 'text/plain', lastModified: 0 }) + const file2 = new File(['hello'], 'hello.txt', { type: 'text/plain', lastModified: 0 }) const fileDiffName = new File(['hello'], 'world.txt', { type: 'text/plain', + lastModified: 0, }) const fileDiffType = new File(['hello'], 'hello.txt', { type: 'text/html', + lastModified: 0, }) const fileDiffSize = new File(['hello world'], 'hello.txt', { type: 'text/plain', + lastModified: 0, }) expect(evaluate(file1, file2)).toEqual(true) @@ -408,16 +411,19 @@ describe('evaluate', () => { }) it('should test equality between File objects', () => { - const file1 = new File(['hello'], 'hello.txt', { type: 'text/plain' }) - const file2 = new File(['hello'], 'hello.txt', { type: 'text/plain' }) + const file1 = new File(['hello'], 'hello.txt', { type: 'text/plain', lastModified: 0 }) + const file2 = new File(['hello'], 'hello.txt', { type: 'text/plain', lastModified: 0 }) const fileDiffName = new File(['hello'], 'world.txt', { type: 'text/plain', + lastModified: 0, }) const fileDiffType = new File(['hello'], 'hello.txt', { type: 'text/html', + lastModified: 0, }) const fileDiffSize = new File(['hello world'], 'hello.txt', { type: 'text/plain', + lastModified: 0, }) expect(evaluate(file1, file2, { mode: 'deep' })).toEqual(true) From ee3609e13f139895e71c8e783a75794d6b800807 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 11:26:59 +0000 Subject: [PATCH 14/17] ci: apply automated fixes and generate docs --- packages/store/tests/evaluate.spec.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/store/tests/evaluate.spec.ts b/packages/store/tests/evaluate.spec.ts index 78719e11..227c632f 100644 --- a/packages/store/tests/evaluate.spec.ts +++ b/packages/store/tests/evaluate.spec.ts @@ -187,8 +187,14 @@ describe('evaluate', () => { }) it('should test equality between File objects', () => { - const file1 = new File(['hello'], 'hello.txt', { type: 'text/plain', lastModified: 0 }) - const file2 = new File(['hello'], 'hello.txt', { type: 'text/plain', lastModified: 0 }) + const file1 = new File(['hello'], 'hello.txt', { + type: 'text/plain', + lastModified: 0, + }) + const file2 = new File(['hello'], 'hello.txt', { + type: 'text/plain', + lastModified: 0, + }) const fileDiffName = new File(['hello'], 'world.txt', { type: 'text/plain', lastModified: 0, @@ -411,8 +417,14 @@ describe('evaluate', () => { }) it('should test equality between File objects', () => { - const file1 = new File(['hello'], 'hello.txt', { type: 'text/plain', lastModified: 0 }) - const file2 = new File(['hello'], 'hello.txt', { type: 'text/plain', lastModified: 0 }) + const file1 = new File(['hello'], 'hello.txt', { + type: 'text/plain', + lastModified: 0, + }) + const file2 = new File(['hello'], 'hello.txt', { + type: 'text/plain', + lastModified: 0, + }) const fileDiffName = new File(['hello'], 'world.txt', { type: 'text/plain', lastModified: 0, From fe24280f91bc028053c720a71242c73d496eab7d Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Tue, 28 Apr 2026 13:55:49 +0200 Subject: [PATCH 15/17] chore: rename --- packages/react-store/tests/index.test.tsx | 24 +-- packages/solid-store/tests/index.test.tsx | 34 +-- .../store/src/{evaluate.ts => compare.ts} | 2 +- packages/store/src/index.ts | 2 +- packages/store/tests/evaluate.spec.ts | 202 +++++++++--------- packages/svelte-store/tests/index.test.ts | 22 +- packages/vue-store/tests/index.test.tsx | 34 +-- 7 files changed, 158 insertions(+), 162 deletions(-) rename packages/store/src/{evaluate.ts => compare.ts} (99%) diff --git a/packages/react-store/tests/index.test.tsx b/packages/react-store/tests/index.test.tsx index 60369d3f..e9bc94a7 100644 --- a/packages/react-store/tests/index.test.tsx +++ b/packages/react-store/tests/index.test.tsx @@ -4,8 +4,8 @@ import { describe, expect, it, test, vi } from 'vitest' import { createAtom, createStore } from '@tanstack/store' import { _useStore, + compare, createStoreContext, - shallow, useAtom, useCreateAtom, useCreateStore, @@ -686,66 +686,66 @@ describe('shallow', () => { test('should return true for shallowly equal objects', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 1, b: 'hello' } - expect(shallow(objA, objB)).toBe(true) + expect(compare(objA, objB)).toBe(true) }) test('should return false for objects with different values', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 2, b: 'world' } - expect(shallow(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return false for objects with different keys', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 1, c: 'world' } // @ts-expect-error - expect(shallow(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return false for objects with different structures', () => { const objA = { a: 1, b: 'hello' } const objB = [1, 'hello'] // @ts-expect-error - expect(shallow(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return false for one object being null', () => { const objA = { a: 1, b: 'hello' } const objB = null - expect(shallow(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return false for one object being undefined', () => { const objA = { a: 1, b: 'hello' } const objB = undefined - expect(shallow(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return true for two null objects', () => { const objA = null const objB = null - expect(shallow(objA, objB)).toBe(true) + expect(compare(objA, objB)).toBe(true) }) test('should return false for objects with different types', () => { const objA = { a: 1, b: 'hello' } const objB = { a: '1', b: 'hello' } // @ts-expect-error - expect(shallow(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return true for shallow equal objects with symbol keys', () => { const sym = Symbol.for('key') const objA = { [sym]: 1 } const objB = { [sym]: 1 } - expect(shallow(objA, objB)).toBe(true) + expect(compare(objA, objB)).toBe(true) }) test('should return false for shallow different values for symbol keys', () => { const sym = Symbol.for('key') const objA = { [sym]: 1 } const objB = { [sym]: 2 } - expect(shallow(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return true for non-enumerable keys', () => { @@ -762,6 +762,6 @@ describe('shallow', () => { value: 2, }) - expect(shallow(objA, objB)).toBe(true) + expect(compare(objA, objB)).toBe(true) }) }) diff --git a/packages/solid-store/tests/index.test.tsx b/packages/solid-store/tests/index.test.tsx index d0f3b7f6..564913d0 100644 --- a/packages/solid-store/tests/index.test.tsx +++ b/packages/solid-store/tests/index.test.tsx @@ -3,8 +3,8 @@ import { render, renderHook } from '@solidjs/testing-library' import { createAtom, createStore } from '@tanstack/store' import { _useStore, + compare, createStoreContext, - evaluate, useAtom, useSelector, useStore, @@ -263,101 +263,101 @@ describe('shallow', () => { test('should return true for shallowly equal objects', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 1, b: 'hello' } - expect(evaluate(objA, objB)).toBe(true) + expect(compare(objA, objB)).toBe(true) }) test('should return false for objects with different values', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 2, b: 'world' } - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return false for objects with different keys', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 1, c: 'world' } // @ts-expect-error - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return false for objects with different structures', () => { const objA = { a: 1, b: 'hello' } const objB = [1, 'hello'] // @ts-expect-error - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return false for one object being null', () => { const objA = { a: 1, b: 'hello' } const objB = null - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return false for one object being undefined', () => { const objA = { a: 1, b: 'hello' } const objB = undefined - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return true for two null objects', () => { const objA = null const objB = null - expect(evaluate(objA, objB)).toBe(true) + expect(compare(objA, objB)).toBe(true) }) test('should return false for objects with different types', () => { const objA = { a: 1, b: 'hello' } const objB = { a: '1', b: 'hello' } // @ts-expect-error - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return true for shallow equal objects with symbol keys', () => { const sym = Symbol.for('key') const objA = { [sym]: 1 } const objB = { [sym]: 1 } - expect(evaluate(objA, objB)).toBe(true) + expect(compare(objA, objB)).toBe(true) }) test('should return false for shallow different values for symbol keys', () => { const sym = Symbol.for('key') const objA = { [sym]: 1 } const objB = { [sym]: 2 } - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return true for shallowly equal maps', () => { const objA = new Map([['1', 'hello']]) const objB = new Map([['1', 'hello']]) - expect(evaluate(objA, objB)).toBe(true) + expect(compare(objA, objB)).toBe(true) }) test('should return false for maps with different values', () => { const objA = new Map([['1', 'hello']]) const objB = new Map([['1', 'world']]) - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return true for shallowly equal sets', () => { const objA = new Set([1]) const objB = new Set([1]) - expect(evaluate(objA, objB)).toBe(true) + expect(compare(objA, objB)).toBe(true) }) test('should return false for sets with different values', () => { const objA = new Set([1]) const objB = new Set([2]) - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return false for dates with different values', () => { const objA = new Date('2025-04-10T14:48:00') const objB = new Date('2025-04-10T14:58:00') - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return true for equal dates', () => { const objA = new Date('2025-02-10') const objB = new Date('2025-02-10') - expect(evaluate(objA, objB)).toBe(true) + expect(compare(objA, objB)).toBe(true) }) }) diff --git a/packages/store/src/evaluate.ts b/packages/store/src/compare.ts similarity index 99% rename from packages/store/src/evaluate.ts rename to packages/store/src/compare.ts index 2843312e..fe959108 100644 --- a/packages/store/src/evaluate.ts +++ b/packages/store/src/compare.ts @@ -1,4 +1,4 @@ -export function evaluate( +export function compare( objA: T, objB: T, config: { mode: 'shallow' | 'deep' } = { mode: 'shallow' }, diff --git a/packages/store/src/index.ts b/packages/store/src/index.ts index b1ecad9a..80123c95 100644 --- a/packages/store/src/index.ts +++ b/packages/store/src/index.ts @@ -2,4 +2,4 @@ export * from './types' export * from './atom' export * from './store' export * from './shallow' -export * from './evaluate' +export * from './compare' diff --git a/packages/store/tests/evaluate.spec.ts b/packages/store/tests/evaluate.spec.ts index 227c632f..22392d9f 100644 --- a/packages/store/tests/evaluate.spec.ts +++ b/packages/store/tests/evaluate.spec.ts @@ -1,31 +1,31 @@ import { describe, expect, it, vi } from 'vitest' -import { evaluate } from '../src/evaluate' +import { compare } from '../src/compare' describe('evaluate', () => { it('should test equality between primitives', () => { - const numbersTrue = evaluate(1, 1) + const numbersTrue = compare(1, 1) expect(numbersTrue).toEqual(true) - const stringFalse = evaluate('uh oh', '') + const stringFalse = compare('uh oh', '') expect(stringFalse).toEqual(false) - const boolTrue = evaluate(true, true) + const boolTrue = compare(true, true) expect(boolTrue).toEqual(true) - const nullFalse = evaluate(null, {}) + const nullFalse = compare(null, {}) expect(nullFalse).toEqual(false) - const undefinedFalse = evaluate(undefined, null) + const undefinedFalse = compare(undefined, null) expect(undefinedFalse).toEqual(false) }) it('should return false for runtime cross-type comparisons', () => { - expect(evaluate(new Date(), new Map())).toEqual(false) - expect(evaluate(new Date(), new Set())).toEqual(false) - expect(evaluate(new Map(), new Set())).toEqual(false) - expect(evaluate(new Date(), {})).toEqual(false) - expect(evaluate(new Map(), {})).toEqual(false) - expect(evaluate(new Set(), {})).toEqual(false) + expect(compare(new Date(), new Map())).toEqual(false) + expect(compare(new Date(), new Set())).toEqual(false) + expect(compare(new Map(), new Set())).toEqual(false) + expect(compare(new Date(), {})).toEqual(false) + expect(compare(new Map(), {})).toEqual(false) + expect(compare(new Set(), {})).toEqual(false) }) it('should not throw a runtime error when File is undefined in the environment', () => { @@ -45,7 +45,7 @@ describe('evaluate', () => { } try { - expect(() => evaluate(file1, file2)).not.toThrow() + expect(() => compare(file1, file2)).not.toThrow() } finally { vi.unstubAllGlobals() } @@ -53,21 +53,21 @@ describe('evaluate', () => { describe('shallow', () => { it('should test equality between arrays', () => { - expect(evaluate([], [])).toEqual(true) + expect(compare([], [])).toEqual(true) - const arrayFalse = evaluate([], ['']) + const arrayFalse = compare([], ['']) expect(arrayFalse).toEqual(false) - const arrayDeepFalse = evaluate([[1]], []) + const arrayDeepFalse = compare([[1]], []) expect(arrayDeepFalse).toEqual(false) - const arrayNestedFalse = evaluate([[1]], [[1]]) + const arrayNestedFalse = compare([[1]], [[1]]) expect(arrayNestedFalse).toEqual(false) - const arrayComplexFalse = evaluate([[{ test: 'true' }], null], [[1], {}]) + const arrayComplexFalse = compare([[{ test: 'true' }], null], [[1], {}]) expect(arrayComplexFalse).toEqual(false) - const arrayComplexFalse2 = evaluate( + const arrayComplexFalse2 = compare( [[{ test: 'true' }], null], [[{ test: 'true' }], null], ) @@ -75,28 +75,28 @@ describe('evaluate', () => { }) it('should test equality between objects', () => { - const objTrue = evaluate({ test: 'same' }, { test: 'same' }) + const objTrue = compare({ test: 'same' }, { test: 'same' }) expect(objTrue).toEqual(true) - const objFalse = evaluate({ test: 'not' }, { test: 'same' }) + const objFalse = compare({ test: 'not' }, { test: 'same' }) expect(objFalse).toEqual(false) - const objDeepFalse = evaluate({ test: 'not' }, { test: { test: 'same' } }) + const objDeepFalse = compare({ test: 'not' }, { test: { test: 'same' } }) expect(objDeepFalse).toEqual(false) - const objDeepArrFalse = evaluate({ test: [] }, { test: [[]] }) + const objDeepArrFalse = compare({ test: [] }, { test: [[]] }) expect(objDeepArrFalse).toEqual(false) - const objNullFalse = evaluate({ test: '' }, null) + const objNullFalse = compare({ test: '' }, null) expect(objNullFalse).toEqual(false) - const objComplexFalse = evaluate( + const objComplexFalse = compare( { test: { testTwo: '' }, arr: [[1]] }, { test: { testTwo: false }, arr: [[1], [0]] }, ) expect(objComplexFalse).toEqual(false) - const objComplexShallowFalse = evaluate( + const objComplexShallowFalse = compare( { test: { testTwo: '' }, arr: [[1]] }, { test: { testTwo: '' }, arr: [[1]] }, ) @@ -108,19 +108,19 @@ describe('evaluate', () => { const date2 = new Date('2025-01-01T00:00:00.000Z') const date3 = new Date('2025-01-02T00:00:00.000Z') - expect(evaluate(date1, date2)).toEqual(true) - expect(evaluate(date1, date3)).toEqual(false) + expect(compare(date1, date2)).toEqual(true) + expect(compare(date1, date3)).toEqual(false) - const dateObjectShallowFalse = evaluate({ date: date1 }, { date: date2 }) + const dateObjectShallowFalse = compare({ date: date1 }, { date: date2 }) expect(dateObjectShallowFalse).toEqual(false) - const dateObjectFalse = evaluate({ date: date1 }, { date: date3 }) + const dateObjectFalse = compare({ date: date1 }, { date: date3 }) expect(dateObjectFalse).toEqual(false) }) it('should test equality between Map objects', () => { expect( - evaluate( + compare( new Map([ ['a', 1], ['b', 2], @@ -131,9 +131,9 @@ describe('evaluate', () => { ]), ), ).toEqual(true) - expect(evaluate(new Map(), new Map())).toEqual(true) + expect(compare(new Map(), new Map())).toEqual(true) expect( - evaluate( + compare( new Map([['a', 1]]), new Map([ ['a', 1], @@ -142,7 +142,7 @@ describe('evaluate', () => { ), ).toEqual(false) expect( - evaluate( + compare( new Map([ ['a', 1], ['b', 2], @@ -154,7 +154,7 @@ describe('evaluate', () => { ), ).toEqual(false) expect( - evaluate( + compare( new Map([ ['a', 1], ['b', 2], @@ -167,23 +167,23 @@ describe('evaluate', () => { ).toEqual(false) const obj = { x: 1 } - expect(evaluate(new Map([['a', obj]]), new Map([['a', obj]]))).toEqual( + expect(compare(new Map([['a', obj]]), new Map([['a', obj]]))).toEqual( true, ) expect( - evaluate(new Map([['a', { x: 1 }]]), new Map([['a', { x: 1 }]])), + compare(new Map([['a', { x: 1 }]]), new Map([['a', { x: 1 }]])), ).toEqual(false) }) it('should test equality between Set objects', () => { - expect(evaluate(new Set([1, 2, 3]), new Set([1, 2, 3]))).toEqual(true) - expect(evaluate(new Set(), new Set())).toEqual(true) - expect(evaluate(new Set([1, 2]), new Set([1, 2, 3]))).toEqual(false) - expect(evaluate(new Set([1, 2, 3]), new Set([1, 2, 4]))).toEqual(false) + expect(compare(new Set([1, 2, 3]), new Set([1, 2, 3]))).toEqual(true) + expect(compare(new Set(), new Set())).toEqual(true) + expect(compare(new Set([1, 2]), new Set([1, 2, 3]))).toEqual(false) + expect(compare(new Set([1, 2, 3]), new Set([1, 2, 4]))).toEqual(false) const obj = { x: 1 } - expect(evaluate(new Set([obj]), new Set([obj]))).toEqual(true) - expect(evaluate(new Set([{ x: 1 }]), new Set([{ x: 1 }]))).toEqual(false) + expect(compare(new Set([obj]), new Set([obj]))).toEqual(true) + expect(compare(new Set([{ x: 1 }]), new Set([{ x: 1 }]))).toEqual(false) }) it('should test equality between File objects', () => { @@ -208,52 +208,48 @@ describe('evaluate', () => { lastModified: 0, }) - expect(evaluate(file1, file2)).toEqual(true) - expect(evaluate(file1, fileDiffName)).toEqual(false) - expect(evaluate(file1, fileDiffType)).toEqual(false) - expect(evaluate(file1, fileDiffSize)).toEqual(false) + expect(compare(file1, file2)).toEqual(true) + expect(compare(file1, fileDiffName)).toEqual(false) + expect(compare(file1, fileDiffType)).toEqual(false) + expect(compare(file1, fileDiffSize)).toEqual(false) - expect(evaluate({ file: file1 }, { file: file2 })).toEqual(false) - expect(evaluate({ file: file1 }, { file: fileDiffName })).toEqual(false) + expect(compare({ file: file1 }, { file: file2 })).toEqual(false) + expect(compare({ file: file1 }, { file: fileDiffName })).toEqual(false) }) it('should test equality between objects with Symbol keys', () => { const sym = Symbol('id') expect( - evaluate({ [sym]: 1, name: 'foo' }, { [sym]: 1, name: 'foo' }), + compare({ [sym]: 1, name: 'foo' }, { [sym]: 1, name: 'foo' }), ).toEqual(true) expect( - evaluate({ [sym]: 1, name: 'foo' }, { [sym]: 2, name: 'foo' }), + compare({ [sym]: 1, name: 'foo' }, { [sym]: 2, name: 'foo' }), ).toEqual(false) - expect(evaluate({ [sym]: 1 } as any, {} as any)).toEqual(false) - expect(evaluate({} as any, { [sym]: 1 } as any)).toEqual(false) + expect(compare({ [sym]: 1 } as any, {} as any)).toEqual(false) + expect(compare({} as any, { [sym]: 1 } as any)).toEqual(false) }) }) describe('deep', () => { it('should test equality between arrays', () => { - expect(evaluate([], [], { mode: 'deep' })).toEqual(true) + expect(compare([], [], { mode: 'deep' })).toEqual(true) - const arrayFalse = evaluate([], [''], { mode: 'deep' }) + const arrayFalse = compare([], [''], { mode: 'deep' }) expect(arrayFalse).toEqual(false) - const arrayDeepFalse = evaluate([[1]], [], { mode: 'deep' }) + const arrayDeepFalse = compare([[1]], [], { mode: 'deep' }) expect(arrayDeepFalse).toEqual(false) - const arrayDeepSearchTrue = evaluate([[1]], [[1]], { mode: 'deep' }) + const arrayDeepSearchTrue = compare([[1]], [[1]], { mode: 'deep' }) expect(arrayDeepSearchTrue).toEqual(true) - const arrayComplexFalse = evaluate( - [[{ test: 'true' }], null], - [[1], {}], - { - mode: 'deep', - }, - ) + const arrayComplexFalse = compare([[{ test: 'true' }], null], [[1], {}], { + mode: 'deep', + }) expect(arrayComplexFalse).toEqual(false) - const arrayComplexTrue = evaluate( + const arrayComplexTrue = compare( [[{ test: 'true' }], null], [[{ test: 'true' }], null], { mode: 'deep' }, @@ -262,45 +258,45 @@ describe('evaluate', () => { }) it('should test equality between objects', () => { - const objTrue = evaluate( + const objTrue = compare( { test: 'same' }, { test: 'same' }, { mode: 'deep' }, ) expect(objTrue).toEqual(true) - const objFalse = evaluate( + const objFalse = compare( { test: 'not' }, { test: 'same' }, { mode: 'deep' }, ) expect(objFalse).toEqual(false) - const objDeepFalse = evaluate( + const objDeepFalse = compare( { test: 'not' }, { test: { test: 'same' } }, { mode: 'deep' }, ) expect(objDeepFalse).toEqual(false) - const objDeepArrFalse = evaluate( + const objDeepArrFalse = compare( { test: [] }, { test: [[]] }, { mode: 'deep' }, ) expect(objDeepArrFalse).toEqual(false) - const objNullFalse = evaluate({ test: '' }, null, { mode: 'deep' }) + const objNullFalse = compare({ test: '' }, null, { mode: 'deep' }) expect(objNullFalse).toEqual(false) - const objComplexFalse = evaluate( + const objComplexFalse = compare( { test: { testTwo: '' }, arr: [[1]] }, { test: { testTwo: false }, arr: [[1], [0]] }, { mode: 'deep' }, ) expect(objComplexFalse).toEqual(false) - const objComplexTrue = evaluate( + const objComplexTrue = compare( { test: { testTwo: '' }, arr: [[1]] }, { test: { testTwo: '' }, arr: [[1]] }, { mode: 'deep' }, @@ -313,17 +309,17 @@ describe('evaluate', () => { const date2 = new Date('2025-01-01T00:00:00.000Z') const date3 = new Date('2025-01-02T00:00:00.000Z') - expect(evaluate(date1, date2, { mode: 'deep' })).toEqual(true) - expect(evaluate(date1, date3, { mode: 'deep' })).toEqual(false) + expect(compare(date1, date2, { mode: 'deep' })).toEqual(true) + expect(compare(date1, date3, { mode: 'deep' })).toEqual(false) - const dateObjectTrue = evaluate( + const dateObjectTrue = compare( { date: date1 }, { date: date2 }, { mode: 'deep' }, ) expect(dateObjectTrue).toEqual(true) - const dateObjectFalse = evaluate( + const dateObjectFalse = compare( { date: date1 }, { date: date3 }, { mode: 'deep' }, @@ -333,7 +329,7 @@ describe('evaluate', () => { it('should test equality between Map objects', () => { expect( - evaluate( + compare( new Map([ ['a', 1], ['b', 2], @@ -345,9 +341,9 @@ describe('evaluate', () => { { mode: 'deep' }, ), ).toEqual(true) - expect(evaluate(new Map(), new Map(), { mode: 'deep' })).toEqual(true) + expect(compare(new Map(), new Map(), { mode: 'deep' })).toEqual(true) expect( - evaluate( + compare( new Map([['a', 1]]), new Map([ ['a', 1], @@ -357,7 +353,7 @@ describe('evaluate', () => { ), ).toEqual(false) expect( - evaluate( + compare( new Map([ ['a', 1], ['b', 2], @@ -371,17 +367,17 @@ describe('evaluate', () => { ).toEqual(false) expect( - evaluate(new Map([['a', { x: 1 }]]), new Map([['a', { x: 1 }]]), { + compare(new Map([['a', { x: 1 }]]), new Map([['a', { x: 1 }]]), { mode: 'deep', }), ).toEqual(true) expect( - evaluate(new Map([['a', { x: 1 }]]), new Map([['a', { x: 2 }]]), { + compare(new Map([['a', { x: 1 }]]), new Map([['a', { x: 2 }]]), { mode: 'deep', }), ).toEqual(false) expect( - evaluate( + compare( new Map([['a', { nested: { x: 1 } }]]), new Map([['a', { nested: { x: 1 } }]]), { mode: 'deep' }, @@ -391,24 +387,24 @@ describe('evaluate', () => { it('should test equality between Set objects', () => { expect( - evaluate(new Set([1, 2, 3]), new Set([1, 2, 3]), { mode: 'deep' }), + compare(new Set([1, 2, 3]), new Set([1, 2, 3]), { mode: 'deep' }), ).toEqual(true) - expect(evaluate(new Set(), new Set(), { mode: 'deep' })).toEqual(true) + expect(compare(new Set(), new Set(), { mode: 'deep' })).toEqual(true) expect( - evaluate(new Set([1, 2]), new Set([1, 2, 3]), { mode: 'deep' }), + compare(new Set([1, 2]), new Set([1, 2, 3]), { mode: 'deep' }), ).toEqual(false) expect( - evaluate(new Set([1, 2, 3]), new Set([1, 2, 4]), { mode: 'deep' }), + compare(new Set([1, 2, 3]), new Set([1, 2, 4]), { mode: 'deep' }), ).toEqual(false) expect( - evaluate(new Set([{ x: 1 }]), new Set([{ x: 1 }]), { mode: 'deep' }), + compare(new Set([{ x: 1 }]), new Set([{ x: 1 }]), { mode: 'deep' }), ).toEqual(true) expect( - evaluate(new Set([{ x: 1 }]), new Set([{ x: 2 }]), { mode: 'deep' }), + compare(new Set([{ x: 1 }]), new Set([{ x: 2 }]), { mode: 'deep' }), ).toEqual(false) expect( - evaluate( + compare( new Set([{ nested: { x: 1 } }]), new Set([{ nested: { x: 1 } }]), { mode: 'deep' }, @@ -438,16 +434,16 @@ describe('evaluate', () => { lastModified: 0, }) - expect(evaluate(file1, file2, { mode: 'deep' })).toEqual(true) - expect(evaluate(file1, fileDiffName, { mode: 'deep' })).toEqual(false) - expect(evaluate(file1, fileDiffType, { mode: 'deep' })).toEqual(false) - expect(evaluate(file1, fileDiffSize, { mode: 'deep' })).toEqual(false) + expect(compare(file1, file2, { mode: 'deep' })).toEqual(true) + expect(compare(file1, fileDiffName, { mode: 'deep' })).toEqual(false) + expect(compare(file1, fileDiffType, { mode: 'deep' })).toEqual(false) + expect(compare(file1, fileDiffSize, { mode: 'deep' })).toEqual(false) expect( - evaluate({ file: file1 }, { file: file2 }, { mode: 'deep' }), + compare({ file: file1 }, { file: file2 }, { mode: 'deep' }), ).toEqual(true) expect( - evaluate({ file: file1 }, { file: fileDiffName }, { mode: 'deep' }), + compare({ file: file1 }, { file: fileDiffName }, { mode: 'deep' }), ).toEqual(false) }) @@ -455,28 +451,28 @@ describe('evaluate', () => { const sym = Symbol('id') expect( - evaluate( + compare( { [sym]: 1, name: 'foo' }, { [sym]: 1, name: 'foo' }, { mode: 'deep' }, ), ).toEqual(true) expect( - evaluate( + compare( { [sym]: 1, name: 'foo' }, { [sym]: 2, name: 'foo' }, { mode: 'deep' }, ), ).toEqual(false) expect( - evaluate( + compare( { [sym]: { nested: true } } as any, { [sym]: { nested: true } } as any, { mode: 'deep' }, ), ).toEqual(true) expect( - evaluate( + compare( { [sym]: { nested: true } } as any, { [sym]: { nested: false } } as any, { mode: 'deep' }, @@ -491,12 +487,12 @@ describe('evaluate', () => { const b: any = { x: 1 } b.self = b - expect(() => evaluate(a, b, { mode: 'deep' })).not.toThrow() - expect(evaluate(a, b, { mode: 'deep' })).toEqual(true) + expect(() => compare(a, b, { mode: 'deep' })).not.toThrow() + expect(compare(a, b, { mode: 'deep' })).toEqual(true) const c: any = { x: 2 } c.self = c - expect(evaluate(a, c, { mode: 'deep' })).toEqual(false) + expect(compare(a, c, { mode: 'deep' })).toEqual(false) }) }) }) diff --git a/packages/svelte-store/tests/index.test.ts b/packages/svelte-store/tests/index.test.ts index 65a38ba0..ede62c00 100644 --- a/packages/svelte-store/tests/index.test.ts +++ b/packages/svelte-store/tests/index.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it, test } from 'vitest' import { render, waitFor } from '@testing-library/svelte' import { userEvent } from '@testing-library/user-event' -import { evaluate } from '../src/index.svelte.js' +import { compare } from '@tanstack/store' import TestBaseStore from './BaseStore.test.svelte' import TestRerender from './Render.test.svelte' import TestValue from './Value.test.svelte' @@ -44,63 +44,63 @@ describe('shallow', () => { test('should return true for shallowly equal objects', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 1, b: 'hello' } - expect(evaluate(objA, objB)).toBe(true) + expect(compare(objA, objB)).toBe(true) }) test('should return false for objects with different values', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 2, b: 'world' } - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return false for objects with different keys', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 1, c: 'world' } // @ts-expect-error testing invalid input - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return false for objects with different structures', () => { const objA = { a: 1, b: 'hello' } const objB = [1, 'hello'] // @ts-expect-error testing invalid input - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return false for one object being null', () => { const objA = { a: 1, b: 'hello' } const objB = null - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return false for one object being undefined', () => { const objA = { a: 1, b: 'hello' } const objB = undefined - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return true for two null objects', () => { const objA = null const objB = null - expect(evaluate(objA, objB)).toBe(true) + expect(compare(objA, objB)).toBe(true) }) test('should return false for objects with different types', () => { const objA = { a: 1, b: 'hello' } const objB = { a: '1', b: 'hello' } // @ts-expect-error testing invalid input - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return false for dates with different values', () => { const objA = new Date('2025-04-10T14:48:00') const objB = new Date('2025-04-10T14:58:00') - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return true for equal dates', () => { const objA = new Date('2025-02-10') const objB = new Date('2025-02-10') - expect(evaluate(objA, objB)).toBe(true) + expect(compare(objA, objB)).toBe(true) }) }) diff --git a/packages/vue-store/tests/index.test.tsx b/packages/vue-store/tests/index.test.tsx index 1009c277..000d6871 100644 --- a/packages/vue-store/tests/index.test.tsx +++ b/packages/vue-store/tests/index.test.tsx @@ -5,7 +5,7 @@ import { createAtom, createStore } from '@tanstack/store' import { userEvent } from '@testing-library/user-event' import { _useStore, - evaluate, + compare, useAtom, useSelector, useStore, @@ -378,101 +378,101 @@ describe('shallow', () => { test('should return true for shallowly equal objects', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 1, b: 'hello' } - expect(evaluate(objA, objB)).toBe(true) + expect(compare(objA, objB)).toBe(true) }) test('should return false for objects with different values', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 2, b: 'world' } - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return false for objects with different keys', () => { const objA = { a: 1, b: 'hello' } const objB = { a: 1, c: 'world' } // @ts-expect-error - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return false for objects with different structures', () => { const objA = { a: 1, b: 'hello' } const objB = [1, 'hello'] // @ts-expect-error - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return false for one object being null', () => { const objA = { a: 1, b: 'hello' } const objB = null - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return false for one object being undefined', () => { const objA = { a: 1, b: 'hello' } const objB = undefined - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return true for two null objects', () => { const objA = null const objB = null - expect(evaluate(objA, objB)).toBe(true) + expect(compare(objA, objB)).toBe(true) }) test('should return false for objects with different types', () => { const objA = { a: 1, b: 'hello' } const objB = { a: '1', b: 'hello' } // @ts-expect-error - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return true for shallow equal objects with symbol keys', () => { const sym = Symbol.for('key') const objA = { [sym]: 1 } const objB = { [sym]: 1 } - expect(evaluate(objA, objB)).toBe(true) + expect(compare(objA, objB)).toBe(true) }) test('should return false for shallow different values for symbol keys', () => { const sym = Symbol.for('key') const objA = { [sym]: 1 } const objB = { [sym]: 2 } - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return true for shallowly equal maps', () => { const objA = new Map([['1', 'hello']]) const objB = new Map([['1', 'hello']]) - expect(evaluate(objA, objB)).toBe(true) + expect(compare(objA, objB)).toBe(true) }) test('should return false for maps with different values', () => { const objA = new Map([['1', 'hello']]) const objB = new Map([['1', 'world']]) - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return true for shallowly equal sets', () => { const objA = new Set([1]) const objB = new Set([1]) - expect(evaluate(objA, objB)).toBe(true) + expect(compare(objA, objB)).toBe(true) }) test('should return false for sets with different values', () => { const objA = new Set([1]) const objB = new Set([2]) - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return false for dates with different values', () => { const objA = new Date('2025-04-10T14:48:00') const objB = new Date('2025-04-10T14:58:00') - expect(evaluate(objA, objB)).toBe(false) + expect(compare(objA, objB)).toBe(false) }) test('should return true for equal dates', () => { const objA = new Date('2025-02-10') const objB = new Date('2025-02-10') - expect(evaluate(objA, objB)).toBe(true) + expect(compare(objA, objB)).toBe(true) }) }) From 913bf8ea930cae33263d4aaafb293599c1197ab3 Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Tue, 28 Apr 2026 13:56:18 +0200 Subject: [PATCH 16/17] fix: subclass instances --- packages/store/src/compare.ts | 10 ++++---- packages/store/tests/evaluate.spec.ts | 35 +++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/packages/store/src/compare.ts b/packages/store/src/compare.ts index fe959108..b4825c68 100644 --- a/packages/store/src/compare.ts +++ b/packages/store/src/compare.ts @@ -39,6 +39,11 @@ function _evaluate( seenB.add(objB as object) } + // guards against runtime cross type evaluation + if (Object.getPrototypeOf(objA) !== Object.getPrototypeOf(objB)) { + return false + } + if (objA instanceof Date && objB instanceof Date) { return objA.getTime() === objB.getTime() } @@ -94,11 +99,6 @@ function _evaluate( return true } - // guards against runtime cross type evaluation - if (Object.getPrototypeOf(objA) !== Object.getPrototypeOf(objB)) { - return false - } - const keysA = getOwnKeys(objA as object) const keysB = getOwnKeys(objB as object) diff --git a/packages/store/tests/evaluate.spec.ts b/packages/store/tests/evaluate.spec.ts index 22392d9f..d8a9eae2 100644 --- a/packages/store/tests/evaluate.spec.ts +++ b/packages/store/tests/evaluate.spec.ts @@ -28,6 +28,41 @@ describe('evaluate', () => { expect(compare(new Set(), {})).toEqual(false) }) + it('should return false when comparing a subclass instance to a base class instance', () => { + class ExtendedMap extends Map {} + class ExtendedSet extends Set {} + class ExtendedDate extends Date {} + + // subclass vs base class, different prototypes, never equal + expect(compare(new ExtendedMap(), new Map())).toEqual(false) + expect(compare(new Map(), new ExtendedMap())).toEqual(false) + expect(compare(new ExtendedSet(), new Set())).toEqual(false) + expect(compare(new Set(), new ExtendedSet())).toEqual(false) + expect(compare(new ExtendedDate(), new Date())).toEqual(false) + expect(compare(new Date(), new ExtendedDate())).toEqual(false) + + // two instances of the same subclass with equal contents are equal + expect(compare(new ExtendedMap(), new ExtendedMap())).toEqual(true) + expect(compare(new ExtendedSet(), new ExtendedSet())).toEqual(true) + + // same checks hold in deep mode + expect( + compare(new ExtendedMap(), new Map(), { mode: 'deep' }), + ).toEqual(false) + expect( + compare(new ExtendedSet(), new Set(), { mode: 'deep' }), + ).toEqual(false) + expect( + compare(new ExtendedDate(), new Date(), { mode: 'deep' }), + ).toEqual(false) + expect( + compare(new ExtendedMap(), new ExtendedMap(), { mode: 'deep' }), + ).toEqual(true) + expect( + compare(new ExtendedSet(), new ExtendedSet(), { mode: 'deep' }), + ).toEqual(true) + }) + it('should not throw a runtime error when File is undefined in the environment', () => { vi.stubGlobal('File', undefined) From 71b2aa0a990061967b5f739d711a28c3b4b13daa Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 11:58:20 +0000 Subject: [PATCH 17/17] ci: apply automated fixes and generate docs --- docs/reference/functions/{evaluate.md => compare.md} | 10 +++++----- docs/reference/index.md | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) rename docs/reference/functions/{evaluate.md => compare.md} (53%) diff --git a/docs/reference/functions/evaluate.md b/docs/reference/functions/compare.md similarity index 53% rename from docs/reference/functions/evaluate.md rename to docs/reference/functions/compare.md index 3766a2ae..6d91cd43 100644 --- a/docs/reference/functions/evaluate.md +++ b/docs/reference/functions/compare.md @@ -1,18 +1,18 @@ --- -id: evaluate -title: evaluate +id: compare +title: compare --- -# Function: evaluate() +# Function: compare() ```ts -function evaluate( +function compare( objA, objB, config): boolean; ``` -Defined in: [evaluate.ts:1](https://github.com/TanStack/store/blob/main/packages/store/src/evaluate.ts#L1) +Defined in: [compare.ts:1](https://github.com/TanStack/store/blob/main/packages/store/src/compare.ts#L1) ## Type Parameters diff --git a/docs/reference/index.md b/docs/reference/index.md index 5b6d355f..baae1f07 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -35,10 +35,10 @@ title: "@tanstack/store" ## Functions - [batch](functions/batch.md) +- [compare](functions/compare.md) - [createAsyncAtom](functions/createAsyncAtom.md) - [createAtom](functions/createAtom.md) - [createStore](functions/createStore.md) -- [evaluate](functions/evaluate.md) - [flush](functions/flush.md) - [shallow](functions/shallow.md) - [toObserver](functions/toObserver.md)