@wenathlan/saddle 1.8.2 → 1.8.5
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/README.md +9 -9
- package/browser/playwright.js +22 -0
- package/docs/.gitkeep +0 -0
- package/docs/gapmatrix.md +3 -3
- package/docs/libraryapi.md +1 -0
- package/docs/logs/.gitkeep +0 -0
- package/docs/packageaudit185.md +23 -0
- package/docs/plans/00.index.md +50 -0
- package/docs/plans/01.architecture.md +86 -0
- package/docs/plans/02.research.computer.use.md +58 -0
- package/docs/plans/03.research.captcha.bypass.md +68 -0
- package/docs/plans/04.research.sandbox.ai.md +52 -0
- package/docs/plans/05.capture.platform.md +57 -0
- package/docs/plans/06.dependencies.md +97 -0
- package/docs/plans/07.captcha.test.page.md +41 -0
- package/docs/plans/08.production.infra.md +70 -0
- package/docs/plans/09.database.schema.md +121 -0
- package/docs/plans/10.cloudinary.storage.md +57 -0
- package/docs/plans/11.movement.logs.json.md +72 -0
- package/docs/plans/12.research.atlas.agent.browser.md +79 -0
- package/docs/plans/13.research.anti.detection.md +898 -0
- package/docs/plans/14.research.proxy.md +1495 -0
- package/docs/plans/15.research.retry.rate.limit.md +1958 -0
- package/docs/plans/16.research.crawling.md +1417 -0
- package/docs/plans/17.research.caching.md +1610 -0
- package/docs/plans/18.research.content.extraction.md +1952 -0
- package/docs/plans/19.research.errors.events.md +1523 -0
- package/docs/plans/20.research.zod.validation.md +1350 -0
- package/docs/plans/21.research.batch.concurrency.md +1888 -0
- package/docs/plans/22.research.universal.runtime.md +944 -0
- package/docs/plans/23.research.ai.integration.md +1465 -0
- package/docs/plans/24.research.memory.persistence.md +1979 -0
- package/docs/plans/25.research.server.api.md +342 -0
- package/docs/plans/26.research.compilation.md +249 -0
- package/docs/plans/27.research.html.parsing.md +251 -0
- package/docs/plans/28.action.plan.md +50 -0
- package/docs/plans/29.api.reference.md +174 -0
- package/docs/plans/30.architecture.plan.md +94 -0
- package/docs/plans/31.auditoria.dados.md +163 -0
- package/docs/plans/32.bots.automacao.computacional.md +214 -0
- package/docs/plans/33.bots.codigo.revisao.md +220 -0
- package/docs/plans/34.bots.seguranca.cicd.md +366 -0
- package/docs/plans/35.comparativo.concorrencia.md +464 -0
- package/docs/plans/36.computational.memory.md +340 -0
- package/docs/plans/37.deploystrategy.md +394 -0
- package/docs/plans/38.flow.md +155 -0
- package/docs/plans/39.multi.platform.bot.md +252 -0
- package/docs/plans/40.npm.publish.md +250 -0
- package/docs/plans/41.o.que.falta.md +407 -0
- package/docs/plans/42.pesquisa.concorrencia.md +721 -0
- package/docs/plans/43.plan.universal.architecture.md +496 -0
- package/docs/plans/44.reference.md +100 -0
- package/docs/plans/45.robotarchitecture.md +237 -0
- package/docs/plans/46.scdnintegration.md +284 -0
- package/docs/plans/47.multiforge.readme.md +129 -0
- package/docs/plans/48.theory.v4.repo.os.md +152 -0
- package/docs/plans/49.third.party.infra.md +12 -0
- package/docs/plans/50.file.as.compute.md +39 -0
- package/docs/plans/51.architecture.virtual.processor.md +80 -0
- package/docs/plans/52.manifesto.v8.md +11 -0
- package/docs/plans/58.cdn.list.md +23 -0
- package/docs/plans/59.sql.frameworks.md +33 -0
- package/docs/plans/60.sql.thirdparty.md +26 -0
- package/docs/plans/61.objective.multiforge.md +63 -0
- package/docs/plans/62.huggingface.upload.md +26 -0
- package/docs/plans/63.kaggle.upload.md +24 -0
- package/docs/plans/64.npm.storage.md +30 -0
- package/docs/plans/65.rclone.terabox.md +32 -0
- package/docs/plans/66.buckets.and.models.todo.md +14 -0
- package/docs/plans/67.database.todo.md +13 -0
- package/docs/plans/68.deploy.packages.todo.md +12 -0
- package/docs/plans/69.report.human.operator.md +133 -0
- package/docs/plans/70.report.brain2qwerty.ems.md +135 -0
- package/docs/plans/71.report.hd.infinito.vram.md +155 -0
- package/docs/plans/72.plan.hd.infinito.node.md +146 -0
- package/docs/plans/73.plan.scifi.repos.md +125 -0
- package/docs/plans/74.000.manifesto.v8.flat.2..md +11 -0
- package/docs/plans/README.md +489 -0
- package/docs/plans/aggregate_platforms.mjs +146 -0
- package/docs/plans/examplesession.json +36 -0
- package/docs/plans/missing-facts.md +192 -0
- package/docs/plans/models.md +64 -0
- package/docs/plans/organize.cjs +270 -0
- package/docs/plans/platforms.md +2887 -0
- package/docs/plans/sites.md +31322 -0
- package/docs/platformpipelineaudit.md +18 -0
- package/docs/platformpipelines.md +13 -0
- package/docs/registryresearch.md +4 -0
- package/docs/release.md +4 -4
- package/docs/release182notes.md +2 -0
- package/docs/release184notes.md +7 -0
- package/docs/release185notes.md +7 -0
- package/docs/sources/farm.py +117 -0
- package/docs/sources/html/saddle1.html +132 -0
- package/docs/sources/html/saddle2.html +157 -0
- package/docs/sources/html/saddle3.html +119 -0
- package/docs/sources/html/saddle4.html +144 -0
- package/docs/sources/html/saddle5.html +72 -0
- package/docs/sources/html/saddle6.html +171 -0
- package/docs/sources/html/saddle7.html +236 -0
- package/docs/sources/saddle.ts +74 -0
- package/docs/sources/schema.prisma +88 -0
- package/docs/sources/script.sh +64 -0
- package/docs/sources/workflows.yml +458 -0
- package/docs/talks1/_body.txt +14 -0
- package/docs/talks1/_index.md +15 -0
- package/docs/talks1/_screenshot.png +0 -0
- package/docs/talks1/assistant-01.md +5 -0
- package/docs/talks1/assistant-02.md +5 -0
- package/docs/talks1/assistant-03.md +531 -0
- package/docs/talks1/assistant-04.md +26 -0
- package/docs/talks1/assistant-05.md +774 -0
- package/docs/talks1/assistant-06.md +1718 -0
- package/docs/talks1/scrape-share.cjs +185 -0
- package/docs/talks1/scrape-share.ts +183 -0
- package/docs/talks1/user-01.md +3 -0
- package/docs/talks1/user-02.md +3 -0
- package/docs/talks1/user-03.md +88 -0
- package/docs/talks1/user-04.md +3 -0
- package/docs/talks1/user-05.md +3 -0
- package/docs/talks1/user-06.md +88 -0
- package/docs/talks1/user-07.md +88 -0
- package/docs/talks2/_body.txt +14 -0
- package/docs/talks2/_index.md +16 -0
- package/docs/talks2/_screenshot.png +0 -0
- package/docs/talks2/assistant-01.md +5 -0
- package/docs/talks2/assistant-02.md +5 -0
- package/docs/talks2/assistant-03.md +424 -0
- package/docs/talks2/assistant-04.md +598 -0
- package/docs/talks2/assistant-05.md +1280 -0
- package/docs/talks2/assistant-06.md +1227 -0
- package/docs/talks2/assistant-07.md +1252 -0
- package/docs/talks2/user-01.md +3 -0
- package/docs/talks2/user-02.md +3 -0
- package/docs/talks2/user-03.md +88 -0
- package/docs/talks2/user-04.md +88 -0
- package/docs/talks2/user-05.md +88 -0
- package/docs/talks2/user-06.md +88 -0
- package/docs/talks2/user-07.md +3 -0
- package/docs/talks3/_body.txt +467 -0
- package/docs/talks3/_index.md +10 -0
- package/docs/talks3/_screenshot.png +0 -0
- package/docs/talks3/assistant-01.md +417 -0
- package/docs/talks3/assistant-02.md +417 -0
- package/docs/talks3/assistant-03.md +29 -0
- package/docs/talks3/assistant-04.md +727 -0
- package/docs/talks3/user-01.md +88 -0
- package/docs/talks3/user-02.md +88 -0
- package/docs/talks3/user-03.md +3 -0
- package/docs/talks3/user-04.md +3 -0
- package/docs/talks4/_body.txt +14 -0
- package/docs/talks4/_index.md +12 -0
- package/docs/talks4/_screenshot.png +0 -0
- package/docs/talks4/assistant-01.md +5 -0
- package/docs/talks4/assistant-02.md +5 -0
- package/docs/talks4/assistant-03.md +35 -0
- package/docs/talks4/assistant-04.md +512 -0
- package/docs/talks4/assistant-05.md +599 -0
- package/docs/talks4/user-01.md +3 -0
- package/docs/talks4/user-02.md +3 -0
- package/docs/talks4/user-03.md +88 -0
- package/docs/talks4/user-04.md +88 -0
- package/docs/talks4/user-05.md +7 -0
- package/docs/talks5/_body.txt +14 -0
- package/docs/talks5/_index.md +13 -0
- package/docs/talks5/_screenshot.png +0 -0
- package/docs/talks5/assistant-01.md +5 -0
- package/docs/talks5/assistant-02.md +5 -0
- package/docs/talks5/assistant-03.md +690 -0
- package/docs/talks5/assistant-04.md +758 -0
- package/docs/talks5/assistant-05.md +974 -0
- package/docs/talks5/user-01.md +3 -0
- package/docs/talks5/user-02.md +3 -0
- package/docs/talks5/user-03.md +105 -0
- package/docs/talks5/user-04.md +105 -0
- package/docs/talks5/user-05.md +63 -0
- package/docs/talks5/user-06.md +105 -0
- package/docs/talks6/_body.txt +14 -0
- package/docs/talks6/_index.md +9 -0
- package/docs/talks6/_screenshot.png +0 -0
- package/docs/talks6/assistant-01.md +5 -0
- package/docs/talks6/assistant-02.md +5 -0
- package/docs/talks6/assistant-03.md +1499 -0
- package/docs/talks6/user-01.md +3 -0
- package/docs/talks6/user-02.md +3 -0
- package/docs/talks6/user-03.md +88 -0
- package/docs/talks6/user-04.md +88 -0
- package/docs/talks7/_body.txt +14 -0
- package/docs/talks7/_index.md +10 -0
- package/docs/talks7/_screenshot.png +0 -0
- package/docs/talks7/assistant-01.md +5 -0
- package/docs/talks7/assistant-02.md +5 -0
- package/docs/talks7/assistant-03.md +523 -0
- package/docs/talks7/assistant-04.md +617 -0
- package/docs/talks7/user-01.md +3 -0
- package/docs/talks7/user-02.md +3 -0
- package/docs/talks7/user-03.md +105 -0
- package/docs/talks7/user-04.md +67 -0
- package/docs/talks8/conversa1.txt +1322 -0
- package/docs/talks8/conversa2.txt +237 -0
- package/docs/talks9/Beyond the Obvious_ 50 Plataformas Auto-Hospedadas de Forja de C/303/263digo para Al/303/251m de Gitea e GitLab.md" +174 -0
- package/docs/talks9/De NPM a Multi-Linguagem_ Uma Arquitetura T/303/251cnica para a Execu/303/247/303/243o Integrada de C/303/263digo no Ecossistema Node.js.md" +59 -0
- package/docs/talks9/De NPM a VMs Virtuais_ Uma An/303/241lise Arquitet/303/264nica para a Realiza/303/247/303/243o do Ciclo de Vida do Projeto SADDLE.md" +91 -0
- package/docs/talks9/Mapeamento da Engrenagem Computacional_ Uma Arquitetura para Execu/303/247/303/243o Isolada e Persist/303/252ncia em Ambientes Distribu/303/255dos.md" +116 -0
- package/docs/talks9/O Cen/303/241rio Pr/303/241tico do SADDLE_ Uma An/303/241lise de Viabilidade e Modelo de Ciclo de Vida Integrado.md" +128 -0
- package/docs/talks9/README (2).md +489 -0
- package/docs/talks9/README.md +198 -0
- package/docs/talks9/Viabilidade do Saddle_ Uma An/303/241lise T/303/251cnica da Transforma/303/247/303/243o de Armazenamento Remoto em Mem/303/263ria Computacional.md" +80 -0
- package/docs/talks9/conversa.txt +544 -0
- package/docs/talks9/other (2).md +39 -0
- package/docs/talks9/other.md +57 -0
- package/docs/talks9/outro.txt +24 -0
- package/extension/README.md +3 -3
- package/extension/serviceworker.js +63 -4
- package/extension/worker.js +3 -1
- package/package.json +12 -2
- package/packager/manifest.js +1 -1
- package/scrape/agent.ts +122 -0
- package/scrape/batch.ts +79 -0
- package/scrape/biome.json +76 -0
- package/scrape/browser.ts +222 -0
- package/scrape/cache.ts +84 -0
- package/scrape/chunking.ts +193 -0
- package/scrape/cli.ts +105 -0
- package/scrape/crawler.ts +115 -0
- package/scrape/dev-server.ts +94 -0
- package/scrape/errors.ts +132 -0
- package/scrape/events.ts +26 -0
- package/scrape/extract.ts +165 -0
- package/scrape/fetch.ts +105 -0
- package/scrape/formats.ts +85 -0
- package/scrape/headers.ts +71 -0
- package/scrape/index.ts +92 -0
- package/scrape/jsdom.d.ts +6 -0
- package/scrape/llms-txt.ts +84 -0
- package/scrape/middleware.ts +90 -0
- package/scrape/package-lock.json +9397 -0
- package/scrape/package.json +1420 -0
- package/scrape/pool.ts +95 -0
- package/scrape/port.ts +18 -0
- package/scrape/proxy.ts +103 -0
- package/scrape/rate-limiter.ts +95 -0
- package/scrape/renderer.ts +194 -0
- package/scrape/retry.ts +64 -0
- package/scrape/robots.ts +137 -0
- package/scrape/scrape.ts +123 -0
- package/scrape/serialize.ts +310 -0
- package/scrape/server.ts +137 -0
- package/scrape/session.ts +109 -0
- package/scrape/sitemap.ts +131 -0
- package/scrape/tokens.ts +45 -0
- package/scrape/tsconfig.json +28 -0
- package/scrape/types.ts +214 -0
- package/scrape/utils.ts +77 -0
- package/scrape/vite.config.ts +55 -0
- package/scrape/vitest.config.ts +17 -0
- package/workflow/templates.js +4 -4
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
# Pesquisa: Server, API e Distribuição (2026)
|
|
2
|
+
|
|
3
|
+
## 1. Framework Comparison
|
|
4
|
+
|
|
5
|
+
### Performance Benchmarks (2026)
|
|
6
|
+
|
|
7
|
+
| Framework | Runtime | req/s (Node.js) | req/s (Bun) | Bundle Size | TypeScript | GitHub Stars | npm Weekly Downloads |
|
|
8
|
+
|-----------|---------|-----------------|-------------|-------------|------------|--------------|---------------------|
|
|
9
|
+
| **Elysia** | Bun-native | N/A | ~184K (peak) | ~30 KB | Native (best) | ~12K | ~300K-700K |
|
|
10
|
+
| **Hono** | Multi-runtime | ~45K-78K | ~55K-62K | ~12-15 KB | First-class | ~22-28K | ~1.2-9.3M |
|
|
11
|
+
| **Fastify** | Node.js | ~42K-114K | ~85K | ~50 KB+ | First-class | ~33-36K | ~4-11M |
|
|
12
|
+
| **Express** | Node.js | ~8K-21K | ~15-21K | ~200 KB+ | Via @types | ~65-69K | ~30-35M |
|
|
13
|
+
|
|
14
|
+
### Recommendation Matrix for Scraping APIs
|
|
15
|
+
|
|
16
|
+
| Use Case | Top Pick | Runner-up | Reason |
|
|
17
|
+
|----------|----------|-----------|--------|
|
|
18
|
+
| **Cloudflare Workers / Edge API** | Hono | — | Native support, 14 KB bundle, runs everywhere |
|
|
19
|
+
| **New Node.js REST API** | Fastify / Hono | Fastify | 3-10x faster than Express, schema validation |
|
|
20
|
+
| **Bun production API** | Elysia | Hono | Peak Bun throughput, end-to-end type safety |
|
|
21
|
+
| **Existing Express codebase** | Express | Fastify | Lowest migration cost, largest ecosystem |
|
|
22
|
+
| **Multi-runtime / portable** | Hono | — | Same code on Node, Bun, Deno, CF Workers, Lambda |
|
|
23
|
+
| **Enterprise large-scale** | Fastify | Express | Plugin system, stability, 300+ plugins |
|
|
24
|
+
|
|
25
|
+
### Framework-Specific Middleware
|
|
26
|
+
|
|
27
|
+
| Capability | Express | Fastify | Hono | Elysia |
|
|
28
|
+
|-----------|---------|---------|------|--------|
|
|
29
|
+
| CORS | `cors` (npm) | `@fastify/cors` | Built-in `hono/cors` | `@elysiajs/cors` |
|
|
30
|
+
| Rate limiting | `express-rate-limit` | `@fastify/rate-limit` | `hono-rate-limiter` | Custom |
|
|
31
|
+
| JWT Auth | `passport`, `jsonwebtoken` | `@fastify/jwt` | `hono/jwt` | `@elysiajs/jwt` |
|
|
32
|
+
| WebSocket | `ws` (standalone) | `@fastify/websocket` | `hono/ws` | Native |
|
|
33
|
+
| OpenAPI/Swagger | `swagger-jsdoc` | `@fastify/swagger` | `@hono/swagger-ui` | `@elysiajs/swagger` |
|
|
34
|
+
|
|
35
|
+
## 2. Rate Limiting on the API Layer
|
|
36
|
+
|
|
37
|
+
### Packages by Framework
|
|
38
|
+
|
|
39
|
+
| Package | Framework | Algorithm |
|
|
40
|
+
|---------|-----------|-----------|
|
|
41
|
+
| `express-rate-limit` 8.5.x | Express | Fixed window, pluggable stores |
|
|
42
|
+
| `@fastify/rate-limit` | Fastify | Fixed window, Redis-backed |
|
|
43
|
+
| `hono-rate-limiter` 0.5.x | Hono | Fixed window, Redis/CF KV |
|
|
44
|
+
| `universal-rate-limit` | All | Fixed/sliding window, token bucket |
|
|
45
|
+
|
|
46
|
+
### Hono Rate Limiting Example
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { rateLimiter } from 'hono-rate-limiter';
|
|
50
|
+
import { RedisStore } from '@hono-rate-limiter/redis';
|
|
51
|
+
import { Redis } from '@upstash/redis';
|
|
52
|
+
|
|
53
|
+
const client = new Redis({ url: REDIS_URL, token: REDIS_TOKEN });
|
|
54
|
+
|
|
55
|
+
app.use('/api/*', rateLimiter({
|
|
56
|
+
windowMs: 60_000,
|
|
57
|
+
limit: 100,
|
|
58
|
+
store: new RedisStore({ client }),
|
|
59
|
+
keyGenerator: (c) => c.var.accountId,
|
|
60
|
+
standardHeaders: 'draft-7',
|
|
61
|
+
}));
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Tiered Rate Limiting
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// Global limit
|
|
68
|
+
app.use(rateLimiter({ windowMs: 60_000, limit: 1000 }));
|
|
69
|
+
|
|
70
|
+
// Per-endpoint for expensive operations
|
|
71
|
+
app.post('/v1/scrape', rateLimiter({
|
|
72
|
+
windowMs: 60_000,
|
|
73
|
+
limit: 10,
|
|
74
|
+
keyGenerator: (c) => c.var.userId,
|
|
75
|
+
}));
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## 3. Streaming Responses
|
|
79
|
+
|
|
80
|
+
### Chunked Transfer Encoding
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
app.get('/api/export', async (req, res) => {
|
|
84
|
+
res.writeHead(200, {
|
|
85
|
+
'Content-Type': 'application/json',
|
|
86
|
+
'Transfer-Encoding': 'chunked',
|
|
87
|
+
});
|
|
88
|
+
res.write('[\n');
|
|
89
|
+
let first = true;
|
|
90
|
+
for await (const batch of scraper.streamResults(url)) {
|
|
91
|
+
for (const row of batch) {
|
|
92
|
+
res.write(first ? '' : ',\n');
|
|
93
|
+
res.write(JSON.stringify(row));
|
|
94
|
+
first = false;
|
|
95
|
+
}
|
|
96
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
97
|
+
}
|
|
98
|
+
res.write('\n]');
|
|
99
|
+
res.end();
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Server-Sent Events (SSE)
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
app.get('/api/scrape/stream', async (req, res) => {
|
|
107
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
108
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
109
|
+
res.setHeader('Connection', 'keep-alive');
|
|
110
|
+
res.setHeader('X-Accel-Buffering', 'no');
|
|
111
|
+
res.flushHeaders();
|
|
112
|
+
|
|
113
|
+
const scraper = scrapeWithProgress(url);
|
|
114
|
+
let eventId = 0;
|
|
115
|
+
|
|
116
|
+
for await (const event of scraper) {
|
|
117
|
+
res.write(`id: ${++eventId}\n`);
|
|
118
|
+
res.write(`event: ${event.type}\n`);
|
|
119
|
+
res.write(`data: ${JSON.stringify(event.data)}\n\n`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
res.write('event: done\ndata: {"status":"complete"}\n\n');
|
|
123
|
+
res.end();
|
|
124
|
+
req.on('close', () => scraper.abort());
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### SSE Gotchas
|
|
129
|
+
- Nginx buffers by default — add `proxy_buffering off;` and `X-Accel-Buffering: no`
|
|
130
|
+
- Cloudflare free tier has 100s idle timeout — send `: keepalive\n\n` every 30s
|
|
131
|
+
- Browsers limit 6 concurrent SSE connections per origin (HTTP/1.1)
|
|
132
|
+
- Skip streaming for responses under 50KB
|
|
133
|
+
|
|
134
|
+
## 4. WebSocket for Real-Time Progress
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { WebSocketServer } from 'ws';
|
|
138
|
+
|
|
139
|
+
const wss = new WebSocketServer({ port: 3001 });
|
|
140
|
+
|
|
141
|
+
wss.on('connection', (ws) => {
|
|
142
|
+
ws.isAlive = true;
|
|
143
|
+
ws.on('pong', () => { ws.isAlive = true; });
|
|
144
|
+
ws.on('message', (data) => {
|
|
145
|
+
const msg = JSON.parse(data);
|
|
146
|
+
if (msg.type === 'subscribe') ws.jobId = msg.jobId;
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Heartbeat
|
|
151
|
+
setInterval(() => {
|
|
152
|
+
wss.clients.forEach(ws => {
|
|
153
|
+
if (!ws.isAlive) return ws.terminate();
|
|
154
|
+
ws.isAlive = false;
|
|
155
|
+
ws.ping();
|
|
156
|
+
});
|
|
157
|
+
}, 30_000);
|
|
158
|
+
|
|
159
|
+
function broadcastJobUpdate(jobId: string, data: unknown) {
|
|
160
|
+
wss.clients.forEach(ws => {
|
|
161
|
+
if (ws.jobId === jobId && ws.readyState === 1) {
|
|
162
|
+
ws.send(JSON.stringify({ type: 'job_update', ...data }));
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## 5. Authentication
|
|
169
|
+
|
|
170
|
+
### API Key Pattern
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
async function verifyApiKey(request: any, reply: any) {
|
|
174
|
+
const apiKey = request.headers['x-api-key'];
|
|
175
|
+
if (!apiKey) return reply.code(401).send({ error: 'API key required' });
|
|
176
|
+
const user = await redis.hgetall(`apikey:${apiKey}`);
|
|
177
|
+
if (!user.user_id) return reply.code(403).send({ error: 'Invalid API key' });
|
|
178
|
+
request.user = { userId: user.user_id };
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### JWT Pattern
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
import jwt from 'jsonwebtoken';
|
|
186
|
+
|
|
187
|
+
function validateJwt(request: any, reply: any) {
|
|
188
|
+
const token = request.headers.authorization?.replace('Bearer ', '');
|
|
189
|
+
if (!token) return reply.code(401).send({ error: 'Token required' });
|
|
190
|
+
try {
|
|
191
|
+
request.user = jwt.verify(token, SECRET, { algorithms: ['HS256'] });
|
|
192
|
+
} catch (e) {
|
|
193
|
+
return reply.code(401).send({ error: 'Invalid token' });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## 6. REST API Design
|
|
199
|
+
|
|
200
|
+
### Standard Endpoints
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
POST /v1/scrape # Sync scrape
|
|
204
|
+
POST /v1/scrape/async # Async scrape (returns task ID)
|
|
205
|
+
GET /v1/scrape/:taskId # Get async job result
|
|
206
|
+
POST /v1/batch # Batch scrape multiple URLs
|
|
207
|
+
POST /v1/crawl # Start recursive crawl
|
|
208
|
+
GET /v1/crawl/:crawlId # Get crawl status/results
|
|
209
|
+
GET /health # Health check
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Request/Response Schema
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
// POST /v1/scrape
|
|
216
|
+
{
|
|
217
|
+
"url": "https://example.com",
|
|
218
|
+
"useBrowser": false,
|
|
219
|
+
"format": "markdown",
|
|
220
|
+
"waitFor": "h1",
|
|
221
|
+
"timeout": 30,
|
|
222
|
+
"proxy": "http://user:pass@proxy:port"
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Response
|
|
226
|
+
{
|
|
227
|
+
"status": "ok",
|
|
228
|
+
"data": {
|
|
229
|
+
"url": "https://example.com",
|
|
230
|
+
"markdown": "# Content...",
|
|
231
|
+
"metadata": { "title": "...", "description": "..." },
|
|
232
|
+
"links": [...],
|
|
233
|
+
"images": [...]
|
|
234
|
+
},
|
|
235
|
+
"timing": { "duration_ms": 1234 }
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Error Response Format
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"error": {
|
|
244
|
+
"code": "RATE_LIMIT_EXCEEDED",
|
|
245
|
+
"message": "Rate limit exceeded. Try again in 60 seconds.",
|
|
246
|
+
"retry_after": 60,
|
|
247
|
+
"request_id": "req_abc123"
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## 7. Docker Deployment
|
|
253
|
+
|
|
254
|
+
### Multi-Stage Dockerfile
|
|
255
|
+
|
|
256
|
+
```dockerfile
|
|
257
|
+
# syntax=docker/dockerfile:1.4
|
|
258
|
+
FROM node:22-alpine AS deps
|
|
259
|
+
WORKDIR /app
|
|
260
|
+
COPY package.json package-lock.json ./
|
|
261
|
+
RUN npm ci --frozen-lockfile
|
|
262
|
+
|
|
263
|
+
FROM node:22-alpine AS builder
|
|
264
|
+
WORKDIR /app
|
|
265
|
+
COPY --from=deps /app/node_modules ./node_modules
|
|
266
|
+
COPY . .
|
|
267
|
+
RUN npm run build
|
|
268
|
+
|
|
269
|
+
FROM node:22-alpine AS runner
|
|
270
|
+
WORKDIR /app
|
|
271
|
+
RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 appuser
|
|
272
|
+
ENV NODE_ENV=production
|
|
273
|
+
COPY package.json package-lock.json ./
|
|
274
|
+
RUN npm ci --frozen-lockfile --omit=dev && npm cache clean --force
|
|
275
|
+
COPY --from=builder /app/dist ./dist
|
|
276
|
+
RUN chown -R appuser:nodejs /app
|
|
277
|
+
USER appuser
|
|
278
|
+
EXPOSE 3000
|
|
279
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
|
|
280
|
+
CMD node -e "require('http').get('http://localhost:3000/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"
|
|
281
|
+
CMD ["node", "--enable-source-maps", "dist/index.js"]
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Docker Compose
|
|
285
|
+
|
|
286
|
+
```yaml
|
|
287
|
+
services:
|
|
288
|
+
api:
|
|
289
|
+
build: .
|
|
290
|
+
ports: ["3000:3000"]
|
|
291
|
+
environment:
|
|
292
|
+
- DATABASE_URL=postgresql://user:pass@postgres:5432/scraper
|
|
293
|
+
- REDIS_URL=redis://redis:6379
|
|
294
|
+
depends_on: [postgres, redis]
|
|
295
|
+
|
|
296
|
+
postgres:
|
|
297
|
+
image: postgres:16-alpine
|
|
298
|
+
volumes: [pgdata:/var/lib/postgresql/data]
|
|
299
|
+
environment:
|
|
300
|
+
POSTGRES_DB: scraper
|
|
301
|
+
POSTGRES_USER: user
|
|
302
|
+
POSTGRES_PASSWORD: pass
|
|
303
|
+
|
|
304
|
+
redis:
|
|
305
|
+
image: redis:7-alpine
|
|
306
|
+
volumes: [redis_data:/data]
|
|
307
|
+
|
|
308
|
+
volumes:
|
|
309
|
+
pgdata:
|
|
310
|
+
redis_data:
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## 8. Serverless Deployment
|
|
314
|
+
|
|
315
|
+
| Platform | Best For | Notes |
|
|
316
|
+
|----------|----------|-------|
|
|
317
|
+
| AWS Lambda (container) | Event-driven scraping | Up to 15 min timeout |
|
|
318
|
+
| Google Cloud Run | Serverless pipeline | Separate scraper + API services |
|
|
319
|
+
| Cloudflare Workers | Edge scraping API | Hono is best choice, 14 KB bundle |
|
|
320
|
+
| Fly.io | Persistent containers | Good for browser-based scraping |
|
|
321
|
+
| Render | Full-stack | Postgres + Redis included |
|
|
322
|
+
|
|
323
|
+
## 9. Observability
|
|
324
|
+
|
|
325
|
+
- Structured JSON logging (Pino for Fastify, built-in for Hono)
|
|
326
|
+
- OpenTelemetry for distributed tracing
|
|
327
|
+
- Metrics: request count, latency p50/p95/p99, scrape success rate
|
|
328
|
+
- Per-user usage dashboards
|
|
329
|
+
- Alerting on anomalous patterns
|
|
330
|
+
|
|
331
|
+
## 10. Security Checklist
|
|
332
|
+
|
|
333
|
+
- [ ] HTTPS everywhere
|
|
334
|
+
- [ ] API key or JWT authentication
|
|
335
|
+
- [ ] Rate limiting per user/API key
|
|
336
|
+
- [ ] Input validation on all endpoints
|
|
337
|
+
- [ ] SSRF protection (prevent scraping internal networks)
|
|
338
|
+
- [ ] CORS configured properly
|
|
339
|
+
- [ ] No secrets in Docker images
|
|
340
|
+
- [ ] Request ID tracking for audit trails
|
|
341
|
+
- [ ] Respect robots.txt
|
|
342
|
+
- [ ] Data retention limits and deletion schedules
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# Pesquisa: Compilação e Build de Bibliotecas (2026)
|
|
2
|
+
|
|
3
|
+
## 1. Bun `--compile` — Standalone Executables
|
|
4
|
+
|
|
5
|
+
Bun's bundler has a mature `--compile` flag that generates standalone binaries with the Bun runtime embedded.
|
|
6
|
+
|
|
7
|
+
### Basic Usage
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bun build ./cli.ts --compile --outfile mycli
|
|
11
|
+
./mycli
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### Cross-Compilation Targets
|
|
15
|
+
|
|
16
|
+
| Target | OS | Arch |
|
|
17
|
+
|--------|-----|------|
|
|
18
|
+
| `bun-linux-x64` | Linux | x64 (glibc) |
|
|
19
|
+
| `bun-linux-arm64` | Linux | arm64 |
|
|
20
|
+
| `bun-windows-x64` | Windows | x64 (.exe) |
|
|
21
|
+
| `bun-windows-arm64` | Windows | arm64 |
|
|
22
|
+
| `bun-darwin-x64` | macOS | x64 |
|
|
23
|
+
| `bun-darwin-arm64` | macOS | arm64 |
|
|
24
|
+
| `bun-linux-x64-musl` | Linux | x64 (Alpine) |
|
|
25
|
+
| `bun-linux-arm64-musl` | Linux | arm64 (musl) |
|
|
26
|
+
|
|
27
|
+
### Advanced Features
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Bytecode compilation (2x faster startup)
|
|
31
|
+
bun build --compile --bytecode ./app.ts --outfile myapp
|
|
32
|
+
|
|
33
|
+
# Cross-compile
|
|
34
|
+
bun build --compile --target=bun-linux-x64 ./src/cli.ts --outfile myapp-linux
|
|
35
|
+
|
|
36
|
+
# Embed files
|
|
37
|
+
bun build --compile ./index.ts ./public/**/*.png
|
|
38
|
+
|
|
39
|
+
# Build-time constants
|
|
40
|
+
bun build --compile --define BUILD_VERSION='"1.2.3"' ./app.ts --outfile myapp
|
|
41
|
+
|
|
42
|
+
# Minification + sourcemaps
|
|
43
|
+
bun build --compile --minify --sourcemap ./app.ts --outfile myapp
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Key Features (2026)
|
|
47
|
+
- Build-time constants via `--define`
|
|
48
|
+
- Bytecode compilation for fast startup
|
|
49
|
+
- Embed files/assets with `with { type: "file" }` import attributes
|
|
50
|
+
- Embed SQLite databases with `with { type: "sqlite", embed: "true" }`
|
|
51
|
+
- Embed N-API addons (`.node` files) directly
|
|
52
|
+
- Full-stack executables (v1.2.17+): embed HTML/CSS/JS frontend
|
|
53
|
+
- Windows metadata (icon, hideConsole, title)
|
|
54
|
+
- macOS codesigning support
|
|
55
|
+
|
|
56
|
+
## 2. tsup vs tsdown
|
|
57
|
+
|
|
58
|
+
### tsup (legacy, v8.5.1 — no longer actively maintained)
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
// tsup.config.ts
|
|
62
|
+
import { defineConfig } from 'tsup'
|
|
63
|
+
export default defineConfig({
|
|
64
|
+
entry: ['src/index.ts', 'src/cli.ts'],
|
|
65
|
+
format: ['cjs', 'esm'],
|
|
66
|
+
dts: true,
|
|
67
|
+
splitting: true,
|
|
68
|
+
clean: true,
|
|
69
|
+
minify: true,
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### tsdown (recommended successor, Rolldown-based)
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npm i tsdown -D
|
|
77
|
+
npx tsdown-migrate # migrate from tsup
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
| Option | tsup | tsdown |
|
|
81
|
+
|--------|------|--------|
|
|
82
|
+
| Default format | `'cjs'` | `'esm'` |
|
|
83
|
+
| Clean | `false` | `true` |
|
|
84
|
+
| DTS | `false` | Auto-enabled |
|
|
85
|
+
| Target | none | Auto-reads from `engines.node` |
|
|
86
|
+
| Backend | esbuild | Rolldown (Rust) |
|
|
87
|
+
|
|
88
|
+
### tsdown Unique Features
|
|
89
|
+
- **`exe: true`** — bundle as Node.js SEA
|
|
90
|
+
- **`exports: true`** — auto-generate `package.json` exports
|
|
91
|
+
- **`publint`/`attw`** — validate package
|
|
92
|
+
- **`devtools`** — Vite DevTools integration
|
|
93
|
+
- **Full CSS pipeline** with preprocessors
|
|
94
|
+
- **Lifecycle hooks**: `build:prepare`, `build:before`, `build:done`
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// tsdown.config.ts
|
|
98
|
+
import { defineConfig } from 'tsdown'
|
|
99
|
+
export default defineConfig({
|
|
100
|
+
entry: ['src/index.ts', 'src/cli.ts'],
|
|
101
|
+
format: ['cjs', 'esm'],
|
|
102
|
+
dts: true,
|
|
103
|
+
exe: false,
|
|
104
|
+
minify: true,
|
|
105
|
+
exports: true,
|
|
106
|
+
publint: true,
|
|
107
|
+
})
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## 3. Node.js SEA (Single Executable Applications)
|
|
111
|
+
|
|
112
|
+
### Step-by-Step Process
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# 1. Write your app
|
|
116
|
+
echo 'console.log("Hello!");' > hello.js
|
|
117
|
+
|
|
118
|
+
# 2. Bundle to single CJS file (esbuild/ncc/webpack)
|
|
119
|
+
npx esbuild hello.ts --bundle --platform=node --outfile=hello.js --format=cjs
|
|
120
|
+
|
|
121
|
+
# 3. Create sea-config.json
|
|
122
|
+
echo '{"main":"hello.js","output":"sea-prep.blob","disableExperimentalSEAWarning":true}' > sea-config.json
|
|
123
|
+
|
|
124
|
+
# 4. Generate the blob
|
|
125
|
+
node --experimental-sea-config sea-config.json
|
|
126
|
+
|
|
127
|
+
# 5. Copy node binary
|
|
128
|
+
cp $(command -v node) hello
|
|
129
|
+
|
|
130
|
+
# 6. Remove signature (macOS)
|
|
131
|
+
codesign --remove-signature hello
|
|
132
|
+
|
|
133
|
+
# 7. Inject blob with postject
|
|
134
|
+
npx postject hello NODE_SEA_BLOB sea-prep.blob \
|
|
135
|
+
--sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2
|
|
136
|
+
|
|
137
|
+
# 8. Re-sign (macOS)
|
|
138
|
+
codesign --sign - hello
|
|
139
|
+
|
|
140
|
+
# 9. Run
|
|
141
|
+
./hello
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### SEA v22+ Features
|
|
145
|
+
- **Assets** — embed files via `"assets"` config field
|
|
146
|
+
- **Startup snapshots** — `"useSnapshot": true`
|
|
147
|
+
- **V8 code cache** — `"useCodeCache": true`
|
|
148
|
+
- **Embedded `execArgv`** — `"execArgv": ["--no-warnings"]`
|
|
149
|
+
|
|
150
|
+
**Key limitation:** SEA only supports CommonJS. Must bundle everything into a single JS file first.
|
|
151
|
+
|
|
152
|
+
## 4. esbuild for Fast Bundling
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# Browser bundle
|
|
156
|
+
esbuild app.jsx --bundle --minify --sourcemap --target=chrome58 --outfile=out.js
|
|
157
|
+
|
|
158
|
+
# Node.js bundle
|
|
159
|
+
esbuild app.js --bundle --platform=node --target=node18 --outfile=out.js
|
|
160
|
+
|
|
161
|
+
# Leave dependencies external (for libraries)
|
|
162
|
+
esbuild app.jsx --bundle --platform=node --packages=external --outfile=out.js
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Key Features
|
|
166
|
+
- Extremely fast (written in Go, parallelized)
|
|
167
|
+
- TypeScript/JSX/TSX out of the box
|
|
168
|
+
- CSS bundling support
|
|
169
|
+
- Plugin system (`onResolve`, `onLoad`, `onStart`, `onEnd`)
|
|
170
|
+
- WASM version available (`esbuild-wasm`)
|
|
171
|
+
|
|
172
|
+
## 5. Creating CLI Tools Without Node.js
|
|
173
|
+
|
|
174
|
+
### Options Ranked
|
|
175
|
+
|
|
176
|
+
1. **Bun `--compile`** (recommended): True cross-compilation, supports Bun + Node.js APIs
|
|
177
|
+
2. **Node.js SEA**: Embeds full Node.js runtime, CJS only
|
|
178
|
+
3. **Deno compile**: Alternative, good for Deno-first projects
|
|
179
|
+
|
|
180
|
+
## 6. Native Dependencies in Compiled Binaries
|
|
181
|
+
|
|
182
|
+
### Strategies
|
|
183
|
+
|
|
184
|
+
1. **Replace with pure-JS alternatives:**
|
|
185
|
+
- `better-sqlite3` → `bun:sqlite` (Bun) or `sql.js` (WASM)
|
|
186
|
+
- `bcrypt` → `bcryptjs`
|
|
187
|
+
- `sharp` → pure-JS image libs
|
|
188
|
+
|
|
189
|
+
2. **Use WASM-based alternatives** — Many native libs now have WASM builds
|
|
190
|
+
|
|
191
|
+
3. **Bundle with `ncc` and mark as external:**
|
|
192
|
+
```bash
|
|
193
|
+
ncc build src/index.ts -o dist --external better-sqlite3
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
4. **With Bun compile**, embed `.node` file directly if you control the require path
|
|
197
|
+
|
|
198
|
+
## 7. WASM Builds
|
|
199
|
+
|
|
200
|
+
### Using tsdown with rolldown-plugin-wasm
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// tsdown.config.ts
|
|
204
|
+
import { wasm } from 'rolldown-plugin-wasm'
|
|
205
|
+
import { defineConfig } from 'tsdown'
|
|
206
|
+
|
|
207
|
+
export default defineConfig({
|
|
208
|
+
entry: ['./src/index.ts'],
|
|
209
|
+
plugins: [wasm({
|
|
210
|
+
maxFileSize: 14 * 1024,
|
|
211
|
+
targetEnv: 'auto',
|
|
212
|
+
})],
|
|
213
|
+
})
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Importing WASM Modules
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// Direct import
|
|
220
|
+
import { add } from './add.wasm'
|
|
221
|
+
add(1, 2)
|
|
222
|
+
|
|
223
|
+
// Async init
|
|
224
|
+
import init from './add.wasm?init'
|
|
225
|
+
const instance = await init()
|
|
226
|
+
instance.exports.add(1, 2)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## 8. Distribution Strategy Matrix
|
|
230
|
+
|
|
231
|
+
| Target | Tool | Output | Needs Runtime? |
|
|
232
|
+
|--------|------|--------|----------------|
|
|
233
|
+
| npm package | tsdown | ESM + CJS + DTS | Yes (consumer's bundler) |
|
|
234
|
+
| npm CLI tool | tsdown + bin field | JS + bin script | Yes (Node.js) |
|
|
235
|
+
| Standalone binary | Bun compile | Native executable | No |
|
|
236
|
+
| Node.js SEA | esbuild + postject | Modified node binary | Embedded Node.js |
|
|
237
|
+
| Browser bundle | esbuild/tsdown | ESM/IIFE JS | No (browser) |
|
|
238
|
+
| Edge/Workers | tsdown + WASM plugin | JS + .wasm | No (edge runtime) |
|
|
239
|
+
| Serverless | @vercel/ncc | Single JS file | Yes (Node.js Lambda) |
|
|
240
|
+
| Deno | deno compile | Native executable | No |
|
|
241
|
+
|
|
242
|
+
## 9. Recommended 2026 Toolchain
|
|
243
|
+
|
|
244
|
+
1. **tsdown** for library building (replaces tsup, uses Rolldown)
|
|
245
|
+
2. **esbuild** for fast development bundling
|
|
246
|
+
3. **Bun `--compile`** for standalone executables
|
|
247
|
+
4. **Node.js SEA** when Node.js-based standalone is needed
|
|
248
|
+
5. **rolldown-plugin-wasm** for WASM builds via tsdown
|
|
249
|
+
6. **@vercel/ncc** for single-file Node.js deployments
|