@deliverart/sdk-js-core 2.1.49 → 2.1.51
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 +429 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
# @deliverart/sdk-js-core
|
|
2
|
+
|
|
3
|
+
Core package for the DeliverArt JavaScript SDK. Provides the API client, plugin system, and base request/response handling.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @deliverart/sdk-js-core
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @deliverart/sdk-js-core
|
|
11
|
+
# or
|
|
12
|
+
yarn add @deliverart/sdk-js-core
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Exported Types
|
|
16
|
+
|
|
17
|
+
### ApiClient
|
|
18
|
+
```typescript
|
|
19
|
+
type ApiClient<Extensions extends ApiExtension = NonNullable<unknown>>
|
|
20
|
+
```
|
|
21
|
+
The main API client interface that provides methods to make API calls and manage plugins.
|
|
22
|
+
|
|
23
|
+
**Methods:**
|
|
24
|
+
- `call<TInputSchema, TOutputSchema, Q, H>(request: AbstractApiRequest): Promise<output<TOutputSchema>>` - Execute an API request
|
|
25
|
+
- `addPlugin<T extends ApiExtension>(plugin: ApiClientPlugin<T>): ApiClient<T>` - Add a plugin to extend client functionality
|
|
26
|
+
- `addRequestMiddleware(middleware: RequestMiddleware): void` - Add middleware to intercept requests
|
|
27
|
+
- `addResponseMiddleware(middleware: ResponseMiddleware): void` - Add middleware to intercept responses
|
|
28
|
+
|
|
29
|
+
### AbstractApiRequest
|
|
30
|
+
```typescript
|
|
31
|
+
abstract class AbstractApiRequest<
|
|
32
|
+
TInputSchema extends ZodType<any, any, any>,
|
|
33
|
+
TOutputSchema extends ZodType<any, any, any>,
|
|
34
|
+
Query = unknown,
|
|
35
|
+
Headers = unknown
|
|
36
|
+
>
|
|
37
|
+
```
|
|
38
|
+
Base class for all API requests. Extend this class to create custom API requests.
|
|
39
|
+
|
|
40
|
+
**Abstract Properties:**
|
|
41
|
+
- `method: 'GET' | 'POST' | 'PATCH' | 'DELETE'` - HTTP method (required)
|
|
42
|
+
- `contentType: 'application/json' | 'multipart/form-data' | 'application/merge-patch+json'` - Content-Type header (required)
|
|
43
|
+
- `accept: 'application/json'` - Accept header (required)
|
|
44
|
+
- `inputSchema: TInputSchema` - Zod schema for request validation (required)
|
|
45
|
+
- `outputSchema: TOutputSchema` - Zod schema for response validation (required)
|
|
46
|
+
- `querySchema?: ZodType<Query>` - Zod schema for query parameters (optional)
|
|
47
|
+
- `headersSchema?: ZodType<Headers>` - Zod schema for headers (optional)
|
|
48
|
+
|
|
49
|
+
**Abstract Methods:**
|
|
50
|
+
- `getPath(): string` - Returns the API endpoint path (required)
|
|
51
|
+
|
|
52
|
+
**Built-in Methods:**
|
|
53
|
+
- `validateInput(): output<TInputSchema>` - Validates the request input
|
|
54
|
+
- `validateQuery(): Query | undefined` - Validates query parameters
|
|
55
|
+
- `validateHeaders(): Headers | undefined` - Validates headers
|
|
56
|
+
- `validateOutput(data: unknown): output<TOutputSchema>` - Validates the response
|
|
57
|
+
- `parseResponse(data: unknown, rawResponse?: Response): output<TOutputSchema>` - Parses and validates the response (can be overridden)
|
|
58
|
+
|
|
59
|
+
### ApiExtension
|
|
60
|
+
```typescript
|
|
61
|
+
type ApiExtension = Record<string, unknown>
|
|
62
|
+
```
|
|
63
|
+
Base type for plugin extensions that add new methods to the API client.
|
|
64
|
+
|
|
65
|
+
### ApiClientPlugin
|
|
66
|
+
```typescript
|
|
67
|
+
interface ApiClientPlugin<T extends ApiExtension = Record<string, unknown>> {
|
|
68
|
+
setup: (client: ApiClient<Record<string, unknown>>) => T
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
Interface for creating plugins that extend the API client functionality.
|
|
72
|
+
|
|
73
|
+
### Errors
|
|
74
|
+
|
|
75
|
+
#### InputValidationError
|
|
76
|
+
```typescript
|
|
77
|
+
class InputValidationError extends Error {
|
|
78
|
+
constructor(public readonly issues: ZodIssue[])
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
Thrown when request input validation fails.
|
|
82
|
+
|
|
83
|
+
**Properties:**
|
|
84
|
+
- `issues: ZodIssue[]` - Array of validation errors from Zod
|
|
85
|
+
|
|
86
|
+
#### OutputValidationError
|
|
87
|
+
```typescript
|
|
88
|
+
class OutputValidationError extends Error {
|
|
89
|
+
constructor(public readonly issues: ZodIssue[])
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
Thrown when response validation fails.
|
|
93
|
+
|
|
94
|
+
**Properties:**
|
|
95
|
+
- `issues: ZodIssue[]` - Array of validation errors from Zod
|
|
96
|
+
|
|
97
|
+
## Usage
|
|
98
|
+
|
|
99
|
+
### Creating the API Client
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import { createApiClient } from '@deliverart/sdk-js-core';
|
|
103
|
+
|
|
104
|
+
const client = createApiClient({
|
|
105
|
+
baseUrl: 'https://api.deliverart.com'
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Parameters:**
|
|
110
|
+
- `baseUrl: string` (required) - The base URL for all API requests
|
|
111
|
+
|
|
112
|
+
### Creating a Custom Request
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { AbstractApiRequest } from '@deliverart/sdk-js-core';
|
|
116
|
+
import { z } from 'zod';
|
|
117
|
+
|
|
118
|
+
// Define input schema
|
|
119
|
+
const createUserInputSchema = z.object({
|
|
120
|
+
name: z.string().min(1),
|
|
121
|
+
email: z.string().email(),
|
|
122
|
+
age: z.number().min(18).optional()
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Define output schema
|
|
126
|
+
const userResponseSchema = z.object({
|
|
127
|
+
id: z.string(),
|
|
128
|
+
name: z.string(),
|
|
129
|
+
email: z.string(),
|
|
130
|
+
age: z.number().nullable(),
|
|
131
|
+
createdAt: z.string()
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Define query parameters schema (optional)
|
|
135
|
+
const getUsersQuerySchema = z.object({
|
|
136
|
+
page: z.number().optional(),
|
|
137
|
+
limit: z.number().optional(),
|
|
138
|
+
search: z.string().optional()
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Create the request class
|
|
142
|
+
class CreateUser extends AbstractApiRequest<
|
|
143
|
+
typeof createUserInputSchema,
|
|
144
|
+
typeof userResponseSchema
|
|
145
|
+
> {
|
|
146
|
+
readonly method = 'POST';
|
|
147
|
+
readonly contentType = 'application/json';
|
|
148
|
+
readonly accept = 'application/json';
|
|
149
|
+
|
|
150
|
+
readonly inputSchema = createUserInputSchema;
|
|
151
|
+
readonly outputSchema = userResponseSchema;
|
|
152
|
+
readonly querySchema = undefined;
|
|
153
|
+
readonly headersSchema = undefined;
|
|
154
|
+
|
|
155
|
+
constructor(input: z.input<typeof createUserInputSchema>) {
|
|
156
|
+
super(input);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
getPath(): string {
|
|
160
|
+
return '/users';
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Request with path parameters
|
|
165
|
+
class GetUser extends AbstractApiRequest<
|
|
166
|
+
typeof z.undefined,
|
|
167
|
+
typeof userResponseSchema
|
|
168
|
+
> {
|
|
169
|
+
readonly method = 'GET';
|
|
170
|
+
readonly contentType = 'application/json';
|
|
171
|
+
readonly accept = 'application/json';
|
|
172
|
+
|
|
173
|
+
readonly inputSchema = z.undefined();
|
|
174
|
+
readonly outputSchema = userResponseSchema;
|
|
175
|
+
readonly querySchema = undefined;
|
|
176
|
+
readonly headersSchema = undefined;
|
|
177
|
+
|
|
178
|
+
private readonly userId: string;
|
|
179
|
+
|
|
180
|
+
constructor(userId: string) {
|
|
181
|
+
super(undefined);
|
|
182
|
+
this.userId = userId;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
getPath(): string {
|
|
186
|
+
return `/users/${this.userId}`;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Request with query parameters
|
|
191
|
+
class GetUsers extends AbstractApiRequest<
|
|
192
|
+
typeof z.undefined,
|
|
193
|
+
typeof z.array(userResponseSchema),
|
|
194
|
+
z.infer<typeof getUsersQuerySchema>
|
|
195
|
+
> {
|
|
196
|
+
readonly method = 'GET';
|
|
197
|
+
readonly contentType = 'application/json';
|
|
198
|
+
readonly accept = 'application/json';
|
|
199
|
+
|
|
200
|
+
readonly inputSchema = z.undefined();
|
|
201
|
+
readonly outputSchema = z.array(userResponseSchema);
|
|
202
|
+
readonly querySchema = getUsersQuerySchema;
|
|
203
|
+
readonly headersSchema = undefined;
|
|
204
|
+
|
|
205
|
+
constructor(options?: { query?: z.infer<typeof getUsersQuerySchema> }) {
|
|
206
|
+
super(undefined, options);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
getPath(): string {
|
|
210
|
+
return '/users';
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Making API Calls
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
import { client } from './client';
|
|
219
|
+
|
|
220
|
+
// Create a user
|
|
221
|
+
const newUser = await client.call(new CreateUser({
|
|
222
|
+
name: 'John Doe',
|
|
223
|
+
email: 'john@example.com',
|
|
224
|
+
age: 25
|
|
225
|
+
}));
|
|
226
|
+
|
|
227
|
+
// Get a user
|
|
228
|
+
const user = await client.call(new GetUser('user-123'));
|
|
229
|
+
|
|
230
|
+
// Get users with filters
|
|
231
|
+
const users = await client.call(new GetUsers({
|
|
232
|
+
query: {
|
|
233
|
+
page: 1,
|
|
234
|
+
limit: 10,
|
|
235
|
+
search: 'john'
|
|
236
|
+
}
|
|
237
|
+
}));
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Adding Middleware
|
|
241
|
+
|
|
242
|
+
#### Request Middleware
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
client.addRequestMiddleware(async (ctx) => {
|
|
246
|
+
console.log('Making request to:', ctx.url.toString());
|
|
247
|
+
|
|
248
|
+
// Modify headers
|
|
249
|
+
return {
|
|
250
|
+
...ctx,
|
|
251
|
+
init: {
|
|
252
|
+
...ctx.init,
|
|
253
|
+
headers: {
|
|
254
|
+
...ctx.init.headers,
|
|
255
|
+
'X-Custom-Header': 'value'
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
});
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**Parameters:**
|
|
263
|
+
- `ctx.url: URL` - The request URL
|
|
264
|
+
- `ctx.init: RequestInit` - The fetch init options
|
|
265
|
+
|
|
266
|
+
**Return:** Modified `RequestContext` or the original context
|
|
267
|
+
|
|
268
|
+
#### Response Middleware
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
client.addResponseMiddleware(async (parsed, requestCtx) => {
|
|
272
|
+
console.log('Response status:', parsed.response.status);
|
|
273
|
+
console.log('Response body:', parsed.body);
|
|
274
|
+
|
|
275
|
+
return parsed;
|
|
276
|
+
});
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Parameters:**
|
|
280
|
+
- `parsed.response: Response` - The fetch Response object
|
|
281
|
+
- `parsed.body: unknown` - The parsed response body
|
|
282
|
+
- `requestCtx: RequestContext` - The original request context
|
|
283
|
+
|
|
284
|
+
**Return:** Modified `ParsedResponse` or the original parsed response
|
|
285
|
+
|
|
286
|
+
### Creating a Plugin
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
import { ApiClient, ApiClientPlugin, ApiExtension } from '@deliverart/sdk-js-core';
|
|
290
|
+
|
|
291
|
+
// Define the extension interface
|
|
292
|
+
interface LoggerExtension extends ApiExtension {
|
|
293
|
+
logger: {
|
|
294
|
+
enable: () => void;
|
|
295
|
+
disable: () => void;
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Create the plugin
|
|
300
|
+
class LoggerPlugin implements ApiClientPlugin<LoggerExtension> {
|
|
301
|
+
private enabled = true;
|
|
302
|
+
|
|
303
|
+
setup(client: ApiClient): LoggerExtension {
|
|
304
|
+
// Add request middleware
|
|
305
|
+
client.addRequestMiddleware(async (ctx) => {
|
|
306
|
+
if (this.enabled) {
|
|
307
|
+
console.log('[Request]', ctx.init.method, ctx.url.toString());
|
|
308
|
+
}
|
|
309
|
+
return ctx;
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// Add response middleware
|
|
313
|
+
client.addResponseMiddleware(async (parsed, ctx) => {
|
|
314
|
+
if (this.enabled) {
|
|
315
|
+
console.log('[Response]', parsed.response.status);
|
|
316
|
+
}
|
|
317
|
+
return parsed;
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// Return extension methods
|
|
321
|
+
return {
|
|
322
|
+
logger: {
|
|
323
|
+
enable: () => {
|
|
324
|
+
this.enabled = true;
|
|
325
|
+
},
|
|
326
|
+
disable: () => {
|
|
327
|
+
this.enabled = false;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Use the plugin
|
|
335
|
+
const client = createApiClient({ baseUrl: 'https://api.example.com' })
|
|
336
|
+
.addPlugin(new LoggerPlugin());
|
|
337
|
+
|
|
338
|
+
// Now you can use the logger methods
|
|
339
|
+
client.logger.disable();
|
|
340
|
+
client.logger.enable();
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Error Handling
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
import { InputValidationError, OutputValidationError } from '@deliverart/sdk-js-core';
|
|
347
|
+
|
|
348
|
+
try {
|
|
349
|
+
await client.call(new CreateUser({
|
|
350
|
+
name: '', // Invalid: min length is 1
|
|
351
|
+
email: 'invalid-email', // Invalid email format
|
|
352
|
+
}));
|
|
353
|
+
} catch (error) {
|
|
354
|
+
if (error instanceof InputValidationError) {
|
|
355
|
+
console.error('Input validation failed:');
|
|
356
|
+
error.issues.forEach(issue => {
|
|
357
|
+
console.error(`- ${issue.path.join('.')}: ${issue.message}`);
|
|
358
|
+
});
|
|
359
|
+
} else if (error instanceof OutputValidationError) {
|
|
360
|
+
console.error('Response validation failed:');
|
|
361
|
+
error.issues.forEach(issue => {
|
|
362
|
+
console.error(`- ${issue.path.join('.')}: ${issue.message}`);
|
|
363
|
+
});
|
|
364
|
+
} else {
|
|
365
|
+
console.error('Other error:', error);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## Advanced Features
|
|
371
|
+
|
|
372
|
+
### Custom Response Parsing
|
|
373
|
+
|
|
374
|
+
Override `parseResponse` to customize how responses are parsed:
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
import { Paginated, responseToPagination } from '@deliverart/sdk-js-global-types';
|
|
378
|
+
|
|
379
|
+
class GetUsers extends AbstractApiRequest</*...*/> {
|
|
380
|
+
// ... other properties ...
|
|
381
|
+
|
|
382
|
+
parseResponse(data: unknown, rawResponse: Response): Paginated<User> {
|
|
383
|
+
const users = z.array(userResponseSchema).parse(data);
|
|
384
|
+
|
|
385
|
+
return this.validateOutput({
|
|
386
|
+
data: users,
|
|
387
|
+
pagination: responseToPagination(rawResponse)
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### FormData Requests
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
const uploadImageInputSchema = z.instanceof(FormData);
|
|
397
|
+
|
|
398
|
+
class UploadImage extends AbstractApiRequest<
|
|
399
|
+
typeof uploadImageInputSchema,
|
|
400
|
+
typeof imageResponseSchema
|
|
401
|
+
> {
|
|
402
|
+
readonly method = 'POST';
|
|
403
|
+
readonly contentType = 'multipart/form-data';
|
|
404
|
+
readonly accept = 'application/json';
|
|
405
|
+
|
|
406
|
+
readonly inputSchema = uploadImageInputSchema;
|
|
407
|
+
readonly outputSchema = imageResponseSchema;
|
|
408
|
+
|
|
409
|
+
constructor(formData: FormData) {
|
|
410
|
+
super(formData);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
getPath(): string {
|
|
414
|
+
return '/images';
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Usage
|
|
419
|
+
const formData = new FormData();
|
|
420
|
+
formData.append('file', fileBlob);
|
|
421
|
+
formData.append('name', 'My Image');
|
|
422
|
+
|
|
423
|
+
const image = await client.call(new UploadImage(formData));
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
## License
|
|
427
|
+
|
|
428
|
+
MIT
|
|
429
|
+
|
package/package.json
CHANGED