ReviewOS

stacks/bun-router

perf(compression): session encryption loads node:crypto for every router import

#891
Closed chris opened this 8 days ago ·
8 days ago

The public barrel pulls node:crypto in eagerly through the session-encryption code, so importing the router initialises the optional native subsystem in every application, including the great majority that never open an encrypted session.

Chunk graph at v0.1.15, from the published dist:

index.js -> chunk-7s1kcj7w.js -> chunk-9y37n6xs.js

and chunk-9y37n6xs.js opens with three of these:

import{createCipheriv as On,createDecipheriv as Rn,randomBytes as st}from"crypto"

src/crypto.ts already has the pattern this wants - getNodeCrypto(), resolved on first use and cached - and auth.ts and middleware/csrf.ts use it for timingSafeEqual. The session-encryption module is the remaining static importer.

Why it is worth doing

It is the last thing standing between the router and a crypto-free import. stacksjs/stacks#2450 removed the equivalent static import from its own signed-URL helper and measured no improvement, because this one loads the subsystem regardless. Fresh process, isolated benchmark config, Bun 1.3.14 / macOS arm64, 7 runs each:

wallRSS
empty process12.2 ms22.0 MB
import 'node:crypto' alone16.4 ms27.4 MB
Stacks router barrel on bun-router 0.1.1533.6 ms43.6 MB

Direction-only startup diagnostics on a developer machine, not throughput or settled-memory claims, and RSS deltas can include shared native initialisation - so they need measuring again after the change rather than subtracting arithmetically.

What would close it

  • Session encryption resolves createCipheriv / createDecipheriv / randomBytes through getNodeCrypto() rather than a static import, caching the module as that helper already does.
  • crypto is absent from the transitive chunk graph of index.js in the built output, asserted by a test over the dist rather than the source, since the source graph is not what ships.
  • Same primitives, same synchronous APIs, same exports; cold and warm encrypt/decrypt behaviour unchanged.
  • Fresh-process baseline and candidate measured, both reported, including if the win does not appear.
8 days ago

This is no longer reproducible on main at 2324944 and is already covered by #883 / 272f696.

The current source has no session-encryption module and no static crypto or node:crypto import. The only crypto load is the cached literal require('node:crypto') inside getNodeCrypto(). The built-entry regression test follows every statically reachable emitted chunk from index.js and rejects eager crypto imports. It passes on Bun 1.4.2, along with cold and repeated use of JWT, CSRF, request IDs, uploads, monitoring, and cache features.

Current validation on Bun 1.4.2: 565 tests pass, 0 fail; lint and both typecheck configurations pass. The emitted crypto code that remains is in a dynamically loaded config chunk and is not statically reachable from the public entry. There is no remaining implementation change for this ticket, so I am closing it as superseded by #883.

Sign in to comment on this issue.