@socketsecurity/lib 5.5.2 → 5.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/dist/dlx/binary.d.ts +27 -52
- package/dist/dlx/binary.js +24 -45
- package/dist/dlx/manifest.d.ts +11 -5
- package/dist/external/libnpmexec.js +2 -2
- package/dist/http-request.d.ts +79 -63
- package/dist/http-request.js +203 -159
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,43 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [5.6.0](https://github.com/SocketDev/socket-lib/releases/tag/v5.6.0) - 2026-02-08
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **http-request**: Added automatic default headers for JSON and text requests
|
|
13
|
+
- `httpJson()` now automatically sets `Accept: application/json` header
|
|
14
|
+
- `httpJson()` automatically sets `Content-Type: application/json` when body is present
|
|
15
|
+
- `httpText()` now automatically sets `Accept: text/plain` header
|
|
16
|
+
- `httpText()` automatically sets `Content-Type: text/plain` when body is present
|
|
17
|
+
- User-provided headers always override defaults
|
|
18
|
+
- Simplifies API usage - no need to manually set common headers
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- **http-request**: Renamed HTTP helper functions to support all HTTP methods (BREAKING CHANGE)
|
|
23
|
+
- `httpGetJson()` → `httpJson()` - Now supports GET, POST, PUT, DELETE, PATCH, etc.
|
|
24
|
+
- `httpGetText()` → `httpText()` - Now supports all HTTP methods via `method` option
|
|
25
|
+
- Functions now accept `method` parameter in options (defaults to 'GET')
|
|
26
|
+
- More flexible API that matches modern fetch-style conventions
|
|
27
|
+
- **Migration**: Replace `httpGetJson()` calls with `httpJson()` and `httpGetText()` with `httpText()`
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- **http-request**: Fixed Content-Type header incorrectly sent with empty string body
|
|
32
|
+
- Empty string body (`""`) no longer triggers Content-Type header
|
|
33
|
+
- Changed condition from `if (body !== undefined)` to `if (body)` for semantic correctness
|
|
34
|
+
- Empty string represents "no content" and should not declare a Content-Type
|
|
35
|
+
- Affects `httpJson()` and `httpText()` functions
|
|
36
|
+
- Fixes potential API compatibility issues with servers expecting no Content-Type for empty bodies
|
|
37
|
+
- Added comprehensive test coverage for empty string edge case
|
|
38
|
+
|
|
39
|
+
## [5.5.3](https://github.com/SocketDev/socket-lib/releases/tag/v5.5.3) - 2026-01-20
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- **deps**: Added patch for execa@2.1.0 to fix signal-exit v4 compatibility. The package was using default import syntax with signal-exit v4, which now exports onExit as a named export.
|
|
44
|
+
|
|
8
45
|
## [5.5.2](https://github.com/SocketDev/socket-lib/releases/tag/v5.5.2) - 2026-01-20
|
|
9
46
|
|
|
10
47
|
### Changed
|
package/dist/dlx/binary.d.ts
CHANGED
|
@@ -10,9 +10,9 @@ export interface DlxBinaryOptions {
|
|
|
10
10
|
*/
|
|
11
11
|
name?: string | undefined;
|
|
12
12
|
/**
|
|
13
|
-
* Expected
|
|
13
|
+
* Expected SRI integrity hash (sha512-<base64>) for verification.
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
integrity?: string | undefined;
|
|
16
16
|
/**
|
|
17
17
|
* Cache TTL in milliseconds (default: 7 days).
|
|
18
18
|
*/
|
|
@@ -47,65 +47,40 @@ export interface DlxBinaryResult {
|
|
|
47
47
|
}
|
|
48
48
|
/**
|
|
49
49
|
* Metadata structure for cached binaries (.dlx-metadata.json).
|
|
50
|
-
* Unified schema shared across TypeScript (dlxBinary) and C++
|
|
50
|
+
* Unified schema shared across TypeScript (dlxBinary) and C++ stub extractor.
|
|
51
51
|
*
|
|
52
|
-
*
|
|
52
|
+
* Fields:
|
|
53
53
|
* - version: Schema version (currently "1.0.0")
|
|
54
54
|
* - cache_key: First 16 chars of SHA-512 hash (matches directory name)
|
|
55
55
|
* - timestamp: Unix timestamp in milliseconds
|
|
56
|
-
* -
|
|
57
|
-
* - checksum_algorithm: "sha512" or "sha256"
|
|
58
|
-
* - platform: "darwin" | "linux" | "win32"
|
|
59
|
-
* - arch: "x64" | "arm64"
|
|
56
|
+
* - integrity: SRI hash (sha512-<base64>, aligned with npm)
|
|
60
57
|
* - size: Size of cached binary in bytes
|
|
61
58
|
* - source: Origin information
|
|
62
|
-
* - type: "download"
|
|
59
|
+
* - type: "download" | "extract" | "package"
|
|
63
60
|
* - url: Download URL (if type is "download")
|
|
64
|
-
* - path: Source binary path (if type is "
|
|
61
|
+
* - path: Source binary path (if type is "extract")
|
|
62
|
+
* - spec: Package spec (if type is "package")
|
|
63
|
+
* - update_check: Update checking metadata (optional)
|
|
64
|
+
* - last_check: Timestamp of last update check
|
|
65
|
+
* - last_notification: Timestamp of last user notification
|
|
66
|
+
* - latest_known: Latest known version string
|
|
65
67
|
*
|
|
66
|
-
*
|
|
67
|
-
* - For C++ decompression:
|
|
68
|
-
* - compressed_size: Size of compressed data in bytes
|
|
69
|
-
* - compression_algorithm: Brotli level (numeric)
|
|
70
|
-
* - compression_ratio: original_size / compressed_size
|
|
71
|
-
*
|
|
72
|
-
* Example (TypeScript download):
|
|
68
|
+
* Example:
|
|
73
69
|
* ```json
|
|
74
70
|
* {
|
|
75
71
|
* "version": "1.0.0",
|
|
76
72
|
* "cache_key": "a1b2c3d4e5f67890",
|
|
77
73
|
* "timestamp": 1730332800000,
|
|
78
|
-
* "
|
|
79
|
-
* "checksum_algorithm": "sha256",
|
|
80
|
-
* "platform": "darwin",
|
|
81
|
-
* "arch": "arm64",
|
|
74
|
+
* "integrity": "sha512-abc123base64...",
|
|
82
75
|
* "size": 15000000,
|
|
83
76
|
* "source": {
|
|
84
77
|
* "type": "download",
|
|
85
78
|
* "url": "https://example.com/binary"
|
|
86
|
-
* }
|
|
87
|
-
* }
|
|
88
|
-
* ```
|
|
89
|
-
*
|
|
90
|
-
* Example (C++ decompression):
|
|
91
|
-
* ```json
|
|
92
|
-
* {
|
|
93
|
-
* "version": "1.0.0",
|
|
94
|
-
* "cache_key": "0123456789abcdef",
|
|
95
|
-
* "timestamp": 1730332800000,
|
|
96
|
-
* "checksum": "sha512-def456...",
|
|
97
|
-
* "checksum_algorithm": "sha512",
|
|
98
|
-
* "platform": "darwin",
|
|
99
|
-
* "arch": "arm64",
|
|
100
|
-
* "size": 13000000,
|
|
101
|
-
* "source": {
|
|
102
|
-
* "type": "decompression",
|
|
103
|
-
* "path": "/usr/local/bin/socket"
|
|
104
79
|
* },
|
|
105
|
-
* "
|
|
106
|
-
* "
|
|
107
|
-
* "
|
|
108
|
-
* "
|
|
80
|
+
* "update_check": {
|
|
81
|
+
* "last_check": 1730332800000,
|
|
82
|
+
* "last_notification": 1730246400000,
|
|
83
|
+
* "latest_known": "2.1.0"
|
|
109
84
|
* }
|
|
110
85
|
* }
|
|
111
86
|
* ```
|
|
@@ -116,17 +91,19 @@ export interface DlxMetadata {
|
|
|
116
91
|
version: string;
|
|
117
92
|
cache_key: string;
|
|
118
93
|
timestamp: number;
|
|
119
|
-
|
|
120
|
-
checksum_algorithm: string;
|
|
121
|
-
platform: string;
|
|
122
|
-
arch: string;
|
|
94
|
+
integrity: string;
|
|
123
95
|
size: number;
|
|
124
96
|
source?: {
|
|
125
|
-
type: 'download' | '
|
|
97
|
+
type: 'download' | 'extract' | 'package';
|
|
126
98
|
url?: string;
|
|
127
99
|
path?: string;
|
|
100
|
+
spec?: string;
|
|
101
|
+
};
|
|
102
|
+
update_check?: {
|
|
103
|
+
last_check: number;
|
|
104
|
+
last_notification: number;
|
|
105
|
+
latest_known: string;
|
|
128
106
|
};
|
|
129
|
-
extra?: Record<string, unknown>;
|
|
130
107
|
}
|
|
131
108
|
/**
|
|
132
109
|
* Clean expired entries from the DLX cache.
|
|
@@ -169,10 +146,8 @@ export declare function getDlxCachePath(): string;
|
|
|
169
146
|
*/
|
|
170
147
|
export declare function listDlxCache(): Promise<Array<{
|
|
171
148
|
age: number;
|
|
172
|
-
|
|
173
|
-
checksum: string;
|
|
149
|
+
integrity: string;
|
|
174
150
|
name: string;
|
|
175
|
-
platform: string;
|
|
176
151
|
size: number;
|
|
177
152
|
url: string;
|
|
178
153
|
}>>;
|
package/dist/dlx/binary.js
CHANGED
|
@@ -30,7 +30,6 @@ module.exports = __toCommonJS(binary_exports);
|
|
|
30
30
|
var import_platform = require("../constants/platform");
|
|
31
31
|
var import_time = require("../constants/time");
|
|
32
32
|
var import_cache = require("./cache");
|
|
33
|
-
var import_manifest = require("./manifest");
|
|
34
33
|
var import_http_request = require("../http-request");
|
|
35
34
|
var import_fs = require("../fs");
|
|
36
35
|
var import_objects = require("../objects");
|
|
@@ -87,7 +86,7 @@ async function isCacheValid(cacheEntryPath, cacheTtl) {
|
|
|
87
86
|
return false;
|
|
88
87
|
}
|
|
89
88
|
}
|
|
90
|
-
async function downloadBinaryFile(url, destPath,
|
|
89
|
+
async function downloadBinaryFile(url, destPath, integrity) {
|
|
91
90
|
const crypto = /* @__PURE__ */ getCrypto();
|
|
92
91
|
const fs = /* @__PURE__ */ getFs();
|
|
93
92
|
const path = /* @__PURE__ */ getPath();
|
|
@@ -100,9 +99,8 @@ async function downloadBinaryFile(url, destPath, checksum) {
|
|
|
100
99
|
const stats = await fs.promises.stat(destPath);
|
|
101
100
|
if (stats.size > 0) {
|
|
102
101
|
const fileBuffer2 = await fs.promises.readFile(destPath);
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
return hasher2.digest("hex");
|
|
102
|
+
const hash2 = crypto.createHash("sha512").update(fileBuffer2).digest("base64");
|
|
103
|
+
return `sha512-${hash2}`;
|
|
106
104
|
}
|
|
107
105
|
}
|
|
108
106
|
try {
|
|
@@ -116,19 +114,18 @@ Check your internet connection or verify the URL is accessible.`,
|
|
|
116
114
|
);
|
|
117
115
|
}
|
|
118
116
|
const fileBuffer = await fs.promises.readFile(destPath);
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (checksum && actualChecksum !== checksum) {
|
|
117
|
+
const hash = crypto.createHash("sha512").update(fileBuffer).digest("base64");
|
|
118
|
+
const actualIntegrity = `sha512-${hash}`;
|
|
119
|
+
if (integrity && actualIntegrity !== integrity) {
|
|
123
120
|
await (0, import_fs.safeDelete)(destPath);
|
|
124
121
|
throw new Error(
|
|
125
|
-
`
|
|
122
|
+
`Integrity mismatch: expected ${integrity}, got ${actualIntegrity}`
|
|
126
123
|
);
|
|
127
124
|
}
|
|
128
125
|
if (!import_platform.WIN32) {
|
|
129
126
|
await fs.promises.chmod(destPath, 493);
|
|
130
127
|
}
|
|
131
|
-
return
|
|
128
|
+
return actualIntegrity;
|
|
132
129
|
},
|
|
133
130
|
{
|
|
134
131
|
// Align with npm npx locking strategy.
|
|
@@ -137,16 +134,13 @@ Check your internet connection or verify the URL is accessible.`,
|
|
|
137
134
|
}
|
|
138
135
|
);
|
|
139
136
|
}
|
|
140
|
-
async function writeMetadata(cacheEntryPath, cacheKey, url,
|
|
137
|
+
async function writeMetadata(cacheEntryPath, cacheKey, url, integrity, size) {
|
|
141
138
|
const metaPath = getMetadataPath(cacheEntryPath);
|
|
142
139
|
const metadata = {
|
|
143
140
|
version: "1.0.0",
|
|
144
141
|
cache_key: cacheKey,
|
|
145
142
|
timestamp: Date.now(),
|
|
146
|
-
|
|
147
|
-
checksum_algorithm: "sha256",
|
|
148
|
-
platform: (0, import_platform.getPlatform)(),
|
|
149
|
-
arch: (0, import_platform.getArch)(),
|
|
143
|
+
integrity,
|
|
150
144
|
size,
|
|
151
145
|
source: {
|
|
152
146
|
type: "download",
|
|
@@ -155,21 +149,6 @@ async function writeMetadata(cacheEntryPath, cacheKey, url, binaryName, checksum
|
|
|
155
149
|
};
|
|
156
150
|
const fs = /* @__PURE__ */ getFs();
|
|
157
151
|
await fs.promises.writeFile(metaPath, JSON.stringify(metadata, null, 2));
|
|
158
|
-
try {
|
|
159
|
-
const spec = `${url}:${binaryName}`;
|
|
160
|
-
await import_manifest.dlxManifest.setBinaryEntry(spec, cacheKey, {
|
|
161
|
-
checksum,
|
|
162
|
-
checksum_algorithm: metadata.checksum_algorithm,
|
|
163
|
-
platform: metadata.platform,
|
|
164
|
-
arch: metadata.arch,
|
|
165
|
-
size,
|
|
166
|
-
source: {
|
|
167
|
-
type: "download",
|
|
168
|
-
url
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
} catch {
|
|
172
|
-
}
|
|
173
152
|
}
|
|
174
153
|
async function cleanDlxCache(maxAge = import_time.DLX_BINARY_CACHE_TTL) {
|
|
175
154
|
const cacheDir = getDlxCachePath();
|
|
@@ -214,8 +193,8 @@ async function cleanDlxCache(maxAge = import_time.DLX_BINARY_CACHE_TTL) {
|
|
|
214
193
|
async function dlxBinary(args, options, spawnExtra) {
|
|
215
194
|
const {
|
|
216
195
|
cacheTtl = import_time.DLX_BINARY_CACHE_TTL,
|
|
217
|
-
checksum,
|
|
218
196
|
force: userForce = false,
|
|
197
|
+
integrity,
|
|
219
198
|
name,
|
|
220
199
|
spawnOptions,
|
|
221
200
|
url,
|
|
@@ -231,13 +210,13 @@ async function dlxBinary(args, options, spawnExtra) {
|
|
|
231
210
|
const cacheEntryDir = path.join(cacheDir, cacheKey);
|
|
232
211
|
const binaryPath = (0, import_normalize.normalizePath)(path.join(cacheEntryDir, binaryName));
|
|
233
212
|
let downloaded = false;
|
|
234
|
-
let
|
|
213
|
+
let computedIntegrity = integrity;
|
|
235
214
|
if (!force && fs.existsSync(cacheEntryDir) && await isCacheValid(cacheEntryDir, cacheTtl)) {
|
|
236
215
|
try {
|
|
237
216
|
const metaPath = getMetadataPath(cacheEntryDir);
|
|
238
217
|
const metadata = await (0, import_fs.readJson)(metaPath, { throws: false });
|
|
239
|
-
if (metadata && typeof metadata === "object" && !Array.isArray(metadata) && typeof metadata["
|
|
240
|
-
|
|
218
|
+
if (metadata && typeof metadata === "object" && !Array.isArray(metadata) && typeof metadata["integrity"] === "string") {
|
|
219
|
+
computedIntegrity = metadata["integrity"];
|
|
241
220
|
} else {
|
|
242
221
|
downloaded = true;
|
|
243
222
|
}
|
|
@@ -271,14 +250,13 @@ Ensure the filesystem is writable or set SOCKET_DLX_DIR to a writable location.`
|
|
|
271
250
|
{ cause: e }
|
|
272
251
|
);
|
|
273
252
|
}
|
|
274
|
-
|
|
253
|
+
computedIntegrity = await downloadBinaryFile(url, binaryPath, integrity);
|
|
275
254
|
const stats = await fs.promises.stat(binaryPath);
|
|
276
255
|
await writeMetadata(
|
|
277
256
|
cacheEntryDir,
|
|
278
257
|
cacheKey,
|
|
279
258
|
url,
|
|
280
|
-
|
|
281
|
-
computedChecksum || "",
|
|
259
|
+
computedIntegrity || "",
|
|
282
260
|
stats.size
|
|
283
261
|
);
|
|
284
262
|
}
|
|
@@ -301,8 +279,8 @@ Ensure the filesystem is writable or set SOCKET_DLX_DIR to a writable location.`
|
|
|
301
279
|
async function downloadBinary(options) {
|
|
302
280
|
const {
|
|
303
281
|
cacheTtl = import_time.DLX_BINARY_CACHE_TTL,
|
|
304
|
-
checksum,
|
|
305
282
|
force = false,
|
|
283
|
+
integrity,
|
|
306
284
|
name,
|
|
307
285
|
url
|
|
308
286
|
} = { __proto__: null, ...options };
|
|
@@ -341,14 +319,17 @@ Ensure the filesystem is writable or set SOCKET_DLX_DIR to a writable location.`
|
|
|
341
319
|
{ cause: e }
|
|
342
320
|
);
|
|
343
321
|
}
|
|
344
|
-
const
|
|
322
|
+
const computedIntegrity = await downloadBinaryFile(
|
|
323
|
+
url,
|
|
324
|
+
binaryPath,
|
|
325
|
+
integrity
|
|
326
|
+
);
|
|
345
327
|
const stats = await fs.promises.stat(binaryPath);
|
|
346
328
|
await writeMetadata(
|
|
347
329
|
cacheEntryDir,
|
|
348
330
|
cacheKey,
|
|
349
331
|
url,
|
|
350
|
-
|
|
351
|
-
computedChecksum || "",
|
|
332
|
+
computedIntegrity || "",
|
|
352
333
|
stats.size
|
|
353
334
|
);
|
|
354
335
|
downloaded = true;
|
|
@@ -406,10 +387,8 @@ async function listDlxCache() {
|
|
|
406
387
|
const binaryStats = await fs.promises.stat(binaryPath);
|
|
407
388
|
results.push({
|
|
408
389
|
age: now - (metaObj["timestamp"] || 0),
|
|
409
|
-
|
|
410
|
-
checksum: metaObj["checksum"] || "",
|
|
390
|
+
integrity: metaObj["integrity"] || "",
|
|
411
391
|
name: binaryFile,
|
|
412
|
-
platform: metaObj["platform"] || "unknown",
|
|
413
392
|
size: binaryStats.size,
|
|
414
393
|
url
|
|
415
394
|
});
|
package/dist/dlx/manifest.d.ts
CHANGED
|
@@ -14,17 +14,23 @@ export interface PackageDetails {
|
|
|
14
14
|
* Details for binary download entries.
|
|
15
15
|
*/
|
|
16
16
|
export interface BinaryDetails {
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
/** SRI integrity hash (sha512-<base64>, aligned with npm). */
|
|
18
|
+
integrity: string;
|
|
19
19
|
platform: string;
|
|
20
20
|
arch: string;
|
|
21
21
|
size: number;
|
|
22
22
|
source: {
|
|
23
|
-
type: 'download';
|
|
24
|
-
url
|
|
23
|
+
type: 'download' | 'extract';
|
|
24
|
+
url?: string;
|
|
25
|
+
path?: string;
|
|
26
|
+
};
|
|
27
|
+
/** Update check metadata (same structure as packages). */
|
|
28
|
+
update_check?: {
|
|
29
|
+
last_check: number;
|
|
30
|
+
last_notification: number;
|
|
31
|
+
latest_known: string;
|
|
25
32
|
};
|
|
26
33
|
}
|
|
27
|
-
export type ChecksumAlgorithm = 'sha256' | 'sha512';
|
|
28
34
|
/**
|
|
29
35
|
* Unified manifest entry for all cached items (packages and binaries).
|
|
30
36
|
* Shared fields at root, type-specific fields in details.
|
|
@@ -11,9 +11,9 @@ var __commonJS = (cb, mod) => function __require() {
|
|
|
11
11
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
// node_modules/.pnpm/libnpmexec@10.
|
|
14
|
+
// node_modules/.pnpm/libnpmexec@10.2.0_supports-color@10.0.0/node_modules/libnpmexec/lib/get-bin-from-manifest.js
|
|
15
15
|
var require_get_bin_from_manifest = __commonJS({
|
|
16
|
-
"node_modules/.pnpm/libnpmexec@10.
|
|
16
|
+
"node_modules/.pnpm/libnpmexec@10.2.0_supports-color@10.0.0/node_modules/libnpmexec/lib/get-bin-from-manifest.js"(exports2, module2) {
|
|
17
17
|
var getBinFromManifest2 = /* @__PURE__ */ __name((mani) => {
|
|
18
18
|
const bin = mani.bin || {};
|
|
19
19
|
if (new Set(Object.values(bin)).size === 1) {
|
package/dist/http-request.d.ts
CHANGED
|
@@ -435,46 +435,6 @@ export interface HttpDownloadResult {
|
|
|
435
435
|
*/
|
|
436
436
|
size: number;
|
|
437
437
|
}
|
|
438
|
-
/**
|
|
439
|
-
* Make an HTTP/HTTPS request with retry logic and redirect support.
|
|
440
|
-
* Provides a fetch-like API using Node.js native http/https modules.
|
|
441
|
-
*
|
|
442
|
-
* This is the main entry point for making HTTP requests. It handles retries,
|
|
443
|
-
* redirects, timeouts, and provides a fetch-compatible response interface.
|
|
444
|
-
*
|
|
445
|
-
* @param url - The URL to request (must start with http:// or https://)
|
|
446
|
-
* @param options - Request configuration options
|
|
447
|
-
* @returns Promise resolving to response object with `.json()`, `.text()`, etc.
|
|
448
|
-
* @throws {Error} When all retries are exhausted, timeout occurs, or non-retryable error happens
|
|
449
|
-
*
|
|
450
|
-
* @example
|
|
451
|
-
* ```ts
|
|
452
|
-
* // Simple GET request
|
|
453
|
-
* const response = await httpRequest('https://api.example.com/data')
|
|
454
|
-
* const data = response.json()
|
|
455
|
-
*
|
|
456
|
-
* // POST with JSON body
|
|
457
|
-
* const response = await httpRequest('https://api.example.com/users', {
|
|
458
|
-
* method: 'POST',
|
|
459
|
-
* headers: { 'Content-Type': 'application/json' },
|
|
460
|
-
* body: JSON.stringify({ name: 'Alice', email: 'alice@example.com' })
|
|
461
|
-
* })
|
|
462
|
-
*
|
|
463
|
-
* // With retries and timeout
|
|
464
|
-
* const response = await httpRequest('https://api.example.com/data', {
|
|
465
|
-
* retries: 3,
|
|
466
|
-
* retryDelay: 1000,
|
|
467
|
-
* timeout: 60000
|
|
468
|
-
* })
|
|
469
|
-
*
|
|
470
|
-
* // Don't follow redirects
|
|
471
|
-
* const response = await httpRequest('https://example.com/redirect', {
|
|
472
|
-
* followRedirects: false
|
|
473
|
-
* })
|
|
474
|
-
* console.log(response.status) // 301, 302, etc.
|
|
475
|
-
* ```
|
|
476
|
-
*/
|
|
477
|
-
export declare function httpRequest(url: string, options?: HttpRequestOptions | undefined): Promise<HttpResponse>;
|
|
478
438
|
/**
|
|
479
439
|
* Download a file from a URL to a local path with redirect support, retry logic, and progress callbacks.
|
|
480
440
|
* Uses streaming to avoid loading entire file in memory.
|
|
@@ -533,8 +493,12 @@ export declare function httpRequest(url: string, options?: HttpRequestOptions |
|
|
|
533
493
|
*/
|
|
534
494
|
export declare function httpDownload(url: string, destPath: string, options?: HttpDownloadOptions | undefined): Promise<HttpDownloadResult>;
|
|
535
495
|
/**
|
|
536
|
-
* Perform
|
|
537
|
-
* Convenience wrapper around `httpRequest` for
|
|
496
|
+
* Perform an HTTP request and parse JSON response.
|
|
497
|
+
* Convenience wrapper around `httpRequest` for JSON API calls.
|
|
498
|
+
* Automatically sets appropriate headers for JSON requests:
|
|
499
|
+
* - `Accept: application/json` (always)
|
|
500
|
+
* - `Content-Type: application/json` (when body is present)
|
|
501
|
+
* User-provided headers override these defaults.
|
|
538
502
|
*
|
|
539
503
|
* @template T - Expected JSON response type (defaults to `unknown`)
|
|
540
504
|
* @param url - The URL to request (must start with http:// or https://)
|
|
@@ -544,34 +508,79 @@ export declare function httpDownload(url: string, destPath: string, options?: Ht
|
|
|
544
508
|
*
|
|
545
509
|
* @example
|
|
546
510
|
* ```ts
|
|
547
|
-
* // Simple JSON GET
|
|
548
|
-
* const data = await
|
|
511
|
+
* // Simple JSON GET (automatically sets Accept: application/json)
|
|
512
|
+
* const data = await httpJson('https://api.example.com/data')
|
|
549
513
|
* console.log(data)
|
|
550
514
|
*
|
|
551
515
|
* // With type safety
|
|
552
516
|
* interface User { id: number; name: string; email: string }
|
|
553
|
-
* const user = await
|
|
517
|
+
* const user = await httpJson<User>('https://api.example.com/user/123')
|
|
554
518
|
* console.log(user.name, user.email)
|
|
555
519
|
*
|
|
556
|
-
* //
|
|
557
|
-
* const
|
|
558
|
-
*
|
|
559
|
-
*
|
|
560
|
-
* 'Accept': 'application/json'
|
|
561
|
-
* }
|
|
520
|
+
* // POST with JSON body (automatically sets Content-Type: application/json)
|
|
521
|
+
* const result = await httpJson('https://api.example.com/users', {
|
|
522
|
+
* method: 'POST',
|
|
523
|
+
* body: JSON.stringify({ name: 'Alice', email: 'alice@example.com' })
|
|
562
524
|
* })
|
|
563
525
|
*
|
|
564
|
-
* // With retries
|
|
565
|
-
* const data = await
|
|
526
|
+
* // With custom headers and retries
|
|
527
|
+
* const data = await httpJson('https://api.example.com/data', {
|
|
528
|
+
* headers: {
|
|
529
|
+
* 'Authorization': 'Bearer token123'
|
|
530
|
+
* },
|
|
566
531
|
* retries: 3,
|
|
567
532
|
* retryDelay: 1000
|
|
568
533
|
* })
|
|
569
534
|
* ```
|
|
570
535
|
*/
|
|
571
|
-
export declare function
|
|
536
|
+
export declare function httpJson<T = unknown>(url: string, options?: HttpRequestOptions | undefined): Promise<T>;
|
|
537
|
+
/**
|
|
538
|
+
* Make an HTTP/HTTPS request with retry logic and redirect support.
|
|
539
|
+
* Provides a fetch-like API using Node.js native http/https modules.
|
|
540
|
+
*
|
|
541
|
+
* This is the main entry point for making HTTP requests. It handles retries,
|
|
542
|
+
* redirects, timeouts, and provides a fetch-compatible response interface.
|
|
543
|
+
*
|
|
544
|
+
* @param url - The URL to request (must start with http:// or https://)
|
|
545
|
+
* @param options - Request configuration options
|
|
546
|
+
* @returns Promise resolving to response object with `.json()`, `.text()`, etc.
|
|
547
|
+
* @throws {Error} When all retries are exhausted, timeout occurs, or non-retryable error happens
|
|
548
|
+
*
|
|
549
|
+
* @example
|
|
550
|
+
* ```ts
|
|
551
|
+
* // Simple GET request
|
|
552
|
+
* const response = await httpRequest('https://api.example.com/data')
|
|
553
|
+
* const data = response.json()
|
|
554
|
+
*
|
|
555
|
+
* // POST with JSON body
|
|
556
|
+
* const response = await httpRequest('https://api.example.com/users', {
|
|
557
|
+
* method: 'POST',
|
|
558
|
+
* headers: { 'Content-Type': 'application/json' },
|
|
559
|
+
* body: JSON.stringify({ name: 'Alice', email: 'alice@example.com' })
|
|
560
|
+
* })
|
|
561
|
+
*
|
|
562
|
+
* // With retries and timeout
|
|
563
|
+
* const response = await httpRequest('https://api.example.com/data', {
|
|
564
|
+
* retries: 3,
|
|
565
|
+
* retryDelay: 1000,
|
|
566
|
+
* timeout: 60000
|
|
567
|
+
* })
|
|
568
|
+
*
|
|
569
|
+
* // Don't follow redirects
|
|
570
|
+
* const response = await httpRequest('https://example.com/redirect', {
|
|
571
|
+
* followRedirects: false
|
|
572
|
+
* })
|
|
573
|
+
* console.log(response.status) // 301, 302, etc.
|
|
574
|
+
* ```
|
|
575
|
+
*/
|
|
576
|
+
export declare function httpRequest(url: string, options?: HttpRequestOptions | undefined): Promise<HttpResponse>;
|
|
572
577
|
/**
|
|
573
|
-
* Perform
|
|
578
|
+
* Perform an HTTP request and return text response.
|
|
574
579
|
* Convenience wrapper around `httpRequest` for fetching text content.
|
|
580
|
+
* Automatically sets appropriate headers for text requests:
|
|
581
|
+
* - `Accept: text/plain` (always)
|
|
582
|
+
* - `Content-Type: text/plain` (when body is present)
|
|
583
|
+
* User-provided headers override these defaults.
|
|
575
584
|
*
|
|
576
585
|
* @param url - The URL to request (must start with http:// or https://)
|
|
577
586
|
* @param options - Request configuration options
|
|
@@ -580,25 +589,32 @@ export declare function httpGetJson<T = unknown>(url: string, options?: HttpRequ
|
|
|
580
589
|
*
|
|
581
590
|
* @example
|
|
582
591
|
* ```ts
|
|
583
|
-
* // Fetch HTML
|
|
584
|
-
* const html = await
|
|
592
|
+
* // Fetch HTML (automatically sets Accept: text/plain)
|
|
593
|
+
* const html = await httpText('https://example.com')
|
|
585
594
|
* console.log(html.includes('<!DOCTYPE html>'))
|
|
586
595
|
*
|
|
587
596
|
* // Fetch plain text
|
|
588
|
-
* const text = await
|
|
597
|
+
* const text = await httpText('https://example.com/file.txt')
|
|
589
598
|
* console.log(text)
|
|
590
599
|
*
|
|
591
|
-
* //
|
|
592
|
-
* const
|
|
600
|
+
* // POST with text body (automatically sets Content-Type: text/plain)
|
|
601
|
+
* const result = await httpText('https://example.com/api', {
|
|
602
|
+
* method: 'POST',
|
|
603
|
+
* body: 'raw text data'
|
|
604
|
+
* })
|
|
605
|
+
*
|
|
606
|
+
* // With custom headers (override defaults)
|
|
607
|
+
* const text = await httpText('https://example.com/data.txt', {
|
|
593
608
|
* headers: {
|
|
594
|
-
* 'Authorization': 'Bearer token123'
|
|
609
|
+
* 'Authorization': 'Bearer token123',
|
|
610
|
+
* 'Accept': 'text/html' // Override default Accept header
|
|
595
611
|
* }
|
|
596
612
|
* })
|
|
597
613
|
*
|
|
598
614
|
* // With timeout
|
|
599
|
-
* const text = await
|
|
615
|
+
* const text = await httpText('https://example.com/large-file.txt', {
|
|
600
616
|
* timeout: 60000 // 1 minute
|
|
601
617
|
* })
|
|
602
618
|
* ```
|
|
603
619
|
*/
|
|
604
|
-
export declare function
|
|
620
|
+
export declare function httpText(url: string, options?: HttpRequestOptions | undefined): Promise<string>;
|
package/dist/http-request.js
CHANGED
|
@@ -20,9 +20,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
var http_request_exports = {};
|
|
21
21
|
__export(http_request_exports, {
|
|
22
22
|
httpDownload: () => httpDownload,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
httpJson: () => httpJson,
|
|
24
|
+
httpRequest: () => httpRequest,
|
|
25
|
+
httpText: () => httpText
|
|
26
26
|
});
|
|
27
27
|
module.exports = __toCommonJS(http_request_exports);
|
|
28
28
|
let _fs;
|
|
@@ -49,38 +49,131 @@ function getHttps() {
|
|
|
49
49
|
}
|
|
50
50
|
return _https;
|
|
51
51
|
}
|
|
52
|
-
async function
|
|
52
|
+
async function httpDownloadAttempt(url, destPath, options) {
|
|
53
53
|
const {
|
|
54
|
-
body,
|
|
55
54
|
followRedirects = true,
|
|
56
55
|
headers = {},
|
|
57
56
|
maxRedirects = 5,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
retryDelay = 1e3,
|
|
61
|
-
timeout = 3e4
|
|
57
|
+
onProgress,
|
|
58
|
+
timeout = 12e4
|
|
62
59
|
} = { __proto__: null, ...options };
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
60
|
+
return await new Promise((resolve, reject) => {
|
|
61
|
+
const parsedUrl = new URL(url);
|
|
62
|
+
const isHttps = parsedUrl.protocol === "https:";
|
|
63
|
+
const httpModule = isHttps ? /* @__PURE__ */ getHttps() : /* @__PURE__ */ getHttp();
|
|
64
|
+
const requestOptions = {
|
|
65
|
+
headers: {
|
|
66
|
+
"User-Agent": "socket-registry/1.0",
|
|
67
|
+
...headers
|
|
68
|
+
},
|
|
69
|
+
hostname: parsedUrl.hostname,
|
|
70
|
+
method: "GET",
|
|
71
|
+
path: parsedUrl.pathname + parsedUrl.search,
|
|
72
|
+
port: parsedUrl.port,
|
|
73
|
+
timeout
|
|
74
|
+
};
|
|
75
|
+
const { createWriteStream } = /* @__PURE__ */ getFs();
|
|
76
|
+
let fileStream;
|
|
77
|
+
let streamClosed = false;
|
|
78
|
+
const closeStream = () => {
|
|
79
|
+
if (!streamClosed && fileStream) {
|
|
80
|
+
streamClosed = true;
|
|
81
|
+
fileStream.close();
|
|
78
82
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
};
|
|
84
|
+
const request = httpModule.request(
|
|
85
|
+
requestOptions,
|
|
86
|
+
(res) => {
|
|
87
|
+
if (followRedirects && res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
88
|
+
if (maxRedirects <= 0) {
|
|
89
|
+
reject(
|
|
90
|
+
new Error(
|
|
91
|
+
`Too many redirects (exceeded maximum: ${maxRedirects})`
|
|
92
|
+
)
|
|
93
|
+
);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const redirectUrl = res.headers.location.startsWith("http") ? res.headers.location : new URL(res.headers.location, url).toString();
|
|
97
|
+
resolve(
|
|
98
|
+
httpDownloadAttempt(redirectUrl, destPath, {
|
|
99
|
+
followRedirects,
|
|
100
|
+
headers,
|
|
101
|
+
maxRedirects: maxRedirects - 1,
|
|
102
|
+
onProgress,
|
|
103
|
+
timeout
|
|
104
|
+
})
|
|
105
|
+
);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (!res.statusCode || res.statusCode < 200 || res.statusCode >= 300) {
|
|
109
|
+
closeStream();
|
|
110
|
+
reject(
|
|
111
|
+
new Error(
|
|
112
|
+
`Download failed: HTTP ${res.statusCode} ${res.statusMessage}`
|
|
113
|
+
)
|
|
114
|
+
);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const totalSize = Number.parseInt(
|
|
118
|
+
res.headers["content-length"] || "0",
|
|
119
|
+
10
|
|
120
|
+
);
|
|
121
|
+
let downloadedSize = 0;
|
|
122
|
+
fileStream = createWriteStream(destPath);
|
|
123
|
+
fileStream.on("error", (error) => {
|
|
124
|
+
closeStream();
|
|
125
|
+
const err = new Error(`Failed to write file: ${error.message}`, {
|
|
126
|
+
cause: error
|
|
127
|
+
});
|
|
128
|
+
reject(err);
|
|
129
|
+
});
|
|
130
|
+
res.on("data", (chunk) => {
|
|
131
|
+
downloadedSize += chunk.length;
|
|
132
|
+
if (onProgress && totalSize > 0) {
|
|
133
|
+
onProgress(downloadedSize, totalSize);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
res.on("end", () => {
|
|
137
|
+
fileStream?.close(() => {
|
|
138
|
+
streamClosed = true;
|
|
139
|
+
resolve({
|
|
140
|
+
path: destPath,
|
|
141
|
+
size: downloadedSize
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
res.on("error", (error) => {
|
|
146
|
+
closeStream();
|
|
147
|
+
reject(error);
|
|
148
|
+
});
|
|
149
|
+
res.pipe(fileStream);
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
request.on("error", (error) => {
|
|
153
|
+
closeStream();
|
|
154
|
+
const code = error.code;
|
|
155
|
+
let message = `HTTP download failed for ${url}: ${error.message}
|
|
156
|
+
`;
|
|
157
|
+
if (code === "ENOTFOUND") {
|
|
158
|
+
message += "DNS lookup failed. Check the hostname and your network connection.";
|
|
159
|
+
} else if (code === "ECONNREFUSED") {
|
|
160
|
+
message += "Connection refused. Verify the server is running and accessible.";
|
|
161
|
+
} else if (code === "ETIMEDOUT") {
|
|
162
|
+
message += "Request timed out. Check your network or increase the timeout value.";
|
|
163
|
+
} else if (code === "ECONNRESET") {
|
|
164
|
+
message += "Connection reset. The server may have closed the connection unexpectedly.";
|
|
165
|
+
} else {
|
|
166
|
+
message += "Check your network connection and verify the URL is correct.";
|
|
167
|
+
}
|
|
168
|
+
reject(new Error(message, { cause: error }));
|
|
169
|
+
});
|
|
170
|
+
request.on("timeout", () => {
|
|
171
|
+
request.destroy();
|
|
172
|
+
closeStream();
|
|
173
|
+
reject(new Error(`Download timed out after ${timeout}ms`));
|
|
174
|
+
});
|
|
175
|
+
request.end();
|
|
176
|
+
});
|
|
84
177
|
}
|
|
85
178
|
async function httpRequestAttempt(url, options) {
|
|
86
179
|
const {
|
|
@@ -236,134 +329,30 @@ async function httpDownload(url, destPath, options) {
|
|
|
236
329
|
}
|
|
237
330
|
throw lastError || new Error("Download failed after retries");
|
|
238
331
|
}
|
|
239
|
-
async function
|
|
332
|
+
async function httpJson(url, options) {
|
|
240
333
|
const {
|
|
241
|
-
|
|
334
|
+
body,
|
|
242
335
|
headers = {},
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const { createWriteStream } = /* @__PURE__ */ getFs();
|
|
263
|
-
let fileStream;
|
|
264
|
-
let streamClosed = false;
|
|
265
|
-
const closeStream = () => {
|
|
266
|
-
if (!streamClosed && fileStream) {
|
|
267
|
-
streamClosed = true;
|
|
268
|
-
fileStream.close();
|
|
269
|
-
}
|
|
270
|
-
};
|
|
271
|
-
const request = httpModule.request(
|
|
272
|
-
requestOptions,
|
|
273
|
-
(res) => {
|
|
274
|
-
if (followRedirects && res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
275
|
-
if (maxRedirects <= 0) {
|
|
276
|
-
reject(
|
|
277
|
-
new Error(
|
|
278
|
-
`Too many redirects (exceeded maximum: ${maxRedirects})`
|
|
279
|
-
)
|
|
280
|
-
);
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
const redirectUrl = res.headers.location.startsWith("http") ? res.headers.location : new URL(res.headers.location, url).toString();
|
|
284
|
-
resolve(
|
|
285
|
-
httpDownloadAttempt(redirectUrl, destPath, {
|
|
286
|
-
followRedirects,
|
|
287
|
-
headers,
|
|
288
|
-
maxRedirects: maxRedirects - 1,
|
|
289
|
-
onProgress,
|
|
290
|
-
timeout
|
|
291
|
-
})
|
|
292
|
-
);
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
if (!res.statusCode || res.statusCode < 200 || res.statusCode >= 300) {
|
|
296
|
-
closeStream();
|
|
297
|
-
reject(
|
|
298
|
-
new Error(
|
|
299
|
-
`Download failed: HTTP ${res.statusCode} ${res.statusMessage}`
|
|
300
|
-
)
|
|
301
|
-
);
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
const totalSize = Number.parseInt(
|
|
305
|
-
res.headers["content-length"] || "0",
|
|
306
|
-
10
|
|
307
|
-
);
|
|
308
|
-
let downloadedSize = 0;
|
|
309
|
-
fileStream = createWriteStream(destPath);
|
|
310
|
-
fileStream.on("error", (error) => {
|
|
311
|
-
closeStream();
|
|
312
|
-
const err = new Error(`Failed to write file: ${error.message}`, {
|
|
313
|
-
cause: error
|
|
314
|
-
});
|
|
315
|
-
reject(err);
|
|
316
|
-
});
|
|
317
|
-
res.on("data", (chunk) => {
|
|
318
|
-
downloadedSize += chunk.length;
|
|
319
|
-
if (onProgress && totalSize > 0) {
|
|
320
|
-
onProgress(downloadedSize, totalSize);
|
|
321
|
-
}
|
|
322
|
-
});
|
|
323
|
-
res.on("end", () => {
|
|
324
|
-
fileStream?.close(() => {
|
|
325
|
-
streamClosed = true;
|
|
326
|
-
resolve({
|
|
327
|
-
path: destPath,
|
|
328
|
-
size: downloadedSize
|
|
329
|
-
});
|
|
330
|
-
});
|
|
331
|
-
});
|
|
332
|
-
res.on("error", (error) => {
|
|
333
|
-
closeStream();
|
|
334
|
-
reject(error);
|
|
335
|
-
});
|
|
336
|
-
res.pipe(fileStream);
|
|
337
|
-
}
|
|
338
|
-
);
|
|
339
|
-
request.on("error", (error) => {
|
|
340
|
-
closeStream();
|
|
341
|
-
const code = error.code;
|
|
342
|
-
let message = `HTTP download failed for ${url}: ${error.message}
|
|
343
|
-
`;
|
|
344
|
-
if (code === "ENOTFOUND") {
|
|
345
|
-
message += "DNS lookup failed. Check the hostname and your network connection.";
|
|
346
|
-
} else if (code === "ECONNREFUSED") {
|
|
347
|
-
message += "Connection refused. Verify the server is running and accessible.";
|
|
348
|
-
} else if (code === "ETIMEDOUT") {
|
|
349
|
-
message += "Request timed out. Check your network or increase the timeout value.";
|
|
350
|
-
} else if (code === "ECONNRESET") {
|
|
351
|
-
message += "Connection reset. The server may have closed the connection unexpectedly.";
|
|
352
|
-
} else {
|
|
353
|
-
message += "Check your network connection and verify the URL is correct.";
|
|
354
|
-
}
|
|
355
|
-
reject(new Error(message, { cause: error }));
|
|
356
|
-
});
|
|
357
|
-
request.on("timeout", () => {
|
|
358
|
-
request.destroy();
|
|
359
|
-
closeStream();
|
|
360
|
-
reject(new Error(`Download timed out after ${timeout}ms`));
|
|
361
|
-
});
|
|
362
|
-
request.end();
|
|
336
|
+
...restOptions
|
|
337
|
+
} = {
|
|
338
|
+
__proto__: null,
|
|
339
|
+
...options
|
|
340
|
+
};
|
|
341
|
+
const defaultHeaders = {
|
|
342
|
+
Accept: "application/json"
|
|
343
|
+
};
|
|
344
|
+
if (body) {
|
|
345
|
+
defaultHeaders["Content-Type"] = "application/json";
|
|
346
|
+
}
|
|
347
|
+
const mergedHeaders = {
|
|
348
|
+
...defaultHeaders,
|
|
349
|
+
...headers
|
|
350
|
+
};
|
|
351
|
+
const response = await httpRequest(url, {
|
|
352
|
+
body,
|
|
353
|
+
headers: mergedHeaders,
|
|
354
|
+
...restOptions
|
|
363
355
|
});
|
|
364
|
-
}
|
|
365
|
-
async function httpGetJson(url, options) {
|
|
366
|
-
const response = await httpRequest(url, { ...options, method: "GET" });
|
|
367
356
|
if (!response.ok) {
|
|
368
357
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
369
358
|
}
|
|
@@ -373,8 +362,63 @@ async function httpGetJson(url, options) {
|
|
|
373
362
|
throw new Error("Failed to parse JSON response", { cause: e });
|
|
374
363
|
}
|
|
375
364
|
}
|
|
376
|
-
async function
|
|
377
|
-
const
|
|
365
|
+
async function httpRequest(url, options) {
|
|
366
|
+
const {
|
|
367
|
+
body,
|
|
368
|
+
followRedirects = true,
|
|
369
|
+
headers = {},
|
|
370
|
+
maxRedirects = 5,
|
|
371
|
+
method = "GET",
|
|
372
|
+
retries = 0,
|
|
373
|
+
retryDelay = 1e3,
|
|
374
|
+
timeout = 3e4
|
|
375
|
+
} = { __proto__: null, ...options };
|
|
376
|
+
let lastError;
|
|
377
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
378
|
+
try {
|
|
379
|
+
return await httpRequestAttempt(url, {
|
|
380
|
+
body,
|
|
381
|
+
followRedirects,
|
|
382
|
+
headers,
|
|
383
|
+
maxRedirects,
|
|
384
|
+
method,
|
|
385
|
+
timeout
|
|
386
|
+
});
|
|
387
|
+
} catch (e) {
|
|
388
|
+
lastError = e;
|
|
389
|
+
if (attempt === retries) {
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
const delayMs = retryDelay * 2 ** attempt;
|
|
393
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
throw lastError || new Error("Request failed after retries");
|
|
397
|
+
}
|
|
398
|
+
async function httpText(url, options) {
|
|
399
|
+
const {
|
|
400
|
+
body,
|
|
401
|
+
headers = {},
|
|
402
|
+
...restOptions
|
|
403
|
+
} = {
|
|
404
|
+
__proto__: null,
|
|
405
|
+
...options
|
|
406
|
+
};
|
|
407
|
+
const defaultHeaders = {
|
|
408
|
+
Accept: "text/plain"
|
|
409
|
+
};
|
|
410
|
+
if (body) {
|
|
411
|
+
defaultHeaders["Content-Type"] = "text/plain";
|
|
412
|
+
}
|
|
413
|
+
const mergedHeaders = {
|
|
414
|
+
...defaultHeaders,
|
|
415
|
+
...headers
|
|
416
|
+
};
|
|
417
|
+
const response = await httpRequest(url, {
|
|
418
|
+
body,
|
|
419
|
+
headers: mergedHeaders,
|
|
420
|
+
...restOptions
|
|
421
|
+
});
|
|
378
422
|
if (!response.ok) {
|
|
379
423
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
380
424
|
}
|
|
@@ -383,7 +427,7 @@ async function httpGetText(url, options) {
|
|
|
383
427
|
// Annotate the CommonJS export names for ESM import in node:
|
|
384
428
|
0 && (module.exports = {
|
|
385
429
|
httpDownload,
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
430
|
+
httpJson,
|
|
431
|
+
httpRequest,
|
|
432
|
+
httpText
|
|
389
433
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@socketsecurity/lib",
|
|
3
|
-
"version": "5.
|
|
4
|
-
"packageManager": "pnpm@10.
|
|
3
|
+
"version": "5.6.0",
|
|
4
|
+
"packageManager": "pnpm@10.29.1",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Core utilities and infrastructure for Socket.dev security tools",
|
|
7
7
|
"keywords": [
|
|
@@ -730,7 +730,7 @@
|
|
|
730
730
|
"@socketregistry/is-unicode-supported": "1.0.5",
|
|
731
731
|
"@socketregistry/packageurl-js": "1.3.5",
|
|
732
732
|
"@socketregistry/yocto-spinner": "1.0.25",
|
|
733
|
-
"@socketsecurity/lib-stable": "npm:@socketsecurity/lib@5.5.
|
|
733
|
+
"@socketsecurity/lib-stable": "npm:@socketsecurity/lib@5.5.3",
|
|
734
734
|
"@types/node": "24.9.2",
|
|
735
735
|
"@typescript/native-preview": "7.0.0-dev.20250920.1",
|
|
736
736
|
"@vitest/coverage-v8": "4.0.3",
|
|
@@ -754,7 +754,7 @@
|
|
|
754
754
|
"globals": "16.4.0",
|
|
755
755
|
"has-flag": "5.0.1",
|
|
756
756
|
"husky": "9.1.7",
|
|
757
|
-
"libnpmexec": "^10.
|
|
757
|
+
"libnpmexec": "^10.2.0",
|
|
758
758
|
"libnpmpack": "9.0.9",
|
|
759
759
|
"lint-staged": "15.2.11",
|
|
760
760
|
"magic-string": "0.30.17",
|
|
@@ -818,8 +818,8 @@
|
|
|
818
818
|
"patchedDependencies": {
|
|
819
819
|
"@npmcli/run-script@10.0.0": "patches/@npmcli__run-script@10.0.0.patch",
|
|
820
820
|
"@sigstore/sign@4.1.0": "patches/@sigstore__sign@4.1.0.patch",
|
|
821
|
-
"
|
|
822
|
-
"
|
|
821
|
+
"execa@5.1.1": "patches/execa@5.1.1.patch",
|
|
822
|
+
"node-gyp@11.5.0": "patches/node-gyp@11.5.0.patch"
|
|
823
823
|
}
|
|
824
824
|
}
|
|
825
825
|
}
|