tapi-rs 5.2.0 → 5.4.0
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 +265 -196
- package/dist/core.d.ts +10 -1
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +47 -6
- package/dist/core.js.map +1 -1
- package/dist/endpoints.d.ts +21 -1
- package/dist/endpoints.d.ts.map +1 -1
- package/dist/endpoints.js +10 -1
- package/dist/endpoints.js.map +1 -1
- package/dist/hook.d.ts.map +1 -1
- package/dist/hook.js +7 -8
- package/dist/hook.js.map +1 -1
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/request.d.ts +1 -1
- package/dist/request.d.ts.map +1 -1
- package/dist/request.js +6 -4
- package/dist/request.js.map +1 -1
- package/dist/sse-hook.d.ts +5 -0
- package/dist/sse-hook.d.ts.map +1 -0
- package/dist/sse-hook.js +39 -0
- package/dist/sse-hook.js.map +1 -0
- package/dist/sse.d.ts +6 -0
- package/dist/sse.d.ts.map +1 -0
- package/dist/sse.js +29 -0
- package/dist/sse.js.map +1 -0
- package/dist/types.d.ts +16 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +2 -2
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,280 +1,349 @@
|
|
|
1
|
-
#
|
|
1
|
+
# tapi
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Type-safe REST API client for TypeScript with React hooks. Pure compile-time types — no runtime schemas, no codegen.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
- **Type Safety** - Full TypeScript support with compile-time type checking
|
|
8
|
-
- **Error as Value** - No thrown errors, all errors returned as discriminated unions
|
|
9
|
-
- **React Hooks** - Built-in React integration with loading states
|
|
10
|
-
- **Zero Runtime Validation** - Pure TypeScript types, no runtime overhead
|
|
11
|
-
- **Lightweight** - No external validation library dependencies
|
|
12
|
-
- **Auto-completion** - Full IDE support with IntelliSense
|
|
13
|
-
- **Internationalization** - Multi-language support for error messages
|
|
14
|
-
|
|
15
|
-
## Installation
|
|
5
|
+
## Install
|
|
16
6
|
|
|
17
7
|
```bash
|
|
18
|
-
npm install tapi
|
|
8
|
+
npm install tapi-rs
|
|
19
9
|
```
|
|
20
10
|
|
|
21
|
-
## Quick
|
|
11
|
+
## Quick start
|
|
22
12
|
|
|
23
|
-
```
|
|
24
|
-
import Tapi from "tapi"
|
|
13
|
+
```ts
|
|
14
|
+
import Tapi from "tapi-rs"
|
|
25
15
|
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
16
|
+
// Define routes
|
|
17
|
+
const routes = {
|
|
18
|
+
getUsers: Tapi.get<{ response: User[] }>()({ endpoint: "/users" }),
|
|
19
|
+
getUser: Tapi.get<{ path: { id: string }; response: User }>()({ endpoint: "/users/:id" }),
|
|
20
|
+
createUser: Tapi.post<{ body: CreateUser; response: User }>()({ endpoint: "/users" }),
|
|
21
|
+
}
|
|
32
22
|
|
|
33
|
-
//
|
|
23
|
+
// Build the client
|
|
34
24
|
const api = Tapi.builder()
|
|
35
25
|
.withHost("https://api.example.com")
|
|
36
|
-
.
|
|
37
|
-
.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
26
|
+
.withRoutes(routes)
|
|
27
|
+
.build()
|
|
28
|
+
|
|
29
|
+
// Make requests
|
|
30
|
+
const response = await api.getUser({ path: { id: "1" } })
|
|
31
|
+
|
|
32
|
+
if (response.ok) {
|
|
33
|
+
console.log(response.data) // User — fully typed
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Defining routes
|
|
38
|
+
|
|
39
|
+
Each route is created with `Tapi.get`, `Tapi.post`, `Tapi.put`, `Tapi.patch`, or `Tapi.delete`. Pass a type object specifying only the params you need:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
// GET with query params
|
|
43
|
+
Tapi.get<{
|
|
44
|
+
query: { page: number; limit: number }
|
|
45
|
+
response: { users: User[]; total: number }
|
|
46
|
+
}>()({ endpoint: "/users" })
|
|
47
|
+
|
|
48
|
+
// POST with body
|
|
49
|
+
Tapi.post<{
|
|
50
|
+
body: { name: string; email: string }
|
|
51
|
+
response: User
|
|
52
|
+
}>()({ endpoint: "/users" })
|
|
53
|
+
|
|
54
|
+
// PUT with path + body
|
|
55
|
+
Tapi.put<{
|
|
56
|
+
path: { id: string }
|
|
57
|
+
body: Partial<User>
|
|
58
|
+
response: User
|
|
59
|
+
}>()({ endpoint: "/users/:id" })
|
|
60
|
+
|
|
61
|
+
// DELETE with path
|
|
62
|
+
Tapi.delete<{
|
|
63
|
+
path: { id: string }
|
|
64
|
+
response: { deleted: boolean }
|
|
65
|
+
}>()({ endpoint: "/users/:id" })
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Available type params: `path`, `body`, `formData`, `query`, `headers`, `response`.
|
|
69
|
+
|
|
70
|
+
## React hooks
|
|
71
|
+
|
|
72
|
+
Every route function has a `.useHook()` method:
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
function UserProfile({ userId }: { userId: string }) {
|
|
76
|
+
const [user, error, loading, refresh, setUser] = api.getUser.useHook({
|
|
77
|
+
path: { id: userId },
|
|
52
78
|
})
|
|
53
|
-
.build();
|
|
54
79
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
80
|
+
if (loading) return <p>Loading...</p>
|
|
81
|
+
if (error) return <p>Error: {error.message}</p>
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<div>
|
|
85
|
+
<h1>{user.name}</h1>
|
|
86
|
+
<button onClick={() => refresh()}>Refresh</button>
|
|
87
|
+
</div>
|
|
88
|
+
)
|
|
59
89
|
}
|
|
60
90
|
```
|
|
61
91
|
|
|
62
|
-
|
|
92
|
+
### Hook return value
|
|
63
93
|
|
|
64
|
-
|
|
94
|
+
Returns a tuple `[data, error, loading, refresh, setter]`:
|
|
65
95
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
96
|
+
| Index | Value | Type |
|
|
97
|
+
|-------|-------|------|
|
|
98
|
+
| 0 | `data` | `T \| null` — response data |
|
|
99
|
+
| 1 | `error` | `Errors<TError> \| null` — error details |
|
|
100
|
+
| 2 | `loading` | `boolean` |
|
|
101
|
+
| 3 | `refresh` | `(resetState?: boolean) => Promise<boolean>` |
|
|
102
|
+
| 4 | `setter` | `(fn: (prev: T) => T) => void` — optimistic updates |
|
|
69
103
|
|
|
70
|
-
|
|
71
|
-
Tapi.get<{ path: { id: string }; response: User }>();
|
|
104
|
+
### Skip fetching
|
|
72
105
|
|
|
73
|
-
|
|
74
|
-
Tapi.get<{ query: { limit?: number }; response: Post[] }>();
|
|
106
|
+
Pass `null` to disable auto-fetching entirely:
|
|
75
107
|
|
|
76
|
-
|
|
77
|
-
|
|
108
|
+
```tsx
|
|
109
|
+
const [user] = api.getUser.useHook(null)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Lazy mode
|
|
113
|
+
|
|
114
|
+
Pass `lazy: true` to create the hook without auto-fetching. Call `refresh()` to trigger manually:
|
|
78
115
|
|
|
79
|
-
|
|
80
|
-
|
|
116
|
+
```tsx
|
|
117
|
+
const [result, error, loading, submit] = api.createUser.useHook({
|
|
118
|
+
body: { name: "Alice", email: "alice@example.com" },
|
|
119
|
+
lazy: true,
|
|
120
|
+
})
|
|
81
121
|
|
|
82
|
-
//
|
|
83
|
-
|
|
122
|
+
// Trigger the request manually
|
|
123
|
+
await submit()
|
|
84
124
|
```
|
|
85
125
|
|
|
86
|
-
|
|
126
|
+
### Optimistic updates
|
|
87
127
|
|
|
88
|
-
|
|
128
|
+
Use the setter to update local data without refetching:
|
|
89
129
|
|
|
90
|
-
```
|
|
91
|
-
const
|
|
130
|
+
```tsx
|
|
131
|
+
const [users, error, loading, refresh, setUsers] = api.getUsers.useHook({})
|
|
92
132
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
} else {
|
|
96
|
-
switch (result.status) {
|
|
97
|
-
case "network_error":
|
|
98
|
-
console.error("Network failed:", result.error);
|
|
99
|
-
break;
|
|
100
|
-
case "api_error":
|
|
101
|
-
console.error(`API error [${result.code}]:`, result.data);
|
|
102
|
-
break;
|
|
103
|
-
case "mapper_error":
|
|
104
|
-
console.error("Mapper failed:", result.error);
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
133
|
+
function removeUser(id: string) {
|
|
134
|
+
setUsers((prev) => prev.filter((u) => u.id !== id))
|
|
107
135
|
}
|
|
108
136
|
```
|
|
109
137
|
|
|
110
|
-
##
|
|
138
|
+
## Cancellation
|
|
111
139
|
|
|
112
|
-
|
|
140
|
+
Hooks automatically cancel in-flight requests when params change or the component unmounts — no stale responses.
|
|
113
141
|
|
|
114
|
-
|
|
115
|
-
function UserProfile({ userId }: { userId: string }) {
|
|
116
|
-
const [user, error, loading, refresh, setUser] = api.users.getById.useHook({
|
|
117
|
-
path: { id: userId }
|
|
118
|
-
});
|
|
142
|
+
For imperative calls, pass an `AbortSignal`:
|
|
119
143
|
|
|
120
|
-
|
|
121
|
-
|
|
144
|
+
```ts
|
|
145
|
+
const controller = new AbortController()
|
|
122
146
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
147
|
+
api.getUser({ path: { id: "1" }, signal: controller.signal })
|
|
148
|
+
|
|
149
|
+
// Cancel the request
|
|
150
|
+
controller.abort()
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Nested routes
|
|
154
|
+
|
|
155
|
+
Group related endpoints under namespaces:
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
const routes = {
|
|
159
|
+
users: {
|
|
160
|
+
list: Tapi.get<{ response: User[] }>()({ endpoint: "/users" }),
|
|
161
|
+
create: Tapi.post<{ body: CreateUser; response: User }>()({ endpoint: "/users" }),
|
|
162
|
+
},
|
|
163
|
+
posts: {
|
|
164
|
+
list: Tapi.get<{ response: Post[] }>()({ endpoint: "/posts" }),
|
|
165
|
+
get: Tapi.get<{ path: { id: string }; response: Post }>()({ endpoint: "/posts/:id" }),
|
|
166
|
+
},
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const api = Tapi.builder()
|
|
170
|
+
.withHost("https://api.example.com")
|
|
171
|
+
.withRoutes(routes)
|
|
172
|
+
.build()
|
|
173
|
+
|
|
174
|
+
const response = await api.users.list({})
|
|
175
|
+
const [posts] = api.posts.list.useHook({})
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Response handling
|
|
179
|
+
|
|
180
|
+
Every request returns an `ApiResponse` — a discriminated union you can narrow with `response.ok`:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
const response = await api.getUser({ path: { id: "1" } })
|
|
184
|
+
|
|
185
|
+
if (response.ok) {
|
|
186
|
+
// Success — response.data is typed
|
|
187
|
+
console.log(response.data)
|
|
188
|
+
} else if (response.status === "api_error") {
|
|
189
|
+
// Server returned an error — response.code, response.message, response.data
|
|
190
|
+
console.log(response.code, response.data)
|
|
191
|
+
} else {
|
|
192
|
+
// Network error — response.error is the original Error
|
|
193
|
+
console.log(response.error)
|
|
130
194
|
}
|
|
131
195
|
```
|
|
132
196
|
|
|
133
|
-
|
|
197
|
+
## FormData & file uploads
|
|
198
|
+
|
|
199
|
+
Use `formData` instead of `body` for multipart requests:
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
const routes = {
|
|
203
|
+
uploadAvatar: Tapi.post<{
|
|
204
|
+
path: { userId: string }
|
|
205
|
+
formData: { avatar: File; description: string }
|
|
206
|
+
response: { url: string }
|
|
207
|
+
}>()({ endpoint: "/users/:userId/avatar" }),
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
await api.uploadAvatar({
|
|
211
|
+
path: { userId: "1" },
|
|
212
|
+
formData: { avatar: file, description: "Profile picture" },
|
|
213
|
+
})
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
File arrays are supported — each file is appended individually to the FormData.
|
|
134
217
|
|
|
135
|
-
|
|
136
|
-
const [data, error, loading, refresh, setter] = api.endpoint.useHook(params);
|
|
218
|
+
## Blob responses
|
|
137
219
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
220
|
+
Set `responseType: "blob"` for binary data:
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
const routes = {
|
|
224
|
+
downloadReport: Tapi.get<{
|
|
225
|
+
path: { id: string }
|
|
226
|
+
response: Blob
|
|
227
|
+
}>()({ endpoint: "/reports/:id/download", responseType: "blob" }),
|
|
228
|
+
}
|
|
143
229
|
```
|
|
144
230
|
|
|
145
|
-
|
|
231
|
+
## URL building
|
|
232
|
+
|
|
233
|
+
Every route function has a `.path()` method to build the full URL without making a request:
|
|
146
234
|
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
lazy: true // Don't fetch on mount
|
|
151
|
-
});
|
|
235
|
+
```ts
|
|
236
|
+
api.getUser.path({ id: "42" })
|
|
237
|
+
// => "https://api.example.com/users/42"
|
|
152
238
|
|
|
153
|
-
|
|
154
|
-
|
|
239
|
+
api.getUsers.path()
|
|
240
|
+
// => "https://api.example.com/users"
|
|
155
241
|
```
|
|
156
242
|
|
|
157
|
-
##
|
|
243
|
+
## Builder options
|
|
158
244
|
|
|
159
|
-
|
|
245
|
+
### Custom error handling
|
|
160
246
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
247
|
+
Parse your API's error format:
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
type ApiError = { code: string; details: string[] }
|
|
164
251
|
|
|
165
252
|
const api = Tapi.builder()
|
|
166
253
|
.withHost("https://api.example.com")
|
|
167
|
-
.withApiError(async (
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
getById: Tapi.get<{
|
|
171
|
-
path: { id: string };
|
|
172
|
-
response: ApiUser;
|
|
173
|
-
mapped: User;
|
|
174
|
-
}>()({
|
|
175
|
-
endpoint: "/users/:id",
|
|
176
|
-
response: Tapi.response<ApiUser, User>((data) => () => ({
|
|
177
|
-
id: data.id,
|
|
178
|
-
name: data.full_name
|
|
179
|
-
}))
|
|
180
|
-
})
|
|
181
|
-
}
|
|
254
|
+
.withApiError<ApiError>(async (response) => {
|
|
255
|
+
const body = await response.json()
|
|
256
|
+
return { code: body.error_code, details: body.messages }
|
|
182
257
|
})
|
|
183
|
-
.
|
|
258
|
+
.withRoutes(routes)
|
|
259
|
+
.build()
|
|
184
260
|
|
|
185
|
-
|
|
186
|
-
|
|
261
|
+
const response = await api.getUser({ path: { id: "1" } })
|
|
262
|
+
|
|
263
|
+
if (!response.ok && response.status === "api_error") {
|
|
264
|
+
console.log(response.data.code) // typed as ApiError
|
|
265
|
+
}
|
|
187
266
|
```
|
|
188
267
|
|
|
189
|
-
###
|
|
268
|
+
### Credentials
|
|
269
|
+
|
|
270
|
+
Set the `credentials` mode for all requests (e.g. cross-origin cookies):
|
|
190
271
|
|
|
191
|
-
```
|
|
272
|
+
```ts
|
|
192
273
|
const api = Tapi.builder()
|
|
193
274
|
.withHost("https://api.example.com")
|
|
194
|
-
.
|
|
195
|
-
.withRoutes(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
mapArg: { limit: number };
|
|
201
|
-
}>()({
|
|
202
|
-
endpoint: "/posts",
|
|
203
|
-
response: Tapi.response<Post[], Post[], { limit: number }>((posts) => (args) => posts.slice(0, args.limit))
|
|
204
|
-
})
|
|
205
|
-
}
|
|
206
|
-
})
|
|
207
|
-
.build();
|
|
275
|
+
.withCredentials("include")
|
|
276
|
+
.withRoutes(routes)
|
|
277
|
+
.build()
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Default headers
|
|
208
281
|
|
|
209
|
-
|
|
210
|
-
const
|
|
282
|
+
```ts
|
|
283
|
+
const api = Tapi.builder()
|
|
284
|
+
.withHost("https://api.example.com")
|
|
285
|
+
.withDefaultHeaders({ Authorization: "Bearer token" })
|
|
286
|
+
.withRoutes(routes)
|
|
287
|
+
.build()
|
|
288
|
+
|
|
289
|
+
// Update headers at runtime
|
|
290
|
+
api.setHeaders({ Authorization: "Bearer new-token" })
|
|
211
291
|
```
|
|
212
292
|
|
|
213
|
-
|
|
293
|
+
### Prefetch callback
|
|
294
|
+
|
|
295
|
+
Runs before every request — useful for injecting auth:
|
|
214
296
|
|
|
215
|
-
```
|
|
297
|
+
```ts
|
|
216
298
|
const api = Tapi.builder()
|
|
217
299
|
.withHost("https://api.example.com")
|
|
218
|
-
.
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
message: error.message || "Unknown error",
|
|
222
|
-
code: response.status,
|
|
223
|
-
details: error
|
|
224
|
-
};
|
|
225
|
-
})
|
|
226
|
-
.withDefaultHeaders({
|
|
227
|
-
Authorization: "Bearer <token>",
|
|
228
|
-
"Content-Type": "application/json"
|
|
229
|
-
})
|
|
230
|
-
.withPrefetch((request) => {
|
|
231
|
-
console.log(`[${request.method}] ${request.url}`);
|
|
232
|
-
})
|
|
233
|
-
.withPostfetch((response) => {
|
|
234
|
-
if (!response.ok) {
|
|
235
|
-
console.error("Request failed:", response.status);
|
|
236
|
-
}
|
|
300
|
+
.withPrefetch(async ({ url, method, headers }) => {
|
|
301
|
+
const token = await getAccessToken()
|
|
302
|
+
headers.set("Authorization", `Bearer ${token}`)
|
|
237
303
|
})
|
|
238
|
-
.withLanguage("en") // "en" | "br"
|
|
239
304
|
.withRoutes(routes)
|
|
240
|
-
.build()
|
|
305
|
+
.build()
|
|
241
306
|
```
|
|
242
307
|
|
|
243
|
-
###
|
|
244
|
-
|
|
245
|
-
Update headers at runtime using `setHeaders`:
|
|
308
|
+
### Postfetch callback
|
|
246
309
|
|
|
247
|
-
|
|
248
|
-
// Update auth token
|
|
249
|
-
api.setHeaders({
|
|
250
|
-
Authorization: `Bearer ${newToken}`
|
|
251
|
-
});
|
|
310
|
+
Runs after every request — useful for logging or global error handling:
|
|
252
311
|
|
|
253
|
-
|
|
254
|
-
api.
|
|
312
|
+
```ts
|
|
313
|
+
const api = Tapi.builder()
|
|
314
|
+
.withHost("https://api.example.com")
|
|
315
|
+
.withPostfetch((response) => {
|
|
316
|
+
if (!response.ok && response.code === 401) {
|
|
317
|
+
redirectToLogin()
|
|
318
|
+
}
|
|
319
|
+
})
|
|
320
|
+
.withRoutes(routes)
|
|
321
|
+
.build()
|
|
255
322
|
```
|
|
256
323
|
|
|
257
|
-
|
|
324
|
+
### Language
|
|
258
325
|
|
|
259
|
-
|
|
326
|
+
Error messages support `"en"` (default) and `"br"` (Brazilian Portuguese):
|
|
260
327
|
|
|
261
|
-
|
|
262
|
-
- `"br"` - Portuguese (Brazil)
|
|
263
|
-
|
|
264
|
-
```typescript
|
|
328
|
+
```ts
|
|
265
329
|
const api = Tapi.builder()
|
|
266
330
|
.withHost("https://api.example.com")
|
|
267
|
-
.withLanguage("br")
|
|
268
|
-
.withApiError(async (res) => res.statusText)
|
|
331
|
+
.withLanguage("br")
|
|
269
332
|
.withRoutes(routes)
|
|
270
|
-
.build()
|
|
333
|
+
.build()
|
|
271
334
|
```
|
|
272
335
|
|
|
273
|
-
##
|
|
336
|
+
## Types
|
|
274
337
|
|
|
275
|
-
|
|
338
|
+
```ts
|
|
339
|
+
import type { ApiResponse, Success, CustomError, NetworkError, Errors } from "tapi-rs"
|
|
276
340
|
|
|
277
|
-
|
|
341
|
+
// ApiResponse<TData, TError> = (Success<TData> | Errors<TError>) & { endpoint: string; method: HttpMethod }
|
|
342
|
+
// Success<T> = { ok: true; status: "success"; data: T }
|
|
343
|
+
// CustomError<T> = { ok: false; code: number; status: "api_error"; message: string; data: T }
|
|
344
|
+
// NetworkError = { ok: false; code: number; status: "network_error"; message: string; error: Error }
|
|
345
|
+
// Errors<T> = NetworkError | CustomError<T>
|
|
346
|
+
```
|
|
278
347
|
|
|
279
348
|
## License
|
|
280
349
|
|
package/dist/core.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as Hook from "./hook";
|
|
2
|
+
import * as SseHook from "./sse-hook";
|
|
2
3
|
import { Language } from "./translations";
|
|
3
4
|
import * as Types from "./types";
|
|
4
5
|
type PathBuilderSignature<T extends Types.RequestConfig<any, any, any, any, any, any, any>> = Types.ExtractPath<T> extends undefined ? () => string : (params: Types.ExtractPath<T>) => string;
|
|
@@ -8,8 +9,11 @@ type RouteFunction<T extends Types.RequestConfig<any, any, any, any, any, any, a
|
|
|
8
9
|
}) | null) => Hook.HookResponse<T, TError>;
|
|
9
10
|
path: PathBuilderSignature<T>;
|
|
10
11
|
};
|
|
12
|
+
type SseRouteFunction<T extends Types.SseConfig<any, any, any>> = Types.SseListenerFunction<T> & {
|
|
13
|
+
useHook: (params: Types.SseCallSignature<T> | null) => SseHook.SseHookResponse<T>;
|
|
14
|
+
};
|
|
11
15
|
export type GenerateApiMethods<T extends Types.RouteDefinitions, TError = string> = {
|
|
12
|
-
[K in keyof T]: T[K] extends Types.RequestConfig<any, any, any, any, any, any, any> ? RouteFunction<T[K], TError> : T[K] extends Types.RouteDefinitions ? GenerateApiMethods<T[K], TError> : never;
|
|
16
|
+
[K in keyof T]: T[K] extends Types.SseConfig<any, any, any> ? SseRouteFunction<T[K]> : T[K] extends Types.RequestConfig<any, any, any, any, any, any, any> ? RouteFunction<T[K], TError> : T[K] extends Types.RouteDefinitions ? GenerateApiMethods<T[K], TError> : never;
|
|
13
17
|
} & {
|
|
14
18
|
/**
|
|
15
19
|
* Update default headers for all requests in this API instance
|
|
@@ -27,6 +31,7 @@ export declare class TapiBuilder<TRoutes extends Types.RouteDefinitions = {}, TE
|
|
|
27
31
|
private defaultHeaders?;
|
|
28
32
|
private errorHandler?;
|
|
29
33
|
private language;
|
|
34
|
+
private credentials?;
|
|
30
35
|
/**
|
|
31
36
|
* Set the host URL for API requests
|
|
32
37
|
*/
|
|
@@ -55,6 +60,10 @@ export declare class TapiBuilder<TRoutes extends Types.RouteDefinitions = {}, TE
|
|
|
55
60
|
* Set the language for error messages
|
|
56
61
|
*/
|
|
57
62
|
withLanguage(language: Language): TapiBuilder<TRoutes, TError, THasHost, THasRoutes>;
|
|
63
|
+
/**
|
|
64
|
+
* Set the credentials mode for all requests (e.g. "include" for cross-origin cookies)
|
|
65
|
+
*/
|
|
66
|
+
withCredentials(credentials: RequestCredentials): TapiBuilder<TRoutes, TError, THasHost, THasRoutes>;
|
|
58
67
|
/**
|
|
59
68
|
* Build the API client with compile-time validation
|
|
60
69
|
*
|
package/dist/core.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
|