zlient 1.0.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 +292 -0
- package/dist/auth.d.ts +36 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/endpoint/BaseEndpoint.d.ts +22 -0
- package/dist/endpoint/BaseEndpoint.d.ts.map +1 -0
- package/dist/http/HttpClient.d.ts +25 -0
- package/dist/http/HttpClient.d.ts.map +1 -0
- package/dist/index.cjs +352 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +329 -0
- package/dist/schemas/common.d.ts +42 -0
- package/dist/schemas/common.d.ts.map +1 -0
- package/dist/types.d.ts +99 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/validation.d.ts +5 -0
- package/dist/validation.d.ts.map +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# zlient
|
|
2
|
+
|
|
3
|
+
A type-safe HTTP client framework with Zod validation for building robust API clients.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔒 **Type-safe**: Full TypeScript support with automatic type inference
|
|
8
|
+
- ✅ **Runtime validation**: Zod schemas for request/response validation
|
|
9
|
+
- 🔄 **Retry logic**: Built-in configurable retry strategies
|
|
10
|
+
- 🎯 **Authentication**: Multiple auth providers (API Key, Bearer Token, Custom)
|
|
11
|
+
- 🪝 **Interceptors**: Before request and after response hooks
|
|
12
|
+
- ⏱️ **Timeouts**: Configurable request timeouts
|
|
13
|
+
- 📦 **Multiple endpoints**: Easy service separation with base URL mapping
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install zlient zod
|
|
19
|
+
# or
|
|
20
|
+
yarn add zlient zod
|
|
21
|
+
# or
|
|
22
|
+
pnpm add zlient zod
|
|
23
|
+
# or
|
|
24
|
+
bun add zlient zod
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { HttpClient, BaseEndpoint, BearerTokenAuth } from 'zlient';
|
|
31
|
+
import { z } from 'zod';
|
|
32
|
+
|
|
33
|
+
// Define your schemas
|
|
34
|
+
const UserSchema = z.object({
|
|
35
|
+
id: z.number(),
|
|
36
|
+
name: z.string(),
|
|
37
|
+
email: z.string().email(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Create a client
|
|
41
|
+
const client = new HttpClient({
|
|
42
|
+
baseUrls: {
|
|
43
|
+
default: 'https://api.example.com',
|
|
44
|
+
},
|
|
45
|
+
auth: new BearerTokenAuth(() => 'your-token'),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Define an endpoint
|
|
49
|
+
class GetUserEndpoint extends BaseEndpoint<typeof z.undefined, typeof UserSchema> {
|
|
50
|
+
protected method = 'GET' as const;
|
|
51
|
+
protected path = (params: { id: number }) => `/users/${params.id}`;
|
|
52
|
+
|
|
53
|
+
constructor(client: HttpClient) {
|
|
54
|
+
super(client, { responseSchema: UserSchema });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Use the endpoint
|
|
59
|
+
const getUserEndpoint = new GetUserEndpoint(client);
|
|
60
|
+
const user = await getUserEndpoint.call({ id: 1 });
|
|
61
|
+
console.log(user); // Fully typed!
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Core Concepts
|
|
65
|
+
|
|
66
|
+
### HttpClient
|
|
67
|
+
|
|
68
|
+
The main HTTP client that handles requests, retries, authentication, and interceptors.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { HttpClient, NoAuth } from 'zlient';
|
|
72
|
+
|
|
73
|
+
const client = new HttpClient({
|
|
74
|
+
baseUrls: {
|
|
75
|
+
default: 'https://api.example.com',
|
|
76
|
+
v2: 'https://api-v2.example.com',
|
|
77
|
+
},
|
|
78
|
+
headers: {
|
|
79
|
+
'Content-Type': 'application/json',
|
|
80
|
+
},
|
|
81
|
+
retry: {
|
|
82
|
+
maxRetries: 3,
|
|
83
|
+
baseDelayMs: 1000,
|
|
84
|
+
jitter: 0.2,
|
|
85
|
+
},
|
|
86
|
+
timeout: {
|
|
87
|
+
requestTimeoutMs: 30000,
|
|
88
|
+
},
|
|
89
|
+
auth: new NoAuth(),
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Authentication
|
|
94
|
+
|
|
95
|
+
#### Bearer Token
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { BearerTokenAuth } from 'zlient';
|
|
99
|
+
|
|
100
|
+
const auth = new BearerTokenAuth(async () => {
|
|
101
|
+
// Fetch token from your auth service
|
|
102
|
+
return await getAccessToken();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
client.setAuth(auth);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
#### API Key
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { ApiKeyAuth } from 'zlient';
|
|
112
|
+
|
|
113
|
+
// Header-based
|
|
114
|
+
const auth = new ApiKeyAuth({
|
|
115
|
+
header: 'X-API-Key',
|
|
116
|
+
value: 'your-api-key',
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Query parameter-based
|
|
120
|
+
const auth = new ApiKeyAuth({
|
|
121
|
+
query: 'apiKey',
|
|
122
|
+
value: 'your-api-key',
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
#### Custom Auth
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import { AuthProvider } from 'zlient';
|
|
130
|
+
|
|
131
|
+
class CustomAuth implements AuthProvider {
|
|
132
|
+
async apply({ init }) {
|
|
133
|
+
init.headers = {
|
|
134
|
+
...init.headers,
|
|
135
|
+
'X-Custom-Auth': 'custom-value',
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### BaseEndpoint
|
|
142
|
+
|
|
143
|
+
Create type-safe endpoints with automatic validation:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { BaseEndpoint, HttpClient } from 'zlient';
|
|
147
|
+
import { z } from 'zod';
|
|
148
|
+
|
|
149
|
+
const CreateUserSchema = z.object({
|
|
150
|
+
name: z.string(),
|
|
151
|
+
email: z.string().email(),
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const UserResponseSchema = z.object({
|
|
155
|
+
id: z.number(),
|
|
156
|
+
name: z.string(),
|
|
157
|
+
email: z.string().email(),
|
|
158
|
+
createdAt: z.string().datetime(),
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
class CreateUserEndpoint extends BaseEndpoint<
|
|
162
|
+
typeof CreateUserSchema,
|
|
163
|
+
typeof UserResponseSchema
|
|
164
|
+
> {
|
|
165
|
+
protected method = 'POST' as const;
|
|
166
|
+
protected path = '/users';
|
|
167
|
+
|
|
168
|
+
constructor(client: HttpClient) {
|
|
169
|
+
super(client, {
|
|
170
|
+
requestSchema: CreateUserSchema,
|
|
171
|
+
responseSchema: UserResponseSchema,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Usage
|
|
177
|
+
const endpoint = new CreateUserEndpoint(client);
|
|
178
|
+
const user = await endpoint.call({
|
|
179
|
+
name: 'John Doe',
|
|
180
|
+
email: 'john@example.com',
|
|
181
|
+
});
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Interceptors
|
|
185
|
+
|
|
186
|
+
Add hooks to inspect or modify requests and responses:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
const client = new HttpClient({
|
|
190
|
+
baseUrls: { default: 'https://api.example.com' },
|
|
191
|
+
interceptors: {
|
|
192
|
+
beforeRequest: [
|
|
193
|
+
async ({ url, init }) => {
|
|
194
|
+
console.log('Making request to:', url);
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
afterResponse: [
|
|
198
|
+
async ({ request, response, parsed }) => {
|
|
199
|
+
console.log('Response received:', response.status);
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Common Schemas
|
|
207
|
+
|
|
208
|
+
The package includes common reusable schemas:
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
import { Id, Timestamps, Meta, ApiErrorSchema, Envelope } from 'zlient';
|
|
212
|
+
|
|
213
|
+
// Use in your schemas
|
|
214
|
+
const MyEntitySchema = z.object({
|
|
215
|
+
id: Id,
|
|
216
|
+
name: z.string(),
|
|
217
|
+
...Timestamps.shape,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// Wrap responses in an envelope
|
|
221
|
+
const MyResponseSchema = Envelope(MyEntitySchema);
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Advanced Usage
|
|
225
|
+
|
|
226
|
+
### Multiple Base URLs
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
const client = new HttpClient({
|
|
230
|
+
baseUrls: {
|
|
231
|
+
default: 'https://api.example.com',
|
|
232
|
+
auth: 'https://auth.example.com',
|
|
233
|
+
cdn: 'https://cdn.example.com',
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Use specific base URL for a request
|
|
238
|
+
await endpoint.call(data, { baseUrlKey: 'auth' });
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Request Options
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
await endpoint.call(data, {
|
|
245
|
+
headers: { 'X-Custom-Header': 'value' },
|
|
246
|
+
baseUrlKey: 'v2',
|
|
247
|
+
signal: abortController.signal,
|
|
248
|
+
query: { filter: 'active', page: 1 },
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Error Handling
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
import { ApiError } from 'zlient';
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
await endpoint.call(data);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
if (error instanceof ApiError) {
|
|
261
|
+
console.error('API Error:', error.message);
|
|
262
|
+
console.error('Status:', error.status);
|
|
263
|
+
console.error('Details:', error.details);
|
|
264
|
+
|
|
265
|
+
if (error.zodError) {
|
|
266
|
+
console.error('Validation errors:', error.zodError.errors);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Building from Source
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
# Clone the repository
|
|
276
|
+
git clone <your-repo-url>
|
|
277
|
+
cd zlient
|
|
278
|
+
|
|
279
|
+
# Install dependencies
|
|
280
|
+
bun install
|
|
281
|
+
|
|
282
|
+
# Build the package
|
|
283
|
+
bun run build
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## License
|
|
287
|
+
|
|
288
|
+
MIT
|
|
289
|
+
|
|
290
|
+
## Contributing
|
|
291
|
+
|
|
292
|
+
Contributions are welcome! Please open an issue or submit a pull request.
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { RequestOptions as ReqOpts } from "./types";
|
|
2
|
+
export interface AuthProvider {
|
|
3
|
+
/**
|
|
4
|
+
* Apply authentication to the outgoing request.
|
|
5
|
+
* Called after SDK headers are assembled, but before request is sent.
|
|
6
|
+
*/
|
|
7
|
+
apply(req: {
|
|
8
|
+
url: string;
|
|
9
|
+
init: RequestInit;
|
|
10
|
+
options?: ReqOpts;
|
|
11
|
+
}): Promise<void> | void;
|
|
12
|
+
}
|
|
13
|
+
export declare class NoAuth implements AuthProvider {
|
|
14
|
+
apply(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export declare class ApiKeyAuth implements AuthProvider {
|
|
17
|
+
private opts;
|
|
18
|
+
constructor(opts: {
|
|
19
|
+
header?: string;
|
|
20
|
+
query?: string;
|
|
21
|
+
value: string;
|
|
22
|
+
});
|
|
23
|
+
apply({ url, init }: {
|
|
24
|
+
url: string;
|
|
25
|
+
init: RequestInit;
|
|
26
|
+
}): void;
|
|
27
|
+
}
|
|
28
|
+
export declare class BearerTokenAuth implements AuthProvider {
|
|
29
|
+
private getToken;
|
|
30
|
+
constructor(getToken: () => Promise<string> | string);
|
|
31
|
+
apply({ init }: {
|
|
32
|
+
url: string;
|
|
33
|
+
init: RequestInit;
|
|
34
|
+
}): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/api/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,IAAI,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzD,MAAM,WAAW,YAAY;IACzB;;;OAGG;IACH,KAAK,CAAC,GAAG,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,WAAW,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC3F;AAED,qBAAa,MAAO,YAAW,YAAY;IACjC,KAAK;CACd;AAED,qBAAa,UAAW,YAAW,YAAY;IAC/B,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAC5E,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,WAAW,CAAA;KAAE;CAS1D;AAED,qBAAa,eAAgB,YAAW,YAAY;IACpC,OAAO,CAAC,QAAQ;gBAAR,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM;IACtD,KAAK,CAAC,EAAE,IAAI,EAAE,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,WAAW,CAAA;KAAE;CAI3D"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { HttpClient } from "../http/HttpClient";
|
|
3
|
+
import { HTTPMethod, RequestOptions } from "../types";
|
|
4
|
+
/**
|
|
5
|
+
* Generic, strongly-typed endpoint with Zod schemas for request and response.
|
|
6
|
+
*/
|
|
7
|
+
export declare abstract class BaseEndpoint<ReqSchema extends z.ZodTypeAny, ResSchema extends z.ZodTypeAny> {
|
|
8
|
+
protected client: HttpClient;
|
|
9
|
+
protected abstract readonly method: keyof typeof HTTPMethod;
|
|
10
|
+
protected abstract readonly path: string | ((params: any) => string);
|
|
11
|
+
protected readonly requestSchema?: ReqSchema;
|
|
12
|
+
protected readonly responseSchema: ResSchema;
|
|
13
|
+
constructor(client: HttpClient, cfg: {
|
|
14
|
+
requestSchema?: ReqSchema;
|
|
15
|
+
responseSchema: ResSchema;
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* Call the endpoint with strong typing derived from schemas.
|
|
19
|
+
*/
|
|
20
|
+
call(args: z.infer<ReqSchema>, options?: RequestOptions): Promise<z.infer<ResSchema>>;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=BaseEndpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseEndpoint.d.ts","sourceRoot":"","sources":["../../src/api/endpoint/BaseEndpoint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAGtD;;GAEG;AACH,8BAAsB,YAAY,CAAC,SAAS,SAAS,CAAC,CAAC,UAAU,EAAE,SAAS,SAAS,CAAC,CAAC,UAAU;IAMjF,SAAS,CAAC,MAAM,EAAE,UAAU;IALxC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,UAAU,CAAC;IAC5D,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,GAAG,KAAK,MAAM,CAAC,CAAC;IACrE,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,CAAC;IAC7C,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,SAAS,CAAC;gBAEvB,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE;QAAE,aAAa,CAAC,EAAE,SAAS,CAAC;QAAC,cAAc,EAAE,SAAS,CAAA;KAAE;IAKvG;;OAEG;IACG,IAAI,CACN,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,EACxB,OAAO,CAAC,EAAE,cAAc,GACzB,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;CAkBjC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ClientOptions, HTTPMethod, RequestOptions } from "../types";
|
|
2
|
+
import type { AuthProvider } from "../auth";
|
|
3
|
+
export declare class HttpClient {
|
|
4
|
+
private fetchImpl;
|
|
5
|
+
private baseUrls;
|
|
6
|
+
private headers;
|
|
7
|
+
private interceptors;
|
|
8
|
+
private retry;
|
|
9
|
+
private timeoutMs?;
|
|
10
|
+
private auth;
|
|
11
|
+
constructor(opts: ClientOptions & {
|
|
12
|
+
auth?: AuthProvider;
|
|
13
|
+
});
|
|
14
|
+
setAuth(auth: AuthProvider): void;
|
|
15
|
+
private resolveBaseUrl;
|
|
16
|
+
private sleep;
|
|
17
|
+
private withRetry;
|
|
18
|
+
private runBeforeHooks;
|
|
19
|
+
private runAfterHooks;
|
|
20
|
+
request<T = unknown>(method: keyof typeof HTTPMethod, path: string, body?: unknown, options?: RequestOptions): Promise<{
|
|
21
|
+
data: T;
|
|
22
|
+
response: Response;
|
|
23
|
+
}>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=HttpClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HttpClient.d.ts","sourceRoot":"","sources":["../../src/api/http/HttpClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,aAAa,EAAa,UAAU,EAAgB,cAAc,EAAgC,MAAM,UAAU,CAAC;AACtI,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,qBAAa,UAAU;IACnB,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,IAAI,CAAe;gBAEf,IAAI,EAAE,aAAa,GAAG;QAAE,IAAI,CAAC,EAAE,YAAY,CAAA;KAAE;IAgBzD,OAAO,CAAC,IAAI,EAAE,YAAY;IAE1B,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,KAAK;YAEC,SAAS;YAiBT,cAAc;YAMd,aAAa;IAMrB,OAAO,CAAC,CAAC,GAAG,OAAO,EACrB,MAAM,EAAE,MAAM,OAAO,UAAU,EAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,cAAc,GACzB,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,QAAQ,CAAA;KAAE,CAAC;CA2D9C"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
11
|
+
var __export = (all) => {
|
|
12
|
+
let target = {};
|
|
13
|
+
for (var name in all) __defProp(target, name, {
|
|
14
|
+
get: all[name],
|
|
15
|
+
enumerable: true
|
|
16
|
+
});
|
|
17
|
+
return target;
|
|
18
|
+
};
|
|
19
|
+
var __copyProps = (to, from, except, desc) => {
|
|
20
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
21
|
+
key = keys[i];
|
|
22
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
23
|
+
get: ((k) => from[k]).bind(null, key),
|
|
24
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return to;
|
|
28
|
+
};
|
|
29
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
30
|
+
value: mod,
|
|
31
|
+
enumerable: true
|
|
32
|
+
}) : target, mod));
|
|
33
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
let zod = require("zod");
|
|
37
|
+
zod = __toESM(zod);
|
|
38
|
+
|
|
39
|
+
//#region src/api/types.ts
|
|
40
|
+
const HTTPMethod = {
|
|
41
|
+
GET: "GET",
|
|
42
|
+
POST: "POST",
|
|
43
|
+
PUT: "PUT",
|
|
44
|
+
PATCH: "PATCH",
|
|
45
|
+
DELETE: "DELETE",
|
|
46
|
+
HEAD: "HEAD",
|
|
47
|
+
OPTIONS: "OPTIONS"
|
|
48
|
+
};
|
|
49
|
+
var ApiError = class extends Error {
|
|
50
|
+
status;
|
|
51
|
+
details;
|
|
52
|
+
zodError;
|
|
53
|
+
constructor(message, options) {
|
|
54
|
+
super(message);
|
|
55
|
+
this.name = "ApiError";
|
|
56
|
+
this.status = options?.status;
|
|
57
|
+
this.details = options?.details;
|
|
58
|
+
this.cause = options?.cause;
|
|
59
|
+
this.zodError = options?.zodError;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const PaginationSchema = zod.z.object({
|
|
63
|
+
items: zod.z.array(zod.z.unknown()),
|
|
64
|
+
total: zod.z.number().int().nonnegative(),
|
|
65
|
+
page: zod.z.number().int().nonnegative(),
|
|
66
|
+
pageSize: zod.z.number().int().positive()
|
|
67
|
+
});
|
|
68
|
+
function toQueryString(q) {
|
|
69
|
+
if (!q) return "";
|
|
70
|
+
if (q instanceof URLSearchParams) {
|
|
71
|
+
const s$1 = q.toString();
|
|
72
|
+
return s$1 ? `?${s$1}` : "";
|
|
73
|
+
}
|
|
74
|
+
const params = new URLSearchParams();
|
|
75
|
+
Object.entries(q).forEach(([k, v]) => {
|
|
76
|
+
if (v !== void 0) params.append(k, String(v));
|
|
77
|
+
});
|
|
78
|
+
const s = params.toString();
|
|
79
|
+
return s ? `?${s}` : "";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/api/auth.ts
|
|
84
|
+
var auth_exports = /* @__PURE__ */ __export({
|
|
85
|
+
ApiKeyAuth: () => ApiKeyAuth,
|
|
86
|
+
BearerTokenAuth: () => BearerTokenAuth,
|
|
87
|
+
NoAuth: () => NoAuth
|
|
88
|
+
});
|
|
89
|
+
var NoAuth, ApiKeyAuth, BearerTokenAuth;
|
|
90
|
+
var init_auth = __esm({ "src/api/auth.ts": (() => {
|
|
91
|
+
NoAuth = class {
|
|
92
|
+
async apply() {}
|
|
93
|
+
};
|
|
94
|
+
ApiKeyAuth = class {
|
|
95
|
+
constructor(opts) {
|
|
96
|
+
this.opts = opts;
|
|
97
|
+
}
|
|
98
|
+
apply({ url, init }) {
|
|
99
|
+
if (this.opts.header) init.headers = {
|
|
100
|
+
...init.headers,
|
|
101
|
+
[this.opts.header]: this.opts.value
|
|
102
|
+
};
|
|
103
|
+
else if (this.opts.query) {
|
|
104
|
+
const u = new URL(url);
|
|
105
|
+
u.searchParams.set(this.opts.query, this.opts.value);
|
|
106
|
+
init.__urlOverride = u.toString();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
BearerTokenAuth = class {
|
|
111
|
+
constructor(getToken) {
|
|
112
|
+
this.getToken = getToken;
|
|
113
|
+
}
|
|
114
|
+
async apply({ init }) {
|
|
115
|
+
const token = await this.getToken();
|
|
116
|
+
init.headers = {
|
|
117
|
+
...init.headers,
|
|
118
|
+
Authorization: `Bearer ${token}`
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}) });
|
|
123
|
+
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/api/validation.ts
|
|
126
|
+
function safeParse(schema, data) {
|
|
127
|
+
const res = schema.safeParse(data);
|
|
128
|
+
if (res.success) return {
|
|
129
|
+
success: true,
|
|
130
|
+
data: res.data
|
|
131
|
+
};
|
|
132
|
+
return {
|
|
133
|
+
success: false,
|
|
134
|
+
error: res.error
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function parseOrThrow(schema, data) {
|
|
138
|
+
const res = schema.safeParse(data);
|
|
139
|
+
if (!res.success) throw new ApiError("Response validation failed", { zodError: res.error });
|
|
140
|
+
return res.data;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region src/api/http/HttpClient.ts
|
|
145
|
+
var HttpClient = class {
|
|
146
|
+
fetchImpl;
|
|
147
|
+
baseUrls;
|
|
148
|
+
headers;
|
|
149
|
+
interceptors;
|
|
150
|
+
retry;
|
|
151
|
+
timeoutMs;
|
|
152
|
+
auth;
|
|
153
|
+
constructor(opts) {
|
|
154
|
+
this.fetchImpl = opts.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
155
|
+
if (!this.fetchImpl) throw new Error("No fetch implementation found. Pass one via options.fetch.");
|
|
156
|
+
this.baseUrls = opts.baseUrls;
|
|
157
|
+
this.headers = opts.headers ?? { "Content-Type": "application/json" };
|
|
158
|
+
this.interceptors = opts.interceptors ?? {};
|
|
159
|
+
this.retry = opts.retry ?? {
|
|
160
|
+
maxRetries: 2,
|
|
161
|
+
baseDelayMs: 250,
|
|
162
|
+
jitter: .2,
|
|
163
|
+
retryMethods: ["GET", "HEAD"]
|
|
164
|
+
};
|
|
165
|
+
this.timeoutMs = opts.timeout?.requestTimeoutMs;
|
|
166
|
+
this.auth = opts["auth"] ?? new (init_auth(), __toCommonJS(auth_exports)).NoAuth();
|
|
167
|
+
}
|
|
168
|
+
setAuth(auth) {
|
|
169
|
+
this.auth = auth;
|
|
170
|
+
}
|
|
171
|
+
resolveBaseUrl(key) {
|
|
172
|
+
const k = key || "default";
|
|
173
|
+
const url = this.baseUrls[k];
|
|
174
|
+
if (!url) throw new Error(`Unknown baseUrl key: ${k}`);
|
|
175
|
+
return url.replace(/\/$/, "");
|
|
176
|
+
}
|
|
177
|
+
sleep(ms) {
|
|
178
|
+
return new Promise((res) => setTimeout(res, ms));
|
|
179
|
+
}
|
|
180
|
+
async withRetry(fn, canRetry) {
|
|
181
|
+
let attempt = 0;
|
|
182
|
+
const { maxRetries, baseDelayMs, jitter = .2 } = this.retry;
|
|
183
|
+
while (true) try {
|
|
184
|
+
return await fn();
|
|
185
|
+
} catch (err) {
|
|
186
|
+
if (attempt >= maxRetries || !canRetry({
|
|
187
|
+
attempt,
|
|
188
|
+
error: err
|
|
189
|
+
})) throw err;
|
|
190
|
+
const backoff = baseDelayMs * 2 ** attempt;
|
|
191
|
+
const j = 1 + (Math.random() * 2 - 1) * jitter;
|
|
192
|
+
await this.sleep(backoff * j);
|
|
193
|
+
attempt++;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async runBeforeHooks(url, init) {
|
|
197
|
+
for (const h of this.interceptors.beforeRequest ?? []) await h({
|
|
198
|
+
url,
|
|
199
|
+
init
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
async runAfterHooks(req, res, parsed) {
|
|
203
|
+
for (const h of this.interceptors.afterResponse ?? []) await h({
|
|
204
|
+
request: req,
|
|
205
|
+
response: res,
|
|
206
|
+
parsed
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
async request(method, path, body, options) {
|
|
210
|
+
let url = `${this.resolveBaseUrl(options?.baseUrlKey)}${path}${toQueryString(options?.query)}`;
|
|
211
|
+
const headers = {
|
|
212
|
+
...this.headers,
|
|
213
|
+
...options?.headers ?? {}
|
|
214
|
+
};
|
|
215
|
+
const controller = new AbortController();
|
|
216
|
+
const signal = options?.signal ?? controller.signal;
|
|
217
|
+
const init = {
|
|
218
|
+
method,
|
|
219
|
+
headers,
|
|
220
|
+
body: body != null ? headers["Content-Type"]?.includes("json") ? JSON.stringify(body) : body : void 0,
|
|
221
|
+
signal
|
|
222
|
+
};
|
|
223
|
+
await this.auth.apply({
|
|
224
|
+
url,
|
|
225
|
+
init,
|
|
226
|
+
options
|
|
227
|
+
});
|
|
228
|
+
if (init.__urlOverride) url = init.__urlOverride;
|
|
229
|
+
await this.runBeforeHooks(url, init);
|
|
230
|
+
const doFetch = async () => {
|
|
231
|
+
let timeoutId;
|
|
232
|
+
if (this.timeoutMs && !options?.signal) timeoutId = setTimeout(() => controller.abort(new ApiError("Request timeout", { status: 0 })), this.timeoutMs);
|
|
233
|
+
try {
|
|
234
|
+
const req = new Request(url, init);
|
|
235
|
+
console.log("HTTP Request:", req.method, req.url, req);
|
|
236
|
+
const res = await this.fetchImpl(req);
|
|
237
|
+
if (!res.ok) {
|
|
238
|
+
let text = "";
|
|
239
|
+
try {
|
|
240
|
+
text = await res.text();
|
|
241
|
+
} catch {}
|
|
242
|
+
throw new ApiError(`HTTP ${res.status}: ${res.statusText}`, {
|
|
243
|
+
status: res.status,
|
|
244
|
+
details: text
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
const data = (res.headers.get("content-type") || "").includes("json") ? await res.json() : await res.text();
|
|
248
|
+
await this.runAfterHooks(new Request(url, init), res, data);
|
|
249
|
+
return {
|
|
250
|
+
data,
|
|
251
|
+
response: res
|
|
252
|
+
};
|
|
253
|
+
} finally {
|
|
254
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
const canRetry = ({ response, error }) => {
|
|
258
|
+
if (error instanceof ApiError && error.status && error.status >= 500) return true;
|
|
259
|
+
if (error?.name === "AbortError") return false;
|
|
260
|
+
if (!error?.status && !response) return true;
|
|
261
|
+
return false;
|
|
262
|
+
};
|
|
263
|
+
if (!this.retry.retryMethods?.includes(method)) return doFetch();
|
|
264
|
+
return this.withRetry(doFetch, canRetry);
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
//#endregion
|
|
269
|
+
//#region src/api/endpoint/BaseEndpoint.ts
|
|
270
|
+
/**
|
|
271
|
+
* Generic, strongly-typed endpoint with Zod schemas for request and response.
|
|
272
|
+
*/
|
|
273
|
+
var BaseEndpoint = class {
|
|
274
|
+
requestSchema;
|
|
275
|
+
responseSchema;
|
|
276
|
+
constructor(client, cfg) {
|
|
277
|
+
this.client = client;
|
|
278
|
+
this.requestSchema = cfg.requestSchema;
|
|
279
|
+
this.responseSchema = cfg.responseSchema;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Call the endpoint with strong typing derived from schemas.
|
|
283
|
+
*/
|
|
284
|
+
async call(args, options) {
|
|
285
|
+
if (this.requestSchema) {
|
|
286
|
+
const parsed = this.requestSchema.safeParse(args);
|
|
287
|
+
if (!parsed.success) throw parsed.error;
|
|
288
|
+
}
|
|
289
|
+
const path = typeof this.path === "function" ? this.path(args) : this.path;
|
|
290
|
+
const body = this.method === "GET" || this.method === "HEAD" ? void 0 : args;
|
|
291
|
+
const { data } = await this.client.request(this.method, path, body, {
|
|
292
|
+
...options,
|
|
293
|
+
query: (this.method === "GET" ? args?.query : void 0) ?? options?.query
|
|
294
|
+
});
|
|
295
|
+
return parseOrThrow(this.responseSchema, data);
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
//#endregion
|
|
300
|
+
//#region src/api/schemas/common.ts
|
|
301
|
+
const Id = zod.z.union([
|
|
302
|
+
zod.z.string().min(1),
|
|
303
|
+
zod.z.number(),
|
|
304
|
+
zod.z.uuid({ version: "v4" })
|
|
305
|
+
]);
|
|
306
|
+
const Timestamps = zod.z.object({
|
|
307
|
+
createdAt: zod.z.iso.datetime(),
|
|
308
|
+
updatedAt: zod.z.iso.datetime()
|
|
309
|
+
});
|
|
310
|
+
const Meta = zod.z.object({
|
|
311
|
+
requestId: zod.z.string().optional(),
|
|
312
|
+
timestamp: zod.z.iso.datetime().optional(),
|
|
313
|
+
traceId: zod.z.string().optional()
|
|
314
|
+
});
|
|
315
|
+
const ErrorDetail = zod.z.object({
|
|
316
|
+
path: zod.z.string().optional(),
|
|
317
|
+
message: zod.z.string()
|
|
318
|
+
});
|
|
319
|
+
const ApiErrorSchema = zod.z.object({
|
|
320
|
+
code: zod.z.string(),
|
|
321
|
+
message: zod.z.string(),
|
|
322
|
+
details: zod.z.array(ErrorDetail).optional()
|
|
323
|
+
});
|
|
324
|
+
const Envelope = (inner) => zod.z.object({
|
|
325
|
+
success: zod.z.boolean(),
|
|
326
|
+
data: inner.optional(),
|
|
327
|
+
error: ApiErrorSchema.optional(),
|
|
328
|
+
meta: Meta.optional()
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
//#endregion
|
|
332
|
+
//#region src/api/index.ts
|
|
333
|
+
init_auth();
|
|
334
|
+
|
|
335
|
+
//#endregion
|
|
336
|
+
exports.ApiError = ApiError;
|
|
337
|
+
exports.ApiErrorSchema = ApiErrorSchema;
|
|
338
|
+
exports.ApiKeyAuth = ApiKeyAuth;
|
|
339
|
+
exports.BaseEndpoint = BaseEndpoint;
|
|
340
|
+
exports.BearerTokenAuth = BearerTokenAuth;
|
|
341
|
+
exports.Envelope = Envelope;
|
|
342
|
+
exports.ErrorDetail = ErrorDetail;
|
|
343
|
+
exports.HTTPMethod = HTTPMethod;
|
|
344
|
+
exports.HttpClient = HttpClient;
|
|
345
|
+
exports.Id = Id;
|
|
346
|
+
exports.Meta = Meta;
|
|
347
|
+
exports.NoAuth = NoAuth;
|
|
348
|
+
exports.PaginationSchema = PaginationSchema;
|
|
349
|
+
exports.Timestamps = Timestamps;
|
|
350
|
+
exports.parseOrThrow = parseOrThrow;
|
|
351
|
+
exports.safeParse = safeParse;
|
|
352
|
+
exports.toQueryString = toQueryString;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/api/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAG7B,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAGvD,cAAc,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
//#region rolldown:runtime
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
11
|
+
var __export = (all) => {
|
|
12
|
+
let target = {};
|
|
13
|
+
for (var name in all) __defProp(target, name, {
|
|
14
|
+
get: all[name],
|
|
15
|
+
enumerable: true
|
|
16
|
+
});
|
|
17
|
+
return target;
|
|
18
|
+
};
|
|
19
|
+
var __copyProps = (to, from, except, desc) => {
|
|
20
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
21
|
+
key = keys[i];
|
|
22
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
23
|
+
get: ((k) => from[k]).bind(null, key),
|
|
24
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return to;
|
|
28
|
+
};
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/api/types.ts
|
|
33
|
+
const HTTPMethod = {
|
|
34
|
+
GET: "GET",
|
|
35
|
+
POST: "POST",
|
|
36
|
+
PUT: "PUT",
|
|
37
|
+
PATCH: "PATCH",
|
|
38
|
+
DELETE: "DELETE",
|
|
39
|
+
HEAD: "HEAD",
|
|
40
|
+
OPTIONS: "OPTIONS"
|
|
41
|
+
};
|
|
42
|
+
var ApiError = class extends Error {
|
|
43
|
+
status;
|
|
44
|
+
details;
|
|
45
|
+
zodError;
|
|
46
|
+
constructor(message, options) {
|
|
47
|
+
super(message);
|
|
48
|
+
this.name = "ApiError";
|
|
49
|
+
this.status = options?.status;
|
|
50
|
+
this.details = options?.details;
|
|
51
|
+
this.cause = options?.cause;
|
|
52
|
+
this.zodError = options?.zodError;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const PaginationSchema = z.object({
|
|
56
|
+
items: z.array(z.unknown()),
|
|
57
|
+
total: z.number().int().nonnegative(),
|
|
58
|
+
page: z.number().int().nonnegative(),
|
|
59
|
+
pageSize: z.number().int().positive()
|
|
60
|
+
});
|
|
61
|
+
function toQueryString(q) {
|
|
62
|
+
if (!q) return "";
|
|
63
|
+
if (q instanceof URLSearchParams) {
|
|
64
|
+
const s$1 = q.toString();
|
|
65
|
+
return s$1 ? `?${s$1}` : "";
|
|
66
|
+
}
|
|
67
|
+
const params = new URLSearchParams();
|
|
68
|
+
Object.entries(q).forEach(([k, v]) => {
|
|
69
|
+
if (v !== void 0) params.append(k, String(v));
|
|
70
|
+
});
|
|
71
|
+
const s = params.toString();
|
|
72
|
+
return s ? `?${s}` : "";
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
//#endregion
|
|
76
|
+
//#region src/api/auth.ts
|
|
77
|
+
var auth_exports = /* @__PURE__ */ __export({
|
|
78
|
+
ApiKeyAuth: () => ApiKeyAuth,
|
|
79
|
+
BearerTokenAuth: () => BearerTokenAuth,
|
|
80
|
+
NoAuth: () => NoAuth
|
|
81
|
+
});
|
|
82
|
+
var NoAuth, ApiKeyAuth, BearerTokenAuth;
|
|
83
|
+
var init_auth = __esm({ "src/api/auth.ts": (() => {
|
|
84
|
+
NoAuth = class {
|
|
85
|
+
async apply() {}
|
|
86
|
+
};
|
|
87
|
+
ApiKeyAuth = class {
|
|
88
|
+
constructor(opts) {
|
|
89
|
+
this.opts = opts;
|
|
90
|
+
}
|
|
91
|
+
apply({ url, init }) {
|
|
92
|
+
if (this.opts.header) init.headers = {
|
|
93
|
+
...init.headers,
|
|
94
|
+
[this.opts.header]: this.opts.value
|
|
95
|
+
};
|
|
96
|
+
else if (this.opts.query) {
|
|
97
|
+
const u = new URL(url);
|
|
98
|
+
u.searchParams.set(this.opts.query, this.opts.value);
|
|
99
|
+
init.__urlOverride = u.toString();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
BearerTokenAuth = class {
|
|
104
|
+
constructor(getToken) {
|
|
105
|
+
this.getToken = getToken;
|
|
106
|
+
}
|
|
107
|
+
async apply({ init }) {
|
|
108
|
+
const token = await this.getToken();
|
|
109
|
+
init.headers = {
|
|
110
|
+
...init.headers,
|
|
111
|
+
Authorization: `Bearer ${token}`
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}) });
|
|
116
|
+
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/api/validation.ts
|
|
119
|
+
function safeParse(schema, data) {
|
|
120
|
+
const res = schema.safeParse(data);
|
|
121
|
+
if (res.success) return {
|
|
122
|
+
success: true,
|
|
123
|
+
data: res.data
|
|
124
|
+
};
|
|
125
|
+
return {
|
|
126
|
+
success: false,
|
|
127
|
+
error: res.error
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function parseOrThrow(schema, data) {
|
|
131
|
+
const res = schema.safeParse(data);
|
|
132
|
+
if (!res.success) throw new ApiError("Response validation failed", { zodError: res.error });
|
|
133
|
+
return res.data;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
//#endregion
|
|
137
|
+
//#region src/api/http/HttpClient.ts
|
|
138
|
+
var HttpClient = class {
|
|
139
|
+
fetchImpl;
|
|
140
|
+
baseUrls;
|
|
141
|
+
headers;
|
|
142
|
+
interceptors;
|
|
143
|
+
retry;
|
|
144
|
+
timeoutMs;
|
|
145
|
+
auth;
|
|
146
|
+
constructor(opts) {
|
|
147
|
+
this.fetchImpl = opts.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
148
|
+
if (!this.fetchImpl) throw new Error("No fetch implementation found. Pass one via options.fetch.");
|
|
149
|
+
this.baseUrls = opts.baseUrls;
|
|
150
|
+
this.headers = opts.headers ?? { "Content-Type": "application/json" };
|
|
151
|
+
this.interceptors = opts.interceptors ?? {};
|
|
152
|
+
this.retry = opts.retry ?? {
|
|
153
|
+
maxRetries: 2,
|
|
154
|
+
baseDelayMs: 250,
|
|
155
|
+
jitter: .2,
|
|
156
|
+
retryMethods: ["GET", "HEAD"]
|
|
157
|
+
};
|
|
158
|
+
this.timeoutMs = opts.timeout?.requestTimeoutMs;
|
|
159
|
+
this.auth = opts["auth"] ?? new (init_auth(), __toCommonJS(auth_exports)).NoAuth();
|
|
160
|
+
}
|
|
161
|
+
setAuth(auth) {
|
|
162
|
+
this.auth = auth;
|
|
163
|
+
}
|
|
164
|
+
resolveBaseUrl(key) {
|
|
165
|
+
const k = key || "default";
|
|
166
|
+
const url = this.baseUrls[k];
|
|
167
|
+
if (!url) throw new Error(`Unknown baseUrl key: ${k}`);
|
|
168
|
+
return url.replace(/\/$/, "");
|
|
169
|
+
}
|
|
170
|
+
sleep(ms) {
|
|
171
|
+
return new Promise((res) => setTimeout(res, ms));
|
|
172
|
+
}
|
|
173
|
+
async withRetry(fn, canRetry) {
|
|
174
|
+
let attempt = 0;
|
|
175
|
+
const { maxRetries, baseDelayMs, jitter = .2 } = this.retry;
|
|
176
|
+
while (true) try {
|
|
177
|
+
return await fn();
|
|
178
|
+
} catch (err) {
|
|
179
|
+
if (attempt >= maxRetries || !canRetry({
|
|
180
|
+
attempt,
|
|
181
|
+
error: err
|
|
182
|
+
})) throw err;
|
|
183
|
+
const backoff = baseDelayMs * 2 ** attempt;
|
|
184
|
+
const j = 1 + (Math.random() * 2 - 1) * jitter;
|
|
185
|
+
await this.sleep(backoff * j);
|
|
186
|
+
attempt++;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async runBeforeHooks(url, init) {
|
|
190
|
+
for (const h of this.interceptors.beforeRequest ?? []) await h({
|
|
191
|
+
url,
|
|
192
|
+
init
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
async runAfterHooks(req, res, parsed) {
|
|
196
|
+
for (const h of this.interceptors.afterResponse ?? []) await h({
|
|
197
|
+
request: req,
|
|
198
|
+
response: res,
|
|
199
|
+
parsed
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
async request(method, path, body, options) {
|
|
203
|
+
let url = `${this.resolveBaseUrl(options?.baseUrlKey)}${path}${toQueryString(options?.query)}`;
|
|
204
|
+
const headers = {
|
|
205
|
+
...this.headers,
|
|
206
|
+
...options?.headers ?? {}
|
|
207
|
+
};
|
|
208
|
+
const controller = new AbortController();
|
|
209
|
+
const signal = options?.signal ?? controller.signal;
|
|
210
|
+
const init = {
|
|
211
|
+
method,
|
|
212
|
+
headers,
|
|
213
|
+
body: body != null ? headers["Content-Type"]?.includes("json") ? JSON.stringify(body) : body : void 0,
|
|
214
|
+
signal
|
|
215
|
+
};
|
|
216
|
+
await this.auth.apply({
|
|
217
|
+
url,
|
|
218
|
+
init,
|
|
219
|
+
options
|
|
220
|
+
});
|
|
221
|
+
if (init.__urlOverride) url = init.__urlOverride;
|
|
222
|
+
await this.runBeforeHooks(url, init);
|
|
223
|
+
const doFetch = async () => {
|
|
224
|
+
let timeoutId;
|
|
225
|
+
if (this.timeoutMs && !options?.signal) timeoutId = setTimeout(() => controller.abort(new ApiError("Request timeout", { status: 0 })), this.timeoutMs);
|
|
226
|
+
try {
|
|
227
|
+
const req = new Request(url, init);
|
|
228
|
+
console.log("HTTP Request:", req.method, req.url, req);
|
|
229
|
+
const res = await this.fetchImpl(req);
|
|
230
|
+
if (!res.ok) {
|
|
231
|
+
let text = "";
|
|
232
|
+
try {
|
|
233
|
+
text = await res.text();
|
|
234
|
+
} catch {}
|
|
235
|
+
throw new ApiError(`HTTP ${res.status}: ${res.statusText}`, {
|
|
236
|
+
status: res.status,
|
|
237
|
+
details: text
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
const data = (res.headers.get("content-type") || "").includes("json") ? await res.json() : await res.text();
|
|
241
|
+
await this.runAfterHooks(new Request(url, init), res, data);
|
|
242
|
+
return {
|
|
243
|
+
data,
|
|
244
|
+
response: res
|
|
245
|
+
};
|
|
246
|
+
} finally {
|
|
247
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
const canRetry = ({ response, error }) => {
|
|
251
|
+
if (error instanceof ApiError && error.status && error.status >= 500) return true;
|
|
252
|
+
if (error?.name === "AbortError") return false;
|
|
253
|
+
if (!error?.status && !response) return true;
|
|
254
|
+
return false;
|
|
255
|
+
};
|
|
256
|
+
if (!this.retry.retryMethods?.includes(method)) return doFetch();
|
|
257
|
+
return this.withRetry(doFetch, canRetry);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
//#endregion
|
|
262
|
+
//#region src/api/endpoint/BaseEndpoint.ts
|
|
263
|
+
/**
|
|
264
|
+
* Generic, strongly-typed endpoint with Zod schemas for request and response.
|
|
265
|
+
*/
|
|
266
|
+
var BaseEndpoint = class {
|
|
267
|
+
requestSchema;
|
|
268
|
+
responseSchema;
|
|
269
|
+
constructor(client, cfg) {
|
|
270
|
+
this.client = client;
|
|
271
|
+
this.requestSchema = cfg.requestSchema;
|
|
272
|
+
this.responseSchema = cfg.responseSchema;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Call the endpoint with strong typing derived from schemas.
|
|
276
|
+
*/
|
|
277
|
+
async call(args, options) {
|
|
278
|
+
if (this.requestSchema) {
|
|
279
|
+
const parsed = this.requestSchema.safeParse(args);
|
|
280
|
+
if (!parsed.success) throw parsed.error;
|
|
281
|
+
}
|
|
282
|
+
const path = typeof this.path === "function" ? this.path(args) : this.path;
|
|
283
|
+
const body = this.method === "GET" || this.method === "HEAD" ? void 0 : args;
|
|
284
|
+
const { data } = await this.client.request(this.method, path, body, {
|
|
285
|
+
...options,
|
|
286
|
+
query: (this.method === "GET" ? args?.query : void 0) ?? options?.query
|
|
287
|
+
});
|
|
288
|
+
return parseOrThrow(this.responseSchema, data);
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
//#endregion
|
|
293
|
+
//#region src/api/schemas/common.ts
|
|
294
|
+
const Id = z.union([
|
|
295
|
+
z.string().min(1),
|
|
296
|
+
z.number(),
|
|
297
|
+
z.uuid({ version: "v4" })
|
|
298
|
+
]);
|
|
299
|
+
const Timestamps = z.object({
|
|
300
|
+
createdAt: z.iso.datetime(),
|
|
301
|
+
updatedAt: z.iso.datetime()
|
|
302
|
+
});
|
|
303
|
+
const Meta = z.object({
|
|
304
|
+
requestId: z.string().optional(),
|
|
305
|
+
timestamp: z.iso.datetime().optional(),
|
|
306
|
+
traceId: z.string().optional()
|
|
307
|
+
});
|
|
308
|
+
const ErrorDetail = z.object({
|
|
309
|
+
path: z.string().optional(),
|
|
310
|
+
message: z.string()
|
|
311
|
+
});
|
|
312
|
+
const ApiErrorSchema = z.object({
|
|
313
|
+
code: z.string(),
|
|
314
|
+
message: z.string(),
|
|
315
|
+
details: z.array(ErrorDetail).optional()
|
|
316
|
+
});
|
|
317
|
+
const Envelope = (inner) => z.object({
|
|
318
|
+
success: z.boolean(),
|
|
319
|
+
data: inner.optional(),
|
|
320
|
+
error: ApiErrorSchema.optional(),
|
|
321
|
+
meta: Meta.optional()
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
//#endregion
|
|
325
|
+
//#region src/api/index.ts
|
|
326
|
+
init_auth();
|
|
327
|
+
|
|
328
|
+
//#endregion
|
|
329
|
+
export { ApiError, ApiErrorSchema, ApiKeyAuth, BaseEndpoint, BearerTokenAuth, Envelope, ErrorDetail, HTTPMethod, HttpClient, Id, Meta, NoAuth, PaginationSchema, Timestamps, parseOrThrow, safeParse, toQueryString };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const Id: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodUUID]>;
|
|
3
|
+
export type Id = z.infer<typeof Id>;
|
|
4
|
+
export declare const Timestamps: z.ZodObject<{
|
|
5
|
+
createdAt: z.ZodISODateTime;
|
|
6
|
+
updatedAt: z.ZodISODateTime;
|
|
7
|
+
}, z.core.$strip>;
|
|
8
|
+
export declare const Meta: z.ZodObject<{
|
|
9
|
+
requestId: z.ZodOptional<z.ZodString>;
|
|
10
|
+
timestamp: z.ZodOptional<z.ZodISODateTime>;
|
|
11
|
+
traceId: z.ZodOptional<z.ZodString>;
|
|
12
|
+
}, z.core.$strip>;
|
|
13
|
+
export declare const ErrorDetail: z.ZodObject<{
|
|
14
|
+
path: z.ZodOptional<z.ZodString>;
|
|
15
|
+
message: z.ZodString;
|
|
16
|
+
}, z.core.$strip>;
|
|
17
|
+
export declare const ApiErrorSchema: z.ZodObject<{
|
|
18
|
+
code: z.ZodString;
|
|
19
|
+
message: z.ZodString;
|
|
20
|
+
details: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
21
|
+
path: z.ZodOptional<z.ZodString>;
|
|
22
|
+
message: z.ZodString;
|
|
23
|
+
}, z.core.$strip>>>;
|
|
24
|
+
}, z.core.$strip>;
|
|
25
|
+
export declare const Envelope: <T extends z.ZodTypeAny>(inner: T) => z.ZodObject<{
|
|
26
|
+
success: z.ZodBoolean;
|
|
27
|
+
data: z.ZodOptional<T>;
|
|
28
|
+
error: z.ZodOptional<z.ZodObject<{
|
|
29
|
+
code: z.ZodString;
|
|
30
|
+
message: z.ZodString;
|
|
31
|
+
details: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
32
|
+
path: z.ZodOptional<z.ZodString>;
|
|
33
|
+
message: z.ZodString;
|
|
34
|
+
}, z.core.$strip>>>;
|
|
35
|
+
}, z.core.$strip>>;
|
|
36
|
+
meta: z.ZodOptional<z.ZodObject<{
|
|
37
|
+
requestId: z.ZodOptional<z.ZodString>;
|
|
38
|
+
timestamp: z.ZodOptional<z.ZodISODateTime>;
|
|
39
|
+
traceId: z.ZodOptional<z.ZodString>;
|
|
40
|
+
}, z.core.$strip>>;
|
|
41
|
+
}, z.core.$strip>;
|
|
42
|
+
//# sourceMappingURL=common.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/api/schemas/common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,EAAE,4DAEX,CAAC;AACL,MAAM,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;AAGpC,eAAO,MAAM,UAAU;;;iBAGrB,CAAC;AAGH,eAAO,MAAM,IAAI;;;;iBAIf,CAAC;AAGH,eAAO,MAAM,WAAW;;;iBAGtB,CAAC;AAGH,eAAO,MAAM,cAAc;;;;;;;iBAIzB,CAAC;AAGH,eAAO,MAAM,QAAQ,GAAI,CAAC,SAAS,CAAC,CAAC,UAAU,EAAE,OAAO,CAAC;;;;;;;;;;;;;;;;iBAMnD,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { z, ZodError } from "zod";
|
|
2
|
+
export type Dictionary<T = unknown> = Record<string, T>;
|
|
3
|
+
export type FetchLike = (input: string | Request | URL, init?: RequestInit) => Promise<Response>;
|
|
4
|
+
export type BaseUrlMap = {
|
|
5
|
+
default: string;
|
|
6
|
+
[service: string]: string;
|
|
7
|
+
};
|
|
8
|
+
export type RetryStrategy = {
|
|
9
|
+
maxRetries: number;
|
|
10
|
+
/** milliseconds */
|
|
11
|
+
baseDelayMs: number;
|
|
12
|
+
/** Jitter factor 0..1 */
|
|
13
|
+
jitter?: number;
|
|
14
|
+
/** HTTP methods eligible for retry */
|
|
15
|
+
retryMethods?: (keyof typeof HTTPMethod)[];
|
|
16
|
+
/** Which status codes are retriable */
|
|
17
|
+
shouldRetry?: (ctx: {
|
|
18
|
+
attempt: number;
|
|
19
|
+
error?: unknown;
|
|
20
|
+
response?: Response;
|
|
21
|
+
}) => boolean;
|
|
22
|
+
};
|
|
23
|
+
export declare const HTTPMethod: {
|
|
24
|
+
readonly GET: "GET";
|
|
25
|
+
readonly POST: "POST";
|
|
26
|
+
readonly PUT: "PUT";
|
|
27
|
+
readonly PATCH: "PATCH";
|
|
28
|
+
readonly DELETE: "DELETE";
|
|
29
|
+
readonly HEAD: "HEAD";
|
|
30
|
+
readonly OPTIONS: "OPTIONS";
|
|
31
|
+
};
|
|
32
|
+
export type HttpMethod = keyof typeof HTTPMethod;
|
|
33
|
+
export type AfterResponseHook = (ctx: {
|
|
34
|
+
request: Request;
|
|
35
|
+
response: Response;
|
|
36
|
+
parsed?: unknown;
|
|
37
|
+
}) => Promise<void> | void;
|
|
38
|
+
export type BeforeRequestHook = (ctx: {
|
|
39
|
+
url: string;
|
|
40
|
+
init: RequestInit;
|
|
41
|
+
}) => Promise<void> | void;
|
|
42
|
+
export interface Interceptors {
|
|
43
|
+
beforeRequest?: BeforeRequestHook[];
|
|
44
|
+
afterResponse?: AfterResponseHook[];
|
|
45
|
+
}
|
|
46
|
+
export interface TimeoutOptions {
|
|
47
|
+
/** ms */
|
|
48
|
+
requestTimeoutMs?: number;
|
|
49
|
+
}
|
|
50
|
+
export interface ClientOptions {
|
|
51
|
+
baseUrls: BaseUrlMap;
|
|
52
|
+
fetch?: FetchLike;
|
|
53
|
+
headers?: Record<string, string>;
|
|
54
|
+
retry?: RetryStrategy;
|
|
55
|
+
interceptors?: Interceptors;
|
|
56
|
+
timeout?: TimeoutOptions;
|
|
57
|
+
}
|
|
58
|
+
export type SafeParseResult<T> = {
|
|
59
|
+
success: true;
|
|
60
|
+
data: T;
|
|
61
|
+
} | {
|
|
62
|
+
success: false;
|
|
63
|
+
error: ZodError;
|
|
64
|
+
};
|
|
65
|
+
export declare class ApiError extends Error {
|
|
66
|
+
status?: number;
|
|
67
|
+
details?: unknown;
|
|
68
|
+
zodError?: ZodError;
|
|
69
|
+
constructor(message: string, options?: {
|
|
70
|
+
status?: number;
|
|
71
|
+
cause?: unknown;
|
|
72
|
+
details?: unknown;
|
|
73
|
+
zodError?: ZodError;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
export type Paginated<T> = {
|
|
77
|
+
items: T[];
|
|
78
|
+
total: number;
|
|
79
|
+
page: number;
|
|
80
|
+
pageSize: number;
|
|
81
|
+
};
|
|
82
|
+
export declare const PaginationSchema: z.ZodObject<{
|
|
83
|
+
items: z.ZodArray<z.ZodUnknown>;
|
|
84
|
+
total: z.ZodNumber;
|
|
85
|
+
page: z.ZodNumber;
|
|
86
|
+
pageSize: z.ZodNumber;
|
|
87
|
+
}, z.core.$strip>;
|
|
88
|
+
export type RequestOptions = {
|
|
89
|
+
/** Override base URL for a single call */
|
|
90
|
+
baseUrlKey?: keyof BaseUrlMap;
|
|
91
|
+
/** Additional headers for this call only */
|
|
92
|
+
headers?: Record<string, string>;
|
|
93
|
+
/** Abort controller */
|
|
94
|
+
signal?: AbortSignal;
|
|
95
|
+
/** Custom query params */
|
|
96
|
+
query?: URLSearchParams | Record<string, string | number | boolean | undefined>;
|
|
97
|
+
};
|
|
98
|
+
export declare function toQueryString(q?: RequestOptions["query"]): string;
|
|
99
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/api/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { SafeParseResult } from "./types";
|
|
3
|
+
export declare function safeParse<T>(schema: z.ZodTypeAny, data: unknown): SafeParseResult<T>;
|
|
4
|
+
export declare function parseOrThrow<T>(schema: z.ZodTypeAny, data: unknown): T;
|
|
5
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/api/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAY,eAAe,EAAE,MAAM,SAAS,CAAC;AAEpD,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,UAAU,EAAE,IAAI,EAAE,OAAO,GAAG,eAAe,CAAC,CAAC,CAAC,CAIpF;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,UAAU,EAAE,IAAI,EAAE,OAAO,GAAG,CAAC,CAMtE"}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "zlient",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A type-safe HTTP client framework with Zod validation",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.cjs",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "rolldown -c && tsc --emitDeclarationOnly --outDir dist",
|
|
21
|
+
"prepublishOnly": "bun run build"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"http-client",
|
|
25
|
+
"api-client",
|
|
26
|
+
"zod",
|
|
27
|
+
"validation",
|
|
28
|
+
"typescript"
|
|
29
|
+
],
|
|
30
|
+
"author": "",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/bun": "latest",
|
|
34
|
+
"rolldown": "^1.0.0-beta.44",
|
|
35
|
+
"typescript": "^5.9.3"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"zod": "^3.0.0 || ^4.0.0"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"zod": "^4.1.12"
|
|
42
|
+
}
|
|
43
|
+
}
|