@flow-conductor/adapter-axios 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 ADDED
@@ -0,0 +1,527 @@
1
+ # @flow-conductor/adapter-axios
2
+
3
+ Axios adapter for flow-conductor. This adapter uses Axios for making HTTP requests, providing features like request/response interceptors, automatic JSON parsing, and better error handling.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @flow-conductor/adapter-axios @flow-conductor/core axios
9
+ ```
10
+
11
+ **Note**: Both `@flow-conductor/core` and `axios` are peer dependencies and must be installed alongside this package.
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { begin } from "@flow-conductor/core";
17
+ import { AxiosRequestAdapter } from "@flow-conductor/adapter-axios";
18
+
19
+ const adapter = new AxiosRequestAdapter();
20
+
21
+ const result = await begin(
22
+ {
23
+ config: {
24
+ url: "https://api.example.com/users",
25
+ method: "GET",
26
+ },
27
+ },
28
+ adapter
29
+ ).execute();
30
+
31
+ console.log(result.data); // Axios automatically parses JSON
32
+ console.log(result.status); // HTTP status code
33
+ console.log(result.headers); // Response headers
34
+ ```
35
+
36
+ ## Usage
37
+
38
+ ### Basic GET Request
39
+
40
+ ```typescript
41
+ import { begin } from "@flow-conductor/core";
42
+ import { AxiosRequestAdapter } from "@flow-conductor/adapter-axios";
43
+
44
+ const adapter = new AxiosRequestAdapter();
45
+
46
+ const result = await begin(
47
+ {
48
+ config: {
49
+ url: "https://api.example.com/users/1",
50
+ method: "GET",
51
+ },
52
+ },
53
+ adapter
54
+ ).execute();
55
+
56
+ const user = result.data; // Already parsed JSON
57
+ console.log(user);
58
+ ```
59
+
60
+ ### POST Request with Data
61
+
62
+ ```typescript
63
+ import { begin } from "@flow-conductor/core";
64
+ import { AxiosRequestAdapter } from "@flow-conductor/adapter-axios";
65
+
66
+ const adapter = new AxiosRequestAdapter();
67
+
68
+ const result = await begin(
69
+ {
70
+ config: {
71
+ url: "https://api.example.com/users",
72
+ method: "POST",
73
+ data: {
74
+ name: "John Doe",
75
+ email: "john@example.com",
76
+ },
77
+ },
78
+ },
79
+ adapter
80
+ ).execute();
81
+
82
+ const newUser = result.data;
83
+ console.log(newUser);
84
+ ```
85
+
86
+ ### Request with Custom Headers
87
+
88
+ ```typescript
89
+ import { begin } from "@flow-conductor/core";
90
+ import { AxiosRequestAdapter } from "@flow-conductor/adapter-axios";
91
+
92
+ const adapter = new AxiosRequestAdapter();
93
+
94
+ const result = await begin(
95
+ {
96
+ config: {
97
+ url: "https://api.example.com/users",
98
+ method: "GET",
99
+ headers: {
100
+ Authorization: "Bearer your-token-here",
101
+ "X-Custom-Header": "value",
102
+ },
103
+ },
104
+ },
105
+ adapter
106
+ ).execute();
107
+ ```
108
+
109
+ ### Chained Requests
110
+
111
+ ```typescript
112
+ import { begin } from "@flow-conductor/core";
113
+ import { AxiosRequestAdapter } from "@flow-conductor/adapter-axios";
114
+
115
+ const adapter = new AxiosRequestAdapter();
116
+
117
+ const result = await begin(
118
+ {
119
+ config: {
120
+ url: "https://api.example.com/users/1",
121
+ method: "GET",
122
+ },
123
+ },
124
+ adapter
125
+ )
126
+ .next({
127
+ config: (previousResult) => {
128
+ const user = previousResult.data;
129
+ return {
130
+ url: `https://api.example.com/users/${user.id}/posts`,
131
+ method: "GET",
132
+ };
133
+ },
134
+ })
135
+ .execute();
136
+
137
+ const posts = result.data;
138
+ console.log(posts);
139
+ ```
140
+
141
+ ## Configuration
142
+
143
+ The `AxiosRequestAdapter` accepts `IRequestConfig` objects and extends them with Axios-specific options. The adapter automatically:
144
+
145
+ - Parses JSON responses (available in `result.data`)
146
+ - Handles request data serialization
147
+ - Supports all Axios configuration options
148
+
149
+ ### Request Config Interface
150
+
151
+ ```typescript
152
+ interface AxiosRequestConfigType extends IRequestConfig {
153
+ url: string;
154
+ method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
155
+ data?: any; // Automatically serialized by Axios
156
+ headers?: Record<string, string>;
157
+ // ... other Axios options (params, timeout, auth, etc.)
158
+ }
159
+ ```
160
+
161
+ ### Supported Axios Options
162
+
163
+ All standard Axios configuration options are supported:
164
+
165
+ ```typescript
166
+ const result = await begin(
167
+ {
168
+ config: {
169
+ url: "https://api.example.com/users",
170
+ method: "GET",
171
+ headers: {
172
+ Authorization: "Bearer token",
173
+ },
174
+ params: {
175
+ page: 1,
176
+ limit: 10,
177
+ },
178
+ timeout: 5000,
179
+ auth: {
180
+ username: "user",
181
+ password: "pass",
182
+ },
183
+ // ... any other AxiosRequestConfig options
184
+ },
185
+ },
186
+ adapter
187
+ ).execute();
188
+ ```
189
+
190
+ ### Query Parameters
191
+
192
+ Axios handles query parameters separately from the URL:
193
+
194
+ ```typescript
195
+ const result = await begin(
196
+ {
197
+ config: {
198
+ url: "https://api.example.com/users",
199
+ method: "GET",
200
+ params: {
201
+ page: 1,
202
+ limit: 10,
203
+ sort: "name",
204
+ },
205
+ },
206
+ },
207
+ adapter
208
+ ).execute();
209
+ ```
210
+
211
+ ### Data Handling
212
+
213
+ Axios automatically handles data serialization:
214
+
215
+ - **Objects**: Automatically JSON stringified
216
+ - **FormData**: Sent as multipart/form-data
217
+ - **URLSearchParams**: Sent as application/x-www-form-urlencoded
218
+ - **Strings**: Sent as-is
219
+
220
+ ```typescript
221
+ // POST with JSON data
222
+ const result = await begin(
223
+ {
224
+ config: {
225
+ url: "https://api.example.com/users",
226
+ method: "POST",
227
+ data: { name: "John", email: "john@example.com" },
228
+ // Content-Type: application/json is automatically set
229
+ },
230
+ },
231
+ adapter
232
+ ).execute();
233
+
234
+ // POST with FormData
235
+ const formData = new FormData();
236
+ formData.append("file", fileBlob);
237
+
238
+ const uploadResult = await begin(
239
+ {
240
+ config: {
241
+ url: "https://api.example.com/upload",
242
+ method: "POST",
243
+ data: formData,
244
+ // Content-Type: multipart/form-data is automatically set
245
+ },
246
+ },
247
+ adapter
248
+ ).execute();
249
+ ```
250
+
251
+ ### Custom Axios Instance
252
+
253
+ You can create a custom Axios instance with default configuration:
254
+
255
+ ```typescript
256
+ import axios from "axios";
257
+ import { AxiosRequestAdapter } from "@flow-conductor/adapter-axios";
258
+
259
+ // Create a custom axios instance
260
+ const axiosInstance = axios.create({
261
+ baseURL: "https://api.example.com",
262
+ timeout: 5000,
263
+ headers: {
264
+ "X-Custom-Header": "value",
265
+ },
266
+ });
267
+
268
+ // Note: The adapter uses the default axios instance
269
+ // For custom instances, you may need to extend the adapter
270
+ ```
271
+
272
+ ## Response Handling
273
+
274
+ The adapter returns an `AxiosResponse` object with the following properties:
275
+
276
+ ```typescript
277
+ const result = await begin(
278
+ {
279
+ config: {
280
+ url: "https://api.example.com/users",
281
+ method: "GET",
282
+ },
283
+ },
284
+ adapter
285
+ ).execute();
286
+
287
+ // Response properties
288
+ console.log(result.data); // Response data (automatically parsed JSON)
289
+ console.log(result.status); // HTTP status code
290
+ console.log(result.statusText); // HTTP status text
291
+ console.log(result.headers); // Response headers
292
+ console.log(result.config); // Request configuration
293
+ ```
294
+
295
+ ### Automatic JSON Parsing
296
+
297
+ Unlike the Fetch API, Axios automatically parses JSON responses:
298
+
299
+ ```typescript
300
+ const result = await begin(
301
+ {
302
+ config: {
303
+ url: "https://api.example.com/users/1",
304
+ method: "GET",
305
+ },
306
+ },
307
+ adapter
308
+ ).execute();
309
+
310
+ // No need to call .json() - data is already parsed
311
+ const user = result.data; // Already a JavaScript object
312
+ console.log(user.name);
313
+ ```
314
+
315
+ ## Error Handling
316
+
317
+ Axios throws errors for HTTP error statuses (4xx, 5xx), making error handling more straightforward:
318
+
319
+ ```typescript
320
+ import { begin } from "@flow-conductor/core";
321
+ import { AxiosRequestAdapter } from "@flow-conductor/adapter-axios";
322
+ import { AxiosError } from "axios";
323
+
324
+ const adapter = new AxiosRequestAdapter();
325
+
326
+ try {
327
+ const result = await begin(
328
+ {
329
+ config: {
330
+ url: "https://api.example.com/users",
331
+ method: "GET",
332
+ },
333
+ },
334
+ adapter
335
+ )
336
+ .withErrorHandler((error) => {
337
+ if (error instanceof AxiosError) {
338
+ console.error("Request failed:", error.message);
339
+ console.error("Status:", error.response?.status);
340
+ console.error("Data:", error.response?.data);
341
+ }
342
+ })
343
+ .execute();
344
+
345
+ const data = result.data;
346
+ console.log(data);
347
+ } catch (error) {
348
+ if (error instanceof AxiosError) {
349
+ // Handle Axios-specific errors
350
+ console.error("Axios error:", error.response?.data);
351
+ } else {
352
+ console.error("Error:", error);
353
+ }
354
+ }
355
+ ```
356
+
357
+ ### Error Response Access
358
+
359
+ Axios provides detailed error information:
360
+
361
+ ```typescript
362
+ try {
363
+ await begin(
364
+ {
365
+ config: {
366
+ url: "https://api.example.com/users",
367
+ method: "GET",
368
+ },
369
+ },
370
+ adapter
371
+ ).execute();
372
+ } catch (error) {
373
+ if (error instanceof AxiosError) {
374
+ if (error.response) {
375
+ // Server responded with error status
376
+ console.error("Status:", error.response.status);
377
+ console.error("Data:", error.response.data);
378
+ console.error("Headers:", error.response.headers);
379
+ } else if (error.request) {
380
+ // Request made but no response received
381
+ console.error("No response:", error.request);
382
+ } else {
383
+ // Error setting up request
384
+ console.error("Error:", error.message);
385
+ }
386
+ }
387
+ }
388
+ ```
389
+
390
+ ## Examples
391
+
392
+ ### Authentication Flow
393
+
394
+ ```typescript
395
+ import { begin } from "@flow-conductor/core";
396
+ import { AxiosRequestAdapter } from "@flow-conductor/adapter-axios";
397
+
398
+ const adapter = new AxiosRequestAdapter();
399
+
400
+ // Login and use token
401
+ const userData = await begin(
402
+ {
403
+ config: {
404
+ url: "https://api.example.com/auth/login",
405
+ method: "POST",
406
+ data: { username: "user", password: "pass" },
407
+ },
408
+ },
409
+ adapter
410
+ )
411
+ .next({
412
+ config: (previousResult) => {
413
+ const auth = previousResult.data;
414
+ return {
415
+ url: "https://api.example.com/user/profile",
416
+ method: "GET",
417
+ headers: { Authorization: `Bearer ${auth.token}` },
418
+ };
419
+ },
420
+ })
421
+ .execute();
422
+
423
+ const profile = userData.data;
424
+ console.log(profile);
425
+ ```
426
+
427
+ ### File Upload
428
+
429
+ ```typescript
430
+ import { begin } from "@flow-conductor/core";
431
+ import { AxiosRequestAdapter } from "@flow-conductor/adapter-axios";
432
+
433
+ const adapter = new AxiosRequestAdapter();
434
+
435
+ const formData = new FormData();
436
+ formData.append("file", fileBlob);
437
+ formData.append("name", "document.pdf");
438
+
439
+ const result = await begin(
440
+ {
441
+ config: {
442
+ url: "https://api.example.com/upload",
443
+ method: "POST",
444
+ data: formData,
445
+ headers: {
446
+ "Content-Type": "multipart/form-data",
447
+ },
448
+ },
449
+ },
450
+ adapter
451
+ ).execute();
452
+
453
+ console.log(result.data);
454
+ ```
455
+
456
+ ### Request with Timeout
457
+
458
+ ```typescript
459
+ const result = await begin(
460
+ {
461
+ config: {
462
+ url: "https://api.example.com/users",
463
+ method: "GET",
464
+ timeout: 5000, // 5 seconds
465
+ },
466
+ },
467
+ adapter
468
+ ).execute();
469
+ ```
470
+
471
+ ### Request with Authentication
472
+
473
+ ```typescript
474
+ const result = await begin(
475
+ {
476
+ config: {
477
+ url: "https://api.example.com/users",
478
+ method: "GET",
479
+ auth: {
480
+ username: "user",
481
+ password: "pass",
482
+ },
483
+ },
484
+ },
485
+ adapter
486
+ ).execute();
487
+ ```
488
+
489
+ ## API Reference
490
+
491
+ ### AxiosRequestAdapter
492
+
493
+ ```typescript
494
+ class AxiosRequestAdapter extends RequestAdapter<AxiosResponse, AxiosRequestConfigType> {
495
+ createRequest(requestConfig: AxiosRequestConfigType): Promise<AxiosResponse>;
496
+ }
497
+ ```
498
+
499
+ ### AxiosRequestConfigType
500
+
501
+ ```typescript
502
+ type AxiosRequestConfigType = IRequestConfig & Partial<AxiosRequestConfig>;
503
+ ```
504
+
505
+ Extends `IRequestConfig` with all Axios configuration options.
506
+
507
+ ## Advantages over Fetch Adapter
508
+
509
+ - **Automatic JSON parsing**: No need to call `.json()` on responses
510
+ - **Better error handling**: Throws errors for HTTP error statuses
511
+ - **Request/Response interceptors**: Can be configured globally
512
+ - **Request cancellation**: Built-in support for canceling requests
513
+ - **Automatic request body serialization**: Handles FormData, URLSearchParams, etc.
514
+ - **Request/Response transformation**: Built-in support for transforming data
515
+ - **Progress tracking**: Support for upload/download progress
516
+
517
+ ## Requirements
518
+
519
+ - `@flow-conductor/core` (peer dependency)
520
+ - `axios` ^1.0.0 (peer dependency)
521
+ - Node.js 18+
522
+ - TypeScript 5.0+
523
+
524
+ ## License
525
+
526
+ MIT
527
+
@@ -0,0 +1,39 @@
1
+ import { RequestAdapter, IRequestConfig, UrlValidationOptions } from '@flow-conductor/core';
2
+ import { AxiosResponse, AxiosRequestConfig } from 'axios';
3
+
4
+ /**
5
+ * Extended request configuration type that combines IRequestConfig with Axios-specific options.
6
+ * Allows using all Axios configuration options while maintaining compatibility with flow-conductor.
7
+ */
8
+ type AxiosRequestConfigType = IRequestConfig & Partial<AxiosRequestConfig>;
9
+ /**
10
+ * Request adapter implementation using Axios as the underlying HTTP client.
11
+ * Provides seamless integration between flow-conductor and Axios.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const adapter = new AxiosRequestAdapter();
16
+ * const chain = begin(
17
+ * { config: { url: 'https://api.example.com/users', method: 'GET' } },
18
+ * adapter
19
+ * );
20
+ * ```
21
+ */
22
+ declare class AxiosRequestAdapter extends RequestAdapter<AxiosResponse, AxiosRequestConfigType> {
23
+ /**
24
+ * Creates a new AxiosRequestAdapter instance.
25
+ *
26
+ * @param urlValidationOptions - Optional URL validation options to prevent SSRF attacks
27
+ */
28
+ constructor(urlValidationOptions?: UrlValidationOptions);
29
+ /**
30
+ * Creates and executes an HTTP request using Axios.
31
+ * Converts flow-conductor request configuration to Axios format.
32
+ *
33
+ * @param requestConfig - The request configuration object
34
+ * @returns A promise that resolves to an AxiosResponse
35
+ */
36
+ createRequest(requestConfig: AxiosRequestConfigType): Promise<AxiosResponse>;
37
+ }
38
+
39
+ export { type AxiosRequestConfigType, AxiosRequestAdapter as default };
package/build/index.js ADDED
@@ -0,0 +1,35 @@
1
+ import { RequestAdapter } from '@flow-conductor/core';
2
+ import axios from 'axios';
3
+
4
+ // src/axios-request-adapter.ts
5
+ var AxiosRequestAdapter = class extends RequestAdapter {
6
+ /**
7
+ * Creates a new AxiosRequestAdapter instance.
8
+ *
9
+ * @param urlValidationOptions - Optional URL validation options to prevent SSRF attacks
10
+ */
11
+ constructor(urlValidationOptions) {
12
+ super(urlValidationOptions);
13
+ }
14
+ /**
15
+ * Creates and executes an HTTP request using Axios.
16
+ * Converts flow-conductor request configuration to Axios format.
17
+ *
18
+ * @param requestConfig - The request configuration object
19
+ * @returns A promise that resolves to an AxiosResponse
20
+ */
21
+ async createRequest(requestConfig) {
22
+ const { url, method, data, ...rest } = requestConfig;
23
+ const axiosConfig = {
24
+ url,
25
+ method: method.toLowerCase(),
26
+ data,
27
+ ...rest
28
+ };
29
+ return axios(axiosConfig);
30
+ }
31
+ };
32
+
33
+ export { AxiosRequestAdapter as default };
34
+ //# sourceMappingURL=index.js.map
35
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/axios-request-adapter.ts"],"names":[],"mappings":";;;;AA2BA,IAAqB,mBAAA,GAArB,cAAiD,cAAA,CAG/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,YAAY,oBAAA,EAA6C;AACvD,IAAA,KAAA,CAAM,oBAAoB,CAAA;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAa,cACX,aAAA,EACwB;AACxB,IAAA,MAAM,EAAE,GAAA,EAAK,MAAA,EAAQ,IAAA,EAAM,GAAG,MAAK,GAAI,aAAA;AAEvC,IAAA,MAAM,WAAA,GAAkC;AAAA,MACtC,GAAA;AAAA,MACA,MAAA,EAAQ,OAAO,WAAA,EAAY;AAAA,MAC3B,IAAA;AAAA,MACA,GAAG;AAAA,KACL;AAEA,IAAA,OAAO,MAAM,WAAW,CAAA;AAAA,EAC1B;AACF","file":"index.js","sourcesContent":["import { RequestAdapter } from \"@flow-conductor/core\";\nimport type {\n IRequestConfig,\n UrlValidationOptions,\n} from \"@flow-conductor/core\";\nimport axios, { type AxiosRequestConfig, type AxiosResponse } from \"axios\";\n\n/**\n * Extended request configuration type that combines IRequestConfig with Axios-specific options.\n * Allows using all Axios configuration options while maintaining compatibility with flow-conductor.\n */\nexport type AxiosRequestConfigType = IRequestConfig &\n Partial<AxiosRequestConfig>;\n\n/**\n * Request adapter implementation using Axios as the underlying HTTP client.\n * Provides seamless integration between flow-conductor and Axios.\n *\n * @example\n * ```typescript\n * const adapter = new AxiosRequestAdapter();\n * const chain = begin(\n * { config: { url: 'https://api.example.com/users', method: 'GET' } },\n * adapter\n * );\n * ```\n */\nexport default class AxiosRequestAdapter extends RequestAdapter<\n AxiosResponse,\n AxiosRequestConfigType\n> {\n /**\n * Creates a new AxiosRequestAdapter instance.\n *\n * @param urlValidationOptions - Optional URL validation options to prevent SSRF attacks\n */\n constructor(urlValidationOptions?: UrlValidationOptions) {\n super(urlValidationOptions);\n }\n\n /**\n * Creates and executes an HTTP request using Axios.\n * Converts flow-conductor request configuration to Axios format.\n *\n * @param requestConfig - The request configuration object\n * @returns A promise that resolves to an AxiosResponse\n */\n public async createRequest(\n requestConfig: AxiosRequestConfigType\n ): Promise<AxiosResponse> {\n const { url, method, data, ...rest } = requestConfig;\n\n const axiosConfig: AxiosRequestConfig = {\n url,\n method: method.toLowerCase() as AxiosRequestConfig[\"method\"],\n data,\n ...rest,\n };\n\n return axios(axiosConfig);\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@flow-conductor/adapter-axios",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "Axios adapter for flow-conductor",
6
+ "main": "./build/index.js",
7
+ "types": "./build/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./build/index.d.ts",
11
+ "import": "./build/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "build/**/*.js",
16
+ "build/**/*.d.ts",
17
+ "build/**/*.js.map",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "keywords": [
22
+ "flow-conductor",
23
+ "request",
24
+ "adapter",
25
+ "axios",
26
+ "http",
27
+ "typescript"
28
+ ],
29
+ "scripts": {
30
+ "test": "node --test --import tsx/esm 'src/__test__/**/*.test.ts'",
31
+ "test:watch": "node --test --import tsx/esm --watch 'src/__test__/**/*.test.ts'",
32
+ "build": "tsup",
33
+ "clean": "rm -rf build",
34
+ "lint": "eslint src --ext .ts",
35
+ "lint:fix": "eslint src --ext .ts --fix",
36
+ "type-check": "tsc --noEmit",
37
+ "prepublishOnly": "npm run clean && npm run lint && npm run type-check && npm run build"
38
+ },
39
+ "author": "Dawid Hermann",
40
+ "license": "MIT",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/dawidhermann/flow-conductor.git",
44
+ "directory": "packages/adapter-axios"
45
+ },
46
+ "sideEffects": false,
47
+ "publishConfig": {
48
+ "access": "public"
49
+ },
50
+ "engines": {
51
+ "node": ">=18.0.0"
52
+ },
53
+ "peerDependencies": {
54
+ "@flow-conductor/core": "^1.0.0",
55
+ "axios": "^1.0.0"
56
+ },
57
+ "devDependencies": {
58
+ "@eslint/js": "^9.39.2",
59
+ "@flow-conductor/core": "*",
60
+ "@typescript-eslint/eslint-plugin": "^8.50.1",
61
+ "@typescript-eslint/parser": "^8.50.1",
62
+ "axios": "^1.7.9",
63
+ "eslint": "^9.39.2",
64
+ "eslint-config-prettier": "^10.1.8",
65
+ "eslint-plugin-prettier": "^5.5.4",
66
+ "prettier": "^3.7.4",
67
+ "tsx": "^4.7.0",
68
+ "typescript": "^5.9.3",
69
+ "typescript-eslint": "^8.50.1"
70
+ }
71
+ }