html-snapshots 5.7.2 → 5.9.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.
@@ -17,10 +17,8 @@
17
17
  */
18
18
  function ensure (options, must) {
19
19
  if (must) {
20
- for (let prop in must) {
21
- if (options[prop] === void 0 || options[prop] === null) {
22
- options[prop] = must[prop];
23
- }
20
+ for (const prop in must) {
21
+ options[prop] ??= must[prop];
24
22
  }
25
23
  }
26
24
  return options;
@@ -126,23 +124,25 @@ function prependMsgToErr (error, message, quoteInput) {
126
124
  * Simple response checker for remote files.
127
125
  * Expected use in robots.txt or sitemap.xml only.
128
126
  *
129
- * @param {IncomingMessage} res - The IncomingMessage response to check.
127
+ * @param {Response} res - The IncomingMessage response to check.
130
128
  * @param {Array} mediaTypes - array of acceptable content-type media type strings.
131
129
  * @returns {String} Error message, empty string (falsy) if OK.
132
130
  */
133
131
  function checkResponse (res, mediaTypes) {
134
- let result = `status: '${res.statusCode}', GET failed.`;
132
+ let result = `status: '${res.status}', GET failed.`;
135
133
 
136
134
  mediaTypes = !Array.isArray(mediaTypes) ? [mediaTypes] : mediaTypes;
137
135
 
138
- if (res.statusCode === 200) {
136
+ if (res.status === 200) {
137
+ const contentType = res.headers.get("Content-Type");
138
+
139
139
  // if content-type exists, and media type found then contentTypeOk
140
140
  const contentTypeOk =
141
- res.headers["content-type"] &&
141
+ contentType &&
142
142
  // empty array and none found return true
143
143
  !mediaTypes.every(mediaType => {
144
144
  // flip -1 to 0 and NOT, so that true == NOT found, found stops loop w/false
145
- return !~res.headers["content-type"].indexOf(mediaType);
145
+ return !~contentType.indexOf(mediaType);
146
146
  });
147
147
 
148
148
  result = contentTypeOk ? "" :
@@ -152,9 +152,43 @@ function checkResponse (res, mediaTypes) {
152
152
  return result;
153
153
  }
154
154
 
155
+ /**
156
+ * Simple fetch with timeout and retries.
157
+ * Retries on throw (timeout) or 500+ response with backoff delay.
158
+ *
159
+ * @param {String|Request} url - The fetch resource
160
+ * @param {Object} options - The fetch RequestInit object
161
+ * @param {Number} timeout - The request timeout in milliseconds
162
+ * @param {Number} retries - The retries on timeout or 500+ responses
163
+ * @param {Number} backoff - The initial backoff to wait between retries, backoff * 2 each iter
164
+ */
165
+ async function simpleFetch (url, options = {}, timeout = 3000, retries = 3, backoff = 1000) {
166
+ try {
167
+ const response = await fetch(url, {
168
+ signal: AbortSignal.timeout(timeout),
169
+ ...options
170
+ });
171
+
172
+ if (!response.ok && response.status >= 500 && retries > 0) {
173
+ throw new Error(`Server error: ${response.status}`);
174
+ }
175
+
176
+ return response;
177
+ } catch (error) {
178
+ if (retries <= 0) throw error;
179
+
180
+ console.warn(`Retrying... (${retries} attempts left)`);
181
+
182
+ await new Promise(resolve => setTimeout(resolve, backoff));
183
+
184
+ return simpleFetch(url, options, timeout, retries - 1, backoff * 2);
185
+ }
186
+ }
187
+
155
188
  module.exports = {
156
189
  checkResponse,
157
190
  ensure,
191
+ get: (url, timeout) => simpleFetch(url, {}, timeout),
158
192
  head,
159
193
  isUrl,
160
194
  isFunction,
@@ -178,23 +178,23 @@ function convert (options, buffer, next, callback) {
178
178
  * @return {Promise} resolves to data on completion.
179
179
  */
180
180
  async function getUrl (options, parseFn) {
181
- const { default:got } = await import("got");
182
- return got({
183
- url: options.source,
184
- responseType: "buffer",
185
- timeout: {
186
- request: options.timeout() // get the default timeout
187
- }
188
- }).then(res => {
189
- let error = common.checkResponse(res, ["text/xml", "application/xml"]);
181
+ try {
182
+ const res = await common.get(options.source, options.timeout());
183
+ const error = common.checkResponse(res, ["text/xml", "application/xml"]);
190
184
  if (error) {
191
185
  throw new Error(error);
192
186
  }
187
+
188
+ const arrayBuffer = await res.arrayBuffer();
189
+
193
190
  const conv = util.promisify(convert);
194
- return conv(options, res.body, parseFn);
195
- }).catch(err => {
196
- throw new Error(common.prependMsgToErr(err, options.source, true));
197
- });
191
+ return conv(options, Buffer.from(arrayBuffer), parseFn);
192
+ }
193
+ catch (err) {
194
+ throw new Error(common.prependMsgToErr(err, options.source, true), {
195
+ cause: err
196
+ });
197
+ }
198
198
  }
199
199
 
200
200
  /**
@@ -122,21 +122,19 @@ function processRobotsTxt (options, body) {
122
122
  * @returns {Promise} resolves to undefined.
123
123
  */
124
124
  async function getRobotsUrl (options) {
125
- const { default:got } = await import("got");
126
- return got({
127
- url: options.source,
128
- timeout: {
129
- request: options.timeout()
130
- }
131
- }).then(res => {
125
+ try {
126
+ const res = await common.get(options.source, options.timeout());
132
127
  const error = common.checkResponse(res, "text/plain");
133
128
  if (error) {
134
129
  throw new Error(error);
135
130
  }
136
- return processRobotsTxt(options, res.body.toString());
137
- }).catch(err => {
138
- throw new Error(common.prependMsgToErr(err, options.source, true));
139
- });
131
+ return processRobotsTxt(options, await res.text());
132
+ }
133
+ catch (err) {
134
+ throw new Error(common.prependMsgToErr(err, options.source, true), {
135
+ cause: err
136
+ });
137
+ }
140
138
  }
141
139
 
142
140
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "html-snapshots",
3
- "version": "5.7.2",
3
+ "version": "5.9.0",
4
4
  "author": {
5
5
  "name": "Alex Grant",
6
6
  "email": "alex@localnerve.com",
@@ -53,15 +53,14 @@
53
53
  "async": "3.2.6",
54
54
  "async-lock": "1.4.1",
55
55
  "combine-errors": "3.0.3",
56
- "got": "^14.6.6",
57
56
  "phantomjs-prebuilt": "2.1.16",
58
- "puppeteer": "^24.40.0",
57
+ "puppeteer": "^24.41.0",
59
58
  "xml2js": "0.6.2"
60
59
  },
61
60
  "devDependencies": {
62
61
  "eslint": "^10.2.0",
63
62
  "@eslint/js": "^10.0.1",
64
- "globals": "^17.4.0",
63
+ "globals": "^17.5.0",
65
64
  "express": "^5.2.1",
66
65
  "mocha": "^11.7.5",
67
66
  "c8": "^11.0.0",
@@ -70,7 +69,7 @@
70
69
  },
71
70
  "overrides": {
72
71
  "request": "npm:@cypress/request@3.0.10",
73
- "diff": "^8.0.4",
72
+ "diff": "^9.0.0",
74
73
  "glob": "^13.0.6",
75
74
  "serialize-javascript": "^7.0.5",
76
75
  "brace-expansion": "^5.0.5"