swallowkit 1.0.0-beta.6 → 1.0.0-beta.61

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 (296) hide show
  1. package/README.ja.md +87 -175
  2. package/README.md +87 -176
  3. package/dist/__tests__/fixtures.d.ts +12 -0
  4. package/dist/__tests__/fixtures.d.ts.map +1 -1
  5. package/dist/__tests__/fixtures.js +101 -0
  6. package/dist/__tests__/fixtures.js.map +1 -1
  7. package/dist/cli/commands/add-auth.d.ts +15 -0
  8. package/dist/cli/commands/add-auth.d.ts.map +1 -0
  9. package/dist/cli/commands/add-auth.js +790 -0
  10. package/dist/cli/commands/add-auth.js.map +1 -0
  11. package/dist/cli/commands/add-connector.d.ts +20 -0
  12. package/dist/cli/commands/add-connector.d.ts.map +1 -0
  13. package/dist/cli/commands/add-connector.js +164 -0
  14. package/dist/cli/commands/add-connector.js.map +1 -0
  15. package/dist/cli/commands/cosmos-worktree-isolation.d.ts +25 -0
  16. package/dist/cli/commands/cosmos-worktree-isolation.d.ts.map +1 -0
  17. package/dist/cli/commands/cosmos-worktree-isolation.js +124 -0
  18. package/dist/cli/commands/cosmos-worktree-isolation.js.map +1 -0
  19. package/dist/cli/commands/create-model.d.ts +1 -4
  20. package/dist/cli/commands/create-model.d.ts.map +1 -1
  21. package/dist/cli/commands/create-model.js +20 -82
  22. package/dist/cli/commands/create-model.js.map +1 -1
  23. package/dist/cli/commands/dev-seeds.d.ts +62 -0
  24. package/dist/cli/commands/dev-seeds.d.ts.map +1 -0
  25. package/dist/cli/commands/dev-seeds.js +474 -0
  26. package/dist/cli/commands/dev-seeds.js.map +1 -0
  27. package/dist/cli/commands/dev.d.ts +38 -0
  28. package/dist/cli/commands/dev.d.ts.map +1 -1
  29. package/dist/cli/commands/dev.js +733 -155
  30. package/dist/cli/commands/dev.js.map +1 -1
  31. package/dist/cli/commands/index.d.ts +1 -0
  32. package/dist/cli/commands/index.d.ts.map +1 -1
  33. package/dist/cli/commands/index.js +3 -1
  34. package/dist/cli/commands/index.js.map +1 -1
  35. package/dist/cli/commands/init.d.ts +74 -0
  36. package/dist/cli/commands/init.d.ts.map +1 -1
  37. package/dist/cli/commands/init.js +1376 -930
  38. package/dist/cli/commands/init.js.map +1 -1
  39. package/dist/cli/commands/scaffold.d.ts +24 -0
  40. package/dist/cli/commands/scaffold.d.ts.map +1 -1
  41. package/dist/cli/commands/scaffold.js +473 -90
  42. package/dist/cli/commands/scaffold.js.map +1 -1
  43. package/dist/cli/index.d.ts +5 -1
  44. package/dist/cli/index.d.ts.map +1 -1
  45. package/dist/cli/index.js +335 -55
  46. package/dist/cli/index.js.map +1 -1
  47. package/dist/core/config.d.ts +14 -3
  48. package/dist/core/config.d.ts.map +1 -1
  49. package/dist/core/config.js +179 -8
  50. package/dist/core/config.js.map +1 -1
  51. package/dist/core/mock/connector-mock-server.d.ts +101 -0
  52. package/dist/core/mock/connector-mock-server.d.ts.map +1 -0
  53. package/dist/core/mock/connector-mock-server.js +480 -0
  54. package/dist/core/mock/connector-mock-server.js.map +1 -0
  55. package/dist/core/mock/zod-mock-generator.d.ts +14 -0
  56. package/dist/core/mock/zod-mock-generator.d.ts.map +1 -0
  57. package/dist/core/mock/zod-mock-generator.js +163 -0
  58. package/dist/core/mock/zod-mock-generator.js.map +1 -0
  59. package/dist/core/operations/auth-operations.d.ts +61 -0
  60. package/dist/core/operations/auth-operations.d.ts.map +1 -0
  61. package/dist/core/operations/auth-operations.js +272 -0
  62. package/dist/core/operations/auth-operations.js.map +1 -0
  63. package/dist/core/operations/create-model.d.ts +15 -0
  64. package/dist/core/operations/create-model.d.ts.map +1 -0
  65. package/dist/core/operations/create-model.js +185 -0
  66. package/dist/core/operations/create-model.js.map +1 -0
  67. package/dist/core/operations/file-session.d.ts +56 -0
  68. package/dist/core/operations/file-session.d.ts.map +1 -0
  69. package/dist/core/operations/file-session.js +203 -0
  70. package/dist/core/operations/file-session.js.map +1 -0
  71. package/dist/core/operations/provision-operations.d.ts +52 -0
  72. package/dist/core/operations/provision-operations.d.ts.map +1 -0
  73. package/dist/core/operations/provision-operations.js +211 -0
  74. package/dist/core/operations/provision-operations.js.map +1 -0
  75. package/dist/core/operations/runtime.d.ts +37 -0
  76. package/dist/core/operations/runtime.d.ts.map +1 -0
  77. package/dist/core/operations/runtime.js +248 -0
  78. package/dist/core/operations/runtime.js.map +1 -0
  79. package/dist/core/operations/scaffold-apply.d.ts +21 -0
  80. package/dist/core/operations/scaffold-apply.d.ts.map +1 -0
  81. package/dist/core/operations/scaffold-apply.js +57 -0
  82. package/dist/core/operations/scaffold-apply.js.map +1 -0
  83. package/dist/core/operations/scaffold-machine.d.ts +16 -0
  84. package/dist/core/operations/scaffold-machine.d.ts.map +1 -0
  85. package/dist/core/operations/scaffold-machine.js +51 -0
  86. package/dist/core/operations/scaffold-machine.js.map +1 -0
  87. package/dist/core/operations/scaffold-plan.d.ts +43 -0
  88. package/dist/core/operations/scaffold-plan.d.ts.map +1 -0
  89. package/dist/core/operations/scaffold-plan.js +187 -0
  90. package/dist/core/operations/scaffold-plan.js.map +1 -0
  91. package/dist/core/project/agent-instructions.d.ts +29 -0
  92. package/dist/core/project/agent-instructions.d.ts.map +1 -0
  93. package/dist/core/project/agent-instructions.js +160 -0
  94. package/dist/core/project/agent-instructions.js.map +1 -0
  95. package/dist/core/project/artifacts.d.ts +58 -0
  96. package/dist/core/project/artifacts.d.ts.map +1 -0
  97. package/dist/core/project/artifacts.js +179 -0
  98. package/dist/core/project/artifacts.js.map +1 -0
  99. package/dist/core/project/boundaries.d.ts +50 -0
  100. package/dist/core/project/boundaries.d.ts.map +1 -0
  101. package/dist/core/project/boundaries.js +117 -0
  102. package/dist/core/project/boundaries.js.map +1 -0
  103. package/dist/core/project/capabilities.d.ts +58 -0
  104. package/dist/core/project/capabilities.d.ts.map +1 -0
  105. package/dist/core/project/capabilities.js +135 -0
  106. package/dist/core/project/capabilities.js.map +1 -0
  107. package/dist/core/project/drift.d.ts +29 -0
  108. package/dist/core/project/drift.d.ts.map +1 -0
  109. package/dist/core/project/drift.js +188 -0
  110. package/dist/core/project/drift.js.map +1 -0
  111. package/dist/core/project/infra.d.ts +49 -0
  112. package/dist/core/project/infra.d.ts.map +1 -0
  113. package/dist/core/project/infra.js +167 -0
  114. package/dist/core/project/infra.js.map +1 -0
  115. package/dist/core/project/manifest.d.ts +92 -0
  116. package/dist/core/project/manifest.d.ts.map +1 -0
  117. package/dist/core/project/manifest.js +321 -0
  118. package/dist/core/project/manifest.js.map +1 -0
  119. package/dist/core/project/state.d.ts +15 -0
  120. package/dist/core/project/state.d.ts.map +1 -0
  121. package/dist/core/project/state.js +106 -0
  122. package/dist/core/project/state.js.map +1 -0
  123. package/dist/core/project/validation.d.ts +20 -0
  124. package/dist/core/project/validation.d.ts.map +1 -0
  125. package/dist/core/project/validation.js +274 -0
  126. package/dist/core/project/validation.js.map +1 -0
  127. package/dist/core/project/workflows.d.ts +43 -0
  128. package/dist/core/project/workflows.d.ts.map +1 -0
  129. package/dist/core/project/workflows.js +299 -0
  130. package/dist/core/project/workflows.js.map +1 -0
  131. package/dist/core/scaffold/auth-generator.d.ts +63 -0
  132. package/dist/core/scaffold/auth-generator.d.ts.map +1 -0
  133. package/dist/core/scaffold/auth-generator.js +1774 -0
  134. package/dist/core/scaffold/auth-generator.js.map +1 -0
  135. package/dist/core/scaffold/connector-functions-generator.d.ts +41 -0
  136. package/dist/core/scaffold/connector-functions-generator.d.ts.map +1 -0
  137. package/dist/core/scaffold/connector-functions-generator.js +1029 -0
  138. package/dist/core/scaffold/connector-functions-generator.js.map +1 -0
  139. package/dist/core/scaffold/functions-generator.d.ts +7 -1
  140. package/dist/core/scaffold/functions-generator.d.ts.map +1 -1
  141. package/dist/core/scaffold/functions-generator.js +771 -69
  142. package/dist/core/scaffold/functions-generator.js.map +1 -1
  143. package/dist/core/scaffold/model-parser.d.ts +21 -1
  144. package/dist/core/scaffold/model-parser.d.ts.map +1 -1
  145. package/dist/core/scaffold/model-parser.js +230 -79
  146. package/dist/core/scaffold/model-parser.js.map +1 -1
  147. package/dist/core/scaffold/native-schema-generator.d.ts +19 -0
  148. package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -0
  149. package/dist/core/scaffold/native-schema-generator.js +753 -0
  150. package/dist/core/scaffold/native-schema-generator.js.map +1 -0
  151. package/dist/core/scaffold/nextjs-generator.d.ts +8 -0
  152. package/dist/core/scaffold/nextjs-generator.d.ts.map +1 -1
  153. package/dist/core/scaffold/nextjs-generator.js +153 -9
  154. package/dist/core/scaffold/nextjs-generator.js.map +1 -1
  155. package/dist/core/scaffold/openapi-generator.d.ts +3 -0
  156. package/dist/core/scaffold/openapi-generator.d.ts.map +1 -0
  157. package/dist/core/scaffold/openapi-generator.js +190 -0
  158. package/dist/core/scaffold/openapi-generator.js.map +1 -0
  159. package/dist/core/scaffold/ui-generator.d.ts +17 -4
  160. package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
  161. package/dist/core/scaffold/ui-generator.js +174 -34
  162. package/dist/core/scaffold/ui-generator.js.map +1 -1
  163. package/dist/core/verify/index.d.ts +69 -0
  164. package/dist/core/verify/index.d.ts.map +1 -0
  165. package/dist/core/verify/index.js +412 -0
  166. package/dist/core/verify/index.js.map +1 -0
  167. package/dist/database/base-model.d.ts +3 -3
  168. package/dist/database/base-model.js +3 -3
  169. package/dist/index.d.ts +2 -2
  170. package/dist/index.d.ts.map +1 -1
  171. package/dist/index.js +2 -1
  172. package/dist/index.js.map +1 -1
  173. package/dist/machine/contracts.d.ts +24 -0
  174. package/dist/machine/contracts.d.ts.map +1 -0
  175. package/dist/machine/contracts.js +3 -0
  176. package/dist/machine/contracts.js.map +1 -0
  177. package/dist/machine/errors.d.ts +14 -0
  178. package/dist/machine/errors.d.ts.map +1 -0
  179. package/dist/machine/errors.js +42 -0
  180. package/dist/machine/errors.js.map +1 -0
  181. package/dist/machine/index.d.ts +3 -0
  182. package/dist/machine/index.d.ts.map +1 -0
  183. package/dist/machine/index.js +566 -0
  184. package/dist/machine/index.js.map +1 -0
  185. package/dist/mcp/index.d.ts +25 -0
  186. package/dist/mcp/index.d.ts.map +1 -0
  187. package/dist/mcp/index.js +425 -0
  188. package/dist/mcp/index.js.map +1 -0
  189. package/dist/types/index.d.ts +109 -0
  190. package/dist/types/index.d.ts.map +1 -1
  191. package/dist/utils/package-manager.d.ts +9 -12
  192. package/dist/utils/package-manager.d.ts.map +1 -1
  193. package/dist/utils/package-manager.js +51 -22
  194. package/dist/utils/package-manager.js.map +1 -1
  195. package/dist/utils/python-uv.d.ts +21 -0
  196. package/dist/utils/python-uv.d.ts.map +1 -0
  197. package/dist/utils/python-uv.js +111 -0
  198. package/dist/utils/python-uv.js.map +1 -0
  199. package/dist/version.d.ts +2 -0
  200. package/dist/version.d.ts.map +1 -0
  201. package/dist/version.js +49 -0
  202. package/dist/version.js.map +1 -0
  203. package/package.json +16 -17
  204. package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +1139 -0
  205. package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +206 -0
  206. package/src/__tests__/__snapshots__/ui-generator.test.ts.snap +532 -0
  207. package/src/__tests__/agent-instructions.test.ts +237 -0
  208. package/src/__tests__/agent-loop-phase34.test.ts +513 -0
  209. package/src/__tests__/auth.test.ts +770 -0
  210. package/src/__tests__/bff-swa-auth.test.ts +275 -0
  211. package/src/__tests__/config.test.ts +295 -0
  212. package/src/__tests__/connector-functions-generator.test.ts +288 -0
  213. package/src/__tests__/connector-mock-server.test.ts +459 -0
  214. package/src/__tests__/connector-model-bff.test.ts +162 -0
  215. package/src/__tests__/cosmos-worktree-isolation.test.ts +149 -0
  216. package/src/__tests__/dev-seeds.test.ts +236 -0
  217. package/src/__tests__/dev.test.ts +296 -0
  218. package/src/__tests__/file-session.test.ts +98 -0
  219. package/src/__tests__/fixtures.ts +184 -0
  220. package/src/__tests__/functions-generator.test.ts +237 -0
  221. package/src/__tests__/init.test.ts +363 -0
  222. package/src/__tests__/jest-globals.d.ts +8 -0
  223. package/src/__tests__/machine.test.ts +371 -0
  224. package/src/__tests__/mcp.test.ts +253 -0
  225. package/src/__tests__/model-parser.test.ts +142 -0
  226. package/src/__tests__/multi-auth.test.ts +160 -0
  227. package/src/__tests__/nextjs-generator.test.ts +105 -0
  228. package/src/__tests__/openapi-generator.test.ts +43 -0
  229. package/src/__tests__/package-manager.test.ts +244 -0
  230. package/src/__tests__/plan-apply.test.ts +499 -0
  231. package/src/__tests__/python-uv.test.ts +48 -0
  232. package/src/__tests__/scaffold.test.ts +302 -0
  233. package/src/__tests__/string-utils.test.ts +75 -0
  234. package/src/__tests__/tsconfig.json +15 -0
  235. package/src/__tests__/ui-generator.test.ts +248 -0
  236. package/src/__tests__/zod-mock-generator.test.ts +132 -0
  237. package/src/cli/commands/add-auth.ts +853 -0
  238. package/src/cli/commands/add-connector.ts +159 -0
  239. package/src/cli/commands/cosmos-worktree-isolation.ts +130 -0
  240. package/src/cli/commands/create-model.ts +62 -0
  241. package/src/cli/commands/dev-seeds.ts +629 -0
  242. package/src/cli/commands/dev.ts +1252 -0
  243. package/src/cli/commands/index.ts +9 -0
  244. package/src/cli/commands/init.ts +3414 -0
  245. package/src/cli/commands/provision.ts +193 -0
  246. package/src/cli/commands/scaffold.ts +1081 -0
  247. package/src/cli/index.ts +352 -0
  248. package/src/core/config.ts +380 -0
  249. package/src/core/mock/connector-mock-server.ts +555 -0
  250. package/src/core/mock/zod-mock-generator.ts +205 -0
  251. package/src/core/operations/auth-operations.ts +360 -0
  252. package/src/core/operations/create-model.ts +191 -0
  253. package/src/core/operations/file-session.ts +222 -0
  254. package/src/core/operations/provision-operations.ts +297 -0
  255. package/src/core/operations/runtime.ts +258 -0
  256. package/src/core/operations/scaffold-apply.ts +106 -0
  257. package/src/core/operations/scaffold-machine.ts +77 -0
  258. package/src/core/operations/scaffold-plan.ts +215 -0
  259. package/src/core/project/agent-instructions.ts +167 -0
  260. package/src/core/project/agent-templates/AGENTS.md +504 -0
  261. package/src/core/project/agent-templates/CLAUDE.md +31 -0
  262. package/src/core/project/agent-templates/azure-functions.instructions.md +22 -0
  263. package/src/core/project/agent-templates/bff-routes.instructions.md +18 -0
  264. package/src/core/project/agent-templates/copilot-instructions.md +27 -0
  265. package/src/core/project/agent-templates/shared-models.instructions.md +72 -0
  266. package/src/core/project/artifacts.ts +205 -0
  267. package/src/core/project/boundaries.ts +170 -0
  268. package/src/core/project/capabilities.ts +193 -0
  269. package/src/core/project/drift.ts +202 -0
  270. package/src/core/project/infra.ts +196 -0
  271. package/src/core/project/manifest.ts +402 -0
  272. package/src/core/project/state.ts +77 -0
  273. package/src/core/project/validation.ts +295 -0
  274. package/src/core/project/workflows.ts +295 -0
  275. package/src/core/scaffold/auth-generator.ts +1824 -0
  276. package/src/core/scaffold/connector-functions-generator.ts +1133 -0
  277. package/src/core/scaffold/functions-generator.ts +972 -0
  278. package/src/core/scaffold/model-parser.ts +781 -0
  279. package/src/core/scaffold/native-schema-generator.ts +904 -0
  280. package/src/core/scaffold/nextjs-generator.ts +382 -0
  281. package/src/core/scaffold/openapi-generator.ts +212 -0
  282. package/src/core/scaffold/ui-generator.ts +1100 -0
  283. package/src/core/verify/index.ts +468 -0
  284. package/src/database/base-model.ts +184 -0
  285. package/src/database/client.ts +140 -0
  286. package/src/database/repository.ts +104 -0
  287. package/src/database/runtime-check.ts +25 -0
  288. package/src/index.ts +27 -0
  289. package/src/machine/contracts.ts +33 -0
  290. package/src/machine/errors.ts +45 -0
  291. package/src/machine/index.ts +690 -0
  292. package/src/mcp/index.ts +423 -0
  293. package/src/types/index.ts +182 -0
  294. package/src/utils/package-manager.ts +251 -0
  295. package/src/utils/python-uv.ts +96 -0
  296. package/src/version.ts +13 -0
@@ -0,0 +1,1029 @@
1
+ "use strict";
2
+ /**
3
+ * コネクタ用 Azure Functions CRUD コード生成
4
+ * RDB(MySQL/PostgreSQL/SQL Server)および 外部 REST API コネクタ向けのハンドラーを生成
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.generateRdbConnectorFunctionTS = generateRdbConnectorFunctionTS;
8
+ exports.generateApiConnectorFunctionTS = generateApiConnectorFunctionTS;
9
+ exports.generateRdbConnectorFunctionCSharp = generateRdbConnectorFunctionCSharp;
10
+ exports.generateApiConnectorFunctionCSharp = generateApiConnectorFunctionCSharp;
11
+ exports.generateRdbConnectorFunctionPython = generateRdbConnectorFunctionPython;
12
+ exports.generateApiConnectorFunctionPython = generateApiConnectorFunctionPython;
13
+ exports.isReadOnlyConnector = isReadOnlyConnector;
14
+ const model_parser_1 = require("./model-parser");
15
+ const auth_generator_1 = require("./auth-generator");
16
+ // ─── TypeScript Generators ────────────────────────────────────────
17
+ /**
18
+ * RDB コネクタ用 TypeScript Azure Functions を生成
19
+ */
20
+ function generateRdbConnectorFunctionTS(model, sharedPackageName, connectorDef, modelConnector, authPolicy, authProvider) {
21
+ const modelCamel = (0, model_parser_1.toCamelCase)(model.name);
22
+ const schemaName = model.schemaName;
23
+ const ops = new Set(modelConnector.operations);
24
+ const table = modelConnector.table;
25
+ const idCol = modelConnector.idColumn || "id";
26
+ const envVar = connectorDef.connectionEnvVar;
27
+ const provider = connectorDef.provider;
28
+ const driverImport = provider === "mysql"
29
+ ? `import mysql from 'mysql2/promise';`
30
+ : provider === "postgres"
31
+ ? `import pg from 'pg';`
32
+ : `import sql from 'mssql';`;
33
+ const getConnection = provider === "mysql"
34
+ ? `async function getConnection() {
35
+ return mysql.createConnection(process.env.${envVar}!);
36
+ }`
37
+ : provider === "postgres"
38
+ ? `async function getConnection() {
39
+ const client = new pg.Client(process.env.${envVar}!);
40
+ await client.connect();
41
+ return client;
42
+ }`
43
+ : `async function getConnection() {
44
+ return sql.connect(process.env.${envVar}!);
45
+ }`;
46
+ const queryAll = provider === "mysql"
47
+ ? `const conn = await getConnection();
48
+ try {
49
+ const [rows] = await conn.query('SELECT * FROM ${table}');
50
+ const validated = z.array(${schemaName}).parse(rows);
51
+ return { status: 200, jsonBody: validated };
52
+ } finally {
53
+ await conn.end();
54
+ }`
55
+ : provider === "postgres"
56
+ ? `const client = await getConnection();
57
+ try {
58
+ const result = await client.query('SELECT * FROM ${table}');
59
+ const validated = z.array(${schemaName}).parse(result.rows);
60
+ return { status: 200, jsonBody: validated };
61
+ } finally {
62
+ await client.end();
63
+ }`
64
+ : `const pool = await getConnection();
65
+ try {
66
+ const result = await pool.request().query('SELECT * FROM ${table}');
67
+ const validated = z.array(${schemaName}).parse(result.recordset);
68
+ return { status: 200, jsonBody: validated };
69
+ } finally {
70
+ await pool.close();
71
+ }`;
72
+ const queryById = provider === "mysql"
73
+ ? `const conn = await getConnection();
74
+ try {
75
+ const [rows] = await conn.query('SELECT * FROM ${table} WHERE ${idCol} = ?', [id]);
76
+ const items = rows as any[];
77
+ if (items.length === 0) {
78
+ return { status: 404, jsonBody: { error: 'Item not found' } };
79
+ }
80
+ const validated = ${schemaName}.parse(items[0]);
81
+ return { status: 200, jsonBody: validated };
82
+ } finally {
83
+ await conn.end();
84
+ }`
85
+ : provider === "postgres"
86
+ ? `const client = await getConnection();
87
+ try {
88
+ const result = await client.query('SELECT * FROM ${table} WHERE ${idCol} = $1', [id]);
89
+ if (result.rows.length === 0) {
90
+ return { status: 404, jsonBody: { error: 'Item not found' } };
91
+ }
92
+ const validated = ${schemaName}.parse(result.rows[0]);
93
+ return { status: 200, jsonBody: validated };
94
+ } finally {
95
+ await client.end();
96
+ }`
97
+ : `const pool = await getConnection();
98
+ try {
99
+ const result = await pool.request()
100
+ .input('id', id)
101
+ .query('SELECT * FROM ${table} WHERE ${idCol} = @id');
102
+ if (result.recordset.length === 0) {
103
+ return { status: 404, jsonBody: { error: 'Item not found' } };
104
+ }
105
+ const validated = ${schemaName}.parse(result.recordset[0]);
106
+ return { status: 200, jsonBody: validated };
107
+ } finally {
108
+ await pool.close();
109
+ }`;
110
+ // Auth guard setup
111
+ const hasAuth = !!authPolicy;
112
+ const namedAuth = Boolean(authPolicy && (authPolicy.policy || typeof authPolicy.read === 'string' || typeof authPolicy.write === 'string'));
113
+ const authImport = hasAuth ? `\n${(0, auth_generator_1.generateAuthImportTS)(namedAuth)}\n` : '';
114
+ const readGuard = hasAuth ? `\n${(0, auth_generator_1.generateAuthGuardTS)(authPolicy, 'read', authProvider)}\n` : '';
115
+ const authCatchBlock = hasAuth
116
+ ? ` const authErr = handleAuthError(error);
117
+ if (authErr) return authErr;\n `
118
+ : ` `;
119
+ let code = `import { app, HttpRequest, HttpResponseInit, InvocationContext } from '@azure/functions';
120
+ import { z } from 'zod/v4';
121
+ import { ${schemaName} } from '${sharedPackageName}';
122
+ ${driverImport}${authImport}
123
+
124
+ ${getConnection}
125
+ `;
126
+ if (ops.has("getAll")) {
127
+ code += `
128
+ // GET /api/${modelCamel} - 全件取得
129
+ app.http('${modelCamel}-get-all', {
130
+ methods: ['GET'],
131
+ route: '${modelCamel}',
132
+ authLevel: 'function',
133
+ handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
134
+ try {${readGuard}
135
+ ${queryAll}
136
+ } catch (error) {
137
+ ${authCatchBlock}context.error(\`Error fetching from ${table}:\`, error);
138
+ return { status: 500, jsonBody: { error: 'Failed to fetch items' } };
139
+ }
140
+ },
141
+ });
142
+ `;
143
+ }
144
+ if (ops.has("getById")) {
145
+ code += `
146
+ // GET /api/${modelCamel}/{id} - ID指定取得
147
+ app.http('${modelCamel}-get-by-id', {
148
+ methods: ['GET'],
149
+ route: '${modelCamel}/{id}',
150
+ authLevel: 'function',
151
+ handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
152
+ try {${readGuard}
153
+ const id = request.params.id;
154
+ if (!id) {
155
+ return { status: 400, jsonBody: { error: 'ID is required' } };
156
+ }
157
+ ${queryById}
158
+ } catch (error) {
159
+ ${authCatchBlock}context.error(\`Error fetching item from ${table}:\`, error);
160
+ return { status: 500, jsonBody: { error: 'Failed to fetch item' } };
161
+ }
162
+ },
163
+ });
164
+ `;
165
+ }
166
+ return code;
167
+ }
168
+ /**
169
+ * API コネクタ用 TypeScript Azure Functions を生成
170
+ */
171
+ function generateApiConnectorFunctionTS(model, sharedPackageName, connectorDef, modelConnector, authPolicy, authProvider) {
172
+ const modelCamel = (0, model_parser_1.toCamelCase)(model.name);
173
+ const schemaName = model.schemaName;
174
+ const ops = new Set(modelConnector.operations);
175
+ const baseUrlEnv = connectorDef.baseUrlEnvVar;
176
+ const endpoints = modelConnector.endpoints || {};
177
+ const authSetup = generateTSAuthSetup(connectorDef);
178
+ // Auth guard setup (user access control, separate from connector auth to external APIs)
179
+ const hasAuth = !!authPolicy;
180
+ const namedAuth = Boolean(authPolicy && (authPolicy.policy || typeof authPolicy.read === 'string' || typeof authPolicy.write === 'string'));
181
+ const authImportLine = hasAuth ? `\n${(0, auth_generator_1.generateAuthImportTS)(namedAuth)}\n` : '';
182
+ const readGuard = hasAuth ? `\n${(0, auth_generator_1.generateAuthGuardTS)(authPolicy, 'read', authProvider)}\n` : '';
183
+ const writeGuard = hasAuth ? `\n${(0, auth_generator_1.generateAuthGuardTS)(authPolicy, 'write', authProvider)}\n` : '';
184
+ const authCatchBlock = hasAuth
185
+ ? ` const authErr = handleAuthError(error);
186
+ if (authErr) return authErr;\n `
187
+ : ` `;
188
+ let code = `import { app, HttpRequest, HttpResponseInit, InvocationContext } from '@azure/functions';
189
+ import { z } from 'zod/v4';
190
+ import { ${schemaName} } from '${sharedPackageName}';${authImportLine}
191
+
192
+ function getBaseUrl(): string {
193
+ return process.env.${baseUrlEnv} || '';
194
+ }
195
+
196
+ ${authSetup.helperCode}
197
+ `;
198
+ if (ops.has("getAll")) {
199
+ const endpoint = endpoints.getAll || `GET /${modelCamel}`;
200
+ const [, epPath] = endpoint.split(" ");
201
+ code += `
202
+ // GET /api/${modelCamel} - 全件取得
203
+ app.http('${modelCamel}-get-all', {
204
+ methods: ['GET'],
205
+ route: '${modelCamel}',
206
+ authLevel: 'function',
207
+ handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
208
+ try {${readGuard}
209
+ const url = getBaseUrl() + '${epPath}';
210
+ const response = await fetch(url, {
211
+ method: 'GET',
212
+ headers: { ...getAuthHeaders() },
213
+ });
214
+
215
+ if (!response.ok) {
216
+ context.error(\`External API error: \${response.status}\`);
217
+ return { status: response.status, jsonBody: { error: 'External API request failed' } };
218
+ }
219
+
220
+ const data = await response.json();
221
+ const validated = z.array(${schemaName}).parse(data);
222
+ return { status: 200, jsonBody: validated };
223
+ } catch (error) {
224
+ ${authCatchBlock}context.error(\`Error fetching from external API:\`, error);
225
+ return { status: 500, jsonBody: { error: 'Failed to fetch items' } };
226
+ }
227
+ },
228
+ });
229
+ `;
230
+ }
231
+ if (ops.has("getById")) {
232
+ const endpoint = endpoints.getById || `GET /${modelCamel}/{id}`;
233
+ const [, epPath] = endpoint.split(" ");
234
+ code += `
235
+ // GET /api/${modelCamel}/{id} - ID指定取得
236
+ app.http('${modelCamel}-get-by-id', {
237
+ methods: ['GET'],
238
+ route: '${modelCamel}/{id}',
239
+ authLevel: 'function',
240
+ handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
241
+ try {${readGuard}
242
+ const id = request.params.id;
243
+ if (!id) {
244
+ return { status: 400, jsonBody: { error: 'ID is required' } };
245
+ }
246
+ const url = getBaseUrl() + '${epPath}'.replace('{id}', id);
247
+ const response = await fetch(url, {
248
+ method: 'GET',
249
+ headers: { ...getAuthHeaders() },
250
+ });
251
+
252
+ if (!response.ok) {
253
+ if (response.status === 404) {
254
+ return { status: 404, jsonBody: { error: 'Item not found' } };
255
+ }
256
+ return { status: response.status, jsonBody: { error: 'External API request failed' } };
257
+ }
258
+
259
+ const data = await response.json();
260
+ const validated = ${schemaName}.parse(data);
261
+ return { status: 200, jsonBody: validated };
262
+ } catch (error) {
263
+ ${authCatchBlock}context.error(\`Error fetching item from external API:\`, error);
264
+ return { status: 500, jsonBody: { error: 'Failed to fetch item' } };
265
+ }
266
+ },
267
+ });
268
+ `;
269
+ }
270
+ if (ops.has("create")) {
271
+ const endpoint = endpoints.create || `POST /${modelCamel}`;
272
+ const [, epPath] = endpoint.split(" ");
273
+ code += `
274
+ // POST /api/${modelCamel} - 新規作成
275
+ app.http('${modelCamel}-create', {
276
+ methods: ['POST'],
277
+ route: '${modelCamel}',
278
+ authLevel: 'function',
279
+ handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
280
+ try {${writeGuard}
281
+ const body = await request.json();
282
+ const url = getBaseUrl() + '${epPath}';
283
+ const response = await fetch(url, {
284
+ method: 'POST',
285
+ headers: { 'Content-Type': 'application/json', ...getAuthHeaders() },
286
+ body: JSON.stringify(body),
287
+ });
288
+
289
+ if (!response.ok) {
290
+ const errorText = await response.text();
291
+ context.error(\`External API error: \${response.status} \${errorText}\`);
292
+ return { status: response.status, jsonBody: { error: 'External API request failed' } };
293
+ }
294
+
295
+ const data = await response.json();
296
+ return { status: 201, jsonBody: data };
297
+ } catch (error) {
298
+ ${authCatchBlock}context.error(\`Error creating item via external API:\`, error);
299
+ return { status: 500, jsonBody: { error: 'Failed to create item' } };
300
+ }
301
+ },
302
+ });
303
+ `;
304
+ }
305
+ if (ops.has("update")) {
306
+ const endpoint = endpoints.update || `PUT /${modelCamel}/{id}`;
307
+ const parts = endpoint.split(" ");
308
+ const method = parts[0];
309
+ const epPath = parts[1];
310
+ code += `
311
+ // PUT /api/${modelCamel}/{id} - 更新
312
+ app.http('${modelCamel}-update', {
313
+ methods: ['PUT'],
314
+ route: '${modelCamel}/{id}',
315
+ authLevel: 'function',
316
+ handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
317
+ try {${writeGuard}
318
+ const id = request.params.id;
319
+ if (!id) {
320
+ return { status: 400, jsonBody: { error: 'ID is required' } };
321
+ }
322
+ const body = await request.json();
323
+ const url = getBaseUrl() + '${epPath}'.replace('{id}', id);
324
+ const response = await fetch(url, {
325
+ method: '${method}',
326
+ headers: { 'Content-Type': 'application/json', ...getAuthHeaders() },
327
+ body: JSON.stringify(body),
328
+ });
329
+
330
+ if (!response.ok) {
331
+ const errorText = await response.text();
332
+ context.error(\`External API error: \${response.status} \${errorText}\`);
333
+ return { status: response.status, jsonBody: { error: 'External API request failed' } };
334
+ }
335
+
336
+ const data = await response.json();
337
+ return { status: 200, jsonBody: data };
338
+ } catch (error) {
339
+ ${authCatchBlock}context.error(\`Error updating item via external API:\`, error);
340
+ return { status: 500, jsonBody: { error: 'Failed to update item' } };
341
+ }
342
+ },
343
+ });
344
+ `;
345
+ }
346
+ if (ops.has("delete")) {
347
+ const endpoint = endpoints.delete || `DELETE /${modelCamel}/{id}`;
348
+ const [, epPath] = endpoint.split(" ");
349
+ code += `
350
+ // DELETE /api/${modelCamel}/{id} - 削除
351
+ app.http('${modelCamel}-delete', {
352
+ methods: ['DELETE'],
353
+ route: '${modelCamel}/{id}',
354
+ authLevel: 'function',
355
+ handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
356
+ try {${writeGuard}
357
+ const id = request.params.id;
358
+ if (!id) {
359
+ return { status: 400, jsonBody: { error: 'ID is required' } };
360
+ }
361
+ const url = getBaseUrl() + '${epPath}'.replace('{id}', id);
362
+ const response = await fetch(url, {
363
+ method: 'DELETE',
364
+ headers: { ...getAuthHeaders() },
365
+ });
366
+
367
+ if (!response.ok && response.status !== 404) {
368
+ return { status: response.status, jsonBody: { error: 'External API request failed' } };
369
+ }
370
+
371
+ return { status: 204 };
372
+ } catch (error) {
373
+ ${authCatchBlock}context.error(\`Error deleting item via external API:\`, error);
374
+ return { status: 500, jsonBody: { error: 'Failed to delete item' } };
375
+ }
376
+ },
377
+ });
378
+ `;
379
+ }
380
+ return code;
381
+ }
382
+ function generateTSAuthSetup(connectorDef) {
383
+ if (!connectorDef.auth) {
384
+ return {
385
+ helperCode: `function getAuthHeaders(): Record<string, string> {
386
+ return {};
387
+ }`,
388
+ };
389
+ }
390
+ const { type, envVar, placement, paramName } = connectorDef.auth;
391
+ if (type === "apiKey" && placement === "query") {
392
+ return {
393
+ helperCode: `function getAuthHeaders(): Record<string, string> {
394
+ return {};
395
+ }
396
+
397
+ function appendAuthQuery(url: string): string {
398
+ const sep = url.includes('?') ? '&' : '?';
399
+ return url + sep + '${paramName || "apiKey"}=' + encodeURIComponent(process.env.${envVar} || '');
400
+ }`,
401
+ };
402
+ }
403
+ if (type === "apiKey" && placement !== "query") {
404
+ return {
405
+ helperCode: `function getAuthHeaders(): Record<string, string> {
406
+ return { '${paramName || "X-Api-Key"}': process.env.${envVar} || '' };
407
+ }`,
408
+ };
409
+ }
410
+ // bearer
411
+ return {
412
+ helperCode: `function getAuthHeaders(): Record<string, string> {
413
+ return { 'Authorization': 'Bearer ' + (process.env.${envVar} || '') };
414
+ }`,
415
+ };
416
+ }
417
+ // ─── C# Generators ────────────────────────────────────────────────
418
+ /**
419
+ * RDB コネクタ用 C# Azure Functions を生成
420
+ */
421
+ function generateRdbConnectorFunctionCSharp(model, connectorDef, modelConnector) {
422
+ const modelName = model.name;
423
+ const modelCamel = (0, model_parser_1.toCamelCase)(modelName);
424
+ const className = `${modelName}ConnectorFunctions`;
425
+ const ops = new Set(modelConnector.operations);
426
+ const table = modelConnector.table;
427
+ const idCol = modelConnector.idColumn || "id";
428
+ const envVar = connectorDef.connectionEnvVar;
429
+ const provider = connectorDef.provider;
430
+ const usingStatements = provider === "mysql"
431
+ ? `using MySqlConnector;`
432
+ : provider === "postgres"
433
+ ? `using Npgsql;`
434
+ : `using Microsoft.Data.SqlClient;`;
435
+ const connType = provider === "mysql"
436
+ ? "MySqlConnection"
437
+ : provider === "postgres"
438
+ ? "NpgsqlConnection"
439
+ : "SqlConnection";
440
+ const cmdType = provider === "mysql"
441
+ ? "MySqlCommand"
442
+ : provider === "postgres"
443
+ ? "NpgsqlCommand"
444
+ : "SqlCommand";
445
+ const paramPrefix = provider === "mysql" ? "@" : provider === "postgres" ? "@" : "@";
446
+ let methods = "";
447
+ if (ops.has("getAll")) {
448
+ methods += `
449
+ [Function("${modelCamel}GetAll")]
450
+ public async Task<HttpResponseData> GetAll(
451
+ [HttpTrigger(AuthorizationLevel.Function, "get", Route = "${modelCamel}")] HttpRequestData request)
452
+ {
453
+ try
454
+ {
455
+ using var connection = new ${connType}(Environment.GetEnvironmentVariable("${envVar}"));
456
+ await connection.OpenAsync();
457
+
458
+ using var command = new ${cmdType}("SELECT * FROM ${table}", connection);
459
+ using var reader = await command.ExecuteReaderAsync();
460
+ var items = new List<Dictionary<string, object?>>();
461
+ while (await reader.ReadAsync())
462
+ {
463
+ var item = new Dictionary<string, object?>();
464
+ for (int i = 0; i < reader.FieldCount; i++)
465
+ {
466
+ item[reader.GetName(i)] = reader.IsDBNull(i) ? null : reader.GetValue(i);
467
+ }
468
+ items.Add(item);
469
+ }
470
+
471
+ return await WriteJsonAsync(request, HttpStatusCode.OK, items);
472
+ }
473
+ catch (Exception ex)
474
+ {
475
+ _logger.LogError(ex, "Failed to fetch items from ${table}.");
476
+ return await WriteJsonAsync(request, HttpStatusCode.InternalServerError, new { error = "Failed to fetch items" });
477
+ }
478
+ }
479
+ `;
480
+ }
481
+ if (ops.has("getById")) {
482
+ methods += `
483
+ [Function("${modelCamel}GetById")]
484
+ public async Task<HttpResponseData> GetById(
485
+ [HttpTrigger(AuthorizationLevel.Function, "get", Route = "${modelCamel}/{id}")] HttpRequestData request,
486
+ string id)
487
+ {
488
+ try
489
+ {
490
+ using var connection = new ${connType}(Environment.GetEnvironmentVariable("${envVar}"));
491
+ await connection.OpenAsync();
492
+
493
+ using var command = new ${cmdType}("SELECT * FROM ${table} WHERE ${idCol} = ${paramPrefix}id", connection);
494
+ command.Parameters.AddWithValue("${paramPrefix}id", id);
495
+ using var reader = await command.ExecuteReaderAsync();
496
+
497
+ if (!await reader.ReadAsync())
498
+ {
499
+ return await WriteJsonAsync(request, HttpStatusCode.NotFound, new { error = "${modelName} not found", id });
500
+ }
501
+
502
+ var item = new Dictionary<string, object?>();
503
+ for (int i = 0; i < reader.FieldCount; i++)
504
+ {
505
+ item[reader.GetName(i)] = reader.IsDBNull(i) ? null : reader.GetValue(i);
506
+ }
507
+
508
+ return await WriteJsonAsync(request, HttpStatusCode.OK, item);
509
+ }
510
+ catch (Exception ex)
511
+ {
512
+ _logger.LogError(ex, "Failed to fetch ${modelName} item {Id} from ${table}.", id);
513
+ return await WriteJsonAsync(request, HttpStatusCode.InternalServerError, new { error = "Failed to fetch item", id });
514
+ }
515
+ }
516
+ `;
517
+ }
518
+ return `using System.Net;
519
+ using System.Text.Json;
520
+ ${usingStatements}
521
+ using Microsoft.Azure.Functions.Worker;
522
+ using Microsoft.Azure.Functions.Worker.Http;
523
+ using Microsoft.Extensions.Logging;
524
+
525
+ namespace SwallowKit.Functions;
526
+
527
+ public sealed class ${className}
528
+ {
529
+ private readonly ILogger<${className}> _logger;
530
+
531
+ public ${className}(ILogger<${className}> logger)
532
+ {
533
+ _logger = logger;
534
+ }
535
+
536
+ private static async Task<HttpResponseData> WriteJsonAsync(HttpRequestData request, HttpStatusCode status, object payload)
537
+ {
538
+ var response = request.CreateResponse(status);
539
+ await response.WriteAsJsonAsync(payload);
540
+ return response;
541
+ }
542
+ ${methods}}
543
+ `;
544
+ }
545
+ /**
546
+ * API コネクタ用 C# Azure Functions を生成
547
+ */
548
+ function generateApiConnectorFunctionCSharp(model, connectorDef, modelConnector) {
549
+ const modelName = model.name;
550
+ const modelCamel = (0, model_parser_1.toCamelCase)(modelName);
551
+ const className = `${modelName}ConnectorFunctions`;
552
+ const ops = new Set(modelConnector.operations);
553
+ const baseUrlEnv = connectorDef.baseUrlEnvVar;
554
+ const endpoints = modelConnector.endpoints || {};
555
+ const authSetup = generateCSharpAuthSetup(connectorDef);
556
+ let methods = "";
557
+ if (ops.has("getAll")) {
558
+ const endpoint = endpoints.getAll || `GET /${modelCamel}`;
559
+ const [, epPath] = endpoint.split(" ");
560
+ methods += `
561
+ [Function("${modelCamel}GetAll")]
562
+ public async Task<HttpResponseData> GetAll(
563
+ [HttpTrigger(AuthorizationLevel.Function, "get", Route = "${modelCamel}")] HttpRequestData request)
564
+ {
565
+ try
566
+ {
567
+ var url = BaseUrl + "${epPath}";
568
+ using var httpRequest = new HttpRequestMessage(HttpMethod.Get, url);
569
+ ${authSetup.applyAuth}
570
+ var response = await _httpClient.SendAsync(httpRequest);
571
+
572
+ if (!response.IsSuccessStatusCode)
573
+ {
574
+ return await WriteJsonAsync(request, (HttpStatusCode)response.StatusCode, new { error = "External API request failed" });
575
+ }
576
+
577
+ var content = await response.Content.ReadAsStringAsync();
578
+ var data = JsonSerializer.Deserialize<JsonElement>(content);
579
+ return await WriteJsonAsync(request, HttpStatusCode.OK, data);
580
+ }
581
+ catch (Exception ex)
582
+ {
583
+ _logger.LogError(ex, "Failed to fetch ${modelName} items from external API.");
584
+ return await WriteJsonAsync(request, HttpStatusCode.InternalServerError, new { error = "Failed to fetch items" });
585
+ }
586
+ }
587
+ `;
588
+ }
589
+ if (ops.has("getById")) {
590
+ const endpoint = endpoints.getById || `GET /${modelCamel}/{id}`;
591
+ const [, epPath] = endpoint.split(" ");
592
+ methods += `
593
+ [Function("${modelCamel}GetById")]
594
+ public async Task<HttpResponseData> GetById(
595
+ [HttpTrigger(AuthorizationLevel.Function, "get", Route = "${modelCamel}/{id}")] HttpRequestData request,
596
+ string id)
597
+ {
598
+ try
599
+ {
600
+ var url = BaseUrl + "${epPath}".Replace("{id}", id);
601
+ using var httpRequest = new HttpRequestMessage(HttpMethod.Get, url);
602
+ ${authSetup.applyAuth}
603
+ var response = await _httpClient.SendAsync(httpRequest);
604
+
605
+ if (response.StatusCode == HttpStatusCode.NotFound)
606
+ {
607
+ return await WriteJsonAsync(request, HttpStatusCode.NotFound, new { error = "${modelName} not found", id });
608
+ }
609
+ if (!response.IsSuccessStatusCode)
610
+ {
611
+ return await WriteJsonAsync(request, (HttpStatusCode)response.StatusCode, new { error = "External API request failed" });
612
+ }
613
+
614
+ var content = await response.Content.ReadAsStringAsync();
615
+ var data = JsonSerializer.Deserialize<JsonElement>(content);
616
+ return await WriteJsonAsync(request, HttpStatusCode.OK, data);
617
+ }
618
+ catch (Exception ex)
619
+ {
620
+ _logger.LogError(ex, "Failed to fetch ${modelName} item {Id} from external API.", id);
621
+ return await WriteJsonAsync(request, HttpStatusCode.InternalServerError, new { error = "Failed to fetch item", id });
622
+ }
623
+ }
624
+ `;
625
+ }
626
+ if (ops.has("create")) {
627
+ const endpoint = endpoints.create || `POST /${modelCamel}`;
628
+ const [, epPath] = endpoint.split(" ");
629
+ methods += `
630
+ [Function("${modelCamel}Create")]
631
+ public async Task<HttpResponseData> Create(
632
+ [HttpTrigger(AuthorizationLevel.Function, "post", Route = "${modelCamel}")] HttpRequestData request)
633
+ {
634
+ try
635
+ {
636
+ using var reader = new StreamReader(request.Body);
637
+ var body = await reader.ReadToEndAsync();
638
+ var url = BaseUrl + "${epPath}";
639
+ using var httpRequest = new HttpRequestMessage(HttpMethod.Post, url)
640
+ {
641
+ Content = new StringContent(body, System.Text.Encoding.UTF8, "application/json")
642
+ };
643
+ ${authSetup.applyAuth}
644
+ var response = await _httpClient.SendAsync(httpRequest);
645
+
646
+ var content = await response.Content.ReadAsStringAsync();
647
+ var data = JsonSerializer.Deserialize<JsonElement>(content);
648
+ var status = response.IsSuccessStatusCode ? HttpStatusCode.Created : (HttpStatusCode)response.StatusCode;
649
+ return await WriteJsonAsync(request, status, data);
650
+ }
651
+ catch (Exception ex)
652
+ {
653
+ _logger.LogError(ex, "Failed to create ${modelName} item via external API.");
654
+ return await WriteJsonAsync(request, HttpStatusCode.InternalServerError, new { error = "Failed to create item" });
655
+ }
656
+ }
657
+ `;
658
+ }
659
+ if (ops.has("update")) {
660
+ const endpoint = endpoints.update || `PUT /${modelCamel}/{id}`;
661
+ const parts = endpoint.split(" ");
662
+ const httpMethod = parts[0] === "PATCH" ? "Patch" : "Put";
663
+ const epPath = parts[1];
664
+ methods += `
665
+ [Function("${modelCamel}Update")]
666
+ public async Task<HttpResponseData> Update(
667
+ [HttpTrigger(AuthorizationLevel.Function, "put", Route = "${modelCamel}/{id}")] HttpRequestData request,
668
+ string id)
669
+ {
670
+ try
671
+ {
672
+ using var reader = new StreamReader(request.Body);
673
+ var body = await reader.ReadToEndAsync();
674
+ var url = BaseUrl + "${epPath}".Replace("{id}", id);
675
+ using var httpRequest = new HttpRequestMessage(HttpMethod.${httpMethod}, url)
676
+ {
677
+ Content = new StringContent(body, System.Text.Encoding.UTF8, "application/json")
678
+ };
679
+ ${authSetup.applyAuth}
680
+ var response = await _httpClient.SendAsync(httpRequest);
681
+
682
+ var content = await response.Content.ReadAsStringAsync();
683
+ var data = JsonSerializer.Deserialize<JsonElement>(content);
684
+ return await WriteJsonAsync(request, response.IsSuccessStatusCode ? HttpStatusCode.OK : (HttpStatusCode)response.StatusCode, data);
685
+ }
686
+ catch (Exception ex)
687
+ {
688
+ _logger.LogError(ex, "Failed to update ${modelName} item {Id} via external API.", id);
689
+ return await WriteJsonAsync(request, HttpStatusCode.InternalServerError, new { error = "Failed to update item", id });
690
+ }
691
+ }
692
+ `;
693
+ }
694
+ if (ops.has("delete")) {
695
+ const endpoint = endpoints.delete || `DELETE /${modelCamel}/{id}`;
696
+ const [, epPath] = endpoint.split(" ");
697
+ methods += `
698
+ [Function("${modelCamel}Delete")]
699
+ public async Task<HttpResponseData> Delete(
700
+ [HttpTrigger(AuthorizationLevel.Function, "delete", Route = "${modelCamel}/{id}")] HttpRequestData request,
701
+ string id)
702
+ {
703
+ try
704
+ {
705
+ var url = BaseUrl + "${epPath}".Replace("{id}", id);
706
+ using var httpRequest = new HttpRequestMessage(HttpMethod.Delete, url);
707
+ ${authSetup.applyAuth}
708
+ var response = await _httpClient.SendAsync(httpRequest);
709
+
710
+ if (!response.IsSuccessStatusCode && response.StatusCode != HttpStatusCode.NotFound)
711
+ {
712
+ return await WriteJsonAsync(request, (HttpStatusCode)response.StatusCode, new { error = "External API request failed" });
713
+ }
714
+
715
+ return request.CreateResponse(HttpStatusCode.NoContent);
716
+ }
717
+ catch (Exception ex)
718
+ {
719
+ _logger.LogError(ex, "Failed to delete ${modelName} item {Id} via external API.", id);
720
+ return await WriteJsonAsync(request, HttpStatusCode.InternalServerError, new { error = "Failed to delete item", id });
721
+ }
722
+ }
723
+ `;
724
+ }
725
+ return `using System.Net;
726
+ using System.Text.Json;
727
+ using Microsoft.Azure.Functions.Worker;
728
+ using Microsoft.Azure.Functions.Worker.Http;
729
+ using Microsoft.Extensions.Logging;
730
+
731
+ namespace SwallowKit.Functions;
732
+
733
+ public sealed class ${className}
734
+ {
735
+ private readonly ILogger<${className}> _logger;
736
+ private readonly HttpClient _httpClient;
737
+ private static string BaseUrl => Environment.GetEnvironmentVariable("${baseUrlEnv}") ?? "";
738
+ ${authSetup.fieldDecl}
739
+
740
+ public ${className}(ILogger<${className}> logger, HttpClient httpClient)
741
+ {
742
+ _logger = logger;
743
+ _httpClient = httpClient;
744
+ }
745
+
746
+ private static async Task<HttpResponseData> WriteJsonAsync(HttpRequestData request, HttpStatusCode status, object payload)
747
+ {
748
+ var response = request.CreateResponse(status);
749
+ await response.WriteAsJsonAsync(payload);
750
+ return response;
751
+ }
752
+ ${methods}}
753
+ `;
754
+ }
755
+ function generateCSharpAuthSetup(connectorDef) {
756
+ if (!connectorDef.auth) {
757
+ return { fieldDecl: "", applyAuth: "" };
758
+ }
759
+ const { type, envVar, placement, paramName } = connectorDef.auth;
760
+ if (type === "apiKey" && placement === "query") {
761
+ return {
762
+ fieldDecl: "",
763
+ applyAuth: `// API key is appended as query parameter
764
+ var uriBuilder = new UriBuilder(httpRequest.RequestUri!);
765
+ var query = System.Web.HttpUtility.ParseQueryString(uriBuilder.Query);
766
+ query["${paramName || "apiKey"}"] = Environment.GetEnvironmentVariable("${envVar}");
767
+ uriBuilder.Query = query.ToString();
768
+ httpRequest.RequestUri = uriBuilder.Uri;`,
769
+ };
770
+ }
771
+ if (type === "apiKey") {
772
+ return {
773
+ fieldDecl: "",
774
+ applyAuth: `httpRequest.Headers.Add("${paramName || "X-Api-Key"}", Environment.GetEnvironmentVariable("${envVar}"));`,
775
+ };
776
+ }
777
+ // bearer
778
+ return {
779
+ fieldDecl: "",
780
+ applyAuth: `httpRequest.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("${envVar}"));`,
781
+ };
782
+ }
783
+ // ─── Python Generators ────────────────────────────────────────────
784
+ /**
785
+ * RDB コネクタ用 Python Azure Functions を生成
786
+ */
787
+ function generateRdbConnectorFunctionPython(model, connectorDef, modelConnector) {
788
+ const modelName = model.name;
789
+ const modelCamel = (0, model_parser_1.toCamelCase)(modelName);
790
+ const modelSnake = (0, model_parser_1.toKebabCase)(modelName).replace(/-/g, "_");
791
+ const ops = new Set(modelConnector.operations);
792
+ const table = modelConnector.table;
793
+ const idCol = modelConnector.idColumn || "id";
794
+ const envVar = connectorDef.connectionEnvVar;
795
+ let handlers = "";
796
+ if (ops.has("getAll")) {
797
+ handlers += `
798
+ @bp.route(route="${modelCamel}", methods=["GET"])
799
+ def ${modelSnake}_get_all(req: func.HttpRequest) -> func.HttpResponse:
800
+ try:
801
+ conn = get_connection()
802
+ cursor = conn.cursor(dictionary=True)
803
+ cursor.execute("SELECT * FROM ${table}")
804
+ items = cursor.fetchall()
805
+ cursor.close()
806
+ conn.close()
807
+ return _json_response(items, 200)
808
+ except Exception as e:
809
+ logging.error(f"Error fetching from ${table}: {e}")
810
+ return _json_response({"error": "Failed to fetch items"}, 500)
811
+ `;
812
+ }
813
+ if (ops.has("getById")) {
814
+ handlers += `
815
+ @bp.route(route="${modelCamel}/{id}", methods=["GET"])
816
+ def ${modelSnake}_get_by_id(req: func.HttpRequest) -> func.HttpResponse:
817
+ item_id = req.route_params.get("id")
818
+ if not item_id:
819
+ return _json_response({"error": "ID is required"}, 400)
820
+ try:
821
+ conn = get_connection()
822
+ cursor = conn.cursor(dictionary=True)
823
+ cursor.execute("SELECT * FROM ${table} WHERE ${idCol} = %s", (item_id,))
824
+ item = cursor.fetchone()
825
+ cursor.close()
826
+ conn.close()
827
+ if item is None:
828
+ return _json_response({"error": "Item not found"}, 404)
829
+ return _json_response(item, 200)
830
+ except Exception as e:
831
+ logging.error(f"Error fetching item from ${table}: {e}")
832
+ return _json_response({"error": "Failed to fetch item"}, 500)
833
+ `;
834
+ }
835
+ const blueprint = `import json
836
+ import logging
837
+ import os
838
+
839
+ import azure.functions as func
840
+ import mysql.connector
841
+
842
+ bp = func.Blueprint()
843
+
844
+
845
+ def _json_response(payload, status_code: int) -> func.HttpResponse:
846
+ return func.HttpResponse(
847
+ body=json.dumps(payload, ensure_ascii=False, default=str),
848
+ status_code=status_code,
849
+ mimetype="application/json",
850
+ )
851
+
852
+
853
+ def get_connection():
854
+ return mysql.connector.connect(
855
+ host=os.environ.get("${envVar}_HOST", "localhost"),
856
+ user=os.environ.get("${envVar}_USER", ""),
857
+ password=os.environ.get("${envVar}_PASSWORD", ""),
858
+ database=os.environ.get("${envVar}_DATABASE", ""),
859
+ )
860
+
861
+ ${handlers}`;
862
+ return {
863
+ blueprint,
864
+ registration: `from blueprints.${modelSnake} import bp as ${modelSnake}_bp\napp.register_blueprint(${modelSnake}_bp)`,
865
+ };
866
+ }
867
+ /**
868
+ * API コネクタ用 Python Azure Functions を生成
869
+ */
870
+ function generateApiConnectorFunctionPython(model, connectorDef, modelConnector) {
871
+ const modelName = model.name;
872
+ const modelCamel = (0, model_parser_1.toCamelCase)(modelName);
873
+ const modelSnake = (0, model_parser_1.toKebabCase)(modelName).replace(/-/g, "_");
874
+ const ops = new Set(modelConnector.operations);
875
+ const baseUrlEnv = connectorDef.baseUrlEnvVar;
876
+ const endpoints = modelConnector.endpoints || {};
877
+ const authSetup = generatePythonAuthSetup(connectorDef);
878
+ let handlers = "";
879
+ if (ops.has("getAll")) {
880
+ const endpoint = endpoints.getAll || `GET /${modelCamel}`;
881
+ const [, epPath] = endpoint.split(" ");
882
+ handlers += `
883
+ @bp.route(route="${modelCamel}", methods=["GET"])
884
+ def ${modelSnake}_get_all(req: func.HttpRequest) -> func.HttpResponse:
885
+ try:
886
+ url = BASE_URL + "${epPath}"
887
+ response = requests.get(url, ${authSetup.requestKwargs})
888
+ response.raise_for_status()
889
+ return _json_response(response.json(), 200)
890
+ except Exception as e:
891
+ logging.error(f"Error fetching from external API: {e}")
892
+ return _json_response({"error": "Failed to fetch items"}, 500)
893
+ `;
894
+ }
895
+ if (ops.has("getById")) {
896
+ const endpoint = endpoints.getById || `GET /${modelCamel}/{id}`;
897
+ const [, epPath] = endpoint.split(" ");
898
+ handlers += `
899
+ @bp.route(route="${modelCamel}/{id}", methods=["GET"])
900
+ def ${modelSnake}_get_by_id(req: func.HttpRequest) -> func.HttpResponse:
901
+ item_id = req.route_params.get("id")
902
+ if not item_id:
903
+ return _json_response({"error": "ID is required"}, 400)
904
+ try:
905
+ url = BASE_URL + "${epPath}".replace("{id}", item_id)
906
+ response = requests.get(url, ${authSetup.requestKwargs})
907
+ if response.status_code == 404:
908
+ return _json_response({"error": "Item not found"}, 404)
909
+ response.raise_for_status()
910
+ return _json_response(response.json(), 200)
911
+ except Exception as e:
912
+ logging.error(f"Error fetching item from external API: {e}")
913
+ return _json_response({"error": "Failed to fetch item"}, 500)
914
+ `;
915
+ }
916
+ if (ops.has("create")) {
917
+ const endpoint = endpoints.create || `POST /${modelCamel}`;
918
+ const [, epPath] = endpoint.split(" ");
919
+ handlers += `
920
+ @bp.route(route="${modelCamel}", methods=["POST"])
921
+ def ${modelSnake}_create(req: func.HttpRequest) -> func.HttpResponse:
922
+ try:
923
+ body = req.get_json()
924
+ url = BASE_URL + "${epPath}"
925
+ response = requests.post(url, json=body, ${authSetup.requestKwargs})
926
+ response.raise_for_status()
927
+ return _json_response(response.json(), 201)
928
+ except Exception as e:
929
+ logging.error(f"Error creating item via external API: {e}")
930
+ return _json_response({"error": "Failed to create item"}, 500)
931
+ `;
932
+ }
933
+ if (ops.has("update")) {
934
+ const endpoint = endpoints.update || `PUT /${modelCamel}/{id}`;
935
+ const parts = endpoint.split(" ");
936
+ const method = parts[0].toLowerCase();
937
+ const epPath = parts[1];
938
+ handlers += `
939
+ @bp.route(route="${modelCamel}/{id}", methods=["PUT"])
940
+ def ${modelSnake}_update(req: func.HttpRequest) -> func.HttpResponse:
941
+ item_id = req.route_params.get("id")
942
+ if not item_id:
943
+ return _json_response({"error": "ID is required"}, 400)
944
+ try:
945
+ body = req.get_json()
946
+ url = BASE_URL + "${epPath}".replace("{id}", item_id)
947
+ response = requests.${method}(url, json=body, ${authSetup.requestKwargs})
948
+ response.raise_for_status()
949
+ return _json_response(response.json(), 200)
950
+ except Exception as e:
951
+ logging.error(f"Error updating item via external API: {e}")
952
+ return _json_response({"error": "Failed to update item"}, 500)
953
+ `;
954
+ }
955
+ if (ops.has("delete")) {
956
+ const endpoint = endpoints.delete || `DELETE /${modelCamel}/{id}`;
957
+ const [, epPath] = endpoint.split(" ");
958
+ handlers += `
959
+ @bp.route(route="${modelCamel}/{id}", methods=["DELETE"])
960
+ def ${modelSnake}_delete(req: func.HttpRequest) -> func.HttpResponse:
961
+ item_id = req.route_params.get("id")
962
+ if not item_id:
963
+ return _json_response({"error": "ID is required"}, 400)
964
+ try:
965
+ url = BASE_URL + "${epPath}".replace("{id}", item_id)
966
+ response = requests.delete(url, ${authSetup.requestKwargs})
967
+ return func.HttpResponse(status_code=204)
968
+ except Exception as e:
969
+ logging.error(f"Error deleting item via external API: {e}")
970
+ return _json_response({"error": "Failed to delete item"}, 500)
971
+ `;
972
+ }
973
+ const blueprint = `import json
974
+ import logging
975
+ import os
976
+
977
+ import azure.functions as func
978
+ import requests
979
+
980
+ bp = func.Blueprint()
981
+ BASE_URL = os.environ.get("${baseUrlEnv}", "")
982
+ ${authSetup.setup}
983
+
984
+
985
+ def _json_response(payload, status_code: int) -> func.HttpResponse:
986
+ return func.HttpResponse(
987
+ body=json.dumps(payload, ensure_ascii=False, default=str),
988
+ status_code=status_code,
989
+ mimetype="application/json",
990
+ )
991
+
992
+ ${handlers}`;
993
+ return {
994
+ blueprint,
995
+ registration: `from blueprints.${modelSnake} import bp as ${modelSnake}_bp\napp.register_blueprint(${modelSnake}_bp)`,
996
+ };
997
+ }
998
+ function generatePythonAuthSetup(connectorDef) {
999
+ if (!connectorDef.auth) {
1000
+ return { setup: "", requestKwargs: "timeout=30" };
1001
+ }
1002
+ const { type, envVar, placement, paramName } = connectorDef.auth;
1003
+ if (type === "apiKey" && placement === "query") {
1004
+ return {
1005
+ setup: `API_KEY = os.environ.get("${envVar}", "")`,
1006
+ requestKwargs: `params={"${paramName || "apiKey"}": API_KEY}, timeout=30`,
1007
+ };
1008
+ }
1009
+ if (type === "apiKey") {
1010
+ return {
1011
+ setup: `API_KEY = os.environ.get("${envVar}", "")`,
1012
+ requestKwargs: `headers={"${paramName || "X-Api-Key"}": API_KEY}, timeout=30`,
1013
+ };
1014
+ }
1015
+ // bearer
1016
+ return {
1017
+ setup: `AUTH_TOKEN = os.environ.get("${envVar}", "")`,
1018
+ requestKwargs: `headers={"Authorization": f"Bearer {AUTH_TOKEN}"}, timeout=30`,
1019
+ };
1020
+ }
1021
+ // ─── Helper: check if model has write operations ──────────────────
1022
+ /**
1023
+ * モデルのコネクタ操作が読み取り専用かどうかを判定
1024
+ */
1025
+ function isReadOnlyConnector(operations) {
1026
+ const writeOps = ["create", "update", "delete"];
1027
+ return !operations.some(op => writeOps.includes(op));
1028
+ }
1029
+ //# sourceMappingURL=connector-functions-generator.js.map