@cosmicdrift/kumiko-dev-server 0.15.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
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>",
|
|
@@ -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
|
+
});
|
|
@@ -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
|
+
});
|
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"
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
|
|
42
42
|
import { readdirSync, statSync } from "node:fs";
|
|
43
43
|
import { join, relative, resolve, sep } from "node:path";
|
|
44
|
+
import { toKebab } from "@cosmicdrift/kumiko-framework/engine";
|
|
44
45
|
import {
|
|
45
46
|
type CallExpression,
|
|
46
47
|
type ImportDeclaration,
|
|
@@ -51,27 +52,6 @@ import {
|
|
|
51
52
|
SyntaxKind,
|
|
52
53
|
} from "ts-morph";
|
|
53
54
|
|
|
54
|
-
/**
|
|
55
|
-
* Replicates `packages/framework/src/engine/qualified-name.ts:toKebab`.
|
|
56
|
-
* Frame's r.defineEvent runs the feature-name + event-name through this
|
|
57
|
-
* helper before joining them — `defineFeature("driverOrders")` writes
|
|
58
|
-
* events under `driver-orders:event:...`. We MUST mirror the same
|
|
59
|
-
* transform here, otherwise the augmentation key drifts from the
|
|
60
|
-
* runtime event-type and strict-mode would catch a phantom mismatch.
|
|
61
|
-
*
|
|
62
|
-
* Inlined (instead of imported from framework) to keep the codegen
|
|
63
|
-
* package boundary clean — codegen doesn't depend on the runtime
|
|
64
|
-
* framework, only the framework-source-tree via paths-mapping at the
|
|
65
|
-
* caller's compile.
|
|
66
|
-
*/
|
|
67
|
-
function toKebab(input: string): string {
|
|
68
|
-
return input
|
|
69
|
-
.replace(/\./g, "-")
|
|
70
|
-
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2")
|
|
71
|
-
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
|
|
72
|
-
.toLowerCase();
|
|
73
|
-
}
|
|
74
|
-
|
|
75
55
|
export type ScannedEvent = {
|
|
76
56
|
/** Qualified event-name as it appears in the events table:
|
|
77
57
|
* `<feature>:event:<inner>`. The KumikoEventTypeMap key. */
|