@cosmicdrift/kumiko-server-runtime 0.352.0 → 0.353.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/build-prod-bundle.d.ts +0 -1
- package/dist/build-prod-bundle.js +3 -4
- package/dist/pii-boot-gate.js +1 -1
- package/dist/request-bound-system-query.d.ts +12 -0
- package/dist/request-bound-system-query.js +9 -0
- package/dist/run-prod-app-static-files.d.ts +1 -5
- package/dist/run-prod-app-static-files.js +14 -6
- package/dist/run-prod-app.js +29 -16
- package/package.json +14 -6
- package/src/changes.json +12 -0
|
@@ -94,7 +94,6 @@ export declare function assertRendererWebShellPresent(css: string, stylesheet: R
|
|
|
94
94
|
export declare function discoverHtmlTemplate(cwd: string): string | undefined;
|
|
95
95
|
export declare function runTailwindOnce(entry: string, cwd: string): Promise<string>;
|
|
96
96
|
export declare function isEntryOutputFor(outName: string, baseName: string): boolean;
|
|
97
|
-
export declare function buildMissingTemplateError(manifest: BuildManifest, entry: ClientEntry): string;
|
|
98
97
|
export declare function injectAssetTags(html: string, manifest: BuildManifest, entry: ClientEntry, buildInfo?: BuildInfo): string;
|
|
99
98
|
export declare function computeBuildId(manifest: BuildManifest): string;
|
|
100
99
|
/** Formatiert ein BuildResult als CLI-freundliche Mehrzeilen-Zusammenfassung
|
|
@@ -672,10 +672,9 @@ async function renderHtml(templatePath, manifest, entry, buildInfo) {
|
|
|
672
672
|
const template = templatePath ? await readFile(templatePath, "utf8") : DEFAULT_HTML;
|
|
673
673
|
return injectAssetTags(template, manifest, entry, buildInfo);
|
|
674
674
|
}
|
|
675
|
-
//
|
|
676
|
-
//
|
|
677
|
-
|
|
678
|
-
export function buildMissingTemplateError(manifest, entry) {
|
|
675
|
+
// The message must name the source file and, for a declared multi-entry,
|
|
676
|
+
// which package.json "kumiko.clientEntries" entry it came from.
|
|
677
|
+
function buildMissingTemplateError(manifest, entry) {
|
|
679
678
|
const cssLine = manifest["styles.css"]
|
|
680
679
|
? ` <link rel="stylesheet" href="/styles.css" />\n`
|
|
681
680
|
: "";
|
package/dist/pii-boot-gate.js
CHANGED
|
@@ -17,7 +17,7 @@ export function assertPiiBootInvariants(features, opts) {
|
|
|
17
17
|
// encryptEventPayloadPii no-ops without a KMS, so a catalogued event writes
|
|
18
18
|
// plaintext PII into kumiko_events with nothing to crypto-shred later.
|
|
19
19
|
const piiEvents = features.flatMap((feature) => Object.values(feature.events ?? {})
|
|
20
|
-
.filter((event) => event.piiFields !==
|
|
20
|
+
.filter((event) => event.piiFields !== "none")
|
|
21
21
|
.map((event) => event.name));
|
|
22
22
|
// skip: nothing PII-annotated is mounted — plaintext gate is moot.
|
|
23
23
|
if (piiEntities.length === 0 && piiEvents.length === 0)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ClientIpResolver } from "@cosmicdrift/kumiko-framework/api";
|
|
2
|
+
import { type SessionUser } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import type { PageHeadSystemQuery } from "@cosmicdrift/kumiko-headless/apex";
|
|
4
|
+
export type QueryDispatcher = {
|
|
5
|
+
readonly query: (type: string, payload: unknown, user: SessionUser) => Promise<unknown>;
|
|
6
|
+
};
|
|
7
|
+
export declare function buildRequestBoundSystemQuery(params: {
|
|
8
|
+
readonly req: Request;
|
|
9
|
+
readonly dispatcher: QueryDispatcher;
|
|
10
|
+
readonly resolver: ClientIpResolver;
|
|
11
|
+
readonly socketAddress?: string;
|
|
12
|
+
}): PageHeadSystemQuery;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { buildRequestContextDataFromRequest, requestContext, } from "@cosmicdrift/kumiko-framework/api";
|
|
2
|
+
import { createAnonymousUser } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
// Single wiring for the anonymous-role systemQuery that page-head and
|
|
4
|
+
// hostDispatch resolvers receive, shared by runProdApp and the dev server so
|
|
5
|
+
// the anonymous role / request-context / client-IP resolution can't drift.
|
|
6
|
+
export function buildRequestBoundSystemQuery(params) {
|
|
7
|
+
const { req, dispatcher, resolver, socketAddress } = params;
|
|
8
|
+
return (type, payload, tenantId) => requestContext.run(requestContext.get() ?? buildRequestContextDataFromRequest(req, { resolver, socketAddress }), () => dispatcher.query(type, payload, createAnonymousUser(tenantId)));
|
|
9
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type CachePolicy } from "@cosmicdrift/kumiko-framework/api";
|
|
2
|
-
import { type
|
|
2
|
+
import { type QueryDispatcher } from "./request-bound-system-query.js";
|
|
3
3
|
import type { HostDispatchFn, PageHeadResolver } from "./run-prod-app.js";
|
|
4
4
|
export declare function readStaticFile(filePath: string): Promise<{
|
|
5
5
|
readonly bytes: Uint8Array;
|
|
@@ -12,9 +12,6 @@ export declare function serveDiskFile(req: Request, pathname: string, file: {
|
|
|
12
12
|
readonly mtimeMs: number;
|
|
13
13
|
}): Response;
|
|
14
14
|
export declare function mimeTypeFor(filePath: string): string;
|
|
15
|
-
type QueryDispatcher = {
|
|
16
|
-
readonly query: (type: string, payload: unknown, user: SessionUser) => Promise<unknown>;
|
|
17
|
-
};
|
|
18
15
|
export type PageHeadOptions = {
|
|
19
16
|
readonly resolvePageHead: PageHeadResolver;
|
|
20
17
|
readonly dispatcher: QueryDispatcher;
|
|
@@ -29,4 +26,3 @@ hostDispatchDispatcher?: QueryDispatcher,
|
|
|
29
26
|
* 1:1 with runProdApp's effective value. */
|
|
30
27
|
trustedProxyHops?: number): (req: Request, socketAddress?: string) => Promise<Response>;
|
|
31
28
|
export declare function staticCachePolicy(pathname: string): CachePolicy;
|
|
32
|
-
export {};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { resolve, sep } from "node:path";
|
|
2
|
-
import {
|
|
3
|
-
import { createAnonymousUser } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
import { cachedResponse, computeStrongEtag, computeWeakEtag, createClientIpResolver, } from "@cosmicdrift/kumiko-framework/api";
|
|
4
3
|
import { resolveAndInjectPageHead } from "@cosmicdrift/kumiko-headless/apex";
|
|
5
4
|
import { ASSETS_DIR } from "./build-prod-bundle.js";
|
|
6
5
|
import { BUNDLED_ASSETS_DIST_DIR } from "./bundled-assets.js";
|
|
6
|
+
import { buildRequestBoundSystemQuery, } from "./request-bound-system-query.js";
|
|
7
7
|
import { stripNoRouteMatchHeader, tryHonoFirst } from "./try-hono-first.js";
|
|
8
8
|
// Static-asset + SPA-fallback serving for runProdApp's HTTP handler. Split
|
|
9
9
|
// out of run-prod-app.ts (#1005, Welle 2) — mechanical relocation, these
|
|
@@ -162,8 +162,12 @@ trustedProxyHops = 0) {
|
|
|
162
162
|
return html;
|
|
163
163
|
const url = new URL(req.url);
|
|
164
164
|
const host = req.headers.get("host") ?? url.host;
|
|
165
|
-
const systemQuery = (
|
|
166
|
-
|
|
165
|
+
const systemQuery = buildRequestBoundSystemQuery({
|
|
166
|
+
req,
|
|
167
|
+
dispatcher: pageHead.dispatcher,
|
|
168
|
+
resolver: clientIpResolver,
|
|
169
|
+
socketAddress,
|
|
170
|
+
});
|
|
167
171
|
const text = new TextDecoder().decode(html.bytes);
|
|
168
172
|
const injected = await resolveAndInjectPageHead(text, pageHead.resolvePageHead, {
|
|
169
173
|
path: url.pathname,
|
|
@@ -192,8 +196,12 @@ trustedProxyHops = 0) {
|
|
|
192
196
|
if (!hostDispatchDispatcher) {
|
|
193
197
|
throw new Error("hostDispatch called deps.systemQuery but buildStaticFallback got no hostDispatchDispatcher");
|
|
194
198
|
}
|
|
195
|
-
return
|
|
196
|
-
|
|
199
|
+
return buildRequestBoundSystemQuery({
|
|
200
|
+
req,
|
|
201
|
+
dispatcher: hostDispatchDispatcher,
|
|
202
|
+
resolver: clientIpResolver,
|
|
203
|
+
socketAddress,
|
|
204
|
+
})(type, payload, tenantId);
|
|
197
205
|
};
|
|
198
206
|
const result = await hostDispatch({ host, path: url.pathname, search: url.search }, { systemQuery });
|
|
199
207
|
if (result.kind === "redirect") {
|
package/dist/run-prod-app.js
CHANGED
|
@@ -176,6 +176,28 @@ export async function runProdApp(options) {
|
|
|
176
176
|
// needed in 9.1's audit (37 references, 25 distinct vars).
|
|
177
177
|
const rawEnvSource = options.envSource ?? process.env;
|
|
178
178
|
const runMode = parseRunMode(rawEnvSource["KUMIKO_DRY_RUN_ENV"]);
|
|
179
|
+
const composedEnvSchema = options.envSchema;
|
|
180
|
+
const validateEnv = (source) => {
|
|
181
|
+
// skip: app declares no env schema, nothing to validate
|
|
182
|
+
if (!composedEnvSchema)
|
|
183
|
+
return;
|
|
184
|
+
try {
|
|
185
|
+
const schema = options.masterKey
|
|
186
|
+
? withOptionalEnvKeys(composedEnvSchema.schema, SECRETS_MASTER_KEK_ENV_KEYS)
|
|
187
|
+
: composedEnvSchema.schema;
|
|
188
|
+
parseEnv(schema, source, {
|
|
189
|
+
sources: composedEnvSchema.sources,
|
|
190
|
+
...(options.pulumiPrefix ? { pulumiPrefix: options.pulumiPrefix } : {}),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
catch (err) {
|
|
194
|
+
if (err instanceof KumikoBootError) {
|
|
195
|
+
const reporter = options.bootErrorReporter ?? defaultBootErrorReporter;
|
|
196
|
+
reporter(err);
|
|
197
|
+
}
|
|
198
|
+
throw err;
|
|
199
|
+
}
|
|
200
|
+
};
|
|
179
201
|
if (options.envSchema) {
|
|
180
202
|
if (isRenderMode(runMode)) {
|
|
181
203
|
// biome-ignore lint/suspicious/noConsole: dry-run output IS the deliverable
|
|
@@ -194,22 +216,7 @@ export async function runProdApp(options) {
|
|
|
194
216
|
// boot-mode AND normal-boot both run env-validation. boot-mode wants
|
|
195
217
|
// a real env-check (all required vars present + schema-valid) before
|
|
196
218
|
// it asserts feature-wiring works.
|
|
197
|
-
|
|
198
|
-
const envSchema = options.masterKey
|
|
199
|
-
? withOptionalEnvKeys(options.envSchema.schema, SECRETS_MASTER_KEK_ENV_KEYS)
|
|
200
|
-
: options.envSchema.schema;
|
|
201
|
-
parseEnv(envSchema, rawEnvSource, {
|
|
202
|
-
sources: options.envSchema.sources,
|
|
203
|
-
...(options.pulumiPrefix ? { pulumiPrefix: options.pulumiPrefix } : {}),
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
catch (err) {
|
|
207
|
-
if (err instanceof KumikoBootError) {
|
|
208
|
-
const reporter = options.bootErrorReporter ?? defaultBootErrorReporter;
|
|
209
|
-
reporter(err);
|
|
210
|
-
}
|
|
211
|
-
throw err;
|
|
212
|
-
}
|
|
219
|
+
validateEnv(rawEnvSource);
|
|
213
220
|
}
|
|
214
221
|
// Slots the schema marks `kms` are decrypted once here so every consumer
|
|
215
222
|
// below (boot probe, master-key keyring, config) sees the same resolved env.
|
|
@@ -219,6 +226,12 @@ export async function runProdApp(options) {
|
|
|
219
226
|
logPrefix: "[runProdApp]",
|
|
220
227
|
})
|
|
221
228
|
: rawEnvSource;
|
|
229
|
+
// The first parse relaxes ciphertext-only slots, so their decrypted
|
|
230
|
+
// plaintext never met the slot's own validators (.regex/.min/.refine).
|
|
231
|
+
if (options.envSchema &&
|
|
232
|
+
kmsSlotsOf(options.envSchema.schema).some((slot) => rawEnvSource[slot] === undefined && envSource[slot] !== undefined)) {
|
|
233
|
+
validateEnv(envSource);
|
|
234
|
+
}
|
|
222
235
|
// 1. Polyfill before anything else — feature code references Temporal.
|
|
223
236
|
const { ensureTemporalPolyfill } = await import("@cosmicdrift/kumiko-framework/time");
|
|
224
237
|
await ensureTemporalPolyfill();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-server-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.353.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>",
|
|
@@ -74,17 +74,21 @@
|
|
|
74
74
|
"types": "./dist/try-hono-first.d.ts",
|
|
75
75
|
"default": "./dist/try-hono-first.js"
|
|
76
76
|
},
|
|
77
|
+
"./request-bound-system-query": {
|
|
78
|
+
"types": "./dist/request-bound-system-query.d.ts",
|
|
79
|
+
"default": "./dist/request-bound-system-query.js"
|
|
80
|
+
},
|
|
77
81
|
"./build-prod-bundle": {
|
|
78
82
|
"types": "./dist/build-prod-bundle.d.ts",
|
|
79
83
|
"default": "./dist/build-prod-bundle.js"
|
|
80
84
|
}
|
|
81
85
|
},
|
|
82
86
|
"dependencies": {
|
|
83
|
-
"@cosmicdrift/kumiko-bundled-features": "0.
|
|
84
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
85
|
-
"@cosmicdrift/kumiko-headless": "0.
|
|
86
|
-
"@cosmicdrift/kumiko-renderer-web": "0.
|
|
87
|
-
"@cosmicdrift/kumiko-types": "0.
|
|
87
|
+
"@cosmicdrift/kumiko-bundled-features": "0.353.0",
|
|
88
|
+
"@cosmicdrift/kumiko-framework": "0.353.0",
|
|
89
|
+
"@cosmicdrift/kumiko-headless": "0.353.0",
|
|
90
|
+
"@cosmicdrift/kumiko-renderer-web": "0.353.0",
|
|
91
|
+
"@cosmicdrift/kumiko-types": "0.353.0",
|
|
88
92
|
"bun-types": "^1.3.13",
|
|
89
93
|
"ioredis": "^5.10.1",
|
|
90
94
|
"temporal-polyfill": "^0.3.2"
|
|
@@ -149,6 +153,10 @@
|
|
|
149
153
|
"types": "./dist/try-hono-first.d.ts",
|
|
150
154
|
"default": "./dist/try-hono-first.js"
|
|
151
155
|
},
|
|
156
|
+
"./request-bound-system-query": {
|
|
157
|
+
"types": "./dist/request-bound-system-query.d.ts",
|
|
158
|
+
"default": "./dist/request-bound-system-query.js"
|
|
159
|
+
},
|
|
152
160
|
"./build-prod-bundle": {
|
|
153
161
|
"types": "./dist/build-prod-bundle.d.ts",
|
|
154
162
|
"default": "./dist/build-prod-bundle.js"
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.353.0",
|
|
4
|
+
"type": "fix",
|
|
5
|
+
"title": "Boot rejects decrypted kms env slots that violate their schema validators",
|
|
6
|
+
"detail": "runProdApp validates Key-Manager-decrypted env slots against their schema\nA `kms` slot delivered only as `_CIPHERTEXT` skipped its own `.regex`/`.min`/`.refine` because the first parse relaxes it. After decryption the env is now parsed again, so a malformed plaintext aborts the boot with a `KumikoBootError` naming the slot."
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"version": "0.353.0",
|
|
10
|
+
"type": "improvement",
|
|
11
|
+
"title": "Prod static files and the dev server share one request-bound systemQuery wiring",
|
|
12
|
+
"detail": "Shared request-bound systemQuery wiring and clearer deploy scaffold warnings\n`@cosmicdrift/kumiko-server-runtime/request-bound-system-query` exports the systemQuery wiring that the prod static-file path and the dev server previously each copied. `scaffoldDeploy` now tells a broken `package.json` (deploy settings fall back to defaults) apart from an unexpected `dependencies`/`workspaces` shape in its warning."
|
|
13
|
+
},
|
|
2
14
|
{
|
|
3
15
|
"version": "0.347.0",
|
|
4
16
|
"type": "fix",
|