@tiesen/effect-tanstack-query 0.0.7 → 0.0.9
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 +53 -27
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/react.d.mts +1 -1
- package/dist/react.mjs +1 -1
- package/dist/{types-BGHsAv5y.d.mts → types-CcB8FTUv.d.mts} +5 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
# effect-tanstack-query
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="https://npmx.dev/package/@tiesen/effect-tanstack-query"><img src="https://img.shields.io/npm/dw/%40tiesen%2Feffect-tanstack-query" alt="Total Downloads"></a>
|
|
5
|
+
<a href="https://npmx.dev/package/@tiesen/effect-tanstack-query"><img src="https://img.shields.io/npm/v/%40tiesen%2Feffect-tanstack-query" alt="Latest Stable Version"></a>
|
|
6
|
+
<a href="https://npmx.dev/package/@tiesen/effect-tanstack-query"><img src="https://img.shields.io/npm/l/%40tiesen%2Feffect-tanstack-query" alt="License"></a>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
Effortlessly bridge **Effect HTTP API clients** with **TanStack Query**. Enjoy end-to-end type safety, zero-boilerplate options (`queryOptions`, `mutationOptions`, `subscriptionOptions`), and built-in streaming support out of the box.
|
|
4
10
|
|
|
5
11
|
## Features
|
|
6
12
|
|
|
7
13
|
- **Seamless Integration**: Effortlessly map Effect `HttpApiClient` endpoints to TanStack Query config objects.
|
|
8
|
-
- **End-to-End Type Safety**: Automatically infers inputs (`params`, `query`, `
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
14
|
+
- **End-to-End Type Safety**: Automatically infers inputs (`params`, `query`, `payload`), response types, and error types directly from your Effect schemas.
|
|
15
|
+
- **Event Streaming Support**: Native handling for live streams and Server-Sent Events (SSE) via `subscriptionOptions` and `useSubscription`.
|
|
16
|
+
- **Zero Boilerplate**: Generates `queryKey`, and execution logic under the hood using ES6 Proxies.
|
|
17
|
+
- **Smart Developer Experience**: Auto-manages Fiber lifecycles and abort signals, with smart optional parameters when no inputs are required.
|
|
11
18
|
|
|
12
19
|
## Installation
|
|
13
20
|
|
|
@@ -15,16 +22,16 @@ Install using your preferred package manager:
|
|
|
15
22
|
|
|
16
23
|
```bash
|
|
17
24
|
# Using npm
|
|
18
|
-
npm install @tiesen/effect-tanstack-query
|
|
25
|
+
npm install @tiesen/effect-tanstack-query @tanstack/react-query effect@beta
|
|
19
26
|
|
|
20
27
|
# Using yarn
|
|
21
|
-
yarn add @tiesen/effect-tanstack-query
|
|
28
|
+
yarn add @tiesen/effect-tanstack-query @tanstack/react-query effect@beta
|
|
22
29
|
|
|
23
30
|
# Using bun
|
|
24
|
-
bun add @tiesen/effect-tanstack-query
|
|
31
|
+
bun add @tiesen/effect-tanstack-query @tanstack/react-query effect@beta
|
|
25
32
|
|
|
26
33
|
# Using pnpm
|
|
27
|
-
pnpm add @tiesen/effect-tanstack-query
|
|
34
|
+
pnpm add @tiesen/effect-tanstack-query @tanstack/react-query effect@beta
|
|
28
35
|
```
|
|
29
36
|
|
|
30
37
|
## Quick Start
|
|
@@ -33,9 +40,14 @@ pnpm add @tiesen/effect-tanstack-query
|
|
|
33
40
|
|
|
34
41
|
```ts
|
|
35
42
|
import { Schema } from 'effect'
|
|
36
|
-
import {
|
|
43
|
+
import {
|
|
44
|
+
HttpApi,
|
|
45
|
+
HttpApiEndpoint,
|
|
46
|
+
HttpApiGroup,
|
|
47
|
+
HttpApiSchema,
|
|
48
|
+
} from 'effect/unstable/httpapi'
|
|
37
49
|
|
|
38
|
-
class ApiGroup extends HttpApiGroup.make('
|
|
50
|
+
class ApiGroup extends HttpApiGroup.make('group')
|
|
39
51
|
.add(
|
|
40
52
|
HttpApiEndpoint.get('hello', '/hello/:name', {
|
|
41
53
|
success: Schema.String,
|
|
@@ -54,6 +66,13 @@ class ApiGroup extends HttpApiGroup.make('ApiGroup')
|
|
|
54
66
|
name: Schema.String,
|
|
55
67
|
}),
|
|
56
68
|
})
|
|
69
|
+
)
|
|
70
|
+
.add(
|
|
71
|
+
HttpApiEndpoint.get('stream', '/stream', {
|
|
72
|
+
success: HttpApiSchema.StreamSse({
|
|
73
|
+
data: Schema.String,
|
|
74
|
+
}),
|
|
75
|
+
})
|
|
57
76
|
) {}
|
|
58
77
|
|
|
59
78
|
export class Api extends HttpApi.make('Api').add(ApiGroup) {}
|
|
@@ -62,27 +81,18 @@ export class Api extends HttpApi.make('Api').add(ApiGroup) {}
|
|
|
62
81
|
### 2. Create an Effect HTTP API Client
|
|
63
82
|
|
|
64
83
|
```ts
|
|
65
|
-
import { Context,
|
|
66
|
-
import {
|
|
67
|
-
FetchHttpClient,
|
|
68
|
-
HttpClient,
|
|
69
|
-
HttpClientRequest,
|
|
70
|
-
} from 'effect/unstable/http'
|
|
84
|
+
import { Context, Layer } from 'effect'
|
|
85
|
+
import { FetchHttpClient } from 'effect/unstable/http'
|
|
71
86
|
import { HttpApiClient } from 'effect/unstable/httpapi'
|
|
72
87
|
|
|
73
88
|
class ApiClient extends Context.Service<
|
|
74
89
|
ApiClient,
|
|
75
90
|
HttpApiClient.ForApi<typeof Api>
|
|
76
91
|
>()('ApiClient') {
|
|
77
|
-
public static
|
|
92
|
+
public static layer = Layer.effect(
|
|
78
93
|
this,
|
|
79
94
|
HttpApiClient.make(Api, {
|
|
80
|
-
|
|
81
|
-
client.pipe(
|
|
82
|
-
HttpClient.mapRequest(
|
|
83
|
-
Function.flow(HttpClientRequest.prependUrl('http://localhost:3000'))
|
|
84
|
-
)
|
|
85
|
-
),
|
|
95
|
+
baseUrl: 'http://localhost:3000',
|
|
86
96
|
})
|
|
87
97
|
).pipe(Layer.provide(FetchHttpClient.layer))
|
|
88
98
|
}
|
|
@@ -94,7 +104,7 @@ class ApiClient extends Context.Service<
|
|
|
94
104
|
import { ManagedRuntime } from 'effect'
|
|
95
105
|
import { createTanstackQueryOptionsProxy } from '@tiesen/effect-tanstack-query'
|
|
96
106
|
|
|
97
|
-
const runtime = ManagedRuntime.make(ApiClient.
|
|
107
|
+
const runtime = ManagedRuntime.make(ApiClient.layer)
|
|
98
108
|
const api = createTanstackQueryOptionsProxy(ApiClient, runtime)
|
|
99
109
|
```
|
|
100
110
|
|
|
@@ -102,26 +112,42 @@ const api = createTanstackQueryOptionsProxy(ApiClient, runtime)
|
|
|
102
112
|
|
|
103
113
|
```ts
|
|
104
114
|
import { useMutation, useQueryClient, useQuery } from '@tanstack/react-query'
|
|
115
|
+
import { useSubscription } from '@tiesen/effect-tanstack-query/react'
|
|
105
116
|
|
|
106
117
|
// Query Example
|
|
107
118
|
const query = useQuery(
|
|
108
|
-
api.
|
|
119
|
+
api.group.hello.queryOptions({
|
|
109
120
|
params: { name: 'John' },
|
|
110
121
|
query: { greeting: 'Hi' },
|
|
111
122
|
})
|
|
112
123
|
)
|
|
113
124
|
|
|
114
125
|
// Mutation Example
|
|
115
|
-
const mutation = useMutation(api.
|
|
126
|
+
const mutation = useMutation(api.group.goodbye.mutationOptions())
|
|
116
127
|
mutation.mutate({ name: 'John' })
|
|
117
128
|
|
|
129
|
+
// Stream Example
|
|
130
|
+
useSubscription(
|
|
131
|
+
api.group.stream.subscriptionOptions(undefined, {
|
|
132
|
+
onData: (data) => console.log('Received data:', data),
|
|
133
|
+
})
|
|
134
|
+
)
|
|
135
|
+
|
|
118
136
|
// Invalidate Query Example
|
|
119
137
|
const queryClient = useQueryClient()
|
|
120
138
|
void queryClient.invalidateQueries({
|
|
121
|
-
queryKey: api.
|
|
139
|
+
queryKey: api.group.hello.getQueryKey(),
|
|
122
140
|
})
|
|
123
141
|
```
|
|
124
142
|
|
|
143
|
+
## Downloads
|
|
144
|
+
|
|
145
|
+
<picture>
|
|
146
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://npmx.dev/api/embed/downloads.svg?packages=%40tiesen%2Feffect-tanstack-query&metric=downloads&startDate=2025-08-01&endDate=2026-07-30&mode=dark&granularity=weekly&locale=en-US&accent=oklch%2867.3%25+0+0%29&yLabel=Weekly+Downloads">
|
|
147
|
+
<source media="(prefers-color-scheme: light)" srcset="https://npmx.dev/api/embed/downloads.svg?packages=%40tiesen%2Feffect-tanstack-query&metric=downloads&startDate=2025-08-01&endDate=2026-07-30&mode=light&granularity=weekly&locale=en-US&accent=oklch%2867.3%25+0+0%29&yLabel=Weekly+Downloads">
|
|
148
|
+
<img alt="npmx downloads" src="https://npmx.dev/api/embed/downloads.svg?packages=%40tiesen%2Feffect-tanstack-query&metric=downloads&startDate=2025-08-01&endDate=2026-07-30&mode=light&granularity=weekly&locale=en-US&accent=oklch%2867.3%25+0+0%29&yLabel=Weekly+Downloads">
|
|
149
|
+
</picture>
|
|
150
|
+
|
|
125
151
|
## License
|
|
126
152
|
|
|
127
153
|
This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for details.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as TanstackQueryOptionsProxy } from "./types-
|
|
1
|
+
import { r as TanstackQueryOptionsProxy } from "./types-CcB8FTUv.mjs";
|
|
2
2
|
import { Service } from "effect/Context";
|
|
3
3
|
import { ManagedRuntime } from "effect/ManagedRuntime";
|
|
4
4
|
//#region src/index.d.ts
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"effect/Effect";import*as t from"effect/Stream";function
|
|
1
|
+
import*as e from"effect/Effect";import*as t from"effect/Fiber";import*as n from"effect/Schedule";import*as r from"effect/Stream";function i(i,a){let o=new Map,s=c=>{let l=c.join(`.`);if(o.has(l))return o.get(l);let u=new Proxy(()=>{},{get(e,t){if(typeof t!=`symbol`&&t!==`then`)return s([...c,String(t)])},apply(o,s,l){let u=c.at(-1),d=c.slice(0,-1),[f,p]=l,m=e.fn(function*(e){let t=yield*i;for(let e of d)t=t[e];return yield*t(e)}),h=(e,t)=>a.runPromise(m(e),{signal:t}),g=(e,t)=>[{type:e},...d,...t?[t]:[]];if(u===`queryOptions`)return{...p,queryKey:g(`query`,f),queryFn:({signal:e})=>h(f,e)};if(u===`getQueryKey`)return g(`query`,f);if(u===`mutationOptions`)return{...p,mutationKey:g(`mutation`,f),mutationFn:e=>h({payload:e})};if(u===`subscriptionOptions`){let{onData:i,onError:o,keepAliveTimeout:s=`10 seconds`}=p;return{...p,subcriptionKey:g(`subscription`,f),subscriptionFn:({signal:c}={})=>{let l=e.gen(function*(){let t=yield*(yield*m({...f,responseMode:`response-only`})).stream.pipe(r.decodeText,r.splitLines,r.filter(e=>e.length>0),r.share({capacity:`unbounded`})),n=t.pipe(r.filter(e=>e.startsWith(`:keep-alive`)),r.timeout(s),r.tapError(()=>e.logError(`Keep-alive timeout, reconnecting...`))),a=yield*t.pipe(r.filter(e=>e.startsWith(`data:`)),r.map(e=>e.slice(5).trim()),r.filter(e=>e.length>0),r.tap(t=>e.sync(()=>i(t))),r.share({capacity:`unbounded`})),o=r.merge(n,a);yield*r.runDrain(o),yield*e.fail(`restart`)}).pipe(e.catchTag(`HttpClientError`,t=>t.reason._tag===`DecodeError`?e.void:(o&&o(t),e.fail(t))),e.scoped,e.retry(n.exponential(`3 seconds`))),u=a.runFork(l),d=()=>t.interrupt(u);return c&&(c.aborted?d():c.addEventListener(`abort`,d,{once:!0})),d}}}}});return o.set(l,u),u};return s([])}export{i as createTanstackQueryOptionsProxy};
|
package/dist/react.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as SubscriptionReturns, t as SubscriptionOptions } from "./types-
|
|
1
|
+
import { n as SubscriptionReturns, t as SubscriptionOptions } from "./types-CcB8FTUv.mjs";
|
|
2
2
|
//#region src/react.d.ts
|
|
3
3
|
declare function useSubscription<TData, TError>({ enabled, ...subscriptionOptions }: SubscriptionOptions<TData, TError> & {
|
|
4
4
|
enabled?: boolean;
|
package/dist/react.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"react";function t({enabled:t=!0,...n}){let[r,i]=e.useState(`disconnected`),[a,o]=e.useState(0),s=e.useCallback(()=>{o(e=>e+1)},[]);return e.useEffect(()=>{if(!t)
|
|
1
|
+
import*as e from"react";function t({enabled:t=!0,...n}){let[r,i]=e.useState(`disconnected`),[a,o]=e.useState(0),s=e.useCallback(()=>{o(e=>e+1)},[]),c=e.useMemo(()=>JSON.stringify(n.subcriptionKey),[n.subcriptionKey]);return e.useEffect(()=>{if(!t)return i(`disconnected`);i(`connecting`);let e=new AbortController,r=n.subscriptionFn({signal:e.signal});return i(`connected`),()=>{e.abort(),r(),i(`disconnected`)}},[t,c,a]),e.useMemo(()=>({status:r,reconnect:s}),[r,s])}export{t as useSubscription};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DefinedInitialDataOptions, MutationOptions } from "@tanstack/react-query";
|
|
2
|
+
import { Input } from "effect/Duration";
|
|
2
3
|
import { Schema } from "effect/Schema";
|
|
3
4
|
import { Client } from "effect/unstable/httpapi/HttpApiClient";
|
|
4
5
|
import { HttpApiEndpoint } from "effect/unstable/httpapi/HttpApiEndpoint";
|
|
@@ -19,9 +20,9 @@ type TanstackQueryOptionsProxy<T> = T extends Client.Method<HttpApiEndpoint<infe
|
|
|
19
20
|
}) & ([Headers] extends [never] ? {} : {
|
|
20
21
|
headers: UnwrapCodec<Headers>;
|
|
21
22
|
})>, options?: {
|
|
22
|
-
onData: (data: StreamSuccess) => void;
|
|
23
|
-
onError?: (error: StreamError) => void;
|
|
24
|
-
}) => SubscriptionOptions<StreamSuccess
|
|
23
|
+
onData: (data: UnwrapCodec<StreamSuccess>) => void;
|
|
24
|
+
onError?: (error: UnwrapCodec<StreamError>) => void;
|
|
25
|
+
}) => SubscriptionOptions<UnwrapCodec<StreamSuccess>, UnwrapCodec<StreamError>> : never;
|
|
25
26
|
mutationOptions: Method extends 'GET' ? never : <TMutation = MutationOptions<UnwrapCodec<Success>, UnwrapCodec<Error>, UnwrapCodec<Payload>>>(input: MakeOptionalInput<([Params] extends [never] ? {} : {
|
|
26
27
|
params: UnwrapCodec<Params>;
|
|
27
28
|
}) & ([Headers] extends [never] ? {} : {
|
|
@@ -43,6 +44,7 @@ interface SubscriptionOptions<TData, TError> {
|
|
|
43
44
|
}) => UnsubscribeFn;
|
|
44
45
|
onData: (data: TData) => void;
|
|
45
46
|
onError?: (error: TError) => void;
|
|
47
|
+
keepAliveTimeout?: Input;
|
|
46
48
|
}
|
|
47
49
|
interface SubscriptionReturns {
|
|
48
50
|
status: 'connecting' | 'connected' | 'disconnected' | 'error';
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiesen/effect-tanstack-query",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"private": false,
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Effortlessly bridge Effect HTTP API clients with TanStack Query. Enjoy end-to-end type safety, zero-boilerplate options, and built-in streaming support out of the box.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"effect",
|
|
8
8
|
"effect-ts",
|