@cosmicdrift/kumiko-server-runtime 0.337.1 → 0.338.0
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/dist/run-prod-app-static-files.js +23 -11
- package/dist/run-prod-app.d.ts +4 -1
- package/dist/run-prod-app.js +7 -0
- package/package.json +5 -5
- package/src/changes.json +14 -0
|
@@ -117,8 +117,17 @@ trustedProxyHops = 0) {
|
|
|
117
117
|
mtimeMs: file.mtimeMs,
|
|
118
118
|
};
|
|
119
119
|
}
|
|
120
|
-
function serveHtmlFile(req, pathname, html, extraHeaders) {
|
|
120
|
+
function serveHtmlFile(req, pathname, html, extraHeaders, status) {
|
|
121
|
+
// A not-found page carries no validator: a matching If-None-Match must
|
|
122
|
+
// never turn a 404 into a bodyless 304.
|
|
123
|
+
if (status === 404) {
|
|
124
|
+
return new Response(req.method === "HEAD" ? null : html.bytes, {
|
|
125
|
+
status,
|
|
126
|
+
headers: { "content-type": html.mime, "cache-control": "no-store", ...extraHeaders },
|
|
127
|
+
});
|
|
128
|
+
}
|
|
121
129
|
return cachedResponse(req, {
|
|
130
|
+
status,
|
|
122
131
|
body: html.bytes,
|
|
123
132
|
etag: html.etag,
|
|
124
133
|
cache: staticCachePolicy(pathname),
|
|
@@ -177,30 +186,33 @@ trustedProxyHops = 0) {
|
|
|
177
186
|
buildRequestContextDataFromRequest(req, { resolver: clientIpResolver, socketAddress }), () => hostDispatchDispatcher.query(type, payload, createAnonymousUser(tenantId)));
|
|
178
187
|
};
|
|
179
188
|
const result = await hostDispatch({ host, path: url.pathname, search: url.search }, { systemQuery });
|
|
180
|
-
if (result.kind === "not-found") {
|
|
181
|
-
return new Response("Not Found", { status: 404 });
|
|
182
|
-
}
|
|
183
189
|
if (result.kind === "redirect") {
|
|
184
190
|
return new Response(null, {
|
|
185
191
|
status: result.status ?? 302,
|
|
186
192
|
headers: { Location: result.to },
|
|
187
193
|
});
|
|
188
194
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
195
|
+
if (result.kind === "not-found") {
|
|
196
|
+
if (result.file === undefined)
|
|
197
|
+
return new Response("Not Found", { status: 404 });
|
|
198
|
+
return serveHostDispatchFile(req, result.file, result.csp, socketAddress, 404);
|
|
199
|
+
}
|
|
200
|
+
return serveHostDispatchFile(req, result.file, result.csp, socketAddress, 200);
|
|
201
|
+
}
|
|
202
|
+
async function serveHostDispatchFile(req, file, csp, socketAddress, status) {
|
|
203
|
+
const html = await readHtmlFile(`${staticDir}/${file}`);
|
|
192
204
|
if (!html) {
|
|
193
205
|
// Author-Fehler: hostDispatch verweist auf nicht-existente Datei.
|
|
194
206
|
// Liefer 500 statt silent-404 damit der Bug schnell auffällt.
|
|
195
|
-
return new Response(`hostDispatch: file not found: ${
|
|
207
|
+
return new Response(`hostDispatch: file not found: ${file}`, { status: 500 });
|
|
196
208
|
}
|
|
197
209
|
// Per-host body (hostDispatch picks the file by host) → Vary: Host,
|
|
198
210
|
// otherwise a shared cache could serve Host A's HTML to Host B.
|
|
199
211
|
const extraHeaders = { vary: "Host" };
|
|
200
|
-
if (
|
|
201
|
-
extraHeaders["content-security-policy"] =
|
|
212
|
+
if (csp)
|
|
213
|
+
extraHeaders["content-security-policy"] = csp;
|
|
202
214
|
const withHead = await applyPageHead(req, html, socketAddress);
|
|
203
|
-
return serveHtmlFile(req, "/index.html", withHead, extraHeaders);
|
|
215
|
+
return serveHtmlFile(req, "/index.html", withHead, extraHeaders, status);
|
|
204
216
|
}
|
|
205
217
|
return async (req, socketAddress) => {
|
|
206
218
|
const url = new URL(req.url);
|
package/dist/run-prod-app.d.ts
CHANGED
|
@@ -179,7 +179,8 @@ export type ExtraContextOption = Record<string, unknown> | ((deps: RunProdAppDep
|
|
|
179
179
|
* itself from the authenticated GET /api/schema, after the
|
|
180
180
|
* clientFeature gates (auth) let it through.
|
|
181
181
|
* - "redirect": 301/302 to the given location.
|
|
182
|
-
* - "not-found": reject
|
|
182
|
+
* - "not-found": reject (e.g. unknown subdomain) with a plain 404, or —
|
|
183
|
+
* with `file` (+ optional `csp`) — serve that page with status 404.
|
|
183
184
|
*
|
|
184
185
|
* Only consulted when the path would otherwise fall through to the HTML
|
|
185
186
|
* fallback — i.e. for "/", "/index.html", or SPA routes that neither Hono
|
|
@@ -198,6 +199,8 @@ export type HostDispatchResult = {
|
|
|
198
199
|
readonly status?: 301 | 302;
|
|
199
200
|
} | {
|
|
200
201
|
readonly kind: "not-found";
|
|
202
|
+
readonly file?: string;
|
|
203
|
+
readonly csp?: string;
|
|
201
204
|
};
|
|
202
205
|
export type HostDispatchFn = (req: {
|
|
203
206
|
readonly host: string;
|
package/dist/run-prod-app.js
CHANGED
|
@@ -242,6 +242,13 @@ export async function runProdApp(options) {
|
|
|
242
242
|
const trustedProxyHops = options.trustedProxyHops ??
|
|
243
243
|
options.auth?.trustedProxyHops ??
|
|
244
244
|
parseTrustedProxyHopsEnv(readEnv(TRUSTED_PROXY_HOPS_ENV, envSource), "runProdApp");
|
|
245
|
+
if (trustedProxyHops === undefined) {
|
|
246
|
+
// With 0 hops every request appears to come from the proxy pod, so all IP-keyed
|
|
247
|
+
// rate limiters collapse into one shared bucket behind a reverse proxy.
|
|
248
|
+
// biome-ignore lint/suspicious/noConsole: boot-time warn, no logger configured this early
|
|
249
|
+
console.warn(`[runProdApp] trustedProxyHops / ${TRUSTED_PROXY_HOPS_ENV} is not set; client IP falls back to the socket address. ` +
|
|
250
|
+
"Behind a reverse proxy all clients share one rate-limit bucket.");
|
|
251
|
+
}
|
|
245
252
|
const port = options.port ?? Number.parseInt(envSource["PORT"] ?? "3000", 10);
|
|
246
253
|
// biome-ignore lint/suspicious/noConsole: boot-time progress hint, no logger configured this early
|
|
247
254
|
console.log(`[runProdApp] booting Kumiko stack on port ${port}…`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-server-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.338.0",
|
|
4
4
|
"description": "Production server-boot runtime for Kumiko apps: connections, schema-drift-gate, seeds, lifecycle, graceful shutdown. Symmetric to kumiko-dev-server's runDevApp, without dev/scaffold/codegen tooling.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -80,10 +80,10 @@
|
|
|
80
80
|
}
|
|
81
81
|
},
|
|
82
82
|
"dependencies": {
|
|
83
|
-
"@cosmicdrift/kumiko-bundled-features": "0.
|
|
84
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
85
|
-
"@cosmicdrift/kumiko-headless": "0.
|
|
86
|
-
"@cosmicdrift/kumiko-renderer-web": "0.
|
|
83
|
+
"@cosmicdrift/kumiko-bundled-features": "0.338.0",
|
|
84
|
+
"@cosmicdrift/kumiko-framework": "0.338.0",
|
|
85
|
+
"@cosmicdrift/kumiko-headless": "0.338.0",
|
|
86
|
+
"@cosmicdrift/kumiko-renderer-web": "0.338.0",
|
|
87
87
|
"bun-types": "^1.3.13",
|
|
88
88
|
"ioredis": "^5.10.1",
|
|
89
89
|
"temporal-polyfill": "^0.3.2"
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.338.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "hostDispatch not-found can serve a custom 404 page",
|
|
6
|
+
"detail": "`HostDispatchResult` `{ kind: \"not-found\" }` accepts `file` (relative to `staticDir`) and `csp`. The page goes through the same delivery as `kind: \"html\"` (page head, `Vary: Host`, CSP) with status 404. A missing file answers 500 with the same message as `html`.",
|
|
7
|
+
"migration": "No code change needed."
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"version": "0.338.0",
|
|
11
|
+
"type": "improvement",
|
|
12
|
+
"title": "boot warning when trustedProxyHops is unset",
|
|
13
|
+
"detail": "runProdApp warns at boot when neither `trustedProxyHops` nor `KUMIKO_TRUSTED_PROXY_HOPS` is set. Behind a reverse proxy all clients then share one IP-keyed rate-limit bucket.",
|
|
14
|
+
"migration": "Set `KUMIKO_TRUSTED_PROXY_HOPS` (for example 1 behind one ingress) or pass `trustedProxyHops` to runProdApp."
|
|
15
|
+
},
|
|
2
16
|
{
|
|
3
17
|
"version": "0.335.0",
|
|
4
18
|
"type": "fix",
|