feat: create TanStack Start Bun server library#1
Conversation
🦋 Changeset detectedLatest commit: 65d19d4 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📝 WalkthroughWalkthroughA new Bun server library for serving TanStack Start production builds was introduced, replacing a simple greeting utility with comprehensive server configuration handling static asset preloading, caching, ETags, gzip compression, and dynamic server handler initialization. Changes
Sequence Diagram(s)sequenceDiagram
actor Client
participant TanStackStartBunServeConfig
participant StaticAssets
participant ServerHandler
participant Bun as Bun.file / Memory
Client->>TanStackStartBunServeConfig: createTanStackStartBunServeConfig(options)
activate TanStackStartBunServeConfig
TanStackStartBunServeConfig->>StaticAssets: initializeStaticRoutes(options)
activate StaticAssets
StaticAssets->>Bun: glob scan webDistPath/client
StaticAssets->>Bun: filter by include/exclude patterns
StaticAssets->>Bun: preload eligible assets (within maxPreloadBytes)
StaticAssets->>Bun: compute gzip & ETag if enabled
StaticAssets-->>TanStackStartBunServeConfig: { loaded, routes, skipped }
deactivate StaticAssets
TanStackStartBunServeConfig->>ServerHandler: initializeServerHandler(options)
activate ServerHandler
ServerHandler->>Bun: check webDistPath/server/server.js exists
ServerHandler->>Bun: dynamic import server module
ServerHandler-->>TanStackStartBunServeConfig: ServerHandler instance
deactivate ServerHandler
TanStackStartBunServeConfig-->>Client: { fetchHandler, routes }
deactivate TanStackStartBunServeConfig
Client->>Client: route GET /asset.css to routes['/asset.css']
activate Client
Client->>StaticAssets: routes['/asset.css'](request)
activate StaticAssets
alt preloaded & ETag matches
StaticAssets-->>Client: 304 Not Modified
else preloaded & accept-encoding: gzip
StaticAssets-->>Client: 200 with gzipped body + Content-Encoding: gzip
else preloaded
StaticAssets-->>Client: 200 with body
else not preloaded
StaticAssets->>Bun: Bun.file(path)
Bun-->>StaticAssets: streamed response
StaticAssets-->>Client: 200 streamed
end
deactivate StaticAssets
deactivate Client
Client->>Client: route POST /api/* to fetchHandler
activate Client
Client->>ServerHandler: fetchHandler(request)
activate ServerHandler
ServerHandler->>Bun: serverModule.default.fetch(request)
Bun-->>ServerHandler: application response
ServerHandler-->>Client: 200 application response
deactivate ServerHandler
deactivate Client
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
src/static-assets.ts (2)
275-296: Consider caching on-demand loaded assets.Assets that pass size limits but fail pattern matching are reloaded from disk on every request (line 276-290). Under high traffic for frequently accessed filtered assets, this incurs repeated disk I/O and gzip compression overhead.
A simple LRU cache could reduce this cost, but for v1.0 with typical usage patterns, this is acceptable to defer.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/static-assets.ts` around lines 275 - 296, Assets that pass withinSizeLimit but are rebuilt on every request (the routes[route] branch that calls loadAssetFromFile then createAssetResponse) should be cached to avoid repeated disk/GZIP work; implement a small LRU (or bounded) cache keyed by filepath + relevant options (enableEtag, enableGzip, gzipMimeTypes, gzipMinBytes, relativePath, metadata.type) and return the cached asset when present, falling back to calling loadAssetFromFile and storing the result on miss; keep createStreamingAssetResponse branch unchanged. Ensure cache eviction/bounded size to avoid unbounded memory growth and use the same cached asset object expected by createAssetResponse.
79-82: Static analysis flagged potential ReDoS - low risk in practice.The
convertGlobToRegExpfunction builds a regex from user-provided glob patterns. While the escaping handles most special characters, pathological patterns could theoretically cause backtracking. In practice, patterns are simple file globs like*.jsor*.css, making exploitation unlikely.If this library may receive untrusted pattern input, consider adding a pattern complexity check or timeout. For typical usage with developer-controlled configuration, this is acceptable.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/static-assets.ts` around lines 79 - 82, The convertGlobToRegExp function can produce regexes that, if crafted by untrusted input, may trigger ReDoS; update convertGlobToRegExp to validate and constrain pattern complexity before building the RegExp: enforce a reasonable max length (e.g., 200–1000 chars) and reject or truncate patterns with excessive consecutive wildcards (e.g., >5 '*'), and replace '*' with a safer subpattern like '[^/]*' (or otherwise restrict wildcard scope) instead of '.*' to reduce backtracking risk; keep the existing escaping logic and throw or return a safe fallback when checks fail.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/static-assets.ts`:
- Around line 275-296: Assets that pass withinSizeLimit but are rebuilt on every
request (the routes[route] branch that calls loadAssetFromFile then
createAssetResponse) should be cached to avoid repeated disk/GZIP work;
implement a small LRU (or bounded) cache keyed by filepath + relevant options
(enableEtag, enableGzip, gzipMimeTypes, gzipMinBytes, relativePath,
metadata.type) and return the cached asset when present, falling back to calling
loadAssetFromFile and storing the result on miss; keep
createStreamingAssetResponse branch unchanged. Ensure cache eviction/bounded
size to avoid unbounded memory growth and use the same cached asset object
expected by createAssetResponse.
- Around line 79-82: The convertGlobToRegExp function can produce regexes that,
if crafted by untrusted input, may trigger ReDoS; update convertGlobToRegExp to
validate and constrain pattern complexity before building the RegExp: enforce a
reasonable max length (e.g., 200–1000 chars) and reject or truncate patterns
with excessive consecutive wildcards (e.g., >5 '*'), and replace '*' with a
safer subpattern like '[^/]*' (or otherwise restrict wildcard scope) instead of
'.*' to reduce backtracking risk; keep the existing escaping logic and throw or
return a safe fallback when checks fail.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0bc866f4-5255-4ad9-9f12-c326d646bdb4
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (10)
.changeset/blue-snails-judge.mdREADME.mdpackage.jsonsrc/index.tssrc/logger.tssrc/server-handler.tssrc/static-assets.tstest/index.test.tstest/logger.test.tstest/tanstack-start-bun-server.test.ts
💤 Files with no reviewable changes (1)
- test/index.test.ts
Create the initial tanstack-start-bun-server library. This adds the Bun server package for running a TanStack Start app in production, along with docs, tests, and release metadata for the 1.0.0 launch.
c02363f to
65d19d4
Compare
Create the initial tanstack-start-bun-server library.
This adds the Bun server package for running a TanStack Start app in production, along with docs, tests, and release metadata for the 1.0.0 launch.
Summary by CodeRabbit
New Features
Documentation
Tests