// @vitest-environment jsdom import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import type { ReactNode } from "react"; import type { MutationIntentFactory } from "../../src/application/ports/mutation-intent-factory.ts"; import { QueryInvalidationProvider } from "../../src/presentation/adapters/query/query-invalidation-provider.tsx"; import { MutationIntentProvider } from "../../src/presentation/adapters/query/mutation-intent-provider.tsx"; import { defineQueryInvalidationTopic, type QueryInvalidationCoordinator, } from "../../src/contracts/query-invalidation.ts"; import { createRuntimeIdentityRegistry } from "../../src/contracts/query-keys.ts"; import type { QueryResultMeasure } from "../../src/contracts/server-state.ts"; import type { CacheScopeSnapshot } from "../../src/contracts/server-state-scope.ts"; export const RESOURCE_INVALIDATION_TOPIC = defineQueryInvalidationTopic("resource"); export function scopeSnapshot( generation = 1, fingerprint = "scope-fingerprint-0001", ): CacheScopeSnapshot & { fence(): void } { let current = true; const lifetime = new AbortController(); const identities = createRuntimeIdentityRegistry({ tokenFactory: () => "scope-identity-token-0001", }); return { generation, fingerprint, identities, signal: lifetime.signal, isCurrent: () => current, fence() { current = false; lifetime.abort(); }, }; } export function measureOne(): QueryResultMeasure { return { itemCount: 1, estimatedBytes: 8 }; } export function queryClient() { return new QueryClient({ defaultOptions: { queries: { retry: false, staleTime: 0, gcTime: Infinity }, mutations: { retry: false }, }, }); } export function deterministicMutationIntentFactory(): MutationIntentFactory { let sequence = 0; return Object.freeze({ create(input) { sequence += 1; return Object.freeze({ intentId: `intent-${sequence}`, operationId: input.operationId, canonicalInputIdentity: input.canonicalInputIdentity, ...(input.requiresIdempotencyKey ? { idempotencyKey: `key-${sequence}` } : {}), createdAtMonotonicMs: sequence, }); }, }); } export function wrapper( client: QueryClient, mutationIntentFactory = deterministicMutationIntentFactory(), ) { const coordinator: QueryInvalidationCoordinator = { async invalidate(topics) { for (const topic of topics) { await client.invalidateQueries({ queryKey: [topic], exact: false, refetchType: "active", }); } }, beginMutation() { return { release: async () => {} }; }, async resetLocal() { await client.cancelQueries(); client.clear(); }, dispose() {}, }; return function QueryWrapper({ children }: { children: ReactNode }) { return ( {children} ); }; }