@cosmicdrift/kumiko-dev-server 0.329.0 → 0.330.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.
Files changed (125) hide show
  1. package/README.md +17 -0
  2. package/bin/kumiko-build.ts +3 -2
  3. package/bin/kumiko-dev.ts +1 -1
  4. package/bin/kumiko-init-deploy.ts +1 -1
  5. package/bin/kumiko-schema-check.ts +4 -1
  6. package/dist/build-server-bundle.d.ts +31 -0
  7. package/dist/build-server-bundle.js +282 -0
  8. package/dist/build.d.ts +2 -0
  9. package/dist/build.js +5 -0
  10. package/dist/cli.d.ts +5 -0
  11. package/dist/cli.js +9 -0
  12. package/dist/codegen/index.d.ts +4 -0
  13. package/dist/codegen/index.js +4 -0
  14. package/dist/codegen/render.d.ts +55 -0
  15. package/dist/codegen/render.js +273 -0
  16. package/dist/codegen/run-codegen.d.ts +20 -0
  17. package/dist/codegen/run-codegen.js +219 -0
  18. package/dist/codegen/scan-events.d.ts +76 -0
  19. package/dist/codegen/scan-events.js +447 -0
  20. package/dist/codegen/watch.d.ts +33 -0
  21. package/dist/codegen/watch.js +112 -0
  22. package/dist/compose-stacks.d.ts +60 -0
  23. package/dist/compose-stacks.js +127 -0
  24. package/dist/crash-tracker.d.ts +17 -0
  25. package/dist/crash-tracker.js +37 -0
  26. package/dist/create-kumiko-server.d.ts +195 -0
  27. package/dist/create-kumiko-server.js +1040 -0
  28. package/dist/env-schema.d.ts +15 -0
  29. package/dist/env-schema.js +60 -0
  30. package/dist/few-shot-corpus.d.ts +72 -0
  31. package/dist/few-shot-corpus.js +262 -0
  32. package/dist/framework-version.d.ts +1 -0
  33. package/dist/framework-version.js +31 -0
  34. package/dist/index.d.ts +22 -0
  35. package/dist/index.js +33 -0
  36. package/dist/init-deploy-cli.d.ts +10 -0
  37. package/dist/init-deploy-cli.js +111 -0
  38. package/dist/kebab.d.ts +1 -0
  39. package/{src/kebab.ts → dist/kebab.js} +2 -3
  40. package/dist/run-dev-app.d.ts +174 -0
  41. package/dist/run-dev-app.js +403 -0
  42. package/dist/scaffold-app-feature.d.ts +16 -0
  43. package/dist/scaffold-app-feature.js +240 -0
  44. package/dist/scaffold-app.d.ts +45 -0
  45. package/dist/scaffold-app.js +852 -0
  46. package/dist/scaffold-demo-tasks.d.ts +8 -0
  47. package/{src/scaffold-demo-tasks.ts → dist/scaffold-demo-tasks.js} +69 -98
  48. package/dist/scaffold-deploy.d.ts +92 -0
  49. package/dist/scaffold-deploy.js +254 -0
  50. package/dist/scaffold-feature.d.ts +26 -0
  51. package/dist/scaffold-feature.js +163 -0
  52. package/dist/schema-apply.d.ts +11 -0
  53. package/dist/schema-apply.js +95 -0
  54. package/dist/schema-check-core.d.ts +2 -0
  55. package/{src/schema-check-core.ts → dist/schema-check-core.js} +8 -10
  56. package/dist/schema-tables-auth-mode.d.ts +2 -0
  57. package/{src/schema-tables-auth-mode.ts → dist/schema-tables-auth-mode.js} +2 -10
  58. package/dist/schema-tables-minimal.d.ts +2 -0
  59. package/{src/schema-tables-minimal.ts → dist/schema-tables-minimal.js} +2 -11
  60. package/dist/setup-test-stack-from-features.d.ts +11 -0
  61. package/dist/setup-test-stack-from-features.js +42 -0
  62. package/dist/welcome-banner.d.ts +15 -0
  63. package/dist/welcome-banner.js +27 -0
  64. package/package.json +71 -24
  65. package/src/changes.json +6 -0
  66. package/src/__tests__/build-prod-bundle.integration.test.ts +0 -282
  67. package/src/__tests__/build-server-bundle.test.ts +0 -228
  68. package/src/__tests__/classify-change.test.ts +0 -87
  69. package/src/__tests__/compose-stacks.test.ts +0 -273
  70. package/src/__tests__/config-resolver-default.integration.test.ts +0 -165
  71. package/src/__tests__/crash-tracker.test.ts +0 -89
  72. package/src/__tests__/create-kumiko-server-errors.test.ts +0 -54
  73. package/src/__tests__/create-kumiko-server-options.test.ts +0 -33
  74. package/src/__tests__/create-kumiko-server.integration.test.ts +0 -1007
  75. package/src/__tests__/discover-format.test.ts +0 -32
  76. package/src/__tests__/env-schema.integration.test.ts +0 -248
  77. package/src/__tests__/env-schema.test.ts +0 -132
  78. package/src/__tests__/few-shot-corpus.test.ts +0 -316
  79. package/src/__tests__/framework-version.test.ts +0 -45
  80. package/src/__tests__/init-deploy-cli.test.ts +0 -113
  81. package/src/__tests__/kumiko-schema-check.test.ts +0 -56
  82. package/src/__tests__/merge-extra-context.test.ts +0 -67
  83. package/src/__tests__/resolve-stylesheet.test.ts +0 -132
  84. package/src/__tests__/run-dev-app-boot-validation.test.ts +0 -38
  85. package/src/__tests__/run-dev-app.integration.test.ts +0 -290
  86. package/src/__tests__/runtime-classification.test.ts +0 -26
  87. package/src/__tests__/saas-identity-wire.integration.test.ts +0 -427
  88. package/src/__tests__/scaffold-app-feature.test.ts +0 -216
  89. package/src/__tests__/scaffold-app.test.ts +0 -630
  90. package/src/__tests__/scaffold-demo-tasks.test.ts +0 -47
  91. package/src/__tests__/scaffold-deploy.test.ts +0 -464
  92. package/src/__tests__/scaffold-dev-cycle.integration.test.ts +0 -109
  93. package/src/__tests__/scaffold-feature.test.ts +0 -143
  94. package/src/__tests__/schema-apply.integration.test.ts +0 -191
  95. package/src/__tests__/setup-test-stack-from-features.integration.test.ts +0 -30
  96. package/src/__tests__/setup-test-stack-job-run-logger.integration.test.ts +0 -72
  97. package/src/__tests__/walkthrough.integration.test.ts +0 -140
  98. package/src/__tests__/welcome-banner.test.ts +0 -42
  99. package/src/build-server-bundle.ts +0 -346
  100. package/src/build.ts +0 -28
  101. package/src/codegen/__tests__/render-codegen.test.ts +0 -100
  102. package/src/codegen/__tests__/run-codegen.test.ts +0 -665
  103. package/src/codegen/__tests__/strict-mode-diagnostics.test.ts +0 -347
  104. package/src/codegen/__tests__/watch.test.ts +0 -222
  105. package/src/codegen/index.ts +0 -19
  106. package/src/codegen/render.ts +0 -305
  107. package/src/codegen/run-codegen.ts +0 -278
  108. package/src/codegen/scan-events.ts +0 -565
  109. package/src/codegen/watch.ts +0 -140
  110. package/src/compose-stacks.ts +0 -193
  111. package/src/crash-tracker.ts +0 -56
  112. package/src/create-kumiko-server.ts +0 -1397
  113. package/src/env-schema.ts +0 -72
  114. package/src/few-shot-corpus.ts +0 -370
  115. package/src/framework-version.ts +0 -39
  116. package/src/index.ts +0 -105
  117. package/src/init-deploy-cli.ts +0 -148
  118. package/src/run-dev-app.ts +0 -694
  119. package/src/scaffold-app-feature.ts +0 -283
  120. package/src/scaffold-app.ts +0 -1033
  121. package/src/scaffold-deploy.ts +0 -393
  122. package/src/scaffold-feature.ts +0 -226
  123. package/src/schema-apply.ts +0 -123
  124. package/src/setup-test-stack-from-features.ts +0 -80
  125. package/src/welcome-banner.ts +0 -43
@@ -1,282 +0,0 @@
1
- // Integration-Tests für buildProdBundle — End-to-End-Pfad gegen das
2
- // echte Filesystem.
3
- //
4
- // Zwei Szenarien:
5
- //
6
- // 1. Vanilla-Pipeline (kein client.tsx, nur public/ + html-template):
7
- // läuft unter Node-Vitest direkt — kein Bun.build, keine
8
- // Subprocess-Akrobatik. Beweist Discovery + html-rendering +
9
- // public-copy + manifest-format.
10
- //
11
- // 2. Volle Pipeline (client.tsx + Bun.build + Hash + Manifest):
12
- // braucht Bun. Spawnen kumiko-build als subprocess via PATH.
13
- // Skipped wenn `bun` nicht erreichbar — selten, aber sauber.
14
-
15
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
16
- import { execFileSync } from "node:child_process";
17
- import { existsSync } from "node:fs";
18
- import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
19
- import { tmpdir } from "node:os";
20
- import { dirname, join, resolve } from "node:path";
21
- import { fileURLToPath } from "node:url";
22
- import { buildProdBundle } from "@cosmicdrift/kumiko-server-runtime/build-prod-bundle";
23
-
24
- const __filename = fileURLToPath(import.meta.url);
25
- const __dirname = dirname(__filename);
26
- const KUMIKO_BUILD_BIN = resolve(__dirname, "../../bin/kumiko-build.ts");
27
-
28
- // Each of these tests spawns kumiko-build, which runs the real Bun.build +
29
- // tailwind pipeline; bun's 5s default kills it mid-build on a loaded CI runner.
30
- const FULL_PIPELINE_TIMEOUT_MS = 60_000;
31
-
32
- function bunAvailable(): boolean {
33
- try {
34
- execFileSync("bun", ["--version"], { stdio: "ignore" });
35
- return true;
36
- } catch {
37
- return false;
38
- }
39
- }
40
-
41
- describe("buildProdBundle (vanilla pipeline)", () => {
42
- let tmp = "";
43
-
44
- beforeEach(async () => {
45
- tmp = await mkdtemp(join(tmpdir(), "kumiko-build-it-"));
46
- });
47
-
48
- afterEach(async () => {
49
- await rm(tmp, { recursive: true, force: true });
50
- });
51
-
52
- test("kopiert public/ 1:1 nach dist/", async () => {
53
- await mkdir(join(tmp, "public"), { recursive: true });
54
- await writeFile(join(tmp, "public/favicon.txt"), "fav-content");
55
- await writeFile(join(tmp, "public/robots.txt"), "User-agent: *\nDisallow:\n");
56
- await writeFile(
57
- join(tmp, "public/index.html"),
58
- `<!doctype html><html><body>Hello</body></html>`,
59
- );
60
-
61
- const result = await buildProdBundle({ cwd: tmp });
62
-
63
- expect(result.manifest).toEqual({});
64
- expect(await readFile(join(tmp, "dist/favicon.txt"), "utf8")).toBe("fav-content");
65
- expect(await readFile(join(tmp, "dist/robots.txt"), "utf8")).toContain("User-agent");
66
- });
67
-
68
- test("rendert public/index.html als Template (Inhalt erhalten)", async () => {
69
- await mkdir(join(tmp, "public"), { recursive: true });
70
- await writeFile(
71
- join(tmp, "public/index.html"),
72
- `<!doctype html><html><head><title>Custom Title</title></head><body>Custom Body</body></html>`,
73
- );
74
-
75
- await buildProdBundle({ cwd: tmp });
76
-
77
- const html = await readFile(join(tmp, "dist/index.html"), "utf8");
78
- expect(html).toContain("Custom Title");
79
- expect(html).toContain("Custom Body");
80
- });
81
-
82
- test("schreibt manifest.json auch wenn leer", async () => {
83
- await mkdir(join(tmp, "public"), { recursive: true });
84
- await writeFile(join(tmp, "public/index.html"), `<html></html>`);
85
-
86
- await buildProdBundle({ cwd: tmp });
87
-
88
- const manifest = JSON.parse(await readFile(join(tmp, "dist/manifest.json"), "utf8"));
89
- expect(manifest).toEqual({});
90
- });
91
-
92
- test("public/index.html landet NICHT als rohes File in dist/ (wird als Template behandelt)", async () => {
93
- await mkdir(join(tmp, "public"), { recursive: true });
94
- const original = `<!doctype html><html><body>SOURCE</body></html>`;
95
- await writeFile(join(tmp, "public/index.html"), original);
96
- await writeFile(join(tmp, "public/other.txt"), "ABC");
97
-
98
- await buildProdBundle({ cwd: tmp });
99
-
100
- // dist/index.html existiert (gerendert), public/other.txt wurde kopiert
101
- expect(existsSync(join(tmp, "dist/index.html"))).toBe(true);
102
- expect(existsSync(join(tmp, "dist/other.txt"))).toBe(true);
103
- // Inhalt: was im Template stand, kommt im Output an (kein Doppel-Pfad
104
- // wo public/index.html und dist/index.html sich gegenseitig überschreiben).
105
- expect(await readFile(join(tmp, "dist/index.html"), "utf8")).toContain("SOURCE");
106
- });
107
-
108
- test("clean wipe: vorhandenes dist/-Junk wird entfernt", async () => {
109
- await mkdir(join(tmp, "public"), { recursive: true });
110
- await writeFile(join(tmp, "public/index.html"), `<html></html>`);
111
- // Junk-File aus altem Build
112
- await mkdir(join(tmp, "dist/old"), { recursive: true });
113
- await writeFile(join(tmp, "dist/old/stale.js"), "old content");
114
-
115
- await buildProdBundle({ cwd: tmp });
116
-
117
- expect(existsSync(join(tmp, "dist/old/stale.js"))).toBe(false);
118
- });
119
-
120
- test("wirft mit klarer Message wenn weder public/ noch index.html noch client da", async () => {
121
- // tmp ist leer
122
- await expect(buildProdBundle({ cwd: tmp })).rejects.toThrow(/nothing to build/);
123
- });
124
-
125
- test("wirft mit Anweisung wenn HTML-Template ohne /client.js Placeholder vorliegt", async () => {
126
- // Simuliert: User hat bereits manifest-entry (faken über option, in
127
- // realer Pipeline kommt's aus Bun.build). Wir testen renderHtml /
128
- // injectAssetTags-Verhalten via lower-level Pfad: HTML ohne Tag,
129
- // public-folder simuliert dass Build was zu tun hat. Erwartete
130
- // Error-Message zitiert das exakte Snippet.
131
- //
132
- // Dieser Pfad wird im realen Build durch buildClientBundle ausgelöst
133
- // und im Vanilla-Test können wir ihn nicht direkt triggern (kein
134
- // Bun.build). Der CLI-Subprocess-Test deckt das ab.
135
- await mkdir(join(tmp, "public"), { recursive: true });
136
- await writeFile(
137
- join(tmp, "public/index.html"),
138
- `<!doctype html><html><body>no script tag here</body></html>`,
139
- );
140
- // Ohne client.tsx läuft Bun.build nicht, manifest bleibt {} →
141
- // injectAssetTags wirft NICHT. Das ist gewollt: wenn nichts zu
142
- // injizieren da ist, ist's auch kein Fehler.
143
- const result = await buildProdBundle({ cwd: tmp });
144
- expect(result.manifest).toEqual({});
145
- });
146
- });
147
-
148
- // Voller Pipeline-Test gegen das echte CLI-bin via subprocess. Bringt
149
- // echtes Bun.build + Tailwind ins Spiel — der Pfad den die anderen
150
- // Tests bewusst nicht laufen können (Vitest unter Node).
151
- describe.skipIf(!bunAvailable())("kumiko-build CLI (full pipeline with bun)", () => {
152
- let tmp = "";
153
-
154
- beforeEach(async () => {
155
- tmp = await mkdtemp(join(tmpdir(), "kumiko-build-cli-"));
156
- });
157
-
158
- afterEach(async () => {
159
- await rm(tmp, { recursive: true, force: true });
160
- });
161
-
162
- test(
163
- "client.ts → hashed bundle, manifest, html mit injected script-tag",
164
- async () => {
165
- // Minimal client ohne externe Deps — Bun.build resolvt nichts.
166
- await mkdir(join(tmp, "src"), { recursive: true });
167
- await writeFile(
168
- join(tmp, "src/client.ts"),
169
- `const root = document.getElementById("root"); if (root) root.textContent = "hi";`,
170
- );
171
- await mkdir(join(tmp, "public"), { recursive: true });
172
- await writeFile(
173
- join(tmp, "public/index.html"),
174
- `<!doctype html><html><head></head><body><div id="root"></div><script type="module" src="/client.js"></script></body></html>`,
175
- );
176
- // package.json mit stylesheet:false äquivalent — wir setzen kein
177
- // src/styles.css und sind außerhalb des monorepos, sodass der
178
- // renderer-web-Fallback fehlschlägt und gracefully undefined liefert.
179
- await writeFile(join(tmp, "package.json"), `{"name":"build-it-fixture","private":true}`);
180
-
181
- execFileSync("bun", [KUMIKO_BUILD_BIN, tmp], { stdio: "pipe" });
182
-
183
- const manifest = JSON.parse(
184
- await readFile(join(tmp, "dist/manifest.json"), "utf8"),
185
- ) as Record<string, string>;
186
- expect(manifest["client.js"]).toMatch(/^\/assets\/client-[a-z0-9]+\.js$/);
187
-
188
- const html = await readFile(join(tmp, "dist/index.html"), "utf8");
189
- expect(html).toContain(`src="${manifest["client.js"]}"`);
190
- expect(html).toContain('id="root"');
191
-
192
- // Hashed asset existiert im dist/
193
- const assetPath = join(tmp, "dist", manifest["client.js"] ?? "");
194
- expect(existsSync(assetPath)).toBe(true);
195
-
196
- // Bundle enthält den User-Code (minified — also Identifier-Namen
197
- // mangled, aber der String-Literal "hi" überlebt).
198
- expect(await readFile(assetPath, "utf8")).toContain('"hi"');
199
- },
200
- FULL_PIPELINE_TIMEOUT_MS,
201
- );
202
-
203
- test(
204
- "client.ts ohne index.html → klarer Error mit Template-Vorschlag",
205
- async () => {
206
- await mkdir(join(tmp, "src"), { recursive: true });
207
- await writeFile(join(tmp, "src/client.ts"), `console.log("hi");`);
208
- await writeFile(join(tmp, "package.json"), `{"name":"no-html","private":true}`);
209
-
210
- let stderr = "";
211
- expect(() => {
212
- try {
213
- execFileSync("bun", [KUMIKO_BUILD_BIN, tmp], { stdio: "pipe" });
214
- } catch (err) {
215
- const e = err as { stderr?: Buffer };
216
- stderr = e.stderr?.toString() ?? "";
217
- throw err;
218
- }
219
- }).toThrow();
220
-
221
- expect(stderr).toContain("kein index.html gefunden");
222
- expect(stderr).toContain(`<script type="module" src="/client.js"></script>`);
223
- },
224
- FULL_PIPELINE_TIMEOUT_MS,
225
- );
226
-
227
- test(
228
- "client.ts + index.html ohne /client.js Placeholder → klarer Error",
229
- async () => {
230
- await mkdir(join(tmp, "src"), { recursive: true });
231
- await writeFile(join(tmp, "src/client.ts"), `console.log("hi");`);
232
- await mkdir(join(tmp, "public"), { recursive: true });
233
- await writeFile(
234
- join(tmp, "public/index.html"),
235
- `<!doctype html><html><body>no script</body></html>`,
236
- );
237
- await writeFile(join(tmp, "package.json"), `{"name":"no-placeholder","private":true}`);
238
-
239
- let stderr = "";
240
- expect(() => {
241
- try {
242
- execFileSync("bun", [KUMIKO_BUILD_BIN, tmp], { stdio: "pipe" });
243
- } catch (err) {
244
- const e = err as { stderr?: Buffer };
245
- stderr = e.stderr?.toString() ?? "";
246
- throw err;
247
- }
248
- }).toThrow();
249
-
250
- expect(stderr).toContain("keinen Entry-Tag für /client.js");
251
- expect(stderr).toContain(`<script type="module" src="/client.js"></script>`);
252
- },
253
- FULL_PIPELINE_TIMEOUT_MS,
254
- );
255
-
256
- test(
257
- "Re-Build mit unverändertem Source produziert identischen Hash (reproducibility)",
258
- async () => {
259
- await mkdir(join(tmp, "src"), { recursive: true });
260
- await writeFile(join(tmp, "src/client.ts"), `console.log("stable");`);
261
- await mkdir(join(tmp, "public"), { recursive: true });
262
- await writeFile(
263
- join(tmp, "public/index.html"),
264
- `<!doctype html><html><body><script type="module" src="/client.js"></script></body></html>`,
265
- );
266
- await writeFile(join(tmp, "package.json"), `{"name":"hash-stability","private":true}`);
267
-
268
- execFileSync("bun", [KUMIKO_BUILD_BIN, tmp], { stdio: "pipe" });
269
- const manifest1 = JSON.parse(
270
- await readFile(join(tmp, "dist/manifest.json"), "utf8"),
271
- ) as Record<string, string>;
272
-
273
- execFileSync("bun", [KUMIKO_BUILD_BIN, tmp], { stdio: "pipe" });
274
- const manifest2 = JSON.parse(
275
- await readFile(join(tmp, "dist/manifest.json"), "utf8"),
276
- ) as Record<string, string>;
277
-
278
- expect(manifest2["client.js"]).toBe(manifest1["client.js"]);
279
- },
280
- FULL_PIPELINE_TIMEOUT_MS,
281
- );
282
- });
@@ -1,228 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
- import { tmpdir } from "node:os";
4
- import { join } from "node:path";
5
- import { buildServerBundle, readExtraRuntimeExternals } from "../build-server-bundle";
6
-
7
- // Baut ein Mini-App-Fixture (bin/main.ts + bin/kumiko.ts teilen ein Modul) und
8
- // prüft das Variante-B-Verhalten: ein Bun.build-Call → server.js + kumiko.js als
9
- // Entries + geteilter chunk statt zwei vollen Bundles.
10
- function makeFixture(): string {
11
- const dir = mkdtempSync(join(tmpdir(), "server-bundle-"));
12
- mkdirSync(join(dir, "bin"));
13
- mkdirSync(join(dir, "src"));
14
- writeFileSync(join(dir, "package.json"), `${JSON.stringify({ name: "fixture-app" })}\n`);
15
- writeFileSync(join(dir, "src/shared.ts"), "export function shared(): number { return 42; }\n");
16
- // main.ts → wird zu server.js umbenannt; kumiko.ts macht findRepoRoot zum
17
- // repoRoot dieses Fixtures, also wird kumiko.js gebaut.
18
- writeFileSync(
19
- join(dir, "bin/main.ts"),
20
- `import { shared } from "../src/shared";\nconsole.log("server", shared());\n`,
21
- );
22
- writeFileSync(
23
- join(dir, "bin/kumiko.ts"),
24
- `import { shared } from "../src/shared";\nconsole.log("migrate", shared());\n`,
25
- );
26
- return dir;
27
- }
28
-
29
- describe("buildServerBundle (multi-entry + splitting)", () => {
30
- test("produces server.js + kumiko.js as entries + a shared chunk", async () => {
31
- const dir = makeFixture();
32
- try {
33
- const result = await buildServerBundle({ cwd: dir, outDir: join(dir, "dist-server") });
34
- const outDir = result.outDir;
35
-
36
- // main.ts wurde zu server.js umbenannt.
37
- expect(existsSync(join(outDir, "server.js"))).toBe(true);
38
- expect(existsSync(join(outDir, "main.js"))).toBe(false);
39
- expect(existsSync(join(outDir, "kumiko.js"))).toBe(true);
40
-
41
- const entryFiles = result.entries.map((e) => e.file).sort();
42
- expect(entryFiles).toEqual(["kumiko.js", "server.js"]);
43
-
44
- // Das geteilte Modul liegt in einem chunk, nicht in beiden Entries inlined.
45
- expect(result.chunks.length).toBeGreaterThanOrEqual(1);
46
- const serverSrc = readFileSync(join(outDir, "server.js"), "utf8");
47
- expect(serverSrc).toContain("chunk-");
48
-
49
- // Kein Legacy-Drizzle-Output mehr.
50
- expect(existsSync(join(outDir, "migration-hooks.js"))).toBe(false);
51
- expect(existsSync(join(outDir, "drizzle.config.ts"))).toBe(false);
52
-
53
- // Runtime-package.json mit start-Script, ohne drizzle-deps.
54
- const pkg = JSON.parse(readFileSync(join(outDir, "package.json"), "utf8"));
55
- expect(pkg.scripts.start).toBe("bun run server.js");
56
- expect(Object.keys(pkg.dependencies)).not.toContain("drizzle-kit");
57
- expect(Object.keys(pkg.dependencies)).not.toContain("drizzle-orm");
58
- // Positive counterpart to the negative checks above — a runtime
59
- // external accidentally dropped from RUNTIME_EXTERNALS (into
60
- // BUILD_ONLY_EXTERNALS instead) would slip through silently. This
61
- // is the exact regression class behind the money-horse prod crash
62
- // ("Cannot find package 'meilisearch'").
63
- expect(Object.keys(pkg.dependencies)).toContain("meilisearch");
64
-
65
- expect(result.totalBytes).toBeGreaterThan(0);
66
- } finally {
67
- rmSync(dir, { recursive: true, force: true });
68
- }
69
- });
70
-
71
- test("pins runtime-deps from node_modules/@cosmicdrift/* for consumer apps without a packages/ dir", async () => {
72
- // Reproduziert #1217: findRepoRoot liefert für eine Consumer-App die
73
- // App-Wurzel selbst (kein packages/-Ordner) — die Version muss aus den
74
- // installierten node_modules/@cosmicdrift/*-Packages kommen, nicht "*".
75
- const dir = makeFixture();
76
- try {
77
- const frameworkPkgDir = join(dir, "node_modules/@cosmicdrift/kumiko-framework");
78
- mkdirSync(frameworkPkgDir, { recursive: true });
79
- writeFileSync(
80
- join(frameworkPkgDir, "package.json"),
81
- `${JSON.stringify({ name: "@cosmicdrift/kumiko-framework", dependencies: { meilisearch: "^0.58.0" } })}\n`,
82
- );
83
- const bundledFeaturesPkgDir = join(dir, "node_modules/@cosmicdrift/kumiko-bundled-features");
84
- mkdirSync(bundledFeaturesPkgDir, { recursive: true });
85
- writeFileSync(
86
- join(bundledFeaturesPkgDir, "package.json"),
87
- `${JSON.stringify({ name: "@cosmicdrift/kumiko-bundled-features", dependencies: { ioredis: "^5.4.1" } })}\n`,
88
- );
89
-
90
- const result = await buildServerBundle({ cwd: dir, outDir: join(dir, "dist-server") });
91
-
92
- expect(result.runtimeDeps["meilisearch"]).toBe("^0.58.0");
93
- expect(result.runtimeDeps["ioredis"]).toBe("^5.4.1");
94
- } finally {
95
- rmSync(dir, { recursive: true, force: true });
96
- }
97
- });
98
-
99
- test("prefers node_modules/@cosmicdrift/* over packages/* when both declare a version", async () => {
100
- // Framework-monorepo checkout: both the installed node_modules package
101
- // (symlinked in real life, diverging here) and packages/framework exist.
102
- // node_modules must win — it reflects what actually gets shipped/installed
103
- // for the consumer, packages/framework is only a repo-root fallback.
104
- const dir = makeFixture();
105
- try {
106
- const frameworkPkgDir = join(dir, "node_modules/@cosmicdrift/kumiko-framework");
107
- mkdirSync(frameworkPkgDir, { recursive: true });
108
- writeFileSync(
109
- join(frameworkPkgDir, "package.json"),
110
- `${JSON.stringify({ name: "@cosmicdrift/kumiko-framework", dependencies: { meilisearch: "^0.58.0" } })}\n`,
111
- );
112
-
113
- mkdirSync(join(dir, "packages/framework"), { recursive: true });
114
- writeFileSync(
115
- join(dir, "packages/framework/package.json"),
116
- `${JSON.stringify({ name: "@cosmicdrift/kumiko-framework", dependencies: { meilisearch: "^0.30.0" } })}\n`,
117
- );
118
-
119
- const result = await buildServerBundle({ cwd: dir, outDir: join(dir, "dist-server") });
120
-
121
- expect(result.runtimeDeps["meilisearch"]).toBe("^0.58.0");
122
- } finally {
123
- rmSync(dir, { recursive: true, force: true });
124
- }
125
- });
126
- });
127
-
128
- describe("readExtraRuntimeExternals (package.json kumiko field, #1484)", () => {
129
- test("reads string list from kumiko.extraRuntimeExternals", () => {
130
- const dir = mkdtempSync(join(tmpdir(), "extra-ext-"));
131
- try {
132
- writeFileSync(
133
- join(dir, "package.json"),
134
- `${JSON.stringify({
135
- name: "app",
136
- kumiko: { extraRuntimeExternals: ["@napi-rs/canvas", "pdf-parse"] },
137
- })}\n`,
138
- );
139
- expect(readExtraRuntimeExternals(dir)).toEqual(["@napi-rs/canvas", "pdf-parse"]);
140
- } finally {
141
- rmSync(dir, { recursive: true, force: true });
142
- }
143
- });
144
-
145
- test("missing / invalid shapes yield empty list", () => {
146
- const dir = mkdtempSync(join(tmpdir(), "extra-ext-bad-"));
147
- try {
148
- writeFileSync(join(dir, "package.json"), `${JSON.stringify({ name: "app" })}\n`);
149
- expect(readExtraRuntimeExternals(dir)).toEqual([]);
150
- writeFileSync(
151
- join(dir, "package.json"),
152
- `${JSON.stringify({ name: "app", kumiko: { extraRuntimeExternals: "nope" } })}\n`,
153
- );
154
- expect(readExtraRuntimeExternals(dir)).toEqual([]);
155
- writeFileSync(
156
- join(dir, "package.json"),
157
- `${JSON.stringify({
158
- name: "app",
159
- kumiko: { extraRuntimeExternals: ["ok", 1, "", " "] },
160
- })}\n`,
161
- );
162
- expect(readExtraRuntimeExternals(dir)).toEqual(["ok"]);
163
- } finally {
164
- rmSync(dir, { recursive: true, force: true });
165
- }
166
- });
167
- });
168
-
169
- describe("buildServerBundle + package.json extras (#1484)", () => {
170
- test("extraRuntimeExternals from options land in runtimeDeps with app pin", async () => {
171
- const dir = makeFixture();
172
- try {
173
- writeFileSync(
174
- join(dir, "package.json"),
175
- `${JSON.stringify({
176
- name: "fixture-app",
177
- dependencies: { "@napi-rs/canvas": "0.1.65" },
178
- kumiko: { extraRuntimeExternals: ["@napi-rs/canvas"] },
179
- })}\n`,
180
- );
181
- const extras = readExtraRuntimeExternals(dir);
182
- const result = await buildServerBundle({
183
- cwd: dir,
184
- outDir: join(dir, "dist-server"),
185
- extraRuntimeExternals: extras,
186
- });
187
- expect(result.runtimeDeps["@napi-rs/canvas"]).toBe("0.1.65");
188
- const distPkg = JSON.parse(readFileSync(join(dir, "dist-server/package.json"), "utf-8")) as {
189
- dependencies: Record<string, string>;
190
- };
191
- expect(distPkg.dependencies["@napi-rs/canvas"]).toBe("0.1.65");
192
- } finally {
193
- rmSync(dir, { recursive: true, force: true });
194
- }
195
- });
196
-
197
- test("app package.json does not override framework runtime pins", async () => {
198
- const dir = makeFixture();
199
- try {
200
- const frameworkPkgDir = join(dir, "node_modules/@cosmicdrift/kumiko-framework");
201
- mkdirSync(frameworkPkgDir, { recursive: true });
202
- writeFileSync(
203
- join(frameworkPkgDir, "package.json"),
204
- `${JSON.stringify({
205
- name: "@cosmicdrift/kumiko-framework",
206
- dependencies: { meilisearch: "^0.58.0" },
207
- })}\n`,
208
- );
209
- writeFileSync(
210
- join(dir, "package.json"),
211
- `${JSON.stringify({
212
- name: "fixture-app",
213
- dependencies: { meilisearch: "^0.1.0", "@napi-rs/canvas": "0.1.65" },
214
- kumiko: { extraRuntimeExternals: ["@napi-rs/canvas"] },
215
- })}\n`,
216
- );
217
- const result = await buildServerBundle({
218
- cwd: dir,
219
- outDir: join(dir, "dist-server"),
220
- extraRuntimeExternals: readExtraRuntimeExternals(dir),
221
- });
222
- expect(result.runtimeDeps["meilisearch"]).toBe("^0.58.0");
223
- expect(result.runtimeDeps["@napi-rs/canvas"]).toBe("0.1.65");
224
- } finally {
225
- rmSync(dir, { recursive: true, force: true });
226
- }
227
- });
228
- });
@@ -1,87 +0,0 @@
1
- // Heuristik für die Watcher-Loop: server-side Änderungen brauchen
2
- // Process-Restart (Bun cached Module-Imports), Client-Side reicht
3
- // Hot-Reload, Tests/non-TS sollen den Watcher gar nicht triggern.
4
- // Wenn die Klassifikation drift bekommt, kommen entweder unnötige
5
- // Restarts (DX schlecht) oder echte Schema-Änderungen schlagen
6
- // nicht durch (UX broken). Beide sind teuer — daher pinnen wir.
7
-
8
- import { describe, expect, test } from "bun:test";
9
- import { classifyChange } from "../create-kumiko-server";
10
-
11
- describe("classifyChange", () => {
12
- test("server-side feature.ts → restart", () => {
13
- expect(classifyChange("/abs/samples/foo/src/features/items/feature.ts")).toBe("restart");
14
- });
15
-
16
- test("server-side schema-Datei → restart", () => {
17
- expect(classifyChange("/abs/samples/foo/src/features/items/schema/item.ts")).toBe("restart");
18
- });
19
-
20
- test("server-side bin/server.ts → restart", () => {
21
- expect(classifyChange("/abs/samples/foo/bin/server.ts")).toBe("restart");
22
- });
23
-
24
- test("client-side web/index.ts → hot-reload", () => {
25
- expect(classifyChange("/abs/samples/foo/src/features/items/web/index.ts")).toBe("hot-reload");
26
- });
27
-
28
- test("client-side web/page.tsx → hot-reload", () => {
29
- expect(classifyChange("/abs/samples/foo/src/features/items/web/page.tsx")).toBe("hot-reload");
30
- });
31
-
32
- test("client.tsx Entry → hot-reload", () => {
33
- expect(classifyChange("/abs/samples/foo/src/app/client.tsx")).toBe("hot-reload");
34
- });
35
-
36
- test("client.ts Entry → hot-reload", () => {
37
- expect(classifyChange("/abs/samples/foo/src/app/client.ts")).toBe("hot-reload");
38
- });
39
-
40
- test("Test-Datei *.test.ts → ignore", () => {
41
- expect(classifyChange("/abs/samples/foo/src/feature.test.ts")).toBe("ignore");
42
- });
43
-
44
- test("Test-Datei *.test.tsx → ignore", () => {
45
- expect(classifyChange("/abs/samples/foo/src/component.test.tsx")).toBe("ignore");
46
- });
47
-
48
- test("__tests__/ Subdir → ignore (auch wenn die Datei selbst nicht *.test.ts heißt)", () => {
49
- expect(classifyChange("/abs/samples/foo/src/__tests__/test-utils.ts")).toBe("ignore");
50
- });
51
-
52
- test("Integration-Test → ignore (würde sonst Schema-Restart auslösen)", () => {
53
- expect(classifyChange("/abs/samples/foo/src/feature.integration.test.ts")).toBe("ignore");
54
- });
55
-
56
- test("E2E-Test → ignore", () => {
57
- expect(classifyChange("/abs/samples/foo/src/something.e2e.ts")).toBe("ignore");
58
- });
59
-
60
- test("CSS/JSON/sonstiges → ignore", () => {
61
- expect(classifyChange("/abs/samples/foo/src/styles.css")).toBe("ignore");
62
- expect(classifyChange("/abs/samples/foo/src/data.json")).toBe("ignore");
63
- expect(classifyChange("/abs/samples/foo/public/index.html")).toBe("ignore");
64
- });
65
-
66
- test("Windows-Pfad-Trenner: web\\ → hot-reload", () => {
67
- expect(classifyChange("C:\\abs\\samples\\foo\\src\\features\\items\\web\\page.tsx")).toBe(
68
- "hot-reload",
69
- );
70
- });
71
-
72
- // Sample-Conventions: publicstatus splittet `/admin/` (Admin-Bundle)
73
- // und `/public/` (Public-Bundle). Beide sind client-side, müssen
74
- // hot-reload sein — sonst kostet jeder Bridge-Edit einen Restart +
75
- // DB-Reset. Andere Samples machen das eventuell auch so.
76
- test("admin/-Subdir → hot-reload", () => {
77
- expect(classifyChange("/abs/samples/foo/src/admin/branding-settings.tsx")).toBe("hot-reload");
78
- });
79
-
80
- test("public/-Subdir → hot-reload", () => {
81
- expect(classifyChange("/abs/samples/foo/src/public/StatusPage.tsx")).toBe("hot-reload");
82
- });
83
-
84
- test("client/-Subdir → hot-reload", () => {
85
- expect(classifyChange("/abs/samples/foo/src/client/widgets.tsx")).toBe("hot-reload");
86
- });
87
- });