swallowkit 1.0.0-beta.9 → 1.0.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/README.ja.md +87 -181
- package/README.md +87 -238
- package/dist/__tests__/fixtures.d.ts +12 -0
- package/dist/__tests__/fixtures.d.ts.map +1 -1
- package/dist/__tests__/fixtures.js +101 -0
- package/dist/__tests__/fixtures.js.map +1 -1
- package/dist/cli/commands/add-auth.d.ts +15 -0
- package/dist/cli/commands/add-auth.d.ts.map +1 -0
- package/dist/cli/commands/add-auth.js +790 -0
- package/dist/cli/commands/add-auth.js.map +1 -0
- package/dist/cli/commands/add-connector.d.ts +20 -0
- package/dist/cli/commands/add-connector.d.ts.map +1 -0
- package/dist/cli/commands/add-connector.js +164 -0
- package/dist/cli/commands/add-connector.js.map +1 -0
- package/dist/cli/commands/cosmos-worktree-isolation.d.ts +25 -0
- package/dist/cli/commands/cosmos-worktree-isolation.d.ts.map +1 -0
- package/dist/cli/commands/cosmos-worktree-isolation.js +124 -0
- package/dist/cli/commands/cosmos-worktree-isolation.js.map +1 -0
- package/dist/cli/commands/create-model.d.ts +1 -4
- package/dist/cli/commands/create-model.d.ts.map +1 -1
- package/dist/cli/commands/create-model.js +20 -82
- package/dist/cli/commands/create-model.js.map +1 -1
- package/dist/cli/commands/dev-seeds.d.ts +27 -0
- package/dist/cli/commands/dev-seeds.d.ts.map +1 -1
- package/dist/cli/commands/dev-seeds.js +188 -6
- package/dist/cli/commands/dev-seeds.js.map +1 -1
- package/dist/cli/commands/dev.d.ts +30 -0
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +495 -129
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/init.d.ts +69 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +788 -766
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +24 -3
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +408 -189
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.d.ts +5 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +335 -58
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config.d.ts +13 -3
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +151 -8
- package/dist/core/config.js.map +1 -1
- package/dist/core/mock/connector-mock-server.d.ts +101 -0
- package/dist/core/mock/connector-mock-server.d.ts.map +1 -0
- package/dist/core/mock/connector-mock-server.js +480 -0
- package/dist/core/mock/connector-mock-server.js.map +1 -0
- package/dist/core/mock/zod-mock-generator.d.ts +14 -0
- package/dist/core/mock/zod-mock-generator.d.ts.map +1 -0
- package/dist/core/mock/zod-mock-generator.js +163 -0
- package/dist/core/mock/zod-mock-generator.js.map +1 -0
- package/dist/core/operations/auth-operations.d.ts +61 -0
- package/dist/core/operations/auth-operations.d.ts.map +1 -0
- package/dist/core/operations/auth-operations.js +272 -0
- package/dist/core/operations/auth-operations.js.map +1 -0
- package/dist/core/operations/create-model.d.ts +15 -0
- package/dist/core/operations/create-model.d.ts.map +1 -0
- package/dist/core/operations/create-model.js +185 -0
- package/dist/core/operations/create-model.js.map +1 -0
- 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 +37 -0
- package/dist/core/operations/runtime.d.ts.map +1 -0
- package/dist/core/operations/runtime.js +248 -0
- package/dist/core/operations/runtime.js.map +1 -0
- 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 +16 -0
- package/dist/core/operations/scaffold-machine.d.ts.map +1 -0
- package/dist/core/operations/scaffold-machine.js +51 -0
- package/dist/core/operations/scaffold-machine.js.map +1 -0
- 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/agent-instructions.d.ts +29 -0
- package/dist/core/project/agent-instructions.d.ts.map +1 -0
- package/dist/core/project/agent-instructions.js +160 -0
- package/dist/core/project/agent-instructions.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/capabilities.d.ts +58 -0
- package/dist/core/project/capabilities.d.ts.map +1 -0
- package/dist/core/project/capabilities.js +135 -0
- package/dist/core/project/capabilities.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/manifest.d.ts +92 -0
- package/dist/core/project/manifest.d.ts.map +1 -0
- package/dist/core/project/manifest.js +321 -0
- package/dist/core/project/manifest.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/validation.d.ts +20 -0
- package/dist/core/project/validation.d.ts.map +1 -0
- package/dist/core/project/validation.js +274 -0
- package/dist/core/project/validation.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 +299 -0
- package/dist/core/project/workflows.js.map +1 -0
- package/dist/core/scaffold/auth-generator.d.ts +63 -0
- package/dist/core/scaffold/auth-generator.d.ts.map +1 -0
- package/dist/core/scaffold/auth-generator.js +1774 -0
- package/dist/core/scaffold/auth-generator.js.map +1 -0
- package/dist/core/scaffold/connector-functions-generator.d.ts +41 -0
- package/dist/core/scaffold/connector-functions-generator.d.ts.map +1 -0
- package/dist/core/scaffold/connector-functions-generator.js +1029 -0
- package/dist/core/scaffold/connector-functions-generator.js.map +1 -0
- package/dist/core/scaffold/functions-generator.d.ts +4 -3
- package/dist/core/scaffold/functions-generator.d.ts.map +1 -1
- package/dist/core/scaffold/functions-generator.js +405 -134
- package/dist/core/scaffold/functions-generator.js.map +1 -1
- package/dist/core/scaffold/model-parser.d.ts +20 -0
- package/dist/core/scaffold/model-parser.d.ts.map +1 -1
- package/dist/core/scaffold/model-parser.js +229 -78
- package/dist/core/scaffold/model-parser.js.map +1 -1
- package/dist/core/scaffold/native-schema-generator.d.ts +19 -0
- package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -0
- package/dist/core/scaffold/native-schema-generator.js +753 -0
- package/dist/core/scaffold/native-schema-generator.js.map +1 -0
- package/dist/core/scaffold/nextjs-generator.d.ts +8 -0
- package/dist/core/scaffold/nextjs-generator.d.ts.map +1 -1
- package/dist/core/scaffold/nextjs-generator.js +152 -8
- package/dist/core/scaffold/nextjs-generator.js.map +1 -1
- package/dist/core/scaffold/ui-generator.d.ts +17 -4
- package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
- package/dist/core/scaffold/ui-generator.js +174 -34
- package/dist/core/scaffold/ui-generator.js.map +1 -1
- package/dist/core/verify/index.d.ts +69 -0
- package/dist/core/verify/index.d.ts.map +1 -0
- package/dist/core/verify/index.js +412 -0
- package/dist/core/verify/index.js.map +1 -0
- package/dist/machine/contracts.d.ts +24 -0
- package/dist/machine/contracts.d.ts.map +1 -0
- package/dist/machine/contracts.js +3 -0
- package/dist/machine/contracts.js.map +1 -0
- package/dist/machine/errors.d.ts +14 -0
- package/dist/machine/errors.d.ts.map +1 -0
- package/dist/machine/errors.js +42 -0
- package/dist/machine/errors.js.map +1 -0
- package/dist/machine/index.d.ts +3 -0
- package/dist/machine/index.d.ts.map +1 -0
- package/dist/machine/index.js +566 -0
- package/dist/machine/index.js.map +1 -0
- package/dist/mcp/index.d.ts +25 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +425 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/types/index.d.ts +105 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/package-manager.d.ts +7 -11
- package/dist/utils/package-manager.d.ts.map +1 -1
- package/dist/utils/package-manager.js +42 -17
- package/dist/utils/package-manager.js.map +1 -1
- package/dist/utils/python-uv.d.ts +21 -0
- package/dist/utils/python-uv.d.ts.map +1 -0
- package/dist/utils/python-uv.js +111 -0
- package/dist/utils/python-uv.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +49 -0
- package/dist/version.js.map +1 -0
- package/package.json +15 -17
- package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +716 -22
- package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +19 -7
- package/src/__tests__/__snapshots__/ui-generator.test.ts.snap +20 -12
- package/src/__tests__/agent-instructions.test.ts +237 -0
- package/src/__tests__/agent-loop-phase34.test.ts +513 -0
- package/src/__tests__/auth.test.ts +770 -0
- package/src/__tests__/bff-swa-auth.test.ts +275 -0
- package/src/__tests__/config.test.ts +176 -3
- package/src/__tests__/connector-functions-generator.test.ts +288 -0
- package/src/__tests__/connector-mock-server.test.ts +459 -0
- package/src/__tests__/connector-model-bff.test.ts +162 -0
- package/src/__tests__/cosmos-worktree-isolation.test.ts +149 -0
- package/src/__tests__/dev-seeds.test.ts +124 -0
- package/src/__tests__/dev.test.ts +254 -0
- package/src/__tests__/file-session.test.ts +98 -0
- package/src/__tests__/fixtures.ts +101 -0
- package/src/__tests__/functions-generator.test.ts +137 -1
- package/src/__tests__/init.test.ts +366 -2
- package/src/__tests__/jest-globals.d.ts +8 -0
- package/src/__tests__/machine.test.ts +371 -0
- package/src/__tests__/mcp.test.ts +253 -0
- package/src/__tests__/model-parser.test.ts +142 -0
- package/src/__tests__/multi-auth.test.ts +160 -0
- package/src/__tests__/nextjs-generator.test.ts +8 -0
- package/src/__tests__/package-manager.test.ts +59 -4
- package/src/__tests__/plan-apply.test.ts +499 -0
- package/src/__tests__/python-uv.test.ts +48 -0
- package/src/__tests__/scaffold.test.ts +287 -24
- package/src/__tests__/tsconfig.json +15 -0
- package/src/__tests__/ui-generator.test.ts +104 -0
- package/src/__tests__/zod-mock-generator.test.ts +132 -0
- package/src/cli/commands/add-auth.ts +853 -0
- package/src/cli/commands/add-connector.ts +159 -0
- package/src/cli/commands/cosmos-worktree-isolation.ts +130 -0
- package/src/cli/commands/create-model.ts +21 -100
- package/src/cli/commands/dev-seeds.ts +278 -7
- package/src/cli/commands/dev.ts +607 -160
- package/src/cli/commands/init.ts +851 -786
- package/src/cli/commands/scaffold.ts +531 -236
- package/src/cli/index.ts +338 -60
- package/src/core/config.ts +145 -9
- package/src/core/mock/connector-mock-server.ts +555 -0
- package/src/core/mock/zod-mock-generator.ts +205 -0
- package/src/core/operations/auth-operations.ts +360 -0
- package/src/core/operations/create-model.ts +191 -0
- package/src/core/operations/file-session.ts +222 -0
- package/src/core/operations/provision-operations.ts +297 -0
- package/src/core/operations/runtime.ts +258 -0
- package/src/core/operations/scaffold-apply.ts +106 -0
- package/src/core/operations/scaffold-machine.ts +77 -0
- package/src/core/operations/scaffold-plan.ts +215 -0
- package/src/core/project/agent-instructions.ts +167 -0
- package/src/core/project/agent-templates/AGENTS.md +504 -0
- package/src/core/project/agent-templates/CLAUDE.md +31 -0
- package/src/core/project/agent-templates/azure-functions.instructions.md +22 -0
- package/src/core/project/agent-templates/bff-routes.instructions.md +18 -0
- package/src/core/project/agent-templates/copilot-instructions.md +27 -0
- package/src/core/project/agent-templates/shared-models.instructions.md +72 -0
- package/src/core/project/artifacts.ts +205 -0
- package/src/core/project/boundaries.ts +170 -0
- package/src/core/project/capabilities.ts +193 -0
- package/src/core/project/drift.ts +202 -0
- package/src/core/project/infra.ts +196 -0
- package/src/core/project/manifest.ts +402 -0
- package/src/core/project/state.ts +77 -0
- package/src/core/project/validation.ts +295 -0
- package/src/core/project/workflows.ts +295 -0
- package/src/core/scaffold/auth-generator.ts +1824 -0
- package/src/core/scaffold/connector-functions-generator.ts +1133 -0
- package/src/core/scaffold/functions-generator.ts +432 -134
- package/src/core/scaffold/model-parser.ts +244 -90
- package/src/core/scaffold/native-schema-generator.ts +904 -0
- package/src/core/scaffold/nextjs-generator.ts +174 -9
- package/src/core/scaffold/ui-generator.ts +191 -36
- package/src/core/verify/index.ts +468 -0
- package/src/machine/contracts.ts +33 -0
- package/src/machine/errors.ts +45 -0
- package/src/machine/index.ts +690 -0
- package/src/mcp/index.ts +423 -0
- package/src/types/index.ts +137 -0
- package/src/utils/package-manager.ts +41 -19
- package/src/utils/python-uv.ts +96 -0
- package/src/version.ts +13 -0
|
@@ -38,21 +38,51 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
38
38
|
})();
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
40
|
exports.scaffoldCommand = scaffoldCommand;
|
|
41
|
-
exports.
|
|
42
|
-
exports.
|
|
41
|
+
exports.resolveAuthPolicy = resolveAuthPolicy;
|
|
42
|
+
exports.resolveModelPath = resolveModelPath;
|
|
43
|
+
exports.createUIAuthOptions = createUIAuthOptions;
|
|
44
|
+
exports.generateCosmosContainer = generateCosmosContainer;
|
|
45
|
+
exports.generateUIComponents = generateUIComponents;
|
|
43
46
|
const fs = __importStar(require("fs"));
|
|
44
47
|
const path = __importStar(require("path"));
|
|
45
48
|
const child_process_1 = require("child_process");
|
|
46
49
|
const config_1 = require("../../core/config");
|
|
47
50
|
const model_parser_1 = require("../../core/scaffold/model-parser");
|
|
48
51
|
const functions_generator_1 = require("../../core/scaffold/functions-generator");
|
|
52
|
+
const connector_functions_generator_1 = require("../../core/scaffold/connector-functions-generator");
|
|
49
53
|
const nextjs_generator_1 = require("../../core/scaffold/nextjs-generator");
|
|
50
|
-
const openapi_generator_1 = require("../../core/scaffold/openapi-generator");
|
|
51
54
|
const ui_generator_1 = require("../../core/scaffold/ui-generator");
|
|
55
|
+
const native_schema_generator_1 = require("../../core/scaffold/native-schema-generator");
|
|
52
56
|
const package_manager_1 = require("../../utils/package-manager");
|
|
57
|
+
const manifest_1 = require("../../core/project/manifest");
|
|
58
|
+
const auth_generator_1 = require("../../core/scaffold/auth-generator");
|
|
59
|
+
const file_session_1 = require("../../core/operations/file-session");
|
|
60
|
+
const artifacts_1 = require("../../core/project/artifacts");
|
|
61
|
+
const SCAFFOLD_GENERATOR = "scaffold";
|
|
62
|
+
function getMachineAwareStdio() {
|
|
63
|
+
return process.env.SWALLOWKIT_MACHINE_OUTPUT === "1" ? "pipe" : "inherit";
|
|
64
|
+
}
|
|
65
|
+
function runSpawnSyncCommand(command, args, cwd) {
|
|
66
|
+
const result = (0, child_process_1.spawnSync)(command, args, {
|
|
67
|
+
cwd,
|
|
68
|
+
stdio: getMachineAwareStdio(),
|
|
69
|
+
shell: true,
|
|
70
|
+
});
|
|
71
|
+
if (typeof result.status === "number" && result.status !== 0) {
|
|
72
|
+
throw new Error(`${command} ${args.join(" ")} exited with code ${result.status}`);
|
|
73
|
+
}
|
|
74
|
+
if (result.error) {
|
|
75
|
+
throw result.error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
53
78
|
async function scaffoldCommand(options) {
|
|
54
79
|
// SwallowKit プロジェクトディレクトリかどうかを検証
|
|
55
80
|
(0, config_1.ensureSwallowKitProject)("scaffold");
|
|
81
|
+
const configValidation = (0, config_1.validateConfig)((0, config_1.getFullConfig)());
|
|
82
|
+
if (!configValidation.valid)
|
|
83
|
+
throw new Error(`Invalid SwallowKit configuration:\n${configValidation.errors.map(error => `- ${error}`).join("\n")}`);
|
|
84
|
+
// Plan(dry-run) や Apply からセッションが供給される。単体実行時は commit セッションを生成。
|
|
85
|
+
const session = (0, file_session_1.getActiveFileSession)() ?? new file_session_1.FileOperationSession("commit");
|
|
56
86
|
console.log("🏗️ SwallowKit Scaffold: Generating CRUD operations...\n");
|
|
57
87
|
try {
|
|
58
88
|
// 1. Resolve model file path
|
|
@@ -64,6 +94,12 @@ async function scaffoldCommand(options) {
|
|
|
64
94
|
console.log(`✅ Model parsed: ${modelInfo.name} (${modelInfo.schemaName})`);
|
|
65
95
|
const backendLanguage = (0, config_1.getBackendLanguage)();
|
|
66
96
|
console.log(`🧠 Backend language: ${backendLanguage}`);
|
|
97
|
+
// コネクタモデルかどうかを判定
|
|
98
|
+
const isConnectorModel = !!modelInfo.connectorConfig;
|
|
99
|
+
if (isConnectorModel) {
|
|
100
|
+
console.log(`🔌 Connector model detected: ${modelInfo.connectorConfig.connector}`);
|
|
101
|
+
console.log(` Operations: ${modelInfo.connectorConfig.operations.join(", ")}`);
|
|
102
|
+
}
|
|
67
103
|
// ネストスキーマ参照があれば表示
|
|
68
104
|
if (modelInfo.nestedSchemaRefs.length > 0) {
|
|
69
105
|
console.log(`🔗 Nested schema references detected:`);
|
|
@@ -83,22 +119,61 @@ async function scaffoldCommand(options) {
|
|
|
83
119
|
const relatedModels = backendLanguage === "typescript"
|
|
84
120
|
? [modelInfo]
|
|
85
121
|
: await collectModelGraph(modelPath);
|
|
122
|
+
for (const relatedModel of relatedModels) {
|
|
123
|
+
session.registerInput(relatedModel.filePath);
|
|
124
|
+
}
|
|
125
|
+
// 4b. 書き込み前に外部 codegen toolchain を検証し、失敗時はプロジェクト無変更で終了する
|
|
126
|
+
if (session.mode === "commit" && backendLanguage !== "typescript" && !isConnectorModel) {
|
|
127
|
+
await (0, native_schema_generator_1.validateNativeSchemaToolchain)(relatedModels, modelInfo, backendLanguage, path.join(process.cwd(), functionsDir));
|
|
128
|
+
}
|
|
129
|
+
if (options.apiOnly) {
|
|
130
|
+
console.log("ℹ️ --api-only skips UI generation only; Functions, BFF routes, OpenAPI, and native schema assets are still updated.");
|
|
131
|
+
}
|
|
86
132
|
// 5. Generate BFF callFunction helper
|
|
87
|
-
await generateCallFunctionHelper();
|
|
133
|
+
await generateCallFunctionHelper(session);
|
|
88
134
|
// 6. Generate Azure Functions code
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
135
|
+
if (isConnectorModel) {
|
|
136
|
+
await generateConnectorFunctionsCode(modelInfo, functionsDir, sharedPackageName, backendLanguage, session);
|
|
137
|
+
// 6b. Install connector driver dependencies
|
|
138
|
+
await installConnectorDriverDependencies(modelInfo, functionsDir, backendLanguage, session);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
await generateFunctionsCode(modelInfo, functionsDir, sharedPackageName, backendLanguage, session);
|
|
142
|
+
if (backendLanguage !== "typescript") {
|
|
143
|
+
await (0, native_schema_generator_1.generateLanguageSchemaArtifacts)(relatedModels, modelInfo, functionsDir, backendLanguage, session);
|
|
144
|
+
}
|
|
92
145
|
}
|
|
93
146
|
// 7. Generate Next.js BFF API Routes
|
|
94
147
|
const apiDir = options.apiDir || "app/api";
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
148
|
+
if (isConnectorModel) {
|
|
149
|
+
await generateConnectorBFFRoutesFiles(modelInfo, apiDir, sharedPackageName, session);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
await generateBFFRoutes(modelInfo, apiDir, sharedPackageName, session);
|
|
153
|
+
}
|
|
154
|
+
// 8. Generate Cosmos DB container Bicep file (skip for connector models)
|
|
155
|
+
if (!isConnectorModel) {
|
|
156
|
+
await generateCosmosContainer(modelInfo, session);
|
|
157
|
+
}
|
|
98
158
|
// 9. Generate UI components (unless --api-only)
|
|
99
159
|
if (!options.apiOnly) {
|
|
100
|
-
|
|
101
|
-
|
|
160
|
+
const uiAuthConfig = (0, config_1.getAuthConfig)();
|
|
161
|
+
const uiAuthPolicy = resolveAuthPolicy(modelInfo, uiAuthConfig);
|
|
162
|
+
const uiAuthOptions = uiAuthPolicy && uiAuthConfig && uiAuthConfig.provider !== 'none'
|
|
163
|
+
? createUIAuthOptions(uiAuthPolicy, uiAuthConfig)
|
|
164
|
+
: undefined;
|
|
165
|
+
await generateUIComponents(modelInfo, sharedPackageName, uiAuthOptions, session);
|
|
166
|
+
if (uiAuthOptions?.authenticatedFetchImport) {
|
|
167
|
+
applyExternalTokenFetchToGeneratedUi(modelInfo, session, uiAuthOptions.authenticatedFetchImport);
|
|
168
|
+
}
|
|
169
|
+
await updateNavigationMenu(modelInfo, session);
|
|
170
|
+
}
|
|
171
|
+
if (session.mode === "commit") {
|
|
172
|
+
(0, artifacts_1.recordSessionOperations)(session.operations, {
|
|
173
|
+
schemaHash: (0, file_session_1.hashFileIfExists)(modelPath) ?? undefined,
|
|
174
|
+
sourceModel: modelInfo.name,
|
|
175
|
+
});
|
|
176
|
+
await (0, manifest_1.syncProjectManifest)();
|
|
102
177
|
}
|
|
103
178
|
console.log("\n✅ Scaffold completed successfully!");
|
|
104
179
|
console.log("\n📝 Next steps:");
|
|
@@ -108,17 +183,68 @@ async function scaffoldCommand(options) {
|
|
|
108
183
|
console.log(" 3. Navigate to the model from the homepage menu");
|
|
109
184
|
}
|
|
110
185
|
if (backendLanguage !== "typescript") {
|
|
111
|
-
console.log(` ${options.apiOnly ? "2" : "4"}. Review generated OpenAPI assets in ${functionsDir}/openapi/ and ${functionsDir}/generated/`);
|
|
186
|
+
console.log(` ${options.apiOnly ? "2" : "4"}. Review generated OpenAPI export and native schema assets in ${functionsDir}/openapi/ and ${functionsDir}/generated/`);
|
|
112
187
|
}
|
|
113
188
|
console.log(` ${options.apiOnly ? (backendLanguage === "typescript" ? "2" : "3") : (backendLanguage === "typescript" ? "4" : "5")}. Ensure BACKEND_FUNCTIONS_BASE_URL is set in your .env.local file`);
|
|
114
189
|
console.log(` ${options.apiOnly ? (backendLanguage === "typescript" ? "3" : "4") : (backendLanguage === "typescript" ? "5" : "6")}. Configure CosmosDBConnection in functions/local.settings.json`);
|
|
115
|
-
console.log(` ${options.apiOnly ? (backendLanguage === "typescript" ? "4" : "5") : (backendLanguage === "typescript" ? "6" : "7")}. Run '${(0, package_manager_1.getCommands)((0, package_manager_1.detectFromProject)()).
|
|
190
|
+
console.log(` ${options.apiOnly ? (backendLanguage === "typescript" ? "4" : "5") : (backendLanguage === "typescript" ? "6" : "7")}. Run '${(0, package_manager_1.getCommands)((0, package_manager_1.detectFromProject)()).exec} swallowkit dev' to test the generated code`);
|
|
116
191
|
}
|
|
117
192
|
catch (error) {
|
|
118
193
|
console.error("\n❌ Scaffold failed:", error.message);
|
|
119
194
|
process.exit(1);
|
|
120
195
|
}
|
|
121
196
|
}
|
|
197
|
+
function applyExternalTokenFetchToGeneratedUi(modelInfo, session, importPath = '@/lib/auth/authenticated-fetch') {
|
|
198
|
+
const uiPrefix = `app/${(0, model_parser_1.toKebabCase)(modelInfo.name)}/`;
|
|
199
|
+
for (const operation of session.operations) {
|
|
200
|
+
if (!operation.path.startsWith(uiPrefix) || !operation.path.endsWith('.tsx') || operation.action === 'delete')
|
|
201
|
+
continue;
|
|
202
|
+
let content = session.readFile(operation.absolutePath);
|
|
203
|
+
if (!content.includes('fetch('))
|
|
204
|
+
continue;
|
|
205
|
+
content = content.replace(/\bfetch\(/g, 'authenticatedFetch(');
|
|
206
|
+
content = content.replace(/(if \(!res\.ok\) throw new Error\([^\n]+\);)/g, `if (res.status === 401) throw new Error('Authentication required');\n $1`);
|
|
207
|
+
if (!content.includes(importPath)) {
|
|
208
|
+
const directive = content.match(/^'use client';\s*\r?\n/)?.[0] ?? '';
|
|
209
|
+
content = `${directive}import { authenticatedFetch } from '${importPath}';\n${content.slice(directive.length)}`;
|
|
210
|
+
}
|
|
211
|
+
session.writeFile(operation.absolutePath, content, {
|
|
212
|
+
ownership: 'managed',
|
|
213
|
+
generator: SCAFFOLD_GENERATOR,
|
|
214
|
+
sourceModel: modelInfo.name,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* モデルの authPolicy と auth config から実効的な認可ポリシーを解決
|
|
220
|
+
* - モデルに authPolicy があればそれを使用
|
|
221
|
+
* - なければ auth.authorization.defaultPolicy に従う
|
|
222
|
+
* - auth 設定がなければ undefined(ガードなし)
|
|
223
|
+
*/
|
|
224
|
+
function resolveAuthPolicy(modelInfo, authConfig) {
|
|
225
|
+
// モデルに明示的な authPolicy がある場合はそれを使用
|
|
226
|
+
if (modelInfo.authPolicy) {
|
|
227
|
+
const normalized = (0, config_1.normalizeAuthConfig)(authConfig);
|
|
228
|
+
for (const policyName of [modelInfo.authPolicy.policy, typeof modelInfo.authPolicy.read === 'string' ? modelInfo.authPolicy.read : undefined, typeof modelInfo.authPolicy.write === 'string' ? modelInfo.authPolicy.write : undefined].filter((value) => Boolean(value))) {
|
|
229
|
+
if (!normalized?.authorization.policies[policyName])
|
|
230
|
+
throw new Error(`Model '${modelInfo.name}' references undefined auth policy '${policyName}'. Define auth.authorization.policies.${policyName}.`);
|
|
231
|
+
}
|
|
232
|
+
return modelInfo.authPolicy;
|
|
233
|
+
}
|
|
234
|
+
// auth 設定がない場合はガードなし
|
|
235
|
+
if (!authConfig || authConfig.provider === 'none') {
|
|
236
|
+
return undefined;
|
|
237
|
+
}
|
|
238
|
+
// defaultPolicy が 'authenticated' なら認証のみ(ロール指定なし)のポリシーを返す
|
|
239
|
+
const defaultPolicy = authConfig.authorization?.defaultPolicy === 'public' ? 'anonymous' : authConfig.authorization?.defaultPolicy ?? 'authenticated';
|
|
240
|
+
if (defaultPolicy === 'authenticated') {
|
|
241
|
+
return {}; // 空のポリシー = 認証のみ、ロール制限なし
|
|
242
|
+
}
|
|
243
|
+
if (defaultPolicy !== 'anonymous')
|
|
244
|
+
return { policy: defaultPolicy };
|
|
245
|
+
// defaultPolicy が 'anonymous' なら認証不要
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
122
248
|
/**
|
|
123
249
|
* モデルファイルのパスを解決
|
|
124
250
|
*/
|
|
@@ -176,50 +302,254 @@ function readSharedPackageName() {
|
|
|
176
302
|
/**
|
|
177
303
|
* Generate BFF callFunction helper (lib/api/call-function.ts)
|
|
178
304
|
*/
|
|
179
|
-
async function generateCallFunctionHelper() {
|
|
305
|
+
async function generateCallFunctionHelper(session) {
|
|
180
306
|
console.log("\n📦 Generating BFF callFunction helper...");
|
|
181
307
|
const cwd = process.cwd();
|
|
182
308
|
const helperDir = path.join(cwd, "lib", "api");
|
|
183
309
|
const helperPath = path.join(helperDir, "call-function.ts");
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
|
|
310
|
+
// auth 設定がある場合は Authorization ヘッダー転送版を生成
|
|
311
|
+
const authConfig = (0, config_1.getAuthConfig)();
|
|
312
|
+
const normalizedAuth = (0, config_1.normalizeAuthConfig)(authConfig);
|
|
313
|
+
const hasAuth = Boolean(normalizedAuth && Object.keys(normalizedAuth.schemes).length > 0);
|
|
314
|
+
let helperCode;
|
|
315
|
+
if (hasAuth) {
|
|
316
|
+
const providers = new Set(Object.values(normalizedAuth.schemes).map(scheme => scheme.provider));
|
|
317
|
+
helperCode = normalizedAuth.schemes.default && Object.keys(normalizedAuth.schemes).length === 1
|
|
318
|
+
? authConfig?.provider === "swa" ? (0, auth_generator_1.generateBFFCallFunctionWithSwaAuth)() : (0, auth_generator_1.generateBFFCallFunctionWithAuth)()
|
|
319
|
+
: providers.has("swa") ? (0, auth_generator_1.generateBFFCallFunctionWithMultipleAuth)() : (0, auth_generator_1.generateBFFCallFunctionWithAuth)();
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
helperCode = (0, nextjs_generator_1.generateBFFCallFunction)();
|
|
323
|
+
}
|
|
324
|
+
session.writeFile(helperPath, helperCode, { ownership: "managed", generator: SCAFFOLD_GENERATOR });
|
|
189
325
|
console.log(`✅ Created: ${helperPath}`);
|
|
190
326
|
}
|
|
191
327
|
/**
|
|
192
328
|
* Generate Azure Functions CRUD code
|
|
193
329
|
*/
|
|
194
|
-
async function generateFunctionsCode(modelInfo, functionsDir, sharedPackageName, backendLanguage) {
|
|
330
|
+
async function generateFunctionsCode(modelInfo, functionsDir, sharedPackageName, backendLanguage, session) {
|
|
195
331
|
console.log("\n🔨 Generating Azure Functions CRUD code...");
|
|
332
|
+
// Resolve auth policy: model-level authPolicy or global defaultPolicy
|
|
333
|
+
const authConfig = (0, config_1.getAuthConfig)();
|
|
334
|
+
const authPolicy = resolveAuthPolicy(modelInfo, authConfig);
|
|
335
|
+
if (authPolicy) {
|
|
336
|
+
console.log(`🔐 Auth policy detected: ${JSON.stringify(authPolicy)}`);
|
|
337
|
+
}
|
|
196
338
|
const modelKebab = (0, model_parser_1.toKebabCase)(modelInfo.name);
|
|
339
|
+
const managedMeta = { ownership: "managed", generator: SCAFFOLD_GENERATOR, sourceModel: modelInfo.name };
|
|
197
340
|
if (backendLanguage === "typescript") {
|
|
341
|
+
const normalizedAuth = (0, config_1.getNormalizedAuthConfig)();
|
|
342
|
+
if (normalizedAuth && Object.keys(normalizedAuth.schemes).some(name => name !== "default")) {
|
|
343
|
+
const routerPath = path.join(process.cwd(), functionsDir, "src", "auth", "auth-router.ts");
|
|
344
|
+
session.writeFile(routerPath, (0, auth_generator_1.generateNamedAuthRouterTS)(normalizedAuth), { ownership: "managed", generator: SCAFFOLD_GENERATOR });
|
|
345
|
+
}
|
|
198
346
|
const functionFilePath = path.join(process.cwd(), functionsDir, "src", `${modelKebab}.ts`);
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
347
|
+
const code = (0, functions_generator_1.generateCompactAzureFunctionsCRUD)(modelInfo, sharedPackageName, authPolicy, authConfig?.provider);
|
|
348
|
+
session.writeFile(functionFilePath, code, managedMeta);
|
|
349
|
+
console.log(`✅ Created: ${functionFilePath}`);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
if (backendLanguage === "csharp") {
|
|
353
|
+
const normalizedAuth = (0, config_1.getNormalizedAuthConfig)();
|
|
354
|
+
if (normalizedAuth && Object.keys(normalizedAuth.schemes).some(name => name !== "default")) {
|
|
355
|
+
const routerPath = path.join(process.cwd(), functionsDir, "Auth", "AuthRouter.cs");
|
|
356
|
+
session.writeFile(routerPath, (0, auth_generator_1.generateNamedAuthRouterCSharp)(normalizedAuth), { ownership: "managed", generator: SCAFFOLD_GENERATOR });
|
|
202
357
|
}
|
|
203
|
-
const
|
|
204
|
-
|
|
358
|
+
const crudDir = path.join(process.cwd(), functionsDir, "Crud");
|
|
359
|
+
const functionFilePath = path.join(crudDir, `${modelInfo.name}CrudFunctions.cs`);
|
|
360
|
+
// Remove init-generated template (singular) to avoid route conflicts
|
|
361
|
+
const templatePath = path.join(crudDir, `${modelInfo.name}Function.cs`);
|
|
362
|
+
if (session.fileExists(templatePath)) {
|
|
363
|
+
session.deleteFile(templatePath, managedMeta);
|
|
364
|
+
console.log(`🗑️ Removed template: ${templatePath}`);
|
|
365
|
+
}
|
|
366
|
+
session.writeFile(functionFilePath, (0, functions_generator_1.generateCSharpAzureFunctionsCRUD)(modelInfo, authPolicy), managedMeta);
|
|
367
|
+
console.log(`✅ Created: ${functionFilePath}`);
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const normalizedAuth = (0, config_1.getNormalizedAuthConfig)();
|
|
371
|
+
if (normalizedAuth && Object.keys(normalizedAuth.schemes).some(name => name !== "default")) {
|
|
372
|
+
const routerPath = path.join(process.cwd(), functionsDir, "auth", "auth_router.py");
|
|
373
|
+
session.writeFile(routerPath, (0, auth_generator_1.generateNamedAuthRouterPython)(normalizedAuth), { ownership: "managed", generator: SCAFFOLD_GENERATOR });
|
|
374
|
+
}
|
|
375
|
+
const blueprintsDir = path.join(process.cwd(), functionsDir, "blueprints");
|
|
376
|
+
const blueprintPath = path.join(blueprintsDir, `${modelKebab.replace(/-/g, "_")}.py`);
|
|
377
|
+
const { blueprint, registration } = (0, functions_generator_1.generatePythonAzureFunctionsCRUD)(modelInfo, authPolicy);
|
|
378
|
+
session.writeFile(blueprintPath, blueprint, managedMeta);
|
|
379
|
+
updatePythonFunctionRegistrations(path.join(process.cwd(), functionsDir, "function_app.py"), registration, session);
|
|
380
|
+
console.log(`✅ Created: ${blueprintPath}`);
|
|
381
|
+
}
|
|
382
|
+
function createUIAuthOptions(policy, authConfig) {
|
|
383
|
+
const normalized = (0, config_1.normalizeAuthConfig)(authConfig);
|
|
384
|
+
const readPolicyName = typeof policy.read === 'string' ? policy.read : policy.policy;
|
|
385
|
+
const writePolicyName = typeof policy.write === 'string' ? policy.write : policy.policy;
|
|
386
|
+
const readNamed = readPolicyName ? normalized?.authorization.policies[readPolicyName] : undefined;
|
|
387
|
+
const named = writePolicyName ? normalized?.authorization.policies[writePolicyName] : undefined;
|
|
388
|
+
const writeRoles = named?.roles ?? (Array.isArray(policy.write) ? policy.write : policy.roles) ?? [];
|
|
389
|
+
const externalScheme = [...(readNamed?.schemes ?? []), ...(named?.schemes ?? [])].find(name => normalized?.schemes[name]?.provider === 'external-token');
|
|
390
|
+
const authScheme = readNamed?.schemes[0] ?? named?.schemes[0];
|
|
391
|
+
const authenticatedFetchImport = externalScheme ? `@/lib/auth/schemes/${(0, model_parser_1.toKebabCase)(externalScheme)}/authenticated-fetch` : authConfig.provider === 'external-token' ? '@/lib/auth/authenticated-fetch' : undefined;
|
|
392
|
+
const authContextImport = authScheme ? `@/lib/auth/schemes/${(0, model_parser_1.toKebabCase)(authScheme)}/auth-context` : '@/lib/auth/auth-context';
|
|
393
|
+
return { authPolicy: policy, writeRoles, authenticatedFetchImport, authContextImport };
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* コネクタモデル用 Azure Functions コード生成
|
|
397
|
+
*/
|
|
398
|
+
async function generateConnectorFunctionsCode(modelInfo, functionsDir, sharedPackageName, backendLanguage, session) {
|
|
399
|
+
console.log("\n🔌 Generating Connector Azure Functions code...");
|
|
400
|
+
const connectorConfig = modelInfo.connectorConfig;
|
|
401
|
+
const connectorDef = (0, config_1.getConnectorDefinition)(connectorConfig.connector);
|
|
402
|
+
if (!connectorDef) {
|
|
403
|
+
throw new Error(`Connector '${connectorConfig.connector}' not found in swallowkit.config.js.\n` +
|
|
404
|
+
` Please add it to the 'connectors' section of your configuration.`);
|
|
405
|
+
}
|
|
406
|
+
// Resolve auth policy (same logic as Cosmos model scaffolding)
|
|
407
|
+
const authConfig = (0, config_1.getAuthConfig)();
|
|
408
|
+
const authPolicy = resolveAuthPolicy(modelInfo, authConfig);
|
|
409
|
+
if (authPolicy) {
|
|
410
|
+
console.log(`🔐 Auth policy detected: ${JSON.stringify(authPolicy)}`);
|
|
411
|
+
}
|
|
412
|
+
const modelKebab = (0, model_parser_1.toKebabCase)(modelInfo.name);
|
|
413
|
+
const connectorManagedMeta = { ownership: "managed", generator: SCAFFOLD_GENERATOR, sourceModel: modelInfo.name };
|
|
414
|
+
if (backendLanguage === "typescript") {
|
|
415
|
+
const functionFilePath = path.join(process.cwd(), functionsDir, "src", `${modelKebab}.ts`);
|
|
416
|
+
let code;
|
|
417
|
+
if (connectorDef.type === "rdb") {
|
|
418
|
+
code = (0, connector_functions_generator_1.generateRdbConnectorFunctionTS)(modelInfo, sharedPackageName, connectorDef, connectorConfig, authPolicy, authConfig?.provider);
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
code = (0, connector_functions_generator_1.generateApiConnectorFunctionTS)(modelInfo, sharedPackageName, connectorDef, connectorConfig, authPolicy, authConfig?.provider);
|
|
422
|
+
}
|
|
423
|
+
session.writeFile(functionFilePath, code, connectorManagedMeta);
|
|
205
424
|
console.log(`✅ Created: ${functionFilePath}`);
|
|
206
425
|
return;
|
|
207
426
|
}
|
|
208
427
|
if (backendLanguage === "csharp") {
|
|
209
|
-
const functionFilePath = path.join(process.cwd(), functionsDir, "
|
|
210
|
-
|
|
211
|
-
|
|
428
|
+
const functionFilePath = path.join(process.cwd(), functionsDir, "Connectors", `${modelInfo.name}ConnectorFunctions.cs`);
|
|
429
|
+
let code;
|
|
430
|
+
if (connectorDef.type === "rdb") {
|
|
431
|
+
code = (0, connector_functions_generator_1.generateRdbConnectorFunctionCSharp)(modelInfo, connectorDef, connectorConfig);
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
code = (0, connector_functions_generator_1.generateApiConnectorFunctionCSharp)(modelInfo, connectorDef, connectorConfig);
|
|
435
|
+
}
|
|
436
|
+
session.writeFile(functionFilePath, code, connectorManagedMeta);
|
|
212
437
|
console.log(`✅ Created: ${functionFilePath}`);
|
|
213
438
|
return;
|
|
214
439
|
}
|
|
440
|
+
// Python
|
|
215
441
|
const blueprintsDir = path.join(process.cwd(), functionsDir, "blueprints");
|
|
216
442
|
const blueprintPath = path.join(blueprintsDir, `${modelKebab.replace(/-/g, "_")}.py`);
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
443
|
+
let result;
|
|
444
|
+
if (connectorDef.type === "rdb") {
|
|
445
|
+
result = (0, connector_functions_generator_1.generateRdbConnectorFunctionPython)(modelInfo, connectorDef, connectorConfig);
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
result = (0, connector_functions_generator_1.generateApiConnectorFunctionPython)(modelInfo, connectorDef, connectorConfig);
|
|
449
|
+
}
|
|
450
|
+
session.writeFile(blueprintPath, result.blueprint, connectorManagedMeta);
|
|
451
|
+
updatePythonFunctionRegistrations(path.join(process.cwd(), functionsDir, "function_app.py"), result.registration, session);
|
|
221
452
|
console.log(`✅ Created: ${blueprintPath}`);
|
|
222
453
|
}
|
|
454
|
+
/**
|
|
455
|
+
* コネクタモデルの scaffold 後に、生成コードが必要とする RDB/API ドライバを
|
|
456
|
+
* functions ディレクトリにインストールする。
|
|
457
|
+
*/
|
|
458
|
+
async function installConnectorDriverDependencies(modelInfo, functionsDir, backendLanguage, session) {
|
|
459
|
+
const connectorConfig = modelInfo.connectorConfig;
|
|
460
|
+
const connectorDef = (0, config_1.getConnectorDefinition)(connectorConfig.connector);
|
|
461
|
+
if (!connectorDef || connectorDef.type !== "rdb")
|
|
462
|
+
return;
|
|
463
|
+
const rdbDef = connectorDef;
|
|
464
|
+
const functionsPath = path.join(process.cwd(), functionsDir);
|
|
465
|
+
if (backendLanguage === "typescript") {
|
|
466
|
+
const driverMap = {
|
|
467
|
+
mysql: { deps: ["mysql2"], devDeps: [] },
|
|
468
|
+
postgres: { deps: ["pg"], devDeps: ["@types/pg"] },
|
|
469
|
+
sqlserver: { deps: ["mssql"], devDeps: [] },
|
|
470
|
+
};
|
|
471
|
+
const entry = driverMap[rdbDef.provider];
|
|
472
|
+
if (!entry)
|
|
473
|
+
return;
|
|
474
|
+
// package.json を読んで、既にインストール済みならスキップ
|
|
475
|
+
const pkgJsonPath = path.join(functionsPath, "package.json");
|
|
476
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
477
|
+
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
|
|
478
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
479
|
+
const missingDeps = entry.deps.filter(d => !allDeps[d]);
|
|
480
|
+
const missingDevDeps = entry.devDeps.filter(d => !allDeps[d]);
|
|
481
|
+
if (missingDeps.length === 0 && missingDevDeps.length === 0)
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
if (session.mode === "collect") {
|
|
485
|
+
session.addWarning(`external-install: ${rdbDef.provider} driver dependencies (${[...entry.deps, ...entry.devDeps].join(", ")}) will be installed into ${functionsDir}/ on apply.`);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
console.log(`\n📦 Installing ${rdbDef.provider} driver dependencies...`);
|
|
489
|
+
const pm = (0, package_manager_1.detectFromProject)(functionsPath);
|
|
490
|
+
const cmds = (0, package_manager_1.getCommands)(pm);
|
|
491
|
+
if (entry.deps.length > 0) {
|
|
492
|
+
runSpawnSyncCommand(cmds.name, [pm === "pnpm" ? "add" : "install", ...entry.deps], functionsPath);
|
|
493
|
+
}
|
|
494
|
+
if (entry.devDeps.length > 0) {
|
|
495
|
+
runSpawnSyncCommand(cmds.name, [pm === "pnpm" ? "add" : "install", "-D", ...entry.devDeps], functionsPath);
|
|
496
|
+
}
|
|
497
|
+
console.log(`✅ ${rdbDef.provider} driver installed`);
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
if (backendLanguage === "csharp") {
|
|
501
|
+
const nugetMap = {
|
|
502
|
+
mysql: "MySqlConnector",
|
|
503
|
+
postgres: "Npgsql",
|
|
504
|
+
sqlserver: "Microsoft.Data.SqlClient",
|
|
505
|
+
};
|
|
506
|
+
const pkg = nugetMap[rdbDef.provider];
|
|
507
|
+
if (!pkg)
|
|
508
|
+
return;
|
|
509
|
+
if (session.mode === "collect") {
|
|
510
|
+
session.addWarning(`external-install: NuGet package ${pkg} will be added to ${functionsDir}/functions.csproj on apply.`);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
console.log(`\n📦 Installing ${rdbDef.provider} NuGet package...`);
|
|
514
|
+
runSpawnSyncCommand("dotnet", ["add", path.join(functionsPath, "functions.csproj"), "package", pkg], functionsPath);
|
|
515
|
+
console.log(`✅ ${pkg} installed`);
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
// Python — requirements.txt に追記
|
|
519
|
+
const pipMap = {
|
|
520
|
+
mysql: "mysql-connector-python",
|
|
521
|
+
postgres: "psycopg2-binary",
|
|
522
|
+
sqlserver: "pymssql",
|
|
523
|
+
};
|
|
524
|
+
const pipPkg = pipMap[rdbDef.provider];
|
|
525
|
+
if (!pipPkg)
|
|
526
|
+
return;
|
|
527
|
+
const reqPath = path.join(functionsPath, "requirements.txt");
|
|
528
|
+
if (session.fileExists(reqPath)) {
|
|
529
|
+
const existing = session.readFile(reqPath);
|
|
530
|
+
if (!existing.includes(pipPkg)) {
|
|
531
|
+
console.log(`\n📦 Adding ${pipPkg} to requirements.txt...`);
|
|
532
|
+
session.writeFile(reqPath, `${existing}\n${pipPkg}\n`, { ownership: "extension-point", generator: SCAFFOLD_GENERATOR });
|
|
533
|
+
console.log(`✅ ${pipPkg} added`);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* コネクタモデル用 BFF ルート生成(操作制限に対応)
|
|
539
|
+
*/
|
|
540
|
+
async function generateConnectorBFFRoutesFiles(modelInfo, apiDir, sharedPackageName, session) {
|
|
541
|
+
console.log("\n🔌 Generating Connector BFF API routes...");
|
|
542
|
+
const modelCamel = (0, model_parser_1.toCamelCase)(modelInfo.name);
|
|
543
|
+
const operations = modelInfo.connectorConfig.operations;
|
|
544
|
+
const listRoutePath = path.join(process.cwd(), apiDir, modelCamel, "route.ts");
|
|
545
|
+
const detailRoutePath = path.join(process.cwd(), apiDir, modelCamel, "[id]", "route.ts");
|
|
546
|
+
const routes = (0, nextjs_generator_1.generateConnectorBFFRoutes)(modelInfo, sharedPackageName, operations);
|
|
547
|
+
const meta = { ownership: "managed", generator: SCAFFOLD_GENERATOR, sourceModel: modelInfo.name };
|
|
548
|
+
session.writeFile(listRoutePath, routes.listRoute, meta);
|
|
549
|
+
session.writeFile(detailRoutePath, routes.detailRoute, meta);
|
|
550
|
+
console.log(`✅ Created: ${listRoutePath}`);
|
|
551
|
+
console.log(`✅ Created: ${detailRoutePath}`);
|
|
552
|
+
}
|
|
223
553
|
async function collectModelGraph(modelPath, seen = new Map()) {
|
|
224
554
|
const resolvedPath = path.resolve(modelPath);
|
|
225
555
|
if (seen.has(resolvedPath)) {
|
|
@@ -249,102 +579,16 @@ function describeFunctionsOutputPath(functionsDir, backendLanguage) {
|
|
|
249
579
|
}
|
|
250
580
|
return `${functionsDir}/blueprints/`;
|
|
251
581
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
fs.mkdirSync(openApiDir, { recursive: true });
|
|
256
|
-
const specPath = path.join(openApiDir, `${(0, model_parser_1.toKebabCase)(rootModel.name)}.openapi.json`);
|
|
257
|
-
fs.writeFileSync(specPath, (0, openapi_generator_1.generateOpenApiDocument)(models, rootModel), "utf-8");
|
|
258
|
-
console.log(`✅ Created: ${specPath}`);
|
|
259
|
-
const outputDir = path.join(process.cwd(), functionsDir, "generated", backendLanguage === "csharp" ? "csharp-models" : "python-models");
|
|
260
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
261
|
-
await runOpenApiGenerator(specPath, outputDir, backendLanguage);
|
|
262
|
-
if (backendLanguage === "csharp") {
|
|
263
|
-
pruneGeneratedCSharpArtifacts(outputDir);
|
|
264
|
-
}
|
|
265
|
-
console.log(`✅ Generated ${backendLanguage} schema assets: ${outputDir}`);
|
|
266
|
-
}
|
|
267
|
-
async function runOpenApiGenerator(specPath, outputDir, backendLanguage) {
|
|
268
|
-
const pm = (0, package_manager_1.detectFromProject)();
|
|
269
|
-
const command = pm === "pnpm" ? "pnpm" : "npx";
|
|
270
|
-
const args = pm === "pnpm"
|
|
271
|
-
? ["exec", "openapi-generator-cli", ...getOpenApiGeneratorArgs(specPath, outputDir, backendLanguage)]
|
|
272
|
-
: ["openapi-generator-cli", ...getOpenApiGeneratorArgs(specPath, outputDir, backendLanguage)];
|
|
273
|
-
await new Promise((resolve, reject) => {
|
|
274
|
-
const child = (0, child_process_1.spawn)(command, args, {
|
|
275
|
-
cwd: process.cwd(),
|
|
276
|
-
shell: true,
|
|
277
|
-
stdio: "inherit",
|
|
278
|
-
});
|
|
279
|
-
child.on("close", (code) => {
|
|
280
|
-
if (code === 0) {
|
|
281
|
-
resolve();
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
reject(new Error(`OpenAPI generator exited with code ${code}`));
|
|
285
|
-
});
|
|
286
|
-
child.on("error", reject);
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
function getOpenApiGeneratorArgs(specPath, outputDir, backendLanguage) {
|
|
290
|
-
const globalProperty = backendLanguage === "csharp"
|
|
291
|
-
? "models,supportingFiles,apis=false,modelDocs=false,modelTests=false"
|
|
292
|
-
: "models,apis=false,supportingFiles=false,modelDocs=false,modelTests=false";
|
|
293
|
-
const baseArgs = [
|
|
294
|
-
"generate",
|
|
295
|
-
"-i",
|
|
296
|
-
specPath,
|
|
297
|
-
"-o",
|
|
298
|
-
outputDir,
|
|
299
|
-
"--global-property",
|
|
300
|
-
globalProperty,
|
|
301
|
-
];
|
|
302
|
-
if (backendLanguage === "csharp") {
|
|
303
|
-
return [
|
|
304
|
-
...baseArgs,
|
|
305
|
-
"-g",
|
|
306
|
-
"csharp",
|
|
307
|
-
"--additional-properties",
|
|
308
|
-
"packageName=SwallowKitBackendModels,targetFramework=net8.0,nullableReferenceTypes=true",
|
|
309
|
-
];
|
|
310
|
-
}
|
|
311
|
-
return [
|
|
312
|
-
...baseArgs,
|
|
313
|
-
"-g",
|
|
314
|
-
"python",
|
|
315
|
-
"--additional-properties",
|
|
316
|
-
"packageName=backend_models,projectName=backend-models",
|
|
317
|
-
];
|
|
318
|
-
}
|
|
319
|
-
function getCSharpSchemaArtifactPruneTargets(outputDir) {
|
|
320
|
-
return [
|
|
321
|
-
path.join(outputDir, "src", "SwallowKitBackendModels.Test"),
|
|
322
|
-
path.join(outputDir, "src", "SwallowKitBackendModels", "Api"),
|
|
323
|
-
path.join(outputDir, "src", "SwallowKitBackendModels", "Extensions"),
|
|
324
|
-
];
|
|
325
|
-
}
|
|
326
|
-
function pruneGeneratedCSharpArtifacts(outputDir) {
|
|
327
|
-
for (const target of getCSharpSchemaArtifactPruneTargets(outputDir)) {
|
|
328
|
-
if (fs.existsSync(target)) {
|
|
329
|
-
fs.rmSync(target, { recursive: true, force: true });
|
|
330
|
-
}
|
|
582
|
+
function updatePythonFunctionRegistrations(functionAppPath, registration, session) {
|
|
583
|
+
if (!session.fileExists(functionAppPath)) {
|
|
584
|
+
throw new Error(`Python Functions entrypoint not found: ${functionAppPath}`);
|
|
331
585
|
}
|
|
332
|
-
const
|
|
333
|
-
if (
|
|
586
|
+
const content = session.readFile(functionAppPath);
|
|
587
|
+
// Check if import line already exists (handles init-generated layout)
|
|
588
|
+
const importLine = registration.split("\n").find((l) => l.startsWith("from ") || l.startsWith("import "));
|
|
589
|
+
if (importLine && content.includes(importLine)) {
|
|
334
590
|
return;
|
|
335
591
|
}
|
|
336
|
-
for (const entry of fs.readdirSync(clientDir, { withFileTypes: true })) {
|
|
337
|
-
if (!entry.isFile() || entry.name === "Option.cs") {
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
fs.rmSync(path.join(clientDir, entry.name), { force: true });
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
function updatePythonFunctionRegistrations(functionAppPath, registration) {
|
|
344
|
-
if (!fs.existsSync(functionAppPath)) {
|
|
345
|
-
throw new Error(`Python Functions entrypoint not found: ${functionAppPath}`);
|
|
346
|
-
}
|
|
347
|
-
const content = fs.readFileSync(functionAppPath, "utf-8");
|
|
348
592
|
if (content.includes(registration)) {
|
|
349
593
|
return;
|
|
350
594
|
}
|
|
@@ -353,40 +597,31 @@ function updatePythonFunctionRegistrations(functionAppPath, registration) {
|
|
|
353
597
|
throw new Error(`Could not find scaffold registration marker in ${functionAppPath}`);
|
|
354
598
|
}
|
|
355
599
|
const updated = content.replace(marker, `${registration}\n${marker}`);
|
|
356
|
-
|
|
600
|
+
session.writeFile(functionAppPath, updated, { ownership: "extension-point", generator: SCAFFOLD_GENERATOR });
|
|
357
601
|
}
|
|
358
602
|
/**
|
|
359
603
|
* Next.js BFF API Routes を生成
|
|
360
604
|
*/
|
|
361
|
-
async function generateBFFRoutes(modelInfo, apiDir, sharedPackageName) {
|
|
605
|
+
async function generateBFFRoutes(modelInfo, apiDir, sharedPackageName, session) {
|
|
362
606
|
console.log("\n🔨 Generating Next.js BFF API routes...");
|
|
363
|
-
const modelKebab = (0, model_parser_1.toKebabCase)(modelInfo.name);
|
|
364
607
|
const modelCamel = modelInfo.name.charAt(0).toLowerCase() + modelInfo.name.slice(1);
|
|
365
608
|
// List route: app/api/[model]/route.ts
|
|
366
609
|
const listRoutePath = path.join(process.cwd(), apiDir, modelCamel, "route.ts");
|
|
367
610
|
// Detail route: app/api/[model]/[id]/route.ts
|
|
368
611
|
const detailRoutePath = path.join(process.cwd(), apiDir, modelCamel, "[id]", "route.ts");
|
|
369
|
-
// ディレクトリを作成
|
|
370
|
-
const listRouteDir = path.dirname(listRoutePath);
|
|
371
|
-
const detailRouteDir = path.dirname(detailRoutePath);
|
|
372
|
-
if (!fs.existsSync(listRouteDir)) {
|
|
373
|
-
fs.mkdirSync(listRouteDir, { recursive: true });
|
|
374
|
-
}
|
|
375
|
-
if (!fs.existsSync(detailRouteDir)) {
|
|
376
|
-
fs.mkdirSync(detailRouteDir, { recursive: true });
|
|
377
|
-
}
|
|
378
612
|
// コードを生成
|
|
379
613
|
const routes = (0, nextjs_generator_1.generateCompactBFFRoutes)(modelInfo, sharedPackageName);
|
|
380
614
|
// ファイルに書き込み
|
|
381
|
-
|
|
382
|
-
|
|
615
|
+
const meta = { ownership: "managed", generator: SCAFFOLD_GENERATOR, sourceModel: modelInfo.name };
|
|
616
|
+
session.writeFile(listRoutePath, routes.listRoute, meta);
|
|
617
|
+
session.writeFile(detailRoutePath, routes.detailRoute, meta);
|
|
383
618
|
console.log(`✅ Created: ${listRoutePath}`);
|
|
384
619
|
console.log(`✅ Created: ${detailRoutePath}`);
|
|
385
620
|
}
|
|
386
621
|
/**
|
|
387
622
|
* Generate Cosmos DB container Bicep file
|
|
388
623
|
*/
|
|
389
|
-
async function generateCosmosContainer(modelInfo) {
|
|
624
|
+
async function generateCosmosContainer(modelInfo, session = new file_session_1.FileOperationSession("commit")) {
|
|
390
625
|
console.log("\n🗄️ Generating Cosmos DB container Bicep file...");
|
|
391
626
|
const modelKebab = (0, model_parser_1.toKebabCase)(modelInfo.name);
|
|
392
627
|
const modelPascal = (0, model_parser_1.toPascalCase)(modelInfo.name);
|
|
@@ -397,12 +632,8 @@ async function generateCosmosContainer(modelInfo) {
|
|
|
397
632
|
console.log("ℹ️ infra directory not found. Skipping Cosmos DB container generation.");
|
|
398
633
|
return;
|
|
399
634
|
}
|
|
400
|
-
// Check if containers directory exists, if not create it
|
|
401
|
-
const containersDir = path.join(infraDir, "containers");
|
|
402
|
-
if (!fs.existsSync(containersDir)) {
|
|
403
|
-
fs.mkdirSync(containersDir, { recursive: true });
|
|
404
|
-
}
|
|
405
635
|
// Generate container Bicep file
|
|
636
|
+
const containersDir = path.join(infraDir, "containers");
|
|
406
637
|
const containerFileName = `${modelKebab}-container.bicep`;
|
|
407
638
|
const containerFilePath = path.join(containersDir, containerFileName);
|
|
408
639
|
const bicepContent = `@description('Cosmos DB account name')
|
|
@@ -415,13 +646,7 @@ param databaseName string
|
|
|
415
646
|
param containerName string = '${modelPascal}s'
|
|
416
647
|
|
|
417
648
|
@description('Partition key path')
|
|
418
|
-
param partitionKeyPath string = '
|
|
419
|
-
|
|
420
|
-
@description('Throughput (RU/s) - only used for Free Tier')
|
|
421
|
-
param throughput int = 400
|
|
422
|
-
|
|
423
|
-
@description('Cosmos DB mode: freetier or serverless')
|
|
424
|
-
param cosmosDbMode string
|
|
649
|
+
param partitionKeyPath string = '${modelInfo.partitionKey}'
|
|
425
650
|
|
|
426
651
|
// Reference existing Cosmos DB account
|
|
427
652
|
resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2023-11-15' existing = {
|
|
@@ -462,33 +687,30 @@ resource container 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/container
|
|
|
462
687
|
]
|
|
463
688
|
}
|
|
464
689
|
}
|
|
465
|
-
options: cosmosDbMode == 'freetier' ? {
|
|
466
|
-
throughput: throughput
|
|
467
|
-
} : {}
|
|
468
690
|
}
|
|
469
691
|
}
|
|
470
692
|
|
|
471
693
|
output containerName string = container.name
|
|
472
694
|
`;
|
|
473
695
|
// Write Bicep file (overwrite if exists)
|
|
474
|
-
|
|
696
|
+
session.writeFile(containerFilePath, bicepContent, { ownership: "managed", generator: SCAFFOLD_GENERATOR, sourceModel: modelInfo.name });
|
|
475
697
|
console.log(`✅ Created: ${containerFilePath}`);
|
|
476
698
|
// Update main.bicep to include this container module
|
|
477
|
-
await updateMainBicepWithContainer(modelKebab, modelPascal);
|
|
699
|
+
await updateMainBicepWithContainer(modelKebab, modelPascal, session);
|
|
478
700
|
}
|
|
479
701
|
/**
|
|
480
702
|
* Update main.bicep to include new container module
|
|
481
703
|
*/
|
|
482
|
-
async function updateMainBicepWithContainer(modelKebab, modelPascal) {
|
|
704
|
+
async function updateMainBicepWithContainer(modelKebab, modelPascal, session) {
|
|
483
705
|
const cwd = process.cwd();
|
|
484
706
|
const mainBicepPath = path.join(cwd, "infra", "main.bicep");
|
|
485
|
-
if (!
|
|
707
|
+
if (!session.fileExists(mainBicepPath)) {
|
|
486
708
|
console.log("ℹ️ main.bicep not found. Please manually add the container module.");
|
|
487
709
|
return;
|
|
488
710
|
}
|
|
489
|
-
let mainBicepContent =
|
|
711
|
+
let mainBicepContent = session.readFile(mainBicepPath);
|
|
490
712
|
// Check if container module already exists
|
|
491
|
-
const containerModuleName = `${
|
|
713
|
+
const containerModuleName = `${modelPascal.charAt(0).toLowerCase()}${modelPascal.slice(1)}Container`;
|
|
492
714
|
if (mainBicepContent.includes(`module ${containerModuleName}`)) {
|
|
493
715
|
console.log(`ℹ️ Container module '${containerModuleName}' already exists in main.bicep`);
|
|
494
716
|
return;
|
|
@@ -523,26 +745,21 @@ module ${containerModuleName} 'containers/${modelKebab}-container.bicep' = {
|
|
|
523
745
|
name: '${modelKebab}-container'
|
|
524
746
|
params: {
|
|
525
747
|
cosmosAccountName: cosmosDbMode == 'freetier' ? cosmosDbFreeTier.outputs.accountName : cosmosDbServerless.outputs.accountName
|
|
526
|
-
databaseName: '
|
|
527
|
-
cosmosDbMode: cosmosDbMode
|
|
748
|
+
databaseName: '\${projectName}Database'
|
|
528
749
|
}
|
|
529
|
-
dependsOn: [
|
|
530
|
-
cosmosDbFreeTier
|
|
531
|
-
cosmosDbServerless
|
|
532
|
-
]
|
|
533
750
|
}`;
|
|
534
751
|
// Insert the module
|
|
535
752
|
mainBicepContent =
|
|
536
753
|
mainBicepContent.slice(0, insertPosition) +
|
|
537
754
|
containerModule +
|
|
538
755
|
mainBicepContent.slice(insertPosition);
|
|
539
|
-
|
|
756
|
+
session.writeFile(mainBicepPath, mainBicepContent, { ownership: "extension-point", generator: SCAFFOLD_GENERATOR });
|
|
540
757
|
console.log(`✅ Added container module to main.bicep`);
|
|
541
758
|
}
|
|
542
759
|
/**
|
|
543
760
|
* Generate UI components (list, detail, form, create, edit pages)
|
|
544
761
|
*/
|
|
545
|
-
async function generateUIComponents(modelInfo, sharedPackageName) {
|
|
762
|
+
async function generateUIComponents(modelInfo, sharedPackageName, authOptions, session = new file_session_1.FileOperationSession("commit")) {
|
|
546
763
|
console.log("\n🎨 Generating UI components...");
|
|
547
764
|
const modelKebab = (0, model_parser_1.toKebabCase)(modelInfo.name);
|
|
548
765
|
const modelName = modelInfo.name;
|
|
@@ -553,44 +770,46 @@ async function generateUIComponents(modelInfo, sharedPackageName) {
|
|
|
553
770
|
const newDir = path.join(modelDir, "new");
|
|
554
771
|
const idDir = path.join(modelDir, "[id]");
|
|
555
772
|
const editDir = path.join(idDir, "edit");
|
|
556
|
-
// Create directories
|
|
557
|
-
[componentsDir, newDir, editDir].forEach(dir => {
|
|
558
|
-
if (!fs.existsSync(dir)) {
|
|
559
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
560
|
-
}
|
|
561
|
-
});
|
|
562
773
|
// Generate and write files
|
|
563
|
-
const listPage = (0, ui_generator_1.generateListPage)(modelInfo, sharedPackageName);
|
|
564
|
-
const detailPage = (0, ui_generator_1.generateDetailPage)(modelInfo, sharedPackageName);
|
|
774
|
+
const listPage = (0, ui_generator_1.generateListPage)(modelInfo, sharedPackageName, authOptions);
|
|
775
|
+
const detailPage = (0, ui_generator_1.generateDetailPage)(modelInfo, sharedPackageName, authOptions);
|
|
565
776
|
const formComponent = (0, ui_generator_1.generateFormComponent)(modelInfo, sharedPackageName);
|
|
566
|
-
const newPage = (0, ui_generator_1.generateNewPage)(modelInfo);
|
|
567
|
-
const editPage = (0, ui_generator_1.generateEditPage)(modelInfo, sharedPackageName);
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
777
|
+
const newPage = (0, ui_generator_1.generateNewPage)(modelInfo, authOptions);
|
|
778
|
+
const editPage = (0, ui_generator_1.generateEditPage)(modelInfo, sharedPackageName, authOptions);
|
|
779
|
+
const authLayout = (0, ui_generator_1.generateAuthLayout)(modelInfo, authOptions);
|
|
780
|
+
const meta = { ownership: "managed", generator: SCAFFOLD_GENERATOR, sourceModel: modelInfo.name };
|
|
781
|
+
session.writeFile(path.join(modelDir, "page.tsx"), listPage, meta);
|
|
782
|
+
session.writeFile(path.join(idDir, "page.tsx"), detailPage, meta);
|
|
783
|
+
session.writeFile(path.join(componentsDir, `${modelName}Form.tsx`), formComponent, meta);
|
|
784
|
+
session.writeFile(path.join(newDir, "page.tsx"), newPage, meta);
|
|
785
|
+
session.writeFile(path.join(editDir, "page.tsx"), editPage, meta);
|
|
786
|
+
if (authLayout) {
|
|
787
|
+
session.writeFile(path.join(modelDir, "layout.tsx"), authLayout, meta);
|
|
788
|
+
}
|
|
573
789
|
console.log(`✅ Created: ${path.join(modelDir, "page.tsx")}`);
|
|
574
790
|
console.log(`✅ Created: ${path.join(idDir, "page.tsx")}`);
|
|
575
791
|
console.log(`✅ Created: ${path.join(componentsDir, `${modelName}Form.tsx`)}`);
|
|
576
792
|
console.log(`✅ Created: ${path.join(newDir, "page.tsx")}`);
|
|
577
793
|
console.log(`✅ Created: ${path.join(editDir, "page.tsx")}`);
|
|
794
|
+
if (authLayout) {
|
|
795
|
+
console.log(`✅ Created: ${path.join(modelDir, "layout.tsx")}`);
|
|
796
|
+
}
|
|
578
797
|
}
|
|
579
798
|
/**
|
|
580
799
|
* Update navigation menu on the homepage
|
|
581
800
|
*/
|
|
582
|
-
async function updateNavigationMenu(modelInfo) {
|
|
801
|
+
async function updateNavigationMenu(modelInfo, session) {
|
|
583
802
|
console.log("\n📋 Updating navigation menu...");
|
|
584
803
|
const modelKebab = (0, model_parser_1.toKebabCase)(modelInfo.name);
|
|
585
804
|
const cwd = process.cwd();
|
|
586
805
|
// Update scaffold config
|
|
587
806
|
const configPath = path.join(cwd, "lib", "scaffold-config.ts");
|
|
588
|
-
if (!
|
|
807
|
+
if (!session.fileExists(configPath)) {
|
|
589
808
|
console.log("⚠️ scaffold-config.ts not found. Skipping navigation menu update.");
|
|
590
809
|
return;
|
|
591
810
|
}
|
|
592
811
|
// Read existing config
|
|
593
|
-
const configContent =
|
|
812
|
+
const configContent = session.readFile(configPath);
|
|
594
813
|
// Parse models array - extract each model entry
|
|
595
814
|
const models = [];
|
|
596
815
|
const modelsMatch = configContent.match(/models:\s*\[([\s\S]*?)\]\s*as\s*ScaffoldModel\[\]/);
|
|
@@ -631,7 +850,7 @@ ${models.map(m => ` { name: '${m.name}', path: '${m.path}', label: '${m.label
|
|
|
631
850
|
] as ScaffoldModel[]
|
|
632
851
|
};
|
|
633
852
|
`;
|
|
634
|
-
|
|
853
|
+
session.writeFile(configPath, newConfigContent, { ownership: "extension-point", generator: SCAFFOLD_GENERATOR });
|
|
635
854
|
console.log(`✅ Added ${modelInfo.name} to navigation menu`);
|
|
636
855
|
}
|
|
637
856
|
//# sourceMappingURL=scaffold.js.map
|