lifecycleion 0.0.9 → 0.0.11

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