>('/users', {
+ validate: (raw) => {
+ const result = UserSchema.safeParse(raw);
+ return result.success
+ ? { success: true, data: result.data }
+ : { success: false, error: result.error };
+ }
+ }),
+ ...rqDefaults()
+ });
+}
+```
+
+### Обработка ошибок с Type Guards
+
+```typescript
+import type { HttpError, NetworkError } from '@asouei/safe-fetch';
+
+const isHttpError = (error: any): error is HttpError =>
+ error?.name === 'HttpError';
+
+const isNetworkError = (error: any): error is NetworkError =>
+ error?.name === 'NetworkError';
+
+function UserList() {
+ const { data, error } = useUsers();
+
+ if (error) {
+ if (isHttpError(error)) {
+ return Ошибка сервера: {error.status} {error.statusText}
;
+ }
+ if (isNetworkError(error)) {
+ return Сетевая ошибка: Проверьте подключение
;
+ }
+ return Неизвестная ошибка: {error.message}
;
+ }
+
+ return {/* отрисовка пользователей */}
;
+}
+```
+
+### Бесконечные запросы
+
+```typescript
+export function useInfiniteUsers() {
+ const queryFn = createQueryFn(api);
+
+ return useInfiniteQuery({
+ queryKey: ['users', 'infinite'],
+ queryFn: ({ pageParam = 1 }) =>
+ queryFn<{ users: User[]; nextPage?: number }>('/users', {
+ query: { page: pageParam, limit: 10 }
+ })(),
+ getNextPageParam: (lastPage) => lastPage.nextPage,
+ ...rqDefaults()
+ });
+}
+```
+
+## Лучшие практики
+
+### 1. Всегда используйте `rqDefaults()`
+```typescript
+// ✅ Хорошо
+useQuery({
+ queryKey: ['users'],
+ queryFn: queryFn('/users'),
+ ...rqDefaults()
+});
+
+// ❌ Избегайте - React Query будет повторять со своей логикой
+useQuery({
+ queryKey: ['users'],
+ queryFn: queryFn('/users')
+ // отсутствует rqDefaults()
+});
+```
+
+### 2. Настраивайте повторы в safe-fetch, а не в React Query
+```typescript
+// ✅ Хорошо
+const api = createSafeFetch({
+ retries: {
+ retries: 2,
+ baseDelayMs: 300
+ }
+});
+
+// ❌ Избегайте - двойные повторы
+useQuery({
+ queryFn: queryFn('/users'),
+ retry: 3 // Не делайте так с safe-fetch
+});
+```
+
+### 3. Правильно обрабатывайте состояния загрузки
+```typescript
+function UserProfile({ id }: { id: string }) {
+ const { data: user, isLoading, error } = useQuery({
+ queryKey: ['users', id],
+ queryFn: queryFn(`/users/${id}`),
+ ...rqDefaults()
+ });
+
+ // Явно обрабатывайте все состояния
+ if (isLoading) return ;
+ if (error) return ;
+ if (!user) return ; // Не должно произойти, но будьте осторожны
+
+ return {user.name}
;
+}
+```
+
+## Совместимость
+
+- **React Query**: v5.x
+- **SSR/Next.js**: Совместимо (чистые функции, без runtime зависимости от React)
+- **Размер бандла**: Минимальный - только тонкие обертки
+
+## Почему такой подход?
+
+Вместо предоставления кастомных хуков типа `useSafeQuery`, этот адаптер фокусируется на:
+
+1. **Минимальная поверхность API**: Только фабричные функции
+2. **Без peer зависимости от React**: Работает в любой настройке React Query
+3. **Композабельность**: Используйте с существующими паттернами React Query
+4. **Типобезопасность**: Сохраняет типизацию ошибок safe-fetch
+
+## Устранение неполадок
+
+### "Query function threw an error"
+Это ожидаемо! Адаптер преобразует результаты `{ ok: false }` в брошенные ошибки, которые может обрабатывать React Query.
+
+### Ошибки типов с функциями запросов
+Убедитесь, что указали ожидаемый тип возврата:
+```typescript
+// ✅ Хорошо
+const queryFn = createQueryFn(api);
+const getUserFn = queryFn('/user/123');
+
+// ❌ Проблемы с типами
+const getUserFn = queryFn('/user/123'); // неизвестный тип возврата
+```
+
+### Повторы работают не как ожидалось
+Не забывайте использовать `rqDefaults()` для отключения повторов React Query:
+```typescript
+useQuery({
+ queryKey: ['data'],
+ queryFn: queryFn('/data'),
+ ...rqDefaults() // Это устанавливает retry: false
+});
+```
+
+## Миграция с прямого safe-fetch
+
+**До:**
+```typescript
+function useUsers() {
+ return useQuery({
+ queryKey: ['users'],
+ queryFn: async () => {
+ const result = await safeFetch.get('/users');
+ if (!result.ok) throw result.error;
+ return result.data;
+ },
+ retry: false
+ });
+}
+```
+
+**После:**
+```typescript
+const queryFn = createQueryFn(api);
+
+function useUsers() {
+ return useQuery({
+ queryKey: ['users'],
+ queryFn: queryFn('/users'),
+ ...rqDefaults()
+ });
+}
+```
+
+## Дорожная карта
+
+- **v0.1**: Основные функции адаптера ✅ **Опубликовано**
+- **v0.2**: Опциональные кастомные хуки (`useSafeQuery`, `useSafeMutation`)
+- **v1.0**: Стабильный продакшн релиз после отзывов сообщества
+
+## Лицензия
+
+MIT © [Aleksandr Mikhailishin](https://github.com/asouei)
\ No newline at end of file
diff --git a/packages/react-query/package.json b/packages/react-query/package.json
new file mode 100644
index 0000000..fe1f841
--- /dev/null
+++ b/packages/react-query/package.json
@@ -0,0 +1,70 @@
+{
+ "name": "@asouei/safe-fetch-react-query",
+ "version": "0.1.0",
+ "description": "React Query adapter for @asouei/safe-fetch",
+ "type": "module",
+ "main": "./dist/index.cjs",
+ "module": "./dist/index.mjs",
+ "types": "./dist/index.d.ts",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.mjs",
+ "require": "./dist/index.cjs"
+ }
+ },
+ "files": [
+ "dist",
+ "README.md",
+ "LICENSE"
+ ],
+ "sideEffects": false,
+ "scripts": {
+ "build": "vite build",
+ "dev": "vite build --watch",
+ "test": "vitest run",
+ "test:watch": "vitest",
+ "clean": "rimraf dist",
+ "prepublishOnly": "npm run clean && npm run build && npm test",
+ "lint": "echo lint:ok"
+ },
+ "keywords": [
+ "react-query",
+ "tanstack",
+ "safe-fetch",
+ "http",
+ "typesafe",
+ "fetch",
+ "adapter",
+ "no-throw"
+ ],
+ "author": "Aleksandr Mikhailishin",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/asouei/safe-fetch.git",
+ "directory": "packages/react-query"
+ },
+ "bugs": {
+ "url": "https://github.com/asouei/safe-fetch/issues"
+ },
+ "homepage": "https://asouei.dev",
+ "publishConfig": {
+ "access": "public"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@tanstack/react-query": "^5.0.0",
+ "@asouei/safe-fetch": "workspace:*"
+ },
+ "devDependencies": {
+ "@types/node": "^22.0.0",
+ "typescript": "^5.9.0",
+ "vite": "^6.0.0",
+ "vite-plugin-dts": "^4.5.4",
+ "vitest": "^3.2.4",
+ "rimraf": "^6.0.1"
+ }
+}
\ No newline at end of file
diff --git a/packages/react-query/src/index.ts b/packages/react-query/src/index.ts
new file mode 100644
index 0000000..7182010
--- /dev/null
+++ b/packages/react-query/src/index.ts
@@ -0,0 +1,31 @@
+import type { SafeFetcher, SafeFetchRequest, SafeResult } from '@asouei/safe-fetch';
+
+export const unwrap = async (p: Promise>): Promise => {
+ const r = await p;
+ if (!r.ok) throw r.error;
+ return r.data;
+};
+
+export const createQueryFn =
+ (api: SafeFetcher) =>
+ (url: string, init?: SafeFetchRequest) =>
+ async (): Promise => {
+ return unwrap(api(url, init));
+ };
+
+export const createMutationFn =
+ (api: SafeFetcher) =>
+ (url: string, init?: SafeFetchRequest) =>
+ async (body?: unknown): Promise => {
+ const method = (init?.method ?? 'POST').toUpperCase();
+ const nextInit: SafeFetchRequest = {
+ ...init,
+ method: method as any,
+ body: body as any
+ };
+ return unwrap(api(url, nextInit));
+ };
+
+export const rqDefaults = () => ({
+ retry: false as const
+});
\ No newline at end of file
diff --git a/packages/react-query/tests/index.test.ts b/packages/react-query/tests/index.test.ts
new file mode 100644
index 0000000..6c509f2
--- /dev/null
+++ b/packages/react-query/tests/index.test.ts
@@ -0,0 +1,138 @@
+import { describe, it, expect, vi } from 'vitest';
+import { createQueryFn, createMutationFn, rqDefaults, unwrap } from '../src';
+
+describe('@asouei/safe-fetch-react-query', () => {
+ describe('unwrap', () => {
+ it('returns data from successful result', async () => {
+ const result = Promise.resolve({
+ ok: true as const,
+ data: { id: 1, name: 'test' },
+ response: new Response()
+ });
+
+ const data = await unwrap(result);
+ expect(data).toEqual({ id: 1, name: 'test' });
+ });
+
+ it('throws error from failed result', async () => {
+ const error = { name: 'HttpError' as const, message: 'Not found', status: 404 };
+ const result = Promise.resolve({
+ ok: false as const,
+ error
+ });
+
+ await expect(unwrap(result as any)).rejects.toEqual(error);
+ });
+ });
+
+ describe('createQueryFn', () => {
+ it('creates function that calls api and returns data', async () => {
+ const mockApi = vi.fn().mockResolvedValue({
+ ok: true,
+ data: { users: ['alice', 'bob'] },
+ response: new Response()
+ });
+
+ const queryFn = createQueryFn(mockApi as any)<{ users: string[] }>('/users');
+ const result = await queryFn();
+
+ expect(result).toEqual({ users: ['alice', 'bob'] });
+ expect(mockApi).toHaveBeenCalledWith('/users', undefined);
+ });
+
+ it('creates function that throws on api error', async () => {
+ const error = { name: 'NetworkError' as const, message: 'Failed' };
+ const mockApi = vi.fn().mockResolvedValue({ ok: false, error });
+
+ const queryFn = createQueryFn(mockApi as any)('/users');
+
+ await expect(queryFn()).rejects.toEqual(error);
+ });
+
+ it('passes init options to api', async () => {
+ const mockApi = vi.fn().mockResolvedValue({
+ ok: true,
+ data: {},
+ response: new Response()
+ });
+
+ const init = { headers: { 'X-Test': 'value' } };
+ const queryFn = createQueryFn(mockApi as any)('/users', init);
+ await queryFn();
+
+ expect(mockApi).toHaveBeenCalledWith('/users', init);
+ });
+ });
+
+ describe('createMutationFn', () => {
+ it('creates function with POST method by default', async () => {
+ const mockApi = vi.fn().mockImplementation((url, init) => {
+ return Promise.resolve({
+ ok: true,
+ data: { method: init?.method, body: init?.body },
+ response: new Response()
+ });
+ });
+
+ const mutationFn = createMutationFn(mockApi as any)<{ method: string; body: any }>('/users');
+ const result = await mutationFn({ name: 'John' });
+
+ expect(result.method).toBe('POST');
+ expect(result.body).toEqual({ name: 'John' });
+ });
+
+ it('uses custom method from init', async () => {
+ const mockApi = vi.fn().mockImplementation((url, init) => {
+ return Promise.resolve({
+ ok: true,
+ data: { method: init?.method },
+ response: new Response()
+ });
+ });
+
+ const mutationFn = createMutationFn(mockApi as any)<{ method: string }>('/users', { method: 'PUT' });
+ const result = await mutationFn({ name: 'Jane' });
+
+ expect(result.method).toBe('PUT');
+ });
+
+ it('merges init options with method and body', async () => {
+ const mockApi = vi.fn().mockImplementation((url, init) => {
+ return Promise.resolve({
+ ok: true,
+ data: init,
+ response: new Response()
+ });
+ });
+
+ const customInit = { headers: { 'Authorization': 'Bearer token' } };
+ const mutationFn = createMutationFn(mockApi as any)('/users', customInit);
+ const result = await mutationFn({ id: 123 });
+
+ expect(result.method).toBe('POST');
+ expect(result.body).toEqual({ id: 123 });
+ expect(result.headers).toEqual({ 'Authorization': 'Bearer token' });
+ });
+
+ it('throws error on api failure', async () => {
+ const error = { name: 'ValidationError' as const, message: 'Invalid data' };
+ const mockApi = vi.fn().mockResolvedValue({ ok: false, error });
+
+ const mutationFn = createMutationFn(mockApi as any)('/users');
+
+ await expect(mutationFn({ invalid: true })).rejects.toEqual(error);
+ });
+ });
+
+ describe('rqDefaults', () => {
+ it('returns retry false', () => {
+ const defaults = rqDefaults();
+ expect(defaults).toEqual({ retry: false });
+ });
+
+ it('returns object with const retry property', () => {
+ const defaults = rqDefaults();
+ expect(defaults.retry).toBe(false);
+ });
+ });
+});
\ No newline at end of file
diff --git a/packages/react-query/tsconfig.json b/packages/react-query/tsconfig.json
new file mode 100644
index 0000000..eec0779
--- /dev/null
+++ b/packages/react-query/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "lib": ["ES2020"],
+ "declaration": true,
+ "declarationMap": true,
+ "outDir": "dist",
+ "strict": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "forceConsistentCasingInFileNames": true,
+ "exactOptionalPropertyTypes": true,
+ "noUncheckedIndexedAccess": true,
+ "noImplicitReturns": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["src/**/*"],
+ "exclude": ["dist", "node_modules", "tests", "**/*.test.ts"]
+}
\ No newline at end of file
diff --git a/packages/react-query/vite.config.ts b/packages/react-query/vite.config.ts
new file mode 100644
index 0000000..46c71e1
--- /dev/null
+++ b/packages/react-query/vite.config.ts
@@ -0,0 +1,59 @@
+///
+import { defineConfig } from 'vite';
+import dts from 'vite-plugin-dts';
+
+export default defineConfig({
+ build: {
+ lib: {
+ entry: 'src/index.ts',
+ name: 'SafeFetchReactQuery',
+ fileName: (format) => {
+ switch (format) {
+ case 'es': return 'index.mjs';
+ case 'cjs': return 'index.cjs';
+ case 'umd': return 'index.umd.cjs';
+ default: return 'index.js';
+ }
+ },
+ formats: ['es', 'cjs']
+ },
+ sourcemap: true,
+ outDir: 'dist',
+ emptyOutDir: true,
+ target: 'es2020',
+ minify: false,
+ rollupOptions: {
+ external: ['@asouei/safe-fetch', '@tanstack/react-query'],
+ output: {
+ globals: {
+ '@asouei/safe-fetch': 'SafeFetch',
+ '@tanstack/react-query': 'ReactQuery'
+ }
+ }
+ }
+ },
+ plugins: [
+ dts({
+ include: ['src/**/*'],
+ exclude: ['**/*.test.*', '**/__tests__/**', 'src/**/*.spec.ts']
+ })
+ ],
+ test: {
+ environment: 'node',
+ globals: true,
+ testTimeout: 15_000,
+ hookTimeout: 15_000,
+ coverage: {
+ provider: 'v8',
+ reporter: ['text', 'html'],
+ exclude: [
+ 'node_modules/',
+ 'tests/',
+ 'dist/',
+ '**/*.d.ts',
+ '**/*.test.*',
+ '**/*.spec.*'
+ ]
+ }
+ }
+});
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index da93104..5755380 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -6,11 +6,13 @@ settings:
importers:
- .:
+ .: {}
+
+ packages/core:
devDependencies:
'@types/node':
specifier: ^22.0.0
- version: 22.18.0
+ version: 22.18.1
jsdom:
specifier: ^26.1.0
version: 26.1.0
@@ -22,13 +24,41 @@ importers:
version: 5.9.2
vite:
specifier: ^6.0.0
- version: 6.3.5(@types/node@22.18.0)
+ version: 6.3.5(@types/node@22.18.1)
vite-plugin-dts:
specifier: ^4.5.4
- version: 4.5.4(@types/node@22.18.0)(rollup@4.50.0)(typescript@5.9.2)(vite@6.3.5(@types/node@22.18.0))
+ version: 4.5.4(@types/node@22.18.1)(rollup@4.50.0)(typescript@5.9.2)(vite@6.3.5(@types/node@22.18.1))
vitest:
specifier: ^3.2.4
- version: 3.2.4(@types/node@22.18.0)(jsdom@26.1.0)
+ version: 3.2.4(@types/node@22.18.1)(jsdom@26.1.0)
+
+ packages/react-query:
+ dependencies:
+ '@asouei/safe-fetch':
+ specifier: workspace:*
+ version: link:../core
+ '@tanstack/react-query':
+ specifier: ^5.0.0
+ version: 5.86.0(react@19.1.1)
+ devDependencies:
+ '@types/node':
+ specifier: ^22.0.0
+ version: 22.18.1
+ rimraf:
+ specifier: ^6.0.1
+ version: 6.0.1
+ typescript:
+ specifier: ^5.9.0
+ version: 5.9.2
+ vite:
+ specifier: ^6.0.0
+ version: 6.3.5(@types/node@22.18.1)
+ vite-plugin-dts:
+ specifier: ^4.5.4
+ version: 4.5.4(@types/node@22.18.1)(rollup@4.50.0)(typescript@5.9.2)(vite@6.3.5(@types/node@22.18.1))
+ vitest:
+ specifier: ^3.2.4
+ version: 3.2.4(@types/node@22.18.1)(jsdom@26.1.0)
packages:
@@ -400,6 +430,14 @@ packages:
'@rushstack/ts-command-line@5.0.2':
resolution: {integrity: sha512-+AkJDbu1GFMPIU8Sb7TLVXDv/Q7Mkvx+wAjEl8XiXVVq+p1FmWW6M3LYpJMmoHNckSofeMecgWg5lfMwNAAsEQ==}
+ '@tanstack/query-core@5.86.0':
+ resolution: {integrity: sha512-Y6ibQm6BXbw6w1p3a5LrPn8Ae64M0dx7hGmnhrm9P+XAkCCKXOwZN0J5Z1wK/0RdNHtR9o+sWHDXd4veNI60tQ==}
+
+ '@tanstack/react-query@5.86.0':
+ resolution: {integrity: sha512-jgS/v0oSJkGHucv9zxOS8rL7mjATh1XO3K4eqAV4WMpAly8okcBrGi1YxRZN5S4B59F54x9JFjWrK5vMAvJYqA==}
+ peerDependencies:
+ react: ^18 || ^19
+
'@types/argparse@1.0.38':
resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==}
@@ -412,8 +450,8 @@ packages:
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
- '@types/node@22.18.0':
- resolution: {integrity: sha512-m5ObIqwsUp6BZzyiy4RdZpzWGub9bqLJMvZDD0QMXhxjqMHMENlj+SqF5QxoUwaQNFe+8kz8XM8ZQhqkQPTgMQ==}
+ '@types/node@22.18.1':
+ resolution: {integrity: sha512-rzSDyhn4cYznVG+PCzGe1lwuMYJrcBS1fc3JqSa2PvtABwWo+dZ1ij5OVok3tqfpEBCBoaR4d7upFJk73HRJDw==}
'@vitest/expect@3.2.4':
resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==}
@@ -453,11 +491,11 @@ packages:
'@volar/typescript@2.4.23':
resolution: {integrity: sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==}
- '@vue/compiler-core@3.5.20':
- resolution: {integrity: sha512-8TWXUyiqFd3GmP4JTX9hbiTFRwYHgVL/vr3cqhr4YQ258+9FADwvj7golk2sWNGHR67QgmCZ8gz80nQcMokhwg==}
+ '@vue/compiler-core@3.5.21':
+ resolution: {integrity: sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==}
- '@vue/compiler-dom@3.5.20':
- resolution: {integrity: sha512-whB44M59XKjqUEYOMPYU0ijUV0G+4fdrHVKDe32abNdX/kJe1NUEMqsi4cwzXa9kyM9w5S8WqFsrfo1ogtBZGQ==}
+ '@vue/compiler-dom@3.5.21':
+ resolution: {integrity: sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==}
'@vue/compiler-vue2@2.7.16':
resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
@@ -470,8 +508,8 @@ packages:
typescript:
optional: true
- '@vue/shared@3.5.20':
- resolution: {integrity: sha512-SoRGP596KU/ig6TfgkCMbXkr4YJ91n/QSdMuqeP5r3hVIYA3CPHUBCc7Skak0EAKV+5lL4KyIh61VA/pK1CIAA==}
+ '@vue/shared@3.5.21':
+ resolution: {integrity: sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==}
acorn@8.15.0:
resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
@@ -756,8 +794,8 @@ packages:
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
- lru-cache@11.1.0:
- resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==}
+ lru-cache@11.2.1:
+ resolution: {integrity: sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==}
engines: {node: 20 || >=22}
lru-cache@6.0.0:
@@ -847,6 +885,10 @@ packages:
quansync@0.2.11:
resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==}
+ react@19.1.1:
+ resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==}
+ engines: {node: '>=0.10.0'}
+
require-from-string@2.0.2:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
@@ -1294,23 +1336,23 @@ snapshots:
'@jridgewell/sourcemap-codec@1.5.5': {}
- '@microsoft/api-extractor-model@7.30.7(@types/node@22.18.0)':
+ '@microsoft/api-extractor-model@7.30.7(@types/node@22.18.1)':
dependencies:
'@microsoft/tsdoc': 0.15.1
'@microsoft/tsdoc-config': 0.17.1
- '@rushstack/node-core-library': 5.14.0(@types/node@22.18.0)
+ '@rushstack/node-core-library': 5.14.0(@types/node@22.18.1)
transitivePeerDependencies:
- '@types/node'
- '@microsoft/api-extractor@7.52.11(@types/node@22.18.0)':
+ '@microsoft/api-extractor@7.52.11(@types/node@22.18.1)':
dependencies:
- '@microsoft/api-extractor-model': 7.30.7(@types/node@22.18.0)
+ '@microsoft/api-extractor-model': 7.30.7(@types/node@22.18.1)
'@microsoft/tsdoc': 0.15.1
'@microsoft/tsdoc-config': 0.17.1
- '@rushstack/node-core-library': 5.14.0(@types/node@22.18.0)
+ '@rushstack/node-core-library': 5.14.0(@types/node@22.18.1)
'@rushstack/rig-package': 0.5.3
- '@rushstack/terminal': 0.15.4(@types/node@22.18.0)
- '@rushstack/ts-command-line': 5.0.2(@types/node@22.18.0)
+ '@rushstack/terminal': 0.15.4(@types/node@22.18.1)
+ '@rushstack/ts-command-line': 5.0.2(@types/node@22.18.1)
lodash: 4.17.21
minimatch: 10.0.3
resolve: 1.22.10
@@ -1400,7 +1442,7 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.50.0':
optional: true
- '@rushstack/node-core-library@5.14.0(@types/node@22.18.0)':
+ '@rushstack/node-core-library@5.14.0(@types/node@22.18.1)':
dependencies:
ajv: 8.13.0
ajv-draft-04: 1.0.0(ajv@8.13.0)
@@ -1411,29 +1453,36 @@ snapshots:
resolve: 1.22.10
semver: 7.5.4
optionalDependencies:
- '@types/node': 22.18.0
+ '@types/node': 22.18.1
'@rushstack/rig-package@0.5.3':
dependencies:
resolve: 1.22.10
strip-json-comments: 3.1.1
- '@rushstack/terminal@0.15.4(@types/node@22.18.0)':
+ '@rushstack/terminal@0.15.4(@types/node@22.18.1)':
dependencies:
- '@rushstack/node-core-library': 5.14.0(@types/node@22.18.0)
+ '@rushstack/node-core-library': 5.14.0(@types/node@22.18.1)
supports-color: 8.1.1
optionalDependencies:
- '@types/node': 22.18.0
+ '@types/node': 22.18.1
- '@rushstack/ts-command-line@5.0.2(@types/node@22.18.0)':
+ '@rushstack/ts-command-line@5.0.2(@types/node@22.18.1)':
dependencies:
- '@rushstack/terminal': 0.15.4(@types/node@22.18.0)
+ '@rushstack/terminal': 0.15.4(@types/node@22.18.1)
'@types/argparse': 1.0.38
argparse: 1.0.10
string-argv: 0.3.2
transitivePeerDependencies:
- '@types/node'
+ '@tanstack/query-core@5.86.0': {}
+
+ '@tanstack/react-query@5.86.0(react@19.1.1)':
+ dependencies:
+ '@tanstack/query-core': 5.86.0
+ react: 19.1.1
+
'@types/argparse@1.0.38': {}
'@types/chai@5.2.2':
@@ -1444,7 +1493,7 @@ snapshots:
'@types/estree@1.0.8': {}
- '@types/node@22.18.0':
+ '@types/node@22.18.1':
dependencies:
undici-types: 6.21.0
@@ -1456,13 +1505,13 @@ snapshots:
chai: 5.3.3
tinyrainbow: 2.0.0
- '@vitest/mocker@3.2.4(vite@6.3.5(@types/node@22.18.0))':
+ '@vitest/mocker@3.2.4(vite@6.3.5(@types/node@22.18.1))':
dependencies:
'@vitest/spy': 3.2.4
estree-walker: 3.0.3
magic-string: 0.30.18
optionalDependencies:
- vite: 6.3.5(@types/node@22.18.0)
+ vite: 6.3.5(@types/node@22.18.1)
'@vitest/pretty-format@3.2.4':
dependencies:
@@ -1502,18 +1551,18 @@ snapshots:
path-browserify: 1.0.1
vscode-uri: 3.1.0
- '@vue/compiler-core@3.5.20':
+ '@vue/compiler-core@3.5.21':
dependencies:
'@babel/parser': 7.28.3
- '@vue/shared': 3.5.20
+ '@vue/shared': 3.5.21
entities: 4.5.0
estree-walker: 2.0.2
source-map-js: 1.2.1
- '@vue/compiler-dom@3.5.20':
+ '@vue/compiler-dom@3.5.21':
dependencies:
- '@vue/compiler-core': 3.5.20
- '@vue/shared': 3.5.20
+ '@vue/compiler-core': 3.5.21
+ '@vue/shared': 3.5.21
'@vue/compiler-vue2@2.7.16':
dependencies:
@@ -1523,9 +1572,9 @@ snapshots:
'@vue/language-core@2.2.0(typescript@5.9.2)':
dependencies:
'@volar/language-core': 2.4.23
- '@vue/compiler-dom': 3.5.20
+ '@vue/compiler-dom': 3.5.21
'@vue/compiler-vue2': 2.7.16
- '@vue/shared': 3.5.20
+ '@vue/shared': 3.5.21
alien-signals: 0.4.14
minimatch: 9.0.5
muggle-string: 0.4.1
@@ -1533,7 +1582,7 @@ snapshots:
optionalDependencies:
typescript: 5.9.2
- '@vue/shared@3.5.20': {}
+ '@vue/shared@3.5.21': {}
acorn@8.15.0: {}
@@ -1818,7 +1867,7 @@ snapshots:
lru-cache@10.4.3: {}
- lru-cache@11.1.0: {}
+ lru-cache@11.2.1: {}
lru-cache@6.0.0:
dependencies:
@@ -1867,7 +1916,7 @@ snapshots:
path-scurry@2.0.0:
dependencies:
- lru-cache: 11.1.0
+ lru-cache: 11.2.1
minipass: 7.1.2
pathe@2.0.3: {}
@@ -1900,6 +1949,8 @@ snapshots:
quansync@0.2.11: {}
+ react@19.1.1: {}
+
require-from-string@2.0.2: {}
resolve@1.22.10:
@@ -2051,13 +2102,13 @@ snapshots:
dependencies:
punycode: 2.3.1
- vite-node@3.2.4(@types/node@22.18.0):
+ vite-node@3.2.4(@types/node@22.18.1):
dependencies:
cac: 6.7.14
debug: 4.4.1
es-module-lexer: 1.7.0
pathe: 2.0.3
- vite: 6.3.5(@types/node@22.18.0)
+ vite: 6.3.5(@types/node@22.18.1)
transitivePeerDependencies:
- '@types/node'
- jiti
@@ -2072,9 +2123,9 @@ snapshots:
- tsx
- yaml
- vite-plugin-dts@4.5.4(@types/node@22.18.0)(rollup@4.50.0)(typescript@5.9.2)(vite@6.3.5(@types/node@22.18.0)):
+ vite-plugin-dts@4.5.4(@types/node@22.18.1)(rollup@4.50.0)(typescript@5.9.2)(vite@6.3.5(@types/node@22.18.1)):
dependencies:
- '@microsoft/api-extractor': 7.52.11(@types/node@22.18.0)
+ '@microsoft/api-extractor': 7.52.11(@types/node@22.18.1)
'@rollup/pluginutils': 5.2.0(rollup@4.50.0)
'@volar/typescript': 2.4.23
'@vue/language-core': 2.2.0(typescript@5.9.2)
@@ -2085,13 +2136,13 @@ snapshots:
magic-string: 0.30.18
typescript: 5.9.2
optionalDependencies:
- vite: 6.3.5(@types/node@22.18.0)
+ vite: 6.3.5(@types/node@22.18.1)
transitivePeerDependencies:
- '@types/node'
- rollup
- supports-color
- vite@6.3.5(@types/node@22.18.0):
+ vite@6.3.5(@types/node@22.18.1):
dependencies:
esbuild: 0.25.9
fdir: 6.5.0(picomatch@4.0.3)
@@ -2100,14 +2151,14 @@ snapshots:
rollup: 4.50.0
tinyglobby: 0.2.14
optionalDependencies:
- '@types/node': 22.18.0
+ '@types/node': 22.18.1
fsevents: 2.3.3
- vitest@3.2.4(@types/node@22.18.0)(jsdom@26.1.0):
+ vitest@3.2.4(@types/node@22.18.1)(jsdom@26.1.0):
dependencies:
'@types/chai': 5.2.2
'@vitest/expect': 3.2.4
- '@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@22.18.0))
+ '@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@22.18.1))
'@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4
@@ -2125,11 +2176,11 @@ snapshots:
tinyglobby: 0.2.14
tinypool: 1.1.1
tinyrainbow: 2.0.0
- vite: 6.3.5(@types/node@22.18.0)
- vite-node: 3.2.4(@types/node@22.18.0)
+ vite: 6.3.5(@types/node@22.18.1)
+ vite-node: 3.2.4(@types/node@22.18.1)
why-is-node-running: 2.3.0
optionalDependencies:
- '@types/node': 22.18.0
+ '@types/node': 22.18.1
jsdom: 26.1.0
transitivePeerDependencies:
- jiti
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
new file mode 100644
index 0000000..ccdc80c
--- /dev/null
+++ b/pnpm-workspace.yaml
@@ -0,0 +1,2 @@
+packages:
+ - "packages/*"
\ No newline at end of file