httpcloak 1.6.6 → 1.6.8-beta.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/lib/index.d.ts CHANGED
@@ -213,9 +213,9 @@ export interface SessionOptions {
213
213
  allowRedirects?: boolean;
214
214
  /** Maximum number of redirects to follow (default: 10) */
215
215
  maxRedirects?: number;
216
- /** Number of retries on failure (default: 3, set to 0 to disable) */
216
+ /** Number of retries on failure (default: 0, opt in by setting a positive integer) */
217
217
  retry?: number;
218
- /** Status codes to retry on (default: [429, 500, 502, 503, 504]) */
218
+ /** Status codes to retry on (default: empty; set explicitly to opt in, e.g. [429, 500, 502, 503, 504]) */
219
219
  retryOnStatus?: number[];
220
220
  /** Minimum wait time between retries in milliseconds (default: 500) */
221
221
  retryWaitMin?: number;
@@ -243,12 +243,32 @@ export interface SessionOptions {
243
243
  switchProtocol?: string;
244
244
  /** Disable internal cookie jar entirely — caller manages cookies via per-request headers (default: false) */
245
245
  withoutCookieJar?: boolean;
246
+ /** Disable ETag / If-Modified-Since handling for the lifetime of the session (default: false) */
247
+ withoutConditionalCache?: boolean;
248
+ /** Full strip: drop ALL sec-ch-ua client hints (including the always-on trio) for the lifetime of the session (default: false) */
249
+ withoutClientHints?: boolean;
250
+ /** High-entropy strip: keep the always-on trio but drop only the Accept-CH high-entropy hints for the lifetime of the session (default: false) */
251
+ withoutHighEntropyClientHints?: boolean;
252
+ /** Skip the ECH (Encrypted Client Hello) HTTPS RR lookup. Saves ~15-20ms on first connect (default: false) */
253
+ disableEch?: boolean;
254
+ /** Disable HTTP/3 racing while keeping H1/H2 auto-negotiation. Reachable indirectly via httpVersion: "h2" but the explicit flag is cleaner (default: false) */
255
+ disableHttp3?: boolean;
246
256
  /** Custom JA3 fingerprint string (e.g., "771,4865-4866-4867-...,0-23-65281-...,29-23-24,0") */
247
257
  ja3?: string;
248
258
  /** Custom Akamai HTTP/2 fingerprint string (e.g., "1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p") */
249
259
  akamai?: string;
250
260
  /** Extra fingerprint options: { tls_alpn, tls_signature_algorithms, tls_cert_compression, tls_permute_extensions } */
251
261
  extraFp?: Record<string, any>;
262
+ /** TCP IP Time-To-Live in the SYN packet. 128 = Windows, 64 = Linux/macOS/iOS/Android. */
263
+ tcpTtl?: number;
264
+ /** TCP Maximum Segment Size option. 1460 for standard Ethernet. */
265
+ tcpMss?: number;
266
+ /** TCP Window Size in the SYN packet. 64240 = Windows 10/11, 65535 = Linux/macOS. */
267
+ tcpWindowSize?: number;
268
+ /** TCP Window Scale option exponent. 8 = Windows, 7 = Linux/Android, 6 = macOS/iOS. */
269
+ tcpWindowScale?: number;
270
+ /** IP Don't-Fragment flag. true on every modern client. */
271
+ tcpDf?: boolean;
252
272
  }
253
273
 
254
274
  export interface RequestOptions {
@@ -283,6 +303,80 @@ export interface RequestOptions {
283
303
  * Set this explicitly when the auto-sniff gets it wrong (e.g., POST to a CORS endpoint without a JSON Accept header).
284
304
  */
285
305
  fetchMode?: "cors" | "no-cors" | "navigate" | "websocket";
306
+
307
+ /**
308
+ * Per-request override for redirect following. true forces redirects on this
309
+ * call, false surfaces the 3xx back to the caller; null / undefined defers
310
+ * to the session-level setting (which itself defaults to follow).
311
+ */
312
+ allowRedirects?: boolean | null;
313
+
314
+ /**
315
+ * Per-request opt-out of the session's ETag / If-Modified-Since handling.
316
+ * When true, no cache validators are injected on the way out and the
317
+ * response's ETag / Last-Modified are not stored. Useful for a one-off
318
+ * fresh fetch without touching the session-wide setting.
319
+ */
320
+ disableConditionalCache?: boolean;
321
+
322
+ /**
323
+ * Per-request full strip of sec-ch-ua client hints. When true, ALL client
324
+ * hints are dropped for this request, including the always-on trio
325
+ * (sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform). One-off only; the
326
+ * session-wide setting is untouched.
327
+ */
328
+ disableClientHints?: boolean;
329
+
330
+ /**
331
+ * Per-request high-entropy strip. When true, this request keeps the
332
+ * always-on trio but drops only the high-entropy Accept-CH hints
333
+ * (e.g. sec-ch-ua-platform-version, -arch, -model, -bitness,
334
+ * -full-version-list). One-off only; the session-wide setting is untouched.
335
+ */
336
+ disableHighEntropyClientHints?: boolean;
337
+
338
+ /**
339
+ * AbortSignal for cancelling an in-flight request. Honored by the async
340
+ * methods (get, post, put, patch, delete, head, options, request). When
341
+ * the signal aborts, the underlying Go-side request is cancelled (DNS /
342
+ * TCP / TLS / HTTP work is torn down) and the returned promise rejects
343
+ * with the signal's reason (or a generic AbortError if none was set).
344
+ *
345
+ * @example
346
+ * const controller = new AbortController();
347
+ * setTimeout(() => controller.abort(new Error("too slow")), 5000);
348
+ * await session.get("https://slow.example.com", { signal: controller.signal });
349
+ */
350
+ signal?: AbortSignal;
351
+ }
352
+
353
+ /**
354
+ * Snapshot returned by `Session.stats()`. Mirrors the wire shape emitted by
355
+ * the Go-side `session.Stats()` after snake_case marshalling.
356
+ */
357
+ export interface SessionStats {
358
+ /** Stable session ID assigned at construction. */
359
+ id: string;
360
+ /** Preset name the session was created with. */
361
+ preset: string;
362
+ /** Construction time, Unix nanoseconds. */
363
+ created_at: number;
364
+ /** Last-request time, Unix nanoseconds. */
365
+ last_used: number;
366
+ /** Total requests serviced by this session. */
367
+ request_count: number;
368
+ /** Whether the session is still usable (false after close). */
369
+ active: boolean;
370
+ /** Live cookie count in the jar. */
371
+ cookie_count: number;
372
+ /** Conditional-cache entry count (one per cached URL). */
373
+ cache_entry_count: number;
374
+ /** Age in nanoseconds (now - created_at). */
375
+ age_ns: number;
376
+ /** Idle time in nanoseconds (now - last_used). */
377
+ idle_time_ns: number;
378
+ /** Transport-level stats (per-protocol). Shape varies; treat as opaque. */
379
+ transport_stats?: Record<string, any>;
286
380
  }
287
381
 
288
382
  export class Session {
@@ -313,8 +407,11 @@ export class Session {
313
407
  /** Refresh the session by closing all connections while keeping TLS session tickets.
314
408
  * This simulates a browser page refresh - connections are severed but 0-RTT
315
409
  * early data can be used on reconnection due to preserved session tickets.
410
+ *
411
+ * @param switchProtocol - Optional protocol to switch to ("h1", "h2", "h3").
412
+ * Overrides any switchProtocol set at construction time. Persists for future refresh() calls.
316
413
  */
317
- refresh(): void;
414
+ refresh(switchProtocol?: "h1" | "h2" | "h3"): void;
318
415
 
319
416
  // Synchronous methods
320
417
  /** Perform a synchronous GET request */
@@ -359,17 +456,11 @@ export class Session {
359
456
  /** Get a specific cookie by name with full metadata */
360
457
  getCookieDetailed(name: string): Cookie | null;
361
458
 
362
- /**
363
- * Get all cookies as a flat name-value object.
364
- * @deprecated Will return Cookie[] with full metadata in a future release. Use getCookiesDetailed() for the new format now.
365
- */
366
- getCookies(): Record<string, string>;
459
+ /** Get all cookies with full metadata. Alias of getCookiesDetailed(); the older flat dict shape was removed in v1.6.5. */
460
+ getCookies(): Cookie[];
367
461
 
368
- /**
369
- * Get a specific cookie value by name.
370
- * @deprecated Will return Cookie|null in a future release. Use getCookieDetailed() for the new format now.
371
- */
372
- getCookie(name: string): string | null;
462
+ /** Get a specific cookie by name. Alias of getCookieDetailed(); the older value-only shape was removed in v1.6.5. */
463
+ getCookie(name: string): Cookie | null;
373
464
 
374
465
  /** Set a cookie in the session */
375
466
  setCookie(
@@ -392,11 +483,94 @@ export class Session {
392
483
  /** Clear all cookies from the session */
393
484
  clearCookies(): void;
394
485
 
486
+ /** Cookies in the session jar with full metadata. Same shape as getCookies(). */
487
+ readonly cookies: Cookie[];
488
+
489
+ // Conditional cache and redirect runtime control
490
+
491
+ /**
492
+ * Drop the session's per-URL conditional-cache map (ETag / Last-Modified).
493
+ * The next request to each URL goes out without If-None-Match /
494
+ * If-Modified-Since headers. Cookies and TLS tickets are not touched.
495
+ */
496
+ clearCache(): void;
497
+
498
+ /**
499
+ * Snapshot of session counters, timestamps and transport-level metrics.
500
+ * Mirrors the keys Go's session.Stats() returns, snake_case on the wire.
501
+ */
502
+ stats(): SessionStats;
503
+
504
+ /**
505
+ * Return the time since the session last serviced a request, in seconds.
506
+ * Returns -1 if the session handle is invalid.
507
+ */
508
+ idleTime(): number;
509
+
510
+ /**
511
+ * Return true if the session is still usable (close() has not been called
512
+ * and the handle is valid).
513
+ */
514
+ isActive(): boolean;
515
+
516
+ /**
517
+ * Reset the idle timer to now without issuing a request. Useful in
518
+ * long-running pools where an external heartbeat shouldn't let a session
519
+ * look idle to a reaper.
520
+ */
521
+ touch(): void;
522
+
523
+ /**
524
+ * Toggle the session's ETag / If-Modified-Since handling at runtime.
525
+ * When disabled, the session stops injecting cache validators on outgoing
526
+ * requests and stops storing them from responses; the existing cache map
527
+ * is preserved (re-enabling resumes using it). Pair with clearCache() to
528
+ * also wipe previously-stored validators.
529
+ */
530
+ setConditionalCache(enabled: boolean): void;
531
+
532
+ /** Read the session's current conditional-cache state. */
533
+ getConditionalCache(): boolean;
534
+
395
535
  /**
396
- * Get cookies as a property.
397
- * @deprecated Will return Cookie[] with full metadata in a future release.
536
+ * Toggle the session's client-hints handling at runtime (full strip).
537
+ * When disabled, the session drops ALL sec-ch-ua client hints, including
538
+ * the always-on trio (sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform).
539
+ * The change takes effect on the next request and persists until set again.
398
540
  */
399
- readonly cookies: Record<string, string>;
541
+ setClientHints(enabled: boolean): void;
542
+
543
+ /** Read the session's current client-hints state. */
544
+ getClientHints(): boolean;
545
+
546
+ /**
547
+ * Toggle the session's high-entropy client-hints handling at runtime.
548
+ * When disabled, the session keeps the always-on trio but drops only the
549
+ * high-entropy Accept-CH hints (e.g. sec-ch-ua-platform-version, -arch,
550
+ * -model, -bitness, -full-version-list). Takes effect on the next request.
551
+ */
552
+ setHighEntropyClientHints(enabled: boolean): void;
553
+
554
+ /** Read the session's current high-entropy client-hints state. */
555
+ getHighEntropyClientHints(): boolean;
556
+
557
+ /**
558
+ * Toggle the session's redirect-following policy at runtime. The change
559
+ * takes effect on the next request and persists until set again.
560
+ */
561
+ setFollowRedirects(enabled: boolean): void;
562
+
563
+ /** Read the session's current redirect-following policy. */
564
+ getFollowRedirects(): boolean;
565
+
566
+ /**
567
+ * Update the session's redirect cap at runtime. Values of zero or below
568
+ * are ignored, leaving the prior cap (or the default of 10) in place.
569
+ */
570
+ setMaxRedirects(max: number): void;
571
+
572
+ /** Read the session's current redirect cap. */
573
+ getMaxRedirects(): number;
400
574
 
401
575
  // Proxy management
402
576
 
@@ -657,6 +831,37 @@ export class Session {
657
831
  * @returns FastResponse with Buffer body
658
832
  */
659
833
  patchFast(url: string, options?: RequestOptions): FastResponse;
834
+
835
+ /**
836
+ * Stream an arbitrary-sized body to the wire without buffering it in memory.
837
+ * `chunks` is any iterable / async-iterable yielding Buffer / Uint8Array /
838
+ * string chunks; the Go side opens an io.Pipe for the body and each chunk
839
+ * flows straight through with no base64 wrap and no JSON envelope.
840
+ *
841
+ * @param method Typically "POST", "PUT" or "PATCH".
842
+ * @param url
843
+ * @param chunks Iterable or async-iterable of body chunks.
844
+ * @param options.headers Headers (Content-Type defaults to application/octet-stream).
845
+ * @param options.contentType Optional explicit Content-Type override.
846
+ * @param options.timeout Per-request timeout in milliseconds.
847
+ * @returns Resolves to a regular `Response` once the upload completes.
848
+ */
849
+ uploadStream(
850
+ method: string,
851
+ url: string,
852
+ chunks: AsyncIterable<Buffer | Uint8Array | string> | Iterable<Buffer | Uint8Array | string>,
853
+ options?: { headers?: Record<string, string>; contentType?: string; timeout?: number }
854
+ ): Promise<Response>;
855
+
856
+ /**
857
+ * Convenience wrapper: streaming POST. Same semantics as
858
+ * `uploadStream("POST", url, chunks, options)`.
859
+ */
860
+ postUpload(
861
+ url: string,
862
+ chunks: AsyncIterable<Buffer | Uint8Array | string> | Iterable<Buffer | Uint8Array | string>,
863
+ options?: { headers?: Record<string, string>; contentType?: string; timeout?: number }
864
+ ): Promise<Response>;
660
865
  }
661
866
 
662
867
  export interface LocalProxyOptions {
@@ -677,16 +882,20 @@ export interface LocalProxyOptions {
677
882
  }
678
883
 
679
884
  export interface LocalProxyStats {
680
- /** Total number of requests processed */
681
- totalRequests: number;
682
- /** Number of active connections */
683
- activeConnections: number;
684
- /** Number of failed requests */
685
- failedRequests: number;
686
- /** Bytes sent */
687
- bytesSent: number;
688
- /** Bytes received */
689
- bytesReceived: number;
885
+ /** Whether the proxy is currently accepting connections */
886
+ running: boolean;
887
+ /** TCP port the proxy is bound to */
888
+ port: number;
889
+ /** Live count of in-flight connections */
890
+ active_conns: number;
891
+ /** Lifetime request counter since the proxy started */
892
+ total_requests: number;
893
+ /** Preset name the proxy was started with */
894
+ preset: string;
895
+ /** Max concurrent connections configured at start time */
896
+ max_connections: number;
897
+ /** Number of sessions currently registered via registerSession() */
898
+ registered_sessions: number;
690
899
  }
691
900
 
692
901
  /**
@@ -790,6 +999,22 @@ export class LocalProxy {
790
999
  */
791
1000
  unregisterSession(sessionId: string): boolean;
792
1001
 
1002
+ /**
1003
+ * Return the IDs of every session currently registered on this proxy.
1004
+ * These are the same IDs the X-HTTPCloak-Session header accepts for
1005
+ * per-request session routing.
1006
+ *
1007
+ * @returns List of registered session IDs (empty array if none).
1008
+ */
1009
+ listSessions(): string[];
1010
+
1011
+ /**
1012
+ * Return true if a session with the given ID is currently registered.
1013
+ * Cheaper than `listSessions().includes(id)` when callers only need an
1014
+ * existence check (no JSON marshal across the FFI boundary).
1015
+ */
1016
+ hasSession(sessionId: string): boolean;
1017
+
793
1018
  /**
794
1019
  * Stop and close the proxy.
795
1020
  * After closing, the LocalProxy instance cannot be reused.
@@ -800,8 +1025,30 @@ export class LocalProxy {
800
1025
  /** Get the httpcloak library version */
801
1026
  export function version(): string;
802
1027
 
803
- /** Get list of available browser presets */
804
- export function availablePresets(): string[];
1028
+ /**
1029
+ * Get available browser presets keyed by preset name.
1030
+ *
1031
+ * Each entry carries the protocols the preset supports (some H1/H2 only, some H1/H2/H3).
1032
+ * The shape is `{ [presetName: string]: { protocols: string[] } }`.
1033
+ *
1034
+ * @example
1035
+ * const presets = availablePresets();
1036
+ * Object.entries(presets).filter(([, info]) => info.protocols.includes("h3"));
1037
+ */
1038
+ export function availablePresets(): Record<string, { protocols: string[] }>;
1039
+
1040
+ /**
1041
+ * Return a fully-resolved JSON dump of a preset's TLS / H2 / H3 / header configuration.
1042
+ *
1043
+ * Useful for inspecting what a preset name actually does at the wire level, or for
1044
+ * dumping a built-in preset, mutating it, and loading it back with `loadPresetFromJSON`
1045
+ * under a new name.
1046
+ *
1047
+ * @param name - Preset name (e.g. "chrome-148-windows", "firefox-148", "chrome-latest")
1048
+ * @returns JSON string. Parse with `JSON.parse` to get the structured form.
1049
+ * @throws {HTTPCloakError} If the preset name is not registered.
1050
+ */
1051
+ export function describePreset(name: string): string;
805
1052
 
806
1053
  /**
807
1054
  * Configure the DNS servers used for ECH (Encrypted Client Hello) config queries.
@@ -859,6 +1106,28 @@ export function request(method: string, url: string, options?: RequestOptions):
859
1106
 
860
1107
  /** Available browser presets */
861
1108
  export const Preset: {
1109
+ CHROME_LATEST: string;
1110
+ CHROME_LATEST_WINDOWS: string;
1111
+ CHROME_LATEST_LINUX: string;
1112
+ CHROME_LATEST_MACOS: string;
1113
+ CHROME_LATEST_IOS: string;
1114
+ CHROME_LATEST_ANDROID: string;
1115
+ CHROME_149: string;
1116
+ CHROME_149_WINDOWS: string;
1117
+ CHROME_149_LINUX: string;
1118
+ CHROME_149_MACOS: string;
1119
+ CHROME_148: string;
1120
+ CHROME_148_WINDOWS: string;
1121
+ CHROME_148_LINUX: string;
1122
+ CHROME_148_MACOS: string;
1123
+ CHROME_148_IOS: string;
1124
+ CHROME_148_ANDROID: string;
1125
+ CHROME_147: string;
1126
+ CHROME_147_WINDOWS: string;
1127
+ CHROME_147_LINUX: string;
1128
+ CHROME_147_MACOS: string;
1129
+ CHROME_147_IOS: string;
1130
+ CHROME_147_ANDROID: string;
862
1131
  CHROME_146: string;
863
1132
  CHROME_146_WINDOWS: string;
864
1133
  CHROME_146_LINUX: string;