@pagepocket/cli 0.6.0 → 0.6.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/dist/cli.js +11 -1
- package/package.json +5 -4
- package/dist/lib/content-type.js +0 -36
- package/dist/lib/css-rewrite.js +0 -62
- package/dist/lib/hackers/capture-network.js +0 -64
- package/dist/lib/hackers/index.js +0 -22
- package/dist/lib/hackers/preload-fetch.js +0 -56
- package/dist/lib/hackers/preload-image.js +0 -61
- package/dist/lib/hackers/preload-xhr.js +0 -59
- package/dist/lib/hackers/replay-beacon.js +0 -21
- package/dist/lib/hackers/replay-dom-rewrite.js +0 -295
- package/dist/lib/hackers/replay-eventsource.js +0 -25
- package/dist/lib/hackers/replay-fetch.js +0 -33
- package/dist/lib/hackers/replay-image.js +0 -48
- package/dist/lib/hackers/replay-svg-image.js +0 -89
- package/dist/lib/hackers/replay-websocket.js +0 -26
- package/dist/lib/hackers/replay-xhr.js +0 -91
- package/dist/lib/hackers/types.js +0 -2
- package/dist/lib/network-records.js +0 -69
- package/dist/lib/replay-script.js +0 -346
- package/dist/lib/resources.js +0 -131
- package/dist/lib/stages/download.js +0 -61
- package/dist/lib/stages/index.js +0 -235
- package/dist/lib/stages/intercept.js +0 -23
- package/dist/lib/stages/trigger.js +0 -56
- package/dist/lib/stages/visit.js +0 -24
- package/dist/lib/types.js +0 -2
- package/dist/preload.js +0 -60
- package/dist/stages/build-snapshot-data.js +0 -14
- package/dist/stages/build-snapshot.js +0 -30
- package/dist/stages/capture-network.js +0 -19
- package/dist/stages/download-resources.js +0 -48
- package/dist/stages/fetch-html.js +0 -69
- package/dist/stages/rewrite-links.js +0 -145
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const core_1 = require("@oclif/core");
|
|
7
7
|
const lib_1 = require("@pagepocket/lib");
|
|
8
8
|
const lighterceptor_adapter_1 = require("@pagepocket/lighterceptor-adapter");
|
|
9
|
+
const puppeteer_adapter_1 = require("@pagepocket/puppeteer-adapter");
|
|
9
10
|
const chalk_1 = __importDefault(require("chalk"));
|
|
10
11
|
const prepare_output_1 = require("./stages/prepare-output");
|
|
11
12
|
const write_snapshot_1 = require("./stages/write-snapshot");
|
|
@@ -15,6 +16,7 @@ class PagepocketCommand extends core_1.Command {
|
|
|
15
16
|
const { args, flags } = await this.parse(PagepocketCommand);
|
|
16
17
|
const targetUrl = args.url;
|
|
17
18
|
const outputFlag = flags.output ? flags.output.trim() : undefined;
|
|
19
|
+
const runtime = flags.runtime;
|
|
18
20
|
const headers = {
|
|
19
21
|
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
|
|
20
22
|
accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
|
@@ -22,7 +24,9 @@ class PagepocketCommand extends core_1.Command {
|
|
|
22
24
|
referer: targetUrl
|
|
23
25
|
};
|
|
24
26
|
const snapshot = await (0, with_spinner_1.withSpinner)(async () => {
|
|
25
|
-
const interceptor =
|
|
27
|
+
const interceptor = runtime === "puppeteer"
|
|
28
|
+
? new puppeteer_adapter_1.PuppeteerAdapter({ gotoOptions: { waitUntil: "networkidle0" } })
|
|
29
|
+
: new lighterceptor_adapter_1.LighterceptorAdapter({ headers });
|
|
26
30
|
const pagepocket = lib_1.PagePocket.fromURL(targetUrl);
|
|
27
31
|
return pagepocket.capture({
|
|
28
32
|
interceptor,
|
|
@@ -55,6 +59,12 @@ PagepocketCommand.flags = {
|
|
|
55
59
|
output: core_1.Flags.string({
|
|
56
60
|
char: "o",
|
|
57
61
|
description: "Output path for the snapshot HTML file"
|
|
62
|
+
}),
|
|
63
|
+
runtime: core_1.Flags.string({
|
|
64
|
+
char: "r",
|
|
65
|
+
description: "Interceptor runtime to use",
|
|
66
|
+
options: ["lighterceptor", "puppeteer"],
|
|
67
|
+
default: "lighterceptor"
|
|
58
68
|
})
|
|
59
69
|
};
|
|
60
70
|
exports.default = PagepocketCommand;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pagepocket/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "CLI for capturing offline snapshots of web pages.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -16,8 +16,9 @@
|
|
|
16
16
|
"@oclif/core": "^4.0.9",
|
|
17
17
|
"chalk": "^4.1.2",
|
|
18
18
|
"ora": "^9.0.0",
|
|
19
|
-
"@pagepocket/lib": "0.6.
|
|
20
|
-
"@pagepocket/
|
|
19
|
+
"@pagepocket/lib": "0.6.1",
|
|
20
|
+
"@pagepocket/puppeteer-adapter": "0.6.1",
|
|
21
|
+
"@pagepocket/lighterceptor-adapter": "0.6.1"
|
|
21
22
|
},
|
|
22
23
|
"devDependencies": {
|
|
23
24
|
"@types/node": "^20.11.30",
|
|
@@ -34,6 +35,6 @@
|
|
|
34
35
|
"scripts": {
|
|
35
36
|
"build": "tsc",
|
|
36
37
|
"start": "node dist/index.js",
|
|
37
|
-
"test": "tsx --test specs
|
|
38
|
+
"test": "tsx --test specs/*.spec.ts"
|
|
38
39
|
}
|
|
39
40
|
}
|
package/dist/lib/content-type.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isTextResponse = exports.extensionFromContentType = void 0;
|
|
4
|
-
const extensionFromContentType = (contentType) => {
|
|
5
|
-
if (!contentType) {
|
|
6
|
-
return "";
|
|
7
|
-
}
|
|
8
|
-
if (contentType.includes("text/css"))
|
|
9
|
-
return ".css";
|
|
10
|
-
if (contentType.includes("javascript"))
|
|
11
|
-
return ".js";
|
|
12
|
-
if (contentType.includes("image/png"))
|
|
13
|
-
return ".png";
|
|
14
|
-
if (contentType.includes("image/jpeg"))
|
|
15
|
-
return ".jpg";
|
|
16
|
-
if (contentType.includes("image/gif"))
|
|
17
|
-
return ".gif";
|
|
18
|
-
if (contentType.includes("image/svg"))
|
|
19
|
-
return ".svg";
|
|
20
|
-
if (contentType.includes("font/woff2"))
|
|
21
|
-
return ".woff2";
|
|
22
|
-
if (contentType.includes("font/woff"))
|
|
23
|
-
return ".woff";
|
|
24
|
-
return "";
|
|
25
|
-
};
|
|
26
|
-
exports.extensionFromContentType = extensionFromContentType;
|
|
27
|
-
const isTextResponse = (contentType) => {
|
|
28
|
-
const lowered = contentType.toLowerCase();
|
|
29
|
-
return (lowered.startsWith("text/") ||
|
|
30
|
-
lowered.includes("json") ||
|
|
31
|
-
lowered.includes("javascript") ||
|
|
32
|
-
lowered.includes("xml") ||
|
|
33
|
-
lowered.includes("svg") ||
|
|
34
|
-
lowered.includes("html"));
|
|
35
|
-
};
|
|
36
|
-
exports.isTextResponse = isTextResponse;
|
package/dist/lib/css-rewrite.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.rewriteCssUrls = exports.buildDataUrlMap = void 0;
|
|
7
|
-
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
8
|
-
const getHeaderValue = (headers, name) => {
|
|
9
|
-
// Normalize header names to find the requested value.
|
|
10
|
-
for (const key in headers) {
|
|
11
|
-
if (key.toLowerCase() === name.toLowerCase()) {
|
|
12
|
-
return headers[key];
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
return undefined;
|
|
16
|
-
};
|
|
17
|
-
const buildDataUrlMap = (records) => {
|
|
18
|
-
// Build a lookup of recorded binary responses so CSS url() can be inlined.
|
|
19
|
-
const map = new Map();
|
|
20
|
-
for (const record of records) {
|
|
21
|
-
if (!record || !record.url || !record.responseBodyBase64) {
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
const headers = record.responseHeaders || {};
|
|
25
|
-
const contentType = getHeaderValue(headers, "content-type") || "application/octet-stream";
|
|
26
|
-
map.set(record.url, `data:${contentType};base64,${record.responseBodyBase64}`);
|
|
27
|
-
}
|
|
28
|
-
return map;
|
|
29
|
-
};
|
|
30
|
-
exports.buildDataUrlMap = buildDataUrlMap;
|
|
31
|
-
const rewriteCssUrls = async (filePath, cssUrl, dataUrlMap) => {
|
|
32
|
-
// Replace external URLs in CSS with data URLs to preserve rendering offline.
|
|
33
|
-
const css = await promises_1.default.readFile(filePath, "utf-8");
|
|
34
|
-
const urlPattern = /url\(\s*(['"]?)([^'")]+)\1\s*\)/g;
|
|
35
|
-
const rewritten = css.replace(urlPattern, (match, quote, rawUrl) => {
|
|
36
|
-
const trimmed = String(rawUrl || "").trim();
|
|
37
|
-
if (!trimmed || trimmed.startsWith("data:") || trimmed.startsWith("blob:")) {
|
|
38
|
-
return match;
|
|
39
|
-
}
|
|
40
|
-
const absolute = (() => {
|
|
41
|
-
try {
|
|
42
|
-
return new URL(trimmed, cssUrl).toString();
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
})();
|
|
48
|
-
if (!absolute) {
|
|
49
|
-
return match;
|
|
50
|
-
}
|
|
51
|
-
const dataUrl = dataUrlMap.get(absolute);
|
|
52
|
-
if (!dataUrl) {
|
|
53
|
-
return match;
|
|
54
|
-
}
|
|
55
|
-
const safeQuote = quote || "";
|
|
56
|
-
return `url(${safeQuote}${dataUrl}${safeQuote})`;
|
|
57
|
-
});
|
|
58
|
-
if (rewritten !== css) {
|
|
59
|
-
await promises_1.default.writeFile(filePath, rewritten, "utf-8");
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
exports.rewriteCssUrls = rewriteCssUrls;
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.captureNetworkRecorder = void 0;
|
|
4
|
-
const content_type_1 = require("../content-type");
|
|
5
|
-
exports.captureNetworkRecorder = {
|
|
6
|
-
id: "capture-network-recorder",
|
|
7
|
-
stage: "capture",
|
|
8
|
-
apply: async ({ page, networkRecords }) => {
|
|
9
|
-
await page.setRequestInterception(true);
|
|
10
|
-
page.on("request", (request) => {
|
|
11
|
-
request.continue().catch(() => undefined);
|
|
12
|
-
});
|
|
13
|
-
page.on("response", async (response) => {
|
|
14
|
-
const request = response.request();
|
|
15
|
-
const url = response.url();
|
|
16
|
-
const headers = response.headers();
|
|
17
|
-
const requestHeaders = request.headers();
|
|
18
|
-
const requestBody = request.postData() || "";
|
|
19
|
-
// Capture the response body while preserving encoding for replay.
|
|
20
|
-
const { responseBody, responseBodyBase64, responseEncoding, error } = await (async () => {
|
|
21
|
-
try {
|
|
22
|
-
const buffer = await response.buffer();
|
|
23
|
-
const contentType = headers["content-type"] || "";
|
|
24
|
-
if ((0, content_type_1.isTextResponse)(contentType)) {
|
|
25
|
-
return {
|
|
26
|
-
responseBody: buffer.toString("utf-8"),
|
|
27
|
-
responseBodyBase64: undefined,
|
|
28
|
-
responseEncoding: "text",
|
|
29
|
-
error: undefined
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
return {
|
|
33
|
-
responseBody: undefined,
|
|
34
|
-
responseBodyBase64: buffer.toString("base64"),
|
|
35
|
-
responseEncoding: "base64",
|
|
36
|
-
error: undefined
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
catch (err) {
|
|
40
|
-
return {
|
|
41
|
-
responseBody: undefined,
|
|
42
|
-
responseBodyBase64: undefined,
|
|
43
|
-
responseEncoding: undefined,
|
|
44
|
-
error: String(err)
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
})();
|
|
48
|
-
networkRecords.push({
|
|
49
|
-
url,
|
|
50
|
-
method: request.method(),
|
|
51
|
-
requestHeaders,
|
|
52
|
-
requestBody,
|
|
53
|
-
status: response.status(),
|
|
54
|
-
statusText: response.statusText(),
|
|
55
|
-
responseHeaders: headers,
|
|
56
|
-
responseBody,
|
|
57
|
-
responseBodyBase64,
|
|
58
|
-
responseEncoding,
|
|
59
|
-
error,
|
|
60
|
-
timestamp: Date.now()
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.replayHackers = exports.preloadHackers = void 0;
|
|
4
|
-
const preload_fetch_1 = require("./preload-fetch");
|
|
5
|
-
const preload_xhr_1 = require("./preload-xhr");
|
|
6
|
-
const replay_beacon_1 = require("./replay-beacon");
|
|
7
|
-
const replay_dom_rewrite_1 = require("./replay-dom-rewrite");
|
|
8
|
-
const replay_eventsource_1 = require("./replay-eventsource");
|
|
9
|
-
const replay_fetch_1 = require("./replay-fetch");
|
|
10
|
-
const replay_svg_image_1 = require("./replay-svg-image");
|
|
11
|
-
const replay_websocket_1 = require("./replay-websocket");
|
|
12
|
-
const replay_xhr_1 = require("./replay-xhr");
|
|
13
|
-
exports.preloadHackers = [preload_fetch_1.preloadFetchRecorder, preload_xhr_1.preloadXhrRecorder];
|
|
14
|
-
exports.replayHackers = [
|
|
15
|
-
replay_fetch_1.replayFetchResponder,
|
|
16
|
-
replay_xhr_1.replayXhrResponder,
|
|
17
|
-
replay_dom_rewrite_1.replayDomRewriter,
|
|
18
|
-
replay_svg_image_1.replaySvgImageRewriter,
|
|
19
|
-
replay_beacon_1.replayBeaconStub,
|
|
20
|
-
replay_websocket_1.replayWebSocketStub,
|
|
21
|
-
replay_eventsource_1.replayEventSourceStub
|
|
22
|
-
];
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.preloadFetchRecorder = void 0;
|
|
4
|
-
exports.preloadFetchRecorder = {
|
|
5
|
-
id: "preload-fetch-recorder",
|
|
6
|
-
stage: "preload",
|
|
7
|
-
build: () => `
|
|
8
|
-
// Record fetch calls and responses for replay.
|
|
9
|
-
const originalFetch = window.fetch.bind(window);
|
|
10
|
-
const recordFetch = async (input, init) => {
|
|
11
|
-
trackPendingStart();
|
|
12
|
-
const url = typeof input === "string" ? toAbsoluteUrl(input) : toAbsoluteUrl(input.url);
|
|
13
|
-
const method = init && init.method ? init.method : (typeof input === "string" ? "GET" : input.method || "GET");
|
|
14
|
-
const requestBody = normalizeBody(init && init.body ? init.body : (typeof input === "string" ? undefined : input.body));
|
|
15
|
-
|
|
16
|
-
try {
|
|
17
|
-
const response = await originalFetch(input, init);
|
|
18
|
-
const clone = response.clone();
|
|
19
|
-
const responseBody = await clone.text();
|
|
20
|
-
const headers = {};
|
|
21
|
-
response.headers.forEach((value, key) => {
|
|
22
|
-
headers[key] = value;
|
|
23
|
-
});
|
|
24
|
-
records.push({
|
|
25
|
-
kind: "fetch",
|
|
26
|
-
url,
|
|
27
|
-
method,
|
|
28
|
-
requestBody,
|
|
29
|
-
status: response.status,
|
|
30
|
-
statusText: response.statusText,
|
|
31
|
-
responseHeaders: headers,
|
|
32
|
-
responseBody,
|
|
33
|
-
timestamp: Date.now()
|
|
34
|
-
});
|
|
35
|
-
return response;
|
|
36
|
-
} catch (error) {
|
|
37
|
-
records.push({
|
|
38
|
-
kind: "fetch",
|
|
39
|
-
url,
|
|
40
|
-
method,
|
|
41
|
-
requestBody,
|
|
42
|
-
error: String(error),
|
|
43
|
-
timestamp: Date.now()
|
|
44
|
-
});
|
|
45
|
-
throw error;
|
|
46
|
-
} finally {
|
|
47
|
-
trackPendingEnd();
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
window.fetch = (input, init) => {
|
|
52
|
-
return recordFetch(input, init);
|
|
53
|
-
};
|
|
54
|
-
window.fetch.__pagepocketOriginal = originalFetch;
|
|
55
|
-
`
|
|
56
|
-
};
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.preloadImageRecorder = void 0;
|
|
4
|
-
exports.preloadImageRecorder = {
|
|
5
|
-
id: "preload-image-recorder",
|
|
6
|
-
stage: "preload",
|
|
7
|
-
build: () => `
|
|
8
|
-
// Intercept image src assignments and record them without triggering network requests.
|
|
9
|
-
const NativeImage = window.Image;
|
|
10
|
-
const placeholderSrc =
|
|
11
|
-
"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
|
|
12
|
-
const nativeDescriptor = Object.getOwnPropertyDescriptor(HTMLImageElement.prototype, "src");
|
|
13
|
-
const nativeSet = nativeDescriptor && nativeDescriptor.set ? nativeDescriptor.set : null;
|
|
14
|
-
const nativeGet = nativeDescriptor && nativeDescriptor.get ? nativeDescriptor.get : null;
|
|
15
|
-
|
|
16
|
-
const recordImage = (input) => {
|
|
17
|
-
if (!input) return;
|
|
18
|
-
const url = toAbsoluteUrl(input);
|
|
19
|
-
records.push({
|
|
20
|
-
kind: "image",
|
|
21
|
-
url,
|
|
22
|
-
method: "GET",
|
|
23
|
-
requestBody: "",
|
|
24
|
-
timestamp: Date.now()
|
|
25
|
-
});
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const shouldBypass = (value) => {
|
|
29
|
-
const raw = String(value || "");
|
|
30
|
-
if (!raw) return true;
|
|
31
|
-
return raw.startsWith("data:") || raw.startsWith("blob:");
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
Object.defineProperty(HTMLImageElement.prototype, "src", {
|
|
35
|
-
set(value) {
|
|
36
|
-
console.info(value)
|
|
37
|
-
|
|
38
|
-
if (shouldBypass(value)) {
|
|
39
|
-
if (nativeSet) nativeSet.call(this, value);
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
const url = toAbsoluteUrl(value);
|
|
43
|
-
this.__webechoImageSrc = url;
|
|
44
|
-
recordImage(url);
|
|
45
|
-
if (nativeSet) nativeSet.call(this, placeholderSrc);
|
|
46
|
-
},
|
|
47
|
-
get() {
|
|
48
|
-
if (this.__webechoImageSrc) return this.__webechoImageSrc;
|
|
49
|
-
return nativeGet ? nativeGet.call(this) : "";
|
|
50
|
-
},
|
|
51
|
-
configurable: true,
|
|
52
|
-
enumerable: true
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
window.Image = function(...args) {
|
|
56
|
-
return new NativeImage(...args);
|
|
57
|
-
};
|
|
58
|
-
window.Image.prototype = NativeImage.prototype;
|
|
59
|
-
Object.defineProperty(window.Image, "name", { value: "Image" });
|
|
60
|
-
`
|
|
61
|
-
};
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.preloadXhrRecorder = void 0;
|
|
4
|
-
exports.preloadXhrRecorder = {
|
|
5
|
-
id: "preload-xhr-recorder",
|
|
6
|
-
stage: "preload",
|
|
7
|
-
build: () => `
|
|
8
|
-
// Record XHR calls and responses for replay.
|
|
9
|
-
const originalOpen = XMLHttpRequest.prototype.open;
|
|
10
|
-
const originalSend = XMLHttpRequest.prototype.send;
|
|
11
|
-
|
|
12
|
-
XMLHttpRequest.prototype.open = function(method, url, ...rest) {
|
|
13
|
-
this.__pagepocketMethod = method;
|
|
14
|
-
this.__pagepocketUrl = toAbsoluteUrl(url);
|
|
15
|
-
return originalOpen.call(this, method, url, ...rest);
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
XMLHttpRequest.prototype.send = function(body) {
|
|
19
|
-
const xhr = this;
|
|
20
|
-
const requestBody = normalizeBody(body);
|
|
21
|
-
|
|
22
|
-
const onLoadEnd = () => {
|
|
23
|
-
const responseHeadersRaw = xhr.getAllResponseHeaders();
|
|
24
|
-
const headers = {};
|
|
25
|
-
responseHeadersRaw
|
|
26
|
-
.trim()
|
|
27
|
-
.split(/\\r?\\n/)
|
|
28
|
-
.filter(Boolean)
|
|
29
|
-
.forEach((line) => {
|
|
30
|
-
const index = line.indexOf(":");
|
|
31
|
-
if (index > -1) {
|
|
32
|
-
const key = line.slice(0, index).trim().toLowerCase();
|
|
33
|
-
const value = line.slice(index + 1).trim();
|
|
34
|
-
headers[key] = value;
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
records.push({
|
|
39
|
-
kind: "xhr",
|
|
40
|
-
url: xhr.__pagepocketUrl || "",
|
|
41
|
-
method: xhr.__pagepocketMethod || "GET",
|
|
42
|
-
requestBody,
|
|
43
|
-
status: xhr.status,
|
|
44
|
-
statusText: xhr.statusText,
|
|
45
|
-
responseHeaders: headers,
|
|
46
|
-
responseBody: xhr.responseText || "",
|
|
47
|
-
timestamp: Date.now()
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
xhr.removeEventListener("loadend", onLoadEnd);
|
|
51
|
-
trackPendingEnd();
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
trackPendingStart();
|
|
55
|
-
xhr.addEventListener("loadend", onLoadEnd);
|
|
56
|
-
return originalSend.call(xhr, body);
|
|
57
|
-
};
|
|
58
|
-
`
|
|
59
|
-
};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.replayBeaconStub = void 0;
|
|
4
|
-
exports.replayBeaconStub = {
|
|
5
|
-
id: "replay-beacon-stub",
|
|
6
|
-
stage: "replay",
|
|
7
|
-
build: () => `
|
|
8
|
-
// Stub beacon calls so analytics doesn't leak outside the snapshot.
|
|
9
|
-
if (navigator.sendBeacon) {
|
|
10
|
-
const originalBeacon = navigator.sendBeacon.bind(navigator);
|
|
11
|
-
navigator.sendBeacon = (url, data) => {
|
|
12
|
-
const record = findRecord("POST", url, data);
|
|
13
|
-
if (record) {
|
|
14
|
-
return true;
|
|
15
|
-
}
|
|
16
|
-
return true;
|
|
17
|
-
};
|
|
18
|
-
navigator.sendBeacon.__pagepocketOriginal = originalBeacon;
|
|
19
|
-
}
|
|
20
|
-
`
|
|
21
|
-
};
|