@cosmicdrift/kumiko-dev-server 0.14.0 → 0.15.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 (33) hide show
  1. package/package.json +8 -12
  2. package/src/__tests__/build-prod-bundle.integration.ts +1 -1
  3. package/src/__tests__/build-prod-bundle.test.ts +1 -1
  4. package/src/__tests__/cache-headers.test.ts +1 -1
  5. package/src/__tests__/classify-change.test.ts +1 -1
  6. package/src/__tests__/compose-features-wiring.integration.ts +10 -9
  7. package/src/__tests__/compose-features.test.ts +1 -1
  8. package/src/__tests__/config-seed-boot.integration.ts +5 -4
  9. package/src/__tests__/crash-tracker.test.ts +1 -1
  10. package/src/__tests__/create-kumiko-server.integration.ts +5 -5
  11. package/src/__tests__/env-schema.integration.ts +1 -1
  12. package/src/__tests__/env-schema.test.ts +1 -1
  13. package/src/__tests__/few-shot-corpus.test.ts +1 -1
  14. package/src/__tests__/inject-schema.test.ts +1 -1
  15. package/src/__tests__/resolve-stylesheet.test.ts +11 -7
  16. package/src/__tests__/resolve-tailwind-cli.test.ts +1 -1
  17. package/src/__tests__/run-prod-app-spec.test.ts +1 -1
  18. package/src/__tests__/run-prod-app.integration.ts +6 -6
  19. package/src/__tests__/scaffold-app-feature.test.ts +1 -1
  20. package/src/__tests__/scaffold-app.test.ts +1 -1
  21. package/src/__tests__/scaffold-deploy.test.ts +1 -1
  22. package/src/__tests__/scaffold-feature.test.ts +1 -1
  23. package/src/__tests__/try-hono-first.test.ts +1 -1
  24. package/src/__tests__/walkthrough.integration.ts +1 -1
  25. package/src/codegen/__tests__/run-codegen.test.ts +1 -1
  26. package/src/codegen/__tests__/strict-mode-diagnostics.test.ts +1 -1
  27. package/src/codegen/__tests__/watch.test.ts +1 -1
  28. package/src/scaffold-app-feature.ts +2 -1
  29. package/src/{drizzle-tables-auth-mode.ts → schema-tables-auth-mode.ts} +1 -1
  30. package/templates/deploy/Dockerfile.template +15 -47
  31. package/CHANGELOG.md +0 -656
  32. package/src/drizzle-config.ts +0 -44
  33. /package/src/{drizzle-tables-minimal.ts → schema-tables-minimal.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-dev-server",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Development server bootstrap for Kumiko apps. Bundles the client, mints dev-JWTs, injects the resolved AppSchema, and seeds an admin. Not for production.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -30,17 +30,13 @@
30
30
  "types": "./src/compose-features.ts",
31
31
  "default": "./src/compose-features.ts"
32
32
  },
33
- "./drizzle-config": {
34
- "types": "./src/drizzle-config.ts",
35
- "default": "./src/drizzle-config.ts"
33
+ "./schema-tables-auth-mode": {
34
+ "types": "./src/schema-tables-auth-mode.ts",
35
+ "default": "./src/schema-tables-auth-mode.ts"
36
36
  },
37
- "./drizzle-tables-auth-mode": {
38
- "types": "./src/drizzle-tables-auth-mode.ts",
39
- "default": "./src/drizzle-tables-auth-mode.ts"
40
- },
41
- "./drizzle-tables-minimal": {
42
- "types": "./src/drizzle-tables-minimal.ts",
43
- "default": "./src/drizzle-tables-minimal.ts"
37
+ "./schema-tables-minimal": {
38
+ "types": "./src/schema-tables-minimal.ts",
39
+ "default": "./src/schema-tables-minimal.ts"
44
40
  }
45
41
  },
46
42
  "bin": {
@@ -63,4 +59,4 @@
63
59
  "README.md",
64
60
  "LICENSE"
65
61
  ]
66
- }
62
+ }
@@ -12,13 +12,13 @@
12
12
  // braucht Bun. Spawnen kumiko-build als subprocess via PATH.
13
13
  // Skipped wenn `bun` nicht erreichbar — selten, aber sauber.
14
14
 
15
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
15
16
  import { execFileSync } from "node:child_process";
16
17
  import { existsSync } from "node:fs";
17
18
  import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
18
19
  import { tmpdir } from "node:os";
19
20
  import { dirname, join, resolve } from "node:path";
20
21
  import { fileURLToPath } from "node:url";
21
- import { afterEach, beforeEach, describe, expect, test } from "vitest";
22
22
  import { buildProdBundle } from "../build-prod-bundle";
23
23
 
24
24
  const __filename = fileURLToPath(import.meta.url);
@@ -6,11 +6,11 @@
6
6
  // laufen im CI als `yarn build` auf der Showcase-App; das ist der
7
7
  // ehrlichere Smoke-Test.
8
8
 
9
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
9
10
  import { existsSync } from "node:fs";
10
11
  import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
11
12
  import { tmpdir } from "node:os";
12
13
  import { join } from "node:path";
13
- import { afterEach, beforeEach, describe, expect, test } from "vitest";
14
14
  import {
15
15
  type ClientEntry,
16
16
  discoverClientEntries,
@@ -7,7 +7,7 @@
7
7
  // /manifest.json, /sw.js → no-cache
8
8
  // alles andere → kein expliziter Header
9
9
 
10
- import { describe, expect, test } from "vitest";
10
+ import { describe, expect, test } from "bun:test";
11
11
  import { cacheHeadersFor } from "../run-prod-app";
12
12
 
13
13
  describe("cacheHeadersFor", () => {
@@ -5,7 +5,7 @@
5
5
  // Restarts (DX schlecht) oder echte Schema-Änderungen schlagen
6
6
  // nicht durch (UX broken). Beide sind teuer — daher pinnen wir.
7
7
 
8
- import { describe, expect, test } from "vitest";
8
+ import { describe, expect, test } from "bun:test";
9
9
  import { classifyChange } from "../create-kumiko-server";
10
10
 
11
11
  describe("classifyChange", () => {
@@ -21,6 +21,7 @@
21
21
  // schreibt in ein lokales Array — gewollter Capture-Spy ohne vitest-
22
22
  // Mock-API (CLAUDE.md "Kein Mock in *.integration.ts").
23
23
 
24
+ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } from "bun:test";
24
25
  import { randomBytes } from "node:crypto";
25
26
  import {
26
27
  AuthErrors,
@@ -34,6 +35,7 @@ import {
34
35
  import { tenantEntity, tenantMembershipsTable } from "@cosmicdrift/kumiko-bundled-features/tenant";
35
36
  import { seedTenantMembership } from "@cosmicdrift/kumiko-bundled-features/tenant/testing";
36
37
  import { UserHandlers, userEntity, userTable } from "@cosmicdrift/kumiko-bundled-features/user";
38
+ import { deleteMany } from "@cosmicdrift/kumiko-framework/bun-db";
37
39
  import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
38
40
  import {
39
41
  setupTestStack,
@@ -42,7 +44,6 @@ import {
42
44
  unsafeCreateEntityTable,
43
45
  unsafePushTables,
44
46
  } from "@cosmicdrift/kumiko-framework/stack";
45
- import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
46
47
  import { composeFeatures } from "../compose-features";
47
48
 
48
49
  const RESET_HMAC = randomBytes(32).toString("base64");
@@ -151,8 +152,8 @@ describe("composeFeatures wiring — passwordReset", () => {
151
152
  });
152
153
 
153
154
  beforeEach(async () => {
154
- await suite.stack.db.delete(userTable);
155
- await suite.stack.db.delete(tenantMembershipsTable);
155
+ await deleteMany(suite.stack.db, userTable, {});
156
+ await deleteMany(suite.stack.db, tenantMembershipsTable, {});
156
157
  suite.resetEmails.length = 0;
157
158
  suite.verifyEmails.length = 0;
158
159
  });
@@ -225,8 +226,8 @@ describe("composeFeatures wiring — emailVerification", () => {
225
226
  });
226
227
 
227
228
  beforeEach(async () => {
228
- await suite.stack.db.delete(userTable);
229
- await suite.stack.db.delete(tenantMembershipsTable);
229
+ await deleteMany(suite.stack.db, userTable, {});
230
+ await deleteMany(suite.stack.db, tenantMembershipsTable, {});
230
231
  suite.resetEmails.length = 0;
231
232
  suite.verifyEmails.length = 0;
232
233
  });
@@ -271,8 +272,8 @@ describe("composeFeatures wiring — asymmetric activation", () => {
271
272
  });
272
273
 
273
274
  beforeEach(async () => {
274
- await suite.stack.db.delete(userTable);
275
- await suite.stack.db.delete(tenantMembershipsTable);
275
+ await deleteMany(suite.stack.db, userTable, {});
276
+ await deleteMany(suite.stack.db, tenantMembershipsTable, {});
276
277
  suite.resetEmails.length = 0;
277
278
  suite.verifyEmails.length = 0;
278
279
  });
@@ -325,8 +326,8 @@ describe("composeFeatures wiring — fail-closed ohne authOptions", () => {
325
326
  });
326
327
 
327
328
  afterEach(async () => {
328
- await suite.stack.db.delete(userTable);
329
- await suite.stack.db.delete(tenantMembershipsTable);
329
+ await deleteMany(suite.stack.db, userTable, {});
330
+ await deleteMany(suite.stack.db, tenantMembershipsTable, {});
330
331
  suite.resetEmails.length = 0;
331
332
  });
332
333
 
@@ -7,8 +7,8 @@
7
7
  // aber die Handler fehlen → POST /api/auth/request-password-reset
8
8
  // dispatched ins Leere → 500.
9
9
 
10
+ import { describe, expect, test } from "bun:test";
10
11
  import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
11
- import { describe, expect, test } from "vitest";
12
12
  import { composeFeatures } from "../compose-features";
13
13
 
14
14
  const noopFeature = defineFeature("noop-app", () => {});
@@ -12,12 +12,14 @@
12
12
  // 3. an admin set on top of a seed wins the resolver cascade; coexistence
13
13
  // vs. override semantics depend on the admin user's tenantId.
14
14
 
15
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
15
16
  import {
16
17
  configValuesTable,
17
18
  createConfigAccessorFactory,
18
19
  createConfigFeature,
19
20
  createConfigResolver,
20
21
  } from "@cosmicdrift/kumiko-bundled-features/config";
22
+ import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
21
23
  import {
22
24
  access,
23
25
  createSystemConfig,
@@ -32,7 +34,6 @@ import {
32
34
  TestUsers,
33
35
  unsafePushTables,
34
36
  } from "@cosmicdrift/kumiko-framework/stack";
35
- import { afterAll, beforeAll, describe, expect, test } from "vitest";
36
37
  import { applyBootSeeds } from "../boot/apply-boot-seeds";
37
38
 
38
39
  const bootSeedsFeature = defineFeature("boot-seeds-test", (r) => {
@@ -82,7 +83,7 @@ describe("config-seed boot wiring", () => {
82
83
  test("first boot: applyBootSeeds writes one row per seed", async () => {
83
84
  await applyBootSeeds({ registry: stack.registry, db: stack.db });
84
85
 
85
- const rows = await stack.db.select().from(configValuesTable);
86
+ const rows = await selectMany(stack.db, configValuesTable);
86
87
  expect(rows.length).toBe(2);
87
88
 
88
89
  const siteKeyDef = stack.registry.getConfigKey(SITE_KEY);
@@ -111,7 +112,7 @@ describe("config-seed boot wiring", () => {
111
112
  test("re-boot: idempotent — every seed already on disk → no extra rows", async () => {
112
113
  await applyBootSeeds({ registry: stack.registry, db: stack.db });
113
114
 
114
- const rows = await stack.db.select().from(configValuesTable);
115
+ const rows = await selectMany(stack.db, configValuesTable);
115
116
  expect(rows.length).toBe(2);
116
117
  });
117
118
 
@@ -151,7 +152,7 @@ describe("config-seed boot wiring", () => {
151
152
  // - 3 rows = coexistence path (admin tenantId !== SYSTEM_TENANT_ID,
152
153
  // new specific-tenant row sits next to the seed system-row).
153
154
  // Either is correct as long as the resolver picks the admin value.
154
- const rows = await stack.db.select().from(configValuesTable);
155
+ const rows = await selectMany(stack.db, configValuesTable);
155
156
  expect([2, 3]).toContain(rows.length);
156
157
  });
157
158
  });
@@ -3,7 +3,7 @@
3
3
  // Wrapper aufgibt oder noch eine Runde respawnt; off-by-one am Fenster-
4
4
  // Rand würde im Bin-Skript schlecht auffallen.
5
5
 
6
- import { describe, expect, test } from "vitest";
6
+ import { describe, expect, test } from "bun:test";
7
7
  import { createCrashTracker } from "../crash-tracker";
8
8
 
9
9
  describe("createCrashTracker", () => {
@@ -1,14 +1,14 @@
1
+ import { afterEach, describe, expect, test } from "bun:test";
1
2
  import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
3
  import { tmpdir } from "node:os";
3
4
  import { join } from "node:path";
5
+ import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
4
6
  import {
5
7
  createBooleanField,
6
8
  createEntity,
7
9
  createTextField,
8
10
  defineFeature,
9
11
  } from "@cosmicdrift/kumiko-framework/engine";
10
- import { sql } from "drizzle-orm";
11
- import { afterEach, describe, expect, test } from "vitest";
12
12
  import { createKumikoServer, type KumikoServerHandle } from "../create-kumiko-server";
13
13
 
14
14
  // Integration-Test: bootet createKumikoServer mit echtem Postgres,
@@ -50,10 +50,10 @@ async function boot(): Promise<KumikoServerHandle> {
50
50
  describe("createKumikoServer", () => {
51
51
  test("bootet den Kumiko-Stack + legt die Feature-Tables an", async () => {
52
52
  const h = await boot();
53
- const rows = await h.stack.db.execute<{ exists: boolean }>(
54
- sql`SELECT to_regclass('public.kumiko_server_probe') IS NOT NULL AS exists`,
53
+ const rows = await asRawClient(h.stack.db).unsafe(
54
+ `SELECT to_regclass('public.kumiko_server_probe') IS NOT NULL AS "exists"`,
55
55
  );
56
- expect(rows[0]?.exists).toBe(true);
56
+ expect((rows as Array<Record<string, unknown>>)[0]?.["exists"]).toBe(true);
57
57
  });
58
58
 
59
59
  test("GET / → HTML + kumiko_auth/kumiko_csrf Set-Cookie", async () => {
@@ -4,9 +4,9 @@
4
4
  // throws (via bootErrorReporter override) and KUMIKO_DRY_RUN_ENV
5
5
  // returns the dry-run handle without booting.
6
6
 
7
+ import { describe, expect, it } from "bun:test";
7
8
  import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
8
9
  import { composeEnvSchema, KumikoBootError } from "@cosmicdrift/kumiko-framework/env";
9
- import { describe, expect, it } from "vitest";
10
10
  import { z } from "zod";
11
11
  import { frameworkCoreEnvSchema } from "../env-schema";
12
12
  import * as devServerPublicApi from "../index";
@@ -1,5 +1,5 @@
1
+ import { describe, expect, it } from "bun:test";
1
2
  import { composeEnvSchema, KumikoBootError, parseEnv } from "@cosmicdrift/kumiko-framework/env";
2
- import { describe, expect, it } from "vitest";
3
3
  import { z } from "zod";
4
4
  import { type FrameworkCoreEnv, frameworkCoreEnvSchema } from "../env-schema";
5
5
 
@@ -13,11 +13,11 @@
13
13
  // because the corpus JSON now lives there — framework is public and must
14
14
  // not carry the eval baseline.
15
15
 
16
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
16
17
  import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
17
18
  import { tmpdir } from "node:os";
18
19
  import { join } from "node:path";
19
20
  import { buildFewShotCorpus, pathToId } from "@cosmicdrift/kumiko-dev-server";
20
- import { afterEach, beforeEach, describe, expect, test } from "vitest";
21
21
 
22
22
  let workdir: string;
23
23
 
@@ -4,7 +4,7 @@
4
4
  // nicht und mountet leer. Tests pinnen die Idempotenz + die zwei
5
5
  // Insertion-Punkte (vor /client.js-Tag oder vor </body>).
6
6
 
7
- import { describe, expect, test } from "vitest";
7
+ import { describe, expect, test } from "bun:test";
8
8
  import { injectSchema } from "../inject-schema";
9
9
 
10
10
  const SCHEMA = '{"features":[]}';
@@ -7,10 +7,10 @@
7
7
  // die anderen Tests dort booten ein TestStack (DB + Redis), das brauchen
8
8
  // wir für reine Resolver-Logik nicht.
9
9
 
10
+ import { describe, expect, test } from "bun:test";
10
11
  import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
11
12
  import { tmpdir } from "node:os";
12
13
  import { join } from "node:path";
13
- import { describe, expect, test } from "vitest";
14
14
  import { resolveStylesheet } from "../create-kumiko-server";
15
15
 
16
16
  describe("resolveStylesheet", () => {
@@ -48,16 +48,20 @@ describe("resolveStylesheet", () => {
48
48
  expect(out).toBeUndefined();
49
49
  });
50
50
 
51
- test("undefined + clientEntry unter Node (ohne Bun): undefined (silent skip)", () => {
52
- // hasBun ist false in vitest-Node — Default-Resolution wird übersprungen,
53
- // ohne Throw. Wer im Test eine echte CSS will, übergibt explicit string.
54
- // Unter Bun (production) würde Bun.resolveSync den Path liefern; das
55
- // testet der bestehende create-kumiko-server.integration.ts.
51
+ test("undefined + clientEntry: undefined (silent skip)", () => {
52
+ // Bun resolveSync findet packages/renderer-web/src/styles.css im
53
+ // Repo. Das ist der prod-Pfad unter Bun (real oder CI) ist der
54
+ // Wert ein absoluter path; unter Node (von wo der Test portiert
55
+ // wurde) wäre es undefined. Wir akzeptieren beides.
56
56
  const out = resolveStylesheet({
57
57
  features: [],
58
58
  clientEntry: "./entry.tsx",
59
59
  });
60
- expect(out).toBeUndefined();
60
+ if (typeof Bun === "undefined") {
61
+ expect(out).toBeUndefined();
62
+ } else {
63
+ expect(out).toMatch(/renderer-web\/src\/styles\.css$/);
64
+ }
61
65
  });
62
66
 
63
67
  test("undefined + clientEntry + src/styles.css existiert → returns App-Theme-Override", () => {
@@ -2,7 +2,7 @@
2
2
  // genau das, was den dev-server-Crash bei flakigem Netz verhindert:
3
3
  // kein Bun → undefined, Package nicht installiert → undefined.
4
4
 
5
- import { describe, expect, test } from "vitest";
5
+ import { describe, expect, test } from "bun:test";
6
6
  import { resolveTailwindCli } from "../resolve-tailwind-cli";
7
7
 
8
8
  describe("resolveTailwindCli", () => {
@@ -4,8 +4,8 @@
4
4
  // "looks like a leak"-Reverts oder "bisschen rauf, sollte reichen"-
5
5
  // Tweaks.
6
6
 
7
+ import { describe, expect, test } from "bun:test";
7
8
  import { SSE_HEARTBEAT_INTERVAL_MS } from "@cosmicdrift/kumiko-framework/api";
8
- import { describe, expect, test } from "vitest";
9
9
  import { buildBunServeOptions } from "../run-prod-app";
10
10
 
11
11
  describe("Bun.serve options for production", () => {
@@ -9,9 +9,11 @@
9
9
  // fetch direkt. Bun.serve-Wiring ist in Production-Coolify selbst
10
10
  // getestet wenn der Container hochfährt.
11
11
 
12
+ import { afterEach, beforeAll, describe, expect, test } from "bun:test";
12
13
  import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
13
14
  import { tmpdir } from "node:os";
14
15
  import { dirname, join } from "node:path";
16
+ import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
15
17
  import { createDbConnection } from "@cosmicdrift/kumiko-framework/db";
16
18
  import {
17
19
  createBooleanField,
@@ -28,9 +30,7 @@ import {
28
30
  createProjectionStateTable,
29
31
  } from "@cosmicdrift/kumiko-framework/pipeline";
30
32
  import { unsafeEnsureEntityTable } from "@cosmicdrift/kumiko-framework/stack";
31
- import { sql } from "drizzle-orm";
32
33
  import postgres from "postgres";
33
- import { afterEach, beforeAll, describe, expect, test } from "vitest";
34
34
  import { z } from "zod";
35
35
  import { type ProdAppHandle, runProdApp } from "../run-prod-app";
36
36
 
@@ -448,7 +448,7 @@ describe("runProdApp", () => {
448
448
  });
449
449
 
450
450
  expect(invocations).toBe(1);
451
- expect(factoryDeps).toEqual({ db: true, redis: true, registry: true });
451
+ expect(factoryDeps!).toEqual({ db: true, redis: true, registry: true });
452
452
  // Smoke: handle is functional (boot completed without error).
453
453
  expect(handle.entrypoint).toBeDefined();
454
454
  });
@@ -465,9 +465,9 @@ describe("runProdApp", () => {
465
465
  seedInvocations++;
466
466
  // Seed-side idempotence: check before inserting. runProdApp doesn't
467
467
  // gate seeds — the seed itself is responsible.
468
- const existing = await db.execute(sql`SELECT 1 FROM prod_widgets LIMIT 1`);
469
- if (existing.length > 0) return;
470
- await db.execute(sql`INSERT INTO prod_widgets (id, tenant_id, name) VALUES
468
+ const existing = await asRawClient(db).unsafe(`SELECT 1 FROM prod_widgets LIMIT 1`);
469
+ if ((existing as Array<Record<string, unknown>>).length > 0) return;
470
+ await asRawClient(db).unsafe(`INSERT INTO prod_widgets (id, tenant_id, name) VALUES
471
471
  (gen_random_uuid(), '00000000-0000-4000-8000-000000000001', 'seeded')`);
472
472
  inserted = true;
473
473
  };
@@ -1,9 +1,9 @@
1
1
  // scaffoldAppFeature unit-tests (DX-2).
2
2
 
3
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
3
4
  import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
4
5
  import { tmpdir } from "node:os";
5
6
  import { join } from "node:path";
6
- import { afterEach, beforeEach, describe, expect, test } from "vitest";
7
7
  import { scaffoldApp } from "../scaffold-app";
8
8
  import { scaffoldAppFeature } from "../scaffold-app-feature";
9
9
 
@@ -1,9 +1,9 @@
1
1
  // scaffoldApp unit-tests (DX-1.0).
2
2
 
3
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
3
4
  import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
4
5
  import { tmpdir } from "node:os";
5
6
  import { join } from "node:path";
6
- import { afterEach, beforeEach, describe, expect, test } from "vitest";
7
7
  import { scaffoldApp } from "../scaffold-app";
8
8
 
9
9
  describe("scaffoldApp", () => {
@@ -1,7 +1,7 @@
1
+ import { afterEach, beforeEach, describe, expect, it } from "bun:test";
1
2
  import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
3
  import { tmpdir } from "node:os";
3
4
  import { join } from "node:path";
4
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
5
  import { scaffoldDeploy } from "../scaffold-deploy";
6
6
 
7
7
  describe("scaffoldDeploy", () => {
@@ -8,12 +8,12 @@
8
8
  // 4. Validation: bad names fail loudly, existing destination refuses
9
9
  // to overwrite
10
10
 
11
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
11
12
  import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
12
13
  import { tmpdir } from "node:os";
13
14
  import { join } from "node:path";
14
15
  import { parseSourceFile, VERSION_HEADER } from "@cosmicdrift/kumiko-framework/engine";
15
16
  import { Project } from "ts-morph";
16
- import { afterEach, beforeEach, describe, expect, test } from "vitest";
17
17
  import { scaffoldFeature } from "../scaffold-feature";
18
18
 
19
19
  let workdir: string;
@@ -5,7 +5,7 @@
5
5
  // sich ändert (z.B. "matched" auch für 4xx anders als 404), MÜSSEN
6
6
  // beide Pfade synchron updaten.
7
7
 
8
- import { describe, expect, test } from "vitest";
8
+ import { describe, expect, test } from "bun:test";
9
9
  import { type HonoLikeApp, tryHonoFirst } from "../try-hono-first";
10
10
 
11
11
  function makeApp(response: Response): HonoLikeApp {
@@ -9,13 +9,13 @@
9
9
  // - composeFeatures(includeBundled:true) yields the exact feature-count
10
10
  // the walkthrough advertises in "Expected output"
11
11
 
12
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
12
13
  import { mkdtempSync, readFileSync, rmSync } from "node:fs";
13
14
  import { tmpdir } from "node:os";
14
15
  import { join } from "node:path";
15
16
  import { createSecretsFeature } from "@cosmicdrift/kumiko-bundled-features/secrets";
16
17
  import { createSessionsFeature } from "@cosmicdrift/kumiko-bundled-features/sessions";
17
18
  import { createRegistry, defineFeature, validateBoot } from "@cosmicdrift/kumiko-framework/engine";
18
- import { afterEach, beforeEach, describe, expect, test } from "vitest";
19
19
  import { composeFeatures } from "../compose-features";
20
20
  import { scaffoldApp } from "../scaffold-app";
21
21
  import { scaffoldAppFeature } from "../scaffold-app-feature";
@@ -16,10 +16,10 @@
16
16
  // - skipped-Flag wenn 0 Events UND kein .kumiko/ schon existiert
17
17
  // - schemas.generated.ts wird erzeugt wenn inline-Schemas, sonst nicht
18
18
 
19
+ import { describe, expect, test } from "bun:test";
19
20
  import { existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
20
21
  import { tmpdir } from "node:os";
21
22
  import { join } from "node:path";
22
- import { describe, expect, test } from "vitest";
23
23
  import { runCodegen } from "../run-codegen";
24
24
 
25
25
  function makeAppDir(): string {
@@ -1,7 +1,7 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
1
2
  import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
3
  import { dirname, join } from "node:path";
3
4
  import * as ts from "typescript";
4
- import { afterAll, beforeAll, describe, expect, test } from "vitest";
5
5
  import { runCodegen } from "../run-codegen";
6
6
 
7
7
  const REPO_ROOT = join(__dirname, "../../../../..");
@@ -8,9 +8,9 @@
8
8
  // 'zod' nicht direct nutzen, runCodegen scant feature-files die
9
9
  // `import { z } from "zod"` haben.
10
10
 
11
+ import { afterAll, describe, expect, test } from "bun:test";
11
12
  import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
12
13
  import { dirname, join } from "node:path";
13
- import { afterAll, describe, expect, test } from "vitest";
14
14
  import type { CodegenResult } from "../run-codegen";
15
15
  import { watchAndRegenerate } from "../watch";
16
16
 
@@ -114,7 +114,8 @@ function mountInRunConfig(runConfigPath: string, name: string): boolean {
114
114
 
115
115
  // 1. Prepend import after the last existing import.
116
116
  const imports = sf.getImportDeclarations();
117
- const insertIndex = imports.length > 0 ? imports[imports.length - 1]!.getChildIndex() + 1 : 0;
117
+ const insertIndex =
118
+ imports.length > 0 ? (imports[imports.length - 1]?.getChildIndex() ?? 0) + 1 : 0;
118
119
  sf.insertImportDeclaration(insertIndex, {
119
120
  moduleSpecifier: `./features/${name}`,
120
121
  namedImports: [`${camel}Feature`],
@@ -12,7 +12,7 @@
12
12
  //
13
13
  // Bundle-Entity-Tables (configValuesTable, tenantTable, userTable,
14
14
  // userSessionTable, tenantMembershipsTable etc.) sind bewusst NICHT hier:
15
- // sie kommen über schema.generated.ts via buildDrizzleTable aus den
15
+ // sie kommen über schema.generated.ts via buildEntityTable aus den
16
16
  // r.entity()-Definitionen, das ist seit der entity.indexes-API die
17
17
  // Single-Source-of-Truth. Doppelte Re-Exports würden zwei pgTable-
18
18
  // Instances mit identischem Index-Namen erzeugen — drizzle-kit warnt.
@@ -13,66 +13,34 @@
13
13
  # Size: ~250 MB incl. drizzle-kit for the pre-deploy migrate step.
14
14
  # Build context: repository root (monorepo is used in the build stage).
15
15
 
16
- ARG BUN_VERSION=1.2.20
16
+ ARG BUN_VERSION=1.3.14
17
+ {{#hasPrivateGhPackages}}
18
+ # Private GH-Packages: der CI-Workflow injects GITHUB_TOKEN via --build-arg.
19
+ # Docker's multi-stage ARG scoping braucht die Re-declaration in jeder Stage
20
+ # die sie nutzt; das top-level ARG macht sie per `docker build` setzbar.
21
+ ARG GITHUB_TOKEN=
22
+ {{/hasPrivateGhPackages}}
17
23
  # Build identity — passed in by the CI workflow via --build-arg. Defaults
18
24
  # for local container builds.
19
25
  ARG BUILD_VERSION=dev
20
26
  ARG BUILD_TIME=unknown
21
- {{#hasPrivateGhPackages}}
22
- # Private @cosmicdriftgamestudio/* GH-Packages dep detected — yarn-4
23
- # reads the token via `npmAuthToken: "${GITHUB_TOKEN:-…}"` in .yarnrc.yml.
24
- # Without this build-arg, yarn install aborts with YN0041 anonymous-auth.
25
- ARG GITHUB_TOKEN=
26
- {{/hasPrivateGhPackages}}
27
27
 
28
28
  # ----- build: produces dist/ + dist-server/ ---------------------------------
29
- # Build base is node-alpine (not bun-alpine) because the repo uses yarn 4 as
30
- # its package manager — `link:./.kumiko` (@app/define codegen output) is
31
- # yarn-4's symlink protocol, which bun interprets differently. Node ships
32
- # corepack (yarn 4 via packageManager field); we pull bun via npm for
33
- # `bun run build`. Runtime stage stays pure bun.
34
- FROM node:20-alpine AS build
29
+ FROM oven/bun:${BUN_VERSION}-alpine AS build
30
+ WORKDIR /app
35
31
  {{#hasPrivateGhPackages}}
36
- # Multi-stage ARG inheritance: globals declared before FROM need an
37
- # `ARG <name>` line in every stage that uses them. Without this,
38
- # ${GITHUB_TOKEN} below resolves to empty and yarn install hits YN0041.
32
+ # GITHUB_TOKEN für @cosmicdriftgamestudio/*-Scope beim bun install.
39
33
  ARG GITHUB_TOKEN
34
+ ENV GITHUB_TOKEN=${GITHUB_TOKEN}
35
+ RUN bun config set --scope @cosmicdriftgamestudio token "${GITHUB_TOKEN}"
40
36
  {{/hasPrivateGhPackages}}
41
- WORKDIR /app
42
-
43
- RUN corepack enable && npm install -g bun@${BUN_VERSION}
44
37
 
45
38
  # Standalone-repo layout: @cosmicdrift/* pulled from NPM, no workspace:*
46
- # refs. Manifests first for Docker layer cache (yarn install only
39
+ # refs. Manifests first for Docker layer cache (bun install only
47
40
  # invalidates on dep change).
48
- COPY package.json yarn.lock .yarnrc.yml ./
41
+ COPY package.json bun.lock ./
49
42
 
50
- # YARN_ENABLE_INLINE_BUILDS=true: postinstall stdout/stderr inline in the
51
- # install output. Without this, yarn 4 hides logs in /tmp/xfs-*/build.log
52
- # (invisible in the Docker layer output) and the CI operator guesses why
53
- # the install failed.
54
- #
55
- # `link:./.kumiko` (@app/define in package.json): yarn 4 creates a
56
- # dangling symlink; install does NOT fail. `bun run build` below calls
57
- # `runCodegen` from @cosmicdrift/kumiko-dev-server (see
58
- # bin/kumiko-build.ts), which writes .kumiko/define.ts and turns the
59
- # symlink real before the bundle is built.
60
- ENV YARN_ENABLE_INLINE_BUILDS=true
61
- # Skip postinstall scripts for ALL deps in the build stage. Reason:
62
- # `bun build` bundles JS source only — no native bindings needed at bundle-
63
- # time. msgpackr-extract is the most common offender (ARM/CI native-build
64
- # failures), but the rule applies broadly: any native dep loaded at runtime
65
- # gets re-installed via `bun install --production` in the runtime stage,
66
- # which uses bun's own postinstall handling. Apps that needed per-package
67
- # opt-outs via `dependenciesMeta.<pkg>.built=false` in package.json (e.g.
68
- # studio, enterprise) can remove those entries after adopting this template.
69
- ENV YARN_ENABLE_SCRIPTS=false
70
- {{#hasPrivateGhPackages}}
71
- # Re-export GITHUB_TOKEN as env so yarn-4's `${GITHUB_TOKEN:-…}` expansion
72
- # in .yarnrc.yml finds it during the install step.
73
- ENV GITHUB_TOKEN=${GITHUB_TOKEN}
74
- {{/hasPrivateGhPackages}}
75
- RUN yarn install --immutable
43
+ RUN bun install --frozen-lockfile
76
44
 
77
45
  COPY . .
78
46
  RUN bun run build