@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
@@ -0,0 +1,240 @@
1
+ // scaffoldAppFeature — DX-2. Scaffolds a fresh feature inside an
2
+ // existing Kumiko-app workspace + auto-mounts it in src/run-config.ts.
3
+ //
4
+ // Sister to `scaffoldFeature` (which targets samples/recipes/ for the
5
+ // framework workspace). This one targets `src/features/<name>/` of an
6
+ // already-scaffolded app (output of `kumiko new app`).
7
+ //
8
+ // Auto-mount via ts-morph: opens src/run-config.ts, finds
9
+ // `export const APP_FEATURES = [...]`, prepends import + appends entry.
10
+ // User's promise "defineFeature → nichts woanders eintragen" is met
11
+ // for the run-config side. Drizzle FEATURE_IMPORT_REGISTRY is NOT
12
+ // touched here — DX-4 auto-discovery resolves that.
13
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
14
+ import { join, resolve } from "node:path";
15
+ import { Project, SyntaxKind } from "ts-morph";
16
+ import { isKebabSegment } from "./kebab.js";
17
+ export function scaffoldAppFeature(options) {
18
+ // Segment-strict guard: a trailing/double hyphen (`product-`, `foo--bar`)
19
+ // would make kebabToCamel produce an invalid identifier.
20
+ if (!isKebabSegment(options.name)) {
21
+ throw new Error(`scaffoldAppFeature: name must be kebab-case (a-z, 0-9, -); got "${options.name}"`);
22
+ }
23
+ const appRoot = resolve(options.appRoot ?? process.cwd());
24
+ const featureDir = join(appRoot, "src", "features", options.name);
25
+ if (existsSync(featureDir)) {
26
+ throw new Error(`scaffoldAppFeature: ${featureDir} already exists — refusing to overwrite`);
27
+ }
28
+ mkdirSync(featureDir, { recursive: true });
29
+ // Volle App-Feature-Konvention (Referenz: bundled-features/tenant):
30
+ // feature.ts = nur Registrierung, schema/ für Entities, i18n.ts für
31
+ // Server-Keys, web/ für die Client-Seite, __tests__/ mit Boot-Test.
32
+ // handlers/ + lib/ entstehen beim ersten echten Handler/Logik-Modul.
33
+ mkdirSync(join(featureDir, "schema"), { recursive: true });
34
+ mkdirSync(join(featureDir, "web"), { recursive: true });
35
+ mkdirSync(join(featureDir, "__tests__"), { recursive: true });
36
+ const files = [];
37
+ const write = (relative, content) => {
38
+ writeFileSync(join(featureDir, relative), content);
39
+ files.push(`src/features/${options.name}/${relative}`);
40
+ };
41
+ write("feature.ts", renderFeature(options.name));
42
+ write("index.ts", renderIndex(options.name));
43
+ write("constants.ts", renderConstants(options.name));
44
+ write("i18n.ts", renderI18n(options.name));
45
+ write(join("schema", `${options.name}-item.ts`), renderEntity(options.name));
46
+ write(join("schema", "index.ts"), renderSchemaIndex(options.name));
47
+ write(join("web", "index.ts"), renderWebIndex(options.name));
48
+ write(join("__tests__", "feature.boot.test.ts"), renderBootTest(options.name));
49
+ const runConfigPath = join(appRoot, "src", "run-config.ts");
50
+ let autoMounted = false;
51
+ if (existsSync(runConfigPath)) {
52
+ try {
53
+ autoMounted = mountInRunConfig(runConfigPath, options.name);
54
+ }
55
+ catch (err) {
56
+ // Roll back the freshly-written feature dir so a re-run isn't blocked
57
+ // by this function's own "already exists" guard. Without this, a
58
+ // shape-mismatch in run-config leaves feature.ts + index.ts on disk and
59
+ // the user is stuck having to hand-delete before retrying.
60
+ rmSync(featureDir, { recursive: true, force: true });
61
+ throw err;
62
+ }
63
+ }
64
+ return {
65
+ featureName: options.name,
66
+ featureDir,
67
+ files,
68
+ autoMounted,
69
+ };
70
+ }
71
+ function renderFeature(name) {
72
+ const camel = kebabToCamel(name);
73
+ return `// ${name} feature — scaffolded by \`kumiko add feature\`. Edit freely.
74
+ //
75
+ // Konvention (Referenz: bundled-features, z.B. tenant/):
76
+ // feature.ts — NUR Registrierung (r.entity / r.screen / r.queryHandler / …)
77
+ // schema/ — Entity-Definitionen
78
+ // handlers/ — eine Datei pro Handler (defineQueryHandler / defineWriteHandler)
79
+ // lib/ — reine Domain-Logik
80
+ // web/ — Client-Seite (index.ts, ein Screen pro Datei)
81
+ // i18n.ts — Server-Translation-Keys
82
+ //
83
+ // Doc-Pointer: https://docs.kumiko.rocks/en/patterns/ for the \`r.*\` API.
84
+
85
+ import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
86
+ import { ${constName(name)} } from "./constants";
87
+ import { ${camel}TranslationKeys } from "./i18n";
88
+ import { ${camel}ItemEntity } from "./schema";
89
+
90
+ export const ${camel}Feature = defineFeature(${constName(name)}, (r) => {
91
+ r.entity("${name}-item", ${camel}ItemEntity);
92
+
93
+ // Deklarativer List-Screen — CRUD-Query kommt aus der Entity.
94
+ // Für query-getriebene Tabellen: type "projectionList"; für
95
+ // Kennzahlen-Grids: type "dashboard".
96
+ r.screen({
97
+ id: "items",
98
+ type: "entityList",
99
+ entity: "${name}-item",
100
+ columns: ["title"],
101
+ defaultSort: { field: "title", dir: "asc" },
102
+ });
103
+
104
+ r.translations({ keys: ${camel}TranslationKeys });
105
+
106
+ r.nav({ id: "items", label: "screen:items.title", screen: "${name}:screen:items" });
107
+ });
108
+ `;
109
+ }
110
+ function renderIndex(name) {
111
+ const camel = kebabToCamel(name);
112
+ return `export { ${camel}Feature } from "./feature";\n`;
113
+ }
114
+ function renderConstants(name) {
115
+ return `export const ${constName(name)} = "${name}" as const;\n`;
116
+ }
117
+ function renderI18n(name) {
118
+ const camel = kebabToCamel(name);
119
+ return `// Server-seitige Translation-Keys (r.translations). Screen-Titel und
120
+ // Field-Labels folgen den Framework-Konventionen — der Boot-Validator
121
+ // prüft die Abdeckung.
122
+
123
+ export const ${camel}TranslationKeys = {
124
+ "screen:items.title": { de: "Einträge", en: "Items" },
125
+ "${name}:entity:${name}-item:field:title": { de: "Titel", en: "Title" },
126
+ };
127
+ `;
128
+ }
129
+ function renderEntity(name) {
130
+ const camel = kebabToCamel(name);
131
+ return `// Entity-Definition — Felder, Annotationen, Transitions.
132
+ // Starter: eine Text-Spalte. Ersetze sie durch deine Domain.
133
+ import { createEntity, createTextField } from "@cosmicdrift/kumiko-framework/engine";
134
+
135
+ export const ${camel}ItemEntity = createEntity({
136
+ fields: {
137
+ title: createTextField({
138
+ required: true,
139
+ sortable: true,
140
+ personal: false,
141
+ reason: "is_business_data",
142
+ }),
143
+ },
144
+ });
145
+ `;
146
+ }
147
+ function renderSchemaIndex(name) {
148
+ const camel = kebabToCamel(name);
149
+ return `export { ${camel}ItemEntity } from "./${name}-item";\n`;
150
+ }
151
+ function renderWebIndex(name) {
152
+ const camel = kebabToCamel(name);
153
+ return `// @runtime client
154
+ // Client-Seite des Features: translations, components (custom-Screens),
155
+ // columnRenderers. In src/app/client.tsx bei
156
+ // createKumikoApp({ clientFeatures: [...] }) einhängen — das passiert
157
+ // NICHT automatisch. Screens: eine Datei pro Screen in diesem Ordner.
158
+
159
+ import type { ClientFeatureDefinition } from "@cosmicdrift/kumiko-renderer-web";
160
+
161
+ export const ${camel}Client: ClientFeatureDefinition = {
162
+ name: "${name}",
163
+ };
164
+ `;
165
+ }
166
+ function renderBootTest(name) {
167
+ const camel = kebabToCamel(name);
168
+ return `import { describe, expect, test } from "bun:test";
169
+ import { validateBoot } from "@cosmicdrift/kumiko-framework/engine";
170
+ import { ${camel}Feature } from "../feature";
171
+
172
+ describe("${name} feature", () => {
173
+ test("boots: Entity, Screens und i18n-Coverage validieren", () => {
174
+ expect(() => validateBoot([${camel}Feature])).not.toThrow();
175
+ });
176
+ });
177
+ `;
178
+ }
179
+ function kebabToCamel(name) {
180
+ return name.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
181
+ }
182
+ function constName(name) {
183
+ return `${name.replace(/-/g, "_").toUpperCase()}_FEATURE`;
184
+ }
185
+ // ts-morph: open run-config, prepend import, append APP_FEATURES entry.
186
+ // Returns true on success, throws on shape-mismatch — the caller rolls back
187
+ // the scaffolded feature dir and re-throws so a re-run isn't blocked.
188
+ function mountInRunConfig(runConfigPath, name) {
189
+ const camel = kebabToCamel(name);
190
+ const project = new Project({
191
+ skipAddingFilesFromTsConfig: true,
192
+ skipFileDependencyResolution: true,
193
+ });
194
+ const sf = project.addSourceFileAtPath(runConfigPath);
195
+ // Import and APP_FEATURES entry are checked independently so a half-applied
196
+ // state self-heals: if the import exists but the entry was hand-removed (or
197
+ // vice versa), re-running adds only the missing half instead of short-
198
+ // circuiting on the import alone and leaving the feature unmounted.
199
+ let changed = false;
200
+ // 1. Prepend import after the last existing import — only if absent.
201
+ if (!sf.getImportDeclaration(`./features/${name}`)) {
202
+ const imports = sf.getImportDeclarations();
203
+ const insertIndex = imports.length > 0 ? (imports[imports.length - 1]?.getChildIndex() ?? 0) + 1 : 0;
204
+ sf.insertImportDeclaration(insertIndex, {
205
+ moduleSpecifier: `./features/${name}`,
206
+ namedImports: [`${camel}Feature`],
207
+ });
208
+ changed = true;
209
+ }
210
+ // 2. Find `export const APP_FEATURES = [...]` and append the entry — only if absent.
211
+ const appFeaturesDecl = sf.getVariableDeclaration("APP_FEATURES");
212
+ if (!appFeaturesDecl) {
213
+ throw new Error(`mountInRunConfig: ${runConfigPath} has no 'APP_FEATURES' declaration — ` +
214
+ `cannot auto-mount. Hand-edit: add '${camel}Feature' to APP_FEATURES.`);
215
+ }
216
+ const initializer = appFeaturesDecl.getInitializerIfKind(SyntaxKind.AsExpression) ??
217
+ appFeaturesDecl.getInitializer();
218
+ if (!initializer) {
219
+ throw new Error(`mountInRunConfig: APP_FEATURES has no initializer — cannot auto-mount.`);
220
+ }
221
+ // Strip `as const` wrapper if present.
222
+ const arr = initializer.getKind() === SyntaxKind.AsExpression
223
+ ? initializer.getFirstChildByKind(SyntaxKind.ArrayLiteralExpression)
224
+ : initializer.asKind(SyntaxKind.ArrayLiteralExpression);
225
+ if (!arr) {
226
+ throw new Error(`mountInRunConfig: APP_FEATURES is not an array literal — cannot auto-mount.`);
227
+ }
228
+ const alreadyListed = arr.getElements().some((el) => el.getText() === `${camel}Feature`);
229
+ if (!alreadyListed) {
230
+ arr.addElement(`${camel}Feature`);
231
+ changed = true;
232
+ }
233
+ if (changed)
234
+ sf.saveSync();
235
+ return true;
236
+ }
237
+ // Re-export so consumers can hint at the file (e.g. for kumiko-cli output).
238
+ export function runConfigPathForApp(appRoot) {
239
+ return join(appRoot, "src", "run-config.ts");
240
+ }
@@ -0,0 +1,45 @@
1
+ export type ScaffoldFeatureEntry = {
2
+ readonly name: string;
3
+ readonly importPath: string;
4
+ readonly exportName: string;
5
+ readonly callExpression: string;
6
+ /** Runtime args for factory-style exports that need config the codegen text can't parse back out (e.g. `{ scopes: {} }`). Omit for zero-arg factories and object-style exports. */
7
+ readonly callArgs?: readonly unknown[];
8
+ };
9
+ export type ScaffoldTestSetup = {
10
+ readonly files: Readonly<Record<string, string>>;
11
+ readonly scripts: Readonly<Record<string, string>>;
12
+ readonly devDependencies: Readonly<Record<string, string>>;
13
+ readonly rulesMarkdown: string;
14
+ };
15
+ export type ScaffoldTestSetupFactory = (input: {
16
+ readonly appName: string;
17
+ readonly frameworkVersion: string;
18
+ }) => ScaffoldTestSetup;
19
+ export type ScaffoldAppOptions = {
20
+ /** kebab-case app name (e.g. "my-shop"). Becomes package-name + folder. */
21
+ readonly name: string;
22
+ /** Absolute or cwd-relative target dir. Default: <cwd>/<name>. */
23
+ readonly destination?: string;
24
+ /** Base dir a relative `destination` (or the name-default) resolves
25
+ * against. Defaults to process.cwd(). Callers with their own cwd-notion
26
+ * (the CLI's ctx.cwd) MUST pass it so the scaffold lands where the
27
+ * command's output claims it does. */
28
+ readonly cwd?: string;
29
+ /** npm-version-pin for @cosmicdrift/* deps. Default "*" for latest. */
30
+ readonly frameworkVersion?: string;
31
+ /** Bundled-features to mount in run-config.ts. Default: secrets + auth-foundation + sessions
32
+ * (the historical foundation). create-kumiko-app passes the picker output
33
+ * here so the generated APP_FEATURES reflects the user's selection. */
34
+ readonly features?: ReadonlyArray<ScaffoldFeatureEntry>;
35
+ /** Test wiring (bunfigs, scripts, example tests, e2e) — `renderTestSetup` from
36
+ * @cosmicdrift/kumiko-testing/scaffold. Without it the app gets the legacy
37
+ * bunfig pair and no example tests. */
38
+ readonly testSetup?: ScaffoldTestSetupFactory;
39
+ };
40
+ export type ScaffoldAppResult = {
41
+ readonly destination: string;
42
+ readonly files: readonly string[];
43
+ readonly appName: string;
44
+ };
45
+ export declare function scaffoldApp(options: ScaffoldAppOptions): Promise<ScaffoldAppResult>;