@flow-conductor/adapter-node-fetch 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 +466 -0
- package/build/index.d.ts +39 -0
- package/build/index.js +56 -0
- package/build/index.js.map +1 -0
- package/package.json +72 -0
package/README.md
ADDED
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
# @flow-conductor/adapter-node-fetch
|
|
2
|
+
|
|
3
|
+
node-fetch adapter for flow-conductor. This adapter uses node-fetch, making it ideal for Node.js environments where you need a reliable HTTP client.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @flow-conductor/adapter-node-fetch @flow-conductor/core node-fetch
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**Note**: `@flow-conductor/core` and `node-fetch` 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 { NodeFetchRequestAdapter } from "@flow-conductor/adapter-node-fetch";
|
|
18
|
+
|
|
19
|
+
const adapter = new NodeFetchRequestAdapter();
|
|
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
|
+
const data = await result.json();
|
|
32
|
+
console.log(data);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
### Basic GET Request
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { begin } from "@flow-conductor/core";
|
|
41
|
+
import { NodeFetchRequestAdapter } from "@flow-conductor/adapter-node-fetch";
|
|
42
|
+
|
|
43
|
+
const adapter = new NodeFetchRequestAdapter();
|
|
44
|
+
|
|
45
|
+
const result = await begin(
|
|
46
|
+
{
|
|
47
|
+
config: {
|
|
48
|
+
url: "https://api.example.com/users/1",
|
|
49
|
+
method: "GET",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
adapter
|
|
53
|
+
).execute();
|
|
54
|
+
|
|
55
|
+
const user = await result.json();
|
|
56
|
+
console.log(user);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### POST Request with Data
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { begin } from "@flow-conductor/core";
|
|
63
|
+
import { NodeFetchRequestAdapter } from "@flow-conductor/adapter-node-fetch";
|
|
64
|
+
|
|
65
|
+
const adapter = new NodeFetchRequestAdapter();
|
|
66
|
+
|
|
67
|
+
const result = await begin(
|
|
68
|
+
{
|
|
69
|
+
config: {
|
|
70
|
+
url: "https://api.example.com/users",
|
|
71
|
+
method: "POST",
|
|
72
|
+
data: {
|
|
73
|
+
name: "John Doe",
|
|
74
|
+
email: "john@example.com",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
adapter
|
|
79
|
+
).execute();
|
|
80
|
+
|
|
81
|
+
const newUser = await result.json();
|
|
82
|
+
console.log(newUser);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Request with Custom Headers
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { begin } from "@flow-conductor/core";
|
|
89
|
+
import { NodeFetchRequestAdapter } from "@flow-conductor/adapter-node-fetch";
|
|
90
|
+
|
|
91
|
+
const adapter = new NodeFetchRequestAdapter();
|
|
92
|
+
|
|
93
|
+
const result = await begin(
|
|
94
|
+
{
|
|
95
|
+
config: {
|
|
96
|
+
url: "https://api.example.com/users",
|
|
97
|
+
method: "GET",
|
|
98
|
+
headers: {
|
|
99
|
+
Authorization: "Bearer your-token-here",
|
|
100
|
+
"X-Custom-Header": "value",
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
adapter
|
|
105
|
+
).execute();
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Chained Requests
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { begin } from "@flow-conductor/core";
|
|
112
|
+
import { NodeFetchRequestAdapter } from "@flow-conductor/adapter-node-fetch";
|
|
113
|
+
|
|
114
|
+
const adapter = new NodeFetchRequestAdapter();
|
|
115
|
+
|
|
116
|
+
const result = await begin(
|
|
117
|
+
{
|
|
118
|
+
config: {
|
|
119
|
+
url: "https://api.example.com/users/1",
|
|
120
|
+
method: "GET",
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
adapter
|
|
124
|
+
)
|
|
125
|
+
.next({
|
|
126
|
+
config: async (previousResult) => {
|
|
127
|
+
const user = await previousResult.json();
|
|
128
|
+
return {
|
|
129
|
+
url: `https://api.example.com/users/${user.id}/posts`,
|
|
130
|
+
method: "GET",
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
})
|
|
134
|
+
.execute();
|
|
135
|
+
|
|
136
|
+
const posts = await result.json();
|
|
137
|
+
console.log(posts);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Configuration
|
|
141
|
+
|
|
142
|
+
The `NodeFetchRequestAdapter` accepts standard `IRequestConfig` objects compatible with node-fetch. The adapter automatically:
|
|
143
|
+
|
|
144
|
+
- JSON stringifies `data` for non-GET requests
|
|
145
|
+
- Sets `Content-Type: application/json` header when data is provided
|
|
146
|
+
- Passes through all other node-fetch options
|
|
147
|
+
|
|
148
|
+
### Request Config Interface
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
interface NodeFetchRequestConfig extends IRequestConfig {
|
|
152
|
+
url: string;
|
|
153
|
+
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
|
|
154
|
+
data?: any; // Will be JSON stringified for non-GET requests
|
|
155
|
+
headers?: Record<string, string>;
|
|
156
|
+
// ... other fetch options (redirect, timeout, etc.)
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Supported Fetch Options
|
|
161
|
+
|
|
162
|
+
All standard node-fetch options are supported:
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
const result = await begin(
|
|
166
|
+
{
|
|
167
|
+
config: {
|
|
168
|
+
url: "https://api.example.com/users",
|
|
169
|
+
method: "GET",
|
|
170
|
+
headers: {
|
|
171
|
+
"Authorization": "Bearer token",
|
|
172
|
+
},
|
|
173
|
+
redirect: "follow", // Redirect handling
|
|
174
|
+
timeout: 5000, // Request timeout
|
|
175
|
+
// ... any other RequestInit options
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
adapter
|
|
179
|
+
).execute();
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Data Handling
|
|
183
|
+
|
|
184
|
+
The adapter automatically handles data serialization:
|
|
185
|
+
|
|
186
|
+
- **GET requests**: Data is ignored (use query parameters in URL instead)
|
|
187
|
+
- **Other methods**: Data is JSON stringified and sent as the request body
|
|
188
|
+
- **Content-Type**: Automatically set to `application/json` when data is provided
|
|
189
|
+
- **String data**: Passed through as-is
|
|
190
|
+
- **Buffer/Uint8Array**: Passed through as binary data
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
// POST with JSON data
|
|
194
|
+
const result = await begin(
|
|
195
|
+
{
|
|
196
|
+
config: {
|
|
197
|
+
url: "https://api.example.com/users",
|
|
198
|
+
method: "POST",
|
|
199
|
+
data: { name: "John", email: "john@example.com" },
|
|
200
|
+
// Content-Type: application/json is automatically added
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
adapter
|
|
204
|
+
).execute();
|
|
205
|
+
|
|
206
|
+
// POST with string data
|
|
207
|
+
const textResult = await begin(
|
|
208
|
+
{
|
|
209
|
+
config: {
|
|
210
|
+
url: "https://api.example.com/data",
|
|
211
|
+
method: "POST",
|
|
212
|
+
data: "raw string data",
|
|
213
|
+
// Content-Type will not be automatically set for string data
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
adapter
|
|
217
|
+
).execute();
|
|
218
|
+
|
|
219
|
+
// PUT with JSON data
|
|
220
|
+
const updateResult = await begin(
|
|
221
|
+
{
|
|
222
|
+
config: {
|
|
223
|
+
url: "https://api.example.com/users/1",
|
|
224
|
+
method: "PUT",
|
|
225
|
+
data: { name: "Jane" },
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
adapter
|
|
229
|
+
).execute();
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Custom Headers
|
|
233
|
+
|
|
234
|
+
You can provide custom headers, which will be merged with default headers:
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
const result = await begin(
|
|
238
|
+
{
|
|
239
|
+
config: {
|
|
240
|
+
url: "https://api.example.com/users",
|
|
241
|
+
method: "POST",
|
|
242
|
+
data: { name: "John" },
|
|
243
|
+
headers: {
|
|
244
|
+
"Authorization": "Bearer token",
|
|
245
|
+
"X-Custom-Header": "value",
|
|
246
|
+
// Content-Type will be automatically added if not specified
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
adapter
|
|
251
|
+
).execute();
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Response Handling
|
|
255
|
+
|
|
256
|
+
The adapter returns a standard `Response` object from node-fetch. You can use all standard Response methods:
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
const result = await begin(
|
|
260
|
+
{
|
|
261
|
+
config: {
|
|
262
|
+
url: "https://api.example.com/users",
|
|
263
|
+
method: "GET",
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
adapter
|
|
267
|
+
).execute();
|
|
268
|
+
|
|
269
|
+
// Standard Response methods
|
|
270
|
+
const json = await result.json();
|
|
271
|
+
const text = await result.text();
|
|
272
|
+
const blob = await result.blob();
|
|
273
|
+
const arrayBuffer = await result.arrayBuffer();
|
|
274
|
+
|
|
275
|
+
// Response properties
|
|
276
|
+
console.log(result.status); // HTTP status code
|
|
277
|
+
console.log(result.statusText); // Status text
|
|
278
|
+
console.log(result.ok); // true if status 200-299
|
|
279
|
+
console.log(result.headers); // Headers object
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Error Handling
|
|
283
|
+
|
|
284
|
+
node-fetch throws errors for network failures and rejects on HTTP error statuses (depending on configuration). You can handle errors using flow-conductor's error handling:
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import { begin } from "@flow-conductor/core";
|
|
288
|
+
import { NodeFetchRequestAdapter } from "@flow-conductor/adapter-node-fetch";
|
|
289
|
+
|
|
290
|
+
const adapter = new NodeFetchRequestAdapter();
|
|
291
|
+
|
|
292
|
+
try {
|
|
293
|
+
const result = await begin(
|
|
294
|
+
{
|
|
295
|
+
config: {
|
|
296
|
+
url: "https://api.example.com/users",
|
|
297
|
+
method: "GET",
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
adapter
|
|
301
|
+
)
|
|
302
|
+
.withErrorHandler((error) => {
|
|
303
|
+
console.error("Request failed:", error);
|
|
304
|
+
})
|
|
305
|
+
.execute();
|
|
306
|
+
|
|
307
|
+
if (!result.ok) {
|
|
308
|
+
throw new Error(`HTTP error! status: ${result.status}`);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const data = await result.json();
|
|
312
|
+
console.log(data);
|
|
313
|
+
} catch (error) {
|
|
314
|
+
console.error("Error:", error);
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Node.js Environment
|
|
319
|
+
|
|
320
|
+
This adapter is specifically designed for Node.js environments and uses `node-fetch` v3, which is ESM-only. Make sure your project is configured for ESM:
|
|
321
|
+
|
|
322
|
+
```json
|
|
323
|
+
{
|
|
324
|
+
"type": "module"
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Requirements
|
|
329
|
+
|
|
330
|
+
- Node.js 18+ (for native ESM support)
|
|
331
|
+
- node-fetch v3.x
|
|
332
|
+
|
|
333
|
+
## Examples
|
|
334
|
+
|
|
335
|
+
### Authentication Flow
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
import { begin } from "@flow-conductor/core";
|
|
339
|
+
import { NodeFetchRequestAdapter } from "@flow-conductor/adapter-node-fetch";
|
|
340
|
+
|
|
341
|
+
const adapter = new NodeFetchRequestAdapter();
|
|
342
|
+
|
|
343
|
+
// Login and use token
|
|
344
|
+
const userData = await begin(
|
|
345
|
+
{
|
|
346
|
+
config: {
|
|
347
|
+
url: "https://api.example.com/auth/login",
|
|
348
|
+
method: "POST",
|
|
349
|
+
data: { username: "user", password: "pass" },
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
adapter
|
|
353
|
+
)
|
|
354
|
+
.next({
|
|
355
|
+
config: async (previousResult) => {
|
|
356
|
+
const auth = await previousResult.json();
|
|
357
|
+
return {
|
|
358
|
+
url: "https://api.example.com/user/profile",
|
|
359
|
+
method: "GET",
|
|
360
|
+
headers: { Authorization: `Bearer ${auth.token}` },
|
|
361
|
+
};
|
|
362
|
+
},
|
|
363
|
+
})
|
|
364
|
+
.execute();
|
|
365
|
+
|
|
366
|
+
const profile = await userData.json();
|
|
367
|
+
console.log(profile);
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### File Upload
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
import { begin } from "@flow-conductor/core";
|
|
374
|
+
import { NodeFetchRequestAdapter } from "@flow-conductor/adapter-node-fetch";
|
|
375
|
+
import { readFileSync } from "fs";
|
|
376
|
+
|
|
377
|
+
const adapter = new NodeFetchRequestAdapter();
|
|
378
|
+
|
|
379
|
+
// Upload file as Buffer
|
|
380
|
+
const fileBuffer = readFileSync("./file.pdf");
|
|
381
|
+
|
|
382
|
+
const result = await begin(
|
|
383
|
+
{
|
|
384
|
+
config: {
|
|
385
|
+
url: "https://api.example.com/upload",
|
|
386
|
+
method: "POST",
|
|
387
|
+
data: fileBuffer,
|
|
388
|
+
headers: {
|
|
389
|
+
"Content-Type": "application/pdf",
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
adapter
|
|
394
|
+
).execute();
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Request with Timeout
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
import { begin } from "@flow-conductor/core";
|
|
401
|
+
import { NodeFetchRequestAdapter } from "@flow-conductor/adapter-node-fetch";
|
|
402
|
+
|
|
403
|
+
const adapter = new NodeFetchRequestAdapter();
|
|
404
|
+
|
|
405
|
+
const result = await begin(
|
|
406
|
+
{
|
|
407
|
+
config: {
|
|
408
|
+
url: "https://api.example.com/users",
|
|
409
|
+
method: "GET",
|
|
410
|
+
timeout: 5000, // 5 second timeout
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
adapter
|
|
414
|
+
).execute();
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
## API Reference
|
|
418
|
+
|
|
419
|
+
### NodeFetchRequestAdapter
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
class NodeFetchRequestAdapter extends RequestAdapter<Response, NodeFetchRequestConfig> {
|
|
423
|
+
createRequest(requestConfig: IRequestConfig): Promise<Response>;
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### NodeFetchRequestConfig
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
type NodeFetchRequestConfig = IRequestConfig;
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
Extends `IRequestConfig` with all standard node-fetch options.
|
|
434
|
+
|
|
435
|
+
## Differences from Native Fetch Adapter
|
|
436
|
+
|
|
437
|
+
The `@flow-conductor/adapter-node-fetch` adapter is similar to `@flow-conductor/adapter-fetch`, but:
|
|
438
|
+
|
|
439
|
+
- **Node.js only**: Designed specifically for Node.js environments
|
|
440
|
+
- **node-fetch dependency**: Uses the `node-fetch` package instead of native fetch
|
|
441
|
+
- **Better Node.js support**: May have better support for Node.js-specific features
|
|
442
|
+
- **Consistent API**: Provides a consistent API across different Node.js versions
|
|
443
|
+
|
|
444
|
+
Choose `@flow-conductor/adapter-node-fetch` if:
|
|
445
|
+
- You're building a Node.js-only application
|
|
446
|
+
- You need features specific to node-fetch
|
|
447
|
+
- You want explicit control over the fetch implementation
|
|
448
|
+
|
|
449
|
+
Choose `@flow-conductor/adapter-fetch` if:
|
|
450
|
+
- You want to use the native Fetch API (Node.js 18+)
|
|
451
|
+
- You want to avoid additional dependencies
|
|
452
|
+
- You're building for both browser and Node.js
|
|
453
|
+
|
|
454
|
+
## Requirements
|
|
455
|
+
|
|
456
|
+
- `@flow-conductor/core` (peer dependency)
|
|
457
|
+
- `node-fetch` v3.x (peer dependency)
|
|
458
|
+
- Node.js 18+ (for native ESM support)
|
|
459
|
+
- TypeScript 5.0+
|
|
460
|
+
|
|
461
|
+
## License
|
|
462
|
+
|
|
463
|
+
MIT
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { RequestAdapter, IRequestConfig, UrlValidationOptions } from '@flow-conductor/core';
|
|
2
|
+
import { Response } from 'node-fetch';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Request configuration type for node-fetch adapter.
|
|
6
|
+
* Extends IRequestConfig with node-fetch-specific options via the index signature.
|
|
7
|
+
*/
|
|
8
|
+
type NodeFetchRequestConfig = IRequestConfig;
|
|
9
|
+
/**
|
|
10
|
+
* Request adapter implementation using node-fetch.
|
|
11
|
+
* Provides a Node.js-specific HTTP client adapter that works in Node.js environments.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const adapter = new NodeFetchRequestAdapter();
|
|
16
|
+
* const chain = begin(
|
|
17
|
+
* { config: { url: 'https://api.example.com/users', method: 'GET' } },
|
|
18
|
+
* adapter
|
|
19
|
+
* );
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
declare class NodeFetchRequestAdapter extends RequestAdapter<Response, NodeFetchRequestConfig> {
|
|
23
|
+
/**
|
|
24
|
+
* Creates a new NodeFetchRequestAdapter 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 node-fetch.
|
|
31
|
+
* Automatically handles JSON serialization for request bodies.
|
|
32
|
+
*
|
|
33
|
+
* @param requestConfig - The request configuration object
|
|
34
|
+
* @returns A promise that resolves to a Response object
|
|
35
|
+
*/
|
|
36
|
+
createRequest(requestConfig: IRequestConfig): Promise<Response>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { type NodeFetchRequestConfig, NodeFetchRequestAdapter as default };
|
package/build/index.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { RequestAdapter } from '@flow-conductor/core';
|
|
2
|
+
import fetch from 'node-fetch';
|
|
3
|
+
|
|
4
|
+
// src/node-fetch-request-adapter.ts
|
|
5
|
+
var NodeFetchRequestAdapter = class extends RequestAdapter {
|
|
6
|
+
/**
|
|
7
|
+
* Creates a new NodeFetchRequestAdapter 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 node-fetch.
|
|
16
|
+
* Automatically handles JSON serialization for request bodies.
|
|
17
|
+
*
|
|
18
|
+
* @param requestConfig - The request configuration object
|
|
19
|
+
* @returns A promise that resolves to a Response object
|
|
20
|
+
*/
|
|
21
|
+
async createRequest(requestConfig) {
|
|
22
|
+
const { data, url, method, headers, ...rest } = requestConfig;
|
|
23
|
+
const fetchConfig = {
|
|
24
|
+
method,
|
|
25
|
+
...rest
|
|
26
|
+
};
|
|
27
|
+
const headersObj = headers ? { ...headers } : {};
|
|
28
|
+
if (data !== void 0) {
|
|
29
|
+
if (data === null) {
|
|
30
|
+
fetchConfig.body = JSON.stringify(null);
|
|
31
|
+
if (!headersObj["Content-Type"] && !headersObj["content-type"]) {
|
|
32
|
+
headersObj["Content-Type"] = "application/json";
|
|
33
|
+
}
|
|
34
|
+
} else if (typeof data === "string") {
|
|
35
|
+
fetchConfig.body = data;
|
|
36
|
+
} else if (data instanceof Buffer) {
|
|
37
|
+
fetchConfig.body = data;
|
|
38
|
+
} else if (data instanceof Uint8Array) {
|
|
39
|
+
fetchConfig.body = Buffer.from(data);
|
|
40
|
+
} else {
|
|
41
|
+
fetchConfig.body = JSON.stringify(data);
|
|
42
|
+
if (!headersObj["Content-Type"] && !headersObj["content-type"]) {
|
|
43
|
+
headersObj["Content-Type"] = "application/json";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (Object.keys(headersObj).length > 0) {
|
|
48
|
+
fetchConfig.headers = headersObj;
|
|
49
|
+
}
|
|
50
|
+
return fetch(url, fetchConfig);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export { NodeFetchRequestAdapter as default };
|
|
55
|
+
//# sourceMappingURL=index.js.map
|
|
56
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/node-fetch-request-adapter.ts"],"names":[],"mappings":";;;;AA2BA,IAAqB,uBAAA,GAArB,cAAqD,cAAA,CAGnD;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,cAAc,aAAA,EAAkD;AAC3E,IAAA,MAAM,EAAE,IAAA,EAAM,GAAA,EAAK,QAAQ,OAAA,EAAS,GAAG,MAAK,GAAI,aAAA;AAChD,IAAA,MAAM,WAAA,GAA2B;AAAA,MAC/B,MAAA;AAAA,MACA,GAAG;AAAA,KACL;AAGA,IAAA,MAAM,aAAqC,OAAA,GACvC,EAAE,GAAI,OAAA,KACN,EAAC;AAGL,IAAA,IAAI,SAAS,MAAA,EAAW;AACtB,MAAA,IAAI,SAAS,IAAA,EAAM;AACjB,QAAA,WAAA,CAAY,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA;AAEtC,QAAA,IAAI,CAAC,UAAA,CAAW,cAAc,KAAK,CAAC,UAAA,CAAW,cAAc,CAAA,EAAG;AAC9D,UAAA,UAAA,CAAW,cAAc,CAAA,GAAI,kBAAA;AAAA,QAC/B;AAAA,MACF,CAAA,MAAA,IAAW,OAAO,IAAA,KAAS,QAAA,EAAU;AACnC,QAAA,WAAA,CAAY,IAAA,GAAO,IAAA;AAAA,MACrB,CAAA,MAAA,IAAW,gBAAgB,MAAA,EAAQ;AACjC,QAAA,WAAA,CAAY,IAAA,GAAO,IAAA;AAAA,MACrB,CAAA,MAAA,IAAW,gBAAgB,UAAA,EAAY;AACrC,QAAA,WAAA,CAAY,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA;AAAA,MACrC,CAAA,MAAO;AACL,QAAA,WAAA,CAAY,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA;AAEtC,QAAA,IAAI,CAAC,UAAA,CAAW,cAAc,KAAK,CAAC,UAAA,CAAW,cAAc,CAAA,EAAG;AAC9D,UAAA,UAAA,CAAW,cAAc,CAAA,GAAI,kBAAA;AAAA,QAC/B;AAAA,MACF;AAAA,IACF;AAGA,IAAA,IAAI,MAAA,CAAO,IAAA,CAAK,UAAU,CAAA,CAAE,SAAS,CAAA,EAAG;AACtC,MAAA,WAAA,CAAY,OAAA,GAAU,UAAA;AAAA,IACxB;AAEA,IAAA,OAAO,KAAA,CAAM,KAAK,WAAW,CAAA;AAAA,EAC/B;AACF","file":"index.js","sourcesContent":["import { RequestAdapter } from \"@flow-conductor/core\";\nimport type {\n IRequestConfig,\n UrlValidationOptions,\n} from \"@flow-conductor/core\";\nimport fetch from \"node-fetch\";\nimport type { RequestInit, Response } from \"node-fetch\";\n\n/**\n * Request configuration type for node-fetch adapter.\n * Extends IRequestConfig with node-fetch-specific options via the index signature.\n */\nexport type NodeFetchRequestConfig = IRequestConfig;\n\n/**\n * Request adapter implementation using node-fetch.\n * Provides a Node.js-specific HTTP client adapter that works in Node.js environments.\n *\n * @example\n * ```typescript\n * const adapter = new NodeFetchRequestAdapter();\n * const chain = begin(\n * { config: { url: 'https://api.example.com/users', method: 'GET' } },\n * adapter\n * );\n * ```\n */\nexport default class NodeFetchRequestAdapter extends RequestAdapter<\n Response,\n NodeFetchRequestConfig\n> {\n /**\n * Creates a new NodeFetchRequestAdapter 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 node-fetch.\n * Automatically handles JSON serialization for request bodies.\n *\n * @param requestConfig - The request configuration object\n * @returns A promise that resolves to a Response object\n */\n public async createRequest(requestConfig: IRequestConfig): Promise<Response> {\n const { data, url, method, headers, ...rest } = requestConfig;\n const fetchConfig: RequestInit = {\n method,\n ...rest,\n };\n\n // Handle headers - merge with existing headers if any\n const headersObj: Record<string, string> = headers\n ? { ...(headers as Record<string, string>) }\n : {};\n\n // Handle request body\n if (data !== undefined) {\n if (data === null) {\n fetchConfig.body = JSON.stringify(null);\n // Set Content-Type header if not already set\n if (!headersObj[\"Content-Type\"] && !headersObj[\"content-type\"]) {\n headersObj[\"Content-Type\"] = \"application/json\";\n }\n } else if (typeof data === \"string\") {\n fetchConfig.body = data;\n } else if (data instanceof Buffer) {\n fetchConfig.body = data;\n } else if (data instanceof Uint8Array) {\n fetchConfig.body = Buffer.from(data);\n } else {\n fetchConfig.body = JSON.stringify(data);\n // Set Content-Type header if not already set\n if (!headersObj[\"Content-Type\"] && !headersObj[\"content-type\"]) {\n headersObj[\"Content-Type\"] = \"application/json\";\n }\n }\n }\n\n // Set headers if we have any\n if (Object.keys(headersObj).length > 0) {\n fetchConfig.headers = headersObj;\n }\n\n return fetch(url, fetchConfig);\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flow-conductor/adapter-node-fetch",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "node-fetch 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
|
+
"node-fetch",
|
|
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-node-fetch"
|
|
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
|
+
"node-fetch": "^3.0.0"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@eslint/js": "^9.39.2",
|
|
59
|
+
"@flow-conductor/core": "*",
|
|
60
|
+
"@types/node-fetch": "^2.6.11",
|
|
61
|
+
"@typescript-eslint/eslint-plugin": "^8.50.1",
|
|
62
|
+
"@typescript-eslint/parser": "^8.50.1",
|
|
63
|
+
"eslint": "^9.39.2",
|
|
64
|
+
"eslint-config-prettier": "^10.1.8",
|
|
65
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
66
|
+
"node-fetch": "^3.3.2",
|
|
67
|
+
"prettier": "^3.7.4",
|
|
68
|
+
"tsx": "^4.7.0",
|
|
69
|
+
"typescript": "^5.9.3",
|
|
70
|
+
"typescript-eslint": "^8.50.1"
|
|
71
|
+
}
|
|
72
|
+
}
|