@teamscala/service-boot 0.23.552 → 0.23.555

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/package.json CHANGED
@@ -27,7 +27,7 @@
27
27
  "repository": "https://github.com/teamgbg/service-boot",
28
28
  "sideEffects": false,
29
29
  "type": "module",
30
- "version": "0.23.552",
30
+ "version": "0.23.555",
31
31
  "dependencies": {
32
32
  "@orpc/openapi": "*",
33
33
  "@orpc/server": "*",
@@ -310,4 +310,4 @@
310
310
  "./ws-boot-self-test": "./src/ws-boot-self-test.ts",
311
311
  "./ws-boot-self-test.ts": "./src/ws-boot-self-test.ts"
312
312
  }
313
- }
313
+ }
@@ -18,16 +18,31 @@ const CACHE_HEADERS = {
18
18
  Vary: "Accept",
19
19
  } as const;
20
20
 
21
+ /**
22
+ * A content-addressed asset URL that answers 404 is a race artifact — a page
23
+ * fetched moments before an asset set rotated, or a rebuild mid-write. The
24
+ * URL carries no meaning a cache may keep: a stale HTML will never become
25
+ * valid again, and Cloudflare's default edge TTL caches a header-less 404 for
26
+ * hours (measured 2026-10-23: chunk 404s served `max-age=14400` from the edge
27
+ * for files that existed at origin), re-breaking every client that held the
28
+ * old page. Never cacheable.
29
+ */
30
+ const NOT_FOUND_HEADERS = { "cache-control": "no-store" } as const;
31
+
32
+ export function assetNotFoundResponse(): Response {
33
+ return new Response("Not Found", { status: 404, headers: NOT_FOUND_HEADERS });
34
+ }
35
+
21
36
  export async function handleAssetProxy(req: Request): Promise<Response> {
22
37
  const url = new URL(req.url);
23
38
  const prefix = "/api/assets/";
24
39
  if (!url.pathname.startsWith(prefix)) {
25
- return new Response("Not Found", { status: 404 });
40
+ return assetNotFoundResponse();
26
41
  }
27
42
 
28
43
  const key = decodeURIComponent(url.pathname.slice(prefix.length));
29
44
  if (!key) {
30
- return new Response("Not Found", { status: 404 });
45
+ return assetNotFoundResponse();
31
46
  }
32
47
 
33
48
  const s3 = await getClient();
@@ -48,7 +63,7 @@ export async function handleAssetProxy(req: Request): Promise<Response> {
48
63
  error: err,
49
64
  key,
50
65
  });
51
- return new Response("Not Found", { status: 404 });
66
+ return assetNotFoundResponse();
52
67
  }
53
68
 
54
69
  const baseEtag = stats.etag || `"${key}"`;
@@ -192,7 +192,6 @@ export async function mountPgBoss(
192
192
  useListenNotify: false,
193
193
  connectionString: pgSslCompatUrl(ctx.databaseUrl),
194
194
  application_name: `pgboss:${ctx.serviceSlug}`,
195
- max: 1,
196
195
  connectionTimeoutMillis: 30_000,
197
196
  });
198
197
  boss.on("error", (err) => {
@@ -95,7 +95,11 @@ export async function registerStaticAssets(
95
95
  headers: { "cache-control": "public, max-age=31536000, immutable" },
96
96
  });
97
97
  }
98
- return c.notFound();
98
+ // A hashed asset URL that misses is a race artifact (page from before an
99
+ // asset rotation, or a rebuild mid-write) — never cacheable, else the
100
+ // edge pins the 404 and re-breaks every client holding the old page
101
+ // (assetNotFoundResponse's header note).
102
+ return new Response("Not Found", { status: 404, headers: { "cache-control": "no-store" } });
99
103
  });
100
104
 
101
105
  app.get("/ext/*", async (c) => {
@@ -110,6 +114,6 @@ export async function registerStaticAssets(
110
114
  headers: { "cache-control": "public, max-age=3600" },
111
115
  });
112
116
  }
113
- return c.notFound();
117
+ return new Response("Not Found", { status: 404, headers: { "cache-control": "no-store" } });
114
118
  });
115
119
  }
@@ -87,7 +87,12 @@ export async function buildMinimalApp(
87
87
  const logo = await resolveErrorPageLogo(serviceSlug);
88
88
  return buildResourceNotFoundResponse("page", logo, 404);
89
89
  }
90
- return c.json({ error: "Not Found" }, 404);
90
+ // No-store: a 404 a cache may keep (Cloudflare's default edge TTL caches
91
+ // a header-less 404 on cacheable extensions for hours) re-serves the miss
92
+ // long after the resource exists — same class as the asset-route misses.
93
+ return c.json({ error: "Not Found" }, 404, {
94
+ "cache-control": "no-store",
95
+ });
91
96
  });
92
97
 
93
98
  app.use("*", readinessGate);