@cosmicdrift/kumiko-dev-server 0.329.0 → 0.330.2
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/README.md +17 -0
- package/bin/kumiko-build.ts +3 -2
- package/bin/kumiko-dev.ts +1 -1
- package/bin/kumiko-init-deploy.ts +1 -1
- package/bin/kumiko-schema-check.ts +4 -1
- package/dist/build-server-bundle.d.ts +31 -0
- package/dist/build-server-bundle.js +282 -0
- package/dist/build.d.ts +2 -0
- package/dist/build.js +5 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +9 -0
- package/dist/codegen/index.d.ts +4 -0
- package/dist/codegen/index.js +4 -0
- package/dist/codegen/render.d.ts +55 -0
- package/dist/codegen/render.js +273 -0
- package/dist/codegen/run-codegen.d.ts +20 -0
- package/dist/codegen/run-codegen.js +219 -0
- package/dist/codegen/scan-events.d.ts +76 -0
- package/dist/codegen/scan-events.js +447 -0
- package/dist/codegen/watch.d.ts +33 -0
- package/dist/codegen/watch.js +112 -0
- package/dist/compose-stacks.d.ts +60 -0
- package/dist/compose-stacks.js +127 -0
- package/dist/crash-tracker.d.ts +17 -0
- package/dist/crash-tracker.js +37 -0
- package/dist/create-kumiko-server.d.ts +195 -0
- package/dist/create-kumiko-server.js +1040 -0
- package/dist/env-schema.d.ts +15 -0
- package/dist/env-schema.js +60 -0
- package/dist/few-shot-corpus.d.ts +72 -0
- package/dist/few-shot-corpus.js +262 -0
- package/dist/framework-version.d.ts +1 -0
- package/dist/framework-version.js +31 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +33 -0
- package/dist/init-deploy-cli.d.ts +10 -0
- package/dist/init-deploy-cli.js +111 -0
- package/dist/kebab.d.ts +1 -0
- package/{src/kebab.ts → dist/kebab.js} +2 -3
- package/dist/run-dev-app.d.ts +174 -0
- package/dist/run-dev-app.js +403 -0
- package/dist/scaffold-app-feature.d.ts +16 -0
- package/dist/scaffold-app-feature.js +240 -0
- package/dist/scaffold-app.d.ts +45 -0
- package/dist/scaffold-app.js +852 -0
- package/dist/scaffold-demo-tasks.d.ts +8 -0
- package/{src/scaffold-demo-tasks.ts → dist/scaffold-demo-tasks.js} +69 -98
- package/dist/scaffold-deploy.d.ts +92 -0
- package/dist/scaffold-deploy.js +254 -0
- package/dist/scaffold-feature.d.ts +26 -0
- package/dist/scaffold-feature.js +163 -0
- package/dist/schema-apply.d.ts +11 -0
- package/dist/schema-apply.js +95 -0
- package/dist/schema-check-core.d.ts +2 -0
- package/{src/schema-check-core.ts → dist/schema-check-core.js} +8 -10
- package/dist/schema-tables-auth-mode.d.ts +2 -0
- package/{src/schema-tables-auth-mode.ts → dist/schema-tables-auth-mode.js} +2 -10
- package/dist/schema-tables-minimal.d.ts +2 -0
- package/{src/schema-tables-minimal.ts → dist/schema-tables-minimal.js} +2 -11
- package/dist/setup-test-stack-from-features.d.ts +11 -0
- package/dist/setup-test-stack-from-features.js +42 -0
- package/dist/welcome-banner.d.ts +15 -0
- package/dist/welcome-banner.js +27 -0
- package/package.json +71 -24
- package/src/changes.json +6 -0
- package/src/__tests__/build-prod-bundle.integration.test.ts +0 -282
- package/src/__tests__/build-server-bundle.test.ts +0 -228
- package/src/__tests__/classify-change.test.ts +0 -87
- package/src/__tests__/compose-stacks.test.ts +0 -273
- package/src/__tests__/config-resolver-default.integration.test.ts +0 -165
- package/src/__tests__/crash-tracker.test.ts +0 -89
- package/src/__tests__/create-kumiko-server-errors.test.ts +0 -54
- package/src/__tests__/create-kumiko-server-options.test.ts +0 -33
- package/src/__tests__/create-kumiko-server.integration.test.ts +0 -1007
- package/src/__tests__/discover-format.test.ts +0 -32
- package/src/__tests__/env-schema.integration.test.ts +0 -248
- package/src/__tests__/env-schema.test.ts +0 -132
- package/src/__tests__/few-shot-corpus.test.ts +0 -316
- package/src/__tests__/framework-version.test.ts +0 -45
- package/src/__tests__/init-deploy-cli.test.ts +0 -113
- package/src/__tests__/kumiko-schema-check.test.ts +0 -56
- package/src/__tests__/merge-extra-context.test.ts +0 -67
- package/src/__tests__/resolve-stylesheet.test.ts +0 -132
- package/src/__tests__/run-dev-app-boot-validation.test.ts +0 -38
- package/src/__tests__/run-dev-app.integration.test.ts +0 -290
- package/src/__tests__/runtime-classification.test.ts +0 -26
- package/src/__tests__/saas-identity-wire.integration.test.ts +0 -427
- package/src/__tests__/scaffold-app-feature.test.ts +0 -216
- package/src/__tests__/scaffold-app.test.ts +0 -630
- package/src/__tests__/scaffold-demo-tasks.test.ts +0 -47
- package/src/__tests__/scaffold-deploy.test.ts +0 -464
- package/src/__tests__/scaffold-dev-cycle.integration.test.ts +0 -109
- package/src/__tests__/scaffold-feature.test.ts +0 -143
- package/src/__tests__/schema-apply.integration.test.ts +0 -191
- package/src/__tests__/setup-test-stack-from-features.integration.test.ts +0 -30
- package/src/__tests__/setup-test-stack-job-run-logger.integration.test.ts +0 -72
- package/src/__tests__/walkthrough.integration.test.ts +0 -140
- package/src/__tests__/welcome-banner.test.ts +0 -42
- package/src/build-server-bundle.ts +0 -346
- package/src/build.ts +0 -28
- package/src/codegen/__tests__/render-codegen.test.ts +0 -100
- package/src/codegen/__tests__/run-codegen.test.ts +0 -665
- package/src/codegen/__tests__/strict-mode-diagnostics.test.ts +0 -347
- package/src/codegen/__tests__/watch.test.ts +0 -222
- package/src/codegen/index.ts +0 -19
- package/src/codegen/render.ts +0 -305
- package/src/codegen/run-codegen.ts +0 -278
- package/src/codegen/scan-events.ts +0 -565
- package/src/codegen/watch.ts +0 -140
- package/src/compose-stacks.ts +0 -193
- package/src/crash-tracker.ts +0 -56
- package/src/create-kumiko-server.ts +0 -1397
- package/src/env-schema.ts +0 -72
- package/src/few-shot-corpus.ts +0 -370
- package/src/framework-version.ts +0 -39
- package/src/index.ts +0 -105
- package/src/init-deploy-cli.ts +0 -148
- package/src/run-dev-app.ts +0 -694
- package/src/scaffold-app-feature.ts +0 -283
- package/src/scaffold-app.ts +0 -1033
- package/src/scaffold-deploy.ts +0 -393
- package/src/scaffold-feature.ts +0 -226
- package/src/schema-apply.ts +0 -123
- package/src/setup-test-stack-from-features.ts +0 -80
- package/src/welcome-banner.ts +0 -43
|
@@ -1,283 +0,0 @@
|
|
|
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
|
-
|
|
14
|
-
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
15
|
-
import { join, resolve } from "node:path";
|
|
16
|
-
import { Project, SyntaxKind } from "ts-morph";
|
|
17
|
-
import { isKebabSegment } from "./kebab";
|
|
18
|
-
|
|
19
|
-
export type ScaffoldAppFeatureOptions = {
|
|
20
|
-
/** kebab-case feature name (e.g. "product-catalog"). */
|
|
21
|
-
readonly name: string;
|
|
22
|
-
/** App workspace root. Defaults to cwd. */
|
|
23
|
-
readonly appRoot?: string;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export type ScaffoldAppFeatureResult = {
|
|
27
|
-
readonly featureName: string;
|
|
28
|
-
readonly featureDir: string;
|
|
29
|
-
readonly files: readonly string[];
|
|
30
|
-
/** Whether src/run-config.ts was auto-mounted. False if run-config
|
|
31
|
-
* is missing — caller gets the scaffolded files but must hand-mount. */
|
|
32
|
-
readonly autoMounted: boolean;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export function scaffoldAppFeature(options: ScaffoldAppFeatureOptions): ScaffoldAppFeatureResult {
|
|
36
|
-
// Segment-strict guard: a trailing/double hyphen (`product-`, `foo--bar`)
|
|
37
|
-
// would make kebabToCamel produce an invalid identifier.
|
|
38
|
-
if (!isKebabSegment(options.name)) {
|
|
39
|
-
throw new Error(
|
|
40
|
-
`scaffoldAppFeature: name must be kebab-case (a-z, 0-9, -); got "${options.name}"`,
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
const appRoot = resolve(options.appRoot ?? process.cwd());
|
|
44
|
-
const featureDir = join(appRoot, "src", "features", options.name);
|
|
45
|
-
if (existsSync(featureDir)) {
|
|
46
|
-
throw new Error(`scaffoldAppFeature: ${featureDir} already exists — refusing to overwrite`);
|
|
47
|
-
}
|
|
48
|
-
mkdirSync(featureDir, { recursive: true });
|
|
49
|
-
|
|
50
|
-
// Volle App-Feature-Konvention (Referenz: bundled-features/tenant):
|
|
51
|
-
// feature.ts = nur Registrierung, schema/ für Entities, i18n.ts für
|
|
52
|
-
// Server-Keys, web/ für die Client-Seite, __tests__/ mit Boot-Test.
|
|
53
|
-
// handlers/ + lib/ entstehen beim ersten echten Handler/Logik-Modul.
|
|
54
|
-
mkdirSync(join(featureDir, "schema"), { recursive: true });
|
|
55
|
-
mkdirSync(join(featureDir, "web"), { recursive: true });
|
|
56
|
-
mkdirSync(join(featureDir, "__tests__"), { recursive: true });
|
|
57
|
-
|
|
58
|
-
const files: string[] = [];
|
|
59
|
-
const write = (relative: string, content: string): void => {
|
|
60
|
-
writeFileSync(join(featureDir, relative), content);
|
|
61
|
-
files.push(`src/features/${options.name}/${relative}`);
|
|
62
|
-
};
|
|
63
|
-
write("feature.ts", renderFeature(options.name));
|
|
64
|
-
write("index.ts", renderIndex(options.name));
|
|
65
|
-
write("constants.ts", renderConstants(options.name));
|
|
66
|
-
write("i18n.ts", renderI18n(options.name));
|
|
67
|
-
write(join("schema", `${options.name}-item.ts`), renderEntity(options.name));
|
|
68
|
-
write(join("schema", "index.ts"), renderSchemaIndex(options.name));
|
|
69
|
-
write(join("web", "index.ts"), renderWebIndex(options.name));
|
|
70
|
-
write(join("__tests__", "feature.boot.test.ts"), renderBootTest(options.name));
|
|
71
|
-
|
|
72
|
-
const runConfigPath = join(appRoot, "src", "run-config.ts");
|
|
73
|
-
let autoMounted = false;
|
|
74
|
-
if (existsSync(runConfigPath)) {
|
|
75
|
-
try {
|
|
76
|
-
autoMounted = mountInRunConfig(runConfigPath, options.name);
|
|
77
|
-
} catch (err) {
|
|
78
|
-
// Roll back the freshly-written feature dir so a re-run isn't blocked
|
|
79
|
-
// by this function's own "already exists" guard. Without this, a
|
|
80
|
-
// shape-mismatch in run-config leaves feature.ts + index.ts on disk and
|
|
81
|
-
// the user is stuck having to hand-delete before retrying.
|
|
82
|
-
rmSync(featureDir, { recursive: true, force: true });
|
|
83
|
-
throw err;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
featureName: options.name,
|
|
89
|
-
featureDir,
|
|
90
|
-
files,
|
|
91
|
-
autoMounted,
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function renderFeature(name: string): string {
|
|
96
|
-
const camel = kebabToCamel(name);
|
|
97
|
-
return `// ${name} feature — scaffolded by \`kumiko add feature\`. Edit freely.
|
|
98
|
-
//
|
|
99
|
-
// Konvention (Referenz: bundled-features, z.B. tenant/):
|
|
100
|
-
// feature.ts — NUR Registrierung (r.entity / r.screen / r.queryHandler / …)
|
|
101
|
-
// schema/ — Entity-Definitionen
|
|
102
|
-
// handlers/ — eine Datei pro Handler (defineQueryHandler / defineWriteHandler)
|
|
103
|
-
// lib/ — reine Domain-Logik
|
|
104
|
-
// web/ — Client-Seite (index.ts, ein Screen pro Datei)
|
|
105
|
-
// i18n.ts — Server-Translation-Keys
|
|
106
|
-
//
|
|
107
|
-
// Doc-Pointer: https://docs.kumiko.rocks/en/patterns/ for the \`r.*\` API.
|
|
108
|
-
|
|
109
|
-
import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
|
|
110
|
-
import { ${constName(name)} } from "./constants";
|
|
111
|
-
import { ${camel}TranslationKeys } from "./i18n";
|
|
112
|
-
import { ${camel}ItemEntity } from "./schema";
|
|
113
|
-
|
|
114
|
-
export const ${camel}Feature = defineFeature(${constName(name)}, (r) => {
|
|
115
|
-
r.entity("${name}-item", ${camel}ItemEntity);
|
|
116
|
-
|
|
117
|
-
// Deklarativer List-Screen — CRUD-Query kommt aus der Entity.
|
|
118
|
-
// Für query-getriebene Tabellen: type "projectionList"; für
|
|
119
|
-
// Kennzahlen-Grids: type "dashboard".
|
|
120
|
-
r.screen({
|
|
121
|
-
id: "items",
|
|
122
|
-
type: "entityList",
|
|
123
|
-
entity: "${name}-item",
|
|
124
|
-
columns: ["title"],
|
|
125
|
-
defaultSort: { field: "title", dir: "asc" },
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
r.translations({ keys: ${camel}TranslationKeys });
|
|
129
|
-
|
|
130
|
-
r.nav({ id: "items", label: "screen:items.title", screen: "${name}:screen:items" });
|
|
131
|
-
});
|
|
132
|
-
`;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function renderIndex(name: string): string {
|
|
136
|
-
const camel = kebabToCamel(name);
|
|
137
|
-
return `export { ${camel}Feature } from "./feature";\n`;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function renderConstants(name: string): string {
|
|
141
|
-
return `export const ${constName(name)} = "${name}" as const;\n`;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function renderI18n(name: string): string {
|
|
145
|
-
const camel = kebabToCamel(name);
|
|
146
|
-
return `// Server-seitige Translation-Keys (r.translations). Screen-Titel und
|
|
147
|
-
// Field-Labels folgen den Framework-Konventionen — der Boot-Validator
|
|
148
|
-
// prüft die Abdeckung.
|
|
149
|
-
|
|
150
|
-
export const ${camel}TranslationKeys = {
|
|
151
|
-
"screen:items.title": { de: "Einträge", en: "Items" },
|
|
152
|
-
"${name}:entity:${name}-item:field:title": { de: "Titel", en: "Title" },
|
|
153
|
-
};
|
|
154
|
-
`;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function renderEntity(name: string): string {
|
|
158
|
-
const camel = kebabToCamel(name);
|
|
159
|
-
return `// Entity-Definition — Felder, Annotationen, Transitions.
|
|
160
|
-
// Starter: eine Text-Spalte. Ersetze sie durch deine Domain.
|
|
161
|
-
import { createEntity, createTextField } from "@cosmicdrift/kumiko-framework/engine";
|
|
162
|
-
|
|
163
|
-
export const ${camel}ItemEntity = createEntity({
|
|
164
|
-
fields: {
|
|
165
|
-
title: createTextField({
|
|
166
|
-
required: true,
|
|
167
|
-
sortable: true,
|
|
168
|
-
personal: false,
|
|
169
|
-
reason: "is_business_data",
|
|
170
|
-
}),
|
|
171
|
-
},
|
|
172
|
-
});
|
|
173
|
-
`;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function renderSchemaIndex(name: string): string {
|
|
177
|
-
const camel = kebabToCamel(name);
|
|
178
|
-
return `export { ${camel}ItemEntity } from "./${name}-item";\n`;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function renderWebIndex(name: string): string {
|
|
182
|
-
const camel = kebabToCamel(name);
|
|
183
|
-
return `// @runtime client
|
|
184
|
-
// Client-Seite des Features: translations, components (custom-Screens),
|
|
185
|
-
// columnRenderers. In src/app/client.tsx bei
|
|
186
|
-
// createKumikoApp({ clientFeatures: [...] }) einhängen — das passiert
|
|
187
|
-
// NICHT automatisch. Screens: eine Datei pro Screen in diesem Ordner.
|
|
188
|
-
|
|
189
|
-
import type { ClientFeatureDefinition } from "@cosmicdrift/kumiko-renderer-web";
|
|
190
|
-
|
|
191
|
-
export const ${camel}Client: ClientFeatureDefinition = {
|
|
192
|
-
name: "${name}",
|
|
193
|
-
};
|
|
194
|
-
`;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function renderBootTest(name: string): string {
|
|
198
|
-
const camel = kebabToCamel(name);
|
|
199
|
-
return `import { describe, expect, test } from "bun:test";
|
|
200
|
-
import { validateBoot } from "@cosmicdrift/kumiko-framework/engine";
|
|
201
|
-
import { ${camel}Feature } from "../feature";
|
|
202
|
-
|
|
203
|
-
describe("${name} feature", () => {
|
|
204
|
-
test("boots: Entity, Screens und i18n-Coverage validieren", () => {
|
|
205
|
-
expect(() => validateBoot([${camel}Feature])).not.toThrow();
|
|
206
|
-
});
|
|
207
|
-
});
|
|
208
|
-
`;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function kebabToCamel(name: string): string {
|
|
212
|
-
return name.replace(/-([a-z0-9])/g, (_, c: string) => c.toUpperCase());
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function constName(name: string): string {
|
|
216
|
-
return `${name.replace(/-/g, "_").toUpperCase()}_FEATURE`;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// ts-morph: open run-config, prepend import, append APP_FEATURES entry.
|
|
220
|
-
// Returns true on success, throws on shape-mismatch — the caller rolls back
|
|
221
|
-
// the scaffolded feature dir and re-throws so a re-run isn't blocked.
|
|
222
|
-
function mountInRunConfig(runConfigPath: string, name: string): boolean {
|
|
223
|
-
const camel = kebabToCamel(name);
|
|
224
|
-
const project = new Project({
|
|
225
|
-
skipAddingFilesFromTsConfig: true,
|
|
226
|
-
skipFileDependencyResolution: true,
|
|
227
|
-
});
|
|
228
|
-
const sf = project.addSourceFileAtPath(runConfigPath);
|
|
229
|
-
|
|
230
|
-
// Import and APP_FEATURES entry are checked independently so a half-applied
|
|
231
|
-
// state self-heals: if the import exists but the entry was hand-removed (or
|
|
232
|
-
// vice versa), re-running adds only the missing half instead of short-
|
|
233
|
-
// circuiting on the import alone and leaving the feature unmounted.
|
|
234
|
-
let changed = false;
|
|
235
|
-
|
|
236
|
-
// 1. Prepend import after the last existing import — only if absent.
|
|
237
|
-
if (!sf.getImportDeclaration(`./features/${name}`)) {
|
|
238
|
-
const imports = sf.getImportDeclarations();
|
|
239
|
-
const insertIndex =
|
|
240
|
-
imports.length > 0 ? (imports[imports.length - 1]?.getChildIndex() ?? 0) + 1 : 0;
|
|
241
|
-
sf.insertImportDeclaration(insertIndex, {
|
|
242
|
-
moduleSpecifier: `./features/${name}`,
|
|
243
|
-
namedImports: [`${camel}Feature`],
|
|
244
|
-
});
|
|
245
|
-
changed = true;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// 2. Find `export const APP_FEATURES = [...]` and append the entry — only if absent.
|
|
249
|
-
const appFeaturesDecl = sf.getVariableDeclaration("APP_FEATURES");
|
|
250
|
-
if (!appFeaturesDecl) {
|
|
251
|
-
throw new Error(
|
|
252
|
-
`mountInRunConfig: ${runConfigPath} has no 'APP_FEATURES' declaration — ` +
|
|
253
|
-
`cannot auto-mount. Hand-edit: add '${camel}Feature' to APP_FEATURES.`,
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
const initializer =
|
|
257
|
-
appFeaturesDecl.getInitializerIfKind(SyntaxKind.AsExpression) ??
|
|
258
|
-
appFeaturesDecl.getInitializer();
|
|
259
|
-
if (!initializer) {
|
|
260
|
-
throw new Error(`mountInRunConfig: APP_FEATURES has no initializer — cannot auto-mount.`);
|
|
261
|
-
}
|
|
262
|
-
// Strip `as const` wrapper if present.
|
|
263
|
-
const arr =
|
|
264
|
-
initializer.getKind() === SyntaxKind.AsExpression
|
|
265
|
-
? initializer.getFirstChildByKind(SyntaxKind.ArrayLiteralExpression)
|
|
266
|
-
: initializer.asKind(SyntaxKind.ArrayLiteralExpression);
|
|
267
|
-
if (!arr) {
|
|
268
|
-
throw new Error(`mountInRunConfig: APP_FEATURES is not an array literal — cannot auto-mount.`);
|
|
269
|
-
}
|
|
270
|
-
const alreadyListed = arr.getElements().some((el) => el.getText() === `${camel}Feature`);
|
|
271
|
-
if (!alreadyListed) {
|
|
272
|
-
arr.addElement(`${camel}Feature`);
|
|
273
|
-
changed = true;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
if (changed) sf.saveSync();
|
|
277
|
-
return true;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Re-export so consumers can hint at the file (e.g. for kumiko-cli output).
|
|
281
|
-
export function runConfigPathForApp(appRoot: string): string {
|
|
282
|
-
return join(appRoot, "src", "run-config.ts");
|
|
283
|
-
}
|