@cosmicdrift/kumiko-dev-server 0.371.1 → 0.373.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/bin/kumiko-build.ts +27 -13
- package/dist/build-server-bundle.d.ts +13 -1
- package/dist/build-server-bundle.js +71 -7
- package/dist/build.d.ts +2 -1
- package/dist/build.js +2 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/create-kumiko-server.js +7 -1
- package/dist/scaffold-app.js +7 -1
- package/dist/write-server-lockfile.d.ts +15 -0
- package/dist/write-server-lockfile.js +47 -0
- package/package.json +9 -9
- package/src/changes.json +13 -0
- package/templates/deploy/Dockerfile.template +4 -3
- package/templates/managed/kumiko-schema.ts.template +3 -0
package/bin/kumiko-build.ts
CHANGED
|
@@ -1,49 +1,57 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
// kumiko-build —
|
|
2
|
+
// kumiko-build — production build for Kumiko apps. Convention-driven:
|
|
3
3
|
//
|
|
4
4
|
// package.json "kumiko.clientEntries"/"kumiko.clientEntry",
|
|
5
|
-
// or src/client.tsx | src/styles.css | public/ →
|
|
6
|
-
// bin/main.ts →
|
|
5
|
+
// or src/client.tsx | src/styles.css | public/ → client bundle (dist/)
|
|
6
|
+
// bin/main.ts → server bundle (dist-server/,
|
|
7
|
+
// incl. bun.lock seeded from the app's bun.lock)
|
|
7
8
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
9
|
+
// Both are built when their conventions match, otherwise skipped. A workspace
|
|
10
|
+
// with only bin/main.ts gets just the server bundle, a browser-only sample
|
|
11
|
+
// just the client.
|
|
11
12
|
//
|
|
12
13
|
// {
|
|
13
14
|
// "scripts": { "build": "kumiko-build" }
|
|
14
15
|
// }
|
|
15
16
|
//
|
|
16
|
-
//
|
|
17
|
+
// An optional path argument overrides cwd:
|
|
17
18
|
// kumiko-build samples/apps/showcase
|
|
19
|
+
//
|
|
20
|
+
// Extra server entry points (repeatable, paths relative to the app dir), each
|
|
21
|
+
// emitted as dist-server/<basename>.js:
|
|
22
|
+
// kumiko-build --entry bin/remote-probe.ts
|
|
18
23
|
|
|
19
24
|
import { existsSync } from "node:fs";
|
|
20
|
-
import { join, resolve } from "node:path";
|
|
25
|
+
import { join, relative, resolve } from "node:path";
|
|
21
26
|
import {
|
|
22
27
|
buildProdBundle,
|
|
23
28
|
buildServerBundle,
|
|
24
29
|
discoverServerEntry,
|
|
25
30
|
formatBuildResult,
|
|
26
31
|
formatServerBuildResult,
|
|
32
|
+
parseKumikoBuildArgs,
|
|
27
33
|
readClientEntriesConfig,
|
|
28
34
|
readExtraRuntimeExternals,
|
|
29
35
|
resolveClientEntries,
|
|
36
|
+
writeServerLockfile,
|
|
30
37
|
} from "@cosmicdrift/kumiko-dev-server/cli";
|
|
31
38
|
|
|
32
39
|
if (process.argv.slice(2).some((arg) => arg === "--help" || arg === "-h")) {
|
|
33
40
|
process.stdout.write(
|
|
34
|
-
"Usage: kumiko-build [app-dir] build client (dist/) and server (dist-server/) bundles\n",
|
|
41
|
+
"Usage: kumiko-build [app-dir] [--entry <path>]... build client (dist/) and server (dist-server/) bundles; --entry adds a server entry point (dist-server/<basename>.js)\n",
|
|
35
42
|
);
|
|
36
43
|
process.exit(0);
|
|
37
44
|
}
|
|
38
45
|
|
|
39
|
-
const explicit = process.argv.slice(2).find((arg) => !arg.startsWith("-"));
|
|
40
|
-
const cwd = explicit ? resolve(process.cwd(), explicit) : process.cwd();
|
|
41
|
-
|
|
42
46
|
const red = "\x1b[31m";
|
|
43
47
|
const yellow = "\x1b[33m";
|
|
48
|
+
const dim = "\x1b[2m";
|
|
44
49
|
const reset = "\x1b[0m";
|
|
45
50
|
|
|
46
51
|
try {
|
|
52
|
+
const buildArgs = parseKumikoBuildArgs(process.argv.slice(2));
|
|
53
|
+
const cwd = buildArgs.appDir ? resolve(process.cwd(), buildArgs.appDir) : process.cwd();
|
|
54
|
+
|
|
47
55
|
// Read once up front — resolveClientEntries can throw (mutual exclusion,
|
|
48
56
|
// malformed declaration, unmigrated legacy client-*.tsx files) and that
|
|
49
57
|
// must surface through the same ✗/exit(1) path as every other build error.
|
|
@@ -77,10 +85,16 @@ try {
|
|
|
77
85
|
const result = await buildServerBundle({
|
|
78
86
|
cwd,
|
|
79
87
|
...(extraRuntimeExternals.length > 0 && { extraRuntimeExternals }),
|
|
88
|
+
...(buildArgs.extraEntries.length > 0 && { extraEntries: buildArgs.extraEntries }),
|
|
80
89
|
});
|
|
81
90
|
const ms = Math.round(performance.now() - t0);
|
|
91
|
+
const lock = await writeServerLockfile({ cwd, outDir: result.outDir });
|
|
92
|
+
const lockLine =
|
|
93
|
+
lock.mode === "seeded"
|
|
94
|
+
? ` ${dim}bun.lock seeded from ${relative(cwd, lock.seedLockfile) || "bun.lock"}${reset}`
|
|
95
|
+
: ` ${yellow}!${reset} bun.lock resolved fresh – no bun.lock found above ${cwd}`;
|
|
82
96
|
// biome-ignore lint/suspicious/noConsole: CLI-Output, einziger Weg
|
|
83
|
-
console.log(formatServerBuildResult(result, ms));
|
|
97
|
+
console.log(`${formatServerBuildResult(result, ms)}\n${lockLine}`);
|
|
84
98
|
}
|
|
85
99
|
} catch (err) {
|
|
86
100
|
// biome-ignore lint/suspicious/noConsole: CLI-Output, einziger Weg
|
|
@@ -8,6 +8,11 @@ export type BuildServerBundleOptions = {
|
|
|
8
8
|
readonly extraRuntimeExternals?: readonly string[];
|
|
9
9
|
/** Zusätzliche Build-only-Externals. App-spezifisch — Default leer. */
|
|
10
10
|
readonly extraBuildOnlyExternals?: readonly string[];
|
|
11
|
+
/** Additional entry points (paths relative to cwd, e.g. "bin/remote-probe.ts"),
|
|
12
|
+
* each emitted as `<basename>.js` next to server.js and sharing its chunks.
|
|
13
|
+
* A missing file or a basename that collides with a built-in entry or another
|
|
14
|
+
* extra entry throws. */
|
|
15
|
+
readonly extraEntries?: readonly string[];
|
|
11
16
|
};
|
|
12
17
|
export type BuildServerBundleEntry = {
|
|
13
18
|
readonly file: string;
|
|
@@ -15,7 +20,7 @@ export type BuildServerBundleEntry = {
|
|
|
15
20
|
};
|
|
16
21
|
export type BuildServerBundleResult = {
|
|
17
22
|
readonly outDir: string;
|
|
18
|
-
/** Named entry files (server.js, plus kumiko.js, worker.js
|
|
23
|
+
/** Named entry files (server.js, plus kumiko.js, worker.js, bootstrap.js and extra entries when present). */
|
|
19
24
|
readonly entries: readonly BuildServerBundleEntry[];
|
|
20
25
|
/** Geteilte Framework-Chunks (von splitting). */
|
|
21
26
|
readonly chunks: readonly BuildServerBundleEntry[];
|
|
@@ -25,6 +30,13 @@ export type BuildServerBundleResult = {
|
|
|
25
30
|
};
|
|
26
31
|
export declare function buildServerBundle(options?: BuildServerBundleOptions): Promise<BuildServerBundleResult>;
|
|
27
32
|
export declare function discoverServerEntry(cwd: string): string | undefined;
|
|
33
|
+
export type KumikoBuildArgs = {
|
|
34
|
+
readonly appDir: string | undefined;
|
|
35
|
+
readonly extraEntries: readonly string[];
|
|
36
|
+
};
|
|
37
|
+
/** Parse `kumiko-build [app-dir] [--entry <path>]...` (also `--entry=<path>`).
|
|
38
|
+
* Unknown flags and a missing `--entry` value throw instead of being ignored. */
|
|
39
|
+
export declare function parseKumikoBuildArgs(args: readonly string[]): KumikoBuildArgs;
|
|
28
40
|
/** Read `package.json#kumiko.extraRuntimeExternals` for kumiko-build (#1484).
|
|
29
41
|
* Invalid / missing shapes → empty list (CLI stays convention-driven). */
|
|
30
42
|
export declare function readExtraRuntimeExternals(cwd: string): readonly string[];
|
|
@@ -12,11 +12,14 @@
|
|
|
12
12
|
// background worker as its own process from the same image)
|
|
13
13
|
// bin/bootstrap.ts → dist-server/bootstrap.js (optional, next to bin/main.ts:
|
|
14
14
|
// one-shot runBootstrap provisioning, run as a k8s Job)
|
|
15
|
+
// extraEntries[i] → dist-server/<basename>.js (opt-in, e.g. a probe script
|
|
16
|
+
// `bin/remote-probe.ts`; CLI: repeatable `--entry <path>`)
|
|
15
17
|
// dist-server/package.json → runtime-deps mit gepinnten Versionen
|
|
16
18
|
//
|
|
17
19
|
// All entries are built in ONE Bun.build call with `splitting`: the framework
|
|
18
|
-
// lands once as shared chunk-*.js and server.js, kumiko.js, worker.js
|
|
19
|
-
// bootstrap.js are thin entries importing it. Separate
|
|
20
|
+
// lands once as shared chunk-*.js and server.js, kumiko.js, worker.js,
|
|
21
|
+
// bootstrap.js and the extra entries are thin entries importing it. Separate
|
|
22
|
+
// bundles would inline ~14 MB per entry.
|
|
20
23
|
//
|
|
21
24
|
// Output:
|
|
22
25
|
//
|
|
@@ -25,8 +28,10 @@
|
|
|
25
28
|
// kumiko.js ← Migrate-CLI-Entry (wenn bin/kumiko.ts gefunden)
|
|
26
29
|
// worker.js ← worker entry (if bin/worker.ts sits next to main.ts)
|
|
27
30
|
// bootstrap.js ← provisioning entry (if bin/bootstrap.ts sits next to main.ts)
|
|
31
|
+
// <basename>.js ← one per extraEntries path
|
|
28
32
|
// chunk-*.js ← geteilte Framework-Chunks
|
|
29
33
|
// package.json ← runtime-deps mit gepinnten Versionen
|
|
34
|
+
// bun.lock ← written by writeServerLockfile (kumiko-build), not by buildServerBundle
|
|
30
35
|
//
|
|
31
36
|
// Externals — Pakete die NICHT ins Bundle gebakt werden, sondern via
|
|
32
37
|
// `bun install` im Runtime-Container nachgeholt werden:
|
|
@@ -41,7 +46,7 @@
|
|
|
41
46
|
// resolution-during-build ab. NICHT in runtime-deps.
|
|
42
47
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
43
48
|
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
44
|
-
import { dirname, join, resolve } from "node:path";
|
|
49
|
+
import { basename, dirname, extname, join, resolve } from "node:path";
|
|
45
50
|
import { parseJsonOrThrow, parseJsonSafe } from "@cosmicdrift/kumiko-framework/utils";
|
|
46
51
|
const hasBun = typeof Bun !== "undefined";
|
|
47
52
|
// Pakete die das Bundle zur Laufzeit weiter referenziert (`from "<pkg>"` nach
|
|
@@ -85,6 +90,33 @@ const BUILD_ONLY_EXTERNALS = [
|
|
|
85
90
|
// ist die Runtime-Convention; der Entry wird von nichts importiert (nur die
|
|
86
91
|
// chunks werden referenziert, per relativem Pfad), darum ist der Rename sicher.
|
|
87
92
|
const ENTRY_RENAMES = { "main.js": "server.js" };
|
|
93
|
+
const OPTIONAL_SIBLING_ENTRY_NAMES = ["worker", "bootstrap"];
|
|
94
|
+
// Output names the built-in entries occupy ("main" is renamed to server.js).
|
|
95
|
+
const RESERVED_ENTRY_OUTPUT_NAMES = new Set([
|
|
96
|
+
"server.js",
|
|
97
|
+
"main.js",
|
|
98
|
+
"kumiko.js",
|
|
99
|
+
...OPTIONAL_SIBLING_ENTRY_NAMES.map((name) => `${name}.js`),
|
|
100
|
+
]);
|
|
101
|
+
function resolveExtraEntries(cwd, extraEntries) {
|
|
102
|
+
const claimedOutputNames = new Map();
|
|
103
|
+
return extraEntries.map((entry) => {
|
|
104
|
+
const absolute = resolve(cwd, entry);
|
|
105
|
+
if (!existsSync(absolute) || !statSync(absolute).isFile()) {
|
|
106
|
+
throw new Error(`[buildServerBundle] extra entry "${entry}" does not exist (resolved to ${absolute}).`);
|
|
107
|
+
}
|
|
108
|
+
const outputName = `${basename(absolute, extname(absolute))}.js`;
|
|
109
|
+
if (RESERVED_ENTRY_OUTPUT_NAMES.has(outputName)) {
|
|
110
|
+
throw new Error(`[buildServerBundle] extra entry "${entry}" would emit ${outputName}, which a built-in entry already uses. Rename the file.`);
|
|
111
|
+
}
|
|
112
|
+
const previous = claimedOutputNames.get(outputName);
|
|
113
|
+
if (previous !== undefined) {
|
|
114
|
+
throw new Error(`[buildServerBundle] extra entries "${previous}" and "${entry}" both emit ${outputName}. Rename one of the files.`);
|
|
115
|
+
}
|
|
116
|
+
claimedOutputNames.set(outputName, entry);
|
|
117
|
+
return absolute;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
88
120
|
export async function buildServerBundle(options = {}) {
|
|
89
121
|
if (!hasBun) {
|
|
90
122
|
throw new Error("buildServerBundle requires Bun runtime (Bun.build is missing).");
|
|
@@ -100,6 +132,7 @@ export async function buildServerBundle(options = {}) {
|
|
|
100
132
|
if (kumikoCli && !existsSync(kumikoCli)) {
|
|
101
133
|
throw new Error(`[buildServerBundle] Repo-Root erkannt (${repoRoot}), aber bin/kumiko.ts fehlt — kann Migrate-CLI nicht bündeln.`);
|
|
102
134
|
}
|
|
135
|
+
const extraEntryPaths = resolveExtraEntries(cwd, options.extraEntries ?? []);
|
|
103
136
|
const externals = [
|
|
104
137
|
...RUNTIME_EXTERNALS,
|
|
105
138
|
...BUILD_ONLY_EXTERNALS,
|
|
@@ -110,10 +143,13 @@ export async function buildServerBundle(options = {}) {
|
|
|
110
143
|
await mkdir(outDir, { recursive: true });
|
|
111
144
|
// Ein Bun.build-Call mit allen Entries + splitting → das Framework wird
|
|
112
145
|
// einmal als shared chunk abgelegt statt pro Entry inlined.
|
|
113
|
-
const optionalSiblingEntries =
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
146
|
+
const optionalSiblingEntries = OPTIONAL_SIBLING_ENTRY_NAMES.map((baseName) => discoverSiblingEntry(dirname(serverEntry), baseName)).filter((path) => path !== undefined);
|
|
147
|
+
const entrypoints = [
|
|
148
|
+
serverEntry,
|
|
149
|
+
...(kumikoCli ? [kumikoCli] : []),
|
|
150
|
+
...optionalSiblingEntries,
|
|
151
|
+
...extraEntryPaths,
|
|
152
|
+
];
|
|
117
153
|
const result = await Bun.build({
|
|
118
154
|
entrypoints,
|
|
119
155
|
outdir: outDir,
|
|
@@ -188,6 +224,34 @@ function findRepoRoot(start) {
|
|
|
188
224
|
}
|
|
189
225
|
return undefined;
|
|
190
226
|
}
|
|
227
|
+
/** Parse `kumiko-build [app-dir] [--entry <path>]...` (also `--entry=<path>`).
|
|
228
|
+
* Unknown flags and a missing `--entry` value throw instead of being ignored. */
|
|
229
|
+
export function parseKumikoBuildArgs(args) {
|
|
230
|
+
let appDir;
|
|
231
|
+
const extraEntries = [];
|
|
232
|
+
for (let i = 0; i < args.length; i++) {
|
|
233
|
+
const arg = args[i];
|
|
234
|
+
if (arg === undefined)
|
|
235
|
+
break;
|
|
236
|
+
if (arg === "--entry" || arg.startsWith("--entry=")) {
|
|
237
|
+
const value = arg === "--entry" ? args[++i] : arg.slice("--entry=".length);
|
|
238
|
+
if (value === undefined || value === "") {
|
|
239
|
+
throw new Error("kumiko-build: --entry needs a path, e.g. --entry bin/remote-probe.ts");
|
|
240
|
+
}
|
|
241
|
+
extraEntries.push(value);
|
|
242
|
+
}
|
|
243
|
+
else if (arg.startsWith("-")) {
|
|
244
|
+
throw new Error(`kumiko-build: unknown option ${arg}`);
|
|
245
|
+
}
|
|
246
|
+
else if (appDir === undefined) {
|
|
247
|
+
appDir = arg;
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
throw new Error(`kumiko-build: unexpected argument ${arg}`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return { appDir, extraEntries };
|
|
254
|
+
}
|
|
191
255
|
/** Read `package.json#kumiko.extraRuntimeExternals` for kumiko-build (#1484).
|
|
192
256
|
* Invalid / missing shapes → empty list (CLI stays convention-driven). */
|
|
193
257
|
export function readExtraRuntimeExternals(cwd) {
|
package/dist/build.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export { ASSETS_DIR, type BuildManifest, type BuildProdBundleOptions, type BuildResult, buildProdBundle, type ClientEntry, type ClientEntryDeclaration, discoverHtmlTemplate, formatBuildResult, injectAssetTags, readClientEntriesConfig, resolveClientEntries, } from "@cosmicdrift/kumiko-server-runtime/build-prod-bundle";
|
|
2
|
-
export { type BuildServerBundleEntry, type BuildServerBundleOptions, type BuildServerBundleResult, buildServerBundle, discoverServerEntry, formatServerBuildResult, readExtraRuntimeExternals, } from "./build-server-bundle.ts";
|
|
2
|
+
export { type BuildServerBundleEntry, type BuildServerBundleOptions, type BuildServerBundleResult, buildServerBundle, discoverServerEntry, formatServerBuildResult, type KumikoBuildArgs, parseKumikoBuildArgs, readExtraRuntimeExternals, } from "./build-server-bundle.ts";
|
|
3
|
+
export { type ServerLockfileResult, type WriteServerLockfileOptions, writeServerLockfile, } from "./write-server-lockfile.ts";
|
package/dist/build.js
CHANGED
|
@@ -2,4 +2,5 @@
|
|
|
2
2
|
// damit der Main-Barrel kein Bun-Toolchain-Bundle in Production-Reads zieht
|
|
3
3
|
// (z.B. wenn drizzle-kit die App-Config unter Node lädt).
|
|
4
4
|
export { ASSETS_DIR, buildProdBundle, discoverHtmlTemplate, formatBuildResult, injectAssetTags, readClientEntriesConfig, resolveClientEntries, } from "@cosmicdrift/kumiko-server-runtime/build-prod-bundle";
|
|
5
|
-
export { buildServerBundle, discoverServerEntry, formatServerBuildResult, readExtraRuntimeExternals, } from "./build-server-bundle.js";
|
|
5
|
+
export { buildServerBundle, discoverServerEntry, formatServerBuildResult, parseKumikoBuildArgs, readExtraRuntimeExternals, } from "./build-server-bundle.js";
|
|
6
|
+
export { writeServerLockfile, } from "./write-server-lockfile.js";
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { buildProdBundle, buildServerBundle, discoverServerEntry, formatBuildResult, formatServerBuildResult, readClientEntriesConfig, readExtraRuntimeExternals, resolveClientEntries, } from "./build.ts";
|
|
1
|
+
export { buildProdBundle, buildServerBundle, discoverServerEntry, formatBuildResult, formatServerBuildResult, parseKumikoBuildArgs, readClientEntriesConfig, readExtraRuntimeExternals, resolveClientEntries, writeServerLockfile, } from "./build.ts";
|
|
2
2
|
export { createCrashTracker } from "./crash-tracker.ts";
|
|
3
3
|
export { runInitDeployCli } from "./init-deploy-cli.ts";
|
package/dist/cli.js
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
// the package self-reference (`@cosmicdrift/kumiko-dev-server/cli`) so they
|
|
3
3
|
// resolve to src in the workspace and to dist once installed from the
|
|
4
4
|
// tarball, where `src` is not shipped.
|
|
5
|
-
export { buildProdBundle, buildServerBundle, discoverServerEntry, formatBuildResult, formatServerBuildResult, readClientEntriesConfig, readExtraRuntimeExternals, resolveClientEntries, } from "./build.js";
|
|
5
|
+
export { buildProdBundle, buildServerBundle, discoverServerEntry, formatBuildResult, formatServerBuildResult, parseKumikoBuildArgs, readClientEntriesConfig, readExtraRuntimeExternals, resolveClientEntries, writeServerLockfile, } from "./build.js";
|
|
6
6
|
export { createCrashTracker } from "./crash-tracker.js";
|
|
7
7
|
export { runInitDeployCli } from "./init-deploy-cli.js";
|
|
@@ -23,12 +23,13 @@ import { resolveAnonymousAccessFromRegistry } from "@cosmicdrift/kumiko-bundled-
|
|
|
23
23
|
import { AUTH_COOKIE_BASE, CSRF_COOKIE_BASE, createClientIpResolver, generateToken, sessionCookieName, } from "@cosmicdrift/kumiko-framework/api";
|
|
24
24
|
import { buildAppSchema } from "@cosmicdrift/kumiko-framework/engine";
|
|
25
25
|
import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
26
|
+
import { rankPreferredLocales } from "@cosmicdrift/kumiko-framework/i18n";
|
|
26
27
|
import { createMetricsForFactory } from "@cosmicdrift/kumiko-framework/observability";
|
|
27
28
|
import { TestUsers } from "@cosmicdrift/kumiko-framework/seeding";
|
|
28
29
|
import { resolveAndInjectPageHead, } from "@cosmicdrift/kumiko-headless/apex";
|
|
29
30
|
import { startDevJobRunners } from "@cosmicdrift/kumiko-server-runtime/boot/job-run-logger";
|
|
30
31
|
import { buildBunServeOptions, resolveDerivedMaxRequestBodySize, } from "@cosmicdrift/kumiko-server-runtime/bun-serve-options";
|
|
31
|
-
import { makeDispatchSystemWrite, } from "@cosmicdrift/kumiko-server-runtime/extra-routes-deps";
|
|
32
|
+
import { makeDispatchSystemQuery, makeDispatchSystemWrite, } from "@cosmicdrift/kumiko-server-runtime/extra-routes-deps";
|
|
32
33
|
import { RENDERER_WEB_FONT_FILE_PATTERN, RENDERER_WEB_FONTS_URL_PREFIX, resolveRendererWebFontsDir, } from "@cosmicdrift/kumiko-server-runtime/renderer-web-fonts";
|
|
33
34
|
import { buildRequestBoundSystemQuery } from "@cosmicdrift/kumiko-server-runtime/request-bound-system-query";
|
|
34
35
|
import { canResolveTailwindStylesheet, resolveTailwindCli, } from "@cosmicdrift/kumiko-server-runtime/resolve-tailwind-cli";
|
|
@@ -633,6 +634,7 @@ export async function createKumikoServer(options) {
|
|
|
633
634
|
registry: stack.registry,
|
|
634
635
|
metricsFor: createMetricsForFactory(stack.observability.meter),
|
|
635
636
|
dispatchSystemWrite: makeDispatchSystemWrite(stack.dispatcher),
|
|
637
|
+
dispatchSystemQuery: makeDispatchSystemQuery(stack.dispatcher),
|
|
636
638
|
});
|
|
637
639
|
}
|
|
638
640
|
// Hook für Caller-spezifische Tables + Seed. Läuft nach den Entity-
|
|
@@ -755,6 +757,10 @@ export async function createKumikoServer(options) {
|
|
|
755
757
|
html = await resolveAndInjectPageHead(html, options.resolvePageHead, {
|
|
756
758
|
path: url.pathname,
|
|
757
759
|
host,
|
|
760
|
+
preferredLocales: rankPreferredLocales({
|
|
761
|
+
queryLang: url.searchParams.get("lang"),
|
|
762
|
+
acceptLanguage: req.headers.get("accept-language"),
|
|
763
|
+
}),
|
|
758
764
|
systemQuery: buildDevSystemQuery(req, socketAddress),
|
|
759
765
|
requestSignal: req.signal,
|
|
760
766
|
});
|
package/dist/scaffold-app.js
CHANGED
|
@@ -211,7 +211,9 @@ function compareImportKeys(a, b) {
|
|
|
211
211
|
function isRelativeSpecifier(specifier) {
|
|
212
212
|
return specifier.startsWith("./") || specifier.startsWith("../");
|
|
213
213
|
}
|
|
214
|
-
function renderImportLine({ specifier, names, typeOnly }) {
|
|
214
|
+
function renderImportLine({ specifier, names, typeOnly, defaultName }) {
|
|
215
|
+
if (defaultName !== undefined)
|
|
216
|
+
return `import ${defaultName} from "${specifier}";`;
|
|
215
217
|
const sorted = [...names].sort(compareImportKeys);
|
|
216
218
|
const keyword = typeOnly === true ? "import type" : "import";
|
|
217
219
|
const oneLine = `${keyword} { ${sorted.join(", ")} } from "${specifier}";`;
|
|
@@ -355,6 +357,7 @@ function renderMain(appName) {
|
|
|
355
357
|
names: ["requireKmsWiring", "resolveKmsWiring"],
|
|
356
358
|
},
|
|
357
359
|
{ specifier: "../src/run-config", names: ["APP_FEATURES"] },
|
|
360
|
+
{ specifier: "../kumiko.config", names: [], defaultName: "appConfig" },
|
|
358
361
|
{ specifier: "./env-schema", names: ["envSchema"] },
|
|
359
362
|
]);
|
|
360
363
|
const authBlock = renderAdminAuthBlock(appName, [
|
|
@@ -385,6 +388,7 @@ const kmsWiring =
|
|
|
385
388
|
|
|
386
389
|
await runProdApp({
|
|
387
390
|
features: APP_FEATURES,
|
|
391
|
+
...(appConfig.validateBootOptions && { validateBootOptions: appConfig.validateBootOptions }),
|
|
388
392
|
envSchema,
|
|
389
393
|
staticDir: "./dist",
|
|
390
394
|
...kmsWiring,
|
|
@@ -398,6 +402,7 @@ function renderServer(appName) {
|
|
|
398
402
|
{ specifier: "@cosmicdrift/kumiko-dev-server", names: ["runDevApp"] },
|
|
399
403
|
{ specifier: "@cosmicdrift/kumiko-framework/engine", names: ["TenantId"], typeOnly: true },
|
|
400
404
|
{ specifier: "../src/run-config", names: ["APP_FEATURES"] },
|
|
405
|
+
{ specifier: "../kumiko.config", names: [], defaultName: "appConfig" },
|
|
401
406
|
{ specifier: "../src/seed", names: ["seedDemoTasks"] },
|
|
402
407
|
]);
|
|
403
408
|
// Dev-only emailVerified skips the login verify gate for the seeded admin;
|
|
@@ -420,6 +425,7 @@ const DEFAULT_TENANT_ID = "${tenantId}" as TenantId;
|
|
|
420
425
|
|
|
421
426
|
await runDevApp({
|
|
422
427
|
features: APP_FEATURES,
|
|
428
|
+
...(appConfig.validateBootOptions && { validateBootOptions: appConfig.validateBootOptions }),
|
|
423
429
|
welcomeBanner: true,
|
|
424
430
|
clientEntry: "./src/client.tsx",
|
|
425
431
|
seeds: [seedDemoTasks],
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type WriteServerLockfileOptions = {
|
|
2
|
+
/** App directory: the search for a seed bun.lock starts here and walks up. */
|
|
3
|
+
readonly cwd: string;
|
|
4
|
+
/** Server bundle directory holding the generated package.json. */
|
|
5
|
+
readonly outDir: string;
|
|
6
|
+
};
|
|
7
|
+
export type ServerLockfileResult = {
|
|
8
|
+
readonly lockfile: string;
|
|
9
|
+
} & ({
|
|
10
|
+
readonly mode: "seeded";
|
|
11
|
+
readonly seedLockfile: string;
|
|
12
|
+
} | {
|
|
13
|
+
readonly mode: "resolved";
|
|
14
|
+
});
|
|
15
|
+
export declare function writeServerLockfile(options: WriteServerLockfileOptions): Promise<ServerLockfileResult>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Pins dist-server/ to the dependency versions the app was tested with. The runtime image
|
|
2
|
+
// installs from dist-server/package.json, whose ranges alone resolve to whatever is newest
|
|
3
|
+
// on the registry at image-build time. Seeding the app's bun.lock keeps already-locked
|
|
4
|
+
// packages at their locked versions; packages the app lock does not carry resolve fresh.
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
import { copyFile } from "node:fs/promises";
|
|
7
|
+
import { dirname, join, resolve } from "node:path";
|
|
8
|
+
function findSeedLockfile(startDir) {
|
|
9
|
+
let dir = resolve(startDir);
|
|
10
|
+
for (;;) {
|
|
11
|
+
const candidate = join(dir, "bun.lock");
|
|
12
|
+
if (existsSync(candidate))
|
|
13
|
+
return candidate;
|
|
14
|
+
const parent = dirname(dir);
|
|
15
|
+
if (parent === dir)
|
|
16
|
+
return undefined;
|
|
17
|
+
dir = parent;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export async function writeServerLockfile(options) {
|
|
21
|
+
const outDir = resolve(options.outDir);
|
|
22
|
+
const lockfile = join(outDir, "bun.lock");
|
|
23
|
+
const seedLockfile = findSeedLockfile(options.cwd);
|
|
24
|
+
if (seedLockfile !== undefined)
|
|
25
|
+
await copyFile(seedLockfile, lockfile);
|
|
26
|
+
// process.execPath: the lock format has to match the Bun that built the bundle.
|
|
27
|
+
const install = Bun.spawn({
|
|
28
|
+
cmd: [process.execPath, "install", "--lockfile-only"],
|
|
29
|
+
cwd: outDir,
|
|
30
|
+
stdout: "pipe",
|
|
31
|
+
stderr: "pipe",
|
|
32
|
+
});
|
|
33
|
+
const [exitCode, stdout, stderr] = await Promise.all([
|
|
34
|
+
install.exited,
|
|
35
|
+
new Response(install.stdout).text(),
|
|
36
|
+
new Response(install.stderr).text(),
|
|
37
|
+
]);
|
|
38
|
+
if (exitCode !== 0) {
|
|
39
|
+
throw new Error(`bun install --lockfile-only failed in ${outDir} (exit ${exitCode}); shipping dist-server without a lockfile would leave runtime versions unpinned.\n${stderr.trim() || stdout.trim()}`);
|
|
40
|
+
}
|
|
41
|
+
if (!existsSync(lockfile)) {
|
|
42
|
+
throw new Error(`bun install --lockfile-only did not write ${lockfile}`);
|
|
43
|
+
}
|
|
44
|
+
return seedLockfile === undefined
|
|
45
|
+
? { mode: "resolved", lockfile }
|
|
46
|
+
: { mode: "seeded", seedLockfile, lockfile };
|
|
47
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-dev-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.373.0",
|
|
4
4
|
"description": "Dev-tooling for Kumiko apps: local dev-server bootstrap (runDevApp), scaffolding. Its compose-stacks/env-schema subpaths are consumed at prod boot too — see @cosmicdrift/kumiko-server-runtime for the prod runner itself.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -72,14 +72,14 @@
|
|
|
72
72
|
"kumiko-upgrade": "./bin/kumiko-upgrade.ts"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@cosmicdrift/kumiko-bundled-features": "0.
|
|
76
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
77
|
-
"@cosmicdrift/kumiko-headless": "0.
|
|
78
|
-
"@cosmicdrift/kumiko-renderer-mail-html": "0.
|
|
79
|
-
"@cosmicdrift/kumiko-renderer-web": "0.
|
|
80
|
-
"@cosmicdrift/kumiko-server-runtime": "0.
|
|
81
|
-
"@cosmicdrift/kumiko-testing": "0.
|
|
82
|
-
"@cosmicdrift/kumiko-types": "0.
|
|
75
|
+
"@cosmicdrift/kumiko-bundled-features": "0.373.0",
|
|
76
|
+
"@cosmicdrift/kumiko-framework": "0.373.0",
|
|
77
|
+
"@cosmicdrift/kumiko-headless": "0.373.0",
|
|
78
|
+
"@cosmicdrift/kumiko-renderer-mail-html": "0.373.0",
|
|
79
|
+
"@cosmicdrift/kumiko-renderer-web": "0.373.0",
|
|
80
|
+
"@cosmicdrift/kumiko-server-runtime": "0.373.0",
|
|
81
|
+
"@cosmicdrift/kumiko-testing": "0.373.0",
|
|
82
|
+
"@cosmicdrift/kumiko-types": "0.373.0",
|
|
83
83
|
"bun-types": "^1.3.13",
|
|
84
84
|
"ts-morph": "^28.0.0",
|
|
85
85
|
"zod": "^4.4.3"
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.373.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "kumiko-build pins dist-server dependencies with a bun.lock seeded from the app's bun.lock",
|
|
6
|
+
"detail": "The lock is written by writeServerLockfile after the bundle build and needs network or a warm Bun cache. The scaffolded Dockerfile runs bun install --production --frozen-lockfile in the runtime stage.",
|
|
7
|
+
"migration": "Apps with their own Dockerfile should run `bun install --production --frozen-lockfile` in dist-server instead of `bun install --production`."
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"version": "0.372.0",
|
|
11
|
+
"type": "improvement",
|
|
12
|
+
"title": "kumiko-build --entry and buildServerBundle extraEntries bundle additional server entry points as dist-server/<basename>.js",
|
|
13
|
+
"detail": "Opt-in per file, not every bin/ helper module; the output shares the chunk files with server.js. Unknown kumiko-build options now fail the build."
|
|
14
|
+
},
|
|
2
15
|
{
|
|
3
16
|
"version": "0.371.0",
|
|
4
17
|
"type": "improvement",
|
|
@@ -60,10 +60,11 @@ WORKDIR /app
|
|
|
60
60
|
|
|
61
61
|
RUN addgroup -S app && adduser -S app -G app
|
|
62
62
|
|
|
63
|
-
# Server bundle + its minimal package.json
|
|
64
|
-
#
|
|
63
|
+
# Server bundle + its minimal package.json and the bun.lock kumiko-build wrote
|
|
64
|
+
# next to it. `--frozen-lockfile` installs exactly the locked externals and
|
|
65
|
+
# fails on drift instead of resolving newer versions.
|
|
65
66
|
COPY --from=build --chown=app:app /app/dist-server ./
|
|
66
|
-
RUN bun install --production
|
|
67
|
+
RUN bun install --production --frozen-lockfile
|
|
67
68
|
|
|
68
69
|
# Client build (static assets) — served by runProdApp.staticDir.
|
|
69
70
|
COPY --from=build --chown=app:app /app/dist ./dist
|
|
@@ -9,4 +9,7 @@ import appConfig from "../kumiko.config";
|
|
|
9
9
|
|
|
10
10
|
export const FEATURES: readonly FeatureDefinition[] = allAppFeatures(appConfig);
|
|
11
11
|
|
|
12
|
+
// `kumiko check` validates the boot with the options kumiko.config.ts declares.
|
|
13
|
+
export const VALIDATE_BOOT_OPTIONS = appConfig.validateBootOptions;
|
|
14
|
+
|
|
12
15
|
export const ENTITY_METAS: readonly EntityTableMeta[] = collectTableMetas(FEATURES);
|