undici 7.0.0-alpha.7 → 7.0.0-alpha.9
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/docs/docs/api/Dispatcher.md +15 -189
- package/index.js +1 -0
- package/lib/cache/memory-cache-store.js +2 -0
- package/lib/cache/sqlite-cache-store.js +32 -43
- package/lib/core/errors.js +2 -2
- package/lib/dispatcher/dispatcher-base.js +4 -2
- package/lib/handler/cache-handler.js +148 -49
- package/lib/handler/cache-revalidation-handler.js +21 -10
- package/lib/handler/decorator-handler.js +3 -0
- package/lib/handler/redirect-handler.js +15 -38
- package/lib/handler/retry-handler.js +65 -100
- package/lib/handler/unwrap-handler.js +2 -2
- package/lib/handler/wrap-handler.js +2 -2
- package/lib/interceptor/cache.js +250 -201
- package/lib/interceptor/response-error.js +9 -5
- package/lib/util/cache.js +42 -31
- package/package.json +4 -3
- package/types/cache-interceptor.d.ts +40 -0
- package/types/dispatcher.d.ts +1 -1
|
@@ -207,7 +207,7 @@ Returns: `Boolean` - `false` if dispatcher is busy and further dispatch calls wo
|
|
|
207
207
|
|
|
208
208
|
* **onRequestStart** `(controller: DispatchController, context: object) => void` - Invoked before request is dispatched on socket. May be invoked multiple times when a request is retried when the request at the head of the pipeline fails.
|
|
209
209
|
* **onRequestUpgrade** `(controller: DispatchController, statusCode: number, headers: Record<string, string | string[]>, socket: Duplex) => void` (optional) - Invoked when request is upgraded. Required if `DispatchOptions.upgrade` is defined or `DispatchOptions.method === 'CONNECT'`.
|
|
210
|
-
* **onResponseStart** `(controller: DispatchController, statusCode: number,
|
|
210
|
+
* **onResponseStart** `(controller: DispatchController, statusCode: number, headers: Record<string, string | string []>, statusMessage?: string) => void` - Invoked when statusCode and headers have been received. May be invoked multiple times due to 1xx informational headers. Not required for `upgrade` requests.
|
|
211
211
|
* **onResponseData** `(controller: DispatchController, chunk: Buffer) => void` - Invoked when response payload data is received. Not required for `upgrade` requests.
|
|
212
212
|
* **onResponseEnd** `(controller: DispatchController, trailers: Record<string, string | string[]>) => void` - Invoked when response payload and trailers have been received and the request has completed. Not required for `upgrade` requests.
|
|
213
213
|
* **onResponseError** `(error: Error) => void` - Invoked when an error has occurred. May not throw.
|
|
@@ -1054,203 +1054,27 @@ const response = await client.request({
|
|
|
1054
1054
|
})
|
|
1055
1055
|
```
|
|
1056
1056
|
|
|
1057
|
-
##### `
|
|
1057
|
+
##### `responseError`
|
|
1058
1058
|
|
|
1059
|
-
|
|
1059
|
+
The `responseError` interceptor throws an error for responses with status code errors (>= 400).
|
|
1060
1060
|
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
**ResponseError Class**
|
|
1064
|
-
|
|
1065
|
-
The `ResponseError` class extends the `UndiciError` class and encapsulates detailed error information. It captures the response status code, headers, and data, providing a structured way to handle errors.
|
|
1066
|
-
|
|
1067
|
-
**Definition**
|
|
1068
|
-
|
|
1069
|
-
```js
|
|
1070
|
-
class ResponseError extends UndiciError {
|
|
1071
|
-
constructor (message, code, { headers, data }) {
|
|
1072
|
-
super(message);
|
|
1073
|
-
this.name = 'ResponseError';
|
|
1074
|
-
this.message = message || 'Response error';
|
|
1075
|
-
this.code = 'UND_ERR_RESPONSE';
|
|
1076
|
-
this.statusCode = code;
|
|
1077
|
-
this.data = data;
|
|
1078
|
-
this.headers = headers;
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
```
|
|
1082
|
-
|
|
1083
|
-
**Interceptor Handler**
|
|
1084
|
-
|
|
1085
|
-
The interceptor's handler class extends `DecoratorHandler` and overrides methods to capture response details and handle errors based on the response status code.
|
|
1086
|
-
|
|
1087
|
-
**Methods**
|
|
1088
|
-
|
|
1089
|
-
- **onConnect**: Initializes response properties.
|
|
1090
|
-
- **onHeaders**: Captures headers and status code. Decodes body if content type is `application/json` or `text/plain`.
|
|
1091
|
-
- **onData**: Appends chunks to the body if status code indicates an error.
|
|
1092
|
-
- **onComplete**: Finalizes error handling, constructs a `ResponseError`, and invokes the `onError` method.
|
|
1093
|
-
- **onError**: Propagates errors to the handler.
|
|
1094
|
-
|
|
1095
|
-
**Definition**
|
|
1096
|
-
|
|
1097
|
-
```js
|
|
1098
|
-
class Handler extends DecoratorHandler {
|
|
1099
|
-
// Private properties
|
|
1100
|
-
#handler;
|
|
1101
|
-
#statusCode;
|
|
1102
|
-
#contentType;
|
|
1103
|
-
#decoder;
|
|
1104
|
-
#headers;
|
|
1105
|
-
#body;
|
|
1106
|
-
|
|
1107
|
-
constructor (opts, { handler }) {
|
|
1108
|
-
super(handler);
|
|
1109
|
-
this.#handler = handler;
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
onConnect (abort) {
|
|
1113
|
-
this.#statusCode = 0;
|
|
1114
|
-
this.#contentType = null;
|
|
1115
|
-
this.#decoder = null;
|
|
1116
|
-
this.#headers = null;
|
|
1117
|
-
this.#body = '';
|
|
1118
|
-
return this.#handler.onConnect(abort);
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
onHeaders (statusCode, rawHeaders, resume, statusMessage, headers = parseHeaders(rawHeaders)) {
|
|
1122
|
-
this.#statusCode = statusCode;
|
|
1123
|
-
this.#headers = headers;
|
|
1124
|
-
this.#contentType = headers['content-type'];
|
|
1125
|
-
|
|
1126
|
-
if (this.#statusCode < 400) {
|
|
1127
|
-
return this.#handler.onHeaders(statusCode, rawHeaders, resume, statusMessage, headers);
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
if (this.#contentType === 'application/json' || this.#contentType === 'text/plain') {
|
|
1131
|
-
this.#decoder = new TextDecoder('utf-8');
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
onData (chunk) {
|
|
1136
|
-
if (this.#statusCode < 400) {
|
|
1137
|
-
return this.#handler.onData(chunk);
|
|
1138
|
-
}
|
|
1139
|
-
this.#body += this.#decoder?.decode(chunk, { stream: true }) ?? '';
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
onComplete (rawTrailers) {
|
|
1143
|
-
if (this.#statusCode >= 400) {
|
|
1144
|
-
this.#body += this.#decoder?.decode(undefined, { stream: false }) ?? '';
|
|
1145
|
-
if (this.#contentType === 'application/json') {
|
|
1146
|
-
try {
|
|
1147
|
-
this.#body = JSON.parse(this.#body);
|
|
1148
|
-
} catch {
|
|
1149
|
-
// Do nothing...
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
|
-
let err;
|
|
1154
|
-
const stackTraceLimit = Error.stackTraceLimit;
|
|
1155
|
-
Error.stackTraceLimit = 0;
|
|
1156
|
-
try {
|
|
1157
|
-
err = new ResponseError('Response Error', this.#statusCode, this.#headers, this.#body);
|
|
1158
|
-
} finally {
|
|
1159
|
-
Error.stackTraceLimit = stackTraceLimit;
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
this.#handler.onError(err);
|
|
1163
|
-
} else {
|
|
1164
|
-
this.#handler.onComplete(rawTrailers);
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
onError (err) {
|
|
1169
|
-
this.#handler.onError(err);
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
module.exports = (dispatch) => (opts, handler) => opts.throwOnError
|
|
1174
|
-
? dispatch(opts, new Handler(opts, { handler }))
|
|
1175
|
-
: dispatch(opts, handler);
|
|
1176
|
-
```
|
|
1177
|
-
|
|
1178
|
-
**Tests**
|
|
1179
|
-
|
|
1180
|
-
Unit tests ensure the interceptor functions correctly, handling both error and non-error responses appropriately.
|
|
1181
|
-
|
|
1182
|
-
**Example Tests**
|
|
1183
|
-
|
|
1184
|
-
- **No Error if `throwOnError` is False**:
|
|
1061
|
+
**Example**
|
|
1185
1062
|
|
|
1186
1063
|
```js
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
const handler = { onError: () => {}, onData: () => {}, onComplete: () => {} };
|
|
1190
|
-
const interceptor = createResponseErrorInterceptor((opts, handler) => handler.onComplete());
|
|
1191
|
-
assert.doesNotThrow(() => interceptor(opts, handler));
|
|
1192
|
-
});
|
|
1193
|
-
```
|
|
1194
|
-
|
|
1195
|
-
- **Error if Status Code is in Specified Error Codes**:
|
|
1196
|
-
|
|
1197
|
-
```js
|
|
1198
|
-
test('should error if request status code is in the specified error codes', async (t) => {
|
|
1199
|
-
const opts = { throwOnError: true, statusCodes: [500] };
|
|
1200
|
-
const response = { statusCode: 500 };
|
|
1201
|
-
let capturedError;
|
|
1202
|
-
const handler = {
|
|
1203
|
-
onError: (err) => { capturedError = err; },
|
|
1204
|
-
onData: () => {},
|
|
1205
|
-
onComplete: () => {}
|
|
1206
|
-
};
|
|
1207
|
-
|
|
1208
|
-
const interceptor = createResponseErrorInterceptor((opts, handler) => {
|
|
1209
|
-
if (opts.throwOnError && opts.statusCodes.includes(response.statusCode)) {
|
|
1210
|
-
handler.onError(new Error('Response Error'));
|
|
1211
|
-
} else {
|
|
1212
|
-
handler.onComplete();
|
|
1213
|
-
}
|
|
1214
|
-
});
|
|
1215
|
-
|
|
1216
|
-
interceptor({ ...opts, response }, handler);
|
|
1217
|
-
|
|
1218
|
-
await new Promise(resolve => setImmediate(resolve));
|
|
1219
|
-
|
|
1220
|
-
assert(capturedError, 'Expected error to be captured but it was not.');
|
|
1221
|
-
assert.strictEqual(capturedError.message, 'Response Error');
|
|
1222
|
-
assert.strictEqual(response.statusCode, 500);
|
|
1223
|
-
});
|
|
1224
|
-
```
|
|
1225
|
-
|
|
1226
|
-
- **No Error if Status Code is Not in Specified Error Codes**:
|
|
1064
|
+
const { Client, interceptors } = require("undici");
|
|
1065
|
+
const { responseError } = interceptors;
|
|
1227
1066
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
const response = { statusCode: 404 };
|
|
1232
|
-
const handler = {
|
|
1233
|
-
onError: () => {},
|
|
1234
|
-
onData: () => {},
|
|
1235
|
-
onComplete: () => {}
|
|
1236
|
-
};
|
|
1237
|
-
|
|
1238
|
-
const interceptor = createResponseErrorInterceptor((opts, handler) => {
|
|
1239
|
-
if (opts.throwOnError && opts.statusCodes.includes(response.statusCode)) {
|
|
1240
|
-
handler.onError(new Error('Response Error'));
|
|
1241
|
-
} else {
|
|
1242
|
-
handler.onComplete();
|
|
1243
|
-
}
|
|
1244
|
-
});
|
|
1067
|
+
const client = new Client("http://example.com").compose(
|
|
1068
|
+
responseError()
|
|
1069
|
+
);
|
|
1245
1070
|
|
|
1246
|
-
|
|
1071
|
+
// Will throw a ResponseError for status codes >= 400
|
|
1072
|
+
await client.request({
|
|
1073
|
+
method: "GET",
|
|
1074
|
+
path: "/"
|
|
1247
1075
|
});
|
|
1248
1076
|
```
|
|
1249
1077
|
|
|
1250
|
-
**Conclusion**
|
|
1251
|
-
|
|
1252
|
-
The Response Error Interceptor provides a robust mechanism for handling HTTP response errors by capturing detailed error information and propagating it through a structured `ResponseError` class. This enhancement improves error handling and debugging capabilities in applications using the interceptor.
|
|
1253
|
-
|
|
1254
1078
|
##### `Cache Interceptor`
|
|
1255
1079
|
|
|
1256
1080
|
The `cache` interceptor implements client-side response caching as described in
|
|
@@ -1260,6 +1084,8 @@ The `cache` interceptor implements client-side response caching as described in
|
|
|
1260
1084
|
|
|
1261
1085
|
- `store` - The [`CacheStore`](/docs/docs/api/CacheStore.md) to store and retrieve responses from. Default is [`MemoryCacheStore`](/docs/docs/api/CacheStore.md#memorycachestore).
|
|
1262
1086
|
- `methods` - The [**safe** HTTP methods](https://www.rfc-editor.org/rfc/rfc9110#section-9.2.1) to cache the response of.
|
|
1087
|
+
- `cacheByDefault` - The default expiration time to cache responses by if they don't have an explicit expiration. If this isn't present, responses without explicit expiration will not be cached. Default `undefined`.
|
|
1088
|
+
- `type` - The type of cache for Undici to act as. Can be `shared` or `private`. Default `shared`.
|
|
1263
1089
|
|
|
1264
1090
|
## Instance Events
|
|
1265
1091
|
|
package/index.js
CHANGED
|
@@ -38,6 +38,7 @@ module.exports.DecoratorHandler = DecoratorHandler
|
|
|
38
38
|
module.exports.RedirectHandler = RedirectHandler
|
|
39
39
|
module.exports.interceptors = {
|
|
40
40
|
redirect: require('./lib/interceptor/redirect'),
|
|
41
|
+
responseError: require('./lib/interceptor/response-error'),
|
|
41
42
|
retry: require('./lib/interceptor/retry'),
|
|
42
43
|
dump: require('./lib/interceptor/dump'),
|
|
43
44
|
dns: require('./lib/interceptor/dns'),
|
|
@@ -89,7 +89,9 @@ class MemoryCacheStore {
|
|
|
89
89
|
statusCode: entry.statusCode,
|
|
90
90
|
headers: entry.headers,
|
|
91
91
|
body: entry.body,
|
|
92
|
+
vary: entry.vary ? entry.vary : undefined,
|
|
92
93
|
etag: entry.etag,
|
|
94
|
+
cacheControlDirectives: entry.cacheControlDirectives,
|
|
93
95
|
cachedAt: entry.cachedAt,
|
|
94
96
|
staleAt: entry.staleAt,
|
|
95
97
|
deleteAt: entry.deleteAt
|
|
@@ -4,7 +4,10 @@ const { DatabaseSync } = require('node:sqlite')
|
|
|
4
4
|
const { Writable } = require('stream')
|
|
5
5
|
const { assertCacheKey, assertCacheValue } = require('../util/cache.js')
|
|
6
6
|
|
|
7
|
-
const VERSION =
|
|
7
|
+
const VERSION = 3
|
|
8
|
+
|
|
9
|
+
// 2gb
|
|
10
|
+
const MAX_ENTRY_SIZE = 2 * 1000 * 1000 * 1000
|
|
8
11
|
|
|
9
12
|
/**
|
|
10
13
|
* @typedef {import('../../types/cache-interceptor.d.ts').default.CacheStore} CacheStore
|
|
@@ -17,8 +20,8 @@ const VERSION = 2
|
|
|
17
20
|
* body: string
|
|
18
21
|
* } & import('../../types/cache-interceptor.d.ts').default.CacheValue} SqliteStoreValue
|
|
19
22
|
*/
|
|
20
|
-
class SqliteCacheStore {
|
|
21
|
-
#maxEntrySize =
|
|
23
|
+
module.exports = class SqliteCacheStore {
|
|
24
|
+
#maxEntrySize = MAX_ENTRY_SIZE
|
|
22
25
|
#maxCount = Infinity
|
|
23
26
|
|
|
24
27
|
/**
|
|
@@ -78,6 +81,11 @@ class SqliteCacheStore {
|
|
|
78
81
|
) {
|
|
79
82
|
throw new TypeError('SqliteCacheStore options.maxEntrySize must be a non-negative integer')
|
|
80
83
|
}
|
|
84
|
+
|
|
85
|
+
if (opts.maxEntrySize > MAX_ENTRY_SIZE) {
|
|
86
|
+
throw new TypeError('SqliteCacheStore options.maxEntrySize must be less than 2gb')
|
|
87
|
+
}
|
|
88
|
+
|
|
81
89
|
this.#maxEntrySize = opts.maxEntrySize
|
|
82
90
|
}
|
|
83
91
|
|
|
@@ -103,11 +111,12 @@ class SqliteCacheStore {
|
|
|
103
111
|
method TEXT NOT NULL,
|
|
104
112
|
|
|
105
113
|
-- Data returned to the interceptor
|
|
106
|
-
body
|
|
114
|
+
body BUF NULL,
|
|
107
115
|
deleteAt INTEGER NOT NULL,
|
|
108
116
|
statusCode INTEGER NOT NULL,
|
|
109
117
|
statusMessage TEXT NOT NULL,
|
|
110
118
|
headers TEXT NULL,
|
|
119
|
+
cacheControlDirectives TEXT NULL,
|
|
111
120
|
etag TEXT NULL,
|
|
112
121
|
vary TEXT NULL,
|
|
113
122
|
cachedAt INTEGER NOT NULL,
|
|
@@ -128,6 +137,7 @@ class SqliteCacheStore {
|
|
|
128
137
|
statusMessage,
|
|
129
138
|
headers,
|
|
130
139
|
etag,
|
|
140
|
+
cacheControlDirectives,
|
|
131
141
|
vary,
|
|
132
142
|
cachedAt,
|
|
133
143
|
staleAt
|
|
@@ -147,6 +157,7 @@ class SqliteCacheStore {
|
|
|
147
157
|
statusMessage = ?,
|
|
148
158
|
headers = ?,
|
|
149
159
|
etag = ?,
|
|
160
|
+
cacheControlDirectives = ?,
|
|
150
161
|
cachedAt = ?,
|
|
151
162
|
staleAt = ?,
|
|
152
163
|
deleteAt = ?
|
|
@@ -164,11 +175,12 @@ class SqliteCacheStore {
|
|
|
164
175
|
statusMessage,
|
|
165
176
|
headers,
|
|
166
177
|
etag,
|
|
178
|
+
cacheControlDirectives,
|
|
167
179
|
vary,
|
|
168
180
|
cachedAt,
|
|
169
181
|
staleAt,
|
|
170
182
|
deleteAt
|
|
171
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
183
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
172
184
|
`)
|
|
173
185
|
|
|
174
186
|
this.#deleteByUrlQuery = this.#db.prepare(
|
|
@@ -218,11 +230,15 @@ class SqliteCacheStore {
|
|
|
218
230
|
* @type {import('../../types/cache-interceptor.d.ts').default.GetResult}
|
|
219
231
|
*/
|
|
220
232
|
const result = {
|
|
221
|
-
body:
|
|
233
|
+
body: Buffer.from(value.body),
|
|
222
234
|
statusCode: value.statusCode,
|
|
223
235
|
statusMessage: value.statusMessage,
|
|
224
236
|
headers: value.headers ? JSON.parse(value.headers) : undefined,
|
|
225
237
|
etag: value.etag ? value.etag : undefined,
|
|
238
|
+
vary: value.vary ?? undefined,
|
|
239
|
+
cacheControlDirectives: value.cacheControlDirectives
|
|
240
|
+
? JSON.parse(value.cacheControlDirectives)
|
|
241
|
+
: undefined,
|
|
226
242
|
cachedAt: value.cachedAt,
|
|
227
243
|
staleAt: value.staleAt,
|
|
228
244
|
deleteAt: value.deleteAt
|
|
@@ -269,12 +285,13 @@ class SqliteCacheStore {
|
|
|
269
285
|
if (existingValue) {
|
|
270
286
|
// Updating an existing response, let's overwrite it
|
|
271
287
|
store.#updateValueQuery.run(
|
|
272
|
-
|
|
288
|
+
Buffer.concat(body),
|
|
273
289
|
value.deleteAt,
|
|
274
290
|
value.statusCode,
|
|
275
291
|
value.statusMessage,
|
|
276
292
|
value.headers ? JSON.stringify(value.headers) : null,
|
|
277
|
-
value.etag,
|
|
293
|
+
value.etag ? value.etag : null,
|
|
294
|
+
value.cacheControlDirectives ? JSON.stringify(value.cacheControlDirectives) : null,
|
|
278
295
|
value.cachedAt,
|
|
279
296
|
value.staleAt,
|
|
280
297
|
value.deleteAt,
|
|
@@ -286,12 +303,13 @@ class SqliteCacheStore {
|
|
|
286
303
|
store.#insertValueQuery.run(
|
|
287
304
|
url,
|
|
288
305
|
key.method,
|
|
289
|
-
|
|
306
|
+
Buffer.concat(body),
|
|
290
307
|
value.deleteAt,
|
|
291
308
|
value.statusCode,
|
|
292
309
|
value.statusMessage,
|
|
293
310
|
value.headers ? JSON.stringify(value.headers) : null,
|
|
294
311
|
value.etag ? value.etag : null,
|
|
312
|
+
value.cacheControlDirectives ? JSON.stringify(value.cacheControlDirectives) : null,
|
|
295
313
|
value.vary ? JSON.stringify(value.vary) : null,
|
|
296
314
|
value.cachedAt,
|
|
297
315
|
value.staleAt,
|
|
@@ -316,7 +334,7 @@ class SqliteCacheStore {
|
|
|
316
334
|
}
|
|
317
335
|
|
|
318
336
|
#prune () {
|
|
319
|
-
if (this
|
|
337
|
+
if (this.size <= this.#maxCount) {
|
|
320
338
|
return 0
|
|
321
339
|
}
|
|
322
340
|
|
|
@@ -341,7 +359,7 @@ class SqliteCacheStore {
|
|
|
341
359
|
* Counts the number of rows in the cache
|
|
342
360
|
* @returns {Number}
|
|
343
361
|
*/
|
|
344
|
-
get
|
|
362
|
+
get size () {
|
|
345
363
|
const { total } = this.#countEntriesQuery.get()
|
|
346
364
|
return total
|
|
347
365
|
}
|
|
@@ -385,10 +403,10 @@ class SqliteCacheStore {
|
|
|
385
403
|
return undefined
|
|
386
404
|
}
|
|
387
405
|
|
|
388
|
-
|
|
406
|
+
value.vary = JSON.parse(value.vary)
|
|
389
407
|
|
|
390
|
-
for (const header in vary) {
|
|
391
|
-
if (headerValueEquals(headers[header], vary[header])) {
|
|
408
|
+
for (const header in value.vary) {
|
|
409
|
+
if (!headerValueEquals(headers[header], value.vary[header])) {
|
|
392
410
|
matches = false
|
|
393
411
|
break
|
|
394
412
|
}
|
|
@@ -426,32 +444,3 @@ function headerValueEquals (lhs, rhs) {
|
|
|
426
444
|
|
|
427
445
|
return lhs === rhs
|
|
428
446
|
}
|
|
429
|
-
|
|
430
|
-
/**
|
|
431
|
-
* @param {Buffer[]} buffers
|
|
432
|
-
* @returns {string[]}
|
|
433
|
-
*/
|
|
434
|
-
function stringifyBufferArray (buffers) {
|
|
435
|
-
const output = new Array(buffers.length)
|
|
436
|
-
for (let i = 0; i < buffers.length; i++) {
|
|
437
|
-
output[i] = buffers[i].toString()
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
return output
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
/**
|
|
444
|
-
* @param {string[]} strings
|
|
445
|
-
* @returns {Buffer[]}
|
|
446
|
-
*/
|
|
447
|
-
function parseBufferArray (strings) {
|
|
448
|
-
const output = new Array(strings.length)
|
|
449
|
-
|
|
450
|
-
for (let i = 0; i < strings.length; i++) {
|
|
451
|
-
output[i] = Buffer.from(strings[i])
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
return output
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
module.exports = SqliteCacheStore
|
package/lib/core/errors.js
CHANGED
|
@@ -196,13 +196,13 @@ class RequestRetryError extends UndiciError {
|
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
class ResponseError extends UndiciError {
|
|
199
|
-
constructor (message, code, { headers,
|
|
199
|
+
constructor (message, code, { headers, body }) {
|
|
200
200
|
super(message)
|
|
201
201
|
this.name = 'ResponseError'
|
|
202
202
|
this.message = message || 'Response error'
|
|
203
203
|
this.code = 'UND_ERR_RESPONSE'
|
|
204
204
|
this.statusCode = code
|
|
205
|
-
this.
|
|
205
|
+
this.body = body
|
|
206
206
|
this.headers = headers
|
|
207
207
|
}
|
|
208
208
|
}
|
|
@@ -130,6 +130,8 @@ class DispatcherBase extends Dispatcher {
|
|
|
130
130
|
throw new InvalidArgumentError('handler must be an object')
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
handler = UnwrapHandler.unwrap(handler)
|
|
134
|
+
|
|
133
135
|
try {
|
|
134
136
|
if (!opts || typeof opts !== 'object') {
|
|
135
137
|
throw new InvalidArgumentError('opts must be an object.')
|
|
@@ -143,10 +145,10 @@ class DispatcherBase extends Dispatcher {
|
|
|
143
145
|
throw new ClientClosedError()
|
|
144
146
|
}
|
|
145
147
|
|
|
146
|
-
return this[kDispatch](opts,
|
|
148
|
+
return this[kDispatch](opts, handler)
|
|
147
149
|
} catch (err) {
|
|
148
150
|
if (typeof handler.onError !== 'function') {
|
|
149
|
-
throw
|
|
151
|
+
throw err
|
|
150
152
|
}
|
|
151
153
|
|
|
152
154
|
handler.onError(err)
|