eth-compress 0.2.2 → 0.3.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/index.ts CHANGED
@@ -1,118 +1,86 @@
1
1
  export const MIN_BODY_SIZE = 1150;
2
2
 
3
- const _sup_enc = new Map<string, string[] | -1>();
4
- const _enc = ['deflate-raw', 'deflate', 'gzip'];
5
- let supported: string | -1 | null = typeof CompressionStream === 'undefined' ? -1 : null;
3
+ const _hasCS = typeof CompressionStream !== 'undefined';
4
+ const _cache = new Map<string, string | -1>();
5
+ const _enc = ['gzip', 'deflate'] as const;
6
+ type SupportedEncoding = (typeof _enc)[number];
6
7
 
7
8
  export type PayloadTransform = (payload: unknown) => unknown;
9
+ export type CompressionMode = 'passive' | 'proactive' | 'gzip' | 'deflate' | PayloadTransform;
8
10
 
9
11
  /**
10
- * Fetch-compatible function that applies HTTP compression (gzip/deflate) to requests.
11
- * Optionally transforms request payloads before sending.
12
- *
13
- * @param input - The resource URL, Request object, or URL string
14
- * @param init - Optional request initialization options
15
- * @param transformPayload - Optional function to transform the request payload
16
- * @returns A Promise that resolves to the Response
12
+ * @param input - URL or Request
13
+ * @param init - Request options
14
+ * @param mode - Compression mode:
15
+ * - 'passive' (default): discover support via Accept-Encoding header first
16
+ * - 'proactive': compress with gzip first, adjust if server rejects
17
+ * - 'gzip' | 'deflate': use specified encoding directly (known support)
18
+ * - PayloadTransform function: transform payload, skip HTTP compression
17
19
  */
18
- //! @__PURE__
19
20
  export async function compressModule(
20
21
  input: string | URL | Request,
21
22
  init?: RequestInit,
22
- ): Promise<Response>;
23
-
24
- //! @__PURE__
25
- export async function compressModule(
26
- input: string | URL | Request,
27
- init: RequestInit | undefined,
28
- transformPayload?: PayloadTransform,
29
- ): Promise<Response>;
30
-
31
- //! @__PURE__
32
- export async function compressModule(
33
- input: string | URL | Request,
34
- init?: RequestInit,
35
- transformPayload?: PayloadTransform,
23
+ mode?: CompressionMode,
36
24
  ): Promise<Response> {
25
+ const req = input instanceof Request ? input : null;
37
26
  const url =
38
- typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;
27
+ typeof input === 'string' ? input : input instanceof Request ? input.url : input.toString();
28
+ const bodyStr = typeof init?.body === 'string' ? init.body : null;
39
29
 
40
- const cached = _sup_enc.get(url);
41
- supported = supported === -1 ? -1 : cached === -1 ? -1 : (cached?.[0] ?? null);
42
-
43
- // Only apply the optional payload transform
44
- // when native HTTP compression is not available for this URL.
45
- if (transformPayload && init?.body && typeof init.body === 'string') {
46
- if (supported === -1 || supported === null) {
30
+ // Custom transform: apply and skip HTTP compression
31
+ if (typeof mode === 'function') {
32
+ if (req && !init) return fetch(req);
33
+ let body = init?.body;
34
+ if (bodyStr)
47
35
  try {
48
- const parsed = JSON.parse(init.body as string);
49
- const next = transformPayload(parsed);
50
- if (next !== undefined) {
51
- init = {
52
- ...init,
53
- body: JSON.stringify(next),
54
- };
55
- }
56
- } catch {
57
- // Non-JSON bodies are left untouched.
58
- }
59
- }
36
+ const next = mode(JSON.parse(bodyStr));
37
+ if (next !== undefined) body = JSON.stringify(next);
38
+ } catch {}
39
+ return fetch(req ?? url, { ...init, body });
60
40
  }
61
41
 
62
- const bodyStr = typeof init?.body === 'string' ? (init.body as string) : null;
42
+ const cached = _cache.get(url);
43
+ const known = mode === 'gzip' || mode === 'deflate';
44
+ const encoding = !_hasCS
45
+ ? null
46
+ : known
47
+ ? (mode as SupportedEncoding)
48
+ : mode === 'proactive'
49
+ ? cached === -1
50
+ ? null
51
+ : (cached ?? 'gzip')
52
+ : typeof cached === 'string'
53
+ ? cached
54
+ : null;
63
55
 
64
- if (supported && supported !== -1 && bodyStr && bodyStr.length >= MIN_BODY_SIZE) {
65
- const compressed = await new Response(
66
- new Blob([bodyStr])
56
+ const shouldCompress = !!encoding && !!bodyStr && bodyStr.length >= MIN_BODY_SIZE;
57
+ const opts: RequestInit = { ...init, priority: 'high' as RequestPriority };
58
+ const headers = new Headers(opts.headers);
59
+ if (shouldCompress) {
60
+ opts.body = await new Response(
61
+ new Blob([bodyStr!])
67
62
  .stream()
68
- .pipeThrough(new CompressionStream(supported as CompressionFormat)),
63
+ .pipeThrough(new CompressionStream(encoding as CompressionFormat)),
69
64
  ).blob();
70
- init = {
71
- ...init,
72
- body: compressed,
73
- headers: { ...(init && init.headers), 'Content-Encoding': supported },
74
- };
65
+ headers.set('Content-Encoding', encoding);
75
66
  }
76
- const response = await fetch(url, init);
67
+ opts.headers = headers;
68
+
69
+ const response = await fetch(req ?? url, opts);
77
70
 
78
- if (supported === null) {
79
- const encodings = response.headers
80
- .get('Accept-Encoding')
81
- ?.split(',')
82
- .filter((e) => _enc.includes(e));
83
- _sup_enc.set(url, encodings?.length ? encodings : -1);
71
+ // Cache discovery for passive/proactive (not known modes)
72
+ if (!known && cached === undefined) {
73
+ const header = response.headers.get('Accept-Encoding');
74
+ const discovered =
75
+ header
76
+ ?.split(',')
77
+ .map((e) => e.trim())
78
+ .find((e): e is SupportedEncoding => _enc.includes(e as SupportedEncoding)) ?? -1;
79
+ _cache.set(
80
+ url,
81
+ mode === 'proactive' && shouldCompress ? (response.ok ? encoding! : discovered) : discovered,
82
+ );
84
83
  }
85
84
 
86
85
  return response;
87
86
  }
88
-
89
- /**
90
- * Combines HTTP compression with EVM JIT compression.
91
- * Just pass this as `fetchFn` to viem's http transport.
92
- *
93
- * @param input - The resource URL or Request object
94
- * @param init - Optional request initialization options
95
- * @returns A Promise that resolves to the Response
96
- *
97
- * @example
98
- * ```ts
99
- * const client = createPublicClient({
100
- * transport: http(rpcUrl, { fetchFn: compressModuleWithJIT })
101
- * })
102
- * ```
103
- *
104
- * If the target RPC endpoint and runtime support native HTTP compression,
105
- * this helper prefers that path and will not apply JIT calldata compression;
106
- * the JIT-based transform is used as a legacy/fallback path when HTTP
107
- * content-encoding is unavailable.
108
- * @pure
109
- */
110
- //! @__PURE__
111
- export const compressModuleWithJIT = (
112
- input: RequestInfo | URL,
113
- init?: RequestInit,
114
- ): Promise<Response> => {
115
- return import('./jit-compressor').then(({ compress_call }) =>
116
- compressModule(input, init, compress_call),
117
- );
118
- };