@involvex/bun-crawl 0.0.0-stage → 0.1.0
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/.github/FUNDING.yml +10 -0
- package/.prettierignore +41 -0
- package/AGENTS.md +357 -0
- package/IMPLEMENTATION_PLAN.md +452 -0
- package/Plan.md +83 -0
- package/README.md +180 -2
- package/dist/bun-crawl.js +101 -0
- package/eslint.config.mjs +37 -0
- package/package.json +40 -5
- package/src/ai/interface.ts +7 -0
- package/src/ai/null-ai.ts +10 -0
- package/src/ai/zen-ai.ts +57 -0
- package/src/cli.ts +309 -0
- package/src/core/cleaner.ts +47 -0
- package/src/core/crawler.ts +146 -0
- package/src/core/fetcher.ts +64 -0
- package/src/core/markdown.ts +108 -0
- package/src/index.ts +13 -0
- package/src/types.ts +54 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# These are supported funding model platforms
|
|
2
|
+
|
|
3
|
+
github: [involvex]
|
|
4
|
+
custom:
|
|
5
|
+
[
|
|
6
|
+
https://buymeacoffee.com/involvex,
|
|
7
|
+
https://paypal.me/involvex,
|
|
8
|
+
https://opencode.ai/go?ref=XS9FHCZT4C,
|
|
9
|
+
https://rewards.bing.com/welcome?rh=14525F68&ref=rafsrchae&form=ML2XE3&OCID=ML2XE3&PUBL=RewardsDO&CREA=ML2XE3,
|
|
10
|
+
]
|
package/.prettierignore
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# AI Agent Directories
|
|
2
|
+
.agent
|
|
3
|
+
.agents
|
|
4
|
+
.kilo
|
|
5
|
+
.claude
|
|
6
|
+
.gemini
|
|
7
|
+
.kilocode
|
|
8
|
+
.opencode
|
|
9
|
+
|
|
10
|
+
# Build & Dependency Directories
|
|
11
|
+
node_modules
|
|
12
|
+
dist
|
|
13
|
+
build
|
|
14
|
+
coverage
|
|
15
|
+
.next
|
|
16
|
+
.nuxt
|
|
17
|
+
.cache
|
|
18
|
+
|
|
19
|
+
# Version Control & Config
|
|
20
|
+
.git
|
|
21
|
+
.vscode
|
|
22
|
+
.idea
|
|
23
|
+
|
|
24
|
+
# Lock Files
|
|
25
|
+
package-lock.json
|
|
26
|
+
yarn.lock
|
|
27
|
+
pnpm-lock.yaml
|
|
28
|
+
bun.lockb
|
|
29
|
+
|
|
30
|
+
# Minified & Generated Files
|
|
31
|
+
*.min.js
|
|
32
|
+
*.min.css
|
|
33
|
+
*.min.js.map
|
|
34
|
+
*.min.css.map
|
|
35
|
+
|
|
36
|
+
# System Files
|
|
37
|
+
.DS_Store
|
|
38
|
+
Thumbs.db
|
|
39
|
+
*.log
|
|
40
|
+
*.tgz
|
|
41
|
+
*.tar.gz
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# AGENTS.md - Agent Instructions for bun-crawl
|
|
2
|
+
|
|
3
|
+
This file provides comprehensive instructions for AI agents working on the **bun-crawl** project - an open-source Firecrawl alternative built with Bun for scraping and crawling websites into clean Markdown.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Project Overview
|
|
8
|
+
|
|
9
|
+
**bun-crawl** is a fast, native Bun-based web scraper and crawler that:
|
|
10
|
+
|
|
11
|
+
- Uses **Mozilla Readability** for article extraction
|
|
12
|
+
- Uses **Turndown** for HTML → Markdown conversion
|
|
13
|
+
- Supports recursive crawling with depth limiting and same-domain filtering
|
|
14
|
+
- Produces standalone binaries with no runtime dependencies
|
|
15
|
+
- Provides a pluggable AI extractor interface for LLM-based content processing
|
|
16
|
+
|
|
17
|
+
**Key Technologies:**
|
|
18
|
+
|
|
19
|
+
- **Runtime:** Bun (v1.3.0+)
|
|
20
|
+
- **Language:** TypeScript (strict mode, ES2022)
|
|
21
|
+
- **CLI Framework:** Citty
|
|
22
|
+
- **HTML Parsing:** linkedom (DOM parser)
|
|
23
|
+
- **Content Extraction:** @mozilla/readability
|
|
24
|
+
- **Markdown Conversion:** turndown
|
|
25
|
+
- **Testing:** Bun's built-in test runner (Jest-compatible)
|
|
26
|
+
- **Linting:** ESLint with TypeScript ESLint
|
|
27
|
+
- **Formatting:** Prettier with @involvex/prettier-config
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 2. Useful Commands
|
|
32
|
+
|
|
33
|
+
### Development
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Install dependencies
|
|
37
|
+
bun install
|
|
38
|
+
|
|
39
|
+
# Run in development mode
|
|
40
|
+
bun run dev scrape https://example.com
|
|
41
|
+
bun run dev crawl https://example.com
|
|
42
|
+
|
|
43
|
+
# Format code
|
|
44
|
+
bun run format
|
|
45
|
+
|
|
46
|
+
# Lint (check)
|
|
47
|
+
bun run lint
|
|
48
|
+
|
|
49
|
+
# Lint (auto-fix)
|
|
50
|
+
bun run lint:fix
|
|
51
|
+
|
|
52
|
+
# Type check
|
|
53
|
+
bun run typecheck
|
|
54
|
+
|
|
55
|
+
# Run tests
|
|
56
|
+
bun run test
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Building
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Production binary (runs prebuild: format, lint:fix, typecheck)
|
|
63
|
+
bun run build
|
|
64
|
+
# Output: ./dist/bun-crawl (Linux/macOS) or ./dist/bun-crawl.exe (Windows)
|
|
65
|
+
|
|
66
|
+
# Cross-compilation
|
|
67
|
+
bun build --compile --target=linux-x64 ./src/cli.ts --outfile bun-crawl-linux
|
|
68
|
+
bun build --compile --target=darwin-x64 ./src/cli.ts --outfile bun-crawl-macos
|
|
69
|
+
bun build --compile --target=windows-x64 ./src/cli.ts --outfile bun-crawl.exe
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Testing
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Run all tests
|
|
76
|
+
bun test
|
|
77
|
+
|
|
78
|
+
# Run tests with coverage
|
|
79
|
+
bun test --coverage
|
|
80
|
+
|
|
81
|
+
# Run specific test file
|
|
82
|
+
bun test src/core/fetcher.test.ts
|
|
83
|
+
|
|
84
|
+
# Watch mode
|
|
85
|
+
bun test --watch
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 3. Project Structure
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
bun-crawl/
|
|
94
|
+
├── src/
|
|
95
|
+
│ ├── cli.ts # CLI entry point (Citty-based)
|
|
96
|
+
│ ├── index.ts # Public API exports
|
|
97
|
+
│ ├── types.ts # TypeScript interfaces
|
|
98
|
+
│ ├── core/
|
|
99
|
+
│ │ ├── fetcher.ts # HTTP fetch wrapper with timeout/UA
|
|
100
|
+
│ │ ├── cleaner.ts # Readability content extraction
|
|
101
|
+
│ │ ├── markdown.ts # Turndown HTML→Markdown converter
|
|
102
|
+
│ │ └── crawler.ts # Recursive crawler (HTMLRewriter)
|
|
103
|
+
│ └── ai/
|
|
104
|
+
│ ├── interface.ts # AIExtractor interface
|
|
105
|
+
│ ├── null-ai.ts # Pass-through extractor (default)
|
|
106
|
+
│ └── zen-ai.ts # Template for Zen/OpenCode integration
|
|
107
|
+
├── package.json
|
|
108
|
+
├── tsconfig.json
|
|
109
|
+
├── README.md
|
|
110
|
+
└── AGENTS.md (this file)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 4. Architecture & Core Components
|
|
116
|
+
|
|
117
|
+
### 4.1 Public API (src/index.ts)
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
// Core classes
|
|
121
|
+
export {Fetcher} from './core/fetcher'
|
|
122
|
+
export {Cleaner} from './core/cleaner'
|
|
123
|
+
export {MarkdownConverter} from './core/markdown'
|
|
124
|
+
export {Crawler} from './core/crawler'
|
|
125
|
+
|
|
126
|
+
// AI Extractors
|
|
127
|
+
export {NullAIExtractor} from './ai/null-ai'
|
|
128
|
+
export {ZenAIExtractor} from './ai/zen-ai'
|
|
129
|
+
export type {AIExtractor, AIExtractorConfig} from './ai/interface'
|
|
130
|
+
|
|
131
|
+
// Types
|
|
132
|
+
export type {
|
|
133
|
+
CrawlOptions,
|
|
134
|
+
CrawlResult,
|
|
135
|
+
FetcherOptions,
|
|
136
|
+
ScrapeResult,
|
|
137
|
+
} from './types'
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 4.2 Core Classes
|
|
141
|
+
|
|
142
|
+
| Class | Purpose | Key Methods |
|
|
143
|
+
| ------------------- | ----------------------------------------- | ------------------------------------------------- |
|
|
144
|
+
| `Fetcher` | HTTP requests with timeout, UA, redirects | `fetch(url)` → `{html, url, status}` |
|
|
145
|
+
| `Cleaner` | Mozilla Readability extraction | `clean(html, url)` → `{content, title, metadata}` |
|
|
146
|
+
| `MarkdownConverter` | Turndown HTML→Markdown | `convert(html)` → `markdown string` |
|
|
147
|
+
| `Crawler` | Recursive crawling | `crawl(startUrl)` → `CrawlResult` |
|
|
148
|
+
|
|
149
|
+
### 4.3 AI Extractor Interface
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
interface AIExtractor {
|
|
153
|
+
extract<T = string>(markdown: string, schemaPrompt: string): Promise<T>
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
- **NullAIExtractor** - Default, passes content through unchanged
|
|
158
|
+
- **ZenAIExtractor** - Template for OpenCode Zen / LLM integration
|
|
159
|
+
- Custom extractors can be implemented for any LLM API
|
|
160
|
+
|
|
161
|
+
### 4.4 Data Flow
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
URL → Fetcher.fetch() → HTML
|
|
165
|
+
→ Cleaner.clean() → {content, title, metadata}
|
|
166
|
+
→ MarkdownConverter.convert() → Markdown
|
|
167
|
+
→ AIExtractor.extract() → Enhanced/Structured Output
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 5. Best Practices & Guidelines
|
|
173
|
+
|
|
174
|
+
### 5.1 Code Style
|
|
175
|
+
|
|
176
|
+
- **Strict TypeScript:** `strict: true`, `verbatimModuleSyntax: true`
|
|
177
|
+
- **ES Modules:** Use `.js` extensions in imports (Bun requirement)
|
|
178
|
+
- **No `any`:** Use proper types; `unknown` preferred over `any`
|
|
179
|
+
- **Async/await:** Prefer over Promise chains
|
|
180
|
+
- **Error handling:** Always wrap fetch/parse in try-catch; use `Error.cause` for chaining
|
|
181
|
+
|
|
182
|
+
### 5.2 Testing Guidelines
|
|
183
|
+
|
|
184
|
+
- Use `bun test` (Jest-compatible API)
|
|
185
|
+
- Test file naming: `*.test.ts` alongside source files
|
|
186
|
+
- Mock external dependencies (fetch, HTML parsing)
|
|
187
|
+
- Test edge cases: empty content, malformed HTML, network errors
|
|
188
|
+
- Aim for >80% coverage on core modules
|
|
189
|
+
|
|
190
|
+
### 5.3 Performance Considerations
|
|
191
|
+
|
|
192
|
+
- **Concurrency:** Respect `CrawlOptions.concurrency` (default 5)
|
|
193
|
+
- **Timeouts:** Default 30s; configurable via `FetcherOptions.timeout`
|
|
194
|
+
- **Memory:** Stream large responses when possible; avoid holding full HTML in memory
|
|
195
|
+
- **Binary size:** `--minify` flag in build; avoid unnecessary dependencies
|
|
196
|
+
|
|
197
|
+
### 5.4 Adding New Features
|
|
198
|
+
|
|
199
|
+
#### New CLI Command
|
|
200
|
+
|
|
201
|
+
1. Add command to `cli.ts` in `parseCliArgs()` validation
|
|
202
|
+
2. Create `run<Command>()` function following `runScrape`/`runCrawl` pattern
|
|
203
|
+
3. Add help text in `showHelp()`
|
|
204
|
+
4. Export any new types from `types.ts` and `index.ts`
|
|
205
|
+
|
|
206
|
+
#### New AI Extractor
|
|
207
|
+
|
|
208
|
+
1. Implement `AIExtractor` interface in `src/ai/`
|
|
209
|
+
2. Export from `src/ai/index.ts` (create if needed) and `src/index.ts`
|
|
210
|
+
3. Add configuration options to `AIExtractorConfig` if needed
|
|
211
|
+
4. Update CLI to accept extractor selection (future enhancement)
|
|
212
|
+
|
|
213
|
+
#### New Markdown Rules
|
|
214
|
+
|
|
215
|
+
Add to `MarkdownConverter.configureRules()` in `markdown.ts`:
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
this.turndown.addRule('ruleName', {
|
|
219
|
+
filter: 'tagName', // or function(node) { return condition }
|
|
220
|
+
replacement: (content, node) => transformedContent,
|
|
221
|
+
})
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### 5.5 Security Best Practices
|
|
225
|
+
|
|
226
|
+
- **Never log sensitive data** (tokens, passwords, full URLs with auth)
|
|
227
|
+
- **Validate URLs** before fetching (protocol allowlist: http/https only)
|
|
228
|
+
- **Respect robots.txt** (future enhancement)
|
|
229
|
+
- **Rate limiting:** Implement delays between requests in crawler
|
|
230
|
+
- **Sanitize output** when rendering HTML in markdown (Turndown handles most)
|
|
231
|
+
|
|
232
|
+
### 5.6 Cross-Platform Compatibility
|
|
233
|
+
|
|
234
|
+
- Use `path` module (not string concatenation) for file paths
|
|
235
|
+
- Test on Windows (`bun-crawl.exe`), Linux, macOS
|
|
236
|
+
- Handle line endings correctly (UTF-8, `\n`)
|
|
237
|
+
- Use `dirname()` and `join()` from `path` module
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## 6. Configuration Files
|
|
242
|
+
|
|
243
|
+
### tsconfig.json (Key Settings)
|
|
244
|
+
|
|
245
|
+
```json
|
|
246
|
+
{
|
|
247
|
+
"compilerOptions": {
|
|
248
|
+
"target": "ES2022",
|
|
249
|
+
"module": "ESNext",
|
|
250
|
+
"moduleResolution": "bundler",
|
|
251
|
+
"strict": true,
|
|
252
|
+
"verbatimModuleSyntax": true,
|
|
253
|
+
"moduleDetection": "force",
|
|
254
|
+
"types": ["bun"]
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### package.json Scripts
|
|
260
|
+
|
|
261
|
+
| Script | Command | Purpose |
|
|
262
|
+
| ----------- | --------------------------------- | -------------------- |
|
|
263
|
+
| `prebuild` | `format && lint:fix && typecheck` | Pre-build validation |
|
|
264
|
+
| `build` | `bun build --compile --minify` | Production binary |
|
|
265
|
+
| `dev` | `bun run src/cli.ts` | Development run |
|
|
266
|
+
| `format` | `prettier . --write` | Code formatting |
|
|
267
|
+
| `lint` | `eslint .` | Lint check |
|
|
268
|
+
| `lint:fix` | `eslint . --fix` | Auto-fix lint issues |
|
|
269
|
+
| `test` | `bun test` | Run tests |
|
|
270
|
+
| `typecheck` | `tsc --noEmit` | Type checking |
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## 7. Common Tasks & Patterns
|
|
275
|
+
|
|
276
|
+
### Adding a Test File
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
// src/core/fetcher.test.ts
|
|
280
|
+
import {describe, it, expect, beforeEach, mock} from 'bun:test'
|
|
281
|
+
import {Fetcher} from './fetcher'
|
|
282
|
+
|
|
283
|
+
describe('Fetcher', () => {
|
|
284
|
+
let fetcher: Fetcher
|
|
285
|
+
|
|
286
|
+
beforeEach(() => {
|
|
287
|
+
fetcher = new Fetcher({timeout: 5000})
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
it('should fetch HTML successfully', async () => {
|
|
291
|
+
// Use mock or test against a local test server
|
|
292
|
+
const result = await fetcher.fetch('https://example.com')
|
|
293
|
+
expect(result.status).toBe(200)
|
|
294
|
+
expect(result.html).toContain('<html')
|
|
295
|
+
})
|
|
296
|
+
})
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Debugging
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
# Verbose logging
|
|
303
|
+
DEBUG=* bun run dev scrape https://example.com
|
|
304
|
+
|
|
305
|
+
# Inspect compiled binary
|
|
306
|
+
bun run build && ./dist/bun-crawl --help
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Handling Bun-Specific APIs
|
|
310
|
+
|
|
311
|
+
- Use `Bun.file()`, `Bun.write()` for file I/O (faster than `fs/promises`)
|
|
312
|
+
- Use `Bun.spawn()` for subprocesses
|
|
313
|
+
- Use `import.meta.resolve()` for module resolution
|
|
314
|
+
- `bun:test` globals: `describe`, `it`, `expect`, `mock`, `spyOn`
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## 8. Troubleshooting
|
|
319
|
+
|
|
320
|
+
| Issue | Solution |
|
|
321
|
+
| ------------------------------- | ------------------------------------------------------------ |
|
|
322
|
+
| `TypeScript` errors on `import` | Add `.js` extension to relative imports |
|
|
323
|
+
| `fetch` not found | Ensure `"types": ["bun"]` in tsconfig.json |
|
|
324
|
+
| Binary won't run on Linux | Check glibc compatibility; try `--target=linux-x64-baseline` |
|
|
325
|
+
| Tests timeout | Increase timeout in `bun test --timeout=60000` |
|
|
326
|
+
| HTMLRewriter errors | Ensure valid HTML; wrap in try-catch |
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## 9. Future Enhancement Areas
|
|
331
|
+
|
|
332
|
+
- [ ] robots.txt compliance
|
|
333
|
+
- [ ] Rate limiting / politeness delays
|
|
334
|
+
- [ ] Sitemap.xml discovery
|
|
335
|
+
- [ ] JavaScript rendering (headless browser)
|
|
336
|
+
- [ ] Structured data extraction (JSON-LD, microdata)
|
|
337
|
+
- [ ] Proxy support
|
|
338
|
+
- [ ] Cookie/session persistence
|
|
339
|
+
- [ ] Incremental crawling (ETag/Last-Modified)
|
|
340
|
+
- [ ] Distributed crawling (queue-based)
|
|
341
|
+
- [ ] Webhook callbacks for async crawls
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## 10. References
|
|
346
|
+
|
|
347
|
+
- **Bun Docs:** https://bun.sh/docs
|
|
348
|
+
- **Mozilla Readability:** https://github.com/mozilla/readability
|
|
349
|
+
- **Turndown:** https://github.com/mixmark-io/turndown
|
|
350
|
+
- **linkedom:** https://github.com/WebReflection/linkedom
|
|
351
|
+
- **Citty:** https://github.com/unjs/citty
|
|
352
|
+
- **TypeScript ESLint:** https://typescript-eslint.io/
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
_Last updated: 2025-09-14_
|
|
357
|
+
_Generated based on project analysis of README.md, package.json, tsconfig.json, and source code_
|