single-file-cli 2.7.0 → 2.7.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/deno.json +1 -1
- package/lib/cdp-client.js +30 -17
- package/lib/version.js +1 -1
- package/package.json +3 -2
- package/test/e2e/navigation.test.js +43 -0
- package/test/e2e/slow-response.test.js +45 -0
- package/update-core.sh +9 -0
package/deno.json
CHANGED
package/lib/cdp-client.js
CHANGED
|
@@ -605,7 +605,7 @@ async function waitForPageReadyState({ Page }, state, { options, debugMessages }
|
|
|
605
605
|
Page.removeEventListener(FRAME_NAVIGATED_EVENT_TYPE, onFrameNavigated);
|
|
606
606
|
};
|
|
607
607
|
const onLifecycleEvent = createLifecycleEventHandler(state, timeoutState, resolve, cleanup, { options, debugMessages });
|
|
608
|
-
const onFrameNavigated = createFrameNavigatedHandler(timeoutState, reject, cleanup, { options, debugMessages });
|
|
608
|
+
const onFrameNavigated = createFrameNavigatedHandler(state, timeoutState, reject, cleanup, { options, debugMessages });
|
|
609
609
|
state.settle = () => {
|
|
610
610
|
clearTimeout(timeoutState.timeoutId);
|
|
611
611
|
cleanup();
|
|
@@ -618,17 +618,20 @@ async function waitForPageReadyState({ Page }, state, { options, debugMessages }
|
|
|
618
618
|
|
|
619
619
|
function createLifecycleEventHandler(state, timeoutState, resolve, cleanup, { options, debugMessages }) {
|
|
620
620
|
return ({ params }) => {
|
|
621
|
-
const { frameId, name } = params;
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
621
|
+
const { frameId, loaderId, name } = params;
|
|
622
|
+
// the frame keeps its ID across documents: the events of the blank page,
|
|
623
|
+
// whose network goes idle while the server has not answered yet, would
|
|
624
|
+
// satisfy the wait and the page would be captured as soon as it commits
|
|
625
|
+
if (frameId !== state.topFrameId || loaderId !== state.loaderId) {
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
logData(["Detecting lifecycle event", name], { options, debugMessages });
|
|
629
|
+
const stateIndex = NETWORK_STATES.indexOf(name);
|
|
630
|
+
if (stateIndex != -1 && (state.reachedStateIndex == -1 || stateIndex < state.reachedStateIndex)) {
|
|
631
|
+
state.reachedStateIndex = stateIndex;
|
|
628
632
|
}
|
|
629
|
-
const shouldResolve =
|
|
630
|
-
(name
|
|
631
|
-
(timeoutState.timeoutId && NETWORK_STATES.indexOf(name) < NETWORK_STATES.indexOf(options.browserWaitUntil)));
|
|
633
|
+
const shouldResolve = name === options.browserWaitUntil ||
|
|
634
|
+
(timeoutState.timeoutId && NETWORK_STATES.indexOf(name) < NETWORK_STATES.indexOf(options.browserWaitUntil));
|
|
632
635
|
if (shouldResolve) {
|
|
633
636
|
// the delay is restarted when the page reaches a further state, so
|
|
634
637
|
// that it is captured once it stopped settling
|
|
@@ -643,15 +646,25 @@ function createLifecycleEventHandler(state, timeoutState, resolve, cleanup, { op
|
|
|
643
646
|
};
|
|
644
647
|
};
|
|
645
648
|
|
|
646
|
-
function createFrameNavigatedHandler(timeoutState, reject, cleanup, { options, debugMessages }) {
|
|
649
|
+
function createFrameNavigatedHandler(state, timeoutState, reject, cleanup, { options, debugMessages }) {
|
|
647
650
|
const UNREACHABLE_URL_ERROR_MESSAGE = "Unreachable URL";
|
|
648
651
|
return ({ params }) => {
|
|
649
652
|
const { frame } = params;
|
|
650
|
-
if (!frame.parentId
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
653
|
+
if (!frame.parentId) {
|
|
654
|
+
if (frame.unreachableUrl) {
|
|
655
|
+
logData(["Detecting unreachable URL", frame.unreachableUrl], { options, debugMessages });
|
|
656
|
+
clearTimeout(timeoutState.timeoutId);
|
|
657
|
+
cleanup();
|
|
658
|
+
reject(new Error(UNREACHABLE_URL_ERROR_MESSAGE + ": " + frame.unreachableUrl));
|
|
659
|
+
} else {
|
|
660
|
+
// a new document starts the wait over: the states the previous one
|
|
661
|
+
// reached and the delay it started say nothing about this one
|
|
662
|
+
logData(["Detecting navigation", frame.url], { options, debugMessages });
|
|
663
|
+
clearTimeout(timeoutState.timeoutId);
|
|
664
|
+
timeoutState.timeoutId = undefined;
|
|
665
|
+
state.reachedStateIndex = -1;
|
|
666
|
+
state.loaderId = frame.loaderId;
|
|
667
|
+
}
|
|
655
668
|
}
|
|
656
669
|
};
|
|
657
670
|
}
|
package/lib/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = "2.7.
|
|
1
|
+
export const version = "2.7.2";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "single-file-cli",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.2",
|
|
4
4
|
"description": "SingleFile CLI",
|
|
5
5
|
"author": "Gildas Lormeau",
|
|
6
6
|
"license": "AGPL-3.0-or-later",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"scripts": {
|
|
18
18
|
"test": "node --test --test-concurrency=2 \"test/**/*.test.js\"",
|
|
19
19
|
"test:dev": "SINGLE_FILE_TARGET=dev node --test --test-concurrency=2 \"test/**/*.test.js\"",
|
|
20
|
-
"lint": "eslint ."
|
|
20
|
+
"lint": "eslint .",
|
|
21
|
+
"update-core": "bash update-core.sh"
|
|
21
22
|
},
|
|
22
23
|
"bin": {
|
|
23
24
|
"single-file": "single-file-node.js"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/* global setTimeout */
|
|
2
|
+
|
|
3
|
+
import { test } from "node:test";
|
|
4
|
+
import assert from "node:assert/strict";
|
|
5
|
+
import { createServer } from "node:http";
|
|
6
|
+
import { execFile } from "node:child_process";
|
|
7
|
+
import { promisify } from "node:util";
|
|
8
|
+
import { mkdtemp, rm, readFile } from "node:fs/promises";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import process from "node:process";
|
|
12
|
+
import { cliDirectory } from "../target.js";
|
|
13
|
+
|
|
14
|
+
const execFileAsync = promisify(execFile);
|
|
15
|
+
|
|
16
|
+
// The first page redirects itself once its network went quiet, inside the
|
|
17
|
+
// settle delay of the wait. The second page keeps its body for longer than
|
|
18
|
+
// that delay, so a capture decided by the first page's state saves it empty.
|
|
19
|
+
test("a page that navigates after its network went quiet is captured once the new page loaded", { timeout: 120000 }, async () => {
|
|
20
|
+
const server = createServer((request, response) => {
|
|
21
|
+
response.writeHead(200, { "content-type": "text/html" });
|
|
22
|
+
if (request.url == "/second") {
|
|
23
|
+
response.write("<!doctype html><title>second</title><p>second page head</p>");
|
|
24
|
+
setTimeout(() => response.end("<p id=late>second page body arrived late</p>"), 4000);
|
|
25
|
+
} else {
|
|
26
|
+
response.end("<!doctype html><title>first</title><p id=first>first page content</p><script>setTimeout(() => location.href = '/second', 800)</script>");
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
await new Promise(resolve => server.listen(0, "localhost", resolve));
|
|
30
|
+
const directory = await mkdtemp(join(tmpdir(), "single-file-test-"));
|
|
31
|
+
try {
|
|
32
|
+
const url = "http://localhost:" + server.address().port + "/";
|
|
33
|
+
const output = join(directory, "out.html");
|
|
34
|
+
await execFileAsync(process.execPath, ["single-file-node.js", url, output], { cwd: cliDirectory });
|
|
35
|
+
const content = await readFile(output, "utf8");
|
|
36
|
+
assert.ok(content.includes("second page body arrived late"), "the page was captured before the second page loaded");
|
|
37
|
+
assert.ok(!content.includes("first page content"), "the first page was captured");
|
|
38
|
+
} finally {
|
|
39
|
+
await rm(directory, { recursive: true });
|
|
40
|
+
server.closeAllConnections();
|
|
41
|
+
server.close();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/* global setTimeout */
|
|
2
|
+
|
|
3
|
+
import { test } from "node:test";
|
|
4
|
+
import assert from "node:assert/strict";
|
|
5
|
+
import { createServer } from "node:http";
|
|
6
|
+
import { execFile } from "node:child_process";
|
|
7
|
+
import { promisify } from "node:util";
|
|
8
|
+
import { mkdtemp, rm, readFile } from "node:fs/promises";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import process from "node:process";
|
|
12
|
+
import { cliDirectory } from "../target.js";
|
|
13
|
+
|
|
14
|
+
const execFileAsync = promisify(execFile);
|
|
15
|
+
|
|
16
|
+
// The server answers after the network of the blank page went idle, and sends
|
|
17
|
+
// the body of the page later still. A wait satisfied by the blank page captures
|
|
18
|
+
// the document as soon as it commits, while it has a head and no body yet.
|
|
19
|
+
test("a page whose server answers late is captured once loaded", { timeout: 120000 }, async () => {
|
|
20
|
+
const server = createServer((request, response) => {
|
|
21
|
+
if (request.url == "/") {
|
|
22
|
+
setTimeout(() => {
|
|
23
|
+
response.writeHead(200, { "content-type": "text/html" });
|
|
24
|
+
response.write("<!doctype html><html><head><title>late</title>");
|
|
25
|
+
setTimeout(() => response.end("</head><body><p id=late>body arrived late</p></body></html>"), 3000);
|
|
26
|
+
}, 3000);
|
|
27
|
+
} else {
|
|
28
|
+
response.writeHead(404);
|
|
29
|
+
response.end();
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
await new Promise(resolve => server.listen(0, "localhost", resolve));
|
|
33
|
+
const directory = await mkdtemp(join(tmpdir(), "single-file-test-"));
|
|
34
|
+
try {
|
|
35
|
+
const url = "http://localhost:" + server.address().port + "/";
|
|
36
|
+
const output = join(directory, "out.html");
|
|
37
|
+
await execFileAsync(process.execPath, ["single-file-node.js", url, output], { cwd: cliDirectory });
|
|
38
|
+
const content = await readFile(output, "utf8");
|
|
39
|
+
assert.ok(content.includes("body arrived late"), "the page was captured before its body arrived");
|
|
40
|
+
} finally {
|
|
41
|
+
await rm(directory, { recursive: true });
|
|
42
|
+
server.closeAllConnections();
|
|
43
|
+
server.close();
|
|
44
|
+
}
|
|
45
|
+
});
|
package/update-core.sh
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
version="${1:-$(npm view single-file-core version)}"
|
|
6
|
+
sed -i.bak "s#^CORE_PACKAGE=\"npm:single-file-core@.*\"#CORE_PACKAGE=\"npm:single-file-core@$version\"#" build.sh
|
|
7
|
+
rm build.sh.bak
|
|
8
|
+
bash build.sh
|
|
9
|
+
echo "single-file-core $version"
|