@stacksjs/bun-router 0.0.24 → 0.0.25

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/dist/cli.js CHANGED
@@ -2047,7 +2047,7 @@ function getPrefix() {
2047
2047
  }
2048
2048
  var prefix = getPrefix();
2049
2049
  // package.json
2050
- var version = "0.0.24";
2050
+ var version = "0.0.25";
2051
2051
 
2052
2052
  // src/cli/middleware.ts
2053
2053
  import fs2 from "fs/promises";
package/dist/index.js CHANGED
@@ -2099,10 +2099,10 @@ function shouldCompress(response, encoding, threshold) {
2099
2099
  return false;
2100
2100
  if (!isCompressible(response.headers.get("content-type")))
2101
2101
  return false;
2102
- const length = Number(response.headers.get("content-length") ?? Number.NaN);
2103
- if (!Number.isFinite(length))
2102
+ if (!response.body)
2104
2103
  return false;
2105
- return length >= threshold;
2104
+ const length = Number(response.headers.get("content-length") ?? Number.NaN);
2105
+ return Number.isFinite(length) ? length >= threshold : true;
2106
2106
  }
2107
2107
  async function compressResponse(response, request2, options = {}) {
2108
2108
  const settings = { ...DEFAULT_COMPRESSION, ...options };
@@ -2114,17 +2114,11 @@ async function compressResponse(response, request2, options = {}) {
2114
2114
  appendVary(response.headers, "Accept-Encoding");
2115
2115
  return response;
2116
2116
  }
2117
- const body = new Uint8Array(await response.arrayBuffer());
2118
- const compressed = encoding === "gzip" ? Bun.gzipSync(body, { level: settings.level }) : Bun.deflateSync(body, { level: settings.level });
2119
- if (compressed.byteLength >= body.byteLength) {
2120
- const headers2 = new Headers(response.headers);
2121
- appendVary(headers2, "Accept-Encoding");
2122
- return new Response(body, { status: response.status, statusText: response.statusText, headers: headers2 });
2123
- }
2124
2117
  const headers = new Headers(response.headers);
2125
2118
  headers.set("Content-Encoding", encoding);
2126
- headers.set("Content-Length", String(compressed.byteLength));
2127
2119
  appendVary(headers, "Accept-Encoding");
2120
+ headers.delete("Content-Length");
2121
+ const compressed = response.body.pipeThrough(new CompressionStream(encoding === "gzip" ? "gzip" : "deflate"));
2128
2122
  return new Response(compressed, { status: response.status, statusText: response.statusText, headers });
2129
2123
  }
2130
2124
  function appendVary(headers, value) {
@@ -7,25 +7,39 @@
7
7
  * HTML and compresses to about a tenth of that, on every request, for every
8
8
  * visitor - and the same is true of any JSON list long enough to matter.
9
9
  *
10
- * Three rules, and the middle one is the important one:
10
+ * Three rules:
11
11
  *
12
12
  * - **Only when the client asked.** `Accept-Encoding` decides, and `Vary` says
13
13
  * so, because a cache that hands a gzipped body to a client that did not ask
14
14
  * is a cache that breaks that client.
15
- * - **Only when the length is known.** A response with no `Content-Length` is a
16
- * stream, and buffering one to compress it would turn a progressive render
17
- * into a wait - the diff manifest that streams a hundred files as they parse
18
- * would arrive all at once, at the end. Streams are left alone.
19
15
  * - **Only when it is worth it.** Below a kilobyte the header costs more than
20
16
  * the saving, and already-compressed bytes (an image, a woff2, a zip) get
21
17
  * bigger rather than smaller.
18
+ * - **Never by buffering.** The body is piped through a `CompressionStream`
19
+ * rather than read into memory, so a response that streams keeps streaming:
20
+ * the diff manifest that sends a hundred files as they parse still arrives a
21
+ * file at a time, compressed. Buffering to compress would have turned this
22
+ * product's progressive render into a wait, which is a worse trade than the
23
+ * bytes are worth.
24
+ *
25
+ * A piped body has no length until it ends, so `Content-Length` comes off and
26
+ * the response goes out chunked. That is the ordinary shape for a compressed
27
+ * response and every client handles it; a browser showing a page as it arrives
28
+ * is not waiting for a number.
22
29
  */
23
30
  /** zlib's own range, so a level Bun would reject is a type error rather than a throw. */
24
31
  export type CompressionLevel = -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
25
32
  export interface CompressionOptions {
26
33
  /** Off turns the whole thing off, for a proxy that already compresses. */
27
34
  enabled?: boolean;
28
- /** zlib level. 6 is the usual balance; 9 costs noticeably more CPU for a few percent. */
35
+ /**
36
+ * Kept for callers that set it, and unused by the stream path.
37
+ *
38
+ * `CompressionStream` has no level knob - it is the platform's, at its own
39
+ * default - and piping is what keeps a streamed response streaming. A level
40
+ * would mean going back to buffering, which is the trade this deliberately
41
+ * does not make.
42
+ */
29
43
  level?: CompressionLevel;
30
44
  /** Bodies below this many bytes are sent as they are. */
31
45
  threshold?: number;
@@ -53,7 +67,16 @@ export declare function isCompressible(contentType: string | null): boolean;
53
67
  * response path is a different change from this one.
54
68
  */
55
69
  export declare function negotiateEncoding(header: string | null): 'gzip' | 'deflate' | null;
56
- /** Whether this response should be compressed at all, and why not when it should not. */
70
+ /**
71
+ * Whether this response should be compressed at all.
72
+ *
73
+ * `Content-Length` is a *hint here rather than a requirement*: Bun does not put
74
+ * one on a `Response` built from a string, so treating its absence as "this is
75
+ * a stream, leave it alone" left every server-rendered page uncompressed - the
76
+ * exact case this exists for. When there is a length, it decides against
77
+ * bodies too small to be worth it; when there is not, the body is piped and
78
+ * the threshold cannot apply.
79
+ */
57
80
  export declare function shouldCompress(response: Response, encoding: string | null, threshold: number): boolean;
58
81
  /**
59
82
  * The response, compressed when that is the right thing to do.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/bun-router",
3
3
  "type": "module",
4
- "version": "0.0.24",
4
+ "version": "0.0.25",
5
5
  "description": "A fast, type-safe router for Bun.",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",
7
7
  "license": "MIT",