Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/remove-tanstack-query.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@effect-app/vue": minor
"@effect-app/e2e": patch
---

Remove the TanStack query engine and its patch, leaving the Atom engine as the only query backend.

- Drop `@tanstack/vue-query` / `@tanstack/query-core` dependencies and the `patches/@tanstack__query-core.patch`.
- Delete `packages/vue/src/internal/tanstackQuery.ts` (`makeTanstackQuery`, `makeTanstackQueryClient`, `makeTanstackQueryInvalidator`, `makeTanstackQueryCacheUpdater`).
- `makeClient` no longer accepts `legacyQueryEngine` (always Atom) and no longer returns `tanstackQueryClient`. The `MakeClientOptions` interface is removed.
- `QueryImpl` no longer takes a `legacyUseQuery` override; `.query()` / `.suspense()` now always run on the Atom engine alongside `.atom()` / `.family()` / `.queryNew()` / `.suspenseNew()`.
- Tests that exercised the tanstack path are dropped or converted to the Atom path (query-span, suspense-regression structural sharing, dependency-invalidation matrix, e2e repo invalidation).
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
],
"patchedDependencies": {
"ts-plugin-sort-import-suggestions": "patches/ts-plugin-sort-import-suggestions.patch",
"@tanstack/query-core": "patches/@tanstack__query-core.patch",
"typescript@6.0.3": "patches/typescript.patch",
"@sendgrid/mail": "patches/@sendgrid__mail.patch"
}
Expand Down
1 change: 0 additions & 1 deletion packages/e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
"@effect/atom-vue": "^4.0.0-beta.90",
"@effect/platform-node": "4.0.0-beta.90",
"@effect/vitest": "4.0.0-beta.90",
"@tanstack/vue-query": "5.96.2",
"@types/node": "25.9.1",
"@vitejs/plugin-vue": "^6.0.7",
"effect": "^4.0.0-beta.90",
Expand Down
58 changes: 0 additions & 58 deletions packages/e2e/test/repoInvalidation.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { invalidateQueries } from "@effect-app/vue/mutate"
import { defaultRegistry } from "@effect/atom-vue"
import { NodeHttpServer } from "@effect/platform-node"
import { expect, it } from "@effect/vitest"
import { QueryClient, VueQueryPlugin } from "@tanstack/vue-query"
import { ApiClientFactory, InvalidStateError, makeRpcClient, NotLoggedInError, OptimisticConcurrencyException } from "effect-app/client"
import * as Context from "effect-app/Context"
import * as Effect from "effect-app/Effect"
Expand All @@ -22,12 +21,10 @@ import { FetchHttpClient } from "effect/unstable/http"
import * as Reactivity from "effect/unstable/reactivity/Reactivity"
import { RpcSerialization } from "effect/unstable/rpc"
import { createServer } from "http"
import { createApp, effectScope, ref } from "vue"
import { RequestContextMiddleware } from "../../infra/src/internal/RequestContextMiddleware.ts"
import { makeRouter } from "../../infra/src/routing.ts"
import { DefaultGenericMiddlewaresLive } from "../../infra/src/routing/middleware.ts"
import { MemoryStoreLive } from "../../infra/src/Store/Memory.ts"
import { makeTanstackQuery, makeTanstackQueryInvalidator } from "../../vue/src/internal/tanstackQuery.ts"

class RequestContextMap extends RpcContextMap.makeMap({
allowAnonymous: RpcContextMap.makeInverted()(NotLoggedInError)
Expand Down Expand Up @@ -210,58 +207,3 @@ it("atom engine: rpc repo write invalidates and refetches; unrelated rpc write d
await runtime.dispose()
}
}, 10_000)

it("tanstack engine: rpc repo write invalidates and refetches; unrelated rpc write does not", async () => {
const [runtime, client, context] = await setup()
const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false, gcTime: Infinity, staleTime: 0 } }
})
const useQuery = makeTanstackQuery(() => context, queryClient)
const invalidator = makeTanstackQueryInvalidator(queryClient)
const app = createApp({ render: () => null })
app.use(VueQueryPlugin, { queryClient })
const scope = effectScope(true)
let handle: any
let data: any
const run = <A, E>(effect: Effect.Effect<A, E, never>) => Effect.runPromise(effect)
const save = (id: string) =>
run(
invalidateQueries(client.SaveRepoItem, undefined, invalidator)(client.SaveRepoItem.handler({ id, label: id }), {
id,
label: id
})
.pipe(Effect.provide(context)) as any
)
const saveOther = (id: string) =>
run(
invalidateQueries(client.SaveOtherItem, undefined, invalidator)(client.SaveOtherItem.handler({ id, label: id }), {
id,
label: id
})
.pipe(Effect.provide(context)) as any
)

app.runWithContext(() =>
scope.run(() => {
const tuple = useQuery(client.GetRepoCount as any)(ref(undefined) as any, {} as any) as any
data = tuple[1]
handle = tuple[3]
})
)

try {
expect(await run(handle.refetch())).toBe(0)

await save("1")
expect(data.value).toBe(1)

const before = await run(client.GetRepoCountRuns.handler().pipe(Effect.provide(context)) as any)
await saveOther("x")
const after = await run(client.GetRepoCountRuns.handler().pipe(Effect.provide(context)) as any)
expect(after).toBe(before)
} finally {
scope.stop()
queryClient.clear()
await runtime.dispose()
}
}, 10_000)
6 changes: 3 additions & 3 deletions packages/vue/docs/query-key-invalidation.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ Both sides route through `keysToHashes`, which hashes **each element** of the ke

- Registration: `withReactivity(reactivityKeys)` → `registerUnsafe` — `reactivityKeys` is a
list of prefix-arrays, each hashed via `Hash.hash`.
- Invalidation: mutations pass `ReadonlyArray<ReadonlyArray<unknown>>` — a *list of
key-arrays* (`buildInvalidateCache` in [`src/mutate.ts`](../src/mutate.ts)) — so each
- Invalidation: mutations pass `ReadonlyArray<ReadonlyArray<unknown>>` — a _list of
key-arrays_ (`buildInvalidateCache` in [`src/mutate.ts`](../src/mutate.ts)) — so each
element is a full namespaced array, matching the registration shape.

The await-tracking map (`keyAtoms` in `atomQuery.ts`) keys on the same `Hash.hash(key)`, so
Expand All @@ -89,7 +89,7 @@ The await-tracking map (`keyAtoms` in `atomQuery.ts`) keys on the same `Hash.has
the one real semantic divergence from TanStack's exact compare. Same applies to
`keyAtoms` and `uniqueKeys`.
2. **Input is one opaque trailing element** (`[...baseKey, input]`). You can invalidate at
any namespace granularity but not *within* an input (no "all inputs where
any namespace granularity but not _within_ an input (no "all inputs where
status=active"). TanStack with the same key shape behaves identically — parity, just a
granularity ceiling.
3. **O(depth) registrations per live query** (depth+1 prefixes, in both the reactivity
Expand Down
1 change: 0 additions & 1 deletion packages/vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
"homepage": "https://github.com/effect-ts-app/libs/tree/main/packages/vue",
"dependencies": {
"@formatjs/intl": "^4.1.12",
"@tanstack/vue-query": "5.96.2",
"@vueuse/core": "^14.3.0",
"change-case": "^5.4.4",
"effect-app": "workspace:*",
Expand Down
Loading
Loading