@lightnet/cli 4.9.0 → 4.9.2
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 +12 -0
- package/package.json +1 -1
- package/src/check-links.js +39 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @lightnet/cli
|
|
2
2
|
|
|
3
|
+
## 4.9.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#473](https://github.com/LightNetDev/LightNet/pull/473) [`9e673cf`](https://github.com/LightNetDev/LightNet/commit/9e673cfdc58cab88f96d2f3a22d23b327be626a6) - Update dependencies.
|
|
8
|
+
|
|
9
|
+
## 4.9.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#471](https://github.com/LightNetDev/LightNet/pull/471) [`667909c`](https://github.com/LightNetDev/LightNet/commit/667909c629835b0bf7f697c6d0a92c1ad92a733e) - Check media links using a range GET and verify the first body byte can be read. Retry unsatisfiable ranges with a plain GET to support empty resources.
|
|
14
|
+
|
|
3
15
|
## 4.9.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/package.json
CHANGED
package/src/check-links.js
CHANGED
|
@@ -18,9 +18,9 @@ import { cancelPrompt } from "./support/prompt-cancel.js"
|
|
|
18
18
|
const mediaDir = "src/content/media"
|
|
19
19
|
const defaultConcurrency = 24
|
|
20
20
|
const defaultTimeoutMs = 10_000
|
|
21
|
-
const headFallbackStatuses = new Set([405, 501])
|
|
22
21
|
const requestHeaders = {
|
|
23
22
|
accept: "*/*",
|
|
23
|
+
"accept-encoding": "identity",
|
|
24
24
|
"user-agent": "@lightnet/cli link checker",
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -359,22 +359,17 @@ function resolveUrl(url, siteUrl) {
|
|
|
359
359
|
* @returns {Promise<LinkCheckResult>}
|
|
360
360
|
*/
|
|
361
361
|
async function checkLink(url, options) {
|
|
362
|
-
const
|
|
362
|
+
const rangeResult = await fetchStatus(url, {
|
|
363
363
|
...options,
|
|
364
|
-
headers: requestHeaders,
|
|
365
|
-
method: "HEAD",
|
|
364
|
+
headers: { ...requestHeaders, range: "bytes=0-0" },
|
|
366
365
|
})
|
|
367
|
-
if (
|
|
368
|
-
return
|
|
366
|
+
if (rangeResult.status !== 416) {
|
|
367
|
+
return rangeResult
|
|
369
368
|
}
|
|
369
|
+
// An empty resource has no first byte; retry without the range constraint.
|
|
370
370
|
return fetchStatus(url, {
|
|
371
371
|
...options,
|
|
372
|
-
headers:
|
|
373
|
-
...requestHeaders,
|
|
374
|
-
"accept-encoding": "identity",
|
|
375
|
-
range: "bytes=0-0",
|
|
376
|
-
},
|
|
377
|
-
method: "GET",
|
|
372
|
+
headers: requestHeaders,
|
|
378
373
|
})
|
|
379
374
|
}
|
|
380
375
|
|
|
@@ -432,7 +427,6 @@ function formatReferenceSources(sources) {
|
|
|
432
427
|
* @param {{
|
|
433
428
|
* fetch: typeof fetch
|
|
434
429
|
* headers?: Record<string, string>
|
|
435
|
-
* method: "GET"|"HEAD"
|
|
436
430
|
* timeoutMs: number
|
|
437
431
|
* }} options
|
|
438
432
|
* @returns {Promise<LinkCheckResult>}
|
|
@@ -443,12 +437,42 @@ async function fetchStatus(url, options) {
|
|
|
443
437
|
try {
|
|
444
438
|
const response = await options.fetch(url, {
|
|
445
439
|
headers: options.headers,
|
|
446
|
-
method:
|
|
440
|
+
method: "GET",
|
|
447
441
|
redirect: "follow",
|
|
448
442
|
signal: controller.signal,
|
|
449
443
|
})
|
|
450
|
-
await response.body?.cancel()
|
|
451
444
|
const ok = response.status >= 200 && response.status < 400
|
|
445
|
+
if (ok) {
|
|
446
|
+
const expectsBody =
|
|
447
|
+
response.status === 206 ||
|
|
448
|
+
Number(response.headers.get("content-length")) > 0
|
|
449
|
+
const reader = response.body?.getReader()
|
|
450
|
+
let receivedByte = false
|
|
451
|
+
try {
|
|
452
|
+
// A server may ignore Range and stream the full file. Stop as soon as
|
|
453
|
+
// the first nonempty chunk proves that the body can be read.
|
|
454
|
+
if (reader) {
|
|
455
|
+
while (true) {
|
|
456
|
+
const { done, value } = await reader.read()
|
|
457
|
+
if (done) break
|
|
458
|
+
if (value.byteLength > 0) {
|
|
459
|
+
receivedByte = true
|
|
460
|
+
break
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
} finally {
|
|
465
|
+
await reader?.cancel()
|
|
466
|
+
}
|
|
467
|
+
if (expectsBody && !receivedByte) {
|
|
468
|
+
return {
|
|
469
|
+
ok: false,
|
|
470
|
+
reason: "Response body ended before the first byte",
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
} else {
|
|
474
|
+
await response.body?.cancel()
|
|
475
|
+
}
|
|
452
476
|
return { ok, status: response.status }
|
|
453
477
|
} catch (error) {
|
|
454
478
|
return {
|