lifecycleion 0.0.8 → 0.0.10
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 +45 -36
- package/dist/lib/domain-utils/domain-utils.cjs +1154 -0
- package/dist/lib/domain-utils/domain-utils.cjs.map +1 -0
- package/dist/lib/domain-utils/domain-utils.d.cts +210 -0
- package/dist/lib/domain-utils/domain-utils.d.ts +210 -0
- package/dist/lib/domain-utils/domain-utils.js +1112 -0
- package/dist/lib/domain-utils/domain-utils.js.map +1 -0
- package/dist/lib/http-client/index.cjs +5254 -0
- package/dist/lib/http-client/index.cjs.map +1 -0
- package/dist/lib/http-client/index.d.cts +372 -0
- package/dist/lib/http-client/index.d.ts +372 -0
- package/dist/lib/http-client/index.js +5207 -0
- package/dist/lib/http-client/index.js.map +1 -0
- package/dist/lib/http-client-mock/index.cjs +525 -0
- package/dist/lib/http-client-mock/index.cjs.map +1 -0
- package/dist/lib/http-client-mock/index.d.cts +129 -0
- package/dist/lib/http-client-mock/index.d.ts +129 -0
- package/dist/lib/http-client-mock/index.js +488 -0
- package/dist/lib/http-client-mock/index.js.map +1 -0
- package/dist/lib/http-client-node/index.cjs +1112 -0
- package/dist/lib/http-client-node/index.cjs.map +1 -0
- package/dist/lib/http-client-node/index.d.cts +43 -0
- package/dist/lib/http-client-node/index.d.ts +43 -0
- package/dist/lib/http-client-node/index.js +1075 -0
- package/dist/lib/http-client-node/index.js.map +1 -0
- package/dist/lib/http-client-xhr/index.cjs +323 -0
- package/dist/lib/http-client-xhr/index.cjs.map +1 -0
- package/dist/lib/http-client-xhr/index.d.cts +23 -0
- package/dist/lib/http-client-xhr/index.d.ts +23 -0
- package/dist/lib/http-client-xhr/index.js +286 -0
- package/dist/lib/http-client-xhr/index.js.map +1 -0
- package/dist/lib/lifecycle-manager/index.cjs +118 -61
- package/dist/lib/lifecycle-manager/index.cjs.map +1 -1
- package/dist/lib/lifecycle-manager/index.js +118 -61
- package/dist/lib/lifecycle-manager/index.js.map +1 -1
- package/dist/lib/lru-cache/index.cjs +274 -0
- package/dist/lib/lru-cache/index.cjs.map +1 -0
- package/dist/lib/lru-cache/index.d.cts +84 -0
- package/dist/lib/lru-cache/index.d.ts +84 -0
- package/dist/lib/lru-cache/index.js +249 -0
- package/dist/lib/lru-cache/index.js.map +1 -0
- package/dist/lib/retry-utils/index.d.cts +3 -23
- package/dist/lib/retry-utils/index.d.ts +3 -23
- package/dist/types-CUPvmYQ8.d.cts +868 -0
- package/dist/types-D_MywcG0.d.cts +23 -0
- package/dist/types-D_MywcG0.d.ts +23 -0
- package/dist/types-Hw2PUTIT.d.ts +868 -0
- package/package.json +45 -3
|
@@ -0,0 +1,1112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/lib/http-client-node/index.ts
|
|
31
|
+
var http_client_node_exports = {};
|
|
32
|
+
__export(http_client_node_exports, {
|
|
33
|
+
NodeAdapter: () => NodeAdapter
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(http_client_node_exports);
|
|
36
|
+
|
|
37
|
+
// src/lib/http-client/adapters/node-adapter.ts
|
|
38
|
+
var http = __toESM(require("http"), 1);
|
|
39
|
+
var https = __toESM(require("https"), 1);
|
|
40
|
+
var import_node_url = require("url");
|
|
41
|
+
|
|
42
|
+
// src/lib/http-client/consts.ts
|
|
43
|
+
var NON_RETRYABLE_HTTP_CLIENT_CALLBACK_ERROR_FLAG = "_lifecycleion_non_retryable_http_client_callback_error";
|
|
44
|
+
var STREAM_FACTORY_ERROR_FLAG = "_lifecycleion_stream_factory_error";
|
|
45
|
+
var STREAM_FACTORY_CANCEL_KEY = "_lifecycleion_stream_factory_cancel_reason";
|
|
46
|
+
var RESPONSE_STREAM_ABORT_FLAG = "_lifecycleion_response_stream_abort";
|
|
47
|
+
var REDIRECT_STATUS_CODES = /* @__PURE__ */ new Set([
|
|
48
|
+
// 301 Moved Permanently
|
|
49
|
+
301,
|
|
50
|
+
// 302 Found
|
|
51
|
+
302,
|
|
52
|
+
// 303 See Other
|
|
53
|
+
303,
|
|
54
|
+
// 307 Temporary Redirect
|
|
55
|
+
307,
|
|
56
|
+
// 308 Permanent Redirect
|
|
57
|
+
308
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
// src/lib/http-client/internal/multipart.ts
|
|
61
|
+
function formatFilename(filename) {
|
|
62
|
+
const fallback = toSafeASCIIQuotedFilenameFallback(filename);
|
|
63
|
+
const base = isSafeASCIIQuotedFilename(filename) ? `filename="${filename}"` : `filename="${fallback}"`;
|
|
64
|
+
if (isSafeASCIIQuotedFilename(filename)) {
|
|
65
|
+
return base;
|
|
66
|
+
}
|
|
67
|
+
const encoded = encodeURIComponent(filename).replace(/'/g, "%27").replace(/\(/g, "%28").replace(/\)/g, "%29").replace(/\*/g, "%2A");
|
|
68
|
+
return `${base}; filename*=UTF-8''${encoded}`;
|
|
69
|
+
}
|
|
70
|
+
function isSafeASCIIQuotedFilename(value) {
|
|
71
|
+
return /^[\u0020-\u007E]+$/.test(value) && !/["\\]/.test(value);
|
|
72
|
+
}
|
|
73
|
+
function toSafeASCIIQuotedFilenameFallback(value) {
|
|
74
|
+
const normalized = value.normalize("NFKD");
|
|
75
|
+
let result = "";
|
|
76
|
+
let wasUnderscore = false;
|
|
77
|
+
for (const char of normalized) {
|
|
78
|
+
const code = char.charCodeAt(0);
|
|
79
|
+
if (code >= 768 && code <= 879) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const isSafeASCII = (
|
|
83
|
+
// 0-9
|
|
84
|
+
code >= 48 && code <= 57 || // A-Z
|
|
85
|
+
code >= 65 && code <= 90 || // a-z
|
|
86
|
+
code >= 97 && code <= 122 || char === "." || char === "-" || char === "_"
|
|
87
|
+
);
|
|
88
|
+
if (isSafeASCII) {
|
|
89
|
+
result += char;
|
|
90
|
+
wasUnderscore = false;
|
|
91
|
+
} else if (!wasUnderscore) {
|
|
92
|
+
result += "_";
|
|
93
|
+
wasUnderscore = true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
result = result.replace(/^_+|_+$/g, "");
|
|
97
|
+
return result.length > 0 ? result : "file";
|
|
98
|
+
}
|
|
99
|
+
function formatFieldName(name) {
|
|
100
|
+
return name.replace(/\r\n|\r|\n/g, " ").replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, "").replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
101
|
+
}
|
|
102
|
+
function sanitizeContentType(raw) {
|
|
103
|
+
return raw.replace(/\r\n|\r|\n/g, "");
|
|
104
|
+
}
|
|
105
|
+
function generateMultipartBoundary() {
|
|
106
|
+
return `----NodeAdapterFormBoundary${Math.random().toString(36).slice(2)}`;
|
|
107
|
+
}
|
|
108
|
+
function calculateMultipartFormDataSize(formData, boundary) {
|
|
109
|
+
let size = 0;
|
|
110
|
+
for (const [name, value] of formData.entries()) {
|
|
111
|
+
const fieldName = formatFieldName(name);
|
|
112
|
+
size += Buffer.byteLength(`--${boundary}\r
|
|
113
|
+
`);
|
|
114
|
+
if (typeof value === "string") {
|
|
115
|
+
size += Buffer.byteLength(
|
|
116
|
+
`Content-Disposition: form-data; name="${fieldName}"\r
|
|
117
|
+
\r
|
|
118
|
+
${value}\r
|
|
119
|
+
`
|
|
120
|
+
);
|
|
121
|
+
} else {
|
|
122
|
+
const filename = value.name || "blob";
|
|
123
|
+
const contentType = sanitizeContentType(value.type) || "application/octet-stream";
|
|
124
|
+
size += Buffer.byteLength(
|
|
125
|
+
`Content-Disposition: form-data; name="${fieldName}"; ${formatFilename(filename)}\r
|
|
126
|
+
Content-Type: ${contentType}\r
|
|
127
|
+
\r
|
|
128
|
+
`
|
|
129
|
+
);
|
|
130
|
+
size += value.size;
|
|
131
|
+
size += Buffer.byteLength("\r\n");
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
size += Buffer.byteLength(`--${boundary}--\r
|
|
135
|
+
`);
|
|
136
|
+
return size;
|
|
137
|
+
}
|
|
138
|
+
async function serializeMultipartFormData(formData, req, boundary, onProgress) {
|
|
139
|
+
const totalSize = calculateMultipartFormDataSize(formData, boundary);
|
|
140
|
+
req.setHeader("Content-Type", `multipart/form-data; boundary=${boundary}`);
|
|
141
|
+
req.setHeader("Content-Length", totalSize.toString());
|
|
142
|
+
let uploadedBytes = 0;
|
|
143
|
+
const write = (data) => new Promise((resolve, reject) => {
|
|
144
|
+
let hasWriteReturned = false;
|
|
145
|
+
let isWriteCallbackDone = false;
|
|
146
|
+
let isDrainDone = true;
|
|
147
|
+
const cleanup = () => {
|
|
148
|
+
req.off("drain", onDrain);
|
|
149
|
+
req.off("close", onClose);
|
|
150
|
+
req.off("error", onError);
|
|
151
|
+
};
|
|
152
|
+
const maybeResolve = () => {
|
|
153
|
+
if (isWriteCallbackDone && isDrainDone) {
|
|
154
|
+
cleanup();
|
|
155
|
+
uploadedBytes += Buffer.byteLength(data);
|
|
156
|
+
onProgress?.({
|
|
157
|
+
loaded: uploadedBytes,
|
|
158
|
+
total: totalSize,
|
|
159
|
+
progress: uploadedBytes / totalSize
|
|
160
|
+
});
|
|
161
|
+
resolve();
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
const onDrain = () => {
|
|
165
|
+
isDrainDone = true;
|
|
166
|
+
maybeResolve();
|
|
167
|
+
};
|
|
168
|
+
const onClose = () => {
|
|
169
|
+
cleanup();
|
|
170
|
+
resolve();
|
|
171
|
+
};
|
|
172
|
+
const onError = (error) => {
|
|
173
|
+
cleanup();
|
|
174
|
+
reject(error);
|
|
175
|
+
};
|
|
176
|
+
const canContinue = req.write(data, (error) => {
|
|
177
|
+
if (error) {
|
|
178
|
+
cleanup();
|
|
179
|
+
reject(error);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
isWriteCallbackDone = true;
|
|
183
|
+
if (hasWriteReturned) {
|
|
184
|
+
maybeResolve();
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
hasWriteReturned = true;
|
|
188
|
+
if (!canContinue) {
|
|
189
|
+
isDrainDone = false;
|
|
190
|
+
req.once("drain", onDrain);
|
|
191
|
+
req.once("close", onClose);
|
|
192
|
+
req.once("error", onError);
|
|
193
|
+
if (req.destroyed) {
|
|
194
|
+
onClose();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
maybeResolve();
|
|
198
|
+
});
|
|
199
|
+
for (const [name, value] of formData.entries()) {
|
|
200
|
+
const fieldName = formatFieldName(name);
|
|
201
|
+
if (req.destroyed) {
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
await write(`--${boundary}\r
|
|
205
|
+
`);
|
|
206
|
+
if (typeof value === "string") {
|
|
207
|
+
await write(
|
|
208
|
+
`Content-Disposition: form-data; name="${fieldName}"\r
|
|
209
|
+
\r
|
|
210
|
+
${value}\r
|
|
211
|
+
`
|
|
212
|
+
);
|
|
213
|
+
} else {
|
|
214
|
+
const filename = value.name || "blob";
|
|
215
|
+
const contentType = sanitizeContentType(value.type) || "application/octet-stream";
|
|
216
|
+
await write(
|
|
217
|
+
`Content-Disposition: form-data; name="${fieldName}"; ${formatFilename(filename)}\r
|
|
218
|
+
Content-Type: ${contentType}\r
|
|
219
|
+
\r
|
|
220
|
+
`
|
|
221
|
+
);
|
|
222
|
+
const reader = value.stream().getReader();
|
|
223
|
+
try {
|
|
224
|
+
while (true) {
|
|
225
|
+
if (req.destroyed) {
|
|
226
|
+
await cancelReaderQuietly(reader);
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
const { done: isDone, value: chunk } = await reader.read();
|
|
230
|
+
if (isDone) {
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
if (req.destroyed) {
|
|
234
|
+
await cancelReaderQuietly(reader);
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
if (chunk) {
|
|
238
|
+
await write(chunk);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
} finally {
|
|
242
|
+
if (req.destroyed) {
|
|
243
|
+
await cancelReaderQuietly(reader);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (!req.destroyed) {
|
|
247
|
+
await write("\r\n");
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (!req.destroyed) {
|
|
252
|
+
await write(`--${boundary}--\r
|
|
253
|
+
`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
async function cancelReaderQuietly(reader) {
|
|
257
|
+
try {
|
|
258
|
+
await reader.cancel();
|
|
259
|
+
} catch {
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// src/lib/http-client/internal/request-body-writer.ts
|
|
264
|
+
var REQUEST_BODY_CHUNK_SIZE = 16 * 1024;
|
|
265
|
+
async function writeRequestBodyChunked(data, req, onProgress) {
|
|
266
|
+
const totalSize = data.length;
|
|
267
|
+
if (totalSize === 0) {
|
|
268
|
+
onProgress?.({ loaded: 0, total: 0, progress: 1 });
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
let uploadedBytes = 0;
|
|
272
|
+
while (uploadedBytes < totalSize && !req.destroyed) {
|
|
273
|
+
const chunk = data.subarray(
|
|
274
|
+
uploadedBytes,
|
|
275
|
+
uploadedBytes + REQUEST_BODY_CHUNK_SIZE
|
|
276
|
+
);
|
|
277
|
+
await writeChunkWithBackpressure(req, chunk, () => {
|
|
278
|
+
uploadedBytes += chunk.length;
|
|
279
|
+
onProgress?.({
|
|
280
|
+
loaded: uploadedBytes,
|
|
281
|
+
total: totalSize,
|
|
282
|
+
progress: uploadedBytes / totalSize
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function writeChunkWithBackpressure(req, chunk, onAccepted) {
|
|
288
|
+
return new Promise((resolve, reject) => {
|
|
289
|
+
let hasWriteReturned = false;
|
|
290
|
+
let isWriteCallbackDone = false;
|
|
291
|
+
let isDrainDone = true;
|
|
292
|
+
let isSettled = false;
|
|
293
|
+
const cleanup = () => {
|
|
294
|
+
req.off("drain", onDrain);
|
|
295
|
+
req.off("close", onClose);
|
|
296
|
+
req.off("error", onError);
|
|
297
|
+
};
|
|
298
|
+
const maybeResolve = () => {
|
|
299
|
+
if (isSettled || !isWriteCallbackDone || !isDrainDone) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
isSettled = true;
|
|
303
|
+
cleanup();
|
|
304
|
+
onAccepted?.();
|
|
305
|
+
resolve();
|
|
306
|
+
};
|
|
307
|
+
const onDrain = () => {
|
|
308
|
+
isDrainDone = true;
|
|
309
|
+
maybeResolve();
|
|
310
|
+
};
|
|
311
|
+
const onClose = () => {
|
|
312
|
+
if (isSettled) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
isSettled = true;
|
|
316
|
+
cleanup();
|
|
317
|
+
resolve();
|
|
318
|
+
};
|
|
319
|
+
const onError = (error) => {
|
|
320
|
+
if (isSettled) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
isSettled = true;
|
|
324
|
+
cleanup();
|
|
325
|
+
reject(error);
|
|
326
|
+
};
|
|
327
|
+
const canContinue = req.write(
|
|
328
|
+
chunk,
|
|
329
|
+
(error) => {
|
|
330
|
+
if (error) {
|
|
331
|
+
cleanup();
|
|
332
|
+
reject(error);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
isWriteCallbackDone = true;
|
|
336
|
+
if (hasWriteReturned) {
|
|
337
|
+
maybeResolve();
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
);
|
|
341
|
+
hasWriteReturned = true;
|
|
342
|
+
if (!canContinue) {
|
|
343
|
+
isDrainDone = false;
|
|
344
|
+
req.once("drain", onDrain);
|
|
345
|
+
req.once("close", onClose);
|
|
346
|
+
req.once("error", onError);
|
|
347
|
+
if (req.destroyed) {
|
|
348
|
+
onClose();
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
maybeResolve();
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// src/lib/http-client/internal/tls-error-utils.ts
|
|
356
|
+
var CERT_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
357
|
+
"UNABLE_TO_VERIFY_LEAF_SIGNATURE",
|
|
358
|
+
"CERT_HAS_EXPIRED",
|
|
359
|
+
"DEPTH_ZERO_SELF_SIGNED_CERT",
|
|
360
|
+
"ERR_TLS_CERT_ALTNAME_INVALID",
|
|
361
|
+
"ERR_SSL_TLSV13_ALERT_CERTIFICATE_REQUIRED",
|
|
362
|
+
"ERR_SSL_PEER_DID_NOT_RETURN_A_CERTIFICATE",
|
|
363
|
+
"SELF_SIGNED_CERT_IN_CHAIN",
|
|
364
|
+
"UNABLE_TO_GET_ISSUER_CERT_LOCALLY"
|
|
365
|
+
]);
|
|
366
|
+
function isTLSCertificateError(error) {
|
|
367
|
+
const code = error.code ?? "";
|
|
368
|
+
if (CERT_ERROR_CODES.has(code)) {
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
if ((code.startsWith("ERR_TLS_") || code.startsWith("ERR_SSL_")) && code.includes("CERT")) {
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
return /certificate|self signed|unable to verify|altname/i.test(
|
|
375
|
+
error.message
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// src/lib/http-client/adapters/node-adapter-utils.ts
|
|
380
|
+
function normalizeNodeRequestHeaders(headers) {
|
|
381
|
+
const result = {};
|
|
382
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
383
|
+
if (value === void 0) {
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
result[key.toLowerCase()] = Array.isArray(value) ? value.map((item) => String(item)) : String(value);
|
|
387
|
+
}
|
|
388
|
+
return result;
|
|
389
|
+
}
|
|
390
|
+
function materializeNodeRequestHeaders(headers) {
|
|
391
|
+
const result = {};
|
|
392
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
393
|
+
if (!Array.isArray(value)) {
|
|
394
|
+
result[key] = value;
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
result[key] = key.toLowerCase() === "cookie" ? (
|
|
398
|
+
// RFC 6265 cookie-pair delimiter for a single Cookie header field.
|
|
399
|
+
value.join("; ")
|
|
400
|
+
) : value.join(", ");
|
|
401
|
+
}
|
|
402
|
+
return result;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// src/lib/http-client/utils.ts
|
|
406
|
+
var import_qs = __toESM(require("qs"), 1);
|
|
407
|
+
|
|
408
|
+
// src/lib/domain-utils/domain-utils.ts
|
|
409
|
+
var import_tldts = require("tldts");
|
|
410
|
+
|
|
411
|
+
// src/lib/domain-utils/helpers.ts
|
|
412
|
+
var import_tr46 = require("tr46");
|
|
413
|
+
var INTERNAL_PSEUDO_TLDS = Object.freeze(
|
|
414
|
+
/* @__PURE__ */ new Set(["localhost", "local", "test", "internal"])
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
// src/lib/domain-utils/domain-utils.ts
|
|
418
|
+
var import_tldts2 = require("tldts");
|
|
419
|
+
|
|
420
|
+
// src/lib/http-client/utils.ts
|
|
421
|
+
function resolveAbsoluteURL(url, baseURL) {
|
|
422
|
+
if (!url) {
|
|
423
|
+
return url;
|
|
424
|
+
}
|
|
425
|
+
try {
|
|
426
|
+
return new URL(url).href;
|
|
427
|
+
} catch {
|
|
428
|
+
}
|
|
429
|
+
if (baseURL) {
|
|
430
|
+
try {
|
|
431
|
+
const base = baseURL.endsWith("/") ? baseURL : `${baseURL}/`;
|
|
432
|
+
return new URL(url, base).href;
|
|
433
|
+
} catch {
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return url;
|
|
437
|
+
}
|
|
438
|
+
function scalarHeader(headers, lowercaseName) {
|
|
439
|
+
const v = headers[lowercaseName];
|
|
440
|
+
if (v === void 0) {
|
|
441
|
+
return void 0;
|
|
442
|
+
}
|
|
443
|
+
return Array.isArray(v) ? v[0] : v;
|
|
444
|
+
}
|
|
445
|
+
function resolveDetectedRedirectURL(requestURL, status, headers, baseURL) {
|
|
446
|
+
if (![301, 302, 303, 307, 308].includes(status)) {
|
|
447
|
+
return void 0;
|
|
448
|
+
}
|
|
449
|
+
const location = scalarHeader(headers, "location");
|
|
450
|
+
if (!location) {
|
|
451
|
+
return void 0;
|
|
452
|
+
}
|
|
453
|
+
try {
|
|
454
|
+
const absoluteRequestURL = resolveAbsoluteURL(requestURL, baseURL);
|
|
455
|
+
return new URL(location, absoluteRequestURL).toString();
|
|
456
|
+
} catch {
|
|
457
|
+
return location;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// src/lib/http-client/adapters/node-adapter.ts
|
|
462
|
+
var NodeAdapter = class {
|
|
463
|
+
_config;
|
|
464
|
+
constructor(config = {}) {
|
|
465
|
+
this._config = config;
|
|
466
|
+
}
|
|
467
|
+
getType() {
|
|
468
|
+
return "node";
|
|
469
|
+
}
|
|
470
|
+
async send(request) {
|
|
471
|
+
const parsedURL = new URL(request.requestURL);
|
|
472
|
+
const urlOptions = (0, import_node_url.urlToHttpOptions)(parsedURL);
|
|
473
|
+
const isHTTPS = parsedURL.protocol === "https:";
|
|
474
|
+
const httpModule = isHTTPS ? https : http;
|
|
475
|
+
const options = {
|
|
476
|
+
method: request.method,
|
|
477
|
+
headers: materializeNodeRequestHeaders(request.headers)
|
|
478
|
+
// Timeout is managed by the client via abort signal — the adapter does
|
|
479
|
+
// not impose its own timeout so the client retains full control.
|
|
480
|
+
};
|
|
481
|
+
if (urlOptions.auth) {
|
|
482
|
+
options.auth = urlOptions.auth;
|
|
483
|
+
}
|
|
484
|
+
if (this._config.socketPath) {
|
|
485
|
+
options.socketPath = this._config.socketPath;
|
|
486
|
+
options.hostname = urlOptions.hostname;
|
|
487
|
+
options.port = urlOptions.port;
|
|
488
|
+
options.path = urlOptions.path;
|
|
489
|
+
} else {
|
|
490
|
+
options.hostname = urlOptions.hostname;
|
|
491
|
+
options.port = urlOptions.port ?? (isHTTPS ? 443 : 80);
|
|
492
|
+
options.path = urlOptions.path;
|
|
493
|
+
}
|
|
494
|
+
if (isHTTPS) {
|
|
495
|
+
const httpsOptions = options;
|
|
496
|
+
if (this._config.mtls) {
|
|
497
|
+
httpsOptions.cert = this._config.mtls.cert;
|
|
498
|
+
httpsOptions.key = this._config.mtls.key;
|
|
499
|
+
if (this._config.mtls.ca) {
|
|
500
|
+
httpsOptions.ca = this._config.mtls.ca;
|
|
501
|
+
}
|
|
502
|
+
httpsOptions.rejectUnauthorized = true;
|
|
503
|
+
}
|
|
504
|
+
if (this._config.rejectUnauthorized === false) {
|
|
505
|
+
httpsOptions.rejectUnauthorized = false;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return new Promise((resolve, reject) => {
|
|
509
|
+
let activeResponseStream;
|
|
510
|
+
let activeBufferedResponse;
|
|
511
|
+
let isStreamFactoryPending = false;
|
|
512
|
+
let uploadedBodyBytes = 0;
|
|
513
|
+
let didFireUpload100 = false;
|
|
514
|
+
const reportUploadProgress = (event) => {
|
|
515
|
+
if (didFireUpload100) {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
if (event.progress === 1) {
|
|
519
|
+
didFireUpload100 = true;
|
|
520
|
+
}
|
|
521
|
+
uploadedBodyBytes = Math.max(uploadedBodyBytes, event.loaded);
|
|
522
|
+
request.onUploadProgress?.(event);
|
|
523
|
+
};
|
|
524
|
+
reportUploadProgress({ loaded: 0, total: 0, progress: 0 });
|
|
525
|
+
const req = httpModule.request(options, (res) => {
|
|
526
|
+
void (async () => {
|
|
527
|
+
const status = res.statusCode ?? 0;
|
|
528
|
+
const headers = normalizeResponseHeaders(res.headers);
|
|
529
|
+
if (request.streamResponse && status === 200) {
|
|
530
|
+
const streamAbort = new AbortController();
|
|
531
|
+
if (request.signal) {
|
|
532
|
+
if (request.signal.aborted) {
|
|
533
|
+
streamAbort.abort();
|
|
534
|
+
}
|
|
535
|
+
request.signal.addEventListener(
|
|
536
|
+
"abort",
|
|
537
|
+
() => {
|
|
538
|
+
streamAbort.abort();
|
|
539
|
+
},
|
|
540
|
+
{ once: true }
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
let writable;
|
|
544
|
+
try {
|
|
545
|
+
isStreamFactoryPending = true;
|
|
546
|
+
writable = await request.streamResponse(
|
|
547
|
+
{
|
|
548
|
+
status: 200,
|
|
549
|
+
headers,
|
|
550
|
+
url: request.requestURL,
|
|
551
|
+
attempt: request.attemptNumber ?? 1,
|
|
552
|
+
requestID: request.requestID ?? ""
|
|
553
|
+
},
|
|
554
|
+
{ signal: streamAbort.signal }
|
|
555
|
+
);
|
|
556
|
+
} catch (error) {
|
|
557
|
+
isStreamFactoryPending = false;
|
|
558
|
+
streamAbort.abort();
|
|
559
|
+
req.destroy();
|
|
560
|
+
reject(markStreamFactoryError(error, req, request.headers));
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
isStreamFactoryPending = false;
|
|
564
|
+
if (streamAbort.signal.aborted) {
|
|
565
|
+
if (writable && !isStreamResponseCancel(writable)) {
|
|
566
|
+
writable.destroy();
|
|
567
|
+
}
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
if (writable === null || isStreamResponseCancel(writable)) {
|
|
571
|
+
const cancelReason = writable !== null ? writable.reason : void 0;
|
|
572
|
+
streamAbort.abort();
|
|
573
|
+
req.destroy();
|
|
574
|
+
const abortErr = new Error(
|
|
575
|
+
"Request cancelled by streamResponse factory"
|
|
576
|
+
);
|
|
577
|
+
abortErr.name = "AbortError";
|
|
578
|
+
Object.assign(abortErr, {
|
|
579
|
+
[STREAM_FACTORY_CANCEL_KEY]: cancelReason ?? true
|
|
580
|
+
});
|
|
581
|
+
reject(abortErr);
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
activeResponseStream = {
|
|
585
|
+
status,
|
|
586
|
+
headers,
|
|
587
|
+
writable
|
|
588
|
+
};
|
|
589
|
+
const totalBytes2 = parseInt(String(headers["content-length"] ?? "0"), 10) || 0;
|
|
590
|
+
const streamResult = await streamResponseBody(
|
|
591
|
+
res,
|
|
592
|
+
writable,
|
|
593
|
+
totalBytes2,
|
|
594
|
+
request.onDownloadProgress
|
|
595
|
+
);
|
|
596
|
+
if (streamResult === true) {
|
|
597
|
+
activeResponseStream = void 0;
|
|
598
|
+
resolveAdapterResponse(
|
|
599
|
+
resolve,
|
|
600
|
+
req,
|
|
601
|
+
request.requestURL,
|
|
602
|
+
request.headers,
|
|
603
|
+
{
|
|
604
|
+
status,
|
|
605
|
+
headers,
|
|
606
|
+
body: null,
|
|
607
|
+
isStreamed: true
|
|
608
|
+
}
|
|
609
|
+
);
|
|
610
|
+
} else {
|
|
611
|
+
activeResponseStream = void 0;
|
|
612
|
+
streamAbort.abort();
|
|
613
|
+
destroyWritableQuietly(writable);
|
|
614
|
+
req.destroy();
|
|
615
|
+
resolveAdapterResponse(
|
|
616
|
+
resolve,
|
|
617
|
+
req,
|
|
618
|
+
request.requestURL,
|
|
619
|
+
request.headers,
|
|
620
|
+
{
|
|
621
|
+
status,
|
|
622
|
+
headers,
|
|
623
|
+
body: null,
|
|
624
|
+
isStreamError: true,
|
|
625
|
+
streamErrorCode: streamResult.code,
|
|
626
|
+
errorCause: streamResult.cause
|
|
627
|
+
}
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
activeBufferedResponse = {
|
|
633
|
+
status,
|
|
634
|
+
headers
|
|
635
|
+
};
|
|
636
|
+
const chunks = [];
|
|
637
|
+
let loadedBytes = 0;
|
|
638
|
+
let didFireDownload100 = false;
|
|
639
|
+
const totalBytes = parseInt(String(headers["content-length"] ?? "0"), 10) || 0;
|
|
640
|
+
res.on("data", (chunk) => {
|
|
641
|
+
chunks.push(chunk);
|
|
642
|
+
loadedBytes += chunk.length;
|
|
643
|
+
const progress = totalBytes > 0 ? loadedBytes / totalBytes : -1;
|
|
644
|
+
if (progress === 1) {
|
|
645
|
+
didFireDownload100 = true;
|
|
646
|
+
}
|
|
647
|
+
request.onDownloadProgress?.({
|
|
648
|
+
loaded: loadedBytes,
|
|
649
|
+
// When total is unknown fall back to loaded so the event always
|
|
650
|
+
// has a sensible non-zero total.
|
|
651
|
+
total: totalBytes > 0 ? totalBytes : loadedBytes,
|
|
652
|
+
progress
|
|
653
|
+
});
|
|
654
|
+
});
|
|
655
|
+
res.on("end", () => {
|
|
656
|
+
activeBufferedResponse = void 0;
|
|
657
|
+
if (!didFireDownload100) {
|
|
658
|
+
request.onDownloadProgress?.({
|
|
659
|
+
loaded: loadedBytes,
|
|
660
|
+
total: loadedBytes,
|
|
661
|
+
progress: 1
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
const body = chunks.length > 0 ? new Uint8Array(Buffer.concat(chunks)) : null;
|
|
665
|
+
resolveAdapterResponse(
|
|
666
|
+
resolve,
|
|
667
|
+
req,
|
|
668
|
+
request.requestURL,
|
|
669
|
+
request.headers,
|
|
670
|
+
{
|
|
671
|
+
status,
|
|
672
|
+
headers,
|
|
673
|
+
body
|
|
674
|
+
}
|
|
675
|
+
);
|
|
676
|
+
});
|
|
677
|
+
res.on("error", (err) => {
|
|
678
|
+
if (!activeBufferedResponse) {
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
activeBufferedResponse = void 0;
|
|
682
|
+
resolveAdapterResponse(
|
|
683
|
+
resolve,
|
|
684
|
+
req,
|
|
685
|
+
request.requestURL,
|
|
686
|
+
request.headers,
|
|
687
|
+
{
|
|
688
|
+
status,
|
|
689
|
+
headers,
|
|
690
|
+
body: null,
|
|
691
|
+
isStreamError: true,
|
|
692
|
+
streamErrorCode: "stream_response_error",
|
|
693
|
+
errorCause: makeResponseStreamError(
|
|
694
|
+
"Response stream error",
|
|
695
|
+
err
|
|
696
|
+
)
|
|
697
|
+
}
|
|
698
|
+
);
|
|
699
|
+
});
|
|
700
|
+
res.on("aborted", () => {
|
|
701
|
+
if (!activeBufferedResponse) {
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
activeBufferedResponse = void 0;
|
|
705
|
+
resolveAdapterResponse(
|
|
706
|
+
resolve,
|
|
707
|
+
req,
|
|
708
|
+
request.requestURL,
|
|
709
|
+
request.headers,
|
|
710
|
+
{
|
|
711
|
+
status,
|
|
712
|
+
headers,
|
|
713
|
+
body: null,
|
|
714
|
+
isStreamError: true,
|
|
715
|
+
streamErrorCode: "stream_response_error",
|
|
716
|
+
errorCause: makeResponseStreamError("Response stream aborted")
|
|
717
|
+
}
|
|
718
|
+
);
|
|
719
|
+
});
|
|
720
|
+
res.on("close", () => {
|
|
721
|
+
if (!activeBufferedResponse) {
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
activeBufferedResponse = void 0;
|
|
725
|
+
resolveAdapterResponse(
|
|
726
|
+
resolve,
|
|
727
|
+
req,
|
|
728
|
+
request.requestURL,
|
|
729
|
+
request.headers,
|
|
730
|
+
{
|
|
731
|
+
status,
|
|
732
|
+
headers,
|
|
733
|
+
body: null,
|
|
734
|
+
isStreamError: true,
|
|
735
|
+
streamErrorCode: "stream_response_error",
|
|
736
|
+
errorCause: makeResponseStreamError(
|
|
737
|
+
"Response stream closed before completion"
|
|
738
|
+
)
|
|
739
|
+
}
|
|
740
|
+
);
|
|
741
|
+
});
|
|
742
|
+
})().catch((error) => {
|
|
743
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
744
|
+
});
|
|
745
|
+
});
|
|
746
|
+
req.on("error", (error) => {
|
|
747
|
+
if (request.signal?.aborted) {
|
|
748
|
+
const abortErr = new Error("Request aborted");
|
|
749
|
+
abortErr.name = "AbortError";
|
|
750
|
+
reject(abortErr);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
if (isTLSCertificateError(error)) {
|
|
754
|
+
resolveAdapterResponse(
|
|
755
|
+
resolve,
|
|
756
|
+
req,
|
|
757
|
+
request.requestURL,
|
|
758
|
+
request.headers,
|
|
759
|
+
{
|
|
760
|
+
status: 495,
|
|
761
|
+
isTransportError: true,
|
|
762
|
+
isRetryable: false,
|
|
763
|
+
headers: {},
|
|
764
|
+
body: null,
|
|
765
|
+
errorCause: error
|
|
766
|
+
}
|
|
767
|
+
);
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
const isRetryableTransportError = uploadedBodyBytes === 0;
|
|
771
|
+
resolveAdapterResponse(
|
|
772
|
+
resolve,
|
|
773
|
+
req,
|
|
774
|
+
request.requestURL,
|
|
775
|
+
request.headers,
|
|
776
|
+
{
|
|
777
|
+
status: 0,
|
|
778
|
+
isTransportError: true,
|
|
779
|
+
isRetryable: isRetryableTransportError,
|
|
780
|
+
headers: {},
|
|
781
|
+
body: null,
|
|
782
|
+
errorCause: error
|
|
783
|
+
}
|
|
784
|
+
);
|
|
785
|
+
});
|
|
786
|
+
if (request.signal) {
|
|
787
|
+
if (request.signal.aborted) {
|
|
788
|
+
req.destroy();
|
|
789
|
+
const abortErr = new Error("Request aborted");
|
|
790
|
+
abortErr.name = "AbortError";
|
|
791
|
+
reject(abortErr);
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
request.signal.addEventListener(
|
|
795
|
+
"abort",
|
|
796
|
+
() => {
|
|
797
|
+
if (activeResponseStream) {
|
|
798
|
+
const { status, headers, writable } = activeResponseStream;
|
|
799
|
+
activeResponseStream = void 0;
|
|
800
|
+
destroyWritableQuietly(writable);
|
|
801
|
+
req.destroy();
|
|
802
|
+
const error = new Error(
|
|
803
|
+
"Request aborted during response streaming"
|
|
804
|
+
);
|
|
805
|
+
error.name = "AbortError";
|
|
806
|
+
reject(
|
|
807
|
+
markResponseStreamAbortError(
|
|
808
|
+
error,
|
|
809
|
+
req,
|
|
810
|
+
request.headers,
|
|
811
|
+
status,
|
|
812
|
+
headers
|
|
813
|
+
)
|
|
814
|
+
);
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
if (activeBufferedResponse) {
|
|
818
|
+
const { status, headers } = activeBufferedResponse;
|
|
819
|
+
activeBufferedResponse = void 0;
|
|
820
|
+
req.destroy();
|
|
821
|
+
const error = new Error(
|
|
822
|
+
"Request aborted during response streaming"
|
|
823
|
+
);
|
|
824
|
+
error.name = "AbortError";
|
|
825
|
+
reject(
|
|
826
|
+
markResponseStreamAbortError(
|
|
827
|
+
error,
|
|
828
|
+
req,
|
|
829
|
+
request.headers,
|
|
830
|
+
status,
|
|
831
|
+
headers
|
|
832
|
+
)
|
|
833
|
+
);
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
if (isStreamFactoryPending) {
|
|
837
|
+
req.destroy();
|
|
838
|
+
const abortErr2 = new Error(
|
|
839
|
+
"Request aborted during streamResponse setup"
|
|
840
|
+
);
|
|
841
|
+
abortErr2.name = "AbortError";
|
|
842
|
+
reject(markStreamFactoryError(abortErr2, req, request.headers));
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
req.destroy();
|
|
846
|
+
const abortErr = new Error("Request aborted");
|
|
847
|
+
abortErr.name = "AbortError";
|
|
848
|
+
reject(abortErr);
|
|
849
|
+
},
|
|
850
|
+
{ once: true }
|
|
851
|
+
);
|
|
852
|
+
}
|
|
853
|
+
if (request.body instanceof FormData) {
|
|
854
|
+
const boundary = generateMultipartBoundary();
|
|
855
|
+
serializeMultipartFormData(
|
|
856
|
+
request.body,
|
|
857
|
+
req,
|
|
858
|
+
boundary,
|
|
859
|
+
reportUploadProgress
|
|
860
|
+
).then(() => {
|
|
861
|
+
req.end();
|
|
862
|
+
}).catch((error) => {
|
|
863
|
+
req.destroy();
|
|
864
|
+
resolveAdapterResponse(
|
|
865
|
+
resolve,
|
|
866
|
+
req,
|
|
867
|
+
request.requestURL,
|
|
868
|
+
request.headers,
|
|
869
|
+
{
|
|
870
|
+
status: 0,
|
|
871
|
+
isTransportError: true,
|
|
872
|
+
isRetryable: false,
|
|
873
|
+
headers: {},
|
|
874
|
+
body: null,
|
|
875
|
+
errorCause: error instanceof Error ? error : new Error(String(error))
|
|
876
|
+
}
|
|
877
|
+
);
|
|
878
|
+
});
|
|
879
|
+
} else if (typeof request.body === "string" || request.body instanceof Uint8Array) {
|
|
880
|
+
const bytes = typeof request.body === "string" ? Buffer.from(request.body, "utf8") : Buffer.from(request.body);
|
|
881
|
+
req.setHeader("Content-Length", bytes.length.toString());
|
|
882
|
+
writeRequestBodyChunked(bytes, req, reportUploadProgress).then(() => {
|
|
883
|
+
req.end();
|
|
884
|
+
}).catch((error) => {
|
|
885
|
+
req.destroy();
|
|
886
|
+
resolveAdapterResponse(
|
|
887
|
+
resolve,
|
|
888
|
+
req,
|
|
889
|
+
request.requestURL,
|
|
890
|
+
request.headers,
|
|
891
|
+
{
|
|
892
|
+
status: 0,
|
|
893
|
+
isTransportError: true,
|
|
894
|
+
isRetryable: false,
|
|
895
|
+
headers: {},
|
|
896
|
+
body: null,
|
|
897
|
+
errorCause: error instanceof Error ? error : new Error(String(error))
|
|
898
|
+
}
|
|
899
|
+
);
|
|
900
|
+
});
|
|
901
|
+
} else {
|
|
902
|
+
reportUploadProgress({ loaded: 0, total: 0, progress: 1 });
|
|
903
|
+
req.end();
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
};
|
|
908
|
+
async function streamResponseBody(res, writable, totalBytes, onProgress) {
|
|
909
|
+
return new Promise((resolve) => {
|
|
910
|
+
let loadedBytes = 0;
|
|
911
|
+
let didFireDownload100 = false;
|
|
912
|
+
let isPaused = false;
|
|
913
|
+
let isSettled = false;
|
|
914
|
+
let didReceiveEnd = false;
|
|
915
|
+
const cleanup = () => {
|
|
916
|
+
removeWritableListener(writable, "drain", onWritableDrain);
|
|
917
|
+
removeWritableListener(writable, "error", onWritableError);
|
|
918
|
+
res.off("data", onResponseData);
|
|
919
|
+
res.off("end", onResponseEnd);
|
|
920
|
+
res.off("error", onResponseError);
|
|
921
|
+
res.off("aborted", onResponseAborted);
|
|
922
|
+
res.off("close", onResponseClose);
|
|
923
|
+
};
|
|
924
|
+
const settle = (result) => {
|
|
925
|
+
if (isSettled) {
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
isSettled = true;
|
|
929
|
+
cleanup();
|
|
930
|
+
resolve(result);
|
|
931
|
+
};
|
|
932
|
+
const onWritableDrain = () => {
|
|
933
|
+
if (isPaused) {
|
|
934
|
+
isPaused = false;
|
|
935
|
+
res.resume();
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
const onWritableError = (error) => {
|
|
939
|
+
settle({ code: "stream_write_error", cause: error });
|
|
940
|
+
};
|
|
941
|
+
const onResponseData = (chunk) => {
|
|
942
|
+
if (isSettled) {
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
let canContinue;
|
|
946
|
+
try {
|
|
947
|
+
canContinue = writable.write(chunk, (error) => {
|
|
948
|
+
if (error || isSettled) {
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
loadedBytes += chunk.length;
|
|
952
|
+
const progress = totalBytes > 0 ? loadedBytes / totalBytes : -1;
|
|
953
|
+
if (progress === 1) {
|
|
954
|
+
didFireDownload100 = true;
|
|
955
|
+
}
|
|
956
|
+
onProgress?.({
|
|
957
|
+
loaded: loadedBytes,
|
|
958
|
+
// Fall back to loaded when total is unknown so the event always
|
|
959
|
+
// has a sensible non-zero total.
|
|
960
|
+
total: totalBytes > 0 ? totalBytes : loadedBytes,
|
|
961
|
+
progress
|
|
962
|
+
});
|
|
963
|
+
});
|
|
964
|
+
} catch (error) {
|
|
965
|
+
settle({
|
|
966
|
+
code: "stream_write_error",
|
|
967
|
+
cause: error instanceof Error ? error : new Error(String(error))
|
|
968
|
+
});
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
if (!canContinue) {
|
|
972
|
+
isPaused = true;
|
|
973
|
+
res.pause();
|
|
974
|
+
}
|
|
975
|
+
};
|
|
976
|
+
const onResponseEnd = () => {
|
|
977
|
+
if (isSettled) {
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
didReceiveEnd = true;
|
|
981
|
+
try {
|
|
982
|
+
writable.end(() => {
|
|
983
|
+
if (isSettled) {
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
if (!didFireDownload100) {
|
|
987
|
+
onProgress?.({
|
|
988
|
+
loaded: loadedBytes,
|
|
989
|
+
total: loadedBytes,
|
|
990
|
+
progress: 1
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
settle(true);
|
|
994
|
+
});
|
|
995
|
+
} catch (error) {
|
|
996
|
+
settle({
|
|
997
|
+
code: "stream_write_error",
|
|
998
|
+
cause: error instanceof Error ? error : new Error(String(error))
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
};
|
|
1002
|
+
const onResponseError = (err) => {
|
|
1003
|
+
const streamError = new Error("Response stream error");
|
|
1004
|
+
streamError.cause = err;
|
|
1005
|
+
settle({ code: "stream_response_error", cause: streamError });
|
|
1006
|
+
};
|
|
1007
|
+
const onResponseAborted = () => {
|
|
1008
|
+
const streamError = new Error("Response stream aborted");
|
|
1009
|
+
settle({ code: "stream_response_error", cause: streamError });
|
|
1010
|
+
};
|
|
1011
|
+
const onResponseClose = () => {
|
|
1012
|
+
if (didReceiveEnd || isSettled) {
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
const streamError = new Error("Response stream closed before completion");
|
|
1016
|
+
settle({ code: "stream_response_error", cause: streamError });
|
|
1017
|
+
};
|
|
1018
|
+
writable.on("drain", onWritableDrain);
|
|
1019
|
+
writable.on("error", onWritableError);
|
|
1020
|
+
res.on("data", onResponseData);
|
|
1021
|
+
res.on("end", onResponseEnd);
|
|
1022
|
+
res.on("error", onResponseError);
|
|
1023
|
+
res.on("aborted", onResponseAborted);
|
|
1024
|
+
res.on("close", onResponseClose);
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
function normalizeResponseHeaders(headers) {
|
|
1028
|
+
const result = {};
|
|
1029
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
1030
|
+
if (value === void 0) {
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
1033
|
+
result[key] = Array.isArray(value) ? value : String(value);
|
|
1034
|
+
}
|
|
1035
|
+
return result;
|
|
1036
|
+
}
|
|
1037
|
+
function snapshotEffectiveRequestHeaders(req, fallbackHeaders) {
|
|
1038
|
+
return normalizeNodeRequestHeaders({
|
|
1039
|
+
// Start with the client-level attempt headers, then overlay any adapter-side
|
|
1040
|
+
// mutations (for example multipart Content-Type/Content-Length).
|
|
1041
|
+
...fallbackHeaders,
|
|
1042
|
+
...req.getHeaders()
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
function resolveAdapterResponse(resolve, req, requestURL, fallbackHeaders, response) {
|
|
1046
|
+
const detectedRedirectURL = resolveDetectedRedirectURL(
|
|
1047
|
+
requestURL,
|
|
1048
|
+
response.status,
|
|
1049
|
+
response.headers
|
|
1050
|
+
);
|
|
1051
|
+
resolve({
|
|
1052
|
+
...response,
|
|
1053
|
+
// Flag redirect responses so HTTPClient can surface wasRedirectDetected on
|
|
1054
|
+
// the final HTTPResponse consistently across all adapters. The actual
|
|
1055
|
+
// follow-or-disable decision is still made by HTTPClient's redirect loop.
|
|
1056
|
+
wasRedirectDetected: REDIRECT_STATUS_CODES.has(response.status),
|
|
1057
|
+
...detectedRedirectURL ? { detectedRedirectURL } : {},
|
|
1058
|
+
effectiveRequestHeaders: snapshotEffectiveRequestHeaders(
|
|
1059
|
+
req,
|
|
1060
|
+
fallbackHeaders
|
|
1061
|
+
)
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
function destroyWritableQuietly(writable) {
|
|
1065
|
+
try {
|
|
1066
|
+
writable.destroy();
|
|
1067
|
+
} catch {
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
function removeWritableListener(writable, event, listener) {
|
|
1071
|
+
const removable = writable;
|
|
1072
|
+
removable.off?.(event, listener);
|
|
1073
|
+
}
|
|
1074
|
+
function makeResponseStreamError(message, cause) {
|
|
1075
|
+
const error = new Error(message);
|
|
1076
|
+
if (cause) {
|
|
1077
|
+
error.cause = cause;
|
|
1078
|
+
}
|
|
1079
|
+
return error;
|
|
1080
|
+
}
|
|
1081
|
+
function markStreamFactoryError(error, req, fallbackHeaders) {
|
|
1082
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
1083
|
+
const tagged = normalized;
|
|
1084
|
+
tagged[NON_RETRYABLE_HTTP_CLIENT_CALLBACK_ERROR_FLAG] = true;
|
|
1085
|
+
Object.assign(normalized, { [STREAM_FACTORY_ERROR_FLAG]: true });
|
|
1086
|
+
Object.assign(normalized, {
|
|
1087
|
+
effectiveRequestHeaders: snapshotEffectiveRequestHeaders(
|
|
1088
|
+
req,
|
|
1089
|
+
fallbackHeaders
|
|
1090
|
+
)
|
|
1091
|
+
});
|
|
1092
|
+
return normalized;
|
|
1093
|
+
}
|
|
1094
|
+
function markResponseStreamAbortError(error, req, fallbackHeaders, status, headers) {
|
|
1095
|
+
const tagged = error;
|
|
1096
|
+
Object.assign(tagged, { [RESPONSE_STREAM_ABORT_FLAG]: true });
|
|
1097
|
+
tagged.effectiveRequestHeaders = snapshotEffectiveRequestHeaders(
|
|
1098
|
+
req,
|
|
1099
|
+
fallbackHeaders
|
|
1100
|
+
);
|
|
1101
|
+
tagged.streamAbortStatus = status;
|
|
1102
|
+
tagged.streamAbortHeaders = headers;
|
|
1103
|
+
return error;
|
|
1104
|
+
}
|
|
1105
|
+
function isStreamResponseCancel(value) {
|
|
1106
|
+
return value !== null && typeof value === "object" && value.cancel === true;
|
|
1107
|
+
}
|
|
1108
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1109
|
+
0 && (module.exports = {
|
|
1110
|
+
NodeAdapter
|
|
1111
|
+
});
|
|
1112
|
+
//# sourceMappingURL=index.cjs.map
|