@util.js/node-http 0.41.8 → 0.42.1
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/index.js +2 -3
- package/lib/Urls.js +396 -131
- package/package.json +8 -3
package/index.js
CHANGED
package/lib/Urls.js
CHANGED
|
@@ -1,154 +1,419 @@
|
|
|
1
|
-
|
|
1
|
+
import https from "node:https";
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import { pipeline } from "node:stream/promises";
|
|
4
|
+
import Files from "@util.js/node-files";
|
|
5
|
+
|
|
6
|
+
// These are global variables for Chromium version management.
|
|
7
|
+
let latestChromiumAndroidStableVersion;
|
|
8
|
+
let latestChromiumAndroidStableVersionLocked = false;
|
|
9
|
+
let googleBotUserAgentHeaderLogged = false;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Determine timeout value in milliseconds from the parameters object.
|
|
13
|
+
*/
|
|
14
|
+
function determineTimeout(params) {
|
|
15
|
+
if (params == null) return -1;
|
|
16
|
+
const timeoutVal = params?.timeoutMs;
|
|
17
|
+
const parsedInt = Number.parseInt(timeoutVal, 10);
|
|
18
|
+
if (timeoutVal != null && !Number.isNaN(parsedInt) && parsedInt >= 0) {
|
|
19
|
+
return parsedInt;
|
|
20
|
+
}
|
|
21
|
+
return -1;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Fetch the latest Chromium Android stable version from the API.
|
|
26
|
+
*/
|
|
27
|
+
async function fetchLatestChromiumAndroidStableVersion() {
|
|
28
|
+
const CHANNEL = "Stable";
|
|
29
|
+
const url = `https://chromiumdash.appspot.com/fetch_releases?platform=Android&channel=${CHANNEL}&num=1`;
|
|
30
|
+
|
|
31
|
+
const response = await fetch(url);
|
|
32
|
+
if (!response.ok)
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Fetching ${url} failed with HTTP status ${response.status}.`,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const data = await response.json();
|
|
38
|
+
if (!data || data.length === 0)
|
|
39
|
+
throw new Error("The Chromium Dash API returned an empty response.");
|
|
40
|
+
|
|
41
|
+
const latest = data[0];
|
|
42
|
+
if (!latest || !latest.version)
|
|
43
|
+
throw new Error(
|
|
44
|
+
"The version field is missing in the Chromium Dash API response.",
|
|
45
|
+
);
|
|
46
|
+
return latest.version;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get the Google Bot user agent header with the latest Chromium version.
|
|
51
|
+
*/
|
|
52
|
+
async function getGoogleBotUserAgentHeader(logger) {
|
|
53
|
+
// https://chromiumdash.appspot.com/releases?platform=Android
|
|
54
|
+
// https://github.com/wasi-master/13ft/blob/main/app/portable.py
|
|
55
|
+
// https://web.archive.org/web/20260811103458/https://developers.google.com/crawling/docs/crawlers-fetchers/google-common-crawlers#googlebot-news#googlebot-news
|
|
56
|
+
|
|
57
|
+
const latestChromiumAndroidStableVersion =
|
|
58
|
+
await getLatestChromiumAndroidStableVersion(logger);
|
|
59
|
+
const userAgent = `Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${latestChromiumAndroidStableVersion} Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)`;
|
|
60
|
+
|
|
61
|
+
if (!googleBotUserAgentHeaderLogged) {
|
|
62
|
+
googleBotUserAgentHeaderLogged = true;
|
|
63
|
+
logger?.error?.("Using the following user agent for HTTP requests: ");
|
|
64
|
+
logger?.error?.(userAgent);
|
|
65
|
+
logger?.error?.();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return userAgent;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get the latest Chromium Android stable version with fallback support.
|
|
73
|
+
*/
|
|
74
|
+
async function getLatestChromiumAndroidStableVersion(logger) {
|
|
75
|
+
while (latestChromiumAndroidStableVersionLocked) {
|
|
76
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
77
|
+
}
|
|
78
|
+
latestChromiumAndroidStableVersionLocked = true;
|
|
79
|
+
try {
|
|
80
|
+
if (latestChromiumAndroidStableVersion)
|
|
81
|
+
return latestChromiumAndroidStableVersion;
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
latestChromiumAndroidStableVersion =
|
|
85
|
+
await fetchLatestChromiumAndroidStableVersion();
|
|
86
|
+
} catch (fetchErr) {
|
|
87
|
+
const fallbackVersion = "152.0.7977.42";
|
|
88
|
+
logger?.error?.(
|
|
89
|
+
`Fetching the latest Chromium version failed: ${fetchErr.message}`,
|
|
90
|
+
);
|
|
91
|
+
logger?.error?.(
|
|
92
|
+
`Falling back to hardcoded version '${fallbackVersion}'...`,
|
|
93
|
+
);
|
|
94
|
+
latestChromiumAndroidStableVersion = fallbackVersion;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
logger?.error?.();
|
|
98
|
+
logger?.error?.(
|
|
99
|
+
`The latest Chromium Android stable version is '${latestChromiumAndroidStableVersion}'.`,
|
|
100
|
+
);
|
|
101
|
+
logger?.error?.();
|
|
102
|
+
return latestChromiumAndroidStableVersion;
|
|
103
|
+
} finally {
|
|
104
|
+
latestChromiumAndroidStableVersionLocked = false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Normalize the input into a valid URL object.
|
|
110
|
+
*/
|
|
111
|
+
function parseUrl(url) {
|
|
112
|
+
try {
|
|
113
|
+
return typeof url === "string" ? new URL(url) : url;
|
|
114
|
+
} catch (err) {
|
|
115
|
+
throw new TypeError(`The provided URL "${url}" is invalid.`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
2
118
|
|
|
3
119
|
class Urls {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Decode a Uniform Resource Identifier (URI).
|
|
122
|
+
*/
|
|
123
|
+
static decodeURI(...args) {
|
|
124
|
+
return decodeURI(...args);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Decode a Uniform Resource Identifier (URI) component.
|
|
129
|
+
*/
|
|
130
|
+
static decodeURIComponent(...args) {
|
|
131
|
+
return decodeURIComponent(...args);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Encode a Uniform Resource Identifier (URI).
|
|
136
|
+
*/
|
|
137
|
+
static encodeURI(...args) {
|
|
138
|
+
return encodeURI(...args);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Encode a Uniform Resource Identifier (URI) component.
|
|
143
|
+
*/
|
|
144
|
+
static encodeURIComponent(...args) {
|
|
145
|
+
return encodeURIComponent(...args);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get HTTP headers for the given URL using a HEAD request.
|
|
150
|
+
* http://stackoverflow.com/questions/5922842/getting-http-headers-with-node-js
|
|
151
|
+
* http://stackoverflow.com/a/6001507
|
|
152
|
+
*/
|
|
153
|
+
static async headers(params) {
|
|
154
|
+
const timeoutMs = determineTimeout(params);
|
|
155
|
+
const {
|
|
156
|
+
headers: customHeaders = {},
|
|
157
|
+
logger,
|
|
158
|
+
url,
|
|
159
|
+
useGoogleBotUserAgent = false,
|
|
160
|
+
} = params || {};
|
|
161
|
+
|
|
162
|
+
const urlObj = parseUrl(url);
|
|
163
|
+
const p = urlObj.protocol === "http:" ? http : https;
|
|
164
|
+
let headers = { ...customHeaders };
|
|
165
|
+
|
|
166
|
+
if (useGoogleBotUserAgent && !headers["User-Agent"]) {
|
|
167
|
+
headers["User-Agent"] = await getGoogleBotUserAgentHeader(logger);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const reqOptions = {
|
|
171
|
+
...params,
|
|
172
|
+
timeout: timeoutMs !== -1 ? timeoutMs : undefined,
|
|
173
|
+
headers,
|
|
174
|
+
hostname: urlObj.hostname,
|
|
175
|
+
method: "HEAD",
|
|
176
|
+
path: urlObj.pathname + urlObj.search,
|
|
177
|
+
port: urlObj.port || (urlObj.protocol === "https:" ? 443 : 80),
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
return new Promise((resolve, reject) => {
|
|
181
|
+
const req = p.request(reqOptions, (res) => {
|
|
182
|
+
res.destroy();
|
|
183
|
+
resolve(res.headers);
|
|
33
184
|
});
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
185
|
+
|
|
186
|
+
req.on("error", reject);
|
|
187
|
+
|
|
188
|
+
if (timeoutMs !== -1) {
|
|
189
|
+
// https://stackoverflow.com/a/12783062
|
|
190
|
+
req.on("socket", (socket) => {
|
|
191
|
+
socket.setTimeout(timeoutMs);
|
|
192
|
+
socket.on("timeout", () => {
|
|
193
|
+
req.destroy(new Error("The request timed out."));
|
|
194
|
+
});
|
|
41
195
|
});
|
|
42
|
-
}
|
|
43
|
-
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
req.end();
|
|
199
|
+
});
|
|
44
200
|
}
|
|
45
201
|
|
|
46
|
-
|
|
47
|
-
|
|
202
|
+
/**
|
|
203
|
+
* Convert the given URL content to a buffer.
|
|
204
|
+
*/
|
|
205
|
+
static async urlToBuffer(params) {
|
|
206
|
+
const timeoutMs = determineTimeout(params);
|
|
207
|
+
const { logger, url, useGoogleBotUserAgent = false } = params || {};
|
|
208
|
+
const chunks = await this.urlToData({
|
|
209
|
+
logger,
|
|
210
|
+
timeoutMs,
|
|
211
|
+
url,
|
|
212
|
+
useGoogleBotUserAgent,
|
|
213
|
+
});
|
|
214
|
+
return Buffer.concat(chunks);
|
|
48
215
|
}
|
|
49
216
|
|
|
50
|
-
|
|
51
|
-
|
|
217
|
+
/**
|
|
218
|
+
* Get data from the given URL as chunks.
|
|
219
|
+
*/
|
|
220
|
+
static async urlToData(params) {
|
|
221
|
+
const timeoutMs = determineTimeout(params);
|
|
222
|
+
const {
|
|
223
|
+
headers,
|
|
224
|
+
logger,
|
|
225
|
+
url,
|
|
226
|
+
useGoogleBotUserAgent = false,
|
|
227
|
+
} = params || {};
|
|
228
|
+
|
|
229
|
+
const readable = await this.urlToReadStream({
|
|
230
|
+
headers,
|
|
231
|
+
logger,
|
|
232
|
+
timeoutMs,
|
|
233
|
+
url,
|
|
234
|
+
useGoogleBotUserAgent,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const chunks = [];
|
|
238
|
+
for await (const chunk of readable) {
|
|
239
|
+
chunks.push(chunk);
|
|
240
|
+
}
|
|
241
|
+
return chunks;
|
|
52
242
|
}
|
|
53
243
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
244
|
+
/**
|
|
245
|
+
* Download the given URL to the given local file.
|
|
246
|
+
*/
|
|
247
|
+
static async urlToFile(params) {
|
|
248
|
+
const timeoutMs = determineTimeout(params);
|
|
249
|
+
const {
|
|
250
|
+
file,
|
|
251
|
+
logger,
|
|
252
|
+
maxRedirects,
|
|
253
|
+
url,
|
|
254
|
+
useGoogleBotUserAgent = false,
|
|
255
|
+
} = params || {};
|
|
66
256
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
: {},
|
|
74
|
-
);
|
|
75
|
-
options = Object.assign(options, {
|
|
76
|
-
host: parsedUrl.host,
|
|
77
|
-
method: "HEAD",
|
|
78
|
-
path: parsedUrl.path,
|
|
79
|
-
port,
|
|
257
|
+
const readable = await this.urlToReadStream({
|
|
258
|
+
logger,
|
|
259
|
+
maxRedirects,
|
|
260
|
+
timeoutMs,
|
|
261
|
+
url,
|
|
262
|
+
useGoogleBotUserAgent,
|
|
80
263
|
});
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
264
|
+
|
|
265
|
+
const writable = Files.createWriteStream(file);
|
|
266
|
+
await pipeline(readable, writable);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Get the HTTP stream for the given URL with redirect and timeout support.
|
|
271
|
+
*/
|
|
272
|
+
static async urlToReadStream(params) {
|
|
273
|
+
const timeoutMs = determineTimeout(params);
|
|
274
|
+
const {
|
|
275
|
+
headers: customHeaders = {},
|
|
276
|
+
logger,
|
|
277
|
+
maxRedirects = 0,
|
|
278
|
+
url,
|
|
279
|
+
useGoogleBotUserAgent = false,
|
|
280
|
+
} = params || {};
|
|
281
|
+
|
|
282
|
+
const urlObj = parseUrl(url);
|
|
283
|
+
const p = urlObj.protocol === "http:" ? http : https;
|
|
284
|
+
let headers = { ...customHeaders };
|
|
285
|
+
|
|
286
|
+
if (useGoogleBotUserAgent && !headers["User-Agent"]) {
|
|
287
|
+
headers["User-Agent"] = await getGoogleBotUserAgentHeader(logger);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return new Promise((resolve, reject) => {
|
|
291
|
+
const req = p.get(urlObj, { headers }, (res) => {
|
|
292
|
+
const { statusCode } = res;
|
|
293
|
+
|
|
294
|
+
if (statusCode !== 200) {
|
|
295
|
+
if (statusCode >= 300 && statusCode < 400 && res.headers.location) {
|
|
296
|
+
if (maxRedirects > 0) {
|
|
297
|
+
const newUrl = new URL(res.headers.location, urlObj);
|
|
298
|
+
res.resume(); // Free memory.
|
|
299
|
+
Urls.urlToReadStream({
|
|
300
|
+
headers,
|
|
301
|
+
logger,
|
|
302
|
+
maxRedirects: maxRedirects - 1,
|
|
303
|
+
timeoutMs,
|
|
304
|
+
url: newUrl,
|
|
305
|
+
useGoogleBotUserAgent,
|
|
306
|
+
})
|
|
307
|
+
.then(resolve)
|
|
308
|
+
.catch(reject);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
res.resume();
|
|
314
|
+
const error = new Error(
|
|
315
|
+
`The request for ${urlObj.href} failed with status code ${statusCode}.`,
|
|
316
|
+
);
|
|
317
|
+
error.headers = res.headers;
|
|
318
|
+
error.statusCode = statusCode;
|
|
319
|
+
error.url = urlObj.href;
|
|
320
|
+
return reject(error);
|
|
94
321
|
}
|
|
322
|
+
|
|
323
|
+
resolve(res);
|
|
95
324
|
});
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
325
|
+
|
|
326
|
+
req.on("error", reject);
|
|
327
|
+
|
|
328
|
+
if (timeoutMs !== -1) {
|
|
329
|
+
// https://stackoverflow.com/a/12783062
|
|
330
|
+
req.on("socket", (socket) => {
|
|
331
|
+
socket.setTimeout(timeoutMs);
|
|
332
|
+
socket.on("timeout", () => {
|
|
333
|
+
req.destroy(new Error("The request timed out."));
|
|
334
|
+
});
|
|
103
335
|
});
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
request.end();
|
|
336
|
+
}
|
|
337
|
+
});
|
|
107
338
|
}
|
|
108
|
-
}
|
|
109
339
|
|
|
110
|
-
|
|
340
|
+
/**
|
|
341
|
+
* Proxy the HTTP request for the given URL to the given web response.
|
|
342
|
+
*/
|
|
343
|
+
static async urlToResponse(params) {
|
|
344
|
+
const timeoutMs = determineTimeout(params);
|
|
345
|
+
const {
|
|
346
|
+
logger,
|
|
347
|
+
response,
|
|
348
|
+
url,
|
|
349
|
+
useGoogleBotUserAgent = false,
|
|
350
|
+
} = params || {};
|
|
111
351
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
352
|
+
const readable = await Urls.urlToReadStream({
|
|
353
|
+
logger,
|
|
354
|
+
timeoutMs,
|
|
355
|
+
url,
|
|
356
|
+
useGoogleBotUserAgent,
|
|
357
|
+
});
|
|
119
358
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
dependencies[dependency] || (dependencies[dependency] = require(dependency))
|
|
124
|
-
);
|
|
125
|
-
}
|
|
359
|
+
response.set(readable.headers);
|
|
360
|
+
await pipeline(readable, response);
|
|
361
|
+
}
|
|
126
362
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
function urlUtil() {
|
|
140
|
-
return get("url");
|
|
141
|
-
}
|
|
363
|
+
/**
|
|
364
|
+
* Convert the given URL content to a string, auto-detecting its charset from Content-Type headers.
|
|
365
|
+
*/
|
|
366
|
+
static async urlToString(params) {
|
|
367
|
+
const timeoutMs = determineTimeout(params);
|
|
368
|
+
const {
|
|
369
|
+
encoding: explicitEncoding,
|
|
370
|
+
headers,
|
|
371
|
+
logger,
|
|
372
|
+
url,
|
|
373
|
+
useGoogleBotUserAgent = false,
|
|
374
|
+
} = params || {};
|
|
142
375
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
376
|
+
const readable = await this.urlToReadStream({
|
|
377
|
+
headers,
|
|
378
|
+
logger,
|
|
379
|
+
timeoutMs,
|
|
380
|
+
url,
|
|
381
|
+
useGoogleBotUserAgent,
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
let encoding = explicitEncoding;
|
|
385
|
+
|
|
386
|
+
if (!encoding) {
|
|
387
|
+
const contentType = readable.headers?.["content-type"];
|
|
388
|
+
if (contentType) {
|
|
389
|
+
const match = contentType.match(/charset=([^;]+)/i);
|
|
390
|
+
if (match) {
|
|
391
|
+
encoding = match[1].trim().replace(/['"]/g, "").toLowerCase();
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Map common charset names to Node.js Buffer encodings.
|
|
397
|
+
if (encoding === "iso-8859-1") {
|
|
398
|
+
encoding = "latin1";
|
|
399
|
+
} else if (encoding === "utf-8") {
|
|
400
|
+
encoding = "utf8";
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const chunks = [];
|
|
404
|
+
for await (const chunk of readable) {
|
|
405
|
+
chunks.push(chunk);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const buffer = Buffer.concat(chunks);
|
|
409
|
+
|
|
410
|
+
try {
|
|
411
|
+
return buffer.toString(encoding || "utf8");
|
|
412
|
+
} catch (err) {
|
|
413
|
+
// Fall back to utf8 if Node.js does not recognize the encoding.
|
|
414
|
+
return buffer.toString("utf8");
|
|
415
|
+
}
|
|
153
416
|
}
|
|
154
417
|
}
|
|
418
|
+
|
|
419
|
+
export default Urls;
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"author": "Chris Topher <c@creemama.com>",
|
|
3
3
|
"dependencies": {
|
|
4
|
-
"@util.js/node-files": "0.
|
|
5
|
-
"@util.js/promises": "0.
|
|
4
|
+
"@util.js/node-files": "0.42.1",
|
|
5
|
+
"@util.js/promises": "0.42.1"
|
|
6
6
|
},
|
|
7
7
|
"description": "JavaScript utility methods for URLs",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./index.js"
|
|
10
|
+
},
|
|
8
11
|
"files": [
|
|
9
12
|
"index.js",
|
|
10
13
|
"lib"
|
|
@@ -19,6 +22,7 @@
|
|
|
19
22
|
"utiljs"
|
|
20
23
|
],
|
|
21
24
|
"license": "MIT",
|
|
25
|
+
"main": "./index.js",
|
|
22
26
|
"name": "@util.js/node-http",
|
|
23
27
|
"publishConfig": {
|
|
24
28
|
"access": "public"
|
|
@@ -31,5 +35,6 @@
|
|
|
31
35
|
"type": "git",
|
|
32
36
|
"url": "https://github.com/creemama/utiljs/tree/master/packages/utiljs-node-http"
|
|
33
37
|
},
|
|
34
|
-
"
|
|
38
|
+
"type": "module",
|
|
39
|
+
"version": "0.42.1"
|
|
35
40
|
}
|