create-kumiko-app 0.4.228 → 0.4.230

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-kumiko-app",
3
- "version": "0.4.228",
3
+ "version": "0.4.230",
4
4
  "description": "`bun create kumiko-app <name>` — scaffold a new Kumiko app with an interactive feature picker.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -27,9 +27,9 @@
27
27
  }
28
28
  },
29
29
  "dependencies": {
30
- "@cosmicdrift/kumiko-dev-server": "0.213.0",
31
- "@cosmicdrift/kumiko-framework": "0.213.0",
32
- "@cosmicdrift/kumiko-server-runtime": "0.213.0",
30
+ "@cosmicdrift/kumiko-dev-server": "0.215.0",
31
+ "@cosmicdrift/kumiko-framework": "0.215.0",
32
+ "@cosmicdrift/kumiko-server-runtime": "0.215.0",
33
33
  "@inquirer/core": "^10.0.0",
34
34
  "@inquirer/prompts": "^7.4.0"
35
35
  },
@@ -7,12 +7,17 @@
7
7
  // only actually booting the resolved set through validateBoot can.
8
8
 
9
9
  import { describe, expect, test } from "bun:test";
10
+ import { frameworkCoreEnvSchema } from "@cosmicdrift/kumiko-dev-server";
11
+ import { resolveKmsWiring } from "@cosmicdrift/kumiko-framework/crypto";
10
12
  import {
11
13
  createRegistry,
12
14
  type FeatureDefinition,
15
+ type TenantId,
13
16
  validateBoot as validateBootRaw,
14
17
  } from "@cosmicdrift/kumiko-framework/engine";
18
+ import { composeEnvSchema } from "@cosmicdrift/kumiko-framework/env";
15
19
  import { withBootValidatorFixture } from "@cosmicdrift/kumiko-framework/testing";
20
+ import { runProdApp } from "@cosmicdrift/kumiko-server-runtime";
16
21
  import { composeFeatures } from "@cosmicdrift/kumiko-server-runtime/compose-features";
17
22
  import { resolveDeps } from "../dep-resolver";
18
23
  import { FEATURE_CONSTRUCTORS } from "../feature-constructors";
@@ -61,3 +66,94 @@ describe("--yes resolved set boots (issue-1174 regression)", () => {
61
66
  expect(() => createRegistry(composed)).not.toThrow();
62
67
  });
63
68
  });
69
+
70
+ // kumiko-framework#2330: validateBoot/createRegistry above never reach
71
+ // runProdApp's PII boot gate (assertPiiBootInvariants) — it's not part of
72
+ // validateBoot, it runs a step later. The --yes set mounts PII-annotated
73
+ // entities (user, tenant-invitation, fileRef via user-data-rights), so the
74
+ // generated bin/main.ts must wire kms/allowPlaintextPii or the first
75
+ // `bun run boot` throws BOOT ABORTED. This drives the SAME resolved feature
76
+ // set through the actual runProdApp (KUMIKO_DRY_RUN_ENV=boot — no DB/Redis
77
+ // connect) to pin both halves of the fix.
78
+ describe("--yes resolved set through runProdApp's PII boot gate (issue-2330 regression)", () => {
79
+ const manifest = loadManifest();
80
+ const recommended = buildChoices(manifest)
81
+ .filter((c) => c.recommended)
82
+ .map((c) => c.name);
83
+ const resolved = resolveDeps(recommended, manifest);
84
+
85
+ const auth = {
86
+ admin: {
87
+ email: "admin@test.local",
88
+ password: "change-me-on-first-deploy",
89
+ displayName: "Admin",
90
+ memberships: [
91
+ {
92
+ tenantId: "93238e36-e2e5-4e2e-8be4-00004739a98c" as TenantId,
93
+ tenantKey: "test",
94
+ tenantName: "test",
95
+ roles: ["TenantAdmin" as const],
96
+ },
97
+ ],
98
+ },
99
+ };
100
+
101
+ async function bootWithWiring(wiring: Record<string, unknown>) {
102
+ const instances = await instantiateResolved(resolved.featureNames);
103
+ const bootFeatures = composeFeatures(instances, { includeBundled: true });
104
+ const envSchema = composeEnvSchema({ core: frameworkCoreEnvSchema, features: bootFeatures });
105
+ return runProdApp({
106
+ features: instances,
107
+ envSchema,
108
+ autoListen: false,
109
+ migrations: false,
110
+ auth,
111
+ envSource: {
112
+ KUMIKO_DRY_RUN_ENV: "boot",
113
+ JWT_SECRET: "a".repeat(32),
114
+ KUMIKO_SECRETS_MASTER_KEY_V1: Buffer.alloc(32, 7).toString("base64"),
115
+ DATABASE_URL: "postgres://dummy:dummy@127.0.0.1:1/dummy",
116
+ REDIS_URL: "redis://127.0.0.1:1",
117
+ },
118
+ ...wiring,
119
+ });
120
+ }
121
+
122
+ test("without kms/allowPlaintextPii wiring, boot aborts (pins the bug this issue reports)", async () => {
123
+ await expect(bootWithWiring({})).rejects.toThrow(
124
+ /BOOT ABORTED.*user.*tenant-invitation.*fileRef/s,
125
+ );
126
+ });
127
+
128
+ test("with resolveKmsWiring's fallback (what the generated bin/main.ts now does), boot succeeds", async () => {
129
+ // Empty object, not process.env: a dev machine with the real trio set
130
+ // would otherwise flip this into the ActiveKmsWiring branch and the test
131
+ // would stop exercising the plaintext fallback it's meant to pin.
132
+ const kmsWiring = resolveKmsWiring(
133
+ {},
134
+ {
135
+ logPrefix: "[test]",
136
+ plaintextReason: "no PLATFORM_KEK / SUBJECT_KEYS_DATABASE_URL / KUMIKO_BLIND_INDEX_KEY set",
137
+ },
138
+ );
139
+ expect(kmsWiring).toHaveProperty("allowPlaintextPii");
140
+ const handle = await bootWithWiring(kmsWiring);
141
+ expect(handle).toBeDefined();
142
+ });
143
+
144
+ test("generated .env.example's unset trio (empty string, not absent) still resolves to the plaintext fallback", async () => {
145
+ // dotenv turns `PLATFORM_KEK=` into process.env.PLATFORM_KEK === "" — the
146
+ // actual value a user gets from `cp .env.example .env` without filling it
147
+ // in. resolveKmsWiring reads raw env (not the zod-validated envSchema),
148
+ // and Boolean("") === Boolean(undefined), so this must behave identically
149
+ // to the {} case above.
150
+ const kmsWiring = resolveKmsWiring({
151
+ PLATFORM_KEK: "",
152
+ SUBJECT_KEYS_DATABASE_URL: "",
153
+ KUMIKO_BLIND_INDEX_KEY: "",
154
+ });
155
+ expect(kmsWiring).toHaveProperty("allowPlaintextPii");
156
+ const handle = await bootWithWiring(kmsWiring);
157
+ expect(handle).toBeDefined();
158
+ });
159
+ });