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.
Files changed (67) hide show
  1. package/README.ja.md +33 -9
  2. package/README.md +33 -9
  3. package/dist/__tests__/fixtures.d.ts +8 -0
  4. package/dist/__tests__/fixtures.d.ts.map +1 -1
  5. package/dist/__tests__/fixtures.js +60 -0
  6. package/dist/__tests__/fixtures.js.map +1 -1
  7. package/dist/cli/commands/add-connector.d.ts +20 -0
  8. package/dist/cli/commands/add-connector.d.ts.map +1 -0
  9. package/dist/cli/commands/add-connector.js +161 -0
  10. package/dist/cli/commands/add-connector.js.map +1 -0
  11. package/dist/cli/commands/create-model.d.ts +1 -0
  12. package/dist/cli/commands/create-model.d.ts.map +1 -1
  13. package/dist/cli/commands/create-model.js +65 -1
  14. package/dist/cli/commands/create-model.js.map +1 -1
  15. package/dist/cli/commands/dev.d.ts.map +1 -1
  16. package/dist/cli/commands/dev.js +49 -3
  17. package/dist/cli/commands/dev.js.map +1 -1
  18. package/dist/cli/commands/scaffold.d.ts.map +1 -1
  19. package/dist/cli/commands/scaffold.js +97 -6
  20. package/dist/cli/commands/scaffold.js.map +1 -1
  21. package/dist/cli/index.js +15 -0
  22. package/dist/cli/index.js.map +1 -1
  23. package/dist/core/config.d.ts +3 -2
  24. package/dist/core/config.d.ts.map +1 -1
  25. package/dist/core/config.js +37 -0
  26. package/dist/core/config.js.map +1 -1
  27. package/dist/core/mock/connector-mock-server.d.ts +67 -0
  28. package/dist/core/mock/connector-mock-server.d.ts.map +1 -0
  29. package/dist/core/mock/connector-mock-server.js +285 -0
  30. package/dist/core/mock/connector-mock-server.js.map +1 -0
  31. package/dist/core/mock/zod-mock-generator.d.ts +14 -0
  32. package/dist/core/mock/zod-mock-generator.d.ts.map +1 -0
  33. package/dist/core/mock/zod-mock-generator.js +163 -0
  34. package/dist/core/mock/zod-mock-generator.js.map +1 -0
  35. package/dist/core/scaffold/connector-functions-generator.d.ts +41 -0
  36. package/dist/core/scaffold/connector-functions-generator.d.ts.map +1 -0
  37. package/dist/core/scaffold/connector-functions-generator.js +1009 -0
  38. package/dist/core/scaffold/connector-functions-generator.js.map +1 -0
  39. package/dist/core/scaffold/model-parser.d.ts +6 -0
  40. package/dist/core/scaffold/model-parser.d.ts.map +1 -1
  41. package/dist/core/scaffold/model-parser.js +94 -0
  42. package/dist/core/scaffold/model-parser.js.map +1 -1
  43. package/dist/core/scaffold/nextjs-generator.d.ts +8 -0
  44. package/dist/core/scaffold/nextjs-generator.d.ts.map +1 -1
  45. package/dist/core/scaffold/nextjs-generator.js +133 -0
  46. package/dist/core/scaffold/nextjs-generator.js.map +1 -1
  47. package/dist/types/index.d.ts +31 -0
  48. package/dist/types/index.d.ts.map +1 -1
  49. package/package.json +1 -1
  50. package/src/__tests__/config.test.ts +141 -0
  51. package/src/__tests__/connector-functions-generator.test.ts +288 -0
  52. package/src/__tests__/connector-mock-server.test.ts +252 -0
  53. package/src/__tests__/connector-model-bff.test.ts +162 -0
  54. package/src/__tests__/fixtures.ts +60 -0
  55. package/src/__tests__/zod-mock-generator.test.ts +132 -0
  56. package/src/cli/commands/add-connector.ts +157 -0
  57. package/src/cli/commands/create-model.ts +72 -2
  58. package/src/cli/commands/dev.ts +55 -4
  59. package/src/cli/commands/scaffold.ts +176 -10
  60. package/src/cli/index.ts +16 -0
  61. package/src/core/config.ts +42 -1
  62. package/src/core/mock/connector-mock-server.ts +307 -0
  63. package/src/core/mock/zod-mock-generator.ts +205 -0
  64. package/src/core/scaffold/connector-functions-generator.ts +1106 -0
  65. package/src/core/scaffold/model-parser.ts +103 -0
  66. package/src/core/scaffold/nextjs-generator.ts +154 -0
  67. package/src/types/index.ts +47 -0
@@ -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
- .action(async (options: DevOptions & { functionsPort?: string; noFunctions?: boolean; seedEnv?: string }) => {
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'}:${functionsPort}`,
716
- FUNCTIONS_BASE_URL: `http://${options.host || 'localhost'}:${functionsPort}`,
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 { generateCompactBFFRoutes, generateBFFCallFunction } from "../../core/scaffold/nextjs-generator";
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 { BackendLanguage } from "../../types";
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
- await generateFunctionsCode(modelInfo, functionsDir, sharedPackageName, backendLanguage);
104
+ if (isConnectorModel) {
105
+ await generateConnectorFunctionsCode(modelInfo, functionsDir, sharedPackageName, backendLanguage);
106
+ } else {
107
+ await generateFunctionsCode(modelInfo, functionsDir, sharedPackageName, backendLanguage);
83
108
 
84
- if (backendLanguage !== "typescript") {
85
- await generateLanguageSchemaArtifacts(relatedModels, modelInfo, functionsDir, backendLanguage);
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
- await generateBFFRoutes(modelInfo, apiDir, sharedPackageName);
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
- await generateCosmosContainer(modelInfo);
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();
@@ -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,