single-file-cli 2.7.1 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@single-file/single-file-cli",
3
- "version": "2.7.1",
3
+ "version": "2.7.2",
4
4
  "description": "SingleFile CLI",
5
5
  "exports": {
6
6
  ".": "./single-file-cli-api.js"
package/lib/cdp-client.js CHANGED
@@ -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
- if (frameId === state.topFrameId) {
623
- logData(["Detecting lifecycle event", name], { options, debugMessages });
624
- const stateIndex = NETWORK_STATES.indexOf(name);
625
- if (stateIndex != -1 && (state.reachedStateIndex == -1 || stateIndex < state.reachedStateIndex)) {
626
- state.reachedStateIndex = stateIndex;
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 = frameId === state.topFrameId &&
630
- (name === options.browserWaitUntil ||
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
@@ -660,6 +663,7 @@ function createFrameNavigatedHandler(state, timeoutState, reject, cleanup, { opt
660
663
  clearTimeout(timeoutState.timeoutId);
661
664
  timeoutState.timeoutId = undefined;
662
665
  state.reachedStateIndex = -1;
666
+ state.loaderId = frame.loaderId;
663
667
  }
664
668
  }
665
669
  };
package/lib/version.js CHANGED
@@ -1 +1 @@
1
- export const version = "2.7.1";
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.1",
3
+ "version": "2.7.2",
4
4
  "description": "SingleFile CLI",
5
5
  "author": "Gildas Lormeau",
6
6
  "license": "AGPL-3.0-or-later",
@@ -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
+ });