koonjs 0.6.2 → 0.7.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.
Files changed (2) hide show
  1. package/README.md +549 -0
  2. package/package.json +22 -13
package/README.md ADDED
@@ -0,0 +1,549 @@
1
+ # koon
2
+
3
+ [![npm](https://img.shields.io/npm/v/koonjs)](https://www.npmjs.com/package/koonjs)
4
+ [![PyPI](https://img.shields.io/pypi/v/koon)](https://pypi.org/project/koon/)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6
+ [![CI](https://img.shields.io/github/actions/workflow/status/scrape-hub/koon/ci.yml?label=CI)](https://github.com/scrape-hub/koon/actions)
7
+
8
+ An HTTP client that impersonates real browsers at the TLS, HTTP/2, and HTTP/3 fingerprint level.
9
+
10
+ Built in Rust on top of BoringSSL with native bindings for **Node.js**, **Python**, **R**, and a **CLI**. Passes Akamai, Cloudflare, and other bot detection systems by reproducing exact browser fingerprints — verified against real browser captures.
11
+
12
+ ## Install
13
+
14
+ **Node.js**
15
+ ```bash
16
+ npm install koonjs
17
+ ```
18
+
19
+ **Python**
20
+ ```bash
21
+ pip install koon
22
+ ```
23
+
24
+ **R**
25
+ ```r
26
+ # Install from source (requires Rust toolchain)
27
+ remotes::install_github("scrape-hub/koon", subdir = "crates/r")
28
+ ```
29
+
30
+ **CLI** — download from [Releases](https://github.com/scrape-hub/koon/releases), or:
31
+ ```bash
32
+ cargo install koon-cli
33
+ ```
34
+
35
+ ## Quick start
36
+
37
+ **Node.js**
38
+ ```javascript
39
+ import { Koon } from 'koonjs';
40
+
41
+ const client = new Koon({ browser: 'chrome145' });
42
+ const resp = await client.get('https://httpbin.org/json');
43
+ console.log(resp.ok); // true
44
+ console.log(resp.text()); // body as string
45
+ console.log(resp.json()); // parsed JSON
46
+ ```
47
+
48
+ **Python**
49
+ ```python
50
+ from koon import KoonSync
51
+
52
+ client = KoonSync("chrome145")
53
+ resp = client.get("https://httpbin.org/json")
54
+ print(resp.ok) # True
55
+ print(resp.json()) # parsed JSON
56
+ ```
57
+
58
+ **R**
59
+ ```r
60
+ library(koon)
61
+
62
+ client <- Koon$new("chrome145")
63
+ resp <- client$get("https://httpbin.org/json")
64
+ resp$ok # TRUE
65
+ resp$text # body as string
66
+ ```
67
+
68
+ **CLI**
69
+ ```bash
70
+ koon -b chrome145 https://example.com
71
+ ```
72
+
73
+ **Rust**
74
+ ```rust
75
+ use koon_core::{Client, Chrome};
76
+
77
+ let client = Client::new(Chrome::v145_windows())?;
78
+ let r = client.get("https://example.com").await?;
79
+ ```
80
+
81
+ ## What it does
82
+
83
+ koon reproduces three fingerprint layers that bot detection systems check:
84
+
85
+ | Layer | What's fingerprinted | How koon matches it |
86
+ |-------|---------------------|-------------------|
87
+ | **TLS** | Cipher suites, curves, extensions, ALPN, GREASE, ALPS | BoringSSL with per-browser config (JA3/JA4 verified) |
88
+ | **HTTP/2** | SETTINGS order, pseudo-header order, WINDOW_UPDATE, PRIORITY frames | Forked h2 crate with header ordering API (Akamai hash verified) |
89
+ | **HTTP/3** | QUIC transport params, H3 settings | Quinn + h3 with browser-matching config |
90
+
91
+ All fingerprints are tested against hashes captured from real browsers. 10 integration tests verify JA3N, JA4, and Akamai hashes for Chrome, Firefox, Safari, Edge, and Opera.
92
+
93
+ ## Supported browsers
94
+
95
+ | Browser | Versions | Platforms | Profiles |
96
+ |---------|----------|-----------|----------|
97
+ | Chrome | 131 – 145 | Windows, macOS, Linux, Android | 60 |
98
+ | Firefox | 135 – 148 | Windows, macOS, Linux, Android | 56 |
99
+ | Safari | 15.6 – 18.3 | macOS, iOS | 15 |
100
+ | Edge | 131 – 145 | Windows, macOS | 30 |
101
+ | Opera | 124 – 127 | Windows, macOS, Linux | 12 |
102
+ | OkHttp | 4, 5 | Android | 2 |
103
+
104
+ **175 profiles** total. Use `koon --list-browsers` (CLI) to see all profiles.
105
+
106
+ ### Profile naming
107
+
108
+ Format: `{browser}{version}{-os}` — all parts except the browser name are optional. Both `chrome145-macos` and `chrome145macos` work (dash is optional).
109
+
110
+ **Desktop browsers with OS variants:**
111
+
112
+ | Browser | Default (macOS) | Windows | macOS | Linux |
113
+ |---------|----------------|---------|-------|-------|
114
+ | Chrome 145 | `chrome145` | `chrome145-windows` | `chrome145-macos` | `chrome145-linux` |
115
+ | Firefox 148 | `firefox148` | `firefox148-windows` | `firefox148-macos` | `firefox148-linux` |
116
+ | Edge 145 | `edge145` | `edge145-windows` | `edge145-macos` | — |
117
+ | Opera 127 | `opera127` | `opera127-windows` | `opera127-macos` | `opera127-linux` |
118
+ | Safari 18.3 | `safari183` | — | `safari183-macos` | — |
119
+
120
+ **Mobile browsers:**
121
+
122
+ | Browser | Example |
123
+ |---------|---------|
124
+ | Chrome Mobile (Android) | `chrome-mobile145` |
125
+ | Firefox Mobile (Android) | `firefox-mobile148` |
126
+ | Safari Mobile (iOS) | `safari-mobile183` |
127
+
128
+ **OkHttp (Android apps):**
129
+
130
+ | Version | Name |
131
+ |---------|------|
132
+ | OkHttp 4 | `okhttp4` |
133
+ | OkHttp 5 | `okhttp5` |
134
+
135
+ **Shorthand** — omit the version to get the latest:
136
+
137
+ | Shorthand | Resolves to |
138
+ |-----------|-------------|
139
+ | `chrome` | Chrome 145 macOS |
140
+ | `firefox` | Firefox 148 macOS |
141
+ | `safari` | Safari 18.3 macOS |
142
+ | `edge` | Edge 145 macOS |
143
+ | `opera` | Opera 127 macOS |
144
+ | `chrome-mobile` | Chrome Mobile 145 Android |
145
+ | `firefox-mobile` | Firefox Mobile 148 Android |
146
+ | `safari-mobile` | Safari Mobile 18.3 iOS |
147
+ | `okhttp` | OkHttp 5 |
148
+
149
+ ## Features
150
+
151
+ - **TLS fingerprint** — cipher list, curves, sigalgs, extension order, GREASE, ALPS, cert compression, delegated credentials
152
+ - **HTTP/2 fingerprint** — SETTINGS order, pseudo-header order, stream dependencies, priority frames, window sizes
153
+ - **HTTP/3 (QUIC)** — Alt-Svc discovery, QUIC transport parameter fingerprinting, H3 connection pooling
154
+ - **Header order preservation** — HTTP/2 (via forked h2) and HTTP/1.1
155
+ - **Encrypted Client Hello** — real ECH from DNS HTTPS records, with GREASE fallback
156
+ - **DNS-over-HTTPS** — Cloudflare and Google resolvers with ECH config discovery
157
+ - **TLS session resumption** — session ticket caching across requests
158
+ - **Cookie jar** — automatic persistence with domain/path/expiry/Secure/HttpOnly/SameSite
159
+ - **Proxy** — HTTP CONNECT and SOCKS5, with H3 fallback to H2 through proxies
160
+ - **MITM proxy server** — local proxy that re-sends all traffic through koon's fingerprinted stack
161
+ - **WebSocket** — `wss://` connections with browser-matching TLS handshake
162
+ - **Streaming responses** — chunked body streaming with async iterator support
163
+ - **Multipart form-data** — file uploads with custom content types
164
+ - **Per-request headers, timeout, and proxy** — override defaults per request without affecting the client
165
+ - **Ergonomic response API** — `ok`, `text()`, `json()`, `header()` on every response
166
+ - **Session persistence** — save/load cookies and TLS session tickets to JSON
167
+ - **Fingerprint randomization** — slight jitter on UA build number, accept-language q-values, H2 window sizes
168
+ - **Response decompression** — gzip, brotli, deflate, zstd (automatic)
169
+ - **Local address binding** — bind outgoing connections to a specific local IP (multi-IP servers, IP rotation)
170
+ - **Connection pooling** — H3 multiplexed + H2 multiplexed + H1.1 keep-alive
171
+ - **Custom redirect hook** — `onRedirect(status, url, headers) → bool` to intercept and stop redirects (captcha detection, geo-block handling)
172
+ - **Automatic retry** — retry on transport errors (connection, TLS, timeout) with automatic proxy rotation
173
+ - **Request hooks** — `onRequest`/`onResponse` observe-only callbacks for logging and debugging
174
+ - **Proxy rotation** — round-robin over multiple proxy URLs, proxy-aware connection pool
175
+ - **Bandwidth tracking** — per-request `bytesSent`/`bytesReceived` + cumulative counters on the client
176
+ - **String body** — `post()`, `put()`, `patch()` accept strings directly (no `Buffer.from()` needed)
177
+ - **User-Agent property** — `client.userAgent` exposes the profile UA for Puppeteer/Playwright sync
178
+ - **Geo-locale matching** — `locale: 'fr-FR'` generates Accept-Language matching proxy geography
179
+ - **Structured errors** — machine-readable `[CODE]` prefix on all errors (TIMEOUT, TLS_ERROR, PROXY_ERROR, etc.)
180
+ - **Connection info** — `resp.tlsResumed` and `resp.connectionReused` for debugging connection behavior
181
+ - **CONNECT proxy headers** — custom headers in the HTTP CONNECT tunnel (session IDs, geo-targeting for Bright Data, Oxylabs)
182
+ - **IPv4/IPv6 toggle** — restrict DNS resolution to a specific IP version
183
+ - **Mobile browser profiles** — Chrome Mobile (Android), Firefox Mobile (Android), Safari Mobile (iOS) with platform-specific TLS/H2 fingerprints
184
+ - **OkHttp profiles** — Android app impersonation (OkHttp 4.x, 5.x) with Conscrypt TLS stack fingerprint
185
+ - **Sync Python API** — `KoonSync` wrapper for all HTTP methods without `asyncio` (WebSocket and streaming remain async-only)
186
+
187
+ ## Usage
188
+
189
+ ### Node.js
190
+
191
+ ```javascript
192
+ import { Koon } from 'koonjs';
193
+
194
+ // Browser profile + options
195
+ const client = new Koon({
196
+ browser: 'chrome145',
197
+ headers: { 'X-Custom': 'value' },
198
+ proxy: 'socks5://127.0.0.1:1080', // optional
199
+ localAddress: '192.168.1.100', // optional: bind to specific IP
200
+ randomize: true, // optional: slight fingerprint jitter
201
+ retries: 3, // optional: retry on transport errors
202
+ locale: 'fr-FR', // optional: Accept-Language for proxy geo
203
+ ipVersion: 4, // optional: force IPv4 DNS resolution
204
+ proxyHeaders: { // optional: CONNECT tunnel headers
205
+ 'X-Session-Id': 'abc123',
206
+ },
207
+ onRedirect: (status, url, headers) => {
208
+ return !url.includes('captcha'); // stop if redirect goes to captcha
209
+ },
210
+ });
211
+
212
+ // HTTP methods
213
+ const r1 = await client.get('https://httpbin.org/get');
214
+ const r2 = await client.post('https://httpbin.org/post', 'data');
215
+ const r3 = await client.put('https://httpbin.org/put', 'data');
216
+ const r4 = await client.delete('https://httpbin.org/delete');
217
+ const r5 = await client.patch('https://httpbin.org/patch', 'data');
218
+ const r6 = await client.head('https://httpbin.org/get');
219
+
220
+ // User-Agent (useful for Puppeteer/Playwright sync)
221
+ console.log(client.userAgent); // "Mozilla/5.0 ... Chrome/145..."
222
+
223
+ // Response
224
+ console.log(r1.ok); // true (status 2xx)
225
+ console.log(r1.status); // 200
226
+ console.log(r1.text()); // body as string (charset-aware)
227
+ console.log(r1.json()); // parsed JSON
228
+ console.log(r1.contentType); // e.g. "text/html; charset=utf-8"
229
+ console.log(r1.header('content-type')); // case-insensitive header lookup
230
+ console.log(r1.body); // raw Buffer
231
+ console.log(r1.tlsResumed); // TLS session was reused
232
+ console.log(r1.connectionReused); // pooled connection was reused
233
+ console.log(r1.remoteAddress); // remote peer IP, or null for H3
234
+ console.log(r1.bytesSent, r1.bytesReceived); // bandwidth per request
235
+
236
+ // Per-request headers, timeout, and proxy
237
+ const r7 = await client.get('https://httpbin.org/get', {
238
+ headers: { 'Authorization': 'Bearer token' },
239
+ timeout: 5, // 5s timeout for this request only
240
+ proxy: 'http://user:pass@other-proxy:8080', // override proxy for this request
241
+ });
242
+
243
+ // Cookies persist automatically
244
+ await client.get('https://httpbin.org/cookies/set/name/value');
245
+ const r = await client.get('https://httpbin.org/cookies');
246
+
247
+ // Clear cookies (keeps TLS sessions and connection pool)
248
+ client.clearCookies();
249
+
250
+ // Session save/load
251
+ const session = client.saveSession(); // JSON string
252
+ const client2 = new Koon({ browser: 'chrome145' });
253
+ client2.loadSession(session);
254
+
255
+ // File: save/load to disk
256
+ client.saveSessionToFile('session.json');
257
+ client2.loadSessionFromFile('session.json');
258
+
259
+ // WebSocket
260
+ const ws = await client.websocket('wss://echo.websocket.org');
261
+ await ws.send('hello');
262
+ const msg = await ws.receive(); // { isText: true, data: Buffer }
263
+ await ws.close();
264
+
265
+ // Streaming
266
+ const stream = await client.requestStreaming('GET', 'https://example.com/large');
267
+ console.log(stream.status);
268
+ const body = await stream.collect(); // or iterate with nextChunk()
269
+
270
+ // Multipart upload
271
+ await client.postMultipart('https://httpbin.org/post', [
272
+ { name: 'field', value: 'text' },
273
+ { name: 'file', fileData: Buffer.from('...'), filename: 'upload.txt', contentType: 'text/plain' },
274
+ ]);
275
+
276
+ // MITM proxy
277
+ import { KoonProxy } from 'koonjs';
278
+ const proxy = await KoonProxy.start({ browser: 'chrome145', listenAddr: '127.0.0.1:8080' });
279
+ console.log(proxy.url); // http://127.0.0.1:8080
280
+ console.log(proxy.caCertPath); // path to CA cert for trust
281
+ await proxy.shutdown();
282
+ ```
283
+
284
+ ### Python
285
+
286
+ `KoonSync` provides a blocking API — no `asyncio` needed:
287
+
288
+ ```python
289
+ from koon import KoonSync
290
+
291
+ # Browser profile + options
292
+ client = KoonSync("chrome145",
293
+ headers={"X-Custom": "value"},
294
+ retries=3, # retry on transport errors
295
+ locale="fr-FR", # Accept-Language for proxy geo
296
+ ip_version=4, # force IPv4 DNS resolution
297
+ proxy_headers={"X-Session-Id": "abc123"}, # CONNECT tunnel headers
298
+ on_redirect=lambda s, u, h: "captcha" not in u,
299
+ )
300
+
301
+ # HTTP methods
302
+ r = client.get("https://httpbin.org/get")
303
+ r = client.post("https://httpbin.org/post", "data")
304
+ r = client.put("https://httpbin.org/put", "data")
305
+ r = client.delete("https://httpbin.org/delete")
306
+ r = client.patch("https://httpbin.org/patch", "data")
307
+ r = client.head("https://httpbin.org/get")
308
+
309
+ # Response
310
+ print(r.ok) # True (status 2xx)
311
+ print(r.status) # 200
312
+ print(r.text) # body as string (charset-aware)
313
+ print(r.json()) # parsed JSON
314
+ print(r.content_type) # e.g. "text/html; charset=utf-8"
315
+ print(r.header("content-type")) # case-insensitive header lookup
316
+ print(r.tls_resumed) # TLS session was reused
317
+ print(r.connection_reused) # pooled connection was reused
318
+ print(r.bytes_sent, r.bytes_received) # bandwidth per request
319
+
320
+ # Per-request headers, timeout, and proxy
321
+ r = client.get("https://httpbin.org/get",
322
+ headers={"Authorization": "Bearer token"},
323
+ timeout=5, # 5s timeout for this request only
324
+ proxy="http://user:pass@other-proxy:8080", # override proxy for this request
325
+ )
326
+
327
+ # Cookies persist automatically
328
+ client.get("https://httpbin.org/cookies/set/name/value")
329
+ r = client.get("https://httpbin.org/cookies")
330
+
331
+ # Clear cookies (keeps TLS sessions and connection pool)
332
+ client.clear_cookies()
333
+
334
+ # Session save/load
335
+ session = client.save_session()
336
+ client2 = KoonSync("chrome145")
337
+ client2.load_session(session)
338
+
339
+ # User-Agent (useful for Puppeteer/Playwright sync)
340
+ print(client.user_agent) # "Mozilla/5.0 ... Chrome/145..."
341
+ ```
342
+
343
+ For async code, use `Koon` instead — same API, but all request methods are coroutines:
344
+
345
+ ```python
346
+ from koon import Koon
347
+
348
+ client = Koon("chrome145")
349
+ resp = await client.get("https://httpbin.org/get")
350
+
351
+ # WebSocket (async only)
352
+ ws = await client.websocket("wss://echo.websocket.org")
353
+ await ws.send("hello")
354
+ msg = await ws.receive()
355
+ await ws.close()
356
+
357
+ # Streaming (async only)
358
+ stream = await client.request_streaming("GET", "https://example.com/large")
359
+ body = await stream.collect()
360
+ ```
361
+
362
+ ### R
363
+
364
+ ```r
365
+ library(koon)
366
+
367
+ # Browser profile + options
368
+ client <- Koon$new("chrome145", proxy = "socks5://127.0.0.1:1080", randomize = TRUE,
369
+ local_address = "192.168.1.100", retries = 3L,
370
+ locale = "fr-FR", ip_version = 4L,
371
+ proxy_headers = c(`X-Session-Id` = "abc123"),
372
+ on_redirect = function(status, url, headers) !grepl("captcha", url))
373
+
374
+ # HTTP methods (synchronous)
375
+ resp <- client$get("https://httpbin.org/get")
376
+ resp <- client$post("https://httpbin.org/post", "data")
377
+ resp <- client$put("https://httpbin.org/put", "data")
378
+ resp <- client$delete("https://httpbin.org/delete")
379
+ resp <- client$patch("https://httpbin.org/patch", "data")
380
+ resp <- client$head("https://httpbin.org/get")
381
+
382
+ # Response
383
+ resp$ok # TRUE (status 2xx)
384
+ resp$status # 200
385
+ resp$version # "HTTP/2.0"
386
+ resp$text # body as string (charset-aware)
387
+ resp$content_type # e.g. "text/html; charset=utf-8"
388
+ resp$body # raw vector
389
+ resp$headers # data.frame with name + value columns
390
+
391
+ # Parse JSON (via jsonlite)
392
+ data <- jsonlite::fromJSON(resp$text)
393
+
394
+ # Per-request headers
395
+ resp <- client$get("https://httpbin.org/get",
396
+ headers = c(Authorization = "Bearer token")
397
+ )
398
+
399
+ # Cookies persist automatically
400
+ client$get("https://httpbin.org/cookies/set/name/value")
401
+ resp <- client$get("https://httpbin.org/cookies")
402
+
403
+ # Clear cookies (keeps TLS sessions and connection pool)
404
+ client$clear_cookies()
405
+
406
+ # Session save/load
407
+ json <- client$save_session()
408
+ client2 <- Koon$new("chrome145")
409
+ client2$load_session(json)
410
+
411
+ # Export profile as JSON
412
+ client$export_profile()
413
+
414
+ # List all browsers
415
+ koon_browsers()
416
+ ```
417
+
418
+ ### CLI
419
+
420
+ ```bash
421
+ # GET with browser profile
422
+ koon -b chrome145 https://example.com
423
+
424
+ # POST with body
425
+ koon -b firefox147 -X POST -d '{"key":"value"}' https://httpbin.org/post
426
+
427
+ # Custom headers
428
+ koon -b safari183 -H "Authorization: Bearer token" https://api.example.com
429
+
430
+ # Verbose output (request/response headers)
431
+ koon -b chrome145 -v https://httpbin.org/get
432
+
433
+ # JSON output
434
+ koon -b chrome145 --json https://httpbin.org/get
435
+
436
+ # Save response to file
437
+ koon -b chrome145 -o page.html https://example.com
438
+
439
+ # Proxy
440
+ koon -b chrome145 --proxy socks5://127.0.0.1:1080 https://example.com
441
+
442
+ # Session persistence
443
+ koon -b chrome145 --save-session session.json https://example.com/login
444
+ koon -b chrome145 --load-session session.json https://example.com/dashboard
445
+
446
+ # DNS-over-HTTPS
447
+ koon -b chrome145 --doh cloudflare https://example.com
448
+
449
+ # OS-specific user-agent
450
+ koon -b chrome145-macos https://example.com
451
+
452
+ # Fingerprint randomization
453
+ koon -b chrome145 --randomize https://example.com
454
+
455
+ # List all browser profiles
456
+ koon --list-browsers
457
+
458
+ # Export profile as JSON
459
+ koon --export-profile chrome145
460
+
461
+ # Start MITM proxy
462
+ koon proxy --browser chrome145 --listen 127.0.0.1:8080
463
+ ```
464
+
465
+ ### Rust
466
+
467
+ ```toml
468
+ [dependencies]
469
+ koon-core = { git = "https://github.com/scrape-hub/koon.git" }
470
+ ```
471
+
472
+ ```rust
473
+ use koon_core::{BrowserProfile, Client};
474
+ use koon_core::profile::Chrome;
475
+
476
+ #[tokio::main]
477
+ async fn main() -> Result<(), koon_core::Error> {
478
+ // From a specific profile constructor
479
+ let client = Client::new(Chrome::v145_windows())?;
480
+
481
+ // Or with builder for full control
482
+ let profile = BrowserProfile::resolve("chrome145")?;
483
+ let client = Client::builder(profile)
484
+ .max_retries(3)
485
+ .locale("fr-FR")
486
+ .ip_version(koon_core::IpVersion::V4)
487
+ .on_redirect(|status, url, _headers| {
488
+ !url.contains("captcha")
489
+ })
490
+ .build()?;
491
+
492
+ let r = client.get("https://example.com").await?;
493
+ println!("{} {} ({} bytes)", r.status, r.version, r.body.len());
494
+
495
+ // Clear cookies without resetting TLS/pool
496
+ client.clear_cookies();
497
+
498
+ Ok(())
499
+ }
500
+ ```
501
+
502
+ ## Architecture
503
+
504
+ ```
505
+ koon-core Rust library — TLS, HTTP/2, HTTP/3, profiles, proxy
506
+ koon-node Node.js native addon via napi-rs
507
+ koon-python Python extension via PyO3 + maturin
508
+ koon-r R package via extendr
509
+ koon-cli Command-line interface via clap
510
+ ```
511
+
512
+ Key dependencies:
513
+ - [boring2](https://github.com/0x676e67/boring2) — BoringSSL Rust bindings
514
+ - [http2](https://github.com/scrape-hub/http2) (fork) — HTTP/2 with header field ordering
515
+ - [quinn](https://github.com/quinn-rs/quinn) + [h3](https://github.com/hyperium/h3) — QUIC / HTTP/3
516
+ - [napi-rs](https://napi.rs) — Rust to Node.js bridge
517
+ - [PyO3](https://pyo3.rs) + [maturin](https://github.com/PyO3/maturin) — Rust to Python bridge
518
+ - [extendr](https://extendr.rs) — Rust to R bridge
519
+
520
+ ## Building from source
521
+
522
+ Only needed if you want to build koon yourself instead of using the published packages.
523
+
524
+ **Requirements:**
525
+ - Rust 1.85+
526
+ - CMake
527
+ - NASM (Windows only, for BoringSSL assembly)
528
+ - C compiler — MSVC (Windows), GCC or Clang (Linux/macOS)
529
+
530
+ ```bash
531
+ # Core library
532
+ cargo build --release -p koon-core
533
+
534
+ # Node.js addon
535
+ cargo build --release -p koon-node
536
+
537
+ # Python package
538
+ cd crates/python && pip install -e .
539
+
540
+ # R package
541
+ cd crates/r && Rscript -e "rextendr::document(); devtools::install()"
542
+
543
+ # CLI binary
544
+ cargo build --release -p koon-cli
545
+ ```
546
+
547
+ ## License
548
+
549
+ [MIT](LICENSE)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koonjs",
3
- "version": "0.6.2",
4
- "description": "Browser-impersonating HTTP client with TLS/HTTP2 fingerprint spoofing",
3
+ "version": "0.7.0",
4
+ "description": "Browser-impersonating HTTP client TLS, HTTP/2, HTTP/3 fingerprint spoofing. Passes Akamai & Cloudflare. 175 browser profiles.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
7
7
  "license": "MIT",
@@ -18,13 +18,14 @@
18
18
  },
19
19
  "files": [
20
20
  "index.js",
21
- "index.d.ts"
21
+ "index.d.ts",
22
+ "README.md"
22
23
  ],
23
24
  "optionalDependencies": {
24
- "@koonjs/win32-x64-msvc": "0.6.2",
25
- "@koonjs/linux-x64-gnu": "0.6.2",
26
- "@koonjs/darwin-arm64": "0.6.2",
27
- "@koonjs/darwin-x64": "0.6.2"
25
+ "@koonjs/win32-x64-msvc": "0.7.0",
26
+ "@koonjs/linux-x64-gnu": "0.7.0",
27
+ "@koonjs/darwin-arm64": "0.7.0",
28
+ "@koonjs/darwin-x64": "0.7.0"
28
29
  },
29
30
  "scripts": {
30
31
  "build": "cargo build --release -p koon-node && node -e \"require('fs').copyFileSync(require('path').resolve(__dirname,'../../target/release/koon_node.dll'), require('path').resolve(__dirname,'koon.win32-x64-msvc.node'))\"",
@@ -33,15 +34,23 @@
33
34
  "test": "node ../../test_node.cjs"
34
35
  },
35
36
  "keywords": [
36
- "tls",
37
- "fingerprint",
38
- "http2",
39
- "browser",
40
- "impersonation",
37
+ "tls-fingerprint",
38
+ "browser-impersonation",
39
+ "http2-fingerprint",
41
40
  "anti-bot",
41
+ "web-scraping",
42
42
  "akamai",
43
43
  "cloudflare",
44
44
  "ja3",
45
- "ja4"
45
+ "ja4",
46
+ "http-client",
47
+ "proxy",
48
+ "scraping",
49
+ "stealth",
50
+ "curl-impersonate",
51
+ "boringssl",
52
+ "http3",
53
+ "quic",
54
+ "websocket"
46
55
  ]
47
56
  }