@checkstack/backend 0.6.2 → 0.6.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @checkstack/backend
2
2
 
3
+ ## 0.6.4
4
+
5
+ ### Patch Changes
6
+
7
+ - a713e0f: Fix static file Content-Length header stripped by Hono middleware
8
+
9
+ Hono's CORS middleware wraps raw `Response` objects and strips Bun's auto-generated headers. Switched to using `c.body()` + `c.header()` so Content-Type and Content-Length survive the middleware pipeline. Extracted a shared `serveFile` helper for all static file routes.
10
+
11
+ ## 0.6.3
12
+
13
+ ### Patch Changes
14
+
15
+ - 3da7582: Fix favicon not loading in production container and add NotFound page
16
+
17
+ - **Backend**: Fix static file serving so root-level files like `/favicon.svg` are served from the dist directory before the SPA fallback catches them
18
+ - **UI**: Add `NotFound` component with stacked-checkmark logo, physics-inspired falling "4" animation, and low-power device fallback
19
+ - **Frontend**: Add catch-all `*` route to display the NotFound page for unmatched routes, and add the Checkstack logo to the navbar
20
+ - **Favicon**: Redesign with stacked checkmarks in the brand purple/indigo palette
21
+
3
22
  ## 0.6.2
4
23
 
5
24
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/backend",
3
- "version": "0.6.2",
3
+ "version": "0.6.4",
4
4
  "checkstack": {
5
5
  "type": "backend"
6
6
  },
@@ -15,11 +15,11 @@
15
15
  "dependencies": {
16
16
  "@checkstack/api-docs-common": "0.1.9",
17
17
  "@checkstack/auth-common": "0.6.1",
18
- "@checkstack/backend-api": "0.11.1",
18
+ "@checkstack/backend-api": "0.12.0",
19
19
  "@checkstack/common": "0.6.5",
20
20
  "@checkstack/drizzle-helper": "0.0.4",
21
- "@checkstack/queue-api": "0.2.12",
22
- "@checkstack/signal-backend": "0.1.18",
21
+ "@checkstack/queue-api": "0.2.13",
22
+ "@checkstack/signal-backend": "0.1.19",
23
23
  "@checkstack/signal-common": "0.1.9",
24
24
  "@hono/zod-validator": "^0.7.6",
25
25
  "@orpc/client": "^1.13.14",
@@ -40,6 +40,6 @@
40
40
  "@types/bun": "latest",
41
41
  "@checkstack/tsconfig": "0.0.5",
42
42
  "@checkstack/scripts": "0.1.2",
43
- "@checkstack/test-utils-backend": "0.1.18"
43
+ "@checkstack/test-utils-backend": "0.1.19"
44
44
  }
45
45
  }
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Server } from "bun";
2
- import { Hono } from "hono";
2
+ import { type Context, Hono } from "hono";
3
3
  import { PluginManager } from "./plugin-manager";
4
4
  import { logger } from "hono/logger";
5
5
  import { migrate } from "drizzle-orm/node-postgres/migrator";
@@ -174,6 +174,13 @@ app.get("/.well-known/jwks.json", async (c) => {
174
174
  const frontendDistPath = process.env.CHECKSTACK_FRONTEND_DIST;
175
175
  if (frontendDistPath && fs.existsSync(frontendDistPath)) {
176
176
  rootLogger.info(`📦 Serving frontend from: ${frontendDistPath}`);
177
+ /** Serve a static file via Hono's context to preserve headers through middleware. */
178
+ const serveFile = async (c: Context, filePath: string) => {
179
+ const file = Bun.file(filePath);
180
+ c.header("Content-Type", file.type);
181
+ c.header("Content-Length", String(file.size));
182
+ return c.body(file.stream());
183
+ };
177
184
 
178
185
  // Serve static assets (JS, CSS, images, etc.)
179
186
  app.get("/assets/*", async (c) => {
@@ -181,10 +188,7 @@ if (frontendDistPath && fs.existsSync(frontendDistPath)) {
181
188
  const filePath = path.join(frontendDistPath, "assets", assetPath);
182
189
 
183
190
  if (fs.existsSync(filePath)) {
184
- const file = Bun.file(filePath);
185
- return new Response(file, {
186
- headers: { "Content-Type": file.type },
187
- });
191
+ return serveFile(c, filePath);
188
192
  }
189
193
  return c.notFound();
190
194
  });
@@ -195,27 +199,34 @@ if (frontendDistPath && fs.existsSync(frontendDistPath)) {
195
199
  const filePath = path.join(frontendDistPath, "vendor", vendorPath);
196
200
 
197
201
  if (fs.existsSync(filePath)) {
198
- const file = Bun.file(filePath);
199
- return new Response(file, {
200
- headers: { "Content-Type": file.type },
201
- });
202
+ return serveFile(c, filePath);
202
203
  }
203
204
  return c.notFound();
204
205
  });
205
206
 
206
- // Serve index.html for all non-API routes (SPA fallback)
207
+ // Serve root-level static files (e.g., /favicon.svg) from the dist directory
208
+ // before the SPA fallback, so they don't get caught by the index.html handler
207
209
  app.get("*", async (c, next) => {
208
210
  // Skip API and WebSocket routes - let them pass through to actual handlers
209
211
  if (c.req.path.startsWith("/api")) {
210
212
  return next();
211
213
  }
212
214
 
215
+ // Check if the request maps to an actual file in the dist root
216
+ // (e.g., /favicon.svg -> dist/favicon.svg)
217
+ const reqPath = c.req.path.slice(1); // Remove leading "/"
218
+ if (reqPath && reqPath !== "" && !reqPath.includes("..")) {
219
+ const staticFilePath = path.join(frontendDistPath, reqPath);
220
+ // Only serve if it's a file (not a directory) and exists
221
+ if (fs.existsSync(staticFilePath) && fs.statSync(staticFilePath).isFile()) {
222
+ return serveFile(c, staticFilePath);
223
+ }
224
+ }
225
+
226
+ // SPA fallback: serve index.html for all remaining non-API routes
213
227
  const indexPath = path.join(frontendDistPath, "index.html");
214
228
  if (fs.existsSync(indexPath)) {
215
- const file = Bun.file(indexPath);
216
- return new Response(file, {
217
- headers: { "Content-Type": "text/html" },
218
- });
229
+ return serveFile(c, indexPath);
219
230
  }
220
231
  return c.notFound();
221
232
  });