@chatbotkit/fetch 0.2.0 → 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/.eslintrc +1 -0
- package/CHANGELOG.md +13 -0
- package/dist/cjs/index.cjs +194 -4
- package/dist/cjs/index.d.ts +71 -0
- package/dist/esm/index.d.ts +71 -0
- package/dist/esm/index.js +189 -4
- package/package.json +19 -5
package/.eslintrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @chatbotkit/fetch
|
|
2
2
|
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- Better and more consistent API types, list filtering and more.
|
|
8
|
+
|
|
9
|
+
## 0.3.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- - Refactored project scripts.
|
|
14
|
+
- Introduced a new usage method.
|
|
15
|
+
|
|
3
16
|
## 0.2.0
|
|
4
17
|
|
|
5
18
|
### Minor Changes
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FormData = exports.Blob = exports.fetch = void 0;
|
|
3
|
+
exports.jsonl = exports.withRetry = exports.withTimeout = exports.FetchError = exports.anySignal = exports.TIMEOUT_ERROR_NAME = exports.ABORT_ERROR_NAME = exports.FormData = exports.Blob = exports.fetch = void 0;
|
|
4
4
|
const node_fetch_native_1 = require("node-fetch-native");
|
|
5
5
|
Object.defineProperty(exports, "fetch", { enumerable: true, get: function () { return node_fetch_native_1.fetch; } });
|
|
6
6
|
Object.defineProperty(exports, "Blob", { enumerable: true, get: function () { return node_fetch_native_1.Blob; } });
|
|
7
7
|
Object.defineProperty(exports, "FormData", { enumerable: true, get: function () { return node_fetch_native_1.FormData; } });
|
|
8
|
-
if (
|
|
9
|
-
|
|
8
|
+
if (
|
|
9
|
+
// eslint-disable-next-line no-undef
|
|
10
|
+
typeof globalThis.ReadableStream === 'function' &&
|
|
11
|
+
// @ts-expect-error polyfill
|
|
12
|
+
// eslint-disable-next-line no-undef
|
|
10
13
|
typeof globalThis.ReadableStream.prototype[Symbol.asyncIterator] !==
|
|
11
14
|
'function') {
|
|
12
|
-
// @ts-
|
|
15
|
+
// @ts-expect-error polyfill
|
|
16
|
+
// eslint-disable-next-line no-undef
|
|
13
17
|
globalThis.ReadableStream.prototype[Symbol.asyncIterator] = function () {
|
|
14
18
|
const reader = this.getReader();
|
|
15
19
|
return {
|
|
@@ -20,3 +24,189 @@ if (typeof globalThis.ReadableStream === 'function' &&
|
|
|
20
24
|
};
|
|
21
25
|
};
|
|
22
26
|
}
|
|
27
|
+
exports.ABORT_ERROR_NAME = 'AbortError';
|
|
28
|
+
exports.TIMEOUT_ERROR_NAME = 'TimeoutError';
|
|
29
|
+
/**
|
|
30
|
+
* @param {AbortSignal[]} signals
|
|
31
|
+
* @returns {AbortSignal}
|
|
32
|
+
*/
|
|
33
|
+
function anySignal(signals) {
|
|
34
|
+
const controller = new AbortController();
|
|
35
|
+
for (const signal of signals) {
|
|
36
|
+
if (signal.aborted) {
|
|
37
|
+
controller.abort(signal.reason);
|
|
38
|
+
return signal;
|
|
39
|
+
}
|
|
40
|
+
signal.addEventListener('abort', () => controller.abort(signal.reason), {
|
|
41
|
+
signal: controller.signal,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return controller.signal;
|
|
45
|
+
}
|
|
46
|
+
exports.anySignal = anySignal;
|
|
47
|
+
/**
|
|
48
|
+
* @todo add definition
|
|
49
|
+
* @todo move to @chatbotkit/fetch sdk
|
|
50
|
+
*/
|
|
51
|
+
class FetchError extends Error {
|
|
52
|
+
/**
|
|
53
|
+
* @param {string} message
|
|
54
|
+
* @param {number|string} code
|
|
55
|
+
* @param {string} url
|
|
56
|
+
* @param {RequestInit & withTimeoutOptions & withRetryOptions} request
|
|
57
|
+
* @param {Response} response
|
|
58
|
+
*/
|
|
59
|
+
constructor(message, code, url, request, response) {
|
|
60
|
+
super(message);
|
|
61
|
+
this.code = code || response.status;
|
|
62
|
+
this.url = url;
|
|
63
|
+
this.request = request;
|
|
64
|
+
this.response = response;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
exports.FetchError = FetchError;
|
|
68
|
+
/**
|
|
69
|
+
* @typedef {(url: string, options?: RequestInit) => Promise<Response>} FetchFn
|
|
70
|
+
*/
|
|
71
|
+
/**
|
|
72
|
+
* @typedef {{
|
|
73
|
+
* timeout?: number
|
|
74
|
+
* }} withTimeoutOptions
|
|
75
|
+
*
|
|
76
|
+
* @param {FetchFn} fetch
|
|
77
|
+
* @param {withTimeoutOptions} [defaultOptions]
|
|
78
|
+
* @returns {FetchFn}
|
|
79
|
+
*/
|
|
80
|
+
function withTimeout(fetch, defaultOptions) {
|
|
81
|
+
/**
|
|
82
|
+
* @param {string} url
|
|
83
|
+
* @param {RequestInit & withTimeoutOptions} [options]
|
|
84
|
+
* @returns {Promise<Response>}
|
|
85
|
+
*/
|
|
86
|
+
return async function fetchWithTimeout(url, options) {
|
|
87
|
+
const timeout = options?.timeout ?? defaultOptions?.timeout ?? 30000;
|
|
88
|
+
let signal;
|
|
89
|
+
let handler;
|
|
90
|
+
if (timeout > 0 && timeout !== Infinity) {
|
|
91
|
+
const abortController = new AbortController();
|
|
92
|
+
// @todo use AbortSignal.timeout(n) when widely supported
|
|
93
|
+
handler = setTimeout(() => {
|
|
94
|
+
abortController.abort(exports.TIMEOUT_ERROR_NAME);
|
|
95
|
+
}, timeout);
|
|
96
|
+
signal = options?.signal
|
|
97
|
+
? anySignal([abortController.signal, options.signal])
|
|
98
|
+
: abortController.signal;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
signal = options?.signal;
|
|
102
|
+
}
|
|
103
|
+
let response;
|
|
104
|
+
try {
|
|
105
|
+
response = await fetch(url, {
|
|
106
|
+
...options,
|
|
107
|
+
signal,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
clearTimeout(handler);
|
|
112
|
+
}
|
|
113
|
+
return response;
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
exports.withTimeout = withTimeout;
|
|
117
|
+
/**
|
|
118
|
+
* @typedef {{
|
|
119
|
+
* retries?: number,
|
|
120
|
+
* retryDelay?: number,
|
|
121
|
+
* retryAbort?: boolean,
|
|
122
|
+
* retryTimeout?: boolean,
|
|
123
|
+
* retryStatuses?: number[]
|
|
124
|
+
* }} withRetryOptions
|
|
125
|
+
*
|
|
126
|
+
* @todo move to @chatbotkit/fetch sdk
|
|
127
|
+
* @param {FetchFn} fetch
|
|
128
|
+
* @param {withRetryOptions} [defaultOptions]
|
|
129
|
+
* @returns {FetchFn}
|
|
130
|
+
*/
|
|
131
|
+
function withRetry(fetch, defaultOptions) {
|
|
132
|
+
/**
|
|
133
|
+
* @param {string} url
|
|
134
|
+
* @param {RequestInit & withRetryOptions} [options]
|
|
135
|
+
* @returns {Promise<Response>}
|
|
136
|
+
*/
|
|
137
|
+
return async function fetchWithRetry(url, options) {
|
|
138
|
+
const retries = options?.retries ?? defaultOptions?.retries ?? 5;
|
|
139
|
+
const retryDelay = options?.retryDelay ?? defaultOptions?.retryDelay ?? 250;
|
|
140
|
+
const retryAbort = options?.retryAbort ?? defaultOptions?.retryAbort ?? false;
|
|
141
|
+
const retryTimeout = options?.retryTimeout ?? defaultOptions?.retryTimeout ?? false;
|
|
142
|
+
const retryStatuses = options?.retryStatuses ??
|
|
143
|
+
defaultOptions?.retryStatuses ?? [429, 500, 502, 503, 504];
|
|
144
|
+
let response;
|
|
145
|
+
try {
|
|
146
|
+
response = await fetch(url, { ...options });
|
|
147
|
+
if (!response.ok) {
|
|
148
|
+
if (retryStatuses.includes(response.status)) {
|
|
149
|
+
throw new FetchError(`Request failed`, response.status, url, options || {}, response);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
return response;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return response;
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
switch (true) {
|
|
159
|
+
case error instanceof Error &&
|
|
160
|
+
error.name === exports.ABORT_ERROR_NAME &&
|
|
161
|
+
!retryAbort:
|
|
162
|
+
{
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
case error instanceof Error &&
|
|
166
|
+
error.name === exports.TIMEOUT_ERROR_NAME &&
|
|
167
|
+
!retryTimeout:
|
|
168
|
+
{
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
case retries === 0: {
|
|
172
|
+
if (response) {
|
|
173
|
+
return response;
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
181
|
+
return await fetchWithRetry(url, {
|
|
182
|
+
...options,
|
|
183
|
+
retries: retries - 1,
|
|
184
|
+
retryDelay: retryDelay * 2,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
exports.withRetry = withRetry;
|
|
190
|
+
/**
|
|
191
|
+
* @param {ReadableStream<Uint8Array> & {[Symbol.asyncIterator](): AsyncIterator<Uint8Array>}} body
|
|
192
|
+
* @returns {AsyncGenerator<Record<string,any>>}
|
|
193
|
+
*/
|
|
194
|
+
async function* jsonl(body) {
|
|
195
|
+
const decoder = new TextDecoder();
|
|
196
|
+
let previous = '';
|
|
197
|
+
for await (const chunk of body) {
|
|
198
|
+
previous += decoder.decode(chunk);
|
|
199
|
+
let eolIndex;
|
|
200
|
+
while ((eolIndex = previous.indexOf('\n')) >= 0) {
|
|
201
|
+
const line = previous.slice(0, eolIndex + 1);
|
|
202
|
+
if (line) {
|
|
203
|
+
yield JSON.parse(line);
|
|
204
|
+
}
|
|
205
|
+
previous = previous.slice(eolIndex + 1);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (previous.trim().length > 0) {
|
|
209
|
+
yield JSON.parse(previous);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
exports.jsonl = jsonl;
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -1,3 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {AbortSignal[]} signals
|
|
3
|
+
* @returns {AbortSignal}
|
|
4
|
+
*/
|
|
5
|
+
export function anySignal(signals: AbortSignal[]): AbortSignal;
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {(url: string, options?: RequestInit) => Promise<Response>} FetchFn
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {{
|
|
11
|
+
* timeout?: number
|
|
12
|
+
* }} withTimeoutOptions
|
|
13
|
+
*
|
|
14
|
+
* @param {FetchFn} fetch
|
|
15
|
+
* @param {withTimeoutOptions} [defaultOptions]
|
|
16
|
+
* @returns {FetchFn}
|
|
17
|
+
*/
|
|
18
|
+
export function withTimeout(fetch: FetchFn, defaultOptions?: withTimeoutOptions | undefined): FetchFn;
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {{
|
|
21
|
+
* retries?: number,
|
|
22
|
+
* retryDelay?: number,
|
|
23
|
+
* retryAbort?: boolean,
|
|
24
|
+
* retryTimeout?: boolean,
|
|
25
|
+
* retryStatuses?: number[]
|
|
26
|
+
* }} withRetryOptions
|
|
27
|
+
*
|
|
28
|
+
* @todo move to @chatbotkit/fetch sdk
|
|
29
|
+
* @param {FetchFn} fetch
|
|
30
|
+
* @param {withRetryOptions} [defaultOptions]
|
|
31
|
+
* @returns {FetchFn}
|
|
32
|
+
*/
|
|
33
|
+
export function withRetry(fetch: FetchFn, defaultOptions?: withRetryOptions | undefined): FetchFn;
|
|
34
|
+
/**
|
|
35
|
+
* @param {ReadableStream<Uint8Array> & {[Symbol.asyncIterator](): AsyncIterator<Uint8Array>}} body
|
|
36
|
+
* @returns {AsyncGenerator<Record<string,any>>}
|
|
37
|
+
*/
|
|
38
|
+
export function jsonl(body: ReadableStream<Uint8Array> & {
|
|
39
|
+
[Symbol.asyncIterator](): AsyncIterator<Uint8Array>;
|
|
40
|
+
}): AsyncGenerator<Record<string, any>>;
|
|
41
|
+
export const ABORT_ERROR_NAME: "AbortError";
|
|
42
|
+
export const TIMEOUT_ERROR_NAME: "TimeoutError";
|
|
43
|
+
/**
|
|
44
|
+
* @todo add definition
|
|
45
|
+
* @todo move to @chatbotkit/fetch sdk
|
|
46
|
+
*/
|
|
47
|
+
export class FetchError extends Error {
|
|
48
|
+
/**
|
|
49
|
+
* @param {string} message
|
|
50
|
+
* @param {number|string} code
|
|
51
|
+
* @param {string} url
|
|
52
|
+
* @param {RequestInit & withTimeoutOptions & withRetryOptions} request
|
|
53
|
+
* @param {Response} response
|
|
54
|
+
*/
|
|
55
|
+
constructor(message: string, code: number | string, url: string, request: RequestInit & withTimeoutOptions & withRetryOptions, response: Response);
|
|
56
|
+
code: string | number;
|
|
57
|
+
url: string;
|
|
58
|
+
request: RequestInit & withTimeoutOptions & withRetryOptions;
|
|
59
|
+
response: Response;
|
|
60
|
+
}
|
|
61
|
+
export type FetchFn = (url: string, options?: RequestInit) => Promise<Response>;
|
|
62
|
+
export type withTimeoutOptions = {
|
|
63
|
+
timeout?: number;
|
|
64
|
+
};
|
|
65
|
+
export type withRetryOptions = {
|
|
66
|
+
retries?: number;
|
|
67
|
+
retryDelay?: number;
|
|
68
|
+
retryAbort?: boolean;
|
|
69
|
+
retryTimeout?: boolean;
|
|
70
|
+
retryStatuses?: number[];
|
|
71
|
+
};
|
|
1
72
|
import { fetch } from 'node-fetch-native';
|
|
2
73
|
import { Blob } from 'node-fetch-native';
|
|
3
74
|
import { FormData } from 'node-fetch-native';
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,3 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {AbortSignal[]} signals
|
|
3
|
+
* @returns {AbortSignal}
|
|
4
|
+
*/
|
|
5
|
+
export function anySignal(signals: AbortSignal[]): AbortSignal;
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {(url: string, options?: RequestInit) => Promise<Response>} FetchFn
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {{
|
|
11
|
+
* timeout?: number
|
|
12
|
+
* }} withTimeoutOptions
|
|
13
|
+
*
|
|
14
|
+
* @param {FetchFn} fetch
|
|
15
|
+
* @param {withTimeoutOptions} [defaultOptions]
|
|
16
|
+
* @returns {FetchFn}
|
|
17
|
+
*/
|
|
18
|
+
export function withTimeout(fetch: FetchFn, defaultOptions?: withTimeoutOptions | undefined): FetchFn;
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {{
|
|
21
|
+
* retries?: number,
|
|
22
|
+
* retryDelay?: number,
|
|
23
|
+
* retryAbort?: boolean,
|
|
24
|
+
* retryTimeout?: boolean,
|
|
25
|
+
* retryStatuses?: number[]
|
|
26
|
+
* }} withRetryOptions
|
|
27
|
+
*
|
|
28
|
+
* @todo move to @chatbotkit/fetch sdk
|
|
29
|
+
* @param {FetchFn} fetch
|
|
30
|
+
* @param {withRetryOptions} [defaultOptions]
|
|
31
|
+
* @returns {FetchFn}
|
|
32
|
+
*/
|
|
33
|
+
export function withRetry(fetch: FetchFn, defaultOptions?: withRetryOptions | undefined): FetchFn;
|
|
34
|
+
/**
|
|
35
|
+
* @param {ReadableStream<Uint8Array> & {[Symbol.asyncIterator](): AsyncIterator<Uint8Array>}} body
|
|
36
|
+
* @returns {AsyncGenerator<Record<string,any>>}
|
|
37
|
+
*/
|
|
38
|
+
export function jsonl(body: ReadableStream<Uint8Array> & {
|
|
39
|
+
[Symbol.asyncIterator](): AsyncIterator<Uint8Array>;
|
|
40
|
+
}): AsyncGenerator<Record<string, any>>;
|
|
41
|
+
export const ABORT_ERROR_NAME: "AbortError";
|
|
42
|
+
export const TIMEOUT_ERROR_NAME: "TimeoutError";
|
|
43
|
+
/**
|
|
44
|
+
* @todo add definition
|
|
45
|
+
* @todo move to @chatbotkit/fetch sdk
|
|
46
|
+
*/
|
|
47
|
+
export class FetchError extends Error {
|
|
48
|
+
/**
|
|
49
|
+
* @param {string} message
|
|
50
|
+
* @param {number|string} code
|
|
51
|
+
* @param {string} url
|
|
52
|
+
* @param {RequestInit & withTimeoutOptions & withRetryOptions} request
|
|
53
|
+
* @param {Response} response
|
|
54
|
+
*/
|
|
55
|
+
constructor(message: string, code: number | string, url: string, request: RequestInit & withTimeoutOptions & withRetryOptions, response: Response);
|
|
56
|
+
code: string | number;
|
|
57
|
+
url: string;
|
|
58
|
+
request: RequestInit & withTimeoutOptions & withRetryOptions;
|
|
59
|
+
response: Response;
|
|
60
|
+
}
|
|
61
|
+
export type FetchFn = (url: string, options?: RequestInit) => Promise<Response>;
|
|
62
|
+
export type withTimeoutOptions = {
|
|
63
|
+
timeout?: number;
|
|
64
|
+
};
|
|
65
|
+
export type withRetryOptions = {
|
|
66
|
+
retries?: number;
|
|
67
|
+
retryDelay?: number;
|
|
68
|
+
retryAbort?: boolean;
|
|
69
|
+
retryTimeout?: boolean;
|
|
70
|
+
retryStatuses?: number[];
|
|
71
|
+
};
|
|
1
72
|
import { fetch } from 'node-fetch-native';
|
|
2
73
|
import { Blob } from 'node-fetch-native';
|
|
3
74
|
import { FormData } from 'node-fetch-native';
|
package/dist/esm/index.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { fetch, Blob, FormData } from 'node-fetch-native';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
export { fetch, Blob, FormData };
|
|
3
|
+
if (
|
|
4
|
+
// eslint-disable-next-line no-undef
|
|
5
|
+
typeof globalThis.ReadableStream === 'function' &&
|
|
6
|
+
// @ts-expect-error polyfill
|
|
7
|
+
// eslint-disable-next-line no-undef
|
|
4
8
|
typeof globalThis.ReadableStream.prototype[Symbol.asyncIterator] !==
|
|
5
9
|
'function') {
|
|
6
|
-
// @ts-
|
|
10
|
+
// @ts-expect-error polyfill
|
|
11
|
+
// eslint-disable-next-line no-undef
|
|
7
12
|
globalThis.ReadableStream.prototype[Symbol.asyncIterator] = function () {
|
|
8
13
|
const reader = this.getReader();
|
|
9
14
|
return {
|
|
@@ -14,4 +19,184 @@ if (typeof globalThis.ReadableStream === 'function' &&
|
|
|
14
19
|
};
|
|
15
20
|
};
|
|
16
21
|
}
|
|
17
|
-
export
|
|
22
|
+
export const ABORT_ERROR_NAME = 'AbortError';
|
|
23
|
+
export const TIMEOUT_ERROR_NAME = 'TimeoutError';
|
|
24
|
+
/**
|
|
25
|
+
* @param {AbortSignal[]} signals
|
|
26
|
+
* @returns {AbortSignal}
|
|
27
|
+
*/
|
|
28
|
+
export function anySignal(signals) {
|
|
29
|
+
const controller = new AbortController();
|
|
30
|
+
for (const signal of signals) {
|
|
31
|
+
if (signal.aborted) {
|
|
32
|
+
controller.abort(signal.reason);
|
|
33
|
+
return signal;
|
|
34
|
+
}
|
|
35
|
+
signal.addEventListener('abort', () => controller.abort(signal.reason), {
|
|
36
|
+
signal: controller.signal,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return controller.signal;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* @todo add definition
|
|
43
|
+
* @todo move to @chatbotkit/fetch sdk
|
|
44
|
+
*/
|
|
45
|
+
export class FetchError extends Error {
|
|
46
|
+
/**
|
|
47
|
+
* @param {string} message
|
|
48
|
+
* @param {number|string} code
|
|
49
|
+
* @param {string} url
|
|
50
|
+
* @param {RequestInit & withTimeoutOptions & withRetryOptions} request
|
|
51
|
+
* @param {Response} response
|
|
52
|
+
*/
|
|
53
|
+
constructor(message, code, url, request, response) {
|
|
54
|
+
super(message);
|
|
55
|
+
this.code = code || response.status;
|
|
56
|
+
this.url = url;
|
|
57
|
+
this.request = request;
|
|
58
|
+
this.response = response;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* @typedef {(url: string, options?: RequestInit) => Promise<Response>} FetchFn
|
|
63
|
+
*/
|
|
64
|
+
/**
|
|
65
|
+
* @typedef {{
|
|
66
|
+
* timeout?: number
|
|
67
|
+
* }} withTimeoutOptions
|
|
68
|
+
*
|
|
69
|
+
* @param {FetchFn} fetch
|
|
70
|
+
* @param {withTimeoutOptions} [defaultOptions]
|
|
71
|
+
* @returns {FetchFn}
|
|
72
|
+
*/
|
|
73
|
+
export function withTimeout(fetch, defaultOptions) {
|
|
74
|
+
/**
|
|
75
|
+
* @param {string} url
|
|
76
|
+
* @param {RequestInit & withTimeoutOptions} [options]
|
|
77
|
+
* @returns {Promise<Response>}
|
|
78
|
+
*/
|
|
79
|
+
return async function fetchWithTimeout(url, options) {
|
|
80
|
+
const timeout = options?.timeout ?? defaultOptions?.timeout ?? 30000;
|
|
81
|
+
let signal;
|
|
82
|
+
let handler;
|
|
83
|
+
if (timeout > 0 && timeout !== Infinity) {
|
|
84
|
+
const abortController = new AbortController();
|
|
85
|
+
// @todo use AbortSignal.timeout(n) when widely supported
|
|
86
|
+
handler = setTimeout(() => {
|
|
87
|
+
abortController.abort(TIMEOUT_ERROR_NAME);
|
|
88
|
+
}, timeout);
|
|
89
|
+
signal = options?.signal
|
|
90
|
+
? anySignal([abortController.signal, options.signal])
|
|
91
|
+
: abortController.signal;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
signal = options?.signal;
|
|
95
|
+
}
|
|
96
|
+
let response;
|
|
97
|
+
try {
|
|
98
|
+
response = await fetch(url, {
|
|
99
|
+
...options,
|
|
100
|
+
signal,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
clearTimeout(handler);
|
|
105
|
+
}
|
|
106
|
+
return response;
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* @typedef {{
|
|
111
|
+
* retries?: number,
|
|
112
|
+
* retryDelay?: number,
|
|
113
|
+
* retryAbort?: boolean,
|
|
114
|
+
* retryTimeout?: boolean,
|
|
115
|
+
* retryStatuses?: number[]
|
|
116
|
+
* }} withRetryOptions
|
|
117
|
+
*
|
|
118
|
+
* @todo move to @chatbotkit/fetch sdk
|
|
119
|
+
* @param {FetchFn} fetch
|
|
120
|
+
* @param {withRetryOptions} [defaultOptions]
|
|
121
|
+
* @returns {FetchFn}
|
|
122
|
+
*/
|
|
123
|
+
export function withRetry(fetch, defaultOptions) {
|
|
124
|
+
/**
|
|
125
|
+
* @param {string} url
|
|
126
|
+
* @param {RequestInit & withRetryOptions} [options]
|
|
127
|
+
* @returns {Promise<Response>}
|
|
128
|
+
*/
|
|
129
|
+
return async function fetchWithRetry(url, options) {
|
|
130
|
+
const retries = options?.retries ?? defaultOptions?.retries ?? 5;
|
|
131
|
+
const retryDelay = options?.retryDelay ?? defaultOptions?.retryDelay ?? 250;
|
|
132
|
+
const retryAbort = options?.retryAbort ?? defaultOptions?.retryAbort ?? false;
|
|
133
|
+
const retryTimeout = options?.retryTimeout ?? defaultOptions?.retryTimeout ?? false;
|
|
134
|
+
const retryStatuses = options?.retryStatuses ??
|
|
135
|
+
defaultOptions?.retryStatuses ?? [429, 500, 502, 503, 504];
|
|
136
|
+
let response;
|
|
137
|
+
try {
|
|
138
|
+
response = await fetch(url, { ...options });
|
|
139
|
+
if (!response.ok) {
|
|
140
|
+
if (retryStatuses.includes(response.status)) {
|
|
141
|
+
throw new FetchError(`Request failed`, response.status, url, options || {}, response);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
return response;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return response;
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
switch (true) {
|
|
151
|
+
case error instanceof Error &&
|
|
152
|
+
error.name === ABORT_ERROR_NAME &&
|
|
153
|
+
!retryAbort:
|
|
154
|
+
{
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
case error instanceof Error &&
|
|
158
|
+
error.name === TIMEOUT_ERROR_NAME &&
|
|
159
|
+
!retryTimeout:
|
|
160
|
+
{
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
case retries === 0: {
|
|
164
|
+
if (response) {
|
|
165
|
+
return response;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
173
|
+
return await fetchWithRetry(url, {
|
|
174
|
+
...options,
|
|
175
|
+
retries: retries - 1,
|
|
176
|
+
retryDelay: retryDelay * 2,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* @param {ReadableStream<Uint8Array> & {[Symbol.asyncIterator](): AsyncIterator<Uint8Array>}} body
|
|
183
|
+
* @returns {AsyncGenerator<Record<string,any>>}
|
|
184
|
+
*/
|
|
185
|
+
export async function* jsonl(body) {
|
|
186
|
+
const decoder = new TextDecoder();
|
|
187
|
+
let previous = '';
|
|
188
|
+
for await (const chunk of body) {
|
|
189
|
+
previous += decoder.decode(chunk);
|
|
190
|
+
let eolIndex;
|
|
191
|
+
while ((eolIndex = previous.indexOf('\n')) >= 0) {
|
|
192
|
+
const line = previous.slice(0, eolIndex + 1);
|
|
193
|
+
if (line) {
|
|
194
|
+
yield JSON.parse(line);
|
|
195
|
+
}
|
|
196
|
+
previous = previous.slice(eolIndex + 1);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (previous.trim().length > 0) {
|
|
200
|
+
yield JSON.parse(previous);
|
|
201
|
+
}
|
|
202
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatbotkit/fetch",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Isomorphic implenetation for fetch specifically designed for @chatbotkit/sdk.",
|
|
5
5
|
"license": "ISC",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=18.16.0"
|
|
8
|
+
},
|
|
6
9
|
"repository": {
|
|
7
10
|
"type": "git",
|
|
8
11
|
"url": "git+https://github.com/chatbotkit/node-sdk.git"
|
|
@@ -30,6 +33,16 @@
|
|
|
30
33
|
"default": "./dist/esm/index.js"
|
|
31
34
|
}
|
|
32
35
|
},
|
|
36
|
+
"./index": {
|
|
37
|
+
"require": {
|
|
38
|
+
"types": "./dist/cjs/index.d.ts",
|
|
39
|
+
"default": "./dist/cjs/index.cjs"
|
|
40
|
+
},
|
|
41
|
+
"import": {
|
|
42
|
+
"types": "./dist/esm/index.d.ts",
|
|
43
|
+
"default": "./dist/esm/index.js"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
33
46
|
"./index.js": {
|
|
34
47
|
"require": {
|
|
35
48
|
"types": "./dist/cjs/index.d.ts",
|
|
@@ -44,11 +57,12 @@
|
|
|
44
57
|
"scripts": {
|
|
45
58
|
"build": "run-s build:*",
|
|
46
59
|
"build:cjs": "tsc -p tsconfig.cjs.json",
|
|
47
|
-
"build:cjs_rename": "
|
|
48
|
-
"build:cjs_rewrite_cjs": "
|
|
49
|
-
"build:cjs_rewrite_ts": "
|
|
60
|
+
"build:cjs_rename": "node ../../scripts/rename-cjs.js",
|
|
61
|
+
"build:cjs_rewrite_cjs": "node ../../scripts/rewrite-cjs.js",
|
|
62
|
+
"build:cjs_rewrite_ts": "node ../../scripts/rewrite-ts.js",
|
|
50
63
|
"build:esm": "tsc -p tsconfig.esm.json",
|
|
51
|
-
"build:exports": "node scripts/build-exports.js",
|
|
64
|
+
"build:exports": "node ../../scripts/build-exports.js",
|
|
65
|
+
"check": "tsc --noEmit",
|
|
52
66
|
"test": "true"
|
|
53
67
|
},
|
|
54
68
|
"types": "./dist/esm/index.d.ts",
|