@cosmicdrift/kumiko-dev-server 0.14.0 → 0.16.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/package.json +8 -12
- package/src/__tests__/build-prod-bundle.integration.ts +1 -1
- package/src/__tests__/build-prod-bundle.test.ts +1 -1
- package/src/__tests__/cache-headers.test.ts +1 -1
- package/src/__tests__/classify-change.test.ts +1 -1
- package/src/__tests__/compose-features-wiring.integration.ts +10 -9
- package/src/__tests__/compose-features.test.ts +1 -1
- package/src/__tests__/config-seed-boot.integration.ts +5 -4
- package/src/__tests__/crash-tracker.test.ts +1 -1
- package/src/__tests__/create-kumiko-server.integration.ts +5 -5
- package/src/__tests__/discover-format.test.ts +32 -0
- package/src/__tests__/env-schema.integration.ts +1 -1
- package/src/__tests__/env-schema.test.ts +1 -1
- package/src/__tests__/few-shot-corpus.test.ts +1 -1
- package/src/__tests__/inject-schema.test.ts +1 -1
- package/src/__tests__/resolve-stylesheet.test.ts +11 -7
- package/src/__tests__/resolve-tailwind-cli.test.ts +1 -1
- package/src/__tests__/run-prod-app-spec.test.ts +1 -1
- package/src/__tests__/run-prod-app.integration.ts +6 -6
- package/src/__tests__/scaffold-app-feature.test.ts +1 -1
- package/src/__tests__/scaffold-app.test.ts +1 -1
- package/src/__tests__/scaffold-deploy.test.ts +1 -1
- package/src/__tests__/scaffold-feature.test.ts +1 -1
- package/src/__tests__/try-hono-first.test.ts +1 -1
- package/src/__tests__/walkthrough.integration.ts +1 -1
- package/src/codegen/__tests__/render-codegen.test.ts +50 -0
- package/src/codegen/__tests__/run-codegen.test.ts +1 -1
- package/src/codegen/__tests__/strict-mode-diagnostics.test.ts +1 -1
- package/src/codegen/__tests__/watch.test.ts +1 -1
- package/src/codegen/render.ts +7 -2
- package/src/codegen/run-codegen.ts +1 -1
- package/src/codegen/scan-events.ts +1 -21
- package/src/scaffold-app-feature.ts +2 -1
- package/src/{drizzle-tables-auth-mode.ts → schema-tables-auth-mode.ts} +1 -1
- package/templates/deploy/Dockerfile.template +15 -47
- package/CHANGELOG.md +0 -656
- package/src/drizzle-config.ts +0 -44
- /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.
|
|
3
|
+
"version": "0.16.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
|
-
"./
|
|
34
|
-
"types": "./src/
|
|
35
|
-
"default": "./src/
|
|
33
|
+
"./schema-tables-auth-mode": {
|
|
34
|
+
"types": "./src/schema-tables-auth-mode.ts",
|
|
35
|
+
"default": "./src/schema-tables-auth-mode.ts"
|
|
36
36
|
},
|
|
37
|
-
"./
|
|
38
|
-
"types": "./src/
|
|
39
|
-
"default": "./src/
|
|
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 "
|
|
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 "
|
|
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
|
|
155
|
-
await suite.stack.db
|
|
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
|
|
229
|
-
await suite.stack.db
|
|
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
|
|
275
|
-
await suite.stack.db
|
|
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
|
|
329
|
-
await suite.stack.db
|
|
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
|
|
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
|
|
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
|
|
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 "
|
|
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.
|
|
54
|
-
|
|
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 () => {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { formatBuildResult } from "../build-prod-bundle";
|
|
6
|
+
import { discoverServerEntry } from "../build-server-bundle";
|
|
7
|
+
|
|
8
|
+
describe("discoverServerEntry", () => {
|
|
9
|
+
test("finds bin/main.ts when present", () => {
|
|
10
|
+
const dir = mkdtempSync(join(tmpdir(), "kumiko-discover-"));
|
|
11
|
+
mkdirSync(join(dir, "bin"));
|
|
12
|
+
writeFileSync(join(dir, "bin/main.ts"), "export {};\n", "utf8");
|
|
13
|
+
expect(discoverServerEntry(dir)).toBe(join(dir, "bin/main.ts"));
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("returns undefined when no entry exists", () => {
|
|
17
|
+
const dir = mkdtempSync(join(tmpdir(), "kumiko-discover-empty-"));
|
|
18
|
+
expect(discoverServerEntry(dir)).toBeUndefined();
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe("formatBuildResult", () => {
|
|
23
|
+
test("includes outDir and manifest entries", () => {
|
|
24
|
+
const out = formatBuildResult(
|
|
25
|
+
{ outDir: "dist/client", manifest: { "app.js": "app.abc123.js" } },
|
|
26
|
+
42,
|
|
27
|
+
);
|
|
28
|
+
expect(out).toContain("dist/client");
|
|
29
|
+
expect(out).toContain("app.js");
|
|
30
|
+
expect(out).toContain("42ms");
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -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 "
|
|
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
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
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
|
-
|
|
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 "
|
|
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.
|
|
469
|
-
if (existing.length > 0) return;
|
|
470
|
-
await db.
|
|
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 "
|
|
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";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { renderDefineFile, renderInlineSchemasFile, renderTypesAugmentation } from "../render";
|
|
6
|
+
import type { ScannedEvent } from "../scan-events";
|
|
7
|
+
|
|
8
|
+
describe("renderTypesAugmentation", () => {
|
|
9
|
+
test("emits empty augmentation when no events", () => {
|
|
10
|
+
const out = renderTypesAugmentation([], "/tmp/app/.kumiko");
|
|
11
|
+
expect(out).toContain("interface KumikoEventTypeMap");
|
|
12
|
+
expect(out).toContain("no r.defineEvent calls discovered yet");
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe("renderInlineSchemasFile", () => {
|
|
17
|
+
test("returns undefined when no inline schemas", () => {
|
|
18
|
+
expect(renderInlineSchemasFile([], "/tmp/app")).toBeUndefined();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("uses app-root-relative paths in source comments", () => {
|
|
22
|
+
const appRoot = mkdtempSync(join(tmpdir(), "kumiko-codegen-"));
|
|
23
|
+
const featurePath = join(appRoot, "src", "feature.ts");
|
|
24
|
+
mkdirSync(join(appRoot, "src"), { recursive: true });
|
|
25
|
+
writeFileSync(featurePath, "// stub", "utf-8");
|
|
26
|
+
const events: ScannedEvent[] = [
|
|
27
|
+
{
|
|
28
|
+
qualifiedName: "inventory:event:product-archived",
|
|
29
|
+
schemaSource: {
|
|
30
|
+
kind: "inline",
|
|
31
|
+
schemaSource: "z.object({ reason: z.string() })",
|
|
32
|
+
generatedConstName: "_kg_inventory__productArchived",
|
|
33
|
+
},
|
|
34
|
+
featureFilePath: featurePath,
|
|
35
|
+
source: { file: featurePath, line: 92 },
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
const out = renderInlineSchemasFile(events, appRoot);
|
|
39
|
+
expect(out).toContain("// inventory:event:product-archived — from src/feature.ts:92");
|
|
40
|
+
expect(out).not.toContain(appRoot);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe("renderDefineFile", () => {
|
|
45
|
+
test("emits defineWriteHandler wrapper with type reference", () => {
|
|
46
|
+
const out = renderDefineFile();
|
|
47
|
+
expect(out).toContain("defineWriteHandler");
|
|
48
|
+
expect(out).toContain("types.generated.d.ts");
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -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
|
|
package/src/codegen/render.ts
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
// actual change, so mtime doesn't tick and the TS language server
|
|
23
23
|
// doesn't reload every 100ms.
|
|
24
24
|
|
|
25
|
+
import { relative } from "node:path";
|
|
25
26
|
import type { ScannedEvent } from "./scan-events";
|
|
26
27
|
import { rewriteImportPath } from "./scan-events";
|
|
27
28
|
|
|
@@ -115,7 +116,10 @@ export function renderTypesAugmentation(
|
|
|
115
116
|
* the schema. Returns undefined when no inline-schemas exist (so the
|
|
116
117
|
* runner can skip writing the file entirely).
|
|
117
118
|
*/
|
|
118
|
-
export function renderInlineSchemasFile(
|
|
119
|
+
export function renderInlineSchemasFile(
|
|
120
|
+
events: readonly ScannedEvent[],
|
|
121
|
+
appRootAbs: string,
|
|
122
|
+
): string | undefined {
|
|
119
123
|
const inlines = events.filter((ev) => ev.schemaSource.kind === "inline");
|
|
120
124
|
if (inlines.length === 0) return undefined;
|
|
121
125
|
|
|
@@ -139,8 +143,9 @@ export function renderInlineSchemasFile(events: readonly ScannedEvent[]): string
|
|
|
139
143
|
});
|
|
140
144
|
for (const ev of sorted) {
|
|
141
145
|
if (ev.schemaSource.kind !== "inline") continue;
|
|
146
|
+
const sourcePath = relative(appRootAbs, ev.featureFilePath).split("\\").join("/");
|
|
142
147
|
lines.push(
|
|
143
|
-
`// ${ev.qualifiedName} — from ${
|
|
148
|
+
`// ${ev.qualifiedName} — from ${sourcePath}:${ev.source.line}`,
|
|
144
149
|
`export const ${ev.schemaSource.generatedConstName} = ${ev.schemaSource.schemaSource};`,
|
|
145
150
|
"",
|
|
146
151
|
);
|
|
@@ -68,7 +68,7 @@ export function runCodegen(opts: CodegenOptions): CodegenResult {
|
|
|
68
68
|
|
|
69
69
|
const typesContent = renderTypesAugmentation(scan.events, outputDir);
|
|
70
70
|
const defineContent = renderDefineFile();
|
|
71
|
-
const schemasContent = renderInlineSchemasFile(scan.events);
|
|
71
|
+
const schemasContent = renderInlineSchemasFile(scan.events, opts.appRoot);
|
|
72
72
|
// package.json — turns `.kumiko/` into a real installable package
|
|
73
73
|
// named `@app/define`. Apps that declare
|
|
74
74
|
// "@app/define": "link:./.kumiko"
|