@link-assistant/web-capture 1.1.2 → 1.7.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 +57 -13
- package/Dockerfile +26 -21
- package/README.md +290 -160
- package/bin/web-capture.js +622 -17
- package/package.json +15 -4
- package/src/animation.js +283 -0
- package/src/archive.js +225 -0
- package/src/batch.js +180 -0
- package/src/browser.js +150 -111
- package/src/docx.js +182 -0
- package/src/extract-images.js +120 -0
- package/src/figures.js +208 -0
- package/src/gdocs.js +335 -0
- package/src/image.js +65 -8
- package/src/index.js +15 -0
- package/src/latex.js +127 -0
- package/src/lib.js +281 -1
- package/src/localize-images.js +202 -0
- package/src/markdown.js +7 -2
- package/src/metadata.js +333 -0
- package/src/pdf.js +76 -0
- package/src/popups.js +171 -0
- package/src/postprocess.js +263 -0
- package/src/retry.js +48 -0
- package/src/themed-image.js +167 -0
- package/src/verify.js +312 -0
- package/tests/e2e/docker.test.js +51 -12
- package/tests/e2e/process.test.js +34 -10
- package/tests/fixtures/habr-articles.js +45 -0
- package/tests/integration/api-endpoints.test.js +231 -0
- package/tests/integration/habr-article.test.js +337 -0
- package/tests/mock/index.test.js +5 -3
- package/tests/unit/animation.test.js +33 -0
- package/tests/unit/batch.test.js +114 -0
- package/tests/unit/browser.test.js +38 -2
- package/tests/unit/extract-images.test.js +233 -0
- package/tests/unit/figures.test.js +74 -0
- package/tests/unit/gdocs.test.js +278 -0
- package/tests/unit/heading-image.test.js +49 -0
- package/tests/unit/latex.test.js +123 -0
- package/tests/unit/localize-images.test.js +78 -0
- package/tests/unit/metadata.test.js +140 -0
- package/tests/unit/postprocess.test.js +110 -0
- package/tests/unit/retry.test.js +111 -0
- package/tests/unit/verify.test.js +182 -0
- package/.github/workflows/release.yml +0 -294
- package/ARCHITECTURE.md +0 -982
- package/LICENSE +0 -24
- package/apply.sh +0 -166
- package/docs/market-analysis-2025-05-13.md +0 -64
- package/scripts/changeset-version.mjs +0 -38
- package/scripts/create-github-release.mjs +0 -93
- package/scripts/create-manual-changeset.mjs +0 -88
- package/scripts/format-github-release.mjs +0 -83
- package/scripts/format-release-notes.mjs +0 -218
- package/scripts/instant-version-bump.mjs +0 -121
- package/scripts/publish-to-npm.mjs +0 -128
- package/scripts/setup-npm.mjs +0 -37
- package/scripts/validate-changeset.mjs +0 -106
- package/scripts/version-and-commit.mjs +0 -237
package/ARCHITECTURE.md
DELETED
|
@@ -1,982 +0,0 @@
|
|
|
1
|
-
# Web Capture Architecture
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
Web Capture is a lightweight microservice designed to fetch web pages and render them in multiple formats: HTML, Markdown, and PNG screenshots. It supports both Puppeteer and Playwright browser engines for headless browser automation to handle JavaScript-heavy websites and provides clean, processed output suitable for AI/LLM consumption, documentation, and archival purposes.
|
|
6
|
-
|
|
7
|
-
**Version**: 1.0.0
|
|
8
|
-
**License**: Unlicense (public domain)
|
|
9
|
-
**Runtime**: Node.js 22.x
|
|
10
|
-
**Module System**: ES Modules (type: "module")
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## System Architecture
|
|
15
|
-
|
|
16
|
-
### High-Level Architecture
|
|
17
|
-
|
|
18
|
-
```mermaid
|
|
19
|
-
graph TB
|
|
20
|
-
subgraph Clients[Client Applications]
|
|
21
|
-
DirectAPI[Direct HTTP Clients]
|
|
22
|
-
PythonClient[Python Scripts]
|
|
23
|
-
JSClient[JavaScript/Node.js]
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
subgraph WebCaptureService[Web Capture Microservice]
|
|
27
|
-
ExpressAPI[Express.js API Server]
|
|
28
|
-
|
|
29
|
-
subgraph Handlers[Request Handlers]
|
|
30
|
-
HTMLHandler[HTML Handler]
|
|
31
|
-
MarkdownHandler[Markdown Handler]
|
|
32
|
-
ImageHandler[Image Handler]
|
|
33
|
-
StreamHandler[Stream Handler]
|
|
34
|
-
FetchHandler[Fetch Handler]
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
subgraph CoreLib[Core Library]
|
|
38
|
-
FetchLogic[Fetch HTML Logic]
|
|
39
|
-
HTMLParser[HTML Parser<br/>Cheerio]
|
|
40
|
-
MarkdownConverter[Markdown Converter<br/>Turndown]
|
|
41
|
-
URLConverter[URL Absolutifier]
|
|
42
|
-
CharsetConverter[Charset Converter<br/>iconv-lite]
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
subgraph Browser[Headless Browser]
|
|
46
|
-
Puppeteer[Puppeteer<br/>Chromium]
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
subgraph External[External Resources]
|
|
51
|
-
TargetWebsites[Target Websites]
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
Clients --> ExpressAPI
|
|
55
|
-
ExpressAPI --> Handlers
|
|
56
|
-
Handlers --> CoreLib
|
|
57
|
-
Handlers --> Puppeteer
|
|
58
|
-
CoreLib --> Puppeteer
|
|
59
|
-
Puppeteer --> TargetWebsites
|
|
60
|
-
FetchLogic --> TargetWebsites
|
|
61
|
-
|
|
62
|
-
style Clients fill:#0088cc,stroke:#006699,stroke-width:3px,color:#fff
|
|
63
|
-
style WebCaptureService fill:#1b5e20,stroke:#4caf50,stroke-width:3px,color:#fff
|
|
64
|
-
style Handlers fill:#e65100,stroke:#ff6f00,stroke-width:3px,color:#fff
|
|
65
|
-
style CoreLib fill:#4a148c,stroke:#9c27b0,stroke-width:3px,color:#fff
|
|
66
|
-
style Browser fill:#01579b,stroke:#0277bd,stroke-width:3px,color:#fff
|
|
67
|
-
style External fill:#b71c1c,stroke:#f44336,stroke-width:3px,color:#fff
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
---
|
|
71
|
-
|
|
72
|
-
## Directory Structure
|
|
73
|
-
|
|
74
|
-
```
|
|
75
|
-
web-capture/
|
|
76
|
-
├── src/
|
|
77
|
-
│ ├── index.js # Express server entry point
|
|
78
|
-
│ ├── browser.js # Browser abstraction layer (Puppeteer & Playwright)
|
|
79
|
-
│ ├── html.js # HTML endpoint handler
|
|
80
|
-
│ ├── markdown.js # Markdown endpoint handler
|
|
81
|
-
│ ├── image.js # Screenshot endpoint handler
|
|
82
|
-
│ ├── stream.js # Stream endpoint handler (stub)
|
|
83
|
-
│ ├── fetch.js # Fetch endpoint handler (stub)
|
|
84
|
-
│ └── lib.js # Core utility functions
|
|
85
|
-
│
|
|
86
|
-
├── tests/
|
|
87
|
-
│ ├── unit/ # Unit tests
|
|
88
|
-
│ │ ├── html2md.test.js
|
|
89
|
-
│ │ ├── convertRelativeUrls.test.js
|
|
90
|
-
│ │ └── browser.test.js
|
|
91
|
-
│ ├── integration/ # Integration tests
|
|
92
|
-
│ │ └── browser-engines.test.js
|
|
93
|
-
│ ├── e2e/ # End-to-end tests
|
|
94
|
-
│ │ ├── process.test.js
|
|
95
|
-
│ │ └── docker.test.js
|
|
96
|
-
│ ├── mock/ # Mock tests
|
|
97
|
-
│ │ └── index.test.js
|
|
98
|
-
│ └── jest.setup.mjs # Jest setup
|
|
99
|
-
│
|
|
100
|
-
├── examples/
|
|
101
|
-
│ ├── js/ # JavaScript examples
|
|
102
|
-
│ │ ├── html_download.js
|
|
103
|
-
│ │ ├── markdown_download.js
|
|
104
|
-
│ │ ├── image_download.js
|
|
105
|
-
│ │ ├── playwright_example.js
|
|
106
|
-
│ │ └── engine_comparison.js
|
|
107
|
-
│ └── python/ # Python examples
|
|
108
|
-
│ ├── html_download.py
|
|
109
|
-
│ ├── markdown_download.py
|
|
110
|
-
│ ├── image_download.py
|
|
111
|
-
│ └── playwright_example.py
|
|
112
|
-
│
|
|
113
|
-
├── docs/
|
|
114
|
-
│ └── market-analysis-2025-05-13.md
|
|
115
|
-
│
|
|
116
|
-
├── Dockerfile # Container definition
|
|
117
|
-
├── docker-compose.yml # Docker orchestration
|
|
118
|
-
├── package.json # Dependencies & scripts
|
|
119
|
-
├── jest.config.mjs # Jest configuration
|
|
120
|
-
├── babel.config.cjs # Babel configuration
|
|
121
|
-
├── apply.sh # Deployment/apply script
|
|
122
|
-
└── LICENSE # Unlicense
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
---
|
|
126
|
-
|
|
127
|
-
## Core Components
|
|
128
|
-
|
|
129
|
-
### 1. Express Server (`src/index.js`)
|
|
130
|
-
|
|
131
|
-
**Entry Point**: Main HTTP server with Express.js
|
|
132
|
-
|
|
133
|
-
**Endpoints**:
|
|
134
|
-
|
|
135
|
-
- `GET /html?url=<URL>` - Raw HTML or Puppeteer-rendered HTML
|
|
136
|
-
- `GET /markdown?url=<URL>` - Markdown conversion
|
|
137
|
-
- `GET /image?url=<URL>` - PNG screenshot
|
|
138
|
-
- `GET /stream?url=<URL>` - Stream handler (future)
|
|
139
|
-
- `GET /fetch?url=<URL>` - Fetch handler (future)
|
|
140
|
-
|
|
141
|
-
**Features**:
|
|
142
|
-
|
|
143
|
-
- Graceful shutdown handling (SIGTERM, SIGINT)
|
|
144
|
-
- Process PID logging
|
|
145
|
-
- Uncaught exception handling
|
|
146
|
-
- Docker-friendly timeout (2s force exit)
|
|
147
|
-
|
|
148
|
-
**Server Configuration**:
|
|
149
|
-
|
|
150
|
-
```javascript
|
|
151
|
-
const port = process.env.PORT || 3000;
|
|
152
|
-
app.listen(port, () => {
|
|
153
|
-
console.log(`Renderer service listening on http://localhost:${port}`);
|
|
154
|
-
});
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
---
|
|
158
|
-
|
|
159
|
-
### 2. HTML Handler (`src/html.js`)
|
|
160
|
-
|
|
161
|
-
**Purpose**: Fetch and return HTML content, with JavaScript rendering support
|
|
162
|
-
|
|
163
|
-
#### Processing Flow
|
|
164
|
-
|
|
165
|
-
```mermaid
|
|
166
|
-
flowchart TD
|
|
167
|
-
A[Receive URL] --> B{URL valid?}
|
|
168
|
-
B -->|No| C[400 Bad Request]
|
|
169
|
-
B -->|Yes| D[Ensure absolute URL]
|
|
170
|
-
D --> E[Fetch HTML directly]
|
|
171
|
-
E --> F{Contains JavaScript<br/>or not HTML?}
|
|
172
|
-
F -->|Yes| G[Launch Puppeteer]
|
|
173
|
-
F -->|No| H[Convert to UTF-8]
|
|
174
|
-
G --> I[Navigate to page]
|
|
175
|
-
I --> J[Wait for networkidle0]
|
|
176
|
-
J --> K[Wait 5 seconds]
|
|
177
|
-
K --> L[Get rendered content]
|
|
178
|
-
L --> M[Convert to UTF-8]
|
|
179
|
-
H --> N[Convert relative URLs to absolute]
|
|
180
|
-
M --> N
|
|
181
|
-
N --> O[Return HTML]
|
|
182
|
-
|
|
183
|
-
style F fill:#e65100,stroke:#ff6f00,stroke-width:2px,color:#fff
|
|
184
|
-
style G fill:#01579b,stroke:#0277bd,stroke-width:2px,color:#fff
|
|
185
|
-
style O fill:#1b5e20,stroke:#4caf50,stroke-width:2px,color:#fff
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
**Key Features**:
|
|
189
|
-
|
|
190
|
-
- **Smart Detection**: Checks if HTML contains JavaScript or is not valid HTML
|
|
191
|
-
- **Puppeteer Rendering**: For JS-heavy sites, renders with headless Chrome
|
|
192
|
-
- **UTF-8 Conversion**: Ensures proper character encoding
|
|
193
|
-
- **URL Absolutification**: Converts all relative URLs to absolute
|
|
194
|
-
- **Runtime JS Hook**: Injects JavaScript to fix dynamically loaded URLs
|
|
195
|
-
|
|
196
|
-
**Puppeteer Configuration**:
|
|
197
|
-
|
|
198
|
-
```javascript
|
|
199
|
-
const browser = await puppeteer.launch({
|
|
200
|
-
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
201
|
-
});
|
|
202
|
-
await page.setViewport({ width: 1280, height: 800 });
|
|
203
|
-
await page.goto(url, {
|
|
204
|
-
waitUntil: 'networkidle0',
|
|
205
|
-
timeout: 30000,
|
|
206
|
-
});
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
---
|
|
210
|
-
|
|
211
|
-
### 3. Markdown Handler (`src/markdown.js`)
|
|
212
|
-
|
|
213
|
-
**Purpose**: Convert web pages to clean, readable Markdown
|
|
214
|
-
|
|
215
|
-
**Process**:
|
|
216
|
-
|
|
217
|
-
1. Fetch HTML (via `fetchHtml`)
|
|
218
|
-
2. Convert to Markdown (via `convertHtmlToMarkdown`)
|
|
219
|
-
3. Return as `text/markdown`
|
|
220
|
-
|
|
221
|
-
**Output**: Clean Markdown suitable for:
|
|
222
|
-
|
|
223
|
-
- AI/LLM ingestion
|
|
224
|
-
- Documentation
|
|
225
|
-
- Note-taking systems
|
|
226
|
-
- Text analysis
|
|
227
|
-
|
|
228
|
-
---
|
|
229
|
-
|
|
230
|
-
### 4. Image Handler (`src/image.js`)
|
|
231
|
-
|
|
232
|
-
**Purpose**: Capture PNG screenshots of web pages
|
|
233
|
-
|
|
234
|
-
**Features**:
|
|
235
|
-
|
|
236
|
-
- Viewport-only screenshot (not full page)
|
|
237
|
-
- Fixed viewport size: 1280x800
|
|
238
|
-
- 5-second wait for dynamic content
|
|
239
|
-
- PNG format output
|
|
240
|
-
|
|
241
|
-
**Process**:
|
|
242
|
-
|
|
243
|
-
```mermaid
|
|
244
|
-
sequenceDiagram
|
|
245
|
-
participant Client
|
|
246
|
-
participant Handler
|
|
247
|
-
participant Puppeteer
|
|
248
|
-
participant Website
|
|
249
|
-
|
|
250
|
-
Client->>Handler: GET /image?url=example.com
|
|
251
|
-
Handler->>Puppeteer: Launch browser
|
|
252
|
-
Puppeteer->>Website: Navigate to URL
|
|
253
|
-
Website-->>Puppeteer: Page loaded
|
|
254
|
-
Puppeteer->>Puppeteer: Wait 5 seconds
|
|
255
|
-
Puppeteer->>Puppeteer: Take screenshot
|
|
256
|
-
Puppeteer-->>Handler: PNG buffer
|
|
257
|
-
Handler->>Puppeteer: Close browser
|
|
258
|
-
Handler-->>Client: image/png response
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
**Response Headers**:
|
|
262
|
-
|
|
263
|
-
```javascript
|
|
264
|
-
res.set('Content-Type', 'image/png');
|
|
265
|
-
res.set('Content-Disposition', 'inline; filename="screenshot.png"');
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
---
|
|
269
|
-
|
|
270
|
-
### 5. Core Library (`src/lib.js`)
|
|
271
|
-
|
|
272
|
-
#### Key Functions
|
|
273
|
-
|
|
274
|
-
**`fetchHtml(url)`**
|
|
275
|
-
|
|
276
|
-
- Simple fetch wrapper using node-fetch
|
|
277
|
-
- Returns HTML as text
|
|
278
|
-
|
|
279
|
-
**`convertHtmlToMarkdown(html, baseUrl)`**
|
|
280
|
-
|
|
281
|
-
- Converts HTML to Markdown using Turndown
|
|
282
|
-
- Cleans HTML before conversion
|
|
283
|
-
- Absolutifies all URLs
|
|
284
|
-
|
|
285
|
-
**HTML Cleaning Operations**:
|
|
286
|
-
|
|
287
|
-
1. Remove `<style>`, `<script>`, `<noscript>` tags
|
|
288
|
-
2. Remove inline event handlers (`onclick`, `onload`, etc.)
|
|
289
|
-
3. Remove `javascript:` links
|
|
290
|
-
4. Remove inline `style` attributes
|
|
291
|
-
5. Remove empty headings
|
|
292
|
-
6. Remove empty links
|
|
293
|
-
7. Remove links with only images (no alt text)
|
|
294
|
-
8. Convert ARIA role tables to semantic `<table>` elements
|
|
295
|
-
|
|
296
|
-
**`convertRelativeUrls(html, baseUrl)`**
|
|
297
|
-
|
|
298
|
-
- Converts all relative URLs to absolute
|
|
299
|
-
- Handles: `<a>`, `<img>`, `<script>`, `<link>`, `<form>`, `<video>`, `<audio>`, `<iframe>`, etc.
|
|
300
|
-
- Processes inline `style` with `url()` references
|
|
301
|
-
- Injects runtime JavaScript hook for dynamic content
|
|
302
|
-
|
|
303
|
-
**Runtime JavaScript Hook**:
|
|
304
|
-
|
|
305
|
-
```javascript
|
|
306
|
-
// Injected script that fixes URLs dynamically
|
|
307
|
-
fixAllUrls(document);
|
|
308
|
-
const observer = new MutationObserver((mutations) => {
|
|
309
|
-
// Fix URLs in newly added elements
|
|
310
|
-
});
|
|
311
|
-
observer.observe(document.body, { childList: true, subtree: true });
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
**`convertToUtf8(html)`**
|
|
315
|
-
|
|
316
|
-
- Detects charset from `<meta>` tag
|
|
317
|
-
- Converts HTML to UTF-8 using iconv-lite
|
|
318
|
-
- Updates charset meta tag
|
|
319
|
-
|
|
320
|
-
**Charset Detection**:
|
|
321
|
-
|
|
322
|
-
```javascript
|
|
323
|
-
const charsetMatch = html.match(/<meta[^>]+charset=["']?([^"'>\s]+)/i);
|
|
324
|
-
const currentCharset = charsetMatch ? charsetMatch[1].toLowerCase() : 'utf-8';
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
---
|
|
328
|
-
|
|
329
|
-
### 6. Browser Abstraction Layer (`src/browser.js`)
|
|
330
|
-
|
|
331
|
-
**Purpose**: Unified interface supporting both Puppeteer and Playwright browser engines
|
|
332
|
-
|
|
333
|
-
#### Key Features
|
|
334
|
-
|
|
335
|
-
- **Engine Selection**: Choose between Puppeteer and Playwright via query parameter or environment variable
|
|
336
|
-
- **Unified API**: Same interface for both engines, enabling easy switching
|
|
337
|
-
- **Automatic Adaptation**: Translates engine-specific options (e.g., `networkidle0` to `networkidle` for Playwright)
|
|
338
|
-
- **Backward Compatibility**: Defaults to Puppeteer for existing deployments
|
|
339
|
-
|
|
340
|
-
#### Browser Factory
|
|
341
|
-
|
|
342
|
-
**`createBrowser(engine, options)`**
|
|
343
|
-
|
|
344
|
-
- Creates a browser instance using the specified engine
|
|
345
|
-
- Returns a unified `BrowserAdapter` object
|
|
346
|
-
- Supported engines: `'puppeteer'` or `'playwright'`
|
|
347
|
-
|
|
348
|
-
**`getBrowserEngine(req)`**
|
|
349
|
-
|
|
350
|
-
- Determines which browser engine to use
|
|
351
|
-
- Priority: query parameter > environment variable > default (puppeteer)
|
|
352
|
-
- Supported query params: `engine` or `browser`
|
|
353
|
-
- Supported values: `puppeteer`, `pptr`, `playwright`, `pw` (case-insensitive)
|
|
354
|
-
|
|
355
|
-
#### Browser Adapter Interface
|
|
356
|
-
|
|
357
|
-
```javascript
|
|
358
|
-
{
|
|
359
|
-
async newPage() // Create a new page
|
|
360
|
-
async close() // Close the browser
|
|
361
|
-
type // Engine type ('puppeteer' or 'playwright')
|
|
362
|
-
_browser // Original browser object (internal)
|
|
363
|
-
}
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
#### Page Adapter Interface
|
|
367
|
-
|
|
368
|
-
```javascript
|
|
369
|
-
{
|
|
370
|
-
async setExtraHTTPHeaders(headers) // Set HTTP headers
|
|
371
|
-
async setUserAgent(userAgent) // Set user agent
|
|
372
|
-
async setViewport(viewport) // Set viewport size
|
|
373
|
-
async goto(url, options) // Navigate to URL
|
|
374
|
-
async content() // Get page HTML
|
|
375
|
-
async screenshot(options) // Take screenshot
|
|
376
|
-
async close() // Close the page
|
|
377
|
-
_page // Original page object (internal)
|
|
378
|
-
_type // Engine type
|
|
379
|
-
}
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
#### Engine Differences Handled
|
|
383
|
-
|
|
384
|
-
The abstraction layer automatically handles differences between engines:
|
|
385
|
-
|
|
386
|
-
1. **Viewport Setting**:
|
|
387
|
-
- Puppeteer: `page.setViewport()`
|
|
388
|
-
- Playwright: `page.setViewportSize()`
|
|
389
|
-
|
|
390
|
-
2. **Network Idle Strategy**:
|
|
391
|
-
- Puppeteer: `waitUntil: 'networkidle0'`
|
|
392
|
-
- Playwright: `waitUntil: 'networkidle'`
|
|
393
|
-
|
|
394
|
-
3. **Browser Launch**:
|
|
395
|
-
- Puppeteer: `puppeteer.launch()`
|
|
396
|
-
- Playwright: `playwright.chromium.launch()`
|
|
397
|
-
|
|
398
|
-
#### Usage Examples
|
|
399
|
-
|
|
400
|
-
**Using Puppeteer (default)**:
|
|
401
|
-
|
|
402
|
-
```javascript
|
|
403
|
-
const browser = await createBrowser('puppeteer');
|
|
404
|
-
const page = await browser.newPage();
|
|
405
|
-
await page.goto('https://example.com');
|
|
406
|
-
const html = await page.content();
|
|
407
|
-
await browser.close();
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
**Using Playwright**:
|
|
411
|
-
|
|
412
|
-
```javascript
|
|
413
|
-
const browser = await createBrowser('playwright');
|
|
414
|
-
const page = await browser.newPage();
|
|
415
|
-
await page.goto('https://example.com');
|
|
416
|
-
const html = await page.content();
|
|
417
|
-
await browser.close();
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
**In Request Handlers**:
|
|
421
|
-
|
|
422
|
-
```javascript
|
|
423
|
-
export async function htmlHandler(req, res) {
|
|
424
|
-
const engine = getBrowserEngine(req); // From query param or env
|
|
425
|
-
const browser = await createBrowser(engine);
|
|
426
|
-
// ... rest of implementation
|
|
427
|
-
}
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
---
|
|
431
|
-
|
|
432
|
-
## API Documentation
|
|
433
|
-
|
|
434
|
-
### GET /html
|
|
435
|
-
|
|
436
|
-
**Description**: Fetch and return HTML content
|
|
437
|
-
|
|
438
|
-
**Parameters**:
|
|
439
|
-
|
|
440
|
-
- `url` (required): Target URL
|
|
441
|
-
- `engine` (optional): Browser engine (`puppeteer` or `playwright`). Default: `puppeteer`
|
|
442
|
-
|
|
443
|
-
**Response**:
|
|
444
|
-
|
|
445
|
-
- `Content-Type: text/html; charset=utf-8`
|
|
446
|
-
- Body: HTML content
|
|
447
|
-
|
|
448
|
-
**Examples**:
|
|
449
|
-
|
|
450
|
-
```bash
|
|
451
|
-
# Using default Puppeteer engine
|
|
452
|
-
curl "http://localhost:3000/html?url=https://example.com"
|
|
453
|
-
|
|
454
|
-
# Using Playwright engine
|
|
455
|
-
curl "http://localhost:3000/html?url=https://example.com&engine=playwright"
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
**Behavior**:
|
|
459
|
-
|
|
460
|
-
- Plain HTML sites: Direct fetch + UTF-8 conversion + URL absolutification
|
|
461
|
-
- JavaScript sites: Browser rendering (Puppeteer or Playwright) + 5s wait + URL absolutification
|
|
462
|
-
|
|
463
|
-
---
|
|
464
|
-
|
|
465
|
-
### GET /markdown
|
|
466
|
-
|
|
467
|
-
**Description**: Convert web page to Markdown
|
|
468
|
-
|
|
469
|
-
**Parameters**:
|
|
470
|
-
|
|
471
|
-
- `url` (required): Target URL
|
|
472
|
-
|
|
473
|
-
**Response**:
|
|
474
|
-
|
|
475
|
-
- `Content-Type: text/markdown`
|
|
476
|
-
- Body: Markdown content
|
|
477
|
-
|
|
478
|
-
**Example**:
|
|
479
|
-
|
|
480
|
-
```bash
|
|
481
|
-
curl "http://localhost:3000/markdown?url=https://example.com" > page.md
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
**Features**:
|
|
485
|
-
|
|
486
|
-
- GitHub Flavored Markdown (GFM)
|
|
487
|
-
- Clean, readable output
|
|
488
|
-
- Absolute URLs preserved
|
|
489
|
-
- Code blocks with fenced syntax
|
|
490
|
-
|
|
491
|
-
---
|
|
492
|
-
|
|
493
|
-
### GET /image
|
|
494
|
-
|
|
495
|
-
**Description**: Capture PNG screenshot
|
|
496
|
-
|
|
497
|
-
**Parameters**:
|
|
498
|
-
|
|
499
|
-
- `url` (required): Target URL
|
|
500
|
-
- `engine` (optional): Browser engine (`puppeteer` or `playwright`). Default: `puppeteer`
|
|
501
|
-
|
|
502
|
-
**Response**:
|
|
503
|
-
|
|
504
|
-
- `Content-Type: image/png`
|
|
505
|
-
- `Content-Disposition: inline; filename="screenshot.png"`
|
|
506
|
-
- Body: PNG image buffer
|
|
507
|
-
|
|
508
|
-
**Examples**:
|
|
509
|
-
|
|
510
|
-
```bash
|
|
511
|
-
# Using default Puppeteer engine
|
|
512
|
-
curl "http://localhost:3000/image?url=https://example.com" > screenshot.png
|
|
513
|
-
|
|
514
|
-
# Using Playwright engine
|
|
515
|
-
curl "http://localhost:3000/image?url=https://example.com&engine=playwright" > screenshot.png
|
|
516
|
-
```
|
|
517
|
-
|
|
518
|
-
**Screenshot Specs**:
|
|
519
|
-
|
|
520
|
-
- Viewport: 1280x800
|
|
521
|
-
- Wait time: 5 seconds after load
|
|
522
|
-
- Format: PNG
|
|
523
|
-
- Scope: Viewport only (not full page)
|
|
524
|
-
|
|
525
|
-
---
|
|
526
|
-
|
|
527
|
-
## Deployment
|
|
528
|
-
|
|
529
|
-
### Docker
|
|
530
|
-
|
|
531
|
-
**Dockerfile** (Node 22 + Chromium):
|
|
532
|
-
|
|
533
|
-
```dockerfile
|
|
534
|
-
FROM node:22-bullseye
|
|
535
|
-
|
|
536
|
-
# Install Chromium and dependencies
|
|
537
|
-
RUN apt-get update && apt-get install -y \
|
|
538
|
-
chromium \
|
|
539
|
-
fonts-liberation \
|
|
540
|
-
# ... more dependencies
|
|
541
|
-
--no-install-recommends
|
|
542
|
-
|
|
543
|
-
# Puppeteer environment
|
|
544
|
-
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
|
545
|
-
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
|
|
546
|
-
ENV PUPPETEER_ARGS="--no-sandbox --disable-setuid-sandbox --disable-dev-shm-usage"
|
|
547
|
-
|
|
548
|
-
COPY package.json yarn.lock ./
|
|
549
|
-
RUN yarn install --frozen-lockfile
|
|
550
|
-
|
|
551
|
-
COPY . .
|
|
552
|
-
EXPOSE 3000
|
|
553
|
-
ENTRYPOINT ["node", "bin/web-capture.js", "--serve"]
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
### Docker Compose
|
|
557
|
-
|
|
558
|
-
```yaml
|
|
559
|
-
version: '3.8'
|
|
560
|
-
services:
|
|
561
|
-
web-capture:
|
|
562
|
-
build: .
|
|
563
|
-
ports:
|
|
564
|
-
- '3000:3000'
|
|
565
|
-
restart: unless-stopped
|
|
566
|
-
environment:
|
|
567
|
-
- PORT=3000
|
|
568
|
-
```
|
|
569
|
-
|
|
570
|
-
**Commands**:
|
|
571
|
-
|
|
572
|
-
```bash
|
|
573
|
-
# Build and start
|
|
574
|
-
yarn start # or docker compose up -d
|
|
575
|
-
|
|
576
|
-
# Build manually
|
|
577
|
-
docker build -t web-capture .
|
|
578
|
-
docker run -p 3000:3000 web-capture
|
|
579
|
-
```
|
|
580
|
-
|
|
581
|
-
---
|
|
582
|
-
|
|
583
|
-
## Usage Examples
|
|
584
|
-
|
|
585
|
-
### JavaScript (Node.js)
|
|
586
|
-
|
|
587
|
-
```javascript
|
|
588
|
-
// examples/js/markdown_download.js
|
|
589
|
-
import fetch from 'node-fetch';
|
|
590
|
-
import fs from 'fs';
|
|
591
|
-
|
|
592
|
-
const url = 'https://example.com';
|
|
593
|
-
const response = await fetch(
|
|
594
|
-
`http://localhost:3000/markdown?url=${encodeURIComponent(url)}`
|
|
595
|
-
);
|
|
596
|
-
const markdown = await response.text();
|
|
597
|
-
fs.writeFileSync('downloaded.md', markdown);
|
|
598
|
-
```
|
|
599
|
-
|
|
600
|
-
### Python
|
|
601
|
-
|
|
602
|
-
```python
|
|
603
|
-
# examples/python/markdown_download.py
|
|
604
|
-
import requests
|
|
605
|
-
|
|
606
|
-
url = 'https://example.com'
|
|
607
|
-
response = requests.get(f'http://localhost:3000/markdown', params={'url': url})
|
|
608
|
-
with open('downloaded.md', 'w') as f:
|
|
609
|
-
f.write(response.text)
|
|
610
|
-
```
|
|
611
|
-
|
|
612
|
-
---
|
|
613
|
-
|
|
614
|
-
## Testing
|
|
615
|
-
|
|
616
|
-
### Test Structure
|
|
617
|
-
|
|
618
|
-
**Unit Tests** (`tests/unit/`):
|
|
619
|
-
|
|
620
|
-
- `html2md.test.js` - Markdown conversion tests
|
|
621
|
-
- `convertRelativeUrls.test.js` - URL absolutification tests
|
|
622
|
-
|
|
623
|
-
**E2E Tests** (`tests/e2e/`):
|
|
624
|
-
|
|
625
|
-
- `process.test.js` - Local process tests
|
|
626
|
-
- `docker.test.js` - Docker container tests
|
|
627
|
-
|
|
628
|
-
**Mock Tests** (`tests/mock/`):
|
|
629
|
-
|
|
630
|
-
- `index.test.js` - Mocked HTTP tests with nock
|
|
631
|
-
|
|
632
|
-
### Running Tests
|
|
633
|
-
|
|
634
|
-
```bash
|
|
635
|
-
# All unit tests
|
|
636
|
-
yarn test
|
|
637
|
-
|
|
638
|
-
# Watch mode
|
|
639
|
-
yarn test:watch
|
|
640
|
-
|
|
641
|
-
# E2E tests (local process)
|
|
642
|
-
yarn test:e2e:process
|
|
643
|
-
|
|
644
|
-
# E2E tests (Docker)
|
|
645
|
-
yarn test:e2e:docker
|
|
646
|
-
|
|
647
|
-
# All tests (build + unit + e2e)
|
|
648
|
-
yarn test:all
|
|
649
|
-
```
|
|
650
|
-
|
|
651
|
-
### Test Framework
|
|
652
|
-
|
|
653
|
-
**Jest Configuration**:
|
|
654
|
-
|
|
655
|
-
```javascript
|
|
656
|
-
export default {
|
|
657
|
-
testEnvironment: 'node',
|
|
658
|
-
transform: {
|
|
659
|
-
'^.+\\.m?js$': 'babel-jest',
|
|
660
|
-
},
|
|
661
|
-
moduleNameMapper: {
|
|
662
|
-
'^(\\.{1,2}/.*)\\.js$': '$1',
|
|
663
|
-
},
|
|
664
|
-
testMatch: ['**/tests/**/*.test.js'],
|
|
665
|
-
};
|
|
666
|
-
```
|
|
667
|
-
|
|
668
|
-
**Tools**:
|
|
669
|
-
|
|
670
|
-
- Jest 29.7.0
|
|
671
|
-
- Supertest (HTTP assertions)
|
|
672
|
-
- Nock (HTTP mocking)
|
|
673
|
-
- Babel (ES modules support)
|
|
674
|
-
|
|
675
|
-
---
|
|
676
|
-
|
|
677
|
-
## Configuration
|
|
678
|
-
|
|
679
|
-
### Environment Variables
|
|
680
|
-
|
|
681
|
-
**General**:
|
|
682
|
-
|
|
683
|
-
- `PORT` - Server port (default: 3000)
|
|
684
|
-
- `BROWSER_ENGINE` - Default browser engine (`puppeteer` or `playwright`)
|
|
685
|
-
|
|
686
|
-
**Puppeteer**:
|
|
687
|
-
|
|
688
|
-
- `PUPPETEER_EXECUTABLE_PATH` - Chromium path (Docker: `/usr/bin/chromium`)
|
|
689
|
-
- `PUPPETEER_SKIP_CHROMIUM_DOWNLOAD` - Skip bundled Chromium (Docker: `true`)
|
|
690
|
-
- `PUPPETEER_ARGS` - Additional Puppeteer launch args
|
|
691
|
-
|
|
692
|
-
**Playwright**:
|
|
693
|
-
|
|
694
|
-
- `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH` - Chromium path (Docker: `/usr/bin/chromium`)
|
|
695
|
-
- `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` - Skip browser download (Docker: `1`)
|
|
696
|
-
|
|
697
|
-
### Browser Configuration
|
|
698
|
-
|
|
699
|
-
**Browser Launch Args** (both engines):
|
|
700
|
-
|
|
701
|
-
```javascript
|
|
702
|
-
args: [
|
|
703
|
-
'--no-sandbox', // Required for Docker
|
|
704
|
-
'--disable-setuid-sandbox', // Required for Docker
|
|
705
|
-
'--disable-dev-shm-usage', // Avoid shared memory issues
|
|
706
|
-
];
|
|
707
|
-
```
|
|
708
|
-
|
|
709
|
-
**Page Options** (both engines):
|
|
710
|
-
|
|
711
|
-
- User Agent: Chrome 120 on Windows 10
|
|
712
|
-
- Viewport: 1280x800
|
|
713
|
-
- Wait strategy: `networkidle0` (Puppeteer) / `networkidle` (Playwright)
|
|
714
|
-
- Timeout: 30 seconds
|
|
715
|
-
- Additional wait: 5 seconds for dynamic content
|
|
716
|
-
|
|
717
|
-
---
|
|
718
|
-
|
|
719
|
-
## Dependencies
|
|
720
|
-
|
|
721
|
-
### Production
|
|
722
|
-
|
|
723
|
-
```json
|
|
724
|
-
{
|
|
725
|
-
"cheerio": "^1.0.0", // HTML parsing
|
|
726
|
-
"express": "^4.18.2", // Web server
|
|
727
|
-
"node-fetch": "^2.7.0", // HTTP client
|
|
728
|
-
"playwright": "^1.49.0", // Headless browser (Playwright)
|
|
729
|
-
"puppeteer": "^24.8.2", // Headless browser (Puppeteer)
|
|
730
|
-
"turndown": "^7.1.1" // HTML to Markdown
|
|
731
|
-
}
|
|
732
|
-
```
|
|
733
|
-
|
|
734
|
-
### Development
|
|
735
|
-
|
|
736
|
-
```json
|
|
737
|
-
{
|
|
738
|
-
"@babel/core": "^7.24.0", // Babel core
|
|
739
|
-
"@babel/preset-env": "^7.24.0", // Babel preset
|
|
740
|
-
"babel-jest": "^29.7.0", // Jest Babel integration
|
|
741
|
-
"get-port": "^7.1.0", // Find available port
|
|
742
|
-
"jest": "^29.7.0", // Test framework
|
|
743
|
-
"nock": "^13.5.4", // HTTP mocking
|
|
744
|
-
"supertest": "^6.3.4", // HTTP assertions
|
|
745
|
-
"turndown-plugin-gfm": "^1.0.2" // GitHub Flavored Markdown
|
|
746
|
-
}
|
|
747
|
-
```
|
|
748
|
-
|
|
749
|
-
---
|
|
750
|
-
|
|
751
|
-
## Performance
|
|
752
|
-
|
|
753
|
-
### Resource Usage
|
|
754
|
-
|
|
755
|
-
- **Memory**: ~200-500MB (Puppeteer + Chromium)
|
|
756
|
-
- **CPU**: Moderate during rendering
|
|
757
|
-
- **Disk**: Minimal (no storage)
|
|
758
|
-
- **Network**: Depends on target website
|
|
759
|
-
|
|
760
|
-
### Optimization Opportunities
|
|
761
|
-
|
|
762
|
-
1. **Browser Pooling**: Reuse Puppeteer browser instances instead of launching for each request
|
|
763
|
-
2. **Caching**: Cache rendered HTML/screenshots with TTL
|
|
764
|
-
3. **Queue System**: Background processing for slow requests
|
|
765
|
-
4. **Resource Limits**: Set memory/CPU limits for Puppeteer
|
|
766
|
-
5. **Parallel Processing**: Handle multiple requests concurrently
|
|
767
|
-
6. **Data Storage**: Migrate to [links-notation](https://github.com/link-foundation/links-notation) for caching results, then to [Doublets](https://github.com/linksplatform/Data.Doublets) for efficient Associative Knowledge Network storage of web content relationships
|
|
768
|
-
|
|
769
|
-
### Timeouts
|
|
770
|
-
|
|
771
|
-
- Page navigation: 30 seconds
|
|
772
|
-
- Post-load wait: 5 seconds
|
|
773
|
-
- Server shutdown: 2 seconds
|
|
774
|
-
|
|
775
|
-
---
|
|
776
|
-
|
|
777
|
-
## Security Considerations
|
|
778
|
-
|
|
779
|
-
### Input Validation
|
|
780
|
-
|
|
781
|
-
- URL parameter is required
|
|
782
|
-
- URL is validated and made absolute
|
|
783
|
-
- Malicious URLs (javascript:, data:, blob:) are handled
|
|
784
|
-
|
|
785
|
-
### Puppeteer Security
|
|
786
|
-
|
|
787
|
-
- `--no-sandbox` required for Docker (but reduces isolation)
|
|
788
|
-
- User agent spoofing (appears as regular browser)
|
|
789
|
-
- No persistent browser data
|
|
790
|
-
- Fresh browser instance per request
|
|
791
|
-
|
|
792
|
-
### Content Sanitization
|
|
793
|
-
|
|
794
|
-
**Markdown Conversion**:
|
|
795
|
-
|
|
796
|
-
- Removes `<script>` tags
|
|
797
|
-
- Removes inline event handlers
|
|
798
|
-
- Removes `javascript:` links
|
|
799
|
-
- Removes inline styles
|
|
800
|
-
|
|
801
|
-
**HTML Output**:
|
|
802
|
-
|
|
803
|
-
- Preserves original content (intentionally)
|
|
804
|
-
- Converts to UTF-8 for consistency
|
|
805
|
-
|
|
806
|
-
### Docker Security
|
|
807
|
-
|
|
808
|
-
- Non-root user execution (recommended but not implemented)
|
|
809
|
-
- Read-only filesystem (can be configured)
|
|
810
|
-
- Resource limits (can be configured)
|
|
811
|
-
|
|
812
|
-
---
|
|
813
|
-
|
|
814
|
-
## Known Issues & TODOs
|
|
815
|
-
|
|
816
|
-
### Active Issues
|
|
817
|
-
|
|
818
|
-
1. **Stream/Fetch Endpoints**: Stub implementations (not functional)
|
|
819
|
-
2. **Browser Reuse**: New browser launched per request (inefficient)
|
|
820
|
-
3. **No Caching**: Every request hits the target website
|
|
821
|
-
4. **No Rate Limiting**: Can be abused
|
|
822
|
-
5. **Large Pages**: Full page screenshots not supported
|
|
823
|
-
|
|
824
|
-
### Recommended Improvements
|
|
825
|
-
|
|
826
|
-
1. **Browser Pool**: Implement Puppeteer browser pooling for efficiency
|
|
827
|
-
2. **Caching Layer**: Redis or in-memory cache for frequent URLs
|
|
828
|
-
3. **Rate Limiting**: Per-IP or per-URL rate limiting
|
|
829
|
-
4. **Authentication**: Optional API key authentication
|
|
830
|
-
5. **Metrics**: Prometheus metrics for monitoring
|
|
831
|
-
6. **Logging**: Structured logging (Winston/Pino)
|
|
832
|
-
7. **Error Handling**: More granular error responses
|
|
833
|
-
8. **Full Page Screenshots**: Option for full-page captures
|
|
834
|
-
9. **PDF Export**: Add `/pdf` endpoint
|
|
835
|
-
10. **Content Storage**: Migrate from ephemeral to [links-notation](https://github.com/link-foundation/links-notation) (human-readable) then [Doublets](https://github.com/linksplatform/Data.Doublets) (Associative Knowledge Network) for persistent storage and content relationship mapping
|
|
836
|
-
|
|
837
|
-
---
|
|
838
|
-
|
|
839
|
-
## Troubleshooting
|
|
840
|
-
|
|
841
|
-
### Common Issues
|
|
842
|
-
|
|
843
|
-
**1. "Error fetching HTML"**
|
|
844
|
-
|
|
845
|
-
- Check if target URL is accessible
|
|
846
|
-
- Verify network connectivity
|
|
847
|
-
- Check if URL requires authentication
|
|
848
|
-
|
|
849
|
-
**2. "Error capturing screenshot"**
|
|
850
|
-
|
|
851
|
-
- Target website may block headless browsers
|
|
852
|
-
- Page may have infinite loading scripts
|
|
853
|
-
- Try increasing timeout
|
|
854
|
-
|
|
855
|
-
**3. Puppeteer launch failed**
|
|
856
|
-
|
|
857
|
-
- In Docker: Ensure Chromium dependencies are installed
|
|
858
|
-
- Check `--no-sandbox` flag is present
|
|
859
|
-
- Verify `PUPPETEER_EXECUTABLE_PATH`
|
|
860
|
-
|
|
861
|
-
**4. Memory issues**
|
|
862
|
-
|
|
863
|
-
- Chromium is memory-intensive
|
|
864
|
-
- Consider increasing Docker memory limits
|
|
865
|
-
- Implement browser pooling
|
|
866
|
-
|
|
867
|
-
**5. Timeout errors**
|
|
868
|
-
|
|
869
|
-
- Increase `timeout` in page.goto()
|
|
870
|
-
- Some sites take > 30s to load
|
|
871
|
-
- Check if site requires user interaction
|
|
872
|
-
|
|
873
|
-
### Debug Mode
|
|
874
|
-
|
|
875
|
-
```bash
|
|
876
|
-
# Enable Puppeteer debugging
|
|
877
|
-
DEBUG=puppeteer:* node src/index.js
|
|
878
|
-
|
|
879
|
-
# Verbose logging
|
|
880
|
-
NODE_ENV=development node src/index.js
|
|
881
|
-
```
|
|
882
|
-
|
|
883
|
-
---
|
|
884
|
-
|
|
885
|
-
## Development
|
|
886
|
-
|
|
887
|
-
### Local Setup
|
|
888
|
-
|
|
889
|
-
```bash
|
|
890
|
-
# Install dependencies
|
|
891
|
-
yarn install
|
|
892
|
-
|
|
893
|
-
# Run in development mode (with nodemon)
|
|
894
|
-
yarn dev
|
|
895
|
-
|
|
896
|
-
# Access the service
|
|
897
|
-
curl http://localhost:3000/html?url=https://example.com
|
|
898
|
-
```
|
|
899
|
-
|
|
900
|
-
### Running Examples
|
|
901
|
-
|
|
902
|
-
```bash
|
|
903
|
-
# JavaScript examples
|
|
904
|
-
yarn examples:javascript
|
|
905
|
-
|
|
906
|
-
# Python examples
|
|
907
|
-
yarn examples:python
|
|
908
|
-
|
|
909
|
-
# All examples (requires Docker build)
|
|
910
|
-
yarn examples
|
|
911
|
-
```
|
|
912
|
-
|
|
913
|
-
### Adding a New Endpoint
|
|
914
|
-
|
|
915
|
-
1. Create handler in `src/`:
|
|
916
|
-
|
|
917
|
-
```javascript
|
|
918
|
-
// src/myEndpoint.js
|
|
919
|
-
export async function myEndpointHandler(req, res) {
|
|
920
|
-
const url = req.query.url;
|
|
921
|
-
// Implementation
|
|
922
|
-
res.send(result);
|
|
923
|
-
}
|
|
924
|
-
```
|
|
925
|
-
|
|
926
|
-
2. Register in `src/index.js`:
|
|
927
|
-
|
|
928
|
-
```javascript
|
|
929
|
-
import { myEndpointHandler } from './myEndpoint.js';
|
|
930
|
-
app.get('/my-endpoint', myEndpointHandler);
|
|
931
|
-
```
|
|
932
|
-
|
|
933
|
-
3. Add tests in `tests/unit/`:
|
|
934
|
-
|
|
935
|
-
```javascript
|
|
936
|
-
// tests/unit/myEndpoint.test.js
|
|
937
|
-
test('myEndpoint works', async () => {
|
|
938
|
-
// Test implementation
|
|
939
|
-
});
|
|
940
|
-
```
|
|
941
|
-
|
|
942
|
-
---
|
|
943
|
-
|
|
944
|
-
## License
|
|
945
|
-
|
|
946
|
-
This software is released into the **public domain** under the [Unlicense](https://unlicense.org).
|
|
947
|
-
|
|
948
|
-
**Important**: The Unlicense is NOT the same as having no license or being "unlicensed". The Unlicense is a specific public domain dedication that explicitly grants everyone the freedom to use, modify, and distribute this software without restrictions.
|
|
949
|
-
|
|
950
|
-
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
|
|
951
|
-
|
|
952
|
-
For the full license text, see the [LICENSE](LICENSE) file or visit [unlicense.org](https://unlicense.org).
|
|
953
|
-
|
|
954
|
-
---
|
|
955
|
-
|
|
956
|
-
## Contributors
|
|
957
|
-
|
|
958
|
-
Deep.Assistant Team
|
|
959
|
-
|
|
960
|
-
---
|
|
961
|
-
|
|
962
|
-
## Glossary
|
|
963
|
-
|
|
964
|
-
- **Puppeteer**: Node library for controlling headless Chrome/Chromium
|
|
965
|
-
- **Playwright**: Cross-browser automation library supporting Chromium, Firefox, and WebKit
|
|
966
|
-
- **Cheerio**: Fast, flexible HTML parser for Node.js
|
|
967
|
-
- **Turndown**: HTML to Markdown converter
|
|
968
|
-
- **iconv-lite**: Character encoding conversion library
|
|
969
|
-
- **Headless Browser**: Browser without GUI, controlled programmatically
|
|
970
|
-
- **networkidle0**: Page load state in Puppeteer (no network activity for 500ms)
|
|
971
|
-
- **networkidle**: Page load state in Playwright (similar to networkidle0)
|
|
972
|
-
- **UTF-8**: Universal character encoding standard
|
|
973
|
-
- **GFM**: GitHub Flavored Markdown
|
|
974
|
-
- **ARIA**: Accessible Rich Internet Applications (web accessibility standard)
|
|
975
|
-
- **Viewport**: Visible area of a web page
|
|
976
|
-
- **ES Modules**: JavaScript module system (import/export)
|
|
977
|
-
- **Browser Abstraction Layer**: Unified interface supporting multiple browser engines
|
|
978
|
-
|
|
979
|
-
---
|
|
980
|
-
|
|
981
|
-
_Document generated: 2025-10-25_
|
|
982
|
-
_Version: 1.0.0_
|