swallowkit 1.0.0-beta.55 → 1.0.0-beta.57
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/dist/cli/commands/add-auth.d.ts.map +1 -1
- package/dist/cli/commands/add-auth.js +154 -157
- package/dist/cli/commands/add-auth.js.map +1 -1
- package/dist/cli/commands/init.d.ts +5 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +178 -25
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +7 -2
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +113 -123
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +127 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config.d.ts +1 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +8 -4
- package/dist/core/config.js.map +1 -1
- package/dist/core/operations/auth-operations.d.ts +57 -0
- package/dist/core/operations/auth-operations.d.ts.map +1 -0
- package/dist/core/operations/auth-operations.js +269 -0
- package/dist/core/operations/auth-operations.js.map +1 -0
- package/dist/core/operations/create-model.d.ts.map +1 -1
- package/dist/core/operations/create-model.js +23 -13
- package/dist/core/operations/create-model.js.map +1 -1
- package/dist/core/operations/file-session.d.ts +56 -0
- package/dist/core/operations/file-session.d.ts.map +1 -0
- package/dist/core/operations/file-session.js +203 -0
- package/dist/core/operations/file-session.js.map +1 -0
- package/dist/core/operations/provision-operations.d.ts +52 -0
- package/dist/core/operations/provision-operations.d.ts.map +1 -0
- package/dist/core/operations/provision-operations.js +211 -0
- package/dist/core/operations/provision-operations.js.map +1 -0
- package/dist/core/operations/runtime.d.ts +5 -0
- package/dist/core/operations/runtime.d.ts.map +1 -1
- package/dist/core/operations/runtime.js +23 -0
- package/dist/core/operations/runtime.js.map +1 -1
- package/dist/core/operations/scaffold-apply.d.ts +21 -0
- package/dist/core/operations/scaffold-apply.d.ts.map +1 -0
- package/dist/core/operations/scaffold-apply.js +57 -0
- package/dist/core/operations/scaffold-apply.js.map +1 -0
- package/dist/core/operations/scaffold-machine.d.ts.map +1 -1
- package/dist/core/operations/scaffold-machine.js +1 -13
- package/dist/core/operations/scaffold-machine.js.map +1 -1
- package/dist/core/operations/scaffold-plan.d.ts +43 -0
- package/dist/core/operations/scaffold-plan.d.ts.map +1 -0
- package/dist/core/operations/scaffold-plan.js +187 -0
- package/dist/core/operations/scaffold-plan.js.map +1 -0
- package/dist/core/project/artifacts.d.ts +58 -0
- package/dist/core/project/artifacts.d.ts.map +1 -0
- package/dist/core/project/artifacts.js +179 -0
- package/dist/core/project/artifacts.js.map +1 -0
- package/dist/core/project/boundaries.d.ts +50 -0
- package/dist/core/project/boundaries.d.ts.map +1 -0
- package/dist/core/project/boundaries.js +117 -0
- package/dist/core/project/boundaries.js.map +1 -0
- package/dist/core/project/drift.d.ts +29 -0
- package/dist/core/project/drift.d.ts.map +1 -0
- package/dist/core/project/drift.js +188 -0
- package/dist/core/project/drift.js.map +1 -0
- package/dist/core/project/infra.d.ts +49 -0
- package/dist/core/project/infra.d.ts.map +1 -0
- package/dist/core/project/infra.js +167 -0
- package/dist/core/project/infra.js.map +1 -0
- package/dist/core/project/state.d.ts +15 -0
- package/dist/core/project/state.d.ts.map +1 -0
- package/dist/core/project/state.js +106 -0
- package/dist/core/project/state.js.map +1 -0
- package/dist/core/project/workflows.d.ts +43 -0
- package/dist/core/project/workflows.d.ts.map +1 -0
- package/dist/core/project/workflows.js +255 -0
- package/dist/core/project/workflows.js.map +1 -0
- package/dist/core/scaffold/auth-generator.d.ts.map +1 -1
- package/dist/core/scaffold/auth-generator.js +24 -27
- package/dist/core/scaffold/auth-generator.js.map +1 -1
- package/dist/core/scaffold/functions-generator.js +9 -9
- package/dist/core/scaffold/native-schema-generator.d.ts +7 -1
- package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -1
- package/dist/core/scaffold/native-schema-generator.js +104 -47
- package/dist/core/scaffold/native-schema-generator.js.map +1 -1
- package/dist/core/scaffold/nextjs-generator.js +6 -6
- package/dist/core/scaffold/ui-generator.js +19 -19
- package/dist/core/scaffold/ui-generator.js.map +1 -1
- package/dist/core/verify/index.d.ts +62 -0
- package/dist/core/verify/index.d.ts.map +1 -0
- package/dist/core/verify/index.js +343 -0
- package/dist/core/verify/index.js.map +1 -0
- package/dist/machine/contracts.d.ts +8 -0
- package/dist/machine/contracts.d.ts.map +1 -1
- package/dist/machine/errors.d.ts +4 -1
- package/dist/machine/errors.d.ts.map +1 -1
- package/dist/machine/errors.js +9 -1
- package/dist/machine/errors.js.map +1 -1
- package/dist/machine/index.d.ts.map +1 -1
- package/dist/machine/index.js +350 -16
- package/dist/machine/index.js.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +202 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +17 -17
- package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +6 -6
- package/src/__tests__/__snapshots__/ui-generator.test.ts.snap +11 -11
- package/src/__tests__/agent-loop-phase34.test.ts +449 -0
- package/src/__tests__/file-session.test.ts +98 -0
- package/src/__tests__/init.test.ts +50 -0
- package/src/__tests__/mcp.test.ts +113 -5
- package/src/__tests__/plan-apply.test.ts +461 -0
- package/src/__tests__/scaffold.test.ts +2 -2
- package/src/cli/commands/add-auth.ts +164 -164
- package/src/cli/commands/init.ts +186 -24
- package/src/cli/commands/scaffold.ts +136 -130
- package/src/cli/index.ts +148 -2
- package/src/core/config.ts +5 -4
- package/src/core/operations/auth-operations.ts +353 -0
- package/src/core/operations/create-model.ts +25 -13
- package/src/core/operations/file-session.ts +222 -0
- package/src/core/operations/provision-operations.ts +297 -0
- package/src/core/operations/runtime.ts +23 -0
- package/src/core/operations/scaffold-apply.ts +106 -0
- package/src/core/operations/scaffold-machine.ts +2 -16
- package/src/core/operations/scaffold-plan.ts +215 -0
- package/src/core/project/artifacts.ts +205 -0
- package/src/core/project/boundaries.ts +170 -0
- package/src/core/project/drift.ts +202 -0
- package/src/core/project/infra.ts +196 -0
- package/src/core/project/state.ts +77 -0
- package/src/core/project/workflows.ts +249 -0
- package/src/core/scaffold/auth-generator.ts +24 -27
- package/src/core/scaffold/functions-generator.ts +9 -9
- package/src/core/scaffold/native-schema-generator.ts +152 -70
- package/src/core/scaffold/nextjs-generator.ts +6 -6
- package/src/core/scaffold/ui-generator.ts +19 -19
- package/src/core/verify/index.ts +392 -0
- package/src/machine/contracts.ts +16 -0
- package/src/machine/errors.ts +12 -1
- package/src/machine/index.ts +444 -15
- package/src/mcp/index.ts +198 -2
- package/src/types/index.ts +17 -0
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
generateAuthLayout,
|
|
32
32
|
UIAuthOptions,
|
|
33
33
|
} from "../../core/scaffold/ui-generator";
|
|
34
|
-
import { generateLanguageSchemaArtifacts } from "../../core/scaffold/native-schema-generator";
|
|
34
|
+
import { generateLanguageSchemaArtifacts, validateNativeSchemaToolchain } from "../../core/scaffold/native-schema-generator";
|
|
35
35
|
import { detectFromProject, getCommands } from "../../utils/package-manager";
|
|
36
36
|
import {
|
|
37
37
|
BackendLanguage,
|
|
@@ -44,6 +44,14 @@ import {
|
|
|
44
44
|
} from "../../types";
|
|
45
45
|
import { syncProjectManifest } from "../../core/project/manifest";
|
|
46
46
|
import { generateBFFCallFunctionWithAuth, generateBFFCallFunctionWithMultipleAuth, generateBFFCallFunctionWithSwaAuth, generateNamedAuthRouterCSharp, generateNamedAuthRouterPython, generateNamedAuthRouterTS } from "../../core/scaffold/auth-generator";
|
|
47
|
+
import {
|
|
48
|
+
FileOperationSession,
|
|
49
|
+
getActiveFileSession,
|
|
50
|
+
hashFileIfExists,
|
|
51
|
+
} from "../../core/operations/file-session";
|
|
52
|
+
import { recordSessionOperations } from "../../core/project/artifacts";
|
|
53
|
+
|
|
54
|
+
const SCAFFOLD_GENERATOR = "scaffold";
|
|
47
55
|
|
|
48
56
|
interface ScaffoldOptions {
|
|
49
57
|
model: string; // モデルファイルのパス(例: "lib/models/todo.ts" or "todo")
|
|
@@ -79,6 +87,9 @@ export async function scaffoldCommand(options: ScaffoldOptions) {
|
|
|
79
87
|
const configValidation = validateConfig(getFullConfig());
|
|
80
88
|
if (!configValidation.valid) throw new Error(`Invalid SwallowKit configuration:\n${configValidation.errors.map(error => `- ${error}`).join("\n")}`);
|
|
81
89
|
|
|
90
|
+
// Plan(dry-run) や Apply からセッションが供給される。単体実行時は commit セッションを生成。
|
|
91
|
+
const session = getActiveFileSession() ?? new FileOperationSession("commit");
|
|
92
|
+
|
|
82
93
|
console.log("🏗️ SwallowKit Scaffold: Generating CRUD operations...\n");
|
|
83
94
|
|
|
84
95
|
try {
|
|
@@ -124,38 +135,51 @@ export async function scaffoldCommand(options: ScaffoldOptions) {
|
|
|
124
135
|
const relatedModels = backendLanguage === "typescript"
|
|
125
136
|
? [modelInfo]
|
|
126
137
|
: await collectModelGraph(modelPath);
|
|
138
|
+
for (const relatedModel of relatedModels) {
|
|
139
|
+
session.registerInput(relatedModel.filePath);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 4b. 書き込み前に外部 codegen toolchain を検証し、失敗時はプロジェクト無変更で終了する
|
|
143
|
+
if (session.mode === "commit" && backendLanguage !== "typescript" && !isConnectorModel) {
|
|
144
|
+
await validateNativeSchemaToolchain(
|
|
145
|
+
relatedModels,
|
|
146
|
+
modelInfo,
|
|
147
|
+
backendLanguage,
|
|
148
|
+
path.join(process.cwd(), functionsDir)
|
|
149
|
+
);
|
|
150
|
+
}
|
|
127
151
|
|
|
128
152
|
if (options.apiOnly) {
|
|
129
153
|
console.log("ℹ️ --api-only skips UI generation only; Functions, BFF routes, OpenAPI, and native schema assets are still updated.");
|
|
130
154
|
}
|
|
131
155
|
|
|
132
156
|
// 5. Generate BFF callFunction helper
|
|
133
|
-
await generateCallFunctionHelper();
|
|
157
|
+
await generateCallFunctionHelper(session);
|
|
134
158
|
|
|
135
159
|
// 6. Generate Azure Functions code
|
|
136
160
|
if (isConnectorModel) {
|
|
137
|
-
await generateConnectorFunctionsCode(modelInfo, functionsDir, sharedPackageName, backendLanguage);
|
|
161
|
+
await generateConnectorFunctionsCode(modelInfo, functionsDir, sharedPackageName, backendLanguage, session);
|
|
138
162
|
// 6b. Install connector driver dependencies
|
|
139
|
-
await installConnectorDriverDependencies(modelInfo, functionsDir, backendLanguage);
|
|
163
|
+
await installConnectorDriverDependencies(modelInfo, functionsDir, backendLanguage, session);
|
|
140
164
|
} else {
|
|
141
|
-
await generateFunctionsCode(modelInfo, functionsDir, sharedPackageName, backendLanguage);
|
|
165
|
+
await generateFunctionsCode(modelInfo, functionsDir, sharedPackageName, backendLanguage, session);
|
|
142
166
|
|
|
143
167
|
if (backendLanguage !== "typescript") {
|
|
144
|
-
await generateLanguageSchemaArtifacts(relatedModels, modelInfo, functionsDir, backendLanguage);
|
|
168
|
+
await generateLanguageSchemaArtifacts(relatedModels, modelInfo, functionsDir, backendLanguage, session);
|
|
145
169
|
}
|
|
146
170
|
}
|
|
147
171
|
|
|
148
172
|
// 7. Generate Next.js BFF API Routes
|
|
149
173
|
const apiDir = options.apiDir || "app/api";
|
|
150
174
|
if (isConnectorModel) {
|
|
151
|
-
await generateConnectorBFFRoutesFiles(modelInfo, apiDir, sharedPackageName);
|
|
175
|
+
await generateConnectorBFFRoutesFiles(modelInfo, apiDir, sharedPackageName, session);
|
|
152
176
|
} else {
|
|
153
|
-
await generateBFFRoutes(modelInfo, apiDir, sharedPackageName);
|
|
177
|
+
await generateBFFRoutes(modelInfo, apiDir, sharedPackageName, session);
|
|
154
178
|
}
|
|
155
179
|
|
|
156
180
|
// 8. Generate Cosmos DB container Bicep file (skip for connector models)
|
|
157
181
|
if (!isConnectorModel) {
|
|
158
|
-
await generateCosmosContainer(modelInfo);
|
|
182
|
+
await generateCosmosContainer(modelInfo, session);
|
|
159
183
|
}
|
|
160
184
|
|
|
161
185
|
// 9. Generate UI components (unless --api-only)
|
|
@@ -166,14 +190,20 @@ export async function scaffoldCommand(options: ScaffoldOptions) {
|
|
|
166
190
|
uiAuthPolicy && uiAuthConfig && uiAuthConfig.provider !== 'none'
|
|
167
191
|
? createUIAuthOptions(uiAuthPolicy, uiAuthConfig)
|
|
168
192
|
: undefined;
|
|
169
|
-
await generateUIComponents(modelInfo, sharedPackageName, uiAuthOptions);
|
|
193
|
+
await generateUIComponents(modelInfo, sharedPackageName, uiAuthOptions, session);
|
|
170
194
|
if (uiAuthOptions?.authenticatedFetchImport) {
|
|
171
|
-
applyExternalTokenFetchToGeneratedUi(modelInfo, uiAuthOptions.authenticatedFetchImport);
|
|
195
|
+
applyExternalTokenFetchToGeneratedUi(modelInfo, session, uiAuthOptions.authenticatedFetchImport);
|
|
172
196
|
}
|
|
173
|
-
await updateNavigationMenu(modelInfo);
|
|
197
|
+
await updateNavigationMenu(modelInfo, session);
|
|
174
198
|
}
|
|
175
199
|
|
|
176
|
-
|
|
200
|
+
if (session.mode === "commit") {
|
|
201
|
+
recordSessionOperations(session.operations, {
|
|
202
|
+
schemaHash: hashFileIfExists(modelPath) ?? undefined,
|
|
203
|
+
sourceModel: modelInfo.name,
|
|
204
|
+
});
|
|
205
|
+
await syncProjectManifest();
|
|
206
|
+
}
|
|
177
207
|
|
|
178
208
|
console.log("\n✅ Scaffold completed successfully!");
|
|
179
209
|
console.log("\n📝 Next steps:");
|
|
@@ -198,29 +228,27 @@ export async function scaffoldCommand(options: ScaffoldOptions) {
|
|
|
198
228
|
}
|
|
199
229
|
}
|
|
200
230
|
|
|
201
|
-
function applyExternalTokenFetchToGeneratedUi(modelInfo: ModelInfo, importPath = '@/lib/auth/authenticated-fetch'): void {
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
);
|
|
216
|
-
if (!content.includes(importPath)) {
|
|
217
|
-
const directive = content.match(/^'use client';\s*\r?\n/)?.[0] ?? '';
|
|
218
|
-
content = `${directive}import { authenticatedFetch } from '${importPath}';\n${content.slice(directive.length)}`;
|
|
219
|
-
}
|
|
220
|
-
fs.writeFileSync(target, content, 'utf-8');
|
|
231
|
+
function applyExternalTokenFetchToGeneratedUi(modelInfo: ModelInfo, session: FileOperationSession, importPath = '@/lib/auth/authenticated-fetch'): void {
|
|
232
|
+
const uiPrefix = `app/${toKebabCase(modelInfo.name)}/`;
|
|
233
|
+
for (const operation of session.operations) {
|
|
234
|
+
if (!operation.path.startsWith(uiPrefix) || !operation.path.endsWith('.tsx') || operation.action === 'delete') continue;
|
|
235
|
+
let content = session.readFile(operation.absolutePath);
|
|
236
|
+
if (!content.includes('fetch(')) continue;
|
|
237
|
+
content = content.replace(/\bfetch\(/g, 'authenticatedFetch(');
|
|
238
|
+
content = content.replace(
|
|
239
|
+
/(if \(!res\.ok\) throw new Error\([^\n]+\);)/g,
|
|
240
|
+
`if (res.status === 401) throw new Error('Authentication required');\n $1`
|
|
241
|
+
);
|
|
242
|
+
if (!content.includes(importPath)) {
|
|
243
|
+
const directive = content.match(/^'use client';\s*\r?\n/)?.[0] ?? '';
|
|
244
|
+
content = `${directive}import { authenticatedFetch } from '${importPath}';\n${content.slice(directive.length)}`;
|
|
221
245
|
}
|
|
222
|
-
|
|
223
|
-
|
|
246
|
+
session.writeFile(operation.absolutePath, content, {
|
|
247
|
+
ownership: 'managed',
|
|
248
|
+
generator: SCAFFOLD_GENERATOR,
|
|
249
|
+
sourceModel: modelInfo.name,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
224
252
|
}
|
|
225
253
|
|
|
226
254
|
/**
|
|
@@ -259,7 +287,7 @@ export function resolveAuthPolicy(modelInfo: ModelInfo, authConfig: AuthConfig |
|
|
|
259
287
|
/**
|
|
260
288
|
* モデルファイルのパスを解決
|
|
261
289
|
*/
|
|
262
|
-
function resolveModelPath(modelInput: string): string {
|
|
290
|
+
export function resolveModelPath(modelInput: string): string {
|
|
263
291
|
const cwd = process.cwd();
|
|
264
292
|
|
|
265
293
|
// 絶対パスの場合
|
|
@@ -327,7 +355,7 @@ function readSharedPackageName(): string {
|
|
|
327
355
|
/**
|
|
328
356
|
* Generate BFF callFunction helper (lib/api/call-function.ts)
|
|
329
357
|
*/
|
|
330
|
-
async function generateCallFunctionHelper(): Promise<void> {
|
|
358
|
+
async function generateCallFunctionHelper(session: FileOperationSession): Promise<void> {
|
|
331
359
|
console.log("\n📦 Generating BFF callFunction helper...");
|
|
332
360
|
|
|
333
361
|
const cwd = process.cwd();
|
|
@@ -335,10 +363,6 @@ async function generateCallFunctionHelper(): Promise<void> {
|
|
|
335
363
|
const helperDir = path.join(cwd, "lib", "api");
|
|
336
364
|
const helperPath = path.join(helperDir, "call-function.ts");
|
|
337
365
|
|
|
338
|
-
if (!fs.existsSync(helperDir)) {
|
|
339
|
-
fs.mkdirSync(helperDir, { recursive: true });
|
|
340
|
-
}
|
|
341
|
-
|
|
342
366
|
// auth 設定がある場合は Authorization ヘッダー転送版を生成
|
|
343
367
|
const authConfig = getAuthConfig();
|
|
344
368
|
const normalizedAuth = normalizeAuthConfig(authConfig);
|
|
@@ -353,7 +377,7 @@ async function generateCallFunctionHelper(): Promise<void> {
|
|
|
353
377
|
helperCode = generateBFFCallFunction();
|
|
354
378
|
}
|
|
355
379
|
|
|
356
|
-
|
|
380
|
+
session.writeFile(helperPath, helperCode, { ownership: "managed", generator: SCAFFOLD_GENERATOR });
|
|
357
381
|
console.log(`✅ Created: ${helperPath}`);
|
|
358
382
|
}
|
|
359
383
|
|
|
@@ -364,7 +388,8 @@ async function generateFunctionsCode(
|
|
|
364
388
|
modelInfo: ModelInfo,
|
|
365
389
|
functionsDir: string,
|
|
366
390
|
sharedPackageName: string,
|
|
367
|
-
backendLanguage: BackendLanguage
|
|
391
|
+
backendLanguage: BackendLanguage,
|
|
392
|
+
session: FileOperationSession
|
|
368
393
|
): Promise<void> {
|
|
369
394
|
console.log("\n🔨 Generating Azure Functions CRUD code...");
|
|
370
395
|
|
|
@@ -376,12 +401,12 @@ async function generateFunctionsCode(
|
|
|
376
401
|
}
|
|
377
402
|
|
|
378
403
|
const modelKebab = toKebabCase(modelInfo.name);
|
|
404
|
+
const managedMeta = { ownership: "managed" as const, generator: SCAFFOLD_GENERATOR, sourceModel: modelInfo.name };
|
|
379
405
|
if (backendLanguage === "typescript") {
|
|
380
406
|
const normalizedAuth = getNormalizedAuthConfig();
|
|
381
407
|
if (normalizedAuth && Object.keys(normalizedAuth.schemes).some(name => name !== "default")) {
|
|
382
408
|
const routerPath = path.join(process.cwd(), functionsDir, "src", "auth", "auth-router.ts");
|
|
383
|
-
|
|
384
|
-
fs.writeFileSync(routerPath, generateNamedAuthRouterTS(normalizedAuth), "utf-8");
|
|
409
|
+
session.writeFile(routerPath, generateNamedAuthRouterTS(normalizedAuth), { ownership: "managed", generator: SCAFFOLD_GENERATOR });
|
|
385
410
|
}
|
|
386
411
|
const functionFilePath = path.join(
|
|
387
412
|
process.cwd(),
|
|
@@ -390,13 +415,8 @@ async function generateFunctionsCode(
|
|
|
390
415
|
`${modelKebab}.ts`
|
|
391
416
|
);
|
|
392
417
|
|
|
393
|
-
const functionDir = path.dirname(functionFilePath);
|
|
394
|
-
if (!fs.existsSync(functionDir)) {
|
|
395
|
-
fs.mkdirSync(functionDir, { recursive: true });
|
|
396
|
-
}
|
|
397
|
-
|
|
398
418
|
const code = generateCompactAzureFunctionsCRUD(modelInfo, sharedPackageName, authPolicy, authConfig?.provider);
|
|
399
|
-
|
|
419
|
+
session.writeFile(functionFilePath, code, managedMeta);
|
|
400
420
|
console.log(`✅ Created: ${functionFilePath}`);
|
|
401
421
|
return;
|
|
402
422
|
}
|
|
@@ -405,21 +425,19 @@ async function generateFunctionsCode(
|
|
|
405
425
|
const normalizedAuth = getNormalizedAuthConfig();
|
|
406
426
|
if (normalizedAuth && Object.keys(normalizedAuth.schemes).some(name => name !== "default")) {
|
|
407
427
|
const routerPath = path.join(process.cwd(), functionsDir, "Auth", "AuthRouter.cs");
|
|
408
|
-
|
|
409
|
-
fs.writeFileSync(routerPath, generateNamedAuthRouterCSharp(normalizedAuth), "utf-8");
|
|
428
|
+
session.writeFile(routerPath, generateNamedAuthRouterCSharp(normalizedAuth), { ownership: "managed", generator: SCAFFOLD_GENERATOR });
|
|
410
429
|
}
|
|
411
430
|
const crudDir = path.join(process.cwd(), functionsDir, "Crud");
|
|
412
431
|
const functionFilePath = path.join(crudDir, `${modelInfo.name}CrudFunctions.cs`);
|
|
413
|
-
fs.mkdirSync(crudDir, { recursive: true });
|
|
414
432
|
|
|
415
433
|
// Remove init-generated template (singular) to avoid route conflicts
|
|
416
434
|
const templatePath = path.join(crudDir, `${modelInfo.name}Function.cs`);
|
|
417
|
-
if (
|
|
418
|
-
|
|
435
|
+
if (session.fileExists(templatePath)) {
|
|
436
|
+
session.deleteFile(templatePath, managedMeta);
|
|
419
437
|
console.log(`🗑️ Removed template: ${templatePath}`);
|
|
420
438
|
}
|
|
421
439
|
|
|
422
|
-
|
|
440
|
+
session.writeFile(functionFilePath, generateCSharpAzureFunctionsCRUD(modelInfo, authPolicy), managedMeta);
|
|
423
441
|
console.log(`✅ Created: ${functionFilePath}`);
|
|
424
442
|
return;
|
|
425
443
|
}
|
|
@@ -427,17 +445,15 @@ async function generateFunctionsCode(
|
|
|
427
445
|
const normalizedAuth = getNormalizedAuthConfig();
|
|
428
446
|
if (normalizedAuth && Object.keys(normalizedAuth.schemes).some(name => name !== "default")) {
|
|
429
447
|
const routerPath = path.join(process.cwd(), functionsDir, "auth", "auth_router.py");
|
|
430
|
-
|
|
431
|
-
fs.writeFileSync(routerPath, generateNamedAuthRouterPython(normalizedAuth), "utf-8");
|
|
448
|
+
session.writeFile(routerPath, generateNamedAuthRouterPython(normalizedAuth), { ownership: "managed", generator: SCAFFOLD_GENERATOR });
|
|
432
449
|
}
|
|
433
450
|
|
|
434
451
|
const blueprintsDir = path.join(process.cwd(), functionsDir, "blueprints");
|
|
435
452
|
const blueprintPath = path.join(blueprintsDir, `${modelKebab.replace(/-/g, "_")}.py`);
|
|
436
|
-
fs.mkdirSync(blueprintsDir, { recursive: true });
|
|
437
453
|
|
|
438
454
|
const { blueprint, registration } = generatePythonAzureFunctionsCRUD(modelInfo, authPolicy);
|
|
439
|
-
|
|
440
|
-
updatePythonFunctionRegistrations(path.join(process.cwd(), functionsDir, "function_app.py"), registration);
|
|
455
|
+
session.writeFile(blueprintPath, blueprint, managedMeta);
|
|
456
|
+
updatePythonFunctionRegistrations(path.join(process.cwd(), functionsDir, "function_app.py"), registration, session);
|
|
441
457
|
console.log(`✅ Created: ${blueprintPath}`);
|
|
442
458
|
}
|
|
443
459
|
|
|
@@ -462,7 +478,8 @@ async function generateConnectorFunctionsCode(
|
|
|
462
478
|
modelInfo: ModelInfo,
|
|
463
479
|
functionsDir: string,
|
|
464
480
|
sharedPackageName: string,
|
|
465
|
-
backendLanguage: BackendLanguage
|
|
481
|
+
backendLanguage: BackendLanguage,
|
|
482
|
+
session: FileOperationSession
|
|
466
483
|
): Promise<void> {
|
|
467
484
|
console.log("\n🔌 Generating Connector Azure Functions code...");
|
|
468
485
|
|
|
@@ -484,6 +501,7 @@ async function generateConnectorFunctionsCode(
|
|
|
484
501
|
}
|
|
485
502
|
|
|
486
503
|
const modelKebab = toKebabCase(modelInfo.name);
|
|
504
|
+
const connectorManagedMeta = { ownership: "managed" as const, generator: SCAFFOLD_GENERATOR, sourceModel: modelInfo.name };
|
|
487
505
|
|
|
488
506
|
if (backendLanguage === "typescript") {
|
|
489
507
|
const functionFilePath = path.join(
|
|
@@ -492,7 +510,6 @@ async function generateConnectorFunctionsCode(
|
|
|
492
510
|
"src",
|
|
493
511
|
`${modelKebab}.ts`
|
|
494
512
|
);
|
|
495
|
-
fs.mkdirSync(path.dirname(functionFilePath), { recursive: true });
|
|
496
513
|
|
|
497
514
|
let code: string;
|
|
498
515
|
if (connectorDef.type === "rdb") {
|
|
@@ -513,7 +530,7 @@ async function generateConnectorFunctionsCode(
|
|
|
513
530
|
);
|
|
514
531
|
}
|
|
515
532
|
|
|
516
|
-
|
|
533
|
+
session.writeFile(functionFilePath, code, connectorManagedMeta);
|
|
517
534
|
console.log(`✅ Created: ${functionFilePath}`);
|
|
518
535
|
return;
|
|
519
536
|
}
|
|
@@ -525,7 +542,6 @@ async function generateConnectorFunctionsCode(
|
|
|
525
542
|
"Connectors",
|
|
526
543
|
`${modelInfo.name}ConnectorFunctions.cs`
|
|
527
544
|
);
|
|
528
|
-
fs.mkdirSync(path.dirname(functionFilePath), { recursive: true });
|
|
529
545
|
|
|
530
546
|
let code: string;
|
|
531
547
|
if (connectorDef.type === "rdb") {
|
|
@@ -542,7 +558,7 @@ async function generateConnectorFunctionsCode(
|
|
|
542
558
|
);
|
|
543
559
|
}
|
|
544
560
|
|
|
545
|
-
|
|
561
|
+
session.writeFile(functionFilePath, code, connectorManagedMeta);
|
|
546
562
|
console.log(`✅ Created: ${functionFilePath}`);
|
|
547
563
|
return;
|
|
548
564
|
}
|
|
@@ -550,7 +566,6 @@ async function generateConnectorFunctionsCode(
|
|
|
550
566
|
// Python
|
|
551
567
|
const blueprintsDir = path.join(process.cwd(), functionsDir, "blueprints");
|
|
552
568
|
const blueprintPath = path.join(blueprintsDir, `${modelKebab.replace(/-/g, "_")}.py`);
|
|
553
|
-
fs.mkdirSync(blueprintsDir, { recursive: true });
|
|
554
569
|
|
|
555
570
|
let result: { blueprint: string; registration: string };
|
|
556
571
|
if (connectorDef.type === "rdb") {
|
|
@@ -567,8 +582,8 @@ async function generateConnectorFunctionsCode(
|
|
|
567
582
|
);
|
|
568
583
|
}
|
|
569
584
|
|
|
570
|
-
|
|
571
|
-
updatePythonFunctionRegistrations(path.join(process.cwd(), functionsDir, "function_app.py"), result.registration);
|
|
585
|
+
session.writeFile(blueprintPath, result.blueprint, connectorManagedMeta);
|
|
586
|
+
updatePythonFunctionRegistrations(path.join(process.cwd(), functionsDir, "function_app.py"), result.registration, session);
|
|
572
587
|
console.log(`✅ Created: ${blueprintPath}`);
|
|
573
588
|
}
|
|
574
589
|
|
|
@@ -579,7 +594,8 @@ async function generateConnectorFunctionsCode(
|
|
|
579
594
|
async function installConnectorDriverDependencies(
|
|
580
595
|
modelInfo: ModelInfo,
|
|
581
596
|
functionsDir: string,
|
|
582
|
-
backendLanguage: BackendLanguage
|
|
597
|
+
backendLanguage: BackendLanguage,
|
|
598
|
+
session: FileOperationSession
|
|
583
599
|
): Promise<void> {
|
|
584
600
|
const connectorConfig = modelInfo.connectorConfig!;
|
|
585
601
|
const connectorDef = getConnectorDefinition(connectorConfig.connector);
|
|
@@ -607,6 +623,13 @@ async function installConnectorDriverDependencies(
|
|
|
607
623
|
if (missingDeps.length === 0 && missingDevDeps.length === 0) return;
|
|
608
624
|
}
|
|
609
625
|
|
|
626
|
+
if (session.mode === "collect") {
|
|
627
|
+
session.addWarning(
|
|
628
|
+
`external-install: ${rdbDef.provider} driver dependencies (${[...entry.deps, ...entry.devDeps].join(", ")}) will be installed into ${functionsDir}/ on apply.`
|
|
629
|
+
);
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
|
|
610
633
|
console.log(`\n📦 Installing ${rdbDef.provider} driver dependencies...`);
|
|
611
634
|
const pm = detectFromProject(functionsPath);
|
|
612
635
|
const cmds = getCommands(pm);
|
|
@@ -629,6 +652,10 @@ async function installConnectorDriverDependencies(
|
|
|
629
652
|
};
|
|
630
653
|
const pkg = nugetMap[rdbDef.provider];
|
|
631
654
|
if (!pkg) return;
|
|
655
|
+
if (session.mode === "collect") {
|
|
656
|
+
session.addWarning(`external-install: NuGet package ${pkg} will be added to ${functionsDir}/functions.csproj on apply.`);
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
632
659
|
console.log(`\n📦 Installing ${rdbDef.provider} NuGet package...`);
|
|
633
660
|
runSpawnSyncCommand("dotnet", ["add", path.join(functionsPath, "functions.csproj"), "package", pkg], functionsPath);
|
|
634
661
|
console.log(`✅ ${pkg} installed`);
|
|
@@ -644,11 +671,11 @@ async function installConnectorDriverDependencies(
|
|
|
644
671
|
const pipPkg = pipMap[rdbDef.provider];
|
|
645
672
|
if (!pipPkg) return;
|
|
646
673
|
const reqPath = path.join(functionsPath, "requirements.txt");
|
|
647
|
-
if (
|
|
648
|
-
const existing =
|
|
674
|
+
if (session.fileExists(reqPath)) {
|
|
675
|
+
const existing = session.readFile(reqPath);
|
|
649
676
|
if (!existing.includes(pipPkg)) {
|
|
650
677
|
console.log(`\n📦 Adding ${pipPkg} to requirements.txt...`);
|
|
651
|
-
|
|
678
|
+
session.writeFile(reqPath, `${existing}\n${pipPkg}\n`, { ownership: "extension-point", generator: SCAFFOLD_GENERATOR });
|
|
652
679
|
console.log(`✅ ${pipPkg} added`);
|
|
653
680
|
}
|
|
654
681
|
}
|
|
@@ -660,7 +687,8 @@ async function installConnectorDriverDependencies(
|
|
|
660
687
|
async function generateConnectorBFFRoutesFiles(
|
|
661
688
|
modelInfo: ModelInfo,
|
|
662
689
|
apiDir: string,
|
|
663
|
-
sharedPackageName: string
|
|
690
|
+
sharedPackageName: string,
|
|
691
|
+
session: FileOperationSession
|
|
664
692
|
): Promise<void> {
|
|
665
693
|
console.log("\n🔌 Generating Connector BFF API routes...");
|
|
666
694
|
|
|
@@ -670,13 +698,11 @@ async function generateConnectorBFFRoutesFiles(
|
|
|
670
698
|
const listRoutePath = path.join(process.cwd(), apiDir, modelCamel, "route.ts");
|
|
671
699
|
const detailRoutePath = path.join(process.cwd(), apiDir, modelCamel, "[id]", "route.ts");
|
|
672
700
|
|
|
673
|
-
fs.mkdirSync(path.dirname(listRoutePath), { recursive: true });
|
|
674
|
-
fs.mkdirSync(path.dirname(detailRoutePath), { recursive: true });
|
|
675
|
-
|
|
676
701
|
const routes = generateConnectorBFFRoutes(modelInfo, sharedPackageName, operations);
|
|
677
702
|
|
|
678
|
-
|
|
679
|
-
|
|
703
|
+
const meta = { ownership: "managed" as const, generator: SCAFFOLD_GENERATOR, sourceModel: modelInfo.name };
|
|
704
|
+
session.writeFile(listRoutePath, routes.listRoute, meta);
|
|
705
|
+
session.writeFile(detailRoutePath, routes.detailRoute, meta);
|
|
680
706
|
|
|
681
707
|
console.log(`✅ Created: ${listRoutePath}`);
|
|
682
708
|
console.log(`✅ Created: ${detailRoutePath}`);
|
|
@@ -717,12 +743,12 @@ function describeFunctionsOutputPath(functionsDir: string, backendLanguage: Back
|
|
|
717
743
|
return `${functionsDir}/blueprints/`;
|
|
718
744
|
}
|
|
719
745
|
|
|
720
|
-
function updatePythonFunctionRegistrations(functionAppPath: string, registration: string): void {
|
|
721
|
-
if (!
|
|
746
|
+
function updatePythonFunctionRegistrations(functionAppPath: string, registration: string, session: FileOperationSession): void {
|
|
747
|
+
if (!session.fileExists(functionAppPath)) {
|
|
722
748
|
throw new Error(`Python Functions entrypoint not found: ${functionAppPath}`);
|
|
723
749
|
}
|
|
724
750
|
|
|
725
|
-
const content =
|
|
751
|
+
const content = session.readFile(functionAppPath);
|
|
726
752
|
|
|
727
753
|
// Check if import line already exists (handles init-generated layout)
|
|
728
754
|
const importLine = registration.split("\n").find((l) => l.startsWith("from ") || l.startsWith("import "));
|
|
@@ -739,7 +765,7 @@ function updatePythonFunctionRegistrations(functionAppPath: string, registration
|
|
|
739
765
|
}
|
|
740
766
|
|
|
741
767
|
const updated = content.replace(marker, `${registration}\n${marker}`);
|
|
742
|
-
|
|
768
|
+
session.writeFile(functionAppPath, updated, { ownership: "extension-point", generator: SCAFFOLD_GENERATOR });
|
|
743
769
|
}
|
|
744
770
|
|
|
745
771
|
/**
|
|
@@ -748,7 +774,8 @@ function updatePythonFunctionRegistrations(functionAppPath: string, registration
|
|
|
748
774
|
async function generateBFFRoutes(
|
|
749
775
|
modelInfo: any,
|
|
750
776
|
apiDir: string,
|
|
751
|
-
sharedPackageName: string
|
|
777
|
+
sharedPackageName: string,
|
|
778
|
+
session: FileOperationSession
|
|
752
779
|
): Promise<void> {
|
|
753
780
|
console.log("\n🔨 Generating Next.js BFF API routes...");
|
|
754
781
|
|
|
@@ -771,24 +798,13 @@ async function generateBFFRoutes(
|
|
|
771
798
|
"route.ts"
|
|
772
799
|
);
|
|
773
800
|
|
|
774
|
-
// ディレクトリを作成
|
|
775
|
-
const listRouteDir = path.dirname(listRoutePath);
|
|
776
|
-
const detailRouteDir = path.dirname(detailRoutePath);
|
|
777
|
-
|
|
778
|
-
if (!fs.existsSync(listRouteDir)) {
|
|
779
|
-
fs.mkdirSync(listRouteDir, { recursive: true });
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
if (!fs.existsSync(detailRouteDir)) {
|
|
783
|
-
fs.mkdirSync(detailRouteDir, { recursive: true });
|
|
784
|
-
}
|
|
785
|
-
|
|
786
801
|
// コードを生成
|
|
787
802
|
const routes = generateCompactBFFRoutes(modelInfo, sharedPackageName);
|
|
788
803
|
|
|
789
804
|
// ファイルに書き込み
|
|
790
|
-
|
|
791
|
-
|
|
805
|
+
const meta = { ownership: "managed" as const, generator: SCAFFOLD_GENERATOR, sourceModel: modelInfo.name };
|
|
806
|
+
session.writeFile(listRoutePath, routes.listRoute, meta);
|
|
807
|
+
session.writeFile(detailRoutePath, routes.detailRoute, meta);
|
|
792
808
|
|
|
793
809
|
console.log(`✅ Created: ${listRoutePath}`);
|
|
794
810
|
console.log(`✅ Created: ${detailRoutePath}`);
|
|
@@ -797,7 +813,7 @@ async function generateBFFRoutes(
|
|
|
797
813
|
/**
|
|
798
814
|
* Generate Cosmos DB container Bicep file
|
|
799
815
|
*/
|
|
800
|
-
export async function generateCosmosContainer(modelInfo: any): Promise<void> {
|
|
816
|
+
export async function generateCosmosContainer(modelInfo: any, session: FileOperationSession = new FileOperationSession("commit")): Promise<void> {
|
|
801
817
|
console.log("\n🗄️ Generating Cosmos DB container Bicep file...");
|
|
802
818
|
|
|
803
819
|
const modelKebab = toKebabCase(modelInfo.name);
|
|
@@ -811,13 +827,8 @@ export async function generateCosmosContainer(modelInfo: any): Promise<void> {
|
|
|
811
827
|
return;
|
|
812
828
|
}
|
|
813
829
|
|
|
814
|
-
// Check if containers directory exists, if not create it
|
|
815
|
-
const containersDir = path.join(infraDir, "containers");
|
|
816
|
-
if (!fs.existsSync(containersDir)) {
|
|
817
|
-
fs.mkdirSync(containersDir, { recursive: true });
|
|
818
|
-
}
|
|
819
|
-
|
|
820
830
|
// Generate container Bicep file
|
|
831
|
+
const containersDir = path.join(infraDir, "containers");
|
|
821
832
|
const containerFileName = `${modelKebab}-container.bicep`;
|
|
822
833
|
const containerFilePath = path.join(containersDir, containerFileName);
|
|
823
834
|
|
|
@@ -879,26 +890,26 @@ output containerName string = container.name
|
|
|
879
890
|
`;
|
|
880
891
|
|
|
881
892
|
// Write Bicep file (overwrite if exists)
|
|
882
|
-
|
|
893
|
+
session.writeFile(containerFilePath, bicepContent, { ownership: "managed", generator: SCAFFOLD_GENERATOR, sourceModel: modelInfo.name });
|
|
883
894
|
console.log(`✅ Created: ${containerFilePath}`);
|
|
884
895
|
|
|
885
896
|
// Update main.bicep to include this container module
|
|
886
|
-
await updateMainBicepWithContainer(modelKebab, modelPascal);
|
|
897
|
+
await updateMainBicepWithContainer(modelKebab, modelPascal, session);
|
|
887
898
|
}
|
|
888
899
|
|
|
889
900
|
/**
|
|
890
901
|
* Update main.bicep to include new container module
|
|
891
902
|
*/
|
|
892
|
-
async function updateMainBicepWithContainer(modelKebab: string, modelPascal: string): Promise<void> {
|
|
903
|
+
async function updateMainBicepWithContainer(modelKebab: string, modelPascal: string, session: FileOperationSession): Promise<void> {
|
|
893
904
|
const cwd = process.cwd();
|
|
894
905
|
const mainBicepPath = path.join(cwd, "infra", "main.bicep");
|
|
895
906
|
|
|
896
|
-
if (!
|
|
907
|
+
if (!session.fileExists(mainBicepPath)) {
|
|
897
908
|
console.log("ℹ️ main.bicep not found. Please manually add the container module.");
|
|
898
909
|
return;
|
|
899
910
|
}
|
|
900
911
|
|
|
901
|
-
let mainBicepContent =
|
|
912
|
+
let mainBicepContent = session.readFile(mainBicepPath);
|
|
902
913
|
|
|
903
914
|
// Check if container module already exists
|
|
904
915
|
const containerModuleName = `${modelPascal.charAt(0).toLowerCase()}${modelPascal.slice(1)}Container`;
|
|
@@ -950,14 +961,14 @@ module ${containerModuleName} 'containers/${modelKebab}-container.bicep' = {
|
|
|
950
961
|
containerModule +
|
|
951
962
|
mainBicepContent.slice(insertPosition);
|
|
952
963
|
|
|
953
|
-
|
|
964
|
+
session.writeFile(mainBicepPath, mainBicepContent, { ownership: "extension-point", generator: SCAFFOLD_GENERATOR });
|
|
954
965
|
console.log(`✅ Added container module to main.bicep`);
|
|
955
966
|
}
|
|
956
967
|
|
|
957
968
|
/**
|
|
958
969
|
* Generate UI components (list, detail, form, create, edit pages)
|
|
959
970
|
*/
|
|
960
|
-
export async function generateUIComponents(modelInfo: ModelInfo, sharedPackageName: string, authOptions?: UIAuthOptions): Promise<void> {
|
|
971
|
+
export async function generateUIComponents(modelInfo: ModelInfo, sharedPackageName: string, authOptions?: UIAuthOptions, session: FileOperationSession = new FileOperationSession("commit")): Promise<void> {
|
|
961
972
|
console.log("\n🎨 Generating UI components...");
|
|
962
973
|
|
|
963
974
|
const modelKebab = toKebabCase(modelInfo.name);
|
|
@@ -971,13 +982,6 @@ export async function generateUIComponents(modelInfo: ModelInfo, sharedPackageNa
|
|
|
971
982
|
const idDir = path.join(modelDir, "[id]");
|
|
972
983
|
const editDir = path.join(idDir, "edit");
|
|
973
984
|
|
|
974
|
-
// Create directories
|
|
975
|
-
[componentsDir, newDir, editDir].forEach(dir => {
|
|
976
|
-
if (!fs.existsSync(dir)) {
|
|
977
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
978
|
-
}
|
|
979
|
-
});
|
|
980
|
-
|
|
981
985
|
// Generate and write files
|
|
982
986
|
const listPage = generateListPage(modelInfo, sharedPackageName, authOptions);
|
|
983
987
|
const detailPage = generateDetailPage(modelInfo, sharedPackageName, authOptions);
|
|
@@ -986,13 +990,14 @@ export async function generateUIComponents(modelInfo: ModelInfo, sharedPackageNa
|
|
|
986
990
|
const editPage = generateEditPage(modelInfo, sharedPackageName, authOptions);
|
|
987
991
|
const authLayout = generateAuthLayout(modelInfo, authOptions);
|
|
988
992
|
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
993
|
+
const meta = { ownership: "managed" as const, generator: SCAFFOLD_GENERATOR, sourceModel: modelInfo.name };
|
|
994
|
+
session.writeFile(path.join(modelDir, "page.tsx"), listPage, meta);
|
|
995
|
+
session.writeFile(path.join(idDir, "page.tsx"), detailPage, meta);
|
|
996
|
+
session.writeFile(path.join(componentsDir, `${modelName}Form.tsx`), formComponent, meta);
|
|
997
|
+
session.writeFile(path.join(newDir, "page.tsx"), newPage, meta);
|
|
998
|
+
session.writeFile(path.join(editDir, "page.tsx"), editPage, meta);
|
|
994
999
|
if (authLayout) {
|
|
995
|
-
|
|
1000
|
+
session.writeFile(path.join(modelDir, "layout.tsx"), authLayout, meta);
|
|
996
1001
|
}
|
|
997
1002
|
|
|
998
1003
|
console.log(`✅ Created: ${path.join(modelDir, "page.tsx")}`);
|
|
@@ -1008,7 +1013,7 @@ export async function generateUIComponents(modelInfo: ModelInfo, sharedPackageNa
|
|
|
1008
1013
|
/**
|
|
1009
1014
|
* Update navigation menu on the homepage
|
|
1010
1015
|
*/
|
|
1011
|
-
async function updateNavigationMenu(modelInfo: any): Promise<void> {
|
|
1016
|
+
async function updateNavigationMenu(modelInfo: any, session: FileOperationSession): Promise<void> {
|
|
1012
1017
|
console.log("\n📋 Updating navigation menu...");
|
|
1013
1018
|
|
|
1014
1019
|
const modelKebab = toKebabCase(modelInfo.name);
|
|
@@ -1017,13 +1022,13 @@ async function updateNavigationMenu(modelInfo: any): Promise<void> {
|
|
|
1017
1022
|
// Update scaffold config
|
|
1018
1023
|
const configPath = path.join(cwd, "lib", "scaffold-config.ts");
|
|
1019
1024
|
|
|
1020
|
-
if (!
|
|
1025
|
+
if (!session.fileExists(configPath)) {
|
|
1021
1026
|
console.log("⚠️ scaffold-config.ts not found. Skipping navigation menu update.");
|
|
1022
1027
|
return;
|
|
1023
1028
|
}
|
|
1024
1029
|
|
|
1025
1030
|
// Read existing config
|
|
1026
|
-
const configContent =
|
|
1031
|
+
const configContent = session.readFile(configPath);
|
|
1027
1032
|
|
|
1028
1033
|
// Parse models array - extract each model entry
|
|
1029
1034
|
const models: Array<{ name: string; path: string; label: string }> = [];
|
|
@@ -1070,6 +1075,7 @@ ${models.map(m => ` { name: '${m.name}', path: '${m.path}', label: '${m.label
|
|
|
1070
1075
|
};
|
|
1071
1076
|
`;
|
|
1072
1077
|
|
|
1073
|
-
|
|
1078
|
+
session.writeFile(configPath, newConfigContent, { ownership: "extension-point", generator: SCAFFOLD_GENERATOR });
|
|
1074
1079
|
console.log(`✅ Added ${modelInfo.name} to navigation menu`);
|
|
1075
1080
|
}
|
|
1081
|
+
|