swallowkit 1.0.0-beta.12 → 1.0.0-beta.13
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 +33 -9
- package/README.md +33 -9
- package/dist/__tests__/fixtures.d.ts +8 -0
- package/dist/__tests__/fixtures.d.ts.map +1 -1
- package/dist/__tests__/fixtures.js +60 -0
- package/dist/__tests__/fixtures.js.map +1 -1
- 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 +161 -0
- package/dist/cli/commands/add-connector.js.map +1 -0
- package/dist/cli/commands/create-model.d.ts +1 -0
- package/dist/cli/commands/create-model.d.ts.map +1 -1
- package/dist/cli/commands/create-model.js +65 -1
- package/dist/cli/commands/create-model.js.map +1 -1
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +49 -3
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +97 -6
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.js +15 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config.d.ts +3 -2
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +37 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/mock/connector-mock-server.d.ts +67 -0
- package/dist/core/mock/connector-mock-server.d.ts.map +1 -0
- package/dist/core/mock/connector-mock-server.js +285 -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/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 +1009 -0
- package/dist/core/scaffold/connector-functions-generator.js.map +1 -0
- package/dist/core/scaffold/model-parser.d.ts +6 -0
- package/dist/core/scaffold/model-parser.d.ts.map +1 -1
- package/dist/core/scaffold/model-parser.js +94 -0
- package/dist/core/scaffold/model-parser.js.map +1 -1
- 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 +133 -0
- package/dist/core/scaffold/nextjs-generator.js.map +1 -1
- package/dist/types/index.d.ts +31 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/config.test.ts +141 -0
- package/src/__tests__/connector-functions-generator.test.ts +288 -0
- package/src/__tests__/connector-mock-server.test.ts +252 -0
- package/src/__tests__/connector-model-bff.test.ts +162 -0
- package/src/__tests__/fixtures.ts +60 -0
- package/src/__tests__/zod-mock-generator.test.ts +132 -0
- package/src/cli/commands/add-connector.ts +157 -0
- package/src/cli/commands/create-model.ts +72 -2
- package/src/cli/commands/dev.ts +55 -4
- package/src/cli/commands/scaffold.ts +176 -10
- package/src/cli/index.ts +16 -0
- package/src/core/config.ts +42 -1
- package/src/core/mock/connector-mock-server.ts +307 -0
- package/src/core/mock/zod-mock-generator.ts +205 -0
- package/src/core/scaffold/connector-functions-generator.ts +1106 -0
- package/src/core/scaffold/model-parser.ts +103 -0
- package/src/core/scaffold/nextjs-generator.ts +154 -0
- package/src/types/index.ts +47 -0
package/src/cli/commands/dev.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { ModelInfo } from '../../core/scaffold/model-parser';
|
|
|
9
9
|
import { applyDevSeedEnvironment, getContainerNameForModel, loadProjectModels } from './dev-seeds';
|
|
10
10
|
import { BackendLanguage } from '../../types';
|
|
11
11
|
import { detectFromProject, getCommands } from '../../utils/package-manager';
|
|
12
|
+
import { ConnectorMockServer } from '../../core/mock/connector-mock-server';
|
|
12
13
|
|
|
13
14
|
interface DevOptions {
|
|
14
15
|
port?: string;
|
|
@@ -18,6 +19,7 @@ interface DevOptions {
|
|
|
18
19
|
verbose?: boolean;
|
|
19
20
|
noFunctions?: boolean;
|
|
20
21
|
seedEnv?: string;
|
|
22
|
+
mockConnectors?: boolean;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export function buildFunctionsStartArgs(functionsPort: string): string[] {
|
|
@@ -323,7 +325,8 @@ export const devCommand = new Command()
|
|
|
323
325
|
.option('--verbose', 'Show verbose logs', false)
|
|
324
326
|
.option('--no-functions', 'Skip Azure Functions startup', false)
|
|
325
327
|
.option('--seed-env <environment>', 'Replace Cosmos DB Emulator data from dev-seeds/<environment> before startup')
|
|
326
|
-
.
|
|
328
|
+
.option('--mock-connectors', 'Start mock server for connector models (serves Zod-generated data)', false)
|
|
329
|
+
.action(async (options: DevOptions & { functionsPort?: string; noFunctions?: boolean; seedEnv?: string; mockConnectors?: boolean }) => {
|
|
327
330
|
// SwallowKit プロジェクトディレクトリかどうかを検証
|
|
328
331
|
ensureSwallowKitProject("dev");
|
|
329
332
|
|
|
@@ -454,10 +457,14 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
454
457
|
// プロセスを管理する配列
|
|
455
458
|
const processes: ChildProcess[] = [];
|
|
456
459
|
let functionsEnv: NodeJS.ProcessEnv = process.env;
|
|
460
|
+
let mockServer: ConnectorMockServer | null = null;
|
|
457
461
|
|
|
458
462
|
// Cleanup processes on Ctrl+C
|
|
459
|
-
process.on('SIGINT', () => {
|
|
463
|
+
process.on('SIGINT', async () => {
|
|
460
464
|
console.log('\n🛑 Stopping development servers...');
|
|
465
|
+
if (mockServer) {
|
|
466
|
+
await mockServer.stop();
|
|
467
|
+
}
|
|
461
468
|
processes.forEach((proc) => {
|
|
462
469
|
if (proc && !proc.killed) {
|
|
463
470
|
proc.kill();
|
|
@@ -695,6 +702,41 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
695
702
|
console.log('');
|
|
696
703
|
console.log('🚀 Starting Next.js development server...');
|
|
697
704
|
|
|
705
|
+
// Mock connector server — start proxy if --mock-connectors is enabled
|
|
706
|
+
let bffTargetPort = functionsPort;
|
|
707
|
+
|
|
708
|
+
if (options.mockConnectors) {
|
|
709
|
+
const allModels = await loadProjectModels();
|
|
710
|
+
const connectorModels = allModels.filter((m) => m.connectorConfig);
|
|
711
|
+
|
|
712
|
+
if (connectorModels.length > 0) {
|
|
713
|
+
const mockPort = parseInt(functionsPort, 10) + 1;
|
|
714
|
+
mockServer = new ConnectorMockServer({
|
|
715
|
+
port: mockPort,
|
|
716
|
+
functionsTarget: `${options.host || 'localhost'}:${functionsPort}`,
|
|
717
|
+
connectorModels,
|
|
718
|
+
allModels,
|
|
719
|
+
seedEnv: options.seedEnv,
|
|
720
|
+
host: options.host || 'localhost',
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
await mockServer.start();
|
|
724
|
+
bffTargetPort = String(mockPort);
|
|
725
|
+
|
|
726
|
+
console.log('');
|
|
727
|
+
console.log(`🔌 Connector mock server started (port: ${mockPort})`);
|
|
728
|
+
console.log(` Mocking ${connectorModels.length} connector model(s):`);
|
|
729
|
+
for (const m of connectorModels) {
|
|
730
|
+
const ops = m.connectorConfig!.operations.join(', ');
|
|
731
|
+
console.log(` - ${m.name} [${ops}]`);
|
|
732
|
+
}
|
|
733
|
+
console.log(` Other routes → proxied to Azure Functions (port: ${functionsPort})`);
|
|
734
|
+
} else {
|
|
735
|
+
console.log('');
|
|
736
|
+
console.log('ℹ️ --mock-connectors specified but no connector models found. Skipping mock server.');
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
698
740
|
// 5. Start Next.js development server
|
|
699
741
|
const nextArgs = buildNextDevArgs(pm, port);
|
|
700
742
|
|
|
@@ -712,8 +754,8 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
712
754
|
|
|
713
755
|
const nextEnv: NodeJS.ProcessEnv = {
|
|
714
756
|
...process.env,
|
|
715
|
-
BACKEND_FUNCTIONS_BASE_URL: `http://${options.host || 'localhost'}:${
|
|
716
|
-
FUNCTIONS_BASE_URL: `http://${options.host || 'localhost'}:${
|
|
757
|
+
BACKEND_FUNCTIONS_BASE_URL: `http://${options.host || 'localhost'}:${bffTargetPort}`,
|
|
758
|
+
FUNCTIONS_BASE_URL: `http://${options.host || 'localhost'}:${bffTargetPort}`,
|
|
717
759
|
};
|
|
718
760
|
|
|
719
761
|
const nextProcess = spawn(pm === 'pnpm' ? 'pnpm' : 'npx', nextArgs, {
|
|
@@ -735,6 +777,9 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
735
777
|
console.log(`\n⏹️ Next.js exited (exit code: ${code})`);
|
|
736
778
|
}
|
|
737
779
|
// Exit all processes when Next.js exits
|
|
780
|
+
if (mockServer) {
|
|
781
|
+
mockServer.stop().catch(() => {});
|
|
782
|
+
}
|
|
738
783
|
processes.forEach((proc) => {
|
|
739
784
|
if (proc && !proc.killed) {
|
|
740
785
|
proc.kill();
|
|
@@ -750,10 +795,16 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
750
795
|
if (hasFunctions && !options.noFunctions) {
|
|
751
796
|
console.log(`⚡ Azure Functions: http://${options.host || 'localhost'}:${functionsPort}`);
|
|
752
797
|
}
|
|
798
|
+
if (mockServer) {
|
|
799
|
+
console.log(`🔌 Mock Proxy: http://${options.host || 'localhost'}:${bffTargetPort} (BFF → here)`);
|
|
800
|
+
}
|
|
753
801
|
console.log('');
|
|
754
802
|
if (hasFunctions && !options.noFunctions) {
|
|
755
803
|
console.log('💡 Azure Functions and Next.js BFF are connected');
|
|
756
804
|
}
|
|
805
|
+
if (mockServer) {
|
|
806
|
+
console.log('💡 Connector models served from mock server (Zod-generated data)');
|
|
807
|
+
}
|
|
757
808
|
console.log('');
|
|
758
809
|
console.log('🛑 Press Ctrl+C to stop');
|
|
759
810
|
console.log('');
|
|
@@ -6,14 +6,23 @@
|
|
|
6
6
|
import * as fs from "fs";
|
|
7
7
|
import * as path from "path";
|
|
8
8
|
import { spawn, spawnSync } from "child_process";
|
|
9
|
-
import { getBackendLanguage, ensureSwallowKitProject } from "../../core/config";
|
|
10
|
-
import { ModelInfo, parseModelFile, toKebabCase, toPascalCase } from "../../core/scaffold/model-parser";
|
|
9
|
+
import { getBackendLanguage, getConnectorDefinition, ensureSwallowKitProject } from "../../core/config";
|
|
10
|
+
import { ModelInfo, parseModelFile, toKebabCase, toPascalCase, toCamelCase } from "../../core/scaffold/model-parser";
|
|
11
11
|
import {
|
|
12
12
|
generateCSharpAzureFunctionsCRUD,
|
|
13
13
|
generateCompactAzureFunctionsCRUD,
|
|
14
14
|
generatePythonAzureFunctionsCRUD,
|
|
15
15
|
} from "../../core/scaffold/functions-generator";
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
generateRdbConnectorFunctionTS,
|
|
18
|
+
generateApiConnectorFunctionTS,
|
|
19
|
+
generateRdbConnectorFunctionCSharp,
|
|
20
|
+
generateApiConnectorFunctionCSharp,
|
|
21
|
+
generateRdbConnectorFunctionPython,
|
|
22
|
+
generateApiConnectorFunctionPython,
|
|
23
|
+
isReadOnlyConnector,
|
|
24
|
+
} from "../../core/scaffold/connector-functions-generator";
|
|
25
|
+
import { generateCompactBFFRoutes, generateBFFCallFunction, generateConnectorBFFRoutes } from "../../core/scaffold/nextjs-generator";
|
|
17
26
|
import { generateOpenApiDocument } from "../../core/scaffold/openapi-generator";
|
|
18
27
|
import {
|
|
19
28
|
generateListPage,
|
|
@@ -23,7 +32,13 @@ import {
|
|
|
23
32
|
generateEditPage,
|
|
24
33
|
} from "../../core/scaffold/ui-generator";
|
|
25
34
|
import { detectFromProject, getCommands } from "../../utils/package-manager";
|
|
26
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
BackendLanguage,
|
|
37
|
+
RdbConnectorConfig,
|
|
38
|
+
ApiConnectorConfig,
|
|
39
|
+
RdbModelConnectorConfig,
|
|
40
|
+
ApiModelConnectorConfig,
|
|
41
|
+
} from "../../types";
|
|
27
42
|
|
|
28
43
|
interface ScaffoldOptions {
|
|
29
44
|
model: string; // モデルファイルのパス(例: "lib/models/todo.ts" or "todo")
|
|
@@ -51,6 +66,13 @@ export async function scaffoldCommand(options: ScaffoldOptions) {
|
|
|
51
66
|
const backendLanguage = getBackendLanguage();
|
|
52
67
|
console.log(`🧠 Backend language: ${backendLanguage}`);
|
|
53
68
|
|
|
69
|
+
// コネクタモデルかどうかを判定
|
|
70
|
+
const isConnectorModel = !!modelInfo.connectorConfig;
|
|
71
|
+
if (isConnectorModel) {
|
|
72
|
+
console.log(`🔌 Connector model detected: ${modelInfo.connectorConfig!.connector}`);
|
|
73
|
+
console.log(` Operations: ${modelInfo.connectorConfig!.operations.join(", ")}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
54
76
|
// ネストスキーマ参照があれば表示
|
|
55
77
|
if (modelInfo.nestedSchemaRefs.length > 0) {
|
|
56
78
|
console.log(`🔗 Nested schema references detected:`);
|
|
@@ -79,18 +101,28 @@ export async function scaffoldCommand(options: ScaffoldOptions) {
|
|
|
79
101
|
await generateCallFunctionHelper();
|
|
80
102
|
|
|
81
103
|
// 6. Generate Azure Functions code
|
|
82
|
-
|
|
104
|
+
if (isConnectorModel) {
|
|
105
|
+
await generateConnectorFunctionsCode(modelInfo, functionsDir, sharedPackageName, backendLanguage);
|
|
106
|
+
} else {
|
|
107
|
+
await generateFunctionsCode(modelInfo, functionsDir, sharedPackageName, backendLanguage);
|
|
83
108
|
|
|
84
|
-
|
|
85
|
-
|
|
109
|
+
if (backendLanguage !== "typescript") {
|
|
110
|
+
await generateLanguageSchemaArtifacts(relatedModels, modelInfo, functionsDir, backendLanguage);
|
|
111
|
+
}
|
|
86
112
|
}
|
|
87
113
|
|
|
88
114
|
// 7. Generate Next.js BFF API Routes
|
|
89
115
|
const apiDir = options.apiDir || "app/api";
|
|
90
|
-
|
|
116
|
+
if (isConnectorModel) {
|
|
117
|
+
await generateConnectorBFFRoutesFiles(modelInfo, apiDir, sharedPackageName);
|
|
118
|
+
} else {
|
|
119
|
+
await generateBFFRoutes(modelInfo, apiDir, sharedPackageName);
|
|
120
|
+
}
|
|
91
121
|
|
|
92
|
-
// 8. Generate Cosmos DB container Bicep file
|
|
93
|
-
|
|
122
|
+
// 8. Generate Cosmos DB container Bicep file (skip for connector models)
|
|
123
|
+
if (!isConnectorModel) {
|
|
124
|
+
await generateCosmosContainer(modelInfo);
|
|
125
|
+
}
|
|
94
126
|
|
|
95
127
|
// 9. Generate UI components (unless --api-only)
|
|
96
128
|
if (!options.apiOnly) {
|
|
@@ -263,6 +295,140 @@ async function generateFunctionsCode(
|
|
|
263
295
|
console.log(`✅ Created: ${blueprintPath}`);
|
|
264
296
|
}
|
|
265
297
|
|
|
298
|
+
/**
|
|
299
|
+
* コネクタモデル用 Azure Functions コード生成
|
|
300
|
+
*/
|
|
301
|
+
async function generateConnectorFunctionsCode(
|
|
302
|
+
modelInfo: ModelInfo,
|
|
303
|
+
functionsDir: string,
|
|
304
|
+
sharedPackageName: string,
|
|
305
|
+
backendLanguage: BackendLanguage
|
|
306
|
+
): Promise<void> {
|
|
307
|
+
console.log("\n🔌 Generating Connector Azure Functions code...");
|
|
308
|
+
|
|
309
|
+
const connectorConfig = modelInfo.connectorConfig!;
|
|
310
|
+
const connectorDef = getConnectorDefinition(connectorConfig.connector);
|
|
311
|
+
|
|
312
|
+
if (!connectorDef) {
|
|
313
|
+
throw new Error(
|
|
314
|
+
`Connector '${connectorConfig.connector}' not found in swallowkit.config.js.\n` +
|
|
315
|
+
` Please add it to the 'connectors' section of your configuration.`
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const modelKebab = toKebabCase(modelInfo.name);
|
|
320
|
+
|
|
321
|
+
if (backendLanguage === "typescript") {
|
|
322
|
+
const functionFilePath = path.join(
|
|
323
|
+
process.cwd(),
|
|
324
|
+
functionsDir,
|
|
325
|
+
"src",
|
|
326
|
+
`${modelKebab}.ts`
|
|
327
|
+
);
|
|
328
|
+
fs.mkdirSync(path.dirname(functionFilePath), { recursive: true });
|
|
329
|
+
|
|
330
|
+
let code: string;
|
|
331
|
+
if (connectorDef.type === "rdb") {
|
|
332
|
+
code = generateRdbConnectorFunctionTS(
|
|
333
|
+
modelInfo, sharedPackageName,
|
|
334
|
+
connectorDef as RdbConnectorConfig,
|
|
335
|
+
connectorConfig as RdbModelConnectorConfig
|
|
336
|
+
);
|
|
337
|
+
} else {
|
|
338
|
+
code = generateApiConnectorFunctionTS(
|
|
339
|
+
modelInfo, sharedPackageName,
|
|
340
|
+
connectorDef as ApiConnectorConfig,
|
|
341
|
+
connectorConfig as ApiModelConnectorConfig
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
fs.writeFileSync(functionFilePath, code, "utf-8");
|
|
346
|
+
console.log(`✅ Created: ${functionFilePath}`);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (backendLanguage === "csharp") {
|
|
351
|
+
const functionFilePath = path.join(
|
|
352
|
+
process.cwd(),
|
|
353
|
+
functionsDir,
|
|
354
|
+
"Connectors",
|
|
355
|
+
`${modelInfo.name}ConnectorFunctions.cs`
|
|
356
|
+
);
|
|
357
|
+
fs.mkdirSync(path.dirname(functionFilePath), { recursive: true });
|
|
358
|
+
|
|
359
|
+
let code: string;
|
|
360
|
+
if (connectorDef.type === "rdb") {
|
|
361
|
+
code = generateRdbConnectorFunctionCSharp(
|
|
362
|
+
modelInfo,
|
|
363
|
+
connectorDef as RdbConnectorConfig,
|
|
364
|
+
connectorConfig as RdbModelConnectorConfig
|
|
365
|
+
);
|
|
366
|
+
} else {
|
|
367
|
+
code = generateApiConnectorFunctionCSharp(
|
|
368
|
+
modelInfo,
|
|
369
|
+
connectorDef as ApiConnectorConfig,
|
|
370
|
+
connectorConfig as ApiModelConnectorConfig
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
fs.writeFileSync(functionFilePath, code, "utf-8");
|
|
375
|
+
console.log(`✅ Created: ${functionFilePath}`);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Python
|
|
380
|
+
const blueprintsDir = path.join(process.cwd(), functionsDir, "blueprints");
|
|
381
|
+
const blueprintPath = path.join(blueprintsDir, `${modelKebab.replace(/-/g, "_")}.py`);
|
|
382
|
+
fs.mkdirSync(blueprintsDir, { recursive: true });
|
|
383
|
+
|
|
384
|
+
let result: { blueprint: string; registration: string };
|
|
385
|
+
if (connectorDef.type === "rdb") {
|
|
386
|
+
result = generateRdbConnectorFunctionPython(
|
|
387
|
+
modelInfo,
|
|
388
|
+
connectorDef as RdbConnectorConfig,
|
|
389
|
+
connectorConfig as RdbModelConnectorConfig
|
|
390
|
+
);
|
|
391
|
+
} else {
|
|
392
|
+
result = generateApiConnectorFunctionPython(
|
|
393
|
+
modelInfo,
|
|
394
|
+
connectorDef as ApiConnectorConfig,
|
|
395
|
+
connectorConfig as ApiModelConnectorConfig
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
fs.writeFileSync(blueprintPath, result.blueprint, "utf-8");
|
|
400
|
+
updatePythonFunctionRegistrations(path.join(process.cwd(), functionsDir, "function_app.py"), result.registration);
|
|
401
|
+
console.log(`✅ Created: ${blueprintPath}`);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* コネクタモデル用 BFF ルート生成(操作制限に対応)
|
|
406
|
+
*/
|
|
407
|
+
async function generateConnectorBFFRoutesFiles(
|
|
408
|
+
modelInfo: ModelInfo,
|
|
409
|
+
apiDir: string,
|
|
410
|
+
sharedPackageName: string
|
|
411
|
+
): Promise<void> {
|
|
412
|
+
console.log("\n🔌 Generating Connector BFF API routes...");
|
|
413
|
+
|
|
414
|
+
const modelCamel = toCamelCase(modelInfo.name);
|
|
415
|
+
const operations = modelInfo.connectorConfig!.operations;
|
|
416
|
+
|
|
417
|
+
const listRoutePath = path.join(process.cwd(), apiDir, modelCamel, "route.ts");
|
|
418
|
+
const detailRoutePath = path.join(process.cwd(), apiDir, modelCamel, "[id]", "route.ts");
|
|
419
|
+
|
|
420
|
+
fs.mkdirSync(path.dirname(listRoutePath), { recursive: true });
|
|
421
|
+
fs.mkdirSync(path.dirname(detailRoutePath), { recursive: true });
|
|
422
|
+
|
|
423
|
+
const routes = generateConnectorBFFRoutes(modelInfo, sharedPackageName, operations);
|
|
424
|
+
|
|
425
|
+
fs.writeFileSync(listRoutePath, routes.listRoute, "utf-8");
|
|
426
|
+
fs.writeFileSync(detailRoutePath, routes.detailRoute, "utf-8");
|
|
427
|
+
|
|
428
|
+
console.log(`✅ Created: ${listRoutePath}`);
|
|
429
|
+
console.log(`✅ Created: ${detailRoutePath}`);
|
|
430
|
+
}
|
|
431
|
+
|
|
266
432
|
async function collectModelGraph(modelPath: string, seen = new Map<string, ModelInfo>()): Promise<ModelInfo[]> {
|
|
267
433
|
const resolvedPath = path.resolve(modelPath);
|
|
268
434
|
if (seen.has(resolvedPath)) {
|
package/src/cli/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ if (process.platform === 'win32') {
|
|
|
10
10
|
import { Command } from "commander";
|
|
11
11
|
import { initCommand, devCommand, devSeedsCommand, scaffoldCommand, createModelCommand } from "./commands";
|
|
12
12
|
import { provisionCommand } from "./commands/provision";
|
|
13
|
+
import { addConnectorCommand } from "./commands/add-connector";
|
|
13
14
|
|
|
14
15
|
const program = new Command();
|
|
15
16
|
|
|
@@ -49,10 +50,12 @@ program
|
|
|
49
50
|
.command("create-model <names...>")
|
|
50
51
|
.description("Create model template files with id, createdAt, and updatedAt fields")
|
|
51
52
|
.option("--models-dir <dir>", "Models directory", "shared/models")
|
|
53
|
+
.option("--connector <name>", "Associate model with a connector defined in swallowkit.config.js")
|
|
52
54
|
.action((names, options) => {
|
|
53
55
|
createModelCommand({
|
|
54
56
|
names,
|
|
55
57
|
modelsDir: options.modelsDir,
|
|
58
|
+
connector: options.connector,
|
|
56
59
|
});
|
|
57
60
|
});
|
|
58
61
|
|
|
@@ -71,4 +74,17 @@ program
|
|
|
71
74
|
});
|
|
72
75
|
});
|
|
73
76
|
|
|
77
|
+
program
|
|
78
|
+
.command("add-connector <name>")
|
|
79
|
+
.description("Add an external data source connector configuration")
|
|
80
|
+
.requiredOption("--type <type>", "Connector type: rdb | api")
|
|
81
|
+
.option("--provider <provider>", "RDB provider: mysql | postgres | sqlserver", "mysql")
|
|
82
|
+
.action((name, options) => {
|
|
83
|
+
addConnectorCommand({
|
|
84
|
+
name,
|
|
85
|
+
type: options.type,
|
|
86
|
+
provider: options.provider,
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
74
90
|
program.parse();
|
package/src/core/config.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
|
-
import { BackendLanguage, SwallowKitConfig } from "../types";
|
|
3
|
+
import { BackendLanguage, ConnectorDefinition, SwallowKitConfig } from "../types";
|
|
4
4
|
import { detectFromProject, getCommands } from "../utils/package-manager";
|
|
5
5
|
|
|
6
6
|
const VALID_BACKEND_LANGUAGES: BackendLanguage[] = ["typescript", "csharp", "python"];
|
|
@@ -81,6 +81,7 @@ function mergeConfig(defaultConfig: SwallowKitConfig, userConfig: Partial<Swallo
|
|
|
81
81
|
...userConfig.api?.cors,
|
|
82
82
|
},
|
|
83
83
|
},
|
|
84
|
+
...(userConfig.connectors ? { connectors: userConfig.connectors } : {}),
|
|
84
85
|
};
|
|
85
86
|
}
|
|
86
87
|
|
|
@@ -168,9 +169,21 @@ export function getFullConfig(configPath?: string): SwallowKitConfig {
|
|
|
168
169
|
return mergeConfig(fileConfig, envConfig);
|
|
169
170
|
}
|
|
170
171
|
|
|
172
|
+
/**
|
|
173
|
+
* コネクタ定義を名前で取得
|
|
174
|
+
*/
|
|
175
|
+
export function getConnectorDefinition(connectorName: string, configPath?: string): ConnectorDefinition | undefined {
|
|
176
|
+
const config = getFullConfig(configPath);
|
|
177
|
+
return config.connectors?.[connectorName];
|
|
178
|
+
}
|
|
179
|
+
|
|
171
180
|
/**
|
|
172
181
|
* 設定の検証
|
|
173
182
|
*/
|
|
183
|
+
const VALID_CONNECTOR_TYPES = ["rdb", "api"];
|
|
184
|
+
const VALID_RDB_PROVIDERS = ["mysql", "postgres", "sqlserver"];
|
|
185
|
+
const VALID_API_AUTH_TYPES = ["apiKey", "bearer", "oauth2"];
|
|
186
|
+
|
|
174
187
|
export function validateConfig(config: SwallowKitConfig): { valid: boolean; errors: string[] } {
|
|
175
188
|
const errors: string[] = [];
|
|
176
189
|
|
|
@@ -188,6 +201,34 @@ export function validateConfig(config: SwallowKitConfig): { valid: boolean; erro
|
|
|
188
201
|
errors.push("Backend language must be one of: typescript, csharp, python");
|
|
189
202
|
}
|
|
190
203
|
|
|
204
|
+
// コネクタ設定の検証
|
|
205
|
+
if (config.connectors) {
|
|
206
|
+
for (const [name, connector] of Object.entries(config.connectors)) {
|
|
207
|
+
if (!VALID_CONNECTOR_TYPES.includes(connector.type)) {
|
|
208
|
+
errors.push(`Connector '${name}': type must be one of: ${VALID_CONNECTOR_TYPES.join(", ")}`);
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (connector.type === "rdb") {
|
|
213
|
+
if (!connector.provider || !VALID_RDB_PROVIDERS.includes(connector.provider)) {
|
|
214
|
+
errors.push(`Connector '${name}': provider must be one of: ${VALID_RDB_PROVIDERS.join(", ")}`);
|
|
215
|
+
}
|
|
216
|
+
if (!connector.connectionEnvVar) {
|
|
217
|
+
errors.push(`Connector '${name}': connectionEnvVar is required`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (connector.type === "api") {
|
|
222
|
+
if (!connector.baseUrlEnvVar) {
|
|
223
|
+
errors.push(`Connector '${name}': baseUrlEnvVar is required`);
|
|
224
|
+
}
|
|
225
|
+
if (connector.auth && !VALID_API_AUTH_TYPES.includes(connector.auth.type)) {
|
|
226
|
+
errors.push(`Connector '${name}': auth.type must be one of: ${VALID_API_AUTH_TYPES.join(", ")}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
191
232
|
return {
|
|
192
233
|
valid: errors.length === 0,
|
|
193
234
|
errors,
|