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.
Files changed (48) hide show
  1. package/README.md +45 -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/http-client/index.cjs +5254 -0
  9. package/dist/lib/http-client/index.cjs.map +1 -0
  10. package/dist/lib/http-client/index.d.cts +372 -0
  11. package/dist/lib/http-client/index.d.ts +372 -0
  12. package/dist/lib/http-client/index.js +5207 -0
  13. package/dist/lib/http-client/index.js.map +1 -0
  14. package/dist/lib/http-client-mock/index.cjs +525 -0
  15. package/dist/lib/http-client-mock/index.cjs.map +1 -0
  16. package/dist/lib/http-client-mock/index.d.cts +129 -0
  17. package/dist/lib/http-client-mock/index.d.ts +129 -0
  18. package/dist/lib/http-client-mock/index.js +488 -0
  19. package/dist/lib/http-client-mock/index.js.map +1 -0
  20. package/dist/lib/http-client-node/index.cjs +1112 -0
  21. package/dist/lib/http-client-node/index.cjs.map +1 -0
  22. package/dist/lib/http-client-node/index.d.cts +43 -0
  23. package/dist/lib/http-client-node/index.d.ts +43 -0
  24. package/dist/lib/http-client-node/index.js +1075 -0
  25. package/dist/lib/http-client-node/index.js.map +1 -0
  26. package/dist/lib/http-client-xhr/index.cjs +323 -0
  27. package/dist/lib/http-client-xhr/index.cjs.map +1 -0
  28. package/dist/lib/http-client-xhr/index.d.cts +23 -0
  29. package/dist/lib/http-client-xhr/index.d.ts +23 -0
  30. package/dist/lib/http-client-xhr/index.js +286 -0
  31. package/dist/lib/http-client-xhr/index.js.map +1 -0
  32. package/dist/lib/lifecycle-manager/index.cjs +118 -61
  33. package/dist/lib/lifecycle-manager/index.cjs.map +1 -1
  34. package/dist/lib/lifecycle-manager/index.js +118 -61
  35. package/dist/lib/lifecycle-manager/index.js.map +1 -1
  36. package/dist/lib/lru-cache/index.cjs +274 -0
  37. package/dist/lib/lru-cache/index.cjs.map +1 -0
  38. package/dist/lib/lru-cache/index.d.cts +84 -0
  39. package/dist/lib/lru-cache/index.d.ts +84 -0
  40. package/dist/lib/lru-cache/index.js +249 -0
  41. package/dist/lib/lru-cache/index.js.map +1 -0
  42. package/dist/lib/retry-utils/index.d.cts +3 -23
  43. package/dist/lib/retry-utils/index.d.ts +3 -23
  44. package/dist/types-CUPvmYQ8.d.cts +868 -0
  45. package/dist/types-D_MywcG0.d.cts +23 -0
  46. package/dist/types-D_MywcG0.d.ts +23 -0
  47. package/dist/types-Hw2PUTIT.d.ts +868 -0
  48. 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