Skip to content

Commit 79da151

Browse files
committed
Improvements to sse, etags, compression
- `c.sse()` to support multi-line messages - Accommodate Bun 1.1.43 bugfix to HEAD content-length header - Avoid etags and compression when body is a stream - Add `maxSize` option to compression middleware (default 2GB) - Add LRU cache for file-based mime detection
1 parent 341597e commit 79da151

File tree

15 files changed

+139
-45
lines changed

15 files changed

+139
-45
lines changed

.github/workflows/workflow.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77
strategy:
88
matrix:
99
package: [bunshine, connect-to-fetch]
10-
bun-version: [1.1.33, latest]
10+
bun-version: [1.1.43, latest]
1111

1212
steps:
1313
- name: ➡️ Checkout repository

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@
44
# IDEs
55
.idea
66
.vscode
7+
8+
vite-examples

packages/bunshine/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## v3.2.3 - Jan 13, 2024
4+
5+
- `c.sse()` to support multi-line messages
6+
- Accommodate Bun 1.1.43 bugfix to HEAD content-length header
7+
- Avoid etags and compression when body is a stream
8+
- Add `maxSize` option to compression middleware (default 2GB)
9+
- Add LRU cache for file-based mime detection
10+
311
## v3.2.2 - Dec 7, 2024
412

513
- Fix mime-type on css

packages/bunshine/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1524,12 +1524,12 @@ The following decisions are based on scripts in /benchmarks:
15241524
and adds only a tiny overhead.
15251525
- `inner-functions.ts` - Context is a class, not a set of functions in a
15261526
closure, which saves about 3% of time.
1527-
- `compression.ts` - gzip is the default preferred format for the compression
1527+
- `compression-speed.ts` - gzip is the default preferred format for the compression
15281528
middleware. Deflate provides no advantage, and Brotli provides 2-8% additional
15291529
size savings at the cost of 100x as much CPU time as gzip. Brotli takes on
15301530
the order of 100ms to compress 100kb of html, compared to sub-milliseconds
15311531
for gzip.
1532-
- `etags.ts` - etag calculation is very fast. On the order of tens of
1532+
- `etags-speed.ts` - etag calculation is very fast. On the order of tens of
15331533
microseconds for 100kb of html.
15341534
- `lru-matcher.ts` - The default LRU cache size used for the router is 4000.
15351535
Cache sizes of 4000+ are all about 1.4x faster than no cache.

packages/bunshine/benchmarks/compression.ts renamed to packages/bunshine/benchmarks/compression-speed.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { promisify } from 'node:util';
22
import { brotliCompress, deflate, gzip } from 'node:zlib';
3-
import { runBenchmarks } from './runBenchmarks.ts';
3+
import { runBenchmarks } from './runBenchmarks';
44

55
/*
66
Conclusion:

packages/bunshine/benchmarks/etags.ts renamed to packages/bunshine/benchmarks/etags-speed.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { defaultEtagsCalculator } from '../src/middleware/etags/etags.ts';
2-
import { runBenchmarks } from './runBenchmarks.ts';
1+
import { defaultEtagsCalculator } from '../src/middleware/etags/etags';
2+
import { runBenchmarks } from './runBenchmarks';
33

44
/*
55
Conclusions:
@@ -17,6 +17,7 @@ const html = await fetch('https://www.npmjs.com/package/bunshine').then(res =>
1717
const t1k = html.slice(1000, 2000);
1818
const t10k = html.slice(1000, 11000);
1919
const t100k = html.slice(1000, 101000);
20+
const t100M = t100k.repeat(1024);
2021

2122
async function getResponse(input: string) {
2223
return new Response(input, {
@@ -40,6 +41,7 @@ await runBenchmarks(
4041
'etags 1k': () => computeEtags(t1k),
4142
'etags 10k': () => computeEtags(t10k),
4243
'etags 100k': () => computeEtags(t100k),
44+
'etags 100M': () => computeEtags(t100M),
4345
},
4446
{ time: 3000 }
4547
);

packages/bunshine/bun.lockb

786 Bytes
Binary file not shown.

packages/bunshine/package.json

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
{
22
"name": "bunshine",
3-
"version": "3.2.2",
3+
"version": "3.2.3",
44
"module": "index.ts",
55
"type": "module",
66
"main": "index.ts",
77
"types": "dist/index.d.ts",
8+
"engines": {
9+
"bun": ">=1.1.33"
10+
},
811
"scripts": {
912
"test-watch": "bun test --watch",
1013
"coverage": "bun test --coverage",
@@ -49,13 +52,14 @@
4952
"lru-cache": "^11.0.2"
5053
},
5154
"devDependencies": {
52-
"@types/bun": "^1.1.14",
55+
"@types/bun": "^1.1.16",
56+
"@types/node": "^22.10.6",
5357
"eventsource": "^2.0.2",
54-
"prettier": "^3.4.1",
58+
"prettier": "^3.4.2",
5559
"prettier-plugin-organize-imports": "^4.1.0",
5660
"redos-detector": "^5.1.3",
57-
"tinybench": "^3.0.6",
58-
"type-fest": "^4.29.0",
59-
"typescript": "^5.7.2"
61+
"tinybench": "^3.1.0",
62+
"type-fest": "^4.32.0",
63+
"typescript": "^5.7.3"
6064
}
6165
}

packages/bunshine/src/HttpRouter/HttpRouter.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ describe('HttpRouter', () => {
340340
app.head('/hi', ({ url }) => {
341341
const name = url.searchParams.get('name');
342342
return new Response(null, {
343-
status: 204,
343+
status: 200,
344344
headers: {
345345
'Content-length': '0',
346346
Message: `Hi ${name}`,
@@ -350,7 +350,7 @@ describe('HttpRouter', () => {
350350
const resp = await fetch(`${server.url}/hi?name=Bob`, {
351351
method: 'HEAD',
352352
});
353-
expect(resp.status).toBe(204);
353+
expect(resp.status).toBe(200);
354354
expect(resp.headers.get('Message')).toBe('Hi Bob');
355355
});
356356
it('should handle POST', async () => {

packages/bunshine/src/middleware/compression/compression.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export function compression(
3636
return async (context, next) => {
3737
const resp = await next();
3838
if (
39+
// no compression for streams such as text/stream
40+
/stream/i.test(resp.headers.get('Content-Type') || '') ||
3941
context.request.method === 'HEAD' ||
4042
!isCompressibleMime(resp.headers.get('Content-Type'))
4143
) {

0 commit comments

Comments
 (0)