@pyreon/query 0.11.5 → 0.11.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -56
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts +7 -7
- package/package.json +16 -16
- package/src/index.ts +21 -33
- package/src/query-client.ts +5 -5
- package/src/tests/query-additional.test.tsx +59 -59
- package/src/tests/query.test.tsx +243 -243
- package/src/tests/sse.test.tsx +131 -131
- package/src/tests/subscription.test.tsx +97 -97
- package/src/use-infinite-query.ts +7 -7
- package/src/use-is-fetching.ts +5 -5
- package/src/use-mutation.ts +8 -8
- package/src/use-queries.ts +6 -6
- package/src/use-query-error-reset-boundary.ts +6 -6
- package/src/use-query.ts +8 -8
- package/src/use-sse.ts +19 -19
- package/src/use-subscription.ts +18 -18
- package/src/use-suspense-query.ts +12 -12
package/README.md
CHANGED
|
@@ -11,14 +11,14 @@ bun add @pyreon/query @tanstack/query-core
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```tsx
|
|
14
|
-
import { QueryClient, QueryClientProvider, useQuery } from
|
|
14
|
+
import { QueryClient, QueryClientProvider, useQuery } from '@pyreon/query'
|
|
15
15
|
|
|
16
16
|
const queryClient = new QueryClient()
|
|
17
17
|
|
|
18
18
|
function UserProfile(props: { id: string }) {
|
|
19
19
|
const query = useQuery(() => ({
|
|
20
|
-
queryKey: [
|
|
21
|
-
queryFn: () => fetch(`/api/users/${props.id}`).then(r => r.json()),
|
|
20
|
+
queryKey: ['user', props.id],
|
|
21
|
+
queryFn: () => fetch(`/api/users/${props.id}`).then((r) => r.json()),
|
|
22
22
|
}))
|
|
23
23
|
|
|
24
24
|
return () => {
|
|
@@ -41,9 +41,9 @@ const App = () => (
|
|
|
41
41
|
|
|
42
42
|
Provide a `QueryClient` to the component tree.
|
|
43
43
|
|
|
44
|
-
| Parameter | Type
|
|
45
|
-
|
|
|
46
|
-
| `client`
|
|
44
|
+
| Parameter | Type | Description |
|
|
45
|
+
| --------- | ------------- | ------------------------------ |
|
|
46
|
+
| `client` | `QueryClient` | TanStack Query client instance |
|
|
47
47
|
|
|
48
48
|
### `useQueryClient()`
|
|
49
49
|
|
|
@@ -55,29 +55,29 @@ Access the `QueryClient` from the nearest `QueryClientProvider`.
|
|
|
55
55
|
|
|
56
56
|
Subscribe to a query with fine-grained reactive signals. Options are passed as a function so reactive values (e.g. signal-based query keys) trigger automatic refetches.
|
|
57
57
|
|
|
58
|
-
| Parameter | Type
|
|
59
|
-
|
|
|
58
|
+
| Parameter | Type | Description |
|
|
59
|
+
| --------- | ---------------------------- | -------------------------------- |
|
|
60
60
|
| `options` | `() => QueryObserverOptions` | Function returning query options |
|
|
61
61
|
|
|
62
62
|
**Returns:** `UseQueryResult<TData, TError>` with:
|
|
63
63
|
|
|
64
|
-
| Property
|
|
65
|
-
|
|
|
66
|
-
| `result`
|
|
67
|
-
| `data`
|
|
68
|
-
| `error`
|
|
69
|
-
| `status`
|
|
70
|
-
| `isPending`
|
|
71
|
-
| `isLoading`
|
|
72
|
-
| `isFetching` | `Signal<boolean>`
|
|
73
|
-
| `isError`
|
|
74
|
-
| `isSuccess`
|
|
75
|
-
| `refetch()`
|
|
64
|
+
| Property | Type | Description |
|
|
65
|
+
| ------------ | ------------------------------------------- | ----------------------- |
|
|
66
|
+
| `result` | `Signal<QueryObserverResult>` | Full observer result |
|
|
67
|
+
| `data` | `Signal<TData \| undefined>` | Query data |
|
|
68
|
+
| `error` | `Signal<TError \| null>` | Query error |
|
|
69
|
+
| `status` | `Signal<"pending" \| "error" \| "success">` | Query status |
|
|
70
|
+
| `isPending` | `Signal<boolean>` | No data yet |
|
|
71
|
+
| `isLoading` | `Signal<boolean>` | First fetch in progress |
|
|
72
|
+
| `isFetching` | `Signal<boolean>` | Any fetch in progress |
|
|
73
|
+
| `isError` | `Signal<boolean>` | Query errored |
|
|
74
|
+
| `isSuccess` | `Signal<boolean>` | Query succeeded |
|
|
75
|
+
| `refetch()` | `() => Promise<QueryObserverResult>` | Trigger manual refetch |
|
|
76
76
|
|
|
77
77
|
```ts
|
|
78
78
|
const userId = signal(1)
|
|
79
79
|
const query = useQuery(() => ({
|
|
80
|
-
queryKey: [
|
|
80
|
+
queryKey: ['user', userId()],
|
|
81
81
|
queryFn: () => fetchUser(userId()),
|
|
82
82
|
}))
|
|
83
83
|
// Changing userId triggers automatic refetch
|
|
@@ -87,41 +87,41 @@ const query = useQuery(() => ({
|
|
|
87
87
|
|
|
88
88
|
Run a mutation with reactive status signals.
|
|
89
89
|
|
|
90
|
-
| Parameter | Type
|
|
91
|
-
|
|
|
90
|
+
| Parameter | Type | Description |
|
|
91
|
+
| --------- | ------------------------- | --------------- |
|
|
92
92
|
| `options` | `MutationObserverOptions` | Mutation config |
|
|
93
93
|
|
|
94
94
|
**Returns:** `UseMutationResult<TData, TError, TVariables, TContext>` with:
|
|
95
95
|
|
|
96
|
-
| Property
|
|
97
|
-
|
|
|
98
|
-
| `data`
|
|
99
|
-
| `error`
|
|
100
|
-
| `status`
|
|
101
|
-
| `isPending`
|
|
102
|
-
| `isSuccess`
|
|
103
|
-
| `isError`
|
|
104
|
-
| `isIdle`
|
|
105
|
-
| `mutate(vars, opts?)`
|
|
106
|
-
| `mutateAsync(vars, opts?)` | `Function`
|
|
107
|
-
| `reset()`
|
|
96
|
+
| Property | Type | Description |
|
|
97
|
+
| -------------------------- | ----------------------------------------------------- | -------------------------- |
|
|
98
|
+
| `data` | `Signal<TData \| undefined>` | Mutation result |
|
|
99
|
+
| `error` | `Signal<TError \| null>` | Mutation error |
|
|
100
|
+
| `status` | `Signal<"idle" \| "pending" \| "success" \| "error">` | Status |
|
|
101
|
+
| `isPending` | `Signal<boolean>` | Mutation in progress |
|
|
102
|
+
| `isSuccess` | `Signal<boolean>` | Mutation succeeded |
|
|
103
|
+
| `isError` | `Signal<boolean>` | Mutation errored |
|
|
104
|
+
| `isIdle` | `Signal<boolean>` | Not yet fired |
|
|
105
|
+
| `mutate(vars, opts?)` | `Function` | Fire-and-forget mutation |
|
|
106
|
+
| `mutateAsync(vars, opts?)` | `Function` | Promise-returning mutation |
|
|
107
|
+
| `reset()` | `() => void` | Reset to idle state |
|
|
108
108
|
|
|
109
109
|
```ts
|
|
110
110
|
const mutation = useMutation({
|
|
111
111
|
mutationFn: (data: { title: string }) =>
|
|
112
|
-
fetch(
|
|
113
|
-
onSuccess: () => queryClient.invalidateQueries({ queryKey: [
|
|
112
|
+
fetch('/api/posts', { method: 'POST', body: JSON.stringify(data) }).then((r) => r.json()),
|
|
113
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['posts'] }),
|
|
114
114
|
})
|
|
115
115
|
|
|
116
|
-
mutation.mutate({ title:
|
|
116
|
+
mutation.mutate({ title: 'New Post' })
|
|
117
117
|
```
|
|
118
118
|
|
|
119
119
|
### `useInfiniteQuery(options)`
|
|
120
120
|
|
|
121
121
|
Paginated/infinite query with the same fine-grained signal pattern as `useQuery`.
|
|
122
122
|
|
|
123
|
-
| Parameter | Type
|
|
124
|
-
|
|
|
123
|
+
| Parameter | Type | Description |
|
|
124
|
+
| --------- | ------------------------------------ | -------------------------- |
|
|
125
125
|
| `options` | `() => InfiniteQueryObserverOptions` | Function returning options |
|
|
126
126
|
|
|
127
127
|
**Returns:** `UseInfiniteQueryResult<TData, TError>` — same shape as `UseQueryResult`.
|
|
@@ -130,8 +130,8 @@ Paginated/infinite query with the same fine-grained signal pattern as `useQuery`
|
|
|
130
130
|
|
|
131
131
|
Run multiple queries in parallel.
|
|
132
132
|
|
|
133
|
-
| Parameter | Type
|
|
134
|
-
|
|
|
133
|
+
| Parameter | Type | Description |
|
|
134
|
+
| --------- | ------------------- | ---------------------- |
|
|
135
135
|
| `options` | `UseQueriesOptions` | Array of query configs |
|
|
136
136
|
|
|
137
137
|
**Returns:** Array of `UseQueryResult` objects.
|
|
@@ -140,19 +140,21 @@ Run multiple queries in parallel.
|
|
|
140
140
|
|
|
141
141
|
Suspense-enabled queries. Data is guaranteed non-undefined after the suspense boundary resolves.
|
|
142
142
|
|
|
143
|
-
| Property | Type
|
|
144
|
-
|
|
|
145
|
-
| `data`
|
|
143
|
+
| Property | Type | Description |
|
|
144
|
+
| -------- | --------------- | ------------------------------ |
|
|
145
|
+
| `data` | `Signal<TData>` | Always defined (non-undefined) |
|
|
146
146
|
|
|
147
147
|
```tsx
|
|
148
148
|
function UserList() {
|
|
149
149
|
const query = useSuspenseQuery({
|
|
150
|
-
queryKey: [
|
|
150
|
+
queryKey: ['users'],
|
|
151
151
|
queryFn: fetchUsers,
|
|
152
152
|
})
|
|
153
153
|
return () => (
|
|
154
154
|
<ul>
|
|
155
|
-
{query.data().map(u =>
|
|
155
|
+
{query.data().map((u) => (
|
|
156
|
+
<li>{u.name}</li>
|
|
157
|
+
))}
|
|
156
158
|
</ul>
|
|
157
159
|
)
|
|
158
160
|
}
|
|
@@ -162,10 +164,10 @@ function UserList() {
|
|
|
162
164
|
|
|
163
165
|
Suspense wrapper component with built-in error handling.
|
|
164
166
|
|
|
165
|
-
| Parameter
|
|
166
|
-
|
|
|
167
|
+
| Parameter | Type | Description |
|
|
168
|
+
| ---------- | ------------ | ---------------- |
|
|
167
169
|
| `fallback` | `VNodeChild` | Loading fallback |
|
|
168
|
-
| `children` | `VNodeChild` | Content
|
|
170
|
+
| `children` | `VNodeChild` | Content |
|
|
169
171
|
|
|
170
172
|
```tsx
|
|
171
173
|
<QuerySuspense fallback={<p>Loading...</p>}>
|
|
@@ -181,8 +183,8 @@ Error boundary for resetting query errors on retry.
|
|
|
181
183
|
|
|
182
184
|
Global counters of active queries/mutations as reactive signals.
|
|
183
185
|
|
|
184
|
-
| Parameter | Type
|
|
185
|
-
|
|
|
186
|
+
| Parameter | Type | Description |
|
|
187
|
+
| --------- | ---------------------------------- | -------------------------------- |
|
|
186
188
|
| `filters` | `QueryFilters` / `MutationFilters` | Optional filters to narrow scope |
|
|
187
189
|
|
|
188
190
|
**Returns:** `Signal<number>`
|
|
@@ -197,11 +199,11 @@ const fetching = useIsFetching()
|
|
|
197
199
|
### SSR Dehydration
|
|
198
200
|
|
|
199
201
|
```ts
|
|
200
|
-
import { QueryClient, dehydrate, hydrate } from
|
|
202
|
+
import { QueryClient, dehydrate, hydrate } from '@pyreon/query'
|
|
201
203
|
|
|
202
204
|
// Server: prefetch and serialize
|
|
203
205
|
const queryClient = new QueryClient()
|
|
204
|
-
await queryClient.prefetchQuery({ queryKey: [
|
|
206
|
+
await queryClient.prefetchQuery({ queryKey: ['users'], queryFn: fetchUsers })
|
|
205
207
|
const dehydratedState = dehydrate(queryClient)
|
|
206
208
|
|
|
207
209
|
// Client: restore cache
|
|
@@ -213,9 +215,9 @@ hydrate(queryClient, dehydratedState)
|
|
|
213
215
|
Options are a function, so reading signals inside auto-tracks dependencies.
|
|
214
216
|
|
|
215
217
|
```ts
|
|
216
|
-
const filter = signal(
|
|
218
|
+
const filter = signal('active')
|
|
217
219
|
const query = useQuery(() => ({
|
|
218
|
-
queryKey: [
|
|
220
|
+
queryKey: ['todos', filter()],
|
|
219
221
|
queryFn: () => fetchTodos(filter()),
|
|
220
222
|
}))
|
|
221
223
|
// Changing filter() triggers a new fetch
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/query-client.ts","../src/use-infinite-query.ts","../src/use-is-fetching.ts","../src/use-mutation.ts","../src/use-queries.ts","../src/use-query.ts","../src/use-query-error-reset-boundary.ts","../src/use-sse.ts","../src/use-subscription.ts","../src/use-suspense-query.ts"],"sourcesContent":["import type { Props, VNode, VNodeChild } from \"@pyreon/core\"\nimport { createContext, onMount, provide, useContext } from \"@pyreon/core\"\nimport type { QueryClient } from \"@tanstack/query-core\"\n\nexport interface QueryClientProviderProps extends Props {\n client: QueryClient\n children?: VNodeChild\n}\n\nexport const QueryClientContext = createContext<QueryClient | null>(null)\n\n/**\n * Provides a QueryClient to all descendant components via context.\n * Wrap your app root with this to enable useQuery / useMutation throughout the tree.\n *\n * @example\n * const client = new QueryClient()\n * mount(h(QueryClientProvider, { client }, h(App, null)), el)\n */\nexport function QueryClientProvider(props: QueryClientProviderProps): VNode {\n provide(QueryClientContext, props.client)\n\n // client.mount() activates window focus refetching and online/offline handling.\n // client.unmount() unsubscribes focusManager + onlineManager when the provider leaves the tree.\n onMount(() => {\n props.client.mount()\n return () => props.client.unmount()\n })\n\n const ch = props.children\n return (typeof ch === \"function\" ? (ch as () => VNodeChild)() : ch) as VNode\n}\n\n/**\n * Returns the nearest QueryClient provided by <QueryClientProvider>.\n * Throws if called outside of one.\n */\nexport function useQueryClient(): QueryClient {\n const client = useContext(QueryClientContext)\n if (!client) {\n throw new Error(\n \"[@pyreon/query] No QueryClient found. Wrap your app with <QueryClientProvider client={client}>.\",\n )\n }\n return client\n}\n","import { onUnmount } from \"@pyreon/core\"\nimport type { Signal } from \"@pyreon/reactivity\"\nimport { batch, effect, signal } from \"@pyreon/reactivity\"\nimport type {\n DefaultError,\n InfiniteData,\n InfiniteQueryObserverOptions,\n InfiniteQueryObserverResult,\n QueryKey,\n QueryObserverResult,\n} from \"@tanstack/query-core\"\nimport { InfiniteQueryObserver } from \"@tanstack/query-core\"\nimport { useQueryClient } from \"./query-client\"\n\nexport interface UseInfiniteQueryResult<TQueryFnData, TError = DefaultError> {\n /** Raw signal — full observer result. */\n result: Signal<InfiniteQueryObserverResult<InfiniteData<TQueryFnData>, TError>>\n data: Signal<InfiniteData<TQueryFnData> | undefined>\n error: Signal<TError | null>\n status: Signal<\"pending\" | \"error\" | \"success\">\n isPending: Signal<boolean>\n isLoading: Signal<boolean>\n isFetching: Signal<boolean>\n isFetchingNextPage: Signal<boolean>\n isFetchingPreviousPage: Signal<boolean>\n isError: Signal<boolean>\n isSuccess: Signal<boolean>\n hasNextPage: Signal<boolean>\n hasPreviousPage: Signal<boolean>\n fetchNextPage: () => Promise<InfiniteQueryObserverResult<InfiniteData<TQueryFnData>, TError>>\n fetchPreviousPage: () => Promise<InfiniteQueryObserverResult<InfiniteData<TQueryFnData>, TError>>\n refetch: () => Promise<QueryObserverResult<InfiniteData<TQueryFnData>, TError>>\n}\n\n/**\n * Subscribe to a paginated / infinite-scroll query.\n * Returns fine-grained reactive signals plus `fetchNextPage`, `fetchPreviousPage`,\n * `hasNextPage` and `hasPreviousPage`.\n *\n * @example\n * const query = useInfiniteQuery(() => ({\n * queryKey: ['posts'],\n * queryFn: ({ pageParam }) => fetchPosts(pageParam as number),\n * initialPageParam: 0,\n * getNextPageParam: (lastPage) => lastPage.nextCursor,\n * }))\n * // query.data()?.pages — array of pages\n * // h('button', { onClick: () => query.fetchNextPage() }, 'Load more')\n */\nexport function useInfiniteQuery<\n TQueryFnData = unknown,\n TError = DefaultError,\n TQueryKey extends QueryKey = QueryKey,\n TPageParam = unknown,\n>(\n options: () => InfiniteQueryObserverOptions<\n TQueryFnData,\n TError,\n InfiniteData<TQueryFnData>,\n TQueryKey,\n TPageParam\n >,\n): UseInfiniteQueryResult<TQueryFnData, TError> {\n const client = useQueryClient()\n const observer = new InfiniteQueryObserver<\n TQueryFnData,\n TError,\n InfiniteData<TQueryFnData>,\n TQueryKey,\n TPageParam\n >(client, options())\n const initial = observer.getCurrentResult()\n\n const resultSig = signal(initial)\n const dataSig = signal<InfiniteData<TQueryFnData> | undefined>(initial.data)\n const errorSig = signal<TError | null>(initial.error ?? null)\n const statusSig = signal(initial.status)\n const isPending = signal(initial.isPending)\n const isLoading = signal(initial.isLoading)\n const isFetching = signal(initial.isFetching)\n const isFetchingNextPage = signal(initial.isFetchingNextPage)\n const isFetchingPreviousPage = signal(initial.isFetchingPreviousPage)\n const isError = signal(initial.isError)\n const isSuccess = signal(initial.isSuccess)\n const hasNextPage = signal(initial.hasNextPage)\n const hasPreviousPage = signal(initial.hasPreviousPage)\n\n const unsub = observer.subscribe((r) => {\n batch(() => {\n resultSig.set(r)\n dataSig.set(r.data)\n errorSig.set(r.error ?? null)\n statusSig.set(r.status)\n isPending.set(r.isPending)\n isLoading.set(r.isLoading)\n isFetching.set(r.isFetching)\n isFetchingNextPage.set(r.isFetchingNextPage)\n isFetchingPreviousPage.set(r.isFetchingPreviousPage)\n isError.set(r.isError)\n isSuccess.set(r.isSuccess)\n hasNextPage.set(r.hasNextPage)\n hasPreviousPage.set(r.hasPreviousPage)\n })\n })\n\n effect(() => {\n observer.setOptions(options())\n })\n\n onUnmount(() => unsub())\n\n return {\n result: resultSig,\n data: dataSig,\n error: errorSig,\n status: statusSig,\n isPending,\n isLoading,\n isFetching,\n isFetchingNextPage,\n isFetchingPreviousPage,\n isError,\n isSuccess,\n hasNextPage,\n hasPreviousPage,\n fetchNextPage: () => observer.fetchNextPage(),\n fetchPreviousPage: () => observer.fetchPreviousPage(),\n refetch: () => observer.refetch(),\n }\n}\n","import { onUnmount } from \"@pyreon/core\"\nimport type { Signal } from \"@pyreon/reactivity\"\nimport { signal } from \"@pyreon/reactivity\"\nimport type { MutationFilters, QueryFilters } from \"@tanstack/query-core\"\nimport { useQueryClient } from \"./query-client\"\n\n/**\n * Returns a signal that tracks how many queries are currently in-flight.\n * Useful for global loading indicators.\n *\n * @example\n * const fetching = useIsFetching()\n * // h('span', null, () => fetching() > 0 ? 'Loading…' : '')\n */\nexport function useIsFetching(filters?: QueryFilters): Signal<number> {\n const client = useQueryClient()\n const count = signal(client.isFetching(filters))\n\n const unsub = client.getQueryCache().subscribe(() => {\n count.set(client.isFetching(filters))\n })\n onUnmount(() => unsub())\n\n return count\n}\n\n/**\n * Returns a signal that tracks how many mutations are currently in-flight.\n *\n * @example\n * const mutating = useIsMutating()\n * // h('span', null, () => mutating() > 0 ? 'Saving…' : '')\n */\nexport function useIsMutating(filters?: MutationFilters): Signal<number> {\n const client = useQueryClient()\n const count = signal(client.isMutating(filters))\n\n const unsub = client.getMutationCache().subscribe(() => {\n count.set(client.isMutating(filters))\n })\n onUnmount(() => unsub())\n\n return count\n}\n","import { onUnmount } from \"@pyreon/core\"\nimport type { Signal } from \"@pyreon/reactivity\"\nimport { batch, signal } from \"@pyreon/reactivity\"\nimport type {\n DefaultError,\n MutateFunction,\n MutationObserverOptions,\n MutationObserverResult,\n} from \"@tanstack/query-core\"\nimport { MutationObserver } from \"@tanstack/query-core\"\nimport { useQueryClient } from \"./query-client\"\n\nexport interface UseMutationResult<\n TData,\n TError = DefaultError,\n TVariables = void,\n TContext = unknown,\n> {\n /** Raw signal — full observer result. Fine-grained accessors below are preferred. */\n result: Signal<MutationObserverResult<TData, TError, TVariables, TContext>>\n data: Signal<TData | undefined>\n error: Signal<TError | null>\n status: Signal<\"idle\" | \"pending\" | \"success\" | \"error\">\n isPending: Signal<boolean>\n isSuccess: Signal<boolean>\n isError: Signal<boolean>\n isIdle: Signal<boolean>\n /** Fire the mutation (fire-and-forget). Errors are captured in the error signal. */\n mutate: (\n variables: TVariables,\n options?: Parameters<MutateFunction<TData, TError, TVariables, TContext>>[1],\n ) => void\n /** Like mutate but returns a promise — use for try/catch error handling. */\n mutateAsync: MutateFunction<TData, TError, TVariables, TContext>\n /** Reset the mutation state back to idle. */\n reset: () => void\n}\n\n/**\n * Run a mutation (create / update / delete). Returns reactive signals for\n * pending / success / error state plus `mutate` and `mutateAsync` functions.\n *\n * @example\n * const mutation = useMutation({\n * mutationFn: (data: CreatePostInput) =>\n * fetch('/api/posts', { method: 'POST', body: JSON.stringify(data) }).then(r => r.json()),\n * onSuccess: () => client.invalidateQueries({ queryKey: ['posts'] }),\n * })\n * // h('button', { onClick: () => mutation.mutate({ title: 'New' }) }, 'Create')\n */\nexport function useMutation<\n TData = unknown,\n TError = DefaultError,\n TVariables = void,\n TContext = unknown,\n>(\n options: MutationObserverOptions<TData, TError, TVariables, TContext>,\n): UseMutationResult<TData, TError, TVariables, TContext> {\n const client = useQueryClient()\n const observer = new MutationObserver<TData, TError, TVariables, TContext>(client, options)\n const initial = observer.getCurrentResult()\n\n // Fine-grained signals: each field is independent so only effects that read\n // e.g. `mutation.isPending()` re-run when isPending changes, not on every update.\n const resultSig = signal<MutationObserverResult<TData, TError, TVariables, TContext>>(initial)\n const dataSig = signal<TData | undefined>(initial.data)\n const errorSig = signal<TError | null>(initial.error ?? null)\n const statusSig = signal<\"idle\" | \"pending\" | \"success\" | \"error\">(initial.status)\n const isPending = signal(initial.isPending)\n const isSuccess = signal(initial.isSuccess)\n const isError = signal(initial.isError)\n const isIdle = signal(initial.isIdle)\n\n // batch() coalesces all signal updates into one notification flush.\n const unsub = observer.subscribe((r) => {\n batch(() => {\n resultSig.set(r)\n dataSig.set(r.data)\n errorSig.set(r.error ?? null)\n statusSig.set(r.status)\n isPending.set(r.isPending)\n isSuccess.set(r.isSuccess)\n isError.set(r.isError)\n isIdle.set(r.isIdle)\n })\n })\n\n onUnmount(() => unsub())\n\n return {\n result: resultSig,\n data: dataSig,\n error: errorSig,\n status: statusSig,\n isPending,\n isSuccess,\n isError,\n isIdle,\n mutate: (vars, callbackOptions) => {\n observer.mutate(vars, callbackOptions).catch(() => {\n // Error is already captured in the error signal via the observer subscription.\n // This catch prevents an unhandled promise rejection for fire-and-forget callers.\n })\n },\n mutateAsync: (vars, callbackOptions) => observer.mutate(vars, callbackOptions),\n reset: () => observer.reset(),\n }\n}\n","import { onUnmount } from \"@pyreon/core\"\nimport type { Signal } from \"@pyreon/reactivity\"\nimport { effect, signal } from \"@pyreon/reactivity\"\nimport type {\n DefaultError,\n QueryKey,\n QueryObserverOptions,\n QueryObserverResult,\n} from \"@tanstack/query-core\"\nimport { QueriesObserver } from \"@tanstack/query-core\"\nimport { useQueryClient } from \"./query-client\"\n\nexport type UseQueriesOptions<TQueryKey extends QueryKey = QueryKey> = QueryObserverOptions<\n unknown,\n DefaultError,\n unknown,\n unknown,\n TQueryKey\n>\n\n/**\n * Subscribe to multiple queries in parallel. Returns a single signal containing\n * the array of results — index-aligned with the `queries` array.\n *\n * `queries` is a reactive function so signal-based keys trigger re-evaluation\n * automatically.\n *\n * @example\n * const userIds = signal([1, 2, 3])\n * const results = useQueries(() =>\n * userIds().map(id => ({\n * queryKey: ['user', id],\n * queryFn: () => fetchUser(id),\n * }))\n * )\n * // results() — QueryObserverResult[]\n * // results()[0].data — first user\n */\nexport function useQueries(queries: () => UseQueriesOptions[]): Signal<QueryObserverResult[]> {\n const client = useQueryClient()\n const observer = new QueriesObserver(client, queries())\n\n const resultSig = signal(observer.getCurrentResult() as readonly QueryObserverResult[]) as Signal<\n QueryObserverResult[]\n >\n\n const unsub = observer.subscribe((results: readonly QueryObserverResult[]) => {\n resultSig.set(results as QueryObserverResult[])\n })\n\n // When signals inside queries() change, update the observer.\n effect(() => {\n observer.setQueries(queries())\n })\n\n onUnmount(() => {\n unsub()\n observer.destroy()\n })\n\n return resultSig\n}\n","import { onUnmount } from \"@pyreon/core\"\nimport type { Signal } from \"@pyreon/reactivity\"\nimport { batch, effect, signal } from \"@pyreon/reactivity\"\nimport type {\n DefaultError,\n QueryKey,\n QueryObserverOptions,\n QueryObserverResult,\n} from \"@tanstack/query-core\"\nimport { QueryObserver } from \"@tanstack/query-core\"\nimport { useQueryClient } from \"./query-client\"\n\nexport interface UseQueryResult<TData, TError = DefaultError> {\n /** Raw signal — the full observer result. Fine-grained accessors below are preferred. */\n result: Signal<QueryObserverResult<TData, TError>>\n data: Signal<TData | undefined>\n error: Signal<TError | null>\n status: Signal<\"pending\" | \"error\" | \"success\">\n isPending: Signal<boolean>\n isLoading: Signal<boolean>\n isFetching: Signal<boolean>\n isError: Signal<boolean>\n isSuccess: Signal<boolean>\n /** Manually trigger a refetch. */\n refetch: () => Promise<QueryObserverResult<TData, TError>>\n}\n\n/**\n * Subscribe to a query. Returns fine-grained reactive signals for data,\n * error and status — each signal only notifies effects that depend on it.\n *\n * `options` is a function so it can read Pyreon signals — when a signal changes\n * (e.g. a reactive query key), the observer is updated and refetches automatically.\n *\n * @example\n * const userId = signal(1)\n * const query = useQuery(() => ({\n * queryKey: ['user', userId()],\n * queryFn: () => fetch(`/api/users/${userId()}`).then(r => r.json()),\n * }))\n * // In template: () => query.data()?.name\n */\nexport function useQuery<TData = unknown, TError = DefaultError, TKey extends QueryKey = QueryKey>(\n options: () => QueryObserverOptions<TData, TError, TData, TData, TKey>,\n): UseQueryResult<TData, TError> {\n const client = useQueryClient()\n const observer = new QueryObserver<TData, TError, TData, TData, TKey>(client, options())\n const initial = observer.getCurrentResult()\n\n // Fine-grained signals: each field is independent so only effects that read\n // e.g. `query.data()` re-run when data changes, not when isFetching flips.\n const resultSig = signal<QueryObserverResult<TData, TError>>(initial)\n const dataSig = signal<TData | undefined>(initial.data)\n const errorSig = signal<TError | null>(initial.error ?? null)\n const statusSig = signal<\"pending\" | \"error\" | \"success\">(initial.status)\n const isPending = signal(initial.isPending)\n const isLoading = signal(initial.isLoading)\n const isFetching = signal(initial.isFetching)\n const isError = signal(initial.isError)\n const isSuccess = signal(initial.isSuccess)\n\n // Subscribe synchronously — data flows before mount (correct for SSR pre-population).\n // batch() coalesces all signal updates into one notification flush.\n const unsub = observer.subscribe((r) => {\n batch(() => {\n resultSig.set(r)\n dataSig.set(r.data)\n errorSig.set(r.error ?? null)\n statusSig.set(r.status)\n isPending.set(r.isPending)\n isLoading.set(r.isLoading)\n isFetching.set(r.isFetching)\n isError.set(r.isError)\n isSuccess.set(r.isSuccess)\n })\n })\n\n // Track reactive options: when signals inside options() change, update the observer.\n // effect() is auto-registered in the component's EffectScope → auto-disposed on unmount.\n effect(() => {\n observer.setOptions(options())\n })\n\n // Unsubscribe the observer on unmount (effect disposal is handled by EffectScope).\n onUnmount(() => unsub())\n\n return {\n result: resultSig,\n data: dataSig,\n error: errorSig,\n status: statusSig,\n isPending,\n isLoading,\n isFetching,\n isError,\n isSuccess,\n refetch: () => observer.refetch(),\n }\n}\n","import type { Props, VNode, VNodeChild } from \"@pyreon/core\"\nimport { createContext, provide, useContext } from \"@pyreon/core\"\nimport { useQueryClient } from \"./query-client\"\n\n// ─── Context ────────────────────────────────────────────────────────────────\n\ninterface ErrorResetBoundaryValue {\n reset: () => void\n}\n\nconst QueryErrorResetBoundaryContext = createContext<ErrorResetBoundaryValue | null>(null)\n\n// ─── QueryErrorResetBoundary ─────────────────────────────────────────────────\n\nexport interface QueryErrorResetBoundaryProps extends Props {\n children?: VNodeChild\n}\n\n/**\n * Wraps a subtree so that `useQueryErrorResetBoundary()` descendants can reset\n * all errored queries within this boundary.\n *\n * Pair with Pyreon's `ErrorBoundary` to retry failed queries when the user\n * dismisses the error fallback:\n *\n * @example\n * h(QueryErrorResetBoundary, null,\n * h(ErrorBoundary, {\n * fallback: (err, boundaryReset) => {\n * const { reset } = useQueryErrorResetBoundary()\n * return h('button', {\n * onClick: () => { reset(); boundaryReset() },\n * }, 'Retry')\n * },\n * }, h(MyComponent, null)),\n * )\n */\nexport function QueryErrorResetBoundary(props: QueryErrorResetBoundaryProps): VNode {\n const client = useQueryClient()\n\n const value: ErrorResetBoundaryValue = {\n reset: () => {\n // Reset all active queries that are in error state so they refetch.\n client.refetchQueries({\n predicate: (query) => query.state.status === \"error\",\n })\n },\n }\n\n provide(QueryErrorResetBoundaryContext, value)\n\n const ch = props.children\n return (typeof ch === \"function\" ? (ch as () => VNodeChild)() : ch) as VNode\n}\n\n// ─── useQueryErrorResetBoundary ──────────────────────────────────────────────\n\n/**\n * Returns the `reset` function provided by the nearest `QueryErrorResetBoundary`.\n * If called outside a boundary, falls back to resetting all errored queries\n * on the current `QueryClient`.\n *\n * @example\n * // Inside an ErrorBoundary fallback:\n * const { reset } = useQueryErrorResetBoundary()\n * h('button', { onClick: () => { reset(); boundaryReset() } }, 'Retry')\n */\nexport function useQueryErrorResetBoundary(): ErrorResetBoundaryValue {\n const boundary = useContext(QueryErrorResetBoundaryContext)\n // Always call useQueryClient to respect hook ordering rules\n const client = useQueryClient()\n\n if (boundary) return boundary\n\n // Fallback: no explicit boundary — use the QueryClient directly.\n return {\n reset: () => {\n client.refetchQueries({\n predicate: (query) => query.state.status === \"error\",\n })\n },\n }\n}\n","import { onUnmount } from \"@pyreon/core\"\nimport type { Signal } from \"@pyreon/reactivity\"\nimport { batch, effect, signal } from \"@pyreon/reactivity\"\nimport type { QueryClient } from \"@tanstack/query-core\"\nimport { useQueryClient } from \"./query-client\"\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport type SSEStatus = \"connecting\" | \"connected\" | \"disconnected\" | \"error\"\n\nexport interface UseSSEOptions<T = string> {\n /** EventSource URL — can be a signal for reactive URLs */\n url: string | (() => string)\n /** Named event type(s) to listen for — if omitted, listens to generic `message` events */\n events?: string | string[]\n /** Parse raw event data — e.g. `JSON.parse` for automatic deserialization */\n parse?: (raw: string) => T\n /** Whether the SSE connection is enabled — default: true */\n enabled?: boolean | (() => boolean)\n /** Whether to automatically reconnect — default: true */\n reconnect?: boolean\n /** Initial reconnect delay in ms — doubles on each retry, default: 1000 */\n reconnectDelay?: number\n /** Maximum reconnect attempts — default: 10, 0 = unlimited */\n maxReconnectAttempts?: number\n /** Whether to send cookies with the request — default: false */\n withCredentials?: boolean\n /** Called when a message is received — use queryClient to invalidate or update cache */\n onMessage?: (data: T, queryClient: QueryClient) => void\n /** Called when the EventSource connection opens */\n onOpen?: (event: Event) => void\n /** Called when a connection error occurs */\n onError?: (event: Event) => void\n}\n\nexport interface UseSSEResult<T> {\n /** Last received message data */\n data: Signal<T | null>\n /** Current connection status */\n status: Signal<SSEStatus>\n /** Last error event */\n error: Signal<Event | null>\n /** Last `id` field received from the server (per SSE spec) */\n lastEventId: () => string\n /** EventSource readyState: 0=CONNECTING, 1=OPEN, 2=CLOSED */\n readyState: () => number\n /** Manually close the connection */\n close: () => void\n /** Manually reconnect */\n reconnect: () => void\n}\n\n// ─── useSSE ─────────────────────────────────────────────────────────────────\n\n/**\n * Reactive Server-Sent Events hook that integrates with TanStack Query.\n * Automatically manages connection lifecycle, reconnection, and cleanup.\n *\n * Use the `onMessage` callback to invalidate or update query cache\n * when the server pushes data.\n *\n * @example\n * ```ts\n * const sse = useSSE({\n * url: '/api/events',\n * parse: JSON.parse,\n * onMessage: (data, queryClient) => {\n * if (data.type === 'order-updated') {\n * queryClient.invalidateQueries({ queryKey: ['orders'] })\n * }\n * },\n * })\n * // sse.data() — last received message (parsed)\n * // sse.status() — 'connecting' | 'connected' | 'disconnected' | 'error'\n * // sse.error() — last error event or null\n * ```\n */\nexport function useSSE<T = string>(options: UseSSEOptions<T>): UseSSEResult<T> {\n const queryClient = useQueryClient()\n const data = signal<T | null>(null)\n const status = signal<SSEStatus>(\"disconnected\")\n const error = signal<Event | null>(null)\n const lastEventId = signal(\"\")\n const readyState = signal<number>(2) // Start as CLOSED until connected\n\n let es: EventSource | null = null\n let reconnectAttempts = 0\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let intentionalClose = false\n\n const reconnectEnabled = options.reconnect !== false\n const baseDelay = options.reconnectDelay ?? 1000\n const maxAttempts = options.maxReconnectAttempts ?? 10\n const eventNames = options.events\n ? Array.isArray(options.events)\n ? options.events\n : [options.events]\n : null\n\n function getUrl(): string {\n return typeof options.url === \"function\" ? options.url() : options.url\n }\n\n function isEnabled(): boolean {\n if (options.enabled === undefined) return true\n return typeof options.enabled === \"function\" ? options.enabled() : options.enabled\n }\n\n function handleMessage(event: MessageEvent): void {\n try {\n // Track lastEventId from the SSE spec\n if (event.lastEventId !== undefined && event.lastEventId !== \"\") {\n lastEventId.set(event.lastEventId)\n }\n const parsed = options.parse ? options.parse(event.data as string) : (event.data as T)\n batch(() => {\n data.set(parsed)\n error.set(null)\n })\n options.onMessage?.(parsed, queryClient)\n } catch {\n // Message handler errors should not crash the subscription\n }\n }\n\n function attachListeners(source: EventSource): void {\n if (eventNames) {\n for (const name of eventNames) {\n source.addEventListener(name, handleMessage as EventListener)\n }\n } else {\n source.onmessage = handleMessage\n }\n }\n\n function removeListeners(source: EventSource): void {\n source.onopen = null\n source.onmessage = null\n source.onerror = null\n\n if (eventNames) {\n for (const name of eventNames) {\n source.removeEventListener(name, handleMessage as EventListener)\n }\n }\n }\n\n function handleError(event: Event): void {\n status.set(\"error\")\n error.set(event)\n readyState.set(es?.readyState ?? EventSource.CLOSED)\n options.onError?.(event)\n\n // EventSource auto-reconnects for transient errors, but if readyState is CLOSED\n // the browser has given up and we need to handle reconnection ourselves\n if (es?.readyState === EventSource.CLOSED) {\n removeListeners(es)\n es.close()\n es = null\n if (!intentionalClose && reconnectEnabled) {\n scheduleReconnect()\n }\n }\n }\n\n function connect(): void {\n // Clean up existing connection\n if (es) {\n removeListeners(es)\n es.close()\n es = null\n }\n\n if (!isEnabled()) {\n status.set(\"disconnected\")\n return\n }\n\n status.set(\"connecting\")\n\n try {\n es = new EventSource(getUrl(), {\n withCredentials: options.withCredentials ?? false,\n })\n readyState.set(EventSource.CONNECTING)\n } catch {\n status.set(\"error\")\n readyState.set(EventSource.CLOSED)\n scheduleReconnect()\n return\n }\n\n es.onopen = (event: Event) => {\n batch(() => {\n status.set(\"connected\")\n error.set(null)\n readyState.set(EventSource.OPEN)\n reconnectAttempts = 0\n })\n options.onOpen?.(event)\n }\n\n attachListeners(es)\n es.onerror = handleError\n }\n\n function scheduleReconnect(): void {\n if (!reconnectEnabled) return\n if (maxAttempts > 0 && reconnectAttempts >= maxAttempts) return\n\n const delay = baseDelay * 2 ** reconnectAttempts\n reconnectAttempts++\n\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null\n if (!intentionalClose && isEnabled()) {\n connect()\n }\n }, delay)\n }\n\n function close(): void {\n intentionalClose = true\n if (reconnectTimer !== null) {\n clearTimeout(reconnectTimer)\n reconnectTimer = null\n }\n if (es) {\n removeListeners(es)\n es.close()\n es = null\n }\n status.set(\"disconnected\")\n readyState.set(EventSource.CLOSED)\n }\n\n function manualReconnect(): void {\n intentionalClose = false\n reconnectAttempts = 0\n connect()\n }\n\n // Track reactive URL and enabled state\n effect(() => {\n // Read reactive values to subscribe to changes\n if (typeof options.url === \"function\") options.url()\n if (typeof options.enabled === \"function\") options.enabled()\n\n intentionalClose = false\n reconnectAttempts = 0\n connect()\n })\n\n // Cleanup on unmount\n onUnmount(() => close())\n\n return {\n data,\n status,\n error,\n lastEventId: () => lastEventId(),\n readyState: () => readyState(),\n close,\n reconnect: manualReconnect,\n }\n}\n","import { onUnmount } from \"@pyreon/core\"\nimport type { Signal } from \"@pyreon/reactivity\"\nimport { batch, effect, signal } from \"@pyreon/reactivity\"\nimport type { QueryClient } from \"@tanstack/query-core\"\nimport { useQueryClient } from \"./query-client\"\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport type SubscriptionStatus = \"connecting\" | \"connected\" | \"disconnected\" | \"error\"\n\nexport interface UseSubscriptionOptions {\n /** WebSocket URL — can be a signal for reactive URLs */\n url: string | (() => string)\n /** WebSocket sub-protocols */\n protocols?: string | string[]\n /** Called when a message is received — use queryClient to invalidate or update cache */\n onMessage: (event: MessageEvent, queryClient: QueryClient) => void\n /** Called when the connection opens */\n onOpen?: (event: Event) => void\n /** Called when the connection closes */\n onClose?: (event: CloseEvent) => void\n /** Called when a connection error occurs */\n onError?: (event: Event) => void\n /** Whether to automatically reconnect — default: true */\n reconnect?: boolean\n /** Initial reconnect delay in ms — doubles on each retry, default: 1000 */\n reconnectDelay?: number\n /** Maximum reconnect attempts — default: 10, 0 = unlimited */\n maxReconnectAttempts?: number\n /** Whether the subscription is enabled — default: true */\n enabled?: boolean | (() => boolean)\n}\n\nexport interface UseSubscriptionResult {\n /** Current connection status */\n status: Signal<SubscriptionStatus>\n /** Send data through the WebSocket */\n send: (data: string | Blob | BufferSource) => void\n /** Manually close the connection */\n close: () => void\n /** Manually reconnect */\n reconnect: () => void\n}\n\n// ─── useSubscription ─────────────────────────────────────────────────────────\n\n/**\n * Reactive WebSocket subscription that integrates with TanStack Query.\n * Automatically manages connection lifecycle, reconnection, and cleanup.\n *\n * Use the `onMessage` callback to invalidate or update query cache\n * when the server pushes data.\n *\n * @example\n * ```ts\n * const sub = useSubscription({\n * url: 'wss://api.example.com/ws',\n * onMessage: (event, queryClient) => {\n * const data = JSON.parse(event.data)\n * if (data.type === 'order-updated') {\n * queryClient.invalidateQueries({ queryKey: ['orders'] })\n * }\n * },\n * })\n * // sub.status() — 'connecting' | 'connected' | 'disconnected' | 'error'\n * // sub.send(JSON.stringify({ type: 'subscribe', channel: 'orders' }))\n * ```\n */\nexport function useSubscription(options: UseSubscriptionOptions): UseSubscriptionResult {\n const queryClient = useQueryClient()\n const status = signal<SubscriptionStatus>(\"disconnected\")\n\n let ws: WebSocket | null = null\n let reconnectAttempts = 0\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let intentionalClose = false\n\n const reconnectEnabled = options.reconnect !== false\n const baseDelay = options.reconnectDelay ?? 1000\n const maxAttempts = options.maxReconnectAttempts ?? 10\n\n function getUrl(): string {\n return typeof options.url === \"function\" ? options.url() : options.url\n }\n\n function isEnabled(): boolean {\n if (options.enabled === undefined) return true\n return typeof options.enabled === \"function\" ? options.enabled() : options.enabled\n }\n\n function connect(): void {\n if (ws) {\n ws.onopen = null\n ws.onmessage = null\n ws.onclose = null\n ws.onerror = null\n if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {\n ws.close()\n }\n }\n\n if (!isEnabled()) {\n status.set(\"disconnected\")\n return\n }\n\n status.set(\"connecting\")\n\n try {\n ws = options.protocols ? new WebSocket(getUrl(), options.protocols) : new WebSocket(getUrl())\n } catch {\n status.set(\"error\")\n scheduleReconnect()\n return\n }\n\n ws.onopen = (event) => {\n batch(() => {\n status.set(\"connected\")\n reconnectAttempts = 0\n })\n options.onOpen?.(event)\n }\n\n ws.onmessage = (event) => {\n try {\n options.onMessage(event, queryClient)\n } catch {\n // Message handler errors should not crash the subscription\n }\n }\n\n ws.onclose = (event) => {\n status.set(\"disconnected\")\n options.onClose?.(event)\n\n if (!intentionalClose && reconnectEnabled) {\n scheduleReconnect()\n }\n }\n\n ws.onerror = (event) => {\n status.set(\"error\")\n options.onError?.(event)\n }\n }\n\n function scheduleReconnect(): void {\n if (!reconnectEnabled) return\n if (maxAttempts > 0 && reconnectAttempts >= maxAttempts) return\n\n const delay = baseDelay * 2 ** reconnectAttempts\n reconnectAttempts++\n\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null\n if (!intentionalClose && isEnabled()) {\n connect()\n }\n }, delay)\n }\n\n function send(data: string | Blob | BufferSource): void {\n if (ws?.readyState === WebSocket.OPEN) {\n ws.send(data)\n }\n }\n\n function close(): void {\n intentionalClose = true\n if (reconnectTimer !== null) {\n clearTimeout(reconnectTimer)\n reconnectTimer = null\n }\n if (ws) {\n ws.onopen = null\n ws.onmessage = null\n ws.onclose = null\n ws.onerror = null\n if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {\n ws.close()\n }\n ws = null\n }\n status.set(\"disconnected\")\n }\n\n function manualReconnect(): void {\n intentionalClose = false\n reconnectAttempts = 0\n connect()\n }\n\n // Track reactive URL and enabled state\n effect(() => {\n // Read reactive values to subscribe to changes\n if (typeof options.url === \"function\") options.url()\n if (typeof options.enabled === \"function\") options.enabled()\n\n intentionalClose = false\n reconnectAttempts = 0\n connect()\n })\n\n // Cleanup on unmount\n onUnmount(() => close())\n\n return {\n status,\n send,\n close,\n reconnect: manualReconnect,\n }\n}\n","import type { VNodeChild, VNodeChildAtom } from \"@pyreon/core\"\nimport { onUnmount } from \"@pyreon/core\"\nimport type { Signal } from \"@pyreon/reactivity\"\nimport { batch, effect, signal } from \"@pyreon/reactivity\"\nimport type {\n DefaultError,\n InfiniteData,\n InfiniteQueryObserverOptions,\n InfiniteQueryObserverResult,\n QueryKey,\n QueryObserverOptions,\n QueryObserverResult,\n} from \"@tanstack/query-core\"\nimport { InfiniteQueryObserver, QueryObserver } from \"@tanstack/query-core\"\nimport { useQueryClient } from \"./query-client\"\n\n// ─── Types ─────────────────────────────────────────────────────────────────\n\n/**\n * Like `UseQueryResult` but `data` is `Signal<TData>` (never undefined).\n * Only use inside a `QuerySuspense` boundary which guarantees the query has\n * succeeded before children are rendered.\n */\nexport interface UseSuspenseQueryResult<TData, TError = DefaultError> {\n result: Signal<QueryObserverResult<TData, TError>>\n /** Always TData — never undefined inside a QuerySuspense boundary. */\n data: Signal<TData>\n error: Signal<TError | null>\n status: Signal<\"pending\" | \"error\" | \"success\">\n isPending: Signal<boolean>\n isFetching: Signal<boolean>\n isError: Signal<boolean>\n isSuccess: Signal<boolean>\n refetch: () => Promise<QueryObserverResult<TData, TError>>\n}\n\nexport interface UseSuspenseInfiniteQueryResult<TQueryFnData, TError = DefaultError> {\n result: Signal<InfiniteQueryObserverResult<InfiniteData<TQueryFnData>, TError>>\n /** Always InfiniteData<TQueryFnData> — never undefined inside a QuerySuspense boundary. */\n data: Signal<InfiniteData<TQueryFnData>>\n error: Signal<TError | null>\n status: Signal<\"pending\" | \"error\" | \"success\">\n isFetching: Signal<boolean>\n isFetchingNextPage: Signal<boolean>\n isFetchingPreviousPage: Signal<boolean>\n isError: Signal<boolean>\n isSuccess: Signal<boolean>\n hasNextPage: Signal<boolean>\n hasPreviousPage: Signal<boolean>\n fetchNextPage: () => Promise<InfiniteQueryObserverResult<InfiniteData<TQueryFnData>, TError>>\n fetchPreviousPage: () => Promise<InfiniteQueryObserverResult<InfiniteData<TQueryFnData>, TError>>\n refetch: () => Promise<QueryObserverResult<InfiniteData<TQueryFnData>, TError>>\n}\n\n// ─── QuerySuspense ──────────────────────────────────────────────────────────\n\ntype AnyQueryLike = {\n isPending: Signal<boolean>\n isError: Signal<boolean>\n error: Signal<unknown>\n}\n\nexport interface QuerySuspenseProps {\n /**\n * A single query result (or array of them) to gate on.\n * Children only render when ALL queries have succeeded.\n */\n query: AnyQueryLike | AnyQueryLike[]\n /** Rendered while any query is pending. */\n fallback?: VNodeChild\n /** Rendered when any query has errored. Defaults to re-throwing to nearest ErrorBoundary. */\n error?: (err: unknown) => VNodeChild\n children: VNodeChild\n}\n\n/**\n * Pyreon-native Suspense boundary for queries. Shows `fallback` while any query\n * is pending. On error, renders the `error` fallback or re-throws to the\n * nearest Pyreon `ErrorBoundary`.\n *\n * Pair with `useSuspenseQuery` / `useSuspenseInfiniteQuery` to get non-undefined\n * `data` types inside children.\n *\n * @example\n * const userQuery = useSuspenseQuery(() => ({ queryKey: ['user'], queryFn: fetchUser }))\n *\n * h(QuerySuspense, {\n * query: userQuery,\n * fallback: h(Spinner, null),\n * error: (err) => h('p', null, `Failed: ${err}`),\n * }, () => h(UserProfile, { user: userQuery.data() }))\n */\nexport function QuerySuspense(props: QuerySuspenseProps): VNodeChild {\n return (): VNodeChildAtom => {\n const queries = Array.isArray(props.query) ? props.query : [props.query]\n\n // Error state — use provided error fallback or re-throw to ErrorBoundary\n for (const q of queries) {\n if (q.isError()) {\n const err = q.error()\n if (props.error) {\n return props.error(err) as VNodeChildAtom\n }\n throw err\n }\n }\n\n // Pending state — show fallback\n if (queries.some((q) => q.isPending())) {\n const fb = props.fallback\n return (\n typeof fb === \"function\" ? (fb as () => VNodeChildAtom)() : (fb ?? null)\n ) as VNodeChildAtom\n }\n\n // All success — render children\n const ch = props.children\n return (typeof ch === \"function\" ? (ch as () => VNodeChildAtom)() : ch) as VNodeChildAtom\n }\n}\n\n// ─── useSuspenseQuery ───────────────────────────────────────────────────────\n\n/**\n * Like `useQuery` but `data` is typed as `Signal<TData>` (never undefined).\n * Designed for use inside a `QuerySuspense` boundary, which guarantees\n * children only render after the query succeeds.\n *\n * @example\n * const user = useSuspenseQuery(() => ({ queryKey: ['user', id()], queryFn: fetchUser }))\n *\n * h(QuerySuspense, { query: user, fallback: h(Spinner, null) },\n * () => h(UserCard, { name: user.data().name }),\n * )\n */\nexport function useSuspenseQuery<\n TData = unknown,\n TError = DefaultError,\n TKey extends QueryKey = QueryKey,\n>(\n options: () => QueryObserverOptions<TData, TError, TData, TData, TKey>,\n): UseSuspenseQueryResult<TData, TError> {\n const client = useQueryClient()\n const observer = new QueryObserver<TData, TError, TData, TData, TKey>(client, options())\n const initial = observer.getCurrentResult()\n\n const resultSig = signal<QueryObserverResult<TData, TError>>(initial)\n const dataSig = signal<TData>(initial.data as TData)\n const errorSig = signal<TError | null>(initial.error ?? null)\n const statusSig = signal<\"pending\" | \"error\" | \"success\">(initial.status)\n const isPending = signal(initial.isPending)\n const isFetching = signal(initial.isFetching)\n const isError = signal(initial.isError)\n const isSuccess = signal(initial.isSuccess)\n\n const unsub = observer.subscribe((r) => {\n batch(() => {\n resultSig.set(r)\n if (r.data !== undefined) dataSig.set(r.data as TData)\n errorSig.set(r.error ?? null)\n statusSig.set(r.status)\n isPending.set(r.isPending)\n isFetching.set(r.isFetching)\n isError.set(r.isError)\n isSuccess.set(r.isSuccess)\n })\n })\n\n effect(() => {\n observer.setOptions(options())\n })\n onUnmount(() => unsub())\n\n return {\n result: resultSig,\n data: dataSig,\n error: errorSig,\n status: statusSig,\n isPending,\n isFetching,\n isError,\n isSuccess,\n refetch: () => observer.refetch(),\n }\n}\n\n// ─── useSuspenseInfiniteQuery ───────────────────────────────────────────────\n\n/**\n * Like `useInfiniteQuery` but `data` is typed as `Signal<InfiniteData<TData>>`\n * (never undefined). Use inside a `QuerySuspense` boundary.\n */\nexport function useSuspenseInfiniteQuery<\n TQueryFnData = unknown,\n TError = DefaultError,\n TQueryKey extends QueryKey = QueryKey,\n TPageParam = unknown,\n>(\n options: () => InfiniteQueryObserverOptions<\n TQueryFnData,\n TError,\n InfiniteData<TQueryFnData>,\n TQueryKey,\n TPageParam\n >,\n): UseSuspenseInfiniteQueryResult<TQueryFnData, TError> {\n const client = useQueryClient()\n const observer = new InfiniteQueryObserver<\n TQueryFnData,\n TError,\n InfiniteData<TQueryFnData>,\n TQueryKey,\n TPageParam\n >(client, options())\n const initial = observer.getCurrentResult()\n\n const resultSig = signal(initial)\n const dataSig = signal(initial.data as InfiniteData<TQueryFnData>)\n const errorSig = signal<TError | null>(initial.error ?? null)\n const statusSig = signal(initial.status)\n const isFetching = signal(initial.isFetching)\n const isFetchingNextPage = signal(initial.isFetchingNextPage)\n const isFetchingPreviousPage = signal(initial.isFetchingPreviousPage)\n const isError = signal(initial.isError)\n const isSuccess = signal(initial.isSuccess)\n const hasNextPage = signal(initial.hasNextPage)\n const hasPreviousPage = signal(initial.hasPreviousPage)\n\n const unsub = observer.subscribe((r) => {\n batch(() => {\n resultSig.set(r)\n if (r.data !== undefined) dataSig.set(r.data)\n errorSig.set(r.error ?? null)\n statusSig.set(r.status)\n isFetching.set(r.isFetching)\n isFetchingNextPage.set(r.isFetchingNextPage)\n isFetchingPreviousPage.set(r.isFetchingPreviousPage)\n isError.set(r.isError)\n isSuccess.set(r.isSuccess)\n hasNextPage.set(r.hasNextPage)\n hasPreviousPage.set(r.hasPreviousPage)\n })\n })\n\n effect(() => {\n observer.setOptions(options())\n })\n onUnmount(() => unsub())\n\n return {\n result: resultSig,\n data: dataSig,\n error: errorSig,\n status: statusSig,\n isFetching,\n isFetchingNextPage,\n isFetchingPreviousPage,\n isError,\n isSuccess,\n hasNextPage,\n hasPreviousPage,\n fetchNextPage: () => observer.fetchNextPage(),\n fetchPreviousPage: () => observer.fetchPreviousPage(),\n refetch: () => observer.refetch(),\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/query-client.ts","../src/use-infinite-query.ts","../src/use-is-fetching.ts","../src/use-mutation.ts","../src/use-queries.ts","../src/use-query.ts","../src/use-query-error-reset-boundary.ts","../src/use-sse.ts","../src/use-subscription.ts","../src/use-suspense-query.ts"],"sourcesContent":["import type { Props, VNode, VNodeChild } from '@pyreon/core'\nimport { createContext, onMount, provide, useContext } from '@pyreon/core'\nimport type { QueryClient } from '@tanstack/query-core'\n\nexport interface QueryClientProviderProps extends Props {\n client: QueryClient\n children?: VNodeChild\n}\n\nexport const QueryClientContext = createContext<QueryClient | null>(null)\n\n/**\n * Provides a QueryClient to all descendant components via context.\n * Wrap your app root with this to enable useQuery / useMutation throughout the tree.\n *\n * @example\n * const client = new QueryClient()\n * mount(h(QueryClientProvider, { client }, h(App, null)), el)\n */\nexport function QueryClientProvider(props: QueryClientProviderProps): VNode {\n provide(QueryClientContext, props.client)\n\n // client.mount() activates window focus refetching and online/offline handling.\n // client.unmount() unsubscribes focusManager + onlineManager when the provider leaves the tree.\n onMount(() => {\n props.client.mount()\n return () => props.client.unmount()\n })\n\n const ch = props.children\n return (typeof ch === 'function' ? (ch as () => VNodeChild)() : ch) as VNode\n}\n\n/**\n * Returns the nearest QueryClient provided by <QueryClientProvider>.\n * Throws if called outside of one.\n */\nexport function useQueryClient(): QueryClient {\n const client = useContext(QueryClientContext)\n if (!client) {\n throw new Error(\n '[@pyreon/query] No QueryClient found. Wrap your app with <QueryClientProvider client={client}>.',\n )\n }\n return client\n}\n","import { onUnmount } from '@pyreon/core'\nimport type { Signal } from '@pyreon/reactivity'\nimport { batch, effect, signal } from '@pyreon/reactivity'\nimport type {\n DefaultError,\n InfiniteData,\n InfiniteQueryObserverOptions,\n InfiniteQueryObserverResult,\n QueryKey,\n QueryObserverResult,\n} from '@tanstack/query-core'\nimport { InfiniteQueryObserver } from '@tanstack/query-core'\nimport { useQueryClient } from './query-client'\n\nexport interface UseInfiniteQueryResult<TQueryFnData, TError = DefaultError> {\n /** Raw signal — full observer result. */\n result: Signal<InfiniteQueryObserverResult<InfiniteData<TQueryFnData>, TError>>\n data: Signal<InfiniteData<TQueryFnData> | undefined>\n error: Signal<TError | null>\n status: Signal<'pending' | 'error' | 'success'>\n isPending: Signal<boolean>\n isLoading: Signal<boolean>\n isFetching: Signal<boolean>\n isFetchingNextPage: Signal<boolean>\n isFetchingPreviousPage: Signal<boolean>\n isError: Signal<boolean>\n isSuccess: Signal<boolean>\n hasNextPage: Signal<boolean>\n hasPreviousPage: Signal<boolean>\n fetchNextPage: () => Promise<InfiniteQueryObserverResult<InfiniteData<TQueryFnData>, TError>>\n fetchPreviousPage: () => Promise<InfiniteQueryObserverResult<InfiniteData<TQueryFnData>, TError>>\n refetch: () => Promise<QueryObserverResult<InfiniteData<TQueryFnData>, TError>>\n}\n\n/**\n * Subscribe to a paginated / infinite-scroll query.\n * Returns fine-grained reactive signals plus `fetchNextPage`, `fetchPreviousPage`,\n * `hasNextPage` and `hasPreviousPage`.\n *\n * @example\n * const query = useInfiniteQuery(() => ({\n * queryKey: ['posts'],\n * queryFn: ({ pageParam }) => fetchPosts(pageParam as number),\n * initialPageParam: 0,\n * getNextPageParam: (lastPage) => lastPage.nextCursor,\n * }))\n * // query.data()?.pages — array of pages\n * // h('button', { onClick: () => query.fetchNextPage() }, 'Load more')\n */\nexport function useInfiniteQuery<\n TQueryFnData = unknown,\n TError = DefaultError,\n TQueryKey extends QueryKey = QueryKey,\n TPageParam = unknown,\n>(\n options: () => InfiniteQueryObserverOptions<\n TQueryFnData,\n TError,\n InfiniteData<TQueryFnData>,\n TQueryKey,\n TPageParam\n >,\n): UseInfiniteQueryResult<TQueryFnData, TError> {\n const client = useQueryClient()\n const observer = new InfiniteQueryObserver<\n TQueryFnData,\n TError,\n InfiniteData<TQueryFnData>,\n TQueryKey,\n TPageParam\n >(client, options())\n const initial = observer.getCurrentResult()\n\n const resultSig = signal(initial)\n const dataSig = signal<InfiniteData<TQueryFnData> | undefined>(initial.data)\n const errorSig = signal<TError | null>(initial.error ?? null)\n const statusSig = signal(initial.status)\n const isPending = signal(initial.isPending)\n const isLoading = signal(initial.isLoading)\n const isFetching = signal(initial.isFetching)\n const isFetchingNextPage = signal(initial.isFetchingNextPage)\n const isFetchingPreviousPage = signal(initial.isFetchingPreviousPage)\n const isError = signal(initial.isError)\n const isSuccess = signal(initial.isSuccess)\n const hasNextPage = signal(initial.hasNextPage)\n const hasPreviousPage = signal(initial.hasPreviousPage)\n\n const unsub = observer.subscribe((r) => {\n batch(() => {\n resultSig.set(r)\n dataSig.set(r.data)\n errorSig.set(r.error ?? null)\n statusSig.set(r.status)\n isPending.set(r.isPending)\n isLoading.set(r.isLoading)\n isFetching.set(r.isFetching)\n isFetchingNextPage.set(r.isFetchingNextPage)\n isFetchingPreviousPage.set(r.isFetchingPreviousPage)\n isError.set(r.isError)\n isSuccess.set(r.isSuccess)\n hasNextPage.set(r.hasNextPage)\n hasPreviousPage.set(r.hasPreviousPage)\n })\n })\n\n effect(() => {\n observer.setOptions(options())\n })\n\n onUnmount(() => unsub())\n\n return {\n result: resultSig,\n data: dataSig,\n error: errorSig,\n status: statusSig,\n isPending,\n isLoading,\n isFetching,\n isFetchingNextPage,\n isFetchingPreviousPage,\n isError,\n isSuccess,\n hasNextPage,\n hasPreviousPage,\n fetchNextPage: () => observer.fetchNextPage(),\n fetchPreviousPage: () => observer.fetchPreviousPage(),\n refetch: () => observer.refetch(),\n }\n}\n","import { onUnmount } from '@pyreon/core'\nimport type { Signal } from '@pyreon/reactivity'\nimport { signal } from '@pyreon/reactivity'\nimport type { MutationFilters, QueryFilters } from '@tanstack/query-core'\nimport { useQueryClient } from './query-client'\n\n/**\n * Returns a signal that tracks how many queries are currently in-flight.\n * Useful for global loading indicators.\n *\n * @example\n * const fetching = useIsFetching()\n * // h('span', null, () => fetching() > 0 ? 'Loading…' : '')\n */\nexport function useIsFetching(filters?: QueryFilters): Signal<number> {\n const client = useQueryClient()\n const count = signal(client.isFetching(filters))\n\n const unsub = client.getQueryCache().subscribe(() => {\n count.set(client.isFetching(filters))\n })\n onUnmount(() => unsub())\n\n return count\n}\n\n/**\n * Returns a signal that tracks how many mutations are currently in-flight.\n *\n * @example\n * const mutating = useIsMutating()\n * // h('span', null, () => mutating() > 0 ? 'Saving…' : '')\n */\nexport function useIsMutating(filters?: MutationFilters): Signal<number> {\n const client = useQueryClient()\n const count = signal(client.isMutating(filters))\n\n const unsub = client.getMutationCache().subscribe(() => {\n count.set(client.isMutating(filters))\n })\n onUnmount(() => unsub())\n\n return count\n}\n","import { onUnmount } from '@pyreon/core'\nimport type { Signal } from '@pyreon/reactivity'\nimport { batch, signal } from '@pyreon/reactivity'\nimport type {\n DefaultError,\n MutateFunction,\n MutationObserverOptions,\n MutationObserverResult,\n} from '@tanstack/query-core'\nimport { MutationObserver } from '@tanstack/query-core'\nimport { useQueryClient } from './query-client'\n\nexport interface UseMutationResult<\n TData,\n TError = DefaultError,\n TVariables = void,\n TContext = unknown,\n> {\n /** Raw signal — full observer result. Fine-grained accessors below are preferred. */\n result: Signal<MutationObserverResult<TData, TError, TVariables, TContext>>\n data: Signal<TData | undefined>\n error: Signal<TError | null>\n status: Signal<'idle' | 'pending' | 'success' | 'error'>\n isPending: Signal<boolean>\n isSuccess: Signal<boolean>\n isError: Signal<boolean>\n isIdle: Signal<boolean>\n /** Fire the mutation (fire-and-forget). Errors are captured in the error signal. */\n mutate: (\n variables: TVariables,\n options?: Parameters<MutateFunction<TData, TError, TVariables, TContext>>[1],\n ) => void\n /** Like mutate but returns a promise — use for try/catch error handling. */\n mutateAsync: MutateFunction<TData, TError, TVariables, TContext>\n /** Reset the mutation state back to idle. */\n reset: () => void\n}\n\n/**\n * Run a mutation (create / update / delete). Returns reactive signals for\n * pending / success / error state plus `mutate` and `mutateAsync` functions.\n *\n * @example\n * const mutation = useMutation({\n * mutationFn: (data: CreatePostInput) =>\n * fetch('/api/posts', { method: 'POST', body: JSON.stringify(data) }).then(r => r.json()),\n * onSuccess: () => client.invalidateQueries({ queryKey: ['posts'] }),\n * })\n * // h('button', { onClick: () => mutation.mutate({ title: 'New' }) }, 'Create')\n */\nexport function useMutation<\n TData = unknown,\n TError = DefaultError,\n TVariables = void,\n TContext = unknown,\n>(\n options: MutationObserverOptions<TData, TError, TVariables, TContext>,\n): UseMutationResult<TData, TError, TVariables, TContext> {\n const client = useQueryClient()\n const observer = new MutationObserver<TData, TError, TVariables, TContext>(client, options)\n const initial = observer.getCurrentResult()\n\n // Fine-grained signals: each field is independent so only effects that read\n // e.g. `mutation.isPending()` re-run when isPending changes, not on every update.\n const resultSig = signal<MutationObserverResult<TData, TError, TVariables, TContext>>(initial)\n const dataSig = signal<TData | undefined>(initial.data)\n const errorSig = signal<TError | null>(initial.error ?? null)\n const statusSig = signal<'idle' | 'pending' | 'success' | 'error'>(initial.status)\n const isPending = signal(initial.isPending)\n const isSuccess = signal(initial.isSuccess)\n const isError = signal(initial.isError)\n const isIdle = signal(initial.isIdle)\n\n // batch() coalesces all signal updates into one notification flush.\n const unsub = observer.subscribe((r) => {\n batch(() => {\n resultSig.set(r)\n dataSig.set(r.data)\n errorSig.set(r.error ?? null)\n statusSig.set(r.status)\n isPending.set(r.isPending)\n isSuccess.set(r.isSuccess)\n isError.set(r.isError)\n isIdle.set(r.isIdle)\n })\n })\n\n onUnmount(() => unsub())\n\n return {\n result: resultSig,\n data: dataSig,\n error: errorSig,\n status: statusSig,\n isPending,\n isSuccess,\n isError,\n isIdle,\n mutate: (vars, callbackOptions) => {\n observer.mutate(vars, callbackOptions).catch(() => {\n // Error is already captured in the error signal via the observer subscription.\n // This catch prevents an unhandled promise rejection for fire-and-forget callers.\n })\n },\n mutateAsync: (vars, callbackOptions) => observer.mutate(vars, callbackOptions),\n reset: () => observer.reset(),\n }\n}\n","import { onUnmount } from '@pyreon/core'\nimport type { Signal } from '@pyreon/reactivity'\nimport { effect, signal } from '@pyreon/reactivity'\nimport type {\n DefaultError,\n QueryKey,\n QueryObserverOptions,\n QueryObserverResult,\n} from '@tanstack/query-core'\nimport { QueriesObserver } from '@tanstack/query-core'\nimport { useQueryClient } from './query-client'\n\nexport type UseQueriesOptions<TQueryKey extends QueryKey = QueryKey> = QueryObserverOptions<\n unknown,\n DefaultError,\n unknown,\n unknown,\n TQueryKey\n>\n\n/**\n * Subscribe to multiple queries in parallel. Returns a single signal containing\n * the array of results — index-aligned with the `queries` array.\n *\n * `queries` is a reactive function so signal-based keys trigger re-evaluation\n * automatically.\n *\n * @example\n * const userIds = signal([1, 2, 3])\n * const results = useQueries(() =>\n * userIds().map(id => ({\n * queryKey: ['user', id],\n * queryFn: () => fetchUser(id),\n * }))\n * )\n * // results() — QueryObserverResult[]\n * // results()[0].data — first user\n */\nexport function useQueries(queries: () => UseQueriesOptions[]): Signal<QueryObserverResult[]> {\n const client = useQueryClient()\n const observer = new QueriesObserver(client, queries())\n\n const resultSig = signal(observer.getCurrentResult() as readonly QueryObserverResult[]) as Signal<\n QueryObserverResult[]\n >\n\n const unsub = observer.subscribe((results: readonly QueryObserverResult[]) => {\n resultSig.set(results as QueryObserverResult[])\n })\n\n // When signals inside queries() change, update the observer.\n effect(() => {\n observer.setQueries(queries())\n })\n\n onUnmount(() => {\n unsub()\n observer.destroy()\n })\n\n return resultSig\n}\n","import { onUnmount } from '@pyreon/core'\nimport type { Signal } from '@pyreon/reactivity'\nimport { batch, effect, signal } from '@pyreon/reactivity'\nimport type {\n DefaultError,\n QueryKey,\n QueryObserverOptions,\n QueryObserverResult,\n} from '@tanstack/query-core'\nimport { QueryObserver } from '@tanstack/query-core'\nimport { useQueryClient } from './query-client'\n\nexport interface UseQueryResult<TData, TError = DefaultError> {\n /** Raw signal — the full observer result. Fine-grained accessors below are preferred. */\n result: Signal<QueryObserverResult<TData, TError>>\n data: Signal<TData | undefined>\n error: Signal<TError | null>\n status: Signal<'pending' | 'error' | 'success'>\n isPending: Signal<boolean>\n isLoading: Signal<boolean>\n isFetching: Signal<boolean>\n isError: Signal<boolean>\n isSuccess: Signal<boolean>\n /** Manually trigger a refetch. */\n refetch: () => Promise<QueryObserverResult<TData, TError>>\n}\n\n/**\n * Subscribe to a query. Returns fine-grained reactive signals for data,\n * error and status — each signal only notifies effects that depend on it.\n *\n * `options` is a function so it can read Pyreon signals — when a signal changes\n * (e.g. a reactive query key), the observer is updated and refetches automatically.\n *\n * @example\n * const userId = signal(1)\n * const query = useQuery(() => ({\n * queryKey: ['user', userId()],\n * queryFn: () => fetch(`/api/users/${userId()}`).then(r => r.json()),\n * }))\n * // In template: () => query.data()?.name\n */\nexport function useQuery<TData = unknown, TError = DefaultError, TKey extends QueryKey = QueryKey>(\n options: () => QueryObserverOptions<TData, TError, TData, TData, TKey>,\n): UseQueryResult<TData, TError> {\n const client = useQueryClient()\n const observer = new QueryObserver<TData, TError, TData, TData, TKey>(client, options())\n const initial = observer.getCurrentResult()\n\n // Fine-grained signals: each field is independent so only effects that read\n // e.g. `query.data()` re-run when data changes, not when isFetching flips.\n const resultSig = signal<QueryObserverResult<TData, TError>>(initial)\n const dataSig = signal<TData | undefined>(initial.data)\n const errorSig = signal<TError | null>(initial.error ?? null)\n const statusSig = signal<'pending' | 'error' | 'success'>(initial.status)\n const isPending = signal(initial.isPending)\n const isLoading = signal(initial.isLoading)\n const isFetching = signal(initial.isFetching)\n const isError = signal(initial.isError)\n const isSuccess = signal(initial.isSuccess)\n\n // Subscribe synchronously — data flows before mount (correct for SSR pre-population).\n // batch() coalesces all signal updates into one notification flush.\n const unsub = observer.subscribe((r) => {\n batch(() => {\n resultSig.set(r)\n dataSig.set(r.data)\n errorSig.set(r.error ?? null)\n statusSig.set(r.status)\n isPending.set(r.isPending)\n isLoading.set(r.isLoading)\n isFetching.set(r.isFetching)\n isError.set(r.isError)\n isSuccess.set(r.isSuccess)\n })\n })\n\n // Track reactive options: when signals inside options() change, update the observer.\n // effect() is auto-registered in the component's EffectScope → auto-disposed on unmount.\n effect(() => {\n observer.setOptions(options())\n })\n\n // Unsubscribe the observer on unmount (effect disposal is handled by EffectScope).\n onUnmount(() => unsub())\n\n return {\n result: resultSig,\n data: dataSig,\n error: errorSig,\n status: statusSig,\n isPending,\n isLoading,\n isFetching,\n isError,\n isSuccess,\n refetch: () => observer.refetch(),\n }\n}\n","import type { Props, VNode, VNodeChild } from '@pyreon/core'\nimport { createContext, provide, useContext } from '@pyreon/core'\nimport { useQueryClient } from './query-client'\n\n// ─── Context ────────────────────────────────────────────────────────────────\n\ninterface ErrorResetBoundaryValue {\n reset: () => void\n}\n\nconst QueryErrorResetBoundaryContext = createContext<ErrorResetBoundaryValue | null>(null)\n\n// ─── QueryErrorResetBoundary ─────────────────────────────────────────────────\n\nexport interface QueryErrorResetBoundaryProps extends Props {\n children?: VNodeChild\n}\n\n/**\n * Wraps a subtree so that `useQueryErrorResetBoundary()` descendants can reset\n * all errored queries within this boundary.\n *\n * Pair with Pyreon's `ErrorBoundary` to retry failed queries when the user\n * dismisses the error fallback:\n *\n * @example\n * h(QueryErrorResetBoundary, null,\n * h(ErrorBoundary, {\n * fallback: (err, boundaryReset) => {\n * const { reset } = useQueryErrorResetBoundary()\n * return h('button', {\n * onClick: () => { reset(); boundaryReset() },\n * }, 'Retry')\n * },\n * }, h(MyComponent, null)),\n * )\n */\nexport function QueryErrorResetBoundary(props: QueryErrorResetBoundaryProps): VNode {\n const client = useQueryClient()\n\n const value: ErrorResetBoundaryValue = {\n reset: () => {\n // Reset all active queries that are in error state so they refetch.\n client.refetchQueries({\n predicate: (query) => query.state.status === 'error',\n })\n },\n }\n\n provide(QueryErrorResetBoundaryContext, value)\n\n const ch = props.children\n return (typeof ch === 'function' ? (ch as () => VNodeChild)() : ch) as VNode\n}\n\n// ─── useQueryErrorResetBoundary ──────────────────────────────────────────────\n\n/**\n * Returns the `reset` function provided by the nearest `QueryErrorResetBoundary`.\n * If called outside a boundary, falls back to resetting all errored queries\n * on the current `QueryClient`.\n *\n * @example\n * // Inside an ErrorBoundary fallback:\n * const { reset } = useQueryErrorResetBoundary()\n * h('button', { onClick: () => { reset(); boundaryReset() } }, 'Retry')\n */\nexport function useQueryErrorResetBoundary(): ErrorResetBoundaryValue {\n const boundary = useContext(QueryErrorResetBoundaryContext)\n // Always call useQueryClient to respect hook ordering rules\n const client = useQueryClient()\n\n if (boundary) return boundary\n\n // Fallback: no explicit boundary — use the QueryClient directly.\n return {\n reset: () => {\n client.refetchQueries({\n predicate: (query) => query.state.status === 'error',\n })\n },\n }\n}\n","import { onUnmount } from '@pyreon/core'\nimport type { Signal } from '@pyreon/reactivity'\nimport { batch, effect, signal } from '@pyreon/reactivity'\nimport type { QueryClient } from '@tanstack/query-core'\nimport { useQueryClient } from './query-client'\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport type SSEStatus = 'connecting' | 'connected' | 'disconnected' | 'error'\n\nexport interface UseSSEOptions<T = string> {\n /** EventSource URL — can be a signal for reactive URLs */\n url: string | (() => string)\n /** Named event type(s) to listen for — if omitted, listens to generic `message` events */\n events?: string | string[]\n /** Parse raw event data — e.g. `JSON.parse` for automatic deserialization */\n parse?: (raw: string) => T\n /** Whether the SSE connection is enabled — default: true */\n enabled?: boolean | (() => boolean)\n /** Whether to automatically reconnect — default: true */\n reconnect?: boolean\n /** Initial reconnect delay in ms — doubles on each retry, default: 1000 */\n reconnectDelay?: number\n /** Maximum reconnect attempts — default: 10, 0 = unlimited */\n maxReconnectAttempts?: number\n /** Whether to send cookies with the request — default: false */\n withCredentials?: boolean\n /** Called when a message is received — use queryClient to invalidate or update cache */\n onMessage?: (data: T, queryClient: QueryClient) => void\n /** Called when the EventSource connection opens */\n onOpen?: (event: Event) => void\n /** Called when a connection error occurs */\n onError?: (event: Event) => void\n}\n\nexport interface UseSSEResult<T> {\n /** Last received message data */\n data: Signal<T | null>\n /** Current connection status */\n status: Signal<SSEStatus>\n /** Last error event */\n error: Signal<Event | null>\n /** Last `id` field received from the server (per SSE spec) */\n lastEventId: () => string\n /** EventSource readyState: 0=CONNECTING, 1=OPEN, 2=CLOSED */\n readyState: () => number\n /** Manually close the connection */\n close: () => void\n /** Manually reconnect */\n reconnect: () => void\n}\n\n// ─── useSSE ─────────────────────────────────────────────────────────────────\n\n/**\n * Reactive Server-Sent Events hook that integrates with TanStack Query.\n * Automatically manages connection lifecycle, reconnection, and cleanup.\n *\n * Use the `onMessage` callback to invalidate or update query cache\n * when the server pushes data.\n *\n * @example\n * ```ts\n * const sse = useSSE({\n * url: '/api/events',\n * parse: JSON.parse,\n * onMessage: (data, queryClient) => {\n * if (data.type === 'order-updated') {\n * queryClient.invalidateQueries({ queryKey: ['orders'] })\n * }\n * },\n * })\n * // sse.data() — last received message (parsed)\n * // sse.status() — 'connecting' | 'connected' | 'disconnected' | 'error'\n * // sse.error() — last error event or null\n * ```\n */\nexport function useSSE<T = string>(options: UseSSEOptions<T>): UseSSEResult<T> {\n const queryClient = useQueryClient()\n const data = signal<T | null>(null)\n const status = signal<SSEStatus>('disconnected')\n const error = signal<Event | null>(null)\n const lastEventId = signal('')\n const readyState = signal<number>(2) // Start as CLOSED until connected\n\n let es: EventSource | null = null\n let reconnectAttempts = 0\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let intentionalClose = false\n\n const reconnectEnabled = options.reconnect !== false\n const baseDelay = options.reconnectDelay ?? 1000\n const maxAttempts = options.maxReconnectAttempts ?? 10\n const eventNames = options.events\n ? Array.isArray(options.events)\n ? options.events\n : [options.events]\n : null\n\n function getUrl(): string {\n return typeof options.url === 'function' ? options.url() : options.url\n }\n\n function isEnabled(): boolean {\n if (options.enabled === undefined) return true\n return typeof options.enabled === 'function' ? options.enabled() : options.enabled\n }\n\n function handleMessage(event: MessageEvent): void {\n try {\n // Track lastEventId from the SSE spec\n if (event.lastEventId !== undefined && event.lastEventId !== '') {\n lastEventId.set(event.lastEventId)\n }\n const parsed = options.parse ? options.parse(event.data as string) : (event.data as T)\n batch(() => {\n data.set(parsed)\n error.set(null)\n })\n options.onMessage?.(parsed, queryClient)\n } catch {\n // Message handler errors should not crash the subscription\n }\n }\n\n function attachListeners(source: EventSource): void {\n if (eventNames) {\n for (const name of eventNames) {\n source.addEventListener(name, handleMessage as EventListener)\n }\n } else {\n source.onmessage = handleMessage\n }\n }\n\n function removeListeners(source: EventSource): void {\n source.onopen = null\n source.onmessage = null\n source.onerror = null\n\n if (eventNames) {\n for (const name of eventNames) {\n source.removeEventListener(name, handleMessage as EventListener)\n }\n }\n }\n\n function handleError(event: Event): void {\n status.set('error')\n error.set(event)\n readyState.set(es?.readyState ?? EventSource.CLOSED)\n options.onError?.(event)\n\n // EventSource auto-reconnects for transient errors, but if readyState is CLOSED\n // the browser has given up and we need to handle reconnection ourselves\n if (es?.readyState === EventSource.CLOSED) {\n removeListeners(es)\n es.close()\n es = null\n if (!intentionalClose && reconnectEnabled) {\n scheduleReconnect()\n }\n }\n }\n\n function connect(): void {\n // Clean up existing connection\n if (es) {\n removeListeners(es)\n es.close()\n es = null\n }\n\n if (!isEnabled()) {\n status.set('disconnected')\n return\n }\n\n status.set('connecting')\n\n try {\n es = new EventSource(getUrl(), {\n withCredentials: options.withCredentials ?? false,\n })\n readyState.set(EventSource.CONNECTING)\n } catch {\n status.set('error')\n readyState.set(EventSource.CLOSED)\n scheduleReconnect()\n return\n }\n\n es.onopen = (event: Event) => {\n batch(() => {\n status.set('connected')\n error.set(null)\n readyState.set(EventSource.OPEN)\n reconnectAttempts = 0\n })\n options.onOpen?.(event)\n }\n\n attachListeners(es)\n es.onerror = handleError\n }\n\n function scheduleReconnect(): void {\n if (!reconnectEnabled) return\n if (maxAttempts > 0 && reconnectAttempts >= maxAttempts) return\n\n const delay = baseDelay * 2 ** reconnectAttempts\n reconnectAttempts++\n\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null\n if (!intentionalClose && isEnabled()) {\n connect()\n }\n }, delay)\n }\n\n function close(): void {\n intentionalClose = true\n if (reconnectTimer !== null) {\n clearTimeout(reconnectTimer)\n reconnectTimer = null\n }\n if (es) {\n removeListeners(es)\n es.close()\n es = null\n }\n status.set('disconnected')\n readyState.set(EventSource.CLOSED)\n }\n\n function manualReconnect(): void {\n intentionalClose = false\n reconnectAttempts = 0\n connect()\n }\n\n // Track reactive URL and enabled state\n effect(() => {\n // Read reactive values to subscribe to changes\n if (typeof options.url === 'function') options.url()\n if (typeof options.enabled === 'function') options.enabled()\n\n intentionalClose = false\n reconnectAttempts = 0\n connect()\n })\n\n // Cleanup on unmount\n onUnmount(() => close())\n\n return {\n data,\n status,\n error,\n lastEventId: () => lastEventId(),\n readyState: () => readyState(),\n close,\n reconnect: manualReconnect,\n }\n}\n","import { onUnmount } from '@pyreon/core'\nimport type { Signal } from '@pyreon/reactivity'\nimport { batch, effect, signal } from '@pyreon/reactivity'\nimport type { QueryClient } from '@tanstack/query-core'\nimport { useQueryClient } from './query-client'\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport type SubscriptionStatus = 'connecting' | 'connected' | 'disconnected' | 'error'\n\nexport interface UseSubscriptionOptions {\n /** WebSocket URL — can be a signal for reactive URLs */\n url: string | (() => string)\n /** WebSocket sub-protocols */\n protocols?: string | string[]\n /** Called when a message is received — use queryClient to invalidate or update cache */\n onMessage: (event: MessageEvent, queryClient: QueryClient) => void\n /** Called when the connection opens */\n onOpen?: (event: Event) => void\n /** Called when the connection closes */\n onClose?: (event: CloseEvent) => void\n /** Called when a connection error occurs */\n onError?: (event: Event) => void\n /** Whether to automatically reconnect — default: true */\n reconnect?: boolean\n /** Initial reconnect delay in ms — doubles on each retry, default: 1000 */\n reconnectDelay?: number\n /** Maximum reconnect attempts — default: 10, 0 = unlimited */\n maxReconnectAttempts?: number\n /** Whether the subscription is enabled — default: true */\n enabled?: boolean | (() => boolean)\n}\n\nexport interface UseSubscriptionResult {\n /** Current connection status */\n status: Signal<SubscriptionStatus>\n /** Send data through the WebSocket */\n send: (data: string | Blob | BufferSource) => void\n /** Manually close the connection */\n close: () => void\n /** Manually reconnect */\n reconnect: () => void\n}\n\n// ─── useSubscription ─────────────────────────────────────────────────────────\n\n/**\n * Reactive WebSocket subscription that integrates with TanStack Query.\n * Automatically manages connection lifecycle, reconnection, and cleanup.\n *\n * Use the `onMessage` callback to invalidate or update query cache\n * when the server pushes data.\n *\n * @example\n * ```ts\n * const sub = useSubscription({\n * url: 'wss://api.example.com/ws',\n * onMessage: (event, queryClient) => {\n * const data = JSON.parse(event.data)\n * if (data.type === 'order-updated') {\n * queryClient.invalidateQueries({ queryKey: ['orders'] })\n * }\n * },\n * })\n * // sub.status() — 'connecting' | 'connected' | 'disconnected' | 'error'\n * // sub.send(JSON.stringify({ type: 'subscribe', channel: 'orders' }))\n * ```\n */\nexport function useSubscription(options: UseSubscriptionOptions): UseSubscriptionResult {\n const queryClient = useQueryClient()\n const status = signal<SubscriptionStatus>('disconnected')\n\n let ws: WebSocket | null = null\n let reconnectAttempts = 0\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let intentionalClose = false\n\n const reconnectEnabled = options.reconnect !== false\n const baseDelay = options.reconnectDelay ?? 1000\n const maxAttempts = options.maxReconnectAttempts ?? 10\n\n function getUrl(): string {\n return typeof options.url === 'function' ? options.url() : options.url\n }\n\n function isEnabled(): boolean {\n if (options.enabled === undefined) return true\n return typeof options.enabled === 'function' ? options.enabled() : options.enabled\n }\n\n function connect(): void {\n if (ws) {\n ws.onopen = null\n ws.onmessage = null\n ws.onclose = null\n ws.onerror = null\n if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {\n ws.close()\n }\n }\n\n if (!isEnabled()) {\n status.set('disconnected')\n return\n }\n\n status.set('connecting')\n\n try {\n ws = options.protocols ? new WebSocket(getUrl(), options.protocols) : new WebSocket(getUrl())\n } catch {\n status.set('error')\n scheduleReconnect()\n return\n }\n\n ws.onopen = (event) => {\n batch(() => {\n status.set('connected')\n reconnectAttempts = 0\n })\n options.onOpen?.(event)\n }\n\n ws.onmessage = (event) => {\n try {\n options.onMessage(event, queryClient)\n } catch {\n // Message handler errors should not crash the subscription\n }\n }\n\n ws.onclose = (event) => {\n status.set('disconnected')\n options.onClose?.(event)\n\n if (!intentionalClose && reconnectEnabled) {\n scheduleReconnect()\n }\n }\n\n ws.onerror = (event) => {\n status.set('error')\n options.onError?.(event)\n }\n }\n\n function scheduleReconnect(): void {\n if (!reconnectEnabled) return\n if (maxAttempts > 0 && reconnectAttempts >= maxAttempts) return\n\n const delay = baseDelay * 2 ** reconnectAttempts\n reconnectAttempts++\n\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null\n if (!intentionalClose && isEnabled()) {\n connect()\n }\n }, delay)\n }\n\n function send(data: string | Blob | BufferSource): void {\n if (ws?.readyState === WebSocket.OPEN) {\n ws.send(data)\n }\n }\n\n function close(): void {\n intentionalClose = true\n if (reconnectTimer !== null) {\n clearTimeout(reconnectTimer)\n reconnectTimer = null\n }\n if (ws) {\n ws.onopen = null\n ws.onmessage = null\n ws.onclose = null\n ws.onerror = null\n if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {\n ws.close()\n }\n ws = null\n }\n status.set('disconnected')\n }\n\n function manualReconnect(): void {\n intentionalClose = false\n reconnectAttempts = 0\n connect()\n }\n\n // Track reactive URL and enabled state\n effect(() => {\n // Read reactive values to subscribe to changes\n if (typeof options.url === 'function') options.url()\n if (typeof options.enabled === 'function') options.enabled()\n\n intentionalClose = false\n reconnectAttempts = 0\n connect()\n })\n\n // Cleanup on unmount\n onUnmount(() => close())\n\n return {\n status,\n send,\n close,\n reconnect: manualReconnect,\n }\n}\n","import type { VNodeChild, VNodeChildAtom } from '@pyreon/core'\nimport { onUnmount } from '@pyreon/core'\nimport type { Signal } from '@pyreon/reactivity'\nimport { batch, effect, signal } from '@pyreon/reactivity'\nimport type {\n DefaultError,\n InfiniteData,\n InfiniteQueryObserverOptions,\n InfiniteQueryObserverResult,\n QueryKey,\n QueryObserverOptions,\n QueryObserverResult,\n} from '@tanstack/query-core'\nimport { InfiniteQueryObserver, QueryObserver } from '@tanstack/query-core'\nimport { useQueryClient } from './query-client'\n\n// ─── Types ─────────────────────────────────────────────────────────────────\n\n/**\n * Like `UseQueryResult` but `data` is `Signal<TData>` (never undefined).\n * Only use inside a `QuerySuspense` boundary which guarantees the query has\n * succeeded before children are rendered.\n */\nexport interface UseSuspenseQueryResult<TData, TError = DefaultError> {\n result: Signal<QueryObserverResult<TData, TError>>\n /** Always TData — never undefined inside a QuerySuspense boundary. */\n data: Signal<TData>\n error: Signal<TError | null>\n status: Signal<'pending' | 'error' | 'success'>\n isPending: Signal<boolean>\n isFetching: Signal<boolean>\n isError: Signal<boolean>\n isSuccess: Signal<boolean>\n refetch: () => Promise<QueryObserverResult<TData, TError>>\n}\n\nexport interface UseSuspenseInfiniteQueryResult<TQueryFnData, TError = DefaultError> {\n result: Signal<InfiniteQueryObserverResult<InfiniteData<TQueryFnData>, TError>>\n /** Always InfiniteData<TQueryFnData> — never undefined inside a QuerySuspense boundary. */\n data: Signal<InfiniteData<TQueryFnData>>\n error: Signal<TError | null>\n status: Signal<'pending' | 'error' | 'success'>\n isFetching: Signal<boolean>\n isFetchingNextPage: Signal<boolean>\n isFetchingPreviousPage: Signal<boolean>\n isError: Signal<boolean>\n isSuccess: Signal<boolean>\n hasNextPage: Signal<boolean>\n hasPreviousPage: Signal<boolean>\n fetchNextPage: () => Promise<InfiniteQueryObserverResult<InfiniteData<TQueryFnData>, TError>>\n fetchPreviousPage: () => Promise<InfiniteQueryObserverResult<InfiniteData<TQueryFnData>, TError>>\n refetch: () => Promise<QueryObserverResult<InfiniteData<TQueryFnData>, TError>>\n}\n\n// ─── QuerySuspense ──────────────────────────────────────────────────────────\n\ntype AnyQueryLike = {\n isPending: Signal<boolean>\n isError: Signal<boolean>\n error: Signal<unknown>\n}\n\nexport interface QuerySuspenseProps {\n /**\n * A single query result (or array of them) to gate on.\n * Children only render when ALL queries have succeeded.\n */\n query: AnyQueryLike | AnyQueryLike[]\n /** Rendered while any query is pending. */\n fallback?: VNodeChild\n /** Rendered when any query has errored. Defaults to re-throwing to nearest ErrorBoundary. */\n error?: (err: unknown) => VNodeChild\n children: VNodeChild\n}\n\n/**\n * Pyreon-native Suspense boundary for queries. Shows `fallback` while any query\n * is pending. On error, renders the `error` fallback or re-throws to the\n * nearest Pyreon `ErrorBoundary`.\n *\n * Pair with `useSuspenseQuery` / `useSuspenseInfiniteQuery` to get non-undefined\n * `data` types inside children.\n *\n * @example\n * const userQuery = useSuspenseQuery(() => ({ queryKey: ['user'], queryFn: fetchUser }))\n *\n * h(QuerySuspense, {\n * query: userQuery,\n * fallback: h(Spinner, null),\n * error: (err) => h('p', null, `Failed: ${err}`),\n * }, () => h(UserProfile, { user: userQuery.data() }))\n */\nexport function QuerySuspense(props: QuerySuspenseProps): VNodeChild {\n return (): VNodeChildAtom => {\n const queries = Array.isArray(props.query) ? props.query : [props.query]\n\n // Error state — use provided error fallback or re-throw to ErrorBoundary\n for (const q of queries) {\n if (q.isError()) {\n const err = q.error()\n if (props.error) {\n return props.error(err) as VNodeChildAtom\n }\n throw err\n }\n }\n\n // Pending state — show fallback\n if (queries.some((q) => q.isPending())) {\n const fb = props.fallback\n return (\n typeof fb === 'function' ? (fb as () => VNodeChildAtom)() : (fb ?? null)\n ) as VNodeChildAtom\n }\n\n // All success — render children\n const ch = props.children\n return (typeof ch === 'function' ? (ch as () => VNodeChildAtom)() : ch) as VNodeChildAtom\n }\n}\n\n// ─── useSuspenseQuery ───────────────────────────────────────────────────────\n\n/**\n * Like `useQuery` but `data` is typed as `Signal<TData>` (never undefined).\n * Designed for use inside a `QuerySuspense` boundary, which guarantees\n * children only render after the query succeeds.\n *\n * @example\n * const user = useSuspenseQuery(() => ({ queryKey: ['user', id()], queryFn: fetchUser }))\n *\n * h(QuerySuspense, { query: user, fallback: h(Spinner, null) },\n * () => h(UserCard, { name: user.data().name }),\n * )\n */\nexport function useSuspenseQuery<\n TData = unknown,\n TError = DefaultError,\n TKey extends QueryKey = QueryKey,\n>(\n options: () => QueryObserverOptions<TData, TError, TData, TData, TKey>,\n): UseSuspenseQueryResult<TData, TError> {\n const client = useQueryClient()\n const observer = new QueryObserver<TData, TError, TData, TData, TKey>(client, options())\n const initial = observer.getCurrentResult()\n\n const resultSig = signal<QueryObserverResult<TData, TError>>(initial)\n const dataSig = signal<TData>(initial.data as TData)\n const errorSig = signal<TError | null>(initial.error ?? null)\n const statusSig = signal<'pending' | 'error' | 'success'>(initial.status)\n const isPending = signal(initial.isPending)\n const isFetching = signal(initial.isFetching)\n const isError = signal(initial.isError)\n const isSuccess = signal(initial.isSuccess)\n\n const unsub = observer.subscribe((r) => {\n batch(() => {\n resultSig.set(r)\n if (r.data !== undefined) dataSig.set(r.data as TData)\n errorSig.set(r.error ?? null)\n statusSig.set(r.status)\n isPending.set(r.isPending)\n isFetching.set(r.isFetching)\n isError.set(r.isError)\n isSuccess.set(r.isSuccess)\n })\n })\n\n effect(() => {\n observer.setOptions(options())\n })\n onUnmount(() => unsub())\n\n return {\n result: resultSig,\n data: dataSig,\n error: errorSig,\n status: statusSig,\n isPending,\n isFetching,\n isError,\n isSuccess,\n refetch: () => observer.refetch(),\n }\n}\n\n// ─── useSuspenseInfiniteQuery ───────────────────────────────────────────────\n\n/**\n * Like `useInfiniteQuery` but `data` is typed as `Signal<InfiniteData<TData>>`\n * (never undefined). Use inside a `QuerySuspense` boundary.\n */\nexport function useSuspenseInfiniteQuery<\n TQueryFnData = unknown,\n TError = DefaultError,\n TQueryKey extends QueryKey = QueryKey,\n TPageParam = unknown,\n>(\n options: () => InfiniteQueryObserverOptions<\n TQueryFnData,\n TError,\n InfiniteData<TQueryFnData>,\n TQueryKey,\n TPageParam\n >,\n): UseSuspenseInfiniteQueryResult<TQueryFnData, TError> {\n const client = useQueryClient()\n const observer = new InfiniteQueryObserver<\n TQueryFnData,\n TError,\n InfiniteData<TQueryFnData>,\n TQueryKey,\n TPageParam\n >(client, options())\n const initial = observer.getCurrentResult()\n\n const resultSig = signal(initial)\n const dataSig = signal(initial.data as InfiniteData<TQueryFnData>)\n const errorSig = signal<TError | null>(initial.error ?? null)\n const statusSig = signal(initial.status)\n const isFetching = signal(initial.isFetching)\n const isFetchingNextPage = signal(initial.isFetchingNextPage)\n const isFetchingPreviousPage = signal(initial.isFetchingPreviousPage)\n const isError = signal(initial.isError)\n const isSuccess = signal(initial.isSuccess)\n const hasNextPage = signal(initial.hasNextPage)\n const hasPreviousPage = signal(initial.hasPreviousPage)\n\n const unsub = observer.subscribe((r) => {\n batch(() => {\n resultSig.set(r)\n if (r.data !== undefined) dataSig.set(r.data)\n errorSig.set(r.error ?? null)\n statusSig.set(r.status)\n isFetching.set(r.isFetching)\n isFetchingNextPage.set(r.isFetchingNextPage)\n isFetchingPreviousPage.set(r.isFetchingPreviousPage)\n isError.set(r.isError)\n isSuccess.set(r.isSuccess)\n hasNextPage.set(r.hasNextPage)\n hasPreviousPage.set(r.hasPreviousPage)\n })\n })\n\n effect(() => {\n observer.setOptions(options())\n })\n onUnmount(() => unsub())\n\n return {\n result: resultSig,\n data: dataSig,\n error: errorSig,\n status: statusSig,\n isFetching,\n isFetchingNextPage,\n isFetchingPreviousPage,\n isError,\n isSuccess,\n hasNextPage,\n hasPreviousPage,\n fetchNextPage: () => observer.fetchNextPage(),\n fetchPreviousPage: () => observer.fetchPreviousPage(),\n refetch: () => observer.refetch(),\n }\n}\n"],"mappings":"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"}
|
package/lib/types/index.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ interface UseInfiniteQueryResult<TQueryFnData, TError = DefaultError> {
|
|
|
30
30
|
result: Signal<InfiniteQueryObserverResult<InfiniteData<TQueryFnData>, TError>>;
|
|
31
31
|
data: Signal<InfiniteData<TQueryFnData> | undefined>;
|
|
32
32
|
error: Signal<TError | null>;
|
|
33
|
-
status: Signal<
|
|
33
|
+
status: Signal<'pending' | 'error' | 'success'>;
|
|
34
34
|
isPending: Signal<boolean>;
|
|
35
35
|
isLoading: Signal<boolean>;
|
|
36
36
|
isFetching: Signal<boolean>;
|
|
@@ -86,7 +86,7 @@ interface UseMutationResult<TData, TError = DefaultError, TVariables = void, TCo
|
|
|
86
86
|
result: Signal<MutationObserverResult<TData, TError, TVariables, TContext>>;
|
|
87
87
|
data: Signal<TData | undefined>;
|
|
88
88
|
error: Signal<TError | null>;
|
|
89
|
-
status: Signal<
|
|
89
|
+
status: Signal<'idle' | 'pending' | 'success' | 'error'>;
|
|
90
90
|
isPending: Signal<boolean>;
|
|
91
91
|
isSuccess: Signal<boolean>;
|
|
92
92
|
isError: Signal<boolean>;
|
|
@@ -140,7 +140,7 @@ interface UseQueryResult<TData, TError = DefaultError> {
|
|
|
140
140
|
result: Signal<QueryObserverResult<TData, TError>>;
|
|
141
141
|
data: Signal<TData | undefined>;
|
|
142
142
|
error: Signal<TError | null>;
|
|
143
|
-
status: Signal<
|
|
143
|
+
status: Signal<'pending' | 'error' | 'success'>;
|
|
144
144
|
isPending: Signal<boolean>;
|
|
145
145
|
isLoading: Signal<boolean>;
|
|
146
146
|
isFetching: Signal<boolean>;
|
|
@@ -206,7 +206,7 @@ declare function QueryErrorResetBoundary(props: QueryErrorResetBoundaryProps): V
|
|
|
206
206
|
declare function useQueryErrorResetBoundary(): ErrorResetBoundaryValue;
|
|
207
207
|
//#endregion
|
|
208
208
|
//#region src/use-sse.d.ts
|
|
209
|
-
type SSEStatus =
|
|
209
|
+
type SSEStatus = 'connecting' | 'connected' | 'disconnected' | 'error';
|
|
210
210
|
interface UseSSEOptions<T = string> {
|
|
211
211
|
/** EventSource URL — can be a signal for reactive URLs */
|
|
212
212
|
url: string | (() => string);
|
|
@@ -273,7 +273,7 @@ interface UseSSEResult<T> {
|
|
|
273
273
|
declare function useSSE<T = string>(options: UseSSEOptions<T>): UseSSEResult<T>;
|
|
274
274
|
//#endregion
|
|
275
275
|
//#region src/use-subscription.d.ts
|
|
276
|
-
type SubscriptionStatus =
|
|
276
|
+
type SubscriptionStatus = 'connecting' | 'connected' | 'disconnected' | 'error';
|
|
277
277
|
interface UseSubscriptionOptions {
|
|
278
278
|
/** WebSocket URL — can be a signal for reactive URLs */
|
|
279
279
|
url: string | (() => string);
|
|
@@ -341,7 +341,7 @@ interface UseSuspenseQueryResult<TData, TError = DefaultError> {
|
|
|
341
341
|
/** Always TData — never undefined inside a QuerySuspense boundary. */
|
|
342
342
|
data: Signal<TData>;
|
|
343
343
|
error: Signal<TError | null>;
|
|
344
|
-
status: Signal<
|
|
344
|
+
status: Signal<'pending' | 'error' | 'success'>;
|
|
345
345
|
isPending: Signal<boolean>;
|
|
346
346
|
isFetching: Signal<boolean>;
|
|
347
347
|
isError: Signal<boolean>;
|
|
@@ -353,7 +353,7 @@ interface UseSuspenseInfiniteQueryResult<TQueryFnData, TError = DefaultError> {
|
|
|
353
353
|
/** Always InfiniteData<TQueryFnData> — never undefined inside a QuerySuspense boundary. */
|
|
354
354
|
data: Signal<InfiniteData<TQueryFnData>>;
|
|
355
355
|
error: Signal<TError | null>;
|
|
356
|
-
status: Signal<
|
|
356
|
+
status: Signal<'pending' | 'error' | 'success'>;
|
|
357
357
|
isFetching: Signal<boolean>;
|
|
358
358
|
isFetchingNextPage: Signal<boolean>;
|
|
359
359
|
isFetchingPreviousPage: Signal<boolean>;
|