@link-assistant/web-capture 2.0.0 → 2.0.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/CHANGELOG.md +11 -0
- package/bin/web-capture.js +2 -5
- package/package.json +22 -11
- package/src/archive.js +0 -1
- package/src/binary-download.js +0 -1
- package/src/docx.js +13 -10
- package/src/fetch.js +0 -1
- package/src/gdocs-images.js +0 -1
- package/src/gdocs.js +0 -1
- package/src/github.js +0 -1
- package/src/lib.js +0 -1
- package/src/markdown.js +0 -1
- package/src/shared-dialog.js +0 -1
- package/src/stream.js +0 -1
- package/src/transport.js +23 -5
- package/src/txt.js +0 -1
- package/.changeset/README.md +0 -8
- package/.changeset/config.json +0 -11
- package/.dockerignore +0 -6
- package/.jscpd.json +0 -19
- package/.lenv +0 -8
- package/.prettierignore +0 -8
- package/.prettierrc +0 -10
- package/Dockerfile +0 -52
- package/docker-compose.yml +0 -11
- package/eslint.config.js +0 -112
- package/examples/js/capture_cache.js +0 -12
- package/examples/js/engine_comparison.js +0 -39
- package/examples/js/html_download.js +0 -23
- package/examples/js/image_download.js +0 -36
- package/examples/js/markdown_download.js +0 -23
- package/examples/js/playwright_example.js +0 -23
- package/examples/python/capture_client.py +0 -20
- package/examples/python/html_download.py +0 -3
- package/examples/python/image_download.py +0 -16
- package/examples/python/markdown_download.py +0 -3
- package/examples/python/playwright_example.py +0 -22
- package/jest.config.mjs +0 -17
- package/tests/ci/dependency-freshness.test.js +0 -74
- package/tests/ci/detect-code-changes.test.js +0 -187
- package/tests/ci/docker-install.test.js +0 -31
- package/tests/ci/rust-dockerfile.test.js +0 -54
- package/tests/ci/workflow-policy.test.js +0 -79
- package/tests/e2e/docker.test.js +0 -186
- package/tests/e2e/process.test.js +0 -215
- package/tests/fixtures/bold-coalesce-and-leakage.html +0 -27
- package/tests/fixtures/deepwiki/deepwiki-full-page.png +0 -0
- package/tests/fixtures/deepwiki/index.md +0 -1500
- package/tests/fixtures/deepwiki/part-1.md +0 -776
- package/tests/fixtures/habr-articles.js +0 -45
- package/tests/fixtures/heading-numbering.html +0 -19
- package/tests/fixtures/list-item-with-br.html +0 -8
- package/tests/fixtures/ol-continuous-numbering.html +0 -12
- package/tests/fixtures/paragraph-vs-line-break.html +0 -11
- package/tests/helpers/nock.js +0 -10
- package/tests/helpers/screenshot.js +0 -10
- package/tests/helpers/search-fixture.cjs +0 -17
- package/tests/helpers/temporary-directory.js +0 -12
- package/tests/integration/api-endpoints.test.js +0 -418
- package/tests/integration/archive-default-layout.test.js +0 -37
- package/tests/integration/browser-engines.test.js +0 -80
- package/tests/integration/browser-packaged-path.test.js +0 -45
- package/tests/integration/deepwiki-download.test.js +0 -144
- package/tests/integration/deepwiki-rendered-page.mock.test.js +0 -121
- package/tests/integration/formalai-contract.test.js +0 -214
- package/tests/integration/gdocs-public-doc.test.js +0 -315
- package/tests/integration/github-readme.test.js +0 -133
- package/tests/integration/google-drive-image.test.js +0 -246
- package/tests/integration/habr-article.test.js +0 -337
- package/tests/integration/search-endpoint.test.js +0 -88
- package/tests/integration/stackoverflow-download.test.js +0 -173
- package/tests/integration/wikipedia-download.test.js +0 -134
- package/tests/jest.setup.mjs +0 -7
- package/tests/mock/index.test.js +0 -225
- package/tests/unit/animation.test.js +0 -33
- package/tests/unit/batch.test.js +0 -114
- package/tests/unit/bold-coalesce-and-leakage.test.js +0 -54
- package/tests/unit/browser-launch-options.test.js +0 -42
- package/tests/unit/browser.test.js +0 -172
- package/tests/unit/cache.test.js +0 -182
- package/tests/unit/cli.test.js +0 -399
- package/tests/unit/convertGoogleDriveUrl.test.js +0 -89
- package/tests/unit/convertRelativeUrls.test.js +0 -34
- package/tests/unit/extract-images.test.js +0 -259
- package/tests/unit/fetch-html.test.js +0 -54
- package/tests/unit/figures.test.js +0 -74
- package/tests/unit/gdocs-export-headers.test.js +0 -28
- package/tests/unit/gdocs-preprocess.test.js +0 -36
- package/tests/unit/gdocs.test.js +0 -1490
- package/tests/unit/github.test.js +0 -156
- package/tests/unit/heading-image.test.js +0 -49
- package/tests/unit/heading-numbering.test.js +0 -28
- package/tests/unit/html2md-br-in-list-item.test.js +0 -23
- package/tests/unit/html2md-ol-numbering.test.js +0 -20
- package/tests/unit/html2md.test.js +0 -695
- package/tests/unit/image-mode-defaults.test.js +0 -54
- package/tests/unit/kreuzberg.test.js +0 -249
- package/tests/unit/latex.test.js +0 -123
- package/tests/unit/localize-images.test.js +0 -114
- package/tests/unit/markdown.test.js +0 -36
- package/tests/unit/metadata.test.js +0 -140
- package/tests/unit/package-runtime-dependencies.test.js +0 -74
- package/tests/unit/paragraph-vs-line-break.test.js +0 -56
- package/tests/unit/postprocess.test.js +0 -110
- package/tests/unit/proxy-headers.test.js +0 -100
- package/tests/unit/publish-verification.test.js +0 -54
- package/tests/unit/retry.test.js +0 -111
- package/tests/unit/search.test.js +0 -339
- package/tests/unit/shared-dialog.test.js +0 -173
- package/tests/unit/stackoverflow.test.js +0 -32
- package/tests/unit/stream.test.js +0 -83
- package/tests/unit/verify.test.js +0 -182
- package/tests/xpaste/xpaste-screenshot-markdown.mock.test.js +0 -375
- package/tests/xpaste/xpaste.e2e.test.js +0 -36
- package/tests/xpaste/xpaste.integration.test.js +0 -41
- package/tests/xpaste/xpaste.mock.test.js +0 -261
- package/tests/xpaste/xpaste.unit.test.js +0 -119
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @link-assistant/web-capture
|
|
2
2
|
|
|
3
|
+
## 2.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 724c1a9: Fix CI/CD false positives, false negatives, and warnings (#177):
|
|
8
|
+
|
|
9
|
+
- Restore the `browser-commander` 0.10.0 pin required by open blocker #160 and repair the npm lock so `npm ci` installs again after the Dependabot update.
|
|
10
|
+
- Use Node's built-in `fetch` instead of `node-fetch`, removing the deprecated `node-domexception` package from the install. Transport diagnostics now include the underlying network error (for example `fetch failed: connect ECONNREFUSED ...`) and no longer report server-side network failures as possible CORS blocks.
|
|
11
|
+
- Load the `docx` library only when a DOCX is generated. Its bundled browser polyfills read `globalThis.localStorage` on import, so on Node 25+ every process that imported the server printed "ExperimentalWarning: localStorage is not available because --localstorage-file was not provided".
|
|
12
|
+
- Publish only the runtime files (`bin/`, `src/`, `README.md`, `CHANGELOG.md`) instead of the tests and fixtures, normalize `bin` and `repository` so `npm publish` no longer auto-corrects them, and install the pre-commit hook from the repository root instead of printing ".git can't be found" on every install.
|
|
13
|
+
|
|
3
14
|
## 2.0.0
|
|
4
15
|
|
|
5
16
|
### Major Changes
|
package/bin/web-capture.js
CHANGED
|
@@ -1110,11 +1110,9 @@ async function captureUrl(url, options) {
|
|
|
1110
1110
|
await import('../src/lib.js');
|
|
1111
1111
|
const { retry } = await import('../src/retry.js');
|
|
1112
1112
|
const cheerio = await import('cheerio');
|
|
1113
|
-
const nodeFetch = await import('node-fetch');
|
|
1114
1113
|
|
|
1114
|
+
// retry() defaults: 3 retries from a 1000ms base delay.
|
|
1115
1115
|
const html = await retry(() => fetchHtml(absoluteUrl), {
|
|
1116
|
-
retries: 3,
|
|
1117
|
-
baseDelay: 1000,
|
|
1118
1116
|
onRetry: (err, attempt) => {
|
|
1119
1117
|
console.error(`Retry ${attempt} fetching page: ${err.message}`);
|
|
1120
1118
|
},
|
|
@@ -1207,8 +1205,7 @@ async function captureUrl(url, options) {
|
|
|
1207
1205
|
if (imageMap.size > 0) {
|
|
1208
1206
|
for (const [imgUrl, localPath] of imageMap) {
|
|
1209
1207
|
try {
|
|
1210
|
-
const
|
|
1211
|
-
const resp = await retry(() => fetchFn(imgUrl), {
|
|
1208
|
+
const resp = await retry(() => fetch(imgUrl), {
|
|
1212
1209
|
retries: 2,
|
|
1213
1210
|
baseDelay: 500,
|
|
1214
1211
|
});
|
package/package.json
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@link-assistant/web-capture",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "CLI and microservice to render web pages as HTML, Markdown, or PNG",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"web-capture": "
|
|
7
|
+
"web-capture": "bin/web-capture.js"
|
|
8
8
|
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"src/",
|
|
12
|
+
"CHANGELOG.md",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
9
15
|
"scripts": {
|
|
10
16
|
"start": "docker compose up -d",
|
|
11
17
|
"dev": "node --watch bin/web-capture.js --serve",
|
|
@@ -19,13 +25,13 @@
|
|
|
19
25
|
"examples:python": "uv run --project .. --locked python examples/python/html_download.py && uv run --project .. --locked python examples/python/markdown_download.py && uv run --project .. --locked python examples/python/image_download.py",
|
|
20
26
|
"examples:javascript": "node examples/js/html_download.js && node examples/js/markdown_download.js && node examples/js/image_download.js",
|
|
21
27
|
"examples": "npm run build && npm run examples:javascript && npm run examples:python",
|
|
22
|
-
"lint": "
|
|
28
|
+
"lint": "node ../scripts/lint.mjs",
|
|
23
29
|
"lint:fix": "eslint . --fix",
|
|
24
30
|
"format": "prettier --write .",
|
|
25
31
|
"format:check": "prettier --check .",
|
|
26
32
|
"check:duplication": "jscpd . --baseline-from-ref origin/main --fail-on-new-clones --fail-on-empty",
|
|
27
33
|
"check": "npm run lint && npm run format:check && npm run check:duplication",
|
|
28
|
-
"prepare": "
|
|
34
|
+
"prepare": "node ../scripts/install-git-hooks.mjs",
|
|
29
35
|
"changeset": "changeset",
|
|
30
36
|
"changeset:version": "node ../scripts/changeset-version.mjs",
|
|
31
37
|
"changeset:publish": "changeset publish",
|
|
@@ -42,15 +48,15 @@
|
|
|
42
48
|
"iconv-lite": "^0.7.3",
|
|
43
49
|
"lino-arguments": "^0.3.0",
|
|
44
50
|
"log-lazy": "^1.0.4",
|
|
45
|
-
"node-fetch": "^3.3.2",
|
|
46
51
|
"playwright": "^1.64.0",
|
|
47
|
-
"puppeteer": "^25.
|
|
48
|
-
"puppeteer-core": "^25.
|
|
52
|
+
"puppeteer": "^25.13.0",
|
|
53
|
+
"puppeteer-core": "^25.13.0",
|
|
49
54
|
"turndown": "^7.2.4",
|
|
50
55
|
"turndown-plugin-gfm": "^1.0.2"
|
|
51
56
|
},
|
|
52
57
|
"devDependencies": {
|
|
53
58
|
"@changesets/cli": "^3.0.3",
|
|
59
|
+
"@eslint/js": "^10.0.1",
|
|
54
60
|
"@types/archiver": "^8.0.0",
|
|
55
61
|
"eslint": "^10.12.0",
|
|
56
62
|
"eslint-config-prettier": "^10.1.8",
|
|
@@ -64,8 +70,7 @@
|
|
|
64
70
|
"nock": "^15.0.1",
|
|
65
71
|
"prettier": "^3.9.9",
|
|
66
72
|
"supertest": "^7.3.1",
|
|
67
|
-
"unzipper": "^0.12.5"
|
|
68
|
-
"@eslint/js": "^10.0.1"
|
|
73
|
+
"unzipper": "^0.12.5"
|
|
69
74
|
},
|
|
70
75
|
"type": "module",
|
|
71
76
|
"keywords": [
|
|
@@ -77,14 +82,14 @@
|
|
|
77
82
|
"license": "Unlicense",
|
|
78
83
|
"repository": {
|
|
79
84
|
"type": "git",
|
|
80
|
-
"url": "https://github.com/link-assistant/web-capture"
|
|
85
|
+
"url": "git+https://github.com/link-assistant/web-capture.git"
|
|
81
86
|
},
|
|
82
87
|
"engines": {
|
|
83
88
|
"node": ">=26.10.0"
|
|
84
89
|
},
|
|
85
90
|
"lint-staged": {
|
|
86
91
|
"*.{js,mjs,cjs}": [
|
|
87
|
-
"eslint --fix --
|
|
92
|
+
"eslint --fix --no-warn-ignored",
|
|
88
93
|
"prettier --write",
|
|
89
94
|
"prettier --check"
|
|
90
95
|
],
|
|
@@ -102,6 +107,12 @@
|
|
|
102
107
|
"overrides": {
|
|
103
108
|
"@istanbuljs/load-nyc-config": {
|
|
104
109
|
"js-yaml": "^4.1.1"
|
|
110
|
+
},
|
|
111
|
+
"cheerio": {
|
|
112
|
+
"encoding-sniffer": "^1.0.2"
|
|
113
|
+
},
|
|
114
|
+
"babel-plugin-istanbul": {
|
|
115
|
+
"test-exclude": "^8.0.0"
|
|
105
116
|
}
|
|
106
117
|
},
|
|
107
118
|
"packageManager": "npm@12.2.0",
|
package/src/archive.js
CHANGED
package/src/binary-download.js
CHANGED
package/src/docx.js
CHANGED
|
@@ -9,18 +9,8 @@
|
|
|
9
9
|
* engine - 'puppeteer' or 'playwright' (only used for browser rendering fallback)
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import fetch from 'node-fetch';
|
|
13
12
|
import * as cheerio from 'cheerio';
|
|
14
13
|
import { URL } from 'url';
|
|
15
|
-
import {
|
|
16
|
-
Document,
|
|
17
|
-
Packer,
|
|
18
|
-
Paragraph,
|
|
19
|
-
TextRun,
|
|
20
|
-
HeadingLevel,
|
|
21
|
-
ImageRun,
|
|
22
|
-
ExternalHyperlink,
|
|
23
|
-
} from 'docx';
|
|
24
14
|
import { fetchHtml, convertRelativeUrls } from './lib.js';
|
|
25
15
|
|
|
26
16
|
export async function docxHandler(req, res) {
|
|
@@ -35,6 +25,19 @@ export async function docxHandler(req, res) {
|
|
|
35
25
|
const absHtml = convertRelativeUrls(html, absoluteUrl);
|
|
36
26
|
const $ = cheerio.load(absHtml);
|
|
37
27
|
|
|
28
|
+
// Loaded on demand: docx bundles a browser util-deprecate that reads
|
|
29
|
+
// globalThis.localStorage on import, which makes Node >=25 print an
|
|
30
|
+
// ExperimentalWarning for every process that merely imports the app.
|
|
31
|
+
const {
|
|
32
|
+
Document,
|
|
33
|
+
Packer,
|
|
34
|
+
Paragraph,
|
|
35
|
+
TextRun,
|
|
36
|
+
HeadingLevel,
|
|
37
|
+
ImageRun,
|
|
38
|
+
ExternalHyperlink,
|
|
39
|
+
} = await import('docx');
|
|
40
|
+
|
|
38
41
|
// Remove unwanted elements
|
|
39
42
|
$('style, script, noscript, nav, footer, header').remove();
|
|
40
43
|
|
package/src/fetch.js
CHANGED
package/src/gdocs-images.js
CHANGED
package/src/gdocs.js
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
// https://docs.googleapis.com/v1/documents/{DOCUMENT_ID}
|
|
8
8
|
// Browser-model capture loads /edit and extracts DOCS_modelChunk data.
|
|
9
9
|
|
|
10
|
-
import fetch from 'node-fetch';
|
|
11
10
|
import * as he from 'he';
|
|
12
11
|
import * as cheerio from 'cheerio';
|
|
13
12
|
import { convertHtmlToMarkdown } from './lib.js';
|
package/src/github.js
CHANGED
package/src/lib.js
CHANGED
package/src/markdown.js
CHANGED
|
@@ -18,7 +18,6 @@ import { applyImageMode } from './extract-images.js';
|
|
|
18
18
|
import { createBrowser, getBrowserEngine } from './browser.js';
|
|
19
19
|
import { retry } from './retry.js';
|
|
20
20
|
import { ZipArchive } from 'archiver';
|
|
21
|
-
import fetch from 'node-fetch';
|
|
22
21
|
import * as cheerio from 'cheerio';
|
|
23
22
|
|
|
24
23
|
const INLINE_MARKDOWN_LINE_LIMIT = 1500;
|
package/src/shared-dialog.js
CHANGED
package/src/stream.js
CHANGED
package/src/transport.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import fetch from 'node-fetch';
|
|
2
|
-
|
|
3
1
|
export const RECEIPT_HEADERS = [
|
|
4
2
|
'cache-control',
|
|
5
3
|
'content-encoding',
|
|
@@ -22,22 +20,42 @@ function selectedHeaders(headers) {
|
|
|
22
20
|
return selected;
|
|
23
21
|
}
|
|
24
22
|
|
|
23
|
+
const TIMEOUT_CAUSE_CODES = new Set([
|
|
24
|
+
'ETIMEDOUT',
|
|
25
|
+
'UND_ERR_CONNECT_TIMEOUT',
|
|
26
|
+
'UND_ERR_HEADERS_TIMEOUT',
|
|
27
|
+
'UND_ERR_BODY_TIMEOUT',
|
|
28
|
+
]);
|
|
29
|
+
|
|
25
30
|
function classifyTransportError(error, signal) {
|
|
26
31
|
if (signal?.aborted || error?.name === 'AbortError') {
|
|
27
32
|
return 'cancelled';
|
|
28
33
|
}
|
|
29
|
-
if (
|
|
34
|
+
if (
|
|
35
|
+
error?.name === 'TimeoutError' ||
|
|
36
|
+
TIMEOUT_CAUSE_CODES.has(error?.cause?.code)
|
|
37
|
+
) {
|
|
30
38
|
return 'timeout';
|
|
31
39
|
}
|
|
32
|
-
|
|
40
|
+
// Node's built-in fetch rejects every network failure with
|
|
41
|
+
// TypeError('fetch failed') and the real reason in `cause`. Browsers give a
|
|
42
|
+
// cause-less TypeError for CORS and network errors alike.
|
|
43
|
+
if (error?.name === 'TypeError' && !error.cause) {
|
|
33
44
|
return 'cors_or_transport';
|
|
34
45
|
}
|
|
35
46
|
return 'transport';
|
|
36
47
|
}
|
|
37
48
|
|
|
49
|
+
/** Message of `error` including its `cause`, e.g. "fetch failed: getaddrinfo ENOTFOUND host". */
|
|
50
|
+
export function describeTransportError(error) {
|
|
51
|
+
const message = error?.message || String(error);
|
|
52
|
+
const cause = error?.cause?.message;
|
|
53
|
+
return cause && !message.includes(cause) ? `${message}: ${cause}` : message;
|
|
54
|
+
}
|
|
55
|
+
|
|
38
56
|
export class CaptureTransportError extends Error {
|
|
39
57
|
constructor(error, url, signal) {
|
|
40
|
-
super(
|
|
58
|
+
super(describeTransportError(error));
|
|
41
59
|
this.name = 'CaptureTransportError';
|
|
42
60
|
this.cause = error;
|
|
43
61
|
this.diagnostics = {
|
package/src/txt.js
CHANGED
package/.changeset/README.md
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
# Changesets
|
|
2
|
-
|
|
3
|
-
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
|
4
|
-
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
|
5
|
-
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
|
6
|
-
|
|
7
|
-
We have a quick list of common questions to get you started engaging with this project in
|
|
8
|
-
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
package/.changeset/config.json
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "../node_modules/@changesets/config/schema.json",
|
|
3
|
-
"changelog": "@changesets/cli/changelog",
|
|
4
|
-
"commit": false,
|
|
5
|
-
"fixed": [],
|
|
6
|
-
"linked": [],
|
|
7
|
-
"access": "public",
|
|
8
|
-
"baseBranch": "main",
|
|
9
|
-
"updateInternalDependencies": "patch",
|
|
10
|
-
"ignore": []
|
|
11
|
-
}
|
package/.dockerignore
DELETED
package/.jscpd.json
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"minTokens": 30,
|
|
3
|
-
"minLines": 5,
|
|
4
|
-
"ignore": [
|
|
5
|
-
"**/node_modules/**",
|
|
6
|
-
"**/build/**",
|
|
7
|
-
"**/dist/**",
|
|
8
|
-
"**/*.min.js",
|
|
9
|
-
"**/coverage/**",
|
|
10
|
-
"**/.changeset/**",
|
|
11
|
-
"**/package-lock.json",
|
|
12
|
-
"**/pnpm-lock.yaml",
|
|
13
|
-
"**/yarn.lock",
|
|
14
|
-
"**/reports/**"
|
|
15
|
-
],
|
|
16
|
-
"reporters": ["console", "html"],
|
|
17
|
-
"output": "./reports/jscpd",
|
|
18
|
-
"mode": "weak"
|
|
19
|
-
}
|
package/.lenv
DELETED
package/.prettierignore
DELETED
package/.prettierrc
DELETED
package/Dockerfile
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
FROM node:26.10.0-trixie
|
|
2
|
-
|
|
3
|
-
WORKDIR /app
|
|
4
|
-
|
|
5
|
-
# Install dependencies for headless Chrome (used by both Puppeteer and Playwright)
|
|
6
|
-
# Retry loop handles transient network errors from Debian mirrors in CI
|
|
7
|
-
RUN apt-get update && \
|
|
8
|
-
for i in 1 2 3; do \
|
|
9
|
-
apt-get install -y \
|
|
10
|
-
chromium \
|
|
11
|
-
fonts-liberation \
|
|
12
|
-
libasound2t64 \
|
|
13
|
-
libatk-bridge2.0-0t64 \
|
|
14
|
-
libatk1.0-0t64 \
|
|
15
|
-
libatspi2.0-0t64 \
|
|
16
|
-
libcups2t64 \
|
|
17
|
-
libdbus-1-3 \
|
|
18
|
-
libdrm2 \
|
|
19
|
-
libgbm1 \
|
|
20
|
-
libgtk-3-0t64 \
|
|
21
|
-
libnspr4 \
|
|
22
|
-
libnss3 \
|
|
23
|
-
libxcomposite1 \
|
|
24
|
-
libxdamage1 \
|
|
25
|
-
libxfixes3 \
|
|
26
|
-
libxrandr2 \
|
|
27
|
-
libxshmfence1 \
|
|
28
|
-
xdg-utils \
|
|
29
|
-
--no-install-recommends && break || \
|
|
30
|
-
{ if [ "$i" -eq 3 ]; then exit 1; fi; \
|
|
31
|
-
echo "apt-get install attempt $i failed, retrying..."; \
|
|
32
|
-
apt-get update && sleep 5; }; \
|
|
33
|
-
done && \
|
|
34
|
-
rm -rf /var/lib/apt/lists/*
|
|
35
|
-
|
|
36
|
-
# Set Puppeteer environment variables
|
|
37
|
-
ENV PUPPETEER_SKIP_DOWNLOAD=true
|
|
38
|
-
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
|
|
39
|
-
ENV PUPPETEER_ARGS="--no-sandbox --disable-setuid-sandbox --disable-dev-shm-usage"
|
|
40
|
-
|
|
41
|
-
# Set Playwright environment variables
|
|
42
|
-
ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
|
|
43
|
-
ENV PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/chromium
|
|
44
|
-
|
|
45
|
-
COPY package.json package-lock.json ./
|
|
46
|
-
RUN npm install --global npm@12.2.0 && npm ci
|
|
47
|
-
|
|
48
|
-
COPY . .
|
|
49
|
-
|
|
50
|
-
EXPOSE 3000
|
|
51
|
-
|
|
52
|
-
ENTRYPOINT ["node", "bin/web-capture.js", "--serve"]
|
package/docker-compose.yml
DELETED
package/eslint.config.js
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import js from '@eslint/js';
|
|
2
|
-
import prettierConfig from 'eslint-config-prettier';
|
|
3
|
-
import prettierPlugin from 'eslint-plugin-prettier';
|
|
4
|
-
|
|
5
|
-
export default [
|
|
6
|
-
js.configs.recommended,
|
|
7
|
-
prettierConfig,
|
|
8
|
-
{
|
|
9
|
-
files: ['**/*.js', '**/*.mjs'],
|
|
10
|
-
plugins: {
|
|
11
|
-
prettier: prettierPlugin,
|
|
12
|
-
},
|
|
13
|
-
languageOptions: {
|
|
14
|
-
ecmaVersion: 'latest',
|
|
15
|
-
sourceType: 'module',
|
|
16
|
-
globals: {
|
|
17
|
-
// Node.js globals
|
|
18
|
-
console: 'readonly',
|
|
19
|
-
process: 'readonly',
|
|
20
|
-
Buffer: 'readonly',
|
|
21
|
-
__dirname: 'readonly',
|
|
22
|
-
__filename: 'readonly',
|
|
23
|
-
global: 'readonly',
|
|
24
|
-
setTimeout: 'readonly',
|
|
25
|
-
clearTimeout: 'readonly',
|
|
26
|
-
setInterval: 'readonly',
|
|
27
|
-
clearInterval: 'readonly',
|
|
28
|
-
// Node.js 18+ globals
|
|
29
|
-
fetch: 'readonly',
|
|
30
|
-
// Runtime-specific globals
|
|
31
|
-
Bun: 'readonly',
|
|
32
|
-
Deno: 'readonly',
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
rules: {
|
|
36
|
-
// Prettier integration
|
|
37
|
-
'prettier/prettier': 'error',
|
|
38
|
-
|
|
39
|
-
// Code quality rules
|
|
40
|
-
'no-unused-vars': 'error',
|
|
41
|
-
'no-console': 'off', // Allow console in this project
|
|
42
|
-
'no-debugger': 'error',
|
|
43
|
-
|
|
44
|
-
// Best practices
|
|
45
|
-
eqeqeq: ['error', 'always'],
|
|
46
|
-
curly: ['error', 'all'],
|
|
47
|
-
'no-var': 'error',
|
|
48
|
-
'prefer-const': 'error',
|
|
49
|
-
'prefer-arrow-callback': 'error',
|
|
50
|
-
'no-duplicate-imports': 'error',
|
|
51
|
-
|
|
52
|
-
// ES6+ features
|
|
53
|
-
'arrow-body-style': ['error', 'as-needed'],
|
|
54
|
-
'object-shorthand': ['error', 'always'],
|
|
55
|
-
'prefer-template': 'error',
|
|
56
|
-
|
|
57
|
-
// Async/await
|
|
58
|
-
'no-async-promise-executor': 'error',
|
|
59
|
-
'require-await': 'warn',
|
|
60
|
-
|
|
61
|
-
// Comments and documentation
|
|
62
|
-
'spaced-comment': ['error', 'always', { markers: ['/'] }],
|
|
63
|
-
|
|
64
|
-
// Complexity rules - reasonable thresholds for maintainability
|
|
65
|
-
complexity: ['warn', 15], // Cyclomatic complexity - allow more complex logic than strict 8
|
|
66
|
-
'max-depth': ['warn', 5], // Maximum nesting depth - slightly more lenient than strict 4
|
|
67
|
-
'max-lines-per-function': [
|
|
68
|
-
'warn',
|
|
69
|
-
{
|
|
70
|
-
max: 150, // More reasonable than strict 50 lines per function
|
|
71
|
-
skipBlankLines: true,
|
|
72
|
-
skipComments: true,
|
|
73
|
-
},
|
|
74
|
-
],
|
|
75
|
-
'max-params': ['warn', 6], // Maximum function parameters - slightly more lenient than strict 5
|
|
76
|
-
'max-statements': ['warn', 60], // Maximum statements per function - reasonable limit for orchestration functions
|
|
77
|
-
'max-lines': ['error', 1500], // Maximum lines per file - counts all lines including blank lines and comments
|
|
78
|
-
},
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
// Test files have different requirements
|
|
82
|
-
files: ['tests/**/*.js', '**/*.test.js', '**/*.test.mjs'],
|
|
83
|
-
languageOptions: {
|
|
84
|
-
globals: {
|
|
85
|
-
// Jest globals
|
|
86
|
-
describe: 'readonly',
|
|
87
|
-
it: 'readonly',
|
|
88
|
-
test: 'readonly',
|
|
89
|
-
expect: 'readonly',
|
|
90
|
-
beforeAll: 'readonly',
|
|
91
|
-
afterAll: 'readonly',
|
|
92
|
-
beforeEach: 'readonly',
|
|
93
|
-
afterEach: 'readonly',
|
|
94
|
-
jest: 'readonly',
|
|
95
|
-
},
|
|
96
|
-
},
|
|
97
|
-
rules: {
|
|
98
|
-
'require-await': 'off', // Async functions without await are common in tests
|
|
99
|
-
'max-lines-per-function': 'off', // Test files can have long test functions
|
|
100
|
-
'max-statements': 'off', // Test files can have many statements
|
|
101
|
-
},
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
ignores: [
|
|
105
|
-
'node_modules/**',
|
|
106
|
-
'coverage/**',
|
|
107
|
-
'dist/**',
|
|
108
|
-
'*.min.js',
|
|
109
|
-
'.eslintcache',
|
|
110
|
-
],
|
|
111
|
-
},
|
|
112
|
-
];
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { CaptureStore, CachedTransport } from '../../src/cache.js';
|
|
2
|
-
import { fetchTransport } from '../../src/transport.js';
|
|
3
|
-
|
|
4
|
-
const store = new CaptureStore('.captures');
|
|
5
|
-
const request = { url: 'https://example.com', method: 'GET', headers: {} };
|
|
6
|
-
const online = new CachedTransport(fetchTransport, store);
|
|
7
|
-
const first = await online.capture(request);
|
|
8
|
-
const offline = new CachedTransport(fetchTransport, store, { offline: true });
|
|
9
|
-
const replay = await offline.capture(request);
|
|
10
|
-
console.log(replay.sha256, replay.cached, replay.stale);
|
|
11
|
-
console.log('Exact replay:', first.receipt.body.equals(replay.receipt.body));
|
|
12
|
-
console.log('Portable fixture:', await store.export(request));
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import fetch from 'node-fetch';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
|
|
4
|
-
const baseUrl = 'http://localhost:3000';
|
|
5
|
-
const targetUrl = 'https://example.com';
|
|
6
|
-
|
|
7
|
-
console.log('Comparing Puppeteer and Playwright engines...\n');
|
|
8
|
-
|
|
9
|
-
// Fetch using Puppeteer
|
|
10
|
-
console.log('1. Fetching HTML using Puppeteer (default)...');
|
|
11
|
-
const startPuppeteer = Date.now();
|
|
12
|
-
const puppeteerResponse = await fetch(
|
|
13
|
-
`${baseUrl}/html?url=${encodeURIComponent(targetUrl)}`
|
|
14
|
-
);
|
|
15
|
-
const puppeteerHtml = await puppeteerResponse.text();
|
|
16
|
-
const puppeteerTime = Date.now() - startPuppeteer;
|
|
17
|
-
fs.writeFileSync('output/puppeteer_comparison.html', puppeteerHtml);
|
|
18
|
-
console.log(` Time: ${puppeteerTime}ms`);
|
|
19
|
-
console.log(` Size: ${puppeteerHtml.length} bytes`);
|
|
20
|
-
|
|
21
|
-
// Fetch using Playwright
|
|
22
|
-
console.log('\n2. Fetching HTML using Playwright...');
|
|
23
|
-
const startPlaywright = Date.now();
|
|
24
|
-
const playwrightResponse = await fetch(
|
|
25
|
-
`${baseUrl}/html?url=${encodeURIComponent(targetUrl)}&engine=playwright`
|
|
26
|
-
);
|
|
27
|
-
const playwrightHtml = await playwrightResponse.text();
|
|
28
|
-
const playwrightTime = Date.now() - startPlaywright;
|
|
29
|
-
fs.writeFileSync('output/playwright_comparison.html', playwrightHtml);
|
|
30
|
-
console.log(` Time: ${playwrightTime}ms`);
|
|
31
|
-
console.log(` Size: ${playwrightHtml.length} bytes`);
|
|
32
|
-
|
|
33
|
-
// Summary
|
|
34
|
-
console.log('\n=== Summary ===');
|
|
35
|
-
console.log(`Puppeteer: ${puppeteerTime}ms, ${puppeteerHtml.length} bytes`);
|
|
36
|
-
console.log(`Playwright: ${playwrightTime}ms, ${playwrightHtml.length} bytes`);
|
|
37
|
-
console.log(`Difference: ${Math.abs(puppeteerTime - playwrightTime)}ms`);
|
|
38
|
-
|
|
39
|
-
console.log('\nResults saved to output/ directory.');
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
|
|
5
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
const url = process.argv[2] || 'https://example.com';
|
|
7
|
-
const endpoint = `http://localhost:3000/html?url=${encodeURIComponent(url)}`;
|
|
8
|
-
|
|
9
|
-
fetch(endpoint)
|
|
10
|
-
.then((res) => {
|
|
11
|
-
if (!res.ok) {
|
|
12
|
-
throw new Error(`HTTP error! status: ${res.status}`);
|
|
13
|
-
}
|
|
14
|
-
return res.text();
|
|
15
|
-
})
|
|
16
|
-
.then((text) => {
|
|
17
|
-
const outputPath = path.join(__dirname, 'downloaded.html');
|
|
18
|
-
fs.writeFileSync(outputPath, text, 'utf-8');
|
|
19
|
-
console.log(`HTML saved to ${outputPath}`);
|
|
20
|
-
})
|
|
21
|
-
.catch((err) => {
|
|
22
|
-
console.error('Error:', err);
|
|
23
|
-
});
|