@cosmicdrift/kumiko-dev-server 0.337.1 → 0.339.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.
@@ -48,7 +48,12 @@ export type DevHostDispatchResult = {
48
48
  readonly to: string;
49
49
  readonly status?: 301 | 302;
50
50
  } | {
51
+ /** Plain 404 by default. `file` serves a static page with status 404
52
+ * (like `static-html`), `entryName` serves that bundle's shell with 404;
53
+ * `file` wins when both are set. */
51
54
  readonly kind: "not-found";
55
+ readonly entryName?: string;
56
+ readonly file?: string;
52
57
  };
53
58
  /** Picks an entry by inspecting the incoming request. Wird von
54
59
  * Multi-Entry-Apps gesetzt; im Single-Entry-Mode irrelevant. */
@@ -723,7 +723,7 @@ export async function createKumikoServer(options) {
723
723
  // resolvePageHead goes through the same headless resolveAndInjectPageHead
724
724
  // that runProdApp uses, so dev and e2e exercise the one timeout and
725
725
  // fallback path instead of a second copy of it (#3026).
726
- const htmlResponse = async (entryName, req, socketAddress) => {
726
+ const htmlResponse = async (entryName, req, socketAddress, status = 200) => {
727
727
  const template = htmlTemplates.get(entryName) ?? defaultTemplate;
728
728
  const headers = new Headers();
729
729
  headers.set("Content-Type", "text/html; charset=utf-8");
@@ -745,7 +745,23 @@ export async function createKumikoServer(options) {
745
745
  systemQuery: buildDevSystemQuery(req, socketAddress),
746
746
  });
747
747
  }
748
- return new Response(html, { headers });
748
+ return new Response(html, { status, headers });
749
+ };
750
+ // Raw file serve, no bundle injection — mirrors prod's `{ kind: "html", file }`.
751
+ const staticHtmlResponse = async (file, status) => {
752
+ let content;
753
+ try {
754
+ content = await readFile(file, "utf-8");
755
+ }
756
+ catch (error) {
757
+ if (error.code !== "ENOENT")
758
+ throw error;
759
+ return new Response(`hostDispatch: file not found: ${file}`, { status: 500 });
760
+ }
761
+ return new Response(content, {
762
+ status,
763
+ headers: { "Content-Type": "text/html; charset=utf-8" },
764
+ });
749
765
  };
750
766
  // --- Fetch handler (runtime-neutral) ---
751
767
  // Bundle-Pfad-Lookup: für jede Entry serven wir
@@ -898,15 +914,15 @@ export async function createKumikoServer(options) {
898
914
  });
899
915
  }
900
916
  if (dispatch.kind === "not-found") {
917
+ if (dispatch.file !== undefined)
918
+ return staticHtmlResponse(dispatch.file, 404);
919
+ if (dispatch.entryName !== undefined) {
920
+ return htmlResponse(dispatch.entryName, req, socketAddress, 404);
921
+ }
901
922
  return new Response("Not Found", { status: 404 });
902
923
  }
903
924
  if (dispatch.kind === "static-html") {
904
- // Raw file serve, no bundle injection — mirrors prod's
905
- // `{ kind: "html", file: ... }` static-html branch.
906
- const file = await readFile(dispatch.file, "utf-8");
907
- return new Response(file, {
908
- headers: { "Content-Type": "text/html; charset=utf-8" },
909
- });
925
+ return staticHtmlResponse(dispatch.file, 200);
910
926
  }
911
927
  return htmlResponse(dispatch.entryName, req, socketAddress);
912
928
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-dev-server",
3
- "version": "0.337.1",
3
+ "version": "0.339.0",
4
4
  "description": "Dev-tooling for Kumiko apps: local dev-server bootstrap (runDevApp), scaffolding, codegen. 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>",
@@ -64,11 +64,11 @@
64
64
  "kumiko-upgrade": "./bin/kumiko-upgrade.ts"
65
65
  },
66
66
  "dependencies": {
67
- "@cosmicdrift/kumiko-bundled-features": "0.337.1",
68
- "@cosmicdrift/kumiko-framework": "0.337.1",
69
- "@cosmicdrift/kumiko-headless": "0.337.1",
70
- "@cosmicdrift/kumiko-renderer-web": "0.337.1",
71
- "@cosmicdrift/kumiko-server-runtime": "0.337.1",
67
+ "@cosmicdrift/kumiko-bundled-features": "0.339.0",
68
+ "@cosmicdrift/kumiko-framework": "0.339.0",
69
+ "@cosmicdrift/kumiko-headless": "0.339.0",
70
+ "@cosmicdrift/kumiko-renderer-web": "0.339.0",
71
+ "@cosmicdrift/kumiko-server-runtime": "0.339.0",
72
72
  "bun-types": "^1.3.13",
73
73
  "ts-morph": "^28.0.0",
74
74
  "zod": "^4.4.3"
package/src/changes.json CHANGED
@@ -1,4 +1,23 @@
1
1
  [
2
+ {
3
+ "version": "0.338.0",
4
+ "type": "fix",
5
+ "title": "Scaffolded migrate step percent-encodes DB_PASSWORD",
6
+ "detail": "dev-server: the scaffolded `deploy/migrate-step.sh` percent-encodes `DB_PASSWORD` in `DATABASE_URL`, so passwords with `@`, `:`, `/`, `#`, `%` or non-ASCII characters no longer break the URL. Re-run `kumiko init-deploy` to pick it up; apps with a hand-written `urlencode` should take the new version, which also encodes bytes >= 0x80 correctly."
7
+ },
8
+ {
9
+ "version": "0.338.0",
10
+ "type": "improvement",
11
+ "title": "dev hostDispatch not-found can serve a custom 404 page",
12
+ "detail": "`DevHostDispatchResult` `{ kind: \"not-found\" }` accepts `file` (served like `static-html`) or `entryName` (that entry's shell), both with status 404. A missing file answers 500 with `hostDispatch: file not found`. Without fields the plain \"Not Found\" 404 stays.",
13
+ "migration": "No code change needed."
14
+ },
15
+ {
16
+ "version": "0.338.0",
17
+ "type": "improvement",
18
+ "title": "New package @cosmicdrift/kumiko-samples with the corpus sources",
19
+ "detail": "New package with the sample recipes, sample apps and bundled-features sources\n`@cosmicdrift/kumiko-samples` ships the sample recipes, sample apps and bundled-features sources in the repository layout. Tools such as the few-shot corpus build can run against a pinned version of it and get the same entry ids and source paths as in the monorepo."
20
+ },
2
21
  {
3
22
  "version": "0.337.1",
4
23
  "type": "improvement",
@@ -2,14 +2,14 @@
2
2
  #
3
3
  # Production-Image for {{appName}}.
4
4
  #
5
- # Server-Bundle variant — the runtime image doesn't know about the
6
- # monorepo any more, only:
5
+ # Server-Bundle variant — the runtime image doesn't carry the source tree
6
+ # or its node_modules any more, only:
7
7
  #
8
8
  # dist/ ← Client build (kumiko-build)
9
9
  # dist-server/ ← Server bundle (bun build) + minimal package.json
10
10
  # kumiko/migrations/ ← Checked-in SQL migrations applied by `kumiko schema apply`
11
11
  #
12
- # Build context: repository root (monorepo is used in the build stage).
12
+ # Build context: repository root (the full source tree is only used in the build stage).
13
13
 
14
14
  ARG BUN_VERSION=1.4.0
15
15
  {{#hasPrivateGhPackages}}
@@ -49,6 +49,21 @@ set -a
49
49
  . ./.env
50
50
  set +a
51
51
 
52
+ # Percent-encode so '@', ':', '/', '#', '%' in the password cannot change how
53
+ # the URL is parsed. LC_ALL=C makes the loop walk bytes, not characters;
54
+ # bash sign-extends bytes >= 0x80 in "'$c", hence the & 255.
55
+ urlencode() {
56
+ local LC_ALL=C s="$1" out="" c i byte
57
+ for ((i = 0; i < ${#s}; i++)); do
58
+ c="${s:i:1}"
59
+ case "$c" in
60
+ [A-Za-z0-9._~-]) out+="$c" ;;
61
+ *) printf -v byte '%d' "'$c"; printf -v c '%%%02X' $((byte & 255)); out+="$c" ;;
62
+ esac
63
+ done
64
+ printf '%s' "$out"
65
+ }
66
+
52
67
  # DATABASE_URL assumes: db user = appName, db name = appName, host "db"
53
68
  # (compose-service-name), port 5432. Adjust to your stack if different.
54
69
  {{#customDbUser}}
@@ -60,7 +75,7 @@ set +a
60
75
  # NAME (`-e DATABASE_URL`, no value) so the expanded password never appears
61
76
  # in `docker run`'s argv — otherwise it would be visible in `ps auxe` for
62
77
  # the duration of the migrate run.
63
- export DATABASE_URL="postgresql://{{dbUser}}:${DB_PASSWORD}@db:5432/{{appName}}"
78
+ export DATABASE_URL="postgresql://{{dbUser}}:$(urlencode "$DB_PASSWORD")@db:5432/{{appName}}"
64
79
  docker run --rm \
65
80
  --network "$STACK_NETWORK" \
66
81
  -e DATABASE_URL \