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
@@ -34,20 +34,176 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.devCommand = void 0;
37
+ exports.buildFunctionsStartArgs = buildFunctionsStartArgs;
38
+ exports.parseCoreToolsVersion = parseCoreToolsVersion;
39
+ exports.compareVersionNumbers = compareVersionNumbers;
40
+ exports.buildFunctionsCoreToolsCommand = buildFunctionsCoreToolsCommand;
41
+ exports.buildNextDevArgs = buildNextDevArgs;
42
+ exports.buildFunctionsBaseUrl = buildFunctionsBaseUrl;
43
+ exports.buildSwaStartArgs = buildSwaStartArgs;
44
+ exports.getSwaCliInstallCommand = getSwaCliInstallCommand;
45
+ exports.getFunctionsReadinessTimeoutMs = getFunctionsReadinessTimeoutMs;
46
+ exports.waitForHttpServerReady = waitForHttpServerReady;
47
+ exports.getPythonVirtualEnvPaths = getPythonVirtualEnvPaths;
48
+ exports.getCSharpFunctionsBuildArtifactPaths = getCSharpFunctionsBuildArtifactPaths;
49
+ exports.buildPythonFunctionsEnv = buildPythonFunctionsEnv;
50
+ exports.buildFunctionsCosmosEnvironment = buildFunctionsCosmosEnvironment;
51
+ exports.buildDevCommand = buildDevCommand;
37
52
  const commander_1 = require("commander");
38
53
  const child_process_1 = require("child_process");
54
+ const http = __importStar(require("http"));
55
+ const https = __importStar(require("https"));
39
56
  const path = __importStar(require("path"));
40
57
  const fs = __importStar(require("fs"));
58
+ const os = __importStar(require("os"));
59
+ const net = __importStar(require("net"));
41
60
  const cosmos_1 = require("@azure/cosmos");
42
61
  const config_1 = require("../../core/config");
62
+ const dev_seeds_1 = require("./dev-seeds");
43
63
  const package_manager_1 = require("../../utils/package-manager");
64
+ const python_uv_1 = require("../../utils/python-uv");
65
+ const connector_mock_server_1 = require("../../core/mock/connector-mock-server");
66
+ const MINIMUM_CSHARP_CORE_TOOLS_VERSION = '4.6.0';
67
+ const NPM_CORE_TOOLS_PACKAGE = 'azure-functions-core-tools@4';
68
+ function normalizeParsedDevOptions(options) {
69
+ return {
70
+ ...options,
71
+ noFunctions: options.noFunctions ?? options.functions === false,
72
+ noSwa: options.noSwa ?? options.swa === false,
73
+ };
74
+ }
75
+ function buildFunctionsStartArgs(functionsPort) {
76
+ return ['start', '--port', functionsPort];
77
+ }
78
+ function parseCoreToolsVersion(output) {
79
+ const match = output.match(/\d+\.\d+\.\d+/);
80
+ return match ? match[0] : null;
81
+ }
82
+ function compareVersionNumbers(left, right) {
83
+ const leftParts = left.split('.').map((value) => Number.parseInt(value, 10));
84
+ const rightParts = right.split('.').map((value) => Number.parseInt(value, 10));
85
+ const length = Math.max(leftParts.length, rightParts.length);
86
+ for (let index = 0; index < length; index += 1) {
87
+ const leftPart = leftParts[index] ?? 0;
88
+ const rightPart = rightParts[index] ?? 0;
89
+ if (leftPart !== rightPart) {
90
+ return leftPart - rightPart;
91
+ }
92
+ }
93
+ return 0;
94
+ }
95
+ function buildFunctionsCoreToolsCommand(backendLanguage, installedVersion) {
96
+ if (backendLanguage === 'csharp' &&
97
+ (!installedVersion || compareVersionNumbers(installedVersion, MINIMUM_CSHARP_CORE_TOOLS_VERSION) < 0)) {
98
+ const reason = installedVersion
99
+ ? `installed func ${installedVersion} is too old for C# isolated`
100
+ : 'func is not installed';
101
+ return {
102
+ command: 'npm',
103
+ argsPrefix: ['exec', '--yes', NPM_CORE_TOOLS_PACKAGE, '--'],
104
+ label: `npm exec ${NPM_CORE_TOOLS_PACKAGE} (${reason})`,
105
+ };
106
+ }
107
+ return {
108
+ command: 'func',
109
+ argsPrefix: [],
110
+ label: installedVersion ? `func ${installedVersion}` : 'func',
111
+ };
112
+ }
113
+ function buildNextDevArgs(pm, port) {
114
+ const baseArgs = ['next', 'dev', '--port', port, '--webpack'];
115
+ return pm === 'pnpm' ? ['exec', ...baseArgs] : baseArgs;
116
+ }
117
+ function buildFunctionsBaseUrl(host, functionsPort) {
118
+ return `http://${host || 'localhost'}:${functionsPort}`;
119
+ }
120
+ function buildSwaStartArgs(host, nextPort, swaPort) {
121
+ return [
122
+ 'start',
123
+ `http://${host || 'localhost'}:${nextPort}`,
124
+ '--swa-config-location',
125
+ '.',
126
+ '--port',
127
+ swaPort,
128
+ ];
129
+ }
130
+ function getSwaCliInstallCommand(pm) {
131
+ return pm === 'pnpm'
132
+ ? 'pnpm add -Dw @azure/static-web-apps-cli'
133
+ : 'npm install -D @azure/static-web-apps-cli';
134
+ }
135
+ function getFunctionsReadinessTimeoutMs(backendLanguage) {
136
+ return backendLanguage === 'csharp' ? 90000 : 30000;
137
+ }
138
+ async function waitForHttpServerReady(url, timeoutMs = 30000, intervalMs = 500) {
139
+ const deadline = Date.now() + timeoutMs;
140
+ while (Date.now() <= deadline) {
141
+ if (await probeHttpServer(url)) {
142
+ return true;
143
+ }
144
+ if (Date.now() + intervalMs > deadline) {
145
+ break;
146
+ }
147
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
148
+ }
149
+ return probeHttpServer(url);
150
+ }
151
+ function getPythonVirtualEnvPaths(functionsDir) {
152
+ const venvDir = path.join(functionsDir, '.venv');
153
+ const binDir = process.platform === 'win32'
154
+ ? path.join(venvDir, 'Scripts')
155
+ : path.join(venvDir, 'bin');
156
+ const pythonExecutable = process.platform === 'win32'
157
+ ? path.join(binDir, 'python.exe')
158
+ : path.join(binDir, 'python');
159
+ return { venvDir, binDir, pythonExecutable };
160
+ }
161
+ function getCSharpFunctionsBuildArtifactPaths(functionsDir) {
162
+ return [
163
+ path.join(functionsDir, 'bin'),
164
+ path.join(functionsDir, 'obj'),
165
+ ];
166
+ }
167
+ function buildPythonFunctionsEnv(baseEnv, functionsDir) {
168
+ const { venvDir, binDir, pythonExecutable } = getPythonVirtualEnvPaths(functionsDir);
169
+ const pathKey = getPathEnvKey(baseEnv);
170
+ const currentPath = baseEnv[pathKey] || '';
171
+ return {
172
+ ...baseEnv,
173
+ [pathKey]: currentPath ? `${binDir}${path.delimiter}${currentPath}` : binDir,
174
+ VIRTUAL_ENV: venvDir,
175
+ languageWorkers__python__defaultExecutablePath: pythonExecutable,
176
+ };
177
+ }
178
+ function buildFunctionsCosmosEnvironment(baseEnv, databaseName) {
179
+ return {
180
+ ...baseEnv,
181
+ COSMOS_DB_DATABASE_NAME: databaseName,
182
+ };
183
+ }
184
+ function getPathEnvKey(env) {
185
+ return Object.keys(env).find((key) => key.toUpperCase() === 'PATH') || 'PATH';
186
+ }
187
+ function prependToPathEnv(env, entry) {
188
+ const pathKey = getPathEnvKey(env);
189
+ const currentPath = env[pathKey] || '';
190
+ return {
191
+ ...env,
192
+ [pathKey]: currentPath ? `${entry}${path.delimiter}${currentPath}` : entry,
193
+ };
194
+ }
44
195
  /**
45
196
  * Check if Azure Functions Core Tools is installed
46
197
  */
47
198
  async function checkCoreTools() {
199
+ return checkCommand('func', ['--version']);
200
+ }
201
+ async function checkCommand(command, args = ['--version'], options) {
48
202
  return new Promise((resolve) => {
49
- const checkProcess = (0, child_process_1.spawn)('func', ['--version'], {
50
- shell: true,
203
+ const checkProcess = (0, child_process_1.spawn)(command, args, {
204
+ cwd: options?.cwd,
205
+ env: options?.env ?? process.env,
206
+ shell: options?.shell ?? true,
51
207
  stdio: 'pipe',
52
208
  });
53
209
  checkProcess.on('close', (code) => {
@@ -58,12 +214,173 @@ async function checkCoreTools() {
58
214
  });
59
215
  });
60
216
  }
217
+ async function resolveProjectLocalUvCommand(projectRoot) {
218
+ const uvEnv = (0, python_uv_1.buildProjectLocalUvEnv)(process.env, projectRoot);
219
+ const { localUvExecutable } = (0, python_uv_1.getProjectLocalUvPaths)(projectRoot);
220
+ if (await checkCommand('uv', ['--version'], { shell: false })) {
221
+ return { command: 'uv', env: uvEnv };
222
+ }
223
+ if (fs.existsSync(localUvExecutable) && await checkCommand(localUvExecutable, ['--version'], { shell: false })) {
224
+ return { command: localUvExecutable, env: uvEnv };
225
+ }
226
+ console.log('📦 Installing project-local uv...');
227
+ const installer = (0, python_uv_1.getProjectLocalUvInstallerCommand)();
228
+ await runCommand(installer.command, installer.args, projectRoot, 'uv installation', (0, python_uv_1.buildProjectLocalUvInstallerEnv)(process.env, projectRoot), false);
229
+ if (!(fs.existsSync(localUvExecutable) && await checkCommand(localUvExecutable, ['--version'], { shell: false }))) {
230
+ throw new Error('Failed to install project-local uv.');
231
+ }
232
+ return { command: localUvExecutable, env: uvEnv };
233
+ }
234
+ async function getCommandPath(command) {
235
+ const locator = process.platform === 'win32' ? 'where' : 'which';
236
+ const result = await captureCommandOutput(locator, [command]);
237
+ const firstLine = result
238
+ .split(/\r?\n/)
239
+ .map((line) => line.trim())
240
+ .find(Boolean);
241
+ return firstLine || null;
242
+ }
243
+ async function resolveInstalledCoreToolsVersion() {
244
+ if (!(await checkCoreTools())) {
245
+ return null;
246
+ }
247
+ try {
248
+ return parseCoreToolsVersion(await captureCommandOutput('func', ['--version']));
249
+ }
250
+ catch {
251
+ return null;
252
+ }
253
+ }
254
+ async function captureCommandOutput(command, args, cwd, env) {
255
+ return new Promise((resolve, reject) => {
256
+ const child = (0, child_process_1.spawn)(command, args, {
257
+ cwd,
258
+ env,
259
+ shell: false,
260
+ stdio: ['ignore', 'pipe', 'pipe'],
261
+ });
262
+ let stdout = '';
263
+ let stderr = '';
264
+ child.stdout?.on('data', (data) => {
265
+ stdout += data.toString();
266
+ });
267
+ child.stderr?.on('data', (data) => {
268
+ stderr += data.toString();
269
+ });
270
+ child.on('close', (code) => {
271
+ if (code === 0) {
272
+ resolve(stdout);
273
+ }
274
+ else {
275
+ reject(new Error(stderr || stdout || `${command} exited with code ${code}`));
276
+ }
277
+ });
278
+ child.on('error', reject);
279
+ });
280
+ }
281
+ async function probeHttpServer(url) {
282
+ return new Promise((resolve) => {
283
+ const target = new URL(url);
284
+ const requestFactory = target.protocol === 'https:' ? https.request : http.request;
285
+ let settled = false;
286
+ const finish = (value) => {
287
+ if (!settled) {
288
+ settled = true;
289
+ resolve(value);
290
+ }
291
+ };
292
+ const request = requestFactory({
293
+ hostname: target.hostname,
294
+ port: target.port,
295
+ path: target.pathname || '/',
296
+ method: 'GET',
297
+ timeout: 1000,
298
+ }, (response) => {
299
+ response.resume();
300
+ finish(true);
301
+ });
302
+ request.on('timeout', () => {
303
+ request.destroy();
304
+ finish(false);
305
+ });
306
+ request.on('error', () => finish(false));
307
+ request.end();
308
+ });
309
+ }
310
+ async function resolvePythonRuntimeDetails(functionsDir, env) {
311
+ const { pythonExecutable } = getPythonVirtualEnvPaths(functionsDir);
312
+ const output = await captureCommandOutput(pythonExecutable, [
313
+ '-c',
314
+ 'import platform; import sys; print(str(sys.version_info.major) + "." + str(sys.version_info.minor)); print(platform.machine())',
315
+ ], functionsDir, env);
316
+ const [version, architecture] = output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
317
+ if (!version || !architecture) {
318
+ throw new Error('Failed to determine Python runtime details.');
319
+ }
320
+ return { version, architecture };
321
+ }
322
+ async function bridgePythonCoreToolsForWindowsArm64(functionsDir, env) {
323
+ if (process.platform !== 'win32' || process.arch !== 'arm64') {
324
+ return env;
325
+ }
326
+ const funcPath = await getCommandPath('func');
327
+ if (!funcPath) {
328
+ return env;
329
+ }
330
+ const { version, architecture } = await resolvePythonRuntimeDetails(functionsDir, env);
331
+ if (architecture.toUpperCase() !== 'AMD64') {
332
+ return env;
333
+ }
334
+ const coreToolsRoot = path.dirname(funcPath);
335
+ const armWorkerDir = path.join(coreToolsRoot, 'workers', 'python', version, 'WINDOWS', 'Arm64');
336
+ if (fs.existsSync(armWorkerDir)) {
337
+ return env;
338
+ }
339
+ const x64WorkerDir = path.join(coreToolsRoot, 'workers', 'python', version, 'WINDOWS', 'X64');
340
+ if (!fs.existsSync(x64WorkerDir)) {
341
+ return env;
342
+ }
343
+ const patchedRoot = path.join(process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'), 'SwallowKit', 'azure-functions-core-tools-python-bridge');
344
+ if (!fs.existsSync(path.join(patchedRoot, 'func.exe'))) {
345
+ console.log('🩹 Creating a local Azure Functions Core Tools bridge for Windows Arm64 Python...');
346
+ fs.mkdirSync(path.dirname(patchedRoot), { recursive: true });
347
+ fs.cpSync(coreToolsRoot, patchedRoot, { recursive: true });
348
+ }
349
+ const patchedArmWorkerDir = path.join(patchedRoot, 'workers', 'python', version, 'WINDOWS', 'Arm64');
350
+ const patchedX64WorkerDir = path.join(patchedRoot, 'workers', 'python', version, 'WINDOWS', 'X64');
351
+ if (!fs.existsSync(patchedArmWorkerDir) && fs.existsSync(patchedX64WorkerDir)) {
352
+ fs.cpSync(patchedX64WorkerDir, patchedArmWorkerDir, { recursive: true });
353
+ }
354
+ console.log(`🩹 Using bridged Azure Functions Core Tools from ${patchedRoot}`);
355
+ return prependToPathEnv(env, patchedRoot);
356
+ }
357
+ async function preparePythonFunctionsEnvironment(functionsDir) {
358
+ const projectRoot = (0, python_uv_1.getPythonProjectRoot)(functionsDir);
359
+ const { command: uvCommand, env: uvEnv } = await resolveProjectLocalUvCommand(projectRoot);
360
+ const { venvDir, pythonExecutable } = getPythonVirtualEnvPaths(functionsDir);
361
+ const hasUsableVirtualEnv = fs.existsSync(pythonExecutable) && await checkCommand(pythonExecutable, ['--version'], {
362
+ cwd: functionsDir,
363
+ env: uvEnv,
364
+ shell: false,
365
+ });
366
+ if (!hasUsableVirtualEnv) {
367
+ const venvArgs = (0, python_uv_1.buildUvVenvArgs)('.venv');
368
+ if (fs.existsSync(venvDir)) {
369
+ venvArgs.push('--clear');
370
+ }
371
+ console.log('📦 Creating Python virtual environment with uv...');
372
+ await runCommand(uvCommand, venvArgs, functionsDir, 'python virtual environment setup', uvEnv, false);
373
+ }
374
+ console.log('📦 Installing Python Azure Functions dependencies with uv...');
375
+ await runCommand(uvCommand, (0, python_uv_1.buildUvPipInstallArgs)(pythonExecutable, 'requirements.txt'), functionsDir, 'python dependency installation', uvEnv, false);
376
+ const pythonEnv = buildPythonFunctionsEnv(uvEnv, functionsDir);
377
+ return bridgePythonCoreToolsForWindowsArm64(functionsDir, pythonEnv);
378
+ }
61
379
  /**
62
380
  * Check if Cosmos DB Emulator is running by checking if port 8081 is open
63
381
  */
64
382
  async function checkCosmosDBEmulator() {
65
383
  return new Promise((resolve) => {
66
- const net = require('net');
67
384
  const socket = new net.Socket();
68
385
  const timeout = setTimeout(() => {
69
386
  socket.destroy();
@@ -81,112 +398,106 @@ async function checkCosmosDBEmulator() {
81
398
  socket.connect(8081, 'localhost');
82
399
  });
83
400
  }
84
- exports.devCommand = new commander_1.Command()
85
- .name('dev')
86
- .description('Start SwallowKit development server (Cosmos DB + Next.js + Azure Functions)')
87
- .option('-p, --port <port>', 'Next.js port', '3000')
88
- .option('-f, --functions-port <port>', 'Azure Functions port', '7071')
89
- .option('--host <host>', 'Host name', 'localhost')
90
- .option('--open', 'Open browser automatically', false)
91
- .option('--verbose', 'Show verbose logs', false)
92
- .option('--no-functions', 'Skip Azure Functions startup', false)
93
- .action(async (options) => {
94
- // SwallowKit プロジェクトディレクトリかどうかを検証
95
- (0, config_1.ensureSwallowKitProject)("dev");
96
- console.log('🚀 Starting SwallowKit development environment...');
97
- if (options.verbose) {
98
- console.log('⚙️ Options:', options);
99
- }
100
- await startDevEnvironment(options);
101
- });
401
+ function buildDevCommand(runDevEnvironment = startDevEnvironment, verifyProject = config_1.ensureSwallowKitProject) {
402
+ return new commander_1.Command()
403
+ .name('dev')
404
+ .description('Start SwallowKit development server (Cosmos DB + Next.js + Azure Functions)')
405
+ .option('-p, --port <port>', 'Next.js port', '3000')
406
+ .option('-f, --functions-port <port>', 'Azure Functions port', '7071')
407
+ .option('--host <host>', 'Host name', 'localhost')
408
+ .option('--open', 'Open browser automatically', false)
409
+ .option('--verbose', 'Show verbose logs', false)
410
+ .option('--no-functions', 'Skip Azure Functions startup')
411
+ .option('--seed-env <environment>', 'Replace Cosmos DB Emulator data from dev-seeds/<environment> before startup')
412
+ .option('--mock-connectors', 'Start mock server for connector models (serves Zod-generated data)', false)
413
+ .option('--swa-port <port>', 'SWA authentication emulator port', '4280')
414
+ .option('--no-swa', 'Skip the SWA authentication emulator')
415
+ .action(async (options) => {
416
+ const normalizedOptions = normalizeParsedDevOptions(options);
417
+ // SwallowKit プロジェクトディレクトリかどうかを検証
418
+ verifyProject("dev");
419
+ console.log('🚀 Starting SwallowKit development environment...');
420
+ if (normalizedOptions.verbose) {
421
+ console.log('⚙️ Options:', normalizedOptions);
422
+ }
423
+ await runDevEnvironment(normalizedOptions);
424
+ });
425
+ }
426
+ exports.devCommand = buildDevCommand();
102
427
  async function initializeCosmosDB(databaseName) {
103
428
  try {
104
- // Read local.settings.json from functions directory
105
- const functionsDir = path.join(process.cwd(), 'functions');
106
- const localSettingsPath = path.join(functionsDir, 'local.settings.json');
107
- if (!fs.existsSync(localSettingsPath)) {
108
- console.log('⚠️ local.settings.json not found. Skipping Cosmos DB initialization.');
109
- return;
110
- }
111
- const localSettings = JSON.parse(fs.readFileSync(localSettingsPath, 'utf-8'));
112
- const connectionString = localSettings.Values?.CosmosDBConnection;
113
- const dbName = localSettings.Values?.COSMOS_DB_DATABASE_NAME || databaseName;
114
- if (!connectionString) {
115
- console.log('⚠️ CosmosDBConnection not found in local.settings.json. Skipping Cosmos DB initialization.');
116
- return;
429
+ const connectionInfoResult = (0, dev_seeds_1.resolveLocalCosmosConnectionInfo)(databaseName);
430
+ if (!connectionInfoResult.ok) {
431
+ if (connectionInfoResult.reason === 'missing-local-settings') {
432
+ console.log('⚠️ local.settings.json not found. Skipping Cosmos DB initialization.');
433
+ }
434
+ else if (connectionInfoResult.reason === 'missing-connection-string') {
435
+ console.log('⚠️ CosmosDBConnection not found in local.settings.json. Skipping Cosmos DB initialization.');
436
+ }
437
+ else {
438
+ console.log('⚠️ Invalid CosmosDB connection string format.');
439
+ }
440
+ return null;
117
441
  }
118
442
  console.log('🗄️ Initializing Cosmos DB...');
119
- // Parse connection string
120
- const endpointMatch = connectionString.match(/AccountEndpoint=([^;]+)/);
121
- const keyMatch = connectionString.match(/AccountKey=([^;]+)/);
122
- if (!endpointMatch || !keyMatch) {
123
- console.log('⚠️ Invalid CosmosDB connection string format.');
124
- return;
443
+ const { endpoint, key, databaseName: dbName, worktreeIsolationEnabled } = connectionInfoResult.value;
444
+ console.log(`📦 Cosmos DB database: ${dbName}`);
445
+ if (worktreeIsolationEnabled) {
446
+ console.log('🌳 Worktree isolation: enabled');
125
447
  }
126
- const endpoint = endpointMatch[1];
127
448
  const client = new cosmos_1.CosmosClient({
128
449
  endpoint: endpoint,
129
- key: keyMatch[1]
450
+ key,
130
451
  });
131
452
  // Create database if not exists
132
453
  const { database } = await client.databases.createIfNotExists({ id: dbName });
133
454
  console.log(`✅ Database "${dbName}" ready`);
134
- // Read lib/scaffold-config.ts to get list of models
135
- const scaffoldConfigPath = path.join(process.cwd(), 'lib', 'scaffold-config.ts');
136
- if (fs.existsSync(scaffoldConfigPath)) {
137
- const scaffoldConfigContent = fs.readFileSync(scaffoldConfigPath, 'utf-8');
138
- // Parse TypeScript file to extract models array
139
- const modelsMatch = scaffoldConfigContent.match(/models:\s*\[([\s\S]*?)\]\s*as\s*ScaffoldModel\[\]/);
140
- if (modelsMatch) {
141
- const modelsArrayContent = modelsMatch[1];
142
- // Extract model names from objects like { name: 'Task', path: '/task', label: 'Task' }
143
- const modelMatches = modelsArrayContent.matchAll(/\{\s*name:\s*['"](\w+)['"]/g);
144
- const models = Array.from(modelMatches, m => m[1]);
145
- for (const modelName of models) {
146
- const containerName = `${modelName}s`; // Pluralize model name
147
- // Try creating container with full partition key definition first
148
- let containerCreated = false;
149
- try {
150
- console.log(`🔧 Creating container "${containerName}" with partition key /id...`);
151
- const containerResponse = await database.containers.createIfNotExists({
152
- id: containerName,
153
- partitionKey: {
154
- paths: ['/id'],
155
- kind: cosmos_1.PartitionKeyKind.Hash,
156
- version: 2
157
- }
158
- });
159
- console.log(`✅ Container "${containerName}" ready (status: ${containerResponse.statusCode})`);
160
- containerCreated = true;
161
- }
162
- catch (error) {
163
- console.log(`⚠️ Failed with full partition key definition: ${error.message}`);
164
- console.log(`🔄 Retrying with simple partition key...`);
455
+ const models = await (0, dev_seeds_1.loadProjectModels)();
456
+ for (const model of models) {
457
+ const containerName = (0, dev_seeds_1.getContainerNameForModel)(model);
458
+ // Try creating container with full partition key definition first
459
+ let containerCreated = false;
460
+ try {
461
+ console.log(`🔧 Creating container "${containerName}" with partition key ${model.partitionKey}...`);
462
+ const containerResponse = await database.containers.createIfNotExists({
463
+ id: containerName,
464
+ partitionKey: {
465
+ paths: [model.partitionKey],
466
+ kind: cosmos_1.PartitionKeyKind.Hash,
467
+ version: 2
165
468
  }
166
- // If first attempt failed, try with simple partition key definition
167
- if (!containerCreated) {
168
- try {
169
- const containerResponse = await database.containers.createIfNotExists({
170
- id: containerName,
171
- partitionKey: {
172
- paths: ['/id']
173
- }
174
- });
175
- console.log(`✅ Container "${containerName}" ready (status: ${containerResponse.statusCode})`);
176
- }
177
- catch (containerError) {
178
- console.error(`❌ Failed to create container "${containerName}":`, containerError.message);
179
- console.error(`Error code: ${containerError.code}`);
180
- if (containerError.body) {
181
- console.error(`Response body:`, JSON.stringify(containerError.body, null, 2));
182
- }
183
- // Continue with other containers
469
+ });
470
+ console.log(`✅ Container "${containerName}" ready (status: ${containerResponse.statusCode})`);
471
+ containerCreated = true;
472
+ }
473
+ catch (error) {
474
+ const message = error instanceof Error ? error.message : String(error);
475
+ console.log(`⚠️ Failed with full partition key definition: ${message}`);
476
+ console.log(`🔄 Retrying with simple partition key...`);
477
+ }
478
+ // If first attempt failed, try with simple partition key definition
479
+ if (!containerCreated) {
480
+ try {
481
+ const containerResponse = await database.containers.createIfNotExists({
482
+ id: containerName,
483
+ partitionKey: {
484
+ paths: [model.partitionKey]
184
485
  }
486
+ });
487
+ console.log(`✅ Container "${containerName}" ready (status: ${containerResponse.statusCode})`);
488
+ }
489
+ catch (containerError) {
490
+ console.error(`❌ Failed to create container "${containerName}":`, containerError.message);
491
+ console.error(`Error code: ${containerError.code}`);
492
+ if (containerError.body) {
493
+ console.error(`Response body:`, JSON.stringify(containerError.body, null, 2));
185
494
  }
495
+ // Continue with other containers
186
496
  }
187
497
  }
188
498
  }
189
499
  console.log('✅ Cosmos DB initialization complete\n');
500
+ return { endpoint, key, databaseName: dbName, models };
190
501
  }
191
502
  catch (error) {
192
503
  console.error('⚠️ Cosmos DB initialization failed:', error.message);
@@ -194,19 +505,58 @@ async function initializeCosmosDB(databaseName) {
194
505
  console.error('Stack trace:', error.stack);
195
506
  }
196
507
  console.log('💡 Make sure Cosmos DB Emulator is running');
508
+ return null;
197
509
  }
198
510
  }
199
511
  async function startDevEnvironment(options) {
200
512
  const port = options.port || '3000';
201
513
  const functionsPort = options.functionsPort || '7071';
514
+ const swaPort = options.swaPort || '4280';
202
515
  // Detect package manager from project lockfile
203
516
  const pm = (0, package_manager_1.detectFromProject)();
204
517
  const pmCmd = (0, package_manager_1.getCommands)(pm);
518
+ const backendLanguage = (0, config_1.getBackendLanguage)();
519
+ const authConfig = (0, config_1.getAuthConfig)();
520
+ const normalizedAuth = (0, config_1.normalizeAuthConfig)(authConfig);
521
+ const useSwaEmulator = Boolean(normalizedAuth && Object.values(normalizedAuth.schemes).some(scheme => scheme.provider === 'swa')) && !options.noSwa;
522
+ const swaCommand = useSwaEmulator ? await resolveSwaCliCommand(process.cwd()) : null;
523
+ if (useSwaEmulator && !swaCommand) {
524
+ const installCommand = getSwaCliInstallCommand(pm);
525
+ console.error('❌ SWA authentication requires Azure Static Web Apps CLI for local development.');
526
+ console.error(' Install it in this project and retry:');
527
+ console.error(`\n ${installCommand}\n`);
528
+ process.exitCode = 1;
529
+ return;
530
+ }
205
531
  // プロセスを管理する配列
206
532
  const processes = [];
533
+ let functionsEnv = process.env;
534
+ let mockServer = null;
535
+ let envLocalPath = '';
536
+ let envLocalDefaultUrl = ''; // default Functions URL to restore on shutdown
537
+ const functionsBaseUrl = buildFunctionsBaseUrl(options.host, functionsPort);
538
+ let functionsReadinessPromise = null;
539
+ let functionsReady = !!options.noFunctions;
207
540
  // Cleanup processes on Ctrl+C
208
- process.on('SIGINT', () => {
541
+ process.on('SIGINT', async () => {
209
542
  console.log('\n🛑 Stopping development servers...');
543
+ // Restore .env.local to default Functions port on shutdown
544
+ if (envLocalPath && envLocalDefaultUrl) {
545
+ try {
546
+ if (fs.existsSync(envLocalPath)) {
547
+ const content = fs.readFileSync(envLocalPath, 'utf-8');
548
+ if (content.includes('BACKEND_FUNCTIONS_BASE_URL=') &&
549
+ !content.includes(`BACKEND_FUNCTIONS_BASE_URL=${envLocalDefaultUrl}`)) {
550
+ const restored = content.replace(/^BACKEND_FUNCTIONS_BASE_URL=.*/m, `BACKEND_FUNCTIONS_BASE_URL=${envLocalDefaultUrl}`);
551
+ fs.writeFileSync(envLocalPath, restored, 'utf-8');
552
+ }
553
+ }
554
+ }
555
+ catch { /* ignore */ }
556
+ }
557
+ if (mockServer) {
558
+ await mockServer.stop();
559
+ }
210
560
  processes.forEach((proc) => {
211
561
  if (proc && !proc.killed) {
212
562
  proc.kill();
@@ -230,12 +580,13 @@ async function startDevEnvironment(options) {
230
580
  }
231
581
  // 2. Check if Azure Functions exists
232
582
  const functionsDir = path.join(process.cwd(), 'functions');
233
- const hasFunctions = fs.existsSync(functionsDir) &&
234
- fs.existsSync(path.join(functionsDir, 'package.json'));
583
+ const hasFunctions = fs.existsSync(functionsDir) && hasFunctionsProject(functionsDir, backendLanguage);
584
+ let functionsCoreToolsCommand = null;
585
+ let installedCoreToolsVersion = null;
235
586
  if (hasFunctions && !options.noFunctions) {
236
- // Check if Azure Functions Core Tools is installed
237
- const coreToolsInstalled = await checkCoreTools();
238
- if (!coreToolsInstalled) {
587
+ installedCoreToolsVersion = await resolveInstalledCoreToolsVersion();
588
+ functionsCoreToolsCommand = buildFunctionsCoreToolsCommand(backendLanguage, installedCoreToolsVersion);
589
+ if (functionsCoreToolsCommand.command === 'func' && !installedCoreToolsVersion) {
239
590
  console.log('');
240
591
  console.log('⚠️ Azure Functions Core Tools not found.');
241
592
  console.log('');
@@ -282,6 +633,9 @@ async function startDevEnvironment(options) {
282
633
  options.noFunctions = true;
283
634
  }
284
635
  }
636
+ else if (functionsCoreToolsCommand.command !== 'func') {
637
+ console.log(`ℹ️ Using ${functionsCoreToolsCommand.label}.`);
638
+ }
285
639
  if (!options.noFunctions) {
286
640
  // Check if Cosmos DB Emulator is running
287
641
  const cosmosRunning = await checkCosmosDBEmulator();
@@ -297,69 +651,92 @@ async function startDevEnvironment(options) {
297
651
  process.exit(1);
298
652
  }
299
653
  // Initialize Cosmos DB before starting Functions
300
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
301
- const appName = packageJson.name || 'App';
302
- const databaseName = `${appName.charAt(0).toUpperCase() + appName.slice(1)}Database`;
303
- await initializeCosmosDB(databaseName);
654
+ const databaseName = (0, dev_seeds_1.getDefaultCosmosDatabaseName)();
655
+ const cosmosInitialization = await initializeCosmosDB(databaseName);
656
+ if (options.seedEnv && cosmosInitialization) {
657
+ await (0, dev_seeds_1.applyDevSeedEnvironment)({
658
+ client: new cosmos_1.CosmosClient({
659
+ endpoint: cosmosInitialization.endpoint,
660
+ key: cosmosInitialization.key,
661
+ }),
662
+ databaseName: cosmosInitialization.databaseName,
663
+ environment: options.seedEnv,
664
+ models: cosmosInitialization.models,
665
+ });
666
+ }
304
667
  console.log('');
305
668
  console.log('🚀 Starting Azure Functions...');
306
- // Check if pnpm install has been run in functions directory
307
- const functionsNodeModules = path.join(functionsDir, 'node_modules');
308
- if (!fs.existsSync(functionsNodeModules)) {
309
- console.log('📦 Installing Azure Functions dependencies...');
310
- const depInstall = (0, child_process_1.spawn)(pm, ['install'], {
311
- cwd: functionsDir,
669
+ if (backendLanguage === 'typescript') {
670
+ const functionsNodeModules = path.join(functionsDir, 'node_modules');
671
+ if (!fs.existsSync(functionsNodeModules)) {
672
+ console.log('📦 Installing Azure Functions dependencies...');
673
+ await runCommand(pm, ['install'], functionsDir, `${pm} install`);
674
+ }
675
+ }
676
+ else if (backendLanguage === 'csharp') {
677
+ console.log('ℹ️ C# Azure Functions can take longer on cold start while the worker builds.');
678
+ }
679
+ else {
680
+ functionsEnv = await preparePythonFunctionsEnvironment(functionsDir);
681
+ }
682
+ if (cosmosInitialization) {
683
+ functionsEnv = buildFunctionsCosmosEnvironment(functionsEnv, cosmosInitialization.databaseName);
684
+ }
685
+ }
686
+ }
687
+ if (hasFunctions && !options.noFunctions) {
688
+ // Build shared package before starting Functions
689
+ const sharedDir = path.join(process.cwd(), 'shared');
690
+ const sharedPkgPath = path.join(sharedDir, 'package.json');
691
+ if (fs.existsSync(sharedDir) && fs.existsSync(sharedPkgPath)) {
692
+ const sharedPkg = JSON.parse(fs.readFileSync(sharedPkgPath, 'utf-8'));
693
+ if (sharedPkg.scripts?.build) {
694
+ console.log('📦 Building shared package...');
695
+ const filterArgs = pm === 'pnpm'
696
+ ? ['run', '--filter', 'shared', 'build']
697
+ : ['run', '--workspace=shared', 'build'];
698
+ const sharedBuild = (0, child_process_1.spawn)(pm, filterArgs, {
699
+ cwd: process.cwd(),
312
700
  shell: true,
313
701
  stdio: 'inherit',
314
702
  });
315
703
  await new Promise((resolve, reject) => {
316
- depInstall.on('close', (code) => {
704
+ sharedBuild.on('close', (code) => {
317
705
  if (code === 0) {
706
+ console.log('✅ Shared package built successfully');
318
707
  resolve();
319
708
  }
320
709
  else {
321
- reject(new Error(`${pm} install failed with code ${code}`));
710
+ reject(new Error(`Shared package build failed with code ${code}`));
322
711
  }
323
712
  });
324
- depInstall.on('error', reject);
713
+ sharedBuild.on('error', reject);
325
714
  });
326
715
  }
716
+ else {
717
+ console.log('⚠️ Shared package has no build script — skipping build. Run "swallowkit add-auth" to fix.');
718
+ }
327
719
  }
328
- }
329
- if (hasFunctions && !options.noFunctions) {
330
- // Build shared package before starting Functions
331
- const sharedDir = path.join(process.cwd(), 'shared');
332
- if (fs.existsSync(sharedDir) && fs.existsSync(path.join(sharedDir, 'package.json'))) {
333
- console.log('📦 Building shared package...');
334
- const filterArgs = pm === 'pnpm'
335
- ? ['run', '--filter', 'shared', 'build']
336
- : ['run', '--workspace=shared', 'build'];
337
- const sharedBuild = (0, child_process_1.spawn)(pm, filterArgs, {
338
- cwd: process.cwd(),
339
- shell: true,
340
- stdio: 'inherit',
341
- });
342
- await new Promise((resolve, reject) => {
343
- sharedBuild.on('close', (code) => {
344
- if (code === 0) {
345
- console.log('✅ Shared package built successfully');
346
- resolve();
347
- }
348
- else {
349
- reject(new Error(`Shared package build failed with code ${code}`));
350
- }
351
- });
352
- sharedBuild.on('error', reject);
353
- });
720
+ // Build TypeScript functions after shared package (functions import from shared)
721
+ if (backendLanguage === 'typescript') {
722
+ const functionsPkgPath = path.join(functionsDir, 'package.json');
723
+ if (fs.existsSync(functionsPkgPath)) {
724
+ const functionsPkg = JSON.parse(fs.readFileSync(functionsPkgPath, 'utf-8'));
725
+ if (functionsPkg.scripts?.build) {
726
+ console.log('📦 Building TypeScript Azure Functions...');
727
+ await runCommand(pm, ['run', 'build'], functionsDir, `${pm} run build`);
728
+ }
729
+ }
354
730
  }
355
731
  // Azure Functions を起動
356
- const funcEnv = { ...process.env, FUNCTIONS_PORT: functionsPort };
357
- const funcProcess = (0, child_process_1.spawn)(pm, ['start'], {
732
+ const functionsCommand = functionsCoreToolsCommand ?? buildFunctionsCoreToolsCommand(backendLanguage, installedCoreToolsVersion);
733
+ const funcProcess = (0, child_process_1.spawn)(functionsCommand.command, [...functionsCommand.argsPrefix, ...buildFunctionsStartArgs(functionsPort)], {
358
734
  cwd: functionsDir,
359
735
  shell: true,
360
736
  stdio: 'pipe', // Always pipe to capture output
361
- env: funcEnv
737
+ env: functionsEnv
362
738
  });
739
+ let pythonWorkerMissing = false;
363
740
  // Functions の出力をそのまま表示(プレフィックス付き)
364
741
  if (funcProcess.stdout) {
365
742
  funcProcess.stdout.on('data', (data) => {
@@ -367,6 +744,10 @@ async function startDevEnvironment(options) {
367
744
  // 各行にプレフィックスを付けて出力
368
745
  const lines = output.split('\n').filter((line) => line.trim());
369
746
  lines.forEach((line) => {
747
+ if (backendLanguage === 'python' &&
748
+ (line.includes('WorkerConfig for runtime: python not found') || line.includes('DefaultWorkerPath:'))) {
749
+ pythonWorkerMissing = true;
750
+ }
370
751
  console.log(`[Functions] ${line}`);
371
752
  });
372
753
  });
@@ -376,6 +757,10 @@ async function startDevEnvironment(options) {
376
757
  const output = data.toString();
377
758
  const lines = output.split('\n').filter((line) => line.trim());
378
759
  lines.forEach((line) => {
760
+ if (backendLanguage === 'python' &&
761
+ (line.includes('WorkerConfig for runtime: python not found') || line.includes('DefaultWorkerPath:'))) {
762
+ pythonWorkerMissing = true;
763
+ }
379
764
  console.error(`[Functions Error] ${line}`);
380
765
  });
381
766
  });
@@ -389,9 +774,15 @@ async function startDevEnvironment(options) {
389
774
  funcProcess.on('close', (code) => {
390
775
  if (code !== 0) {
391
776
  console.log(`\n⏹️ Azure Functions exited (exit code: ${code})`);
777
+ if (backendLanguage === 'python' && pythonWorkerMissing) {
778
+ console.log('💡 Your Azure Functions Core Tools installation is missing the Python worker for this OS/architecture.');
779
+ console.log(' Reinstall a matching Core Tools v4 package (Windows users should prefer the official x64/x86 MSI for their machine).');
780
+ console.log(' SwallowKit local Python dev uses functions/.venv and requirements.txt, but Core Tools still needs its own bundled Python worker.');
781
+ }
392
782
  }
393
783
  });
394
- console.log(`✅ Azure Functions started (port: ${functionsPort})`);
784
+ console.log(`⏳ Waiting for Azure Functions to accept requests at ${functionsBaseUrl}...`);
785
+ functionsReadinessPromise = waitForHttpServerReady(functionsBaseUrl, getFunctionsReadinessTimeoutMs(backendLanguage));
395
786
  }
396
787
  else if (!hasFunctions) {
397
788
  console.log('');
@@ -403,24 +794,97 @@ async function startDevEnvironment(options) {
403
794
  }
404
795
  console.log('');
405
796
  console.log('🚀 Starting Next.js development server...');
797
+ // Mock connector server — start proxy if --mock-connectors is enabled
798
+ let bffTargetPort = functionsPort;
799
+ if (options.mockConnectors) {
800
+ const allModels = await (0, dev_seeds_1.loadProjectModels)();
801
+ const connectorModels = allModels.filter((m) => m.connectorConfig);
802
+ // Resolve auth config — auth functions use RDB connectors, mocked alongside other models
803
+ let mockAuthConfig;
804
+ if (authConfig?.provider === 'custom-jwt' && authConfig.customJwt) {
805
+ // Read JWT_SECRET from functions/local.settings.json if available
806
+ let jwtSecret = 'dev-jwt-secret-change-in-production-min-32-chars!!';
807
+ try {
808
+ const localSettingsPath = path.join(process.cwd(), 'functions', 'local.settings.json');
809
+ if (fs.existsSync(localSettingsPath)) {
810
+ const localSettings = JSON.parse(fs.readFileSync(localSettingsPath, 'utf-8'));
811
+ jwtSecret = localSettings.Values?.[authConfig.customJwt.jwtSecretEnv || 'JWT_SECRET'] || jwtSecret;
812
+ }
813
+ }
814
+ catch { /* ignore */ }
815
+ mockAuthConfig = {
816
+ jwtSecret,
817
+ tokenExpiry: authConfig.customJwt.tokenExpiry,
818
+ customJwt: {
819
+ userTable: authConfig.customJwt.userTable,
820
+ loginIdColumn: authConfig.customJwt.loginIdColumn,
821
+ passwordHashColumn: authConfig.customJwt.passwordHashColumn,
822
+ rolesColumn: authConfig.customJwt.rolesColumn,
823
+ },
824
+ defaultPolicy: authConfig.authorization?.defaultPolicy === "public" ? "anonymous" :
825
+ authConfig.authorization?.defaultPolicy === "authenticated" || authConfig.authorization?.defaultPolicy === "anonymous"
826
+ ? authConfig.authorization.defaultPolicy : undefined,
827
+ };
828
+ }
829
+ if (connectorModels.length > 0 || mockAuthConfig) {
830
+ const mockPort = parseInt(functionsPort, 10) + 1;
831
+ mockServer = new connector_mock_server_1.ConnectorMockServer({
832
+ port: mockPort,
833
+ functionsTarget: `${options.host || 'localhost'}:${functionsPort}`,
834
+ connectorModels,
835
+ allModels,
836
+ seedEnv: options.seedEnv,
837
+ host: options.host || 'localhost',
838
+ authConfig: mockAuthConfig,
839
+ });
840
+ await mockServer.start();
841
+ bffTargetPort = String(mockPort);
842
+ const modelCount = connectorModels.length + (mockAuthConfig ? 1 : 0);
843
+ console.log('');
844
+ console.log(`🔌 Mock server started (port: ${mockPort}) — ${modelCount} model(s) mocked via Zod/seed data`);
845
+ for (const m of connectorModels) {
846
+ const ops = m.connectorConfig.operations.join(', ');
847
+ console.log(` - ${m.name} [${ops}]`);
848
+ }
849
+ if (mockAuthConfig) {
850
+ console.log(` - auth [login, me, logout]`);
851
+ }
852
+ console.log(` Other routes → proxied to Azure Functions (port: ${functionsPort})`);
853
+ }
854
+ else {
855
+ console.log('');
856
+ console.log('ℹ️ --mock-connectors specified but no connector models found. Skipping mock server.');
857
+ }
858
+ }
406
859
  // 5. Start Next.js development server
407
- const nextArgs = pm === 'pnpm'
408
- ? ['exec', 'next', 'dev', '--port', port]
409
- : ['next', 'dev', '--port', port];
410
- if (options.open) {
411
- // Next.js 14+ deprecated --open option, so we open browser manually
412
- setTimeout(() => {
413
- const url = `http://${options.host || 'localhost'}:${port}`;
414
- console.log(`\n🌐 Opening browser: ${url}`);
415
- const start = process.platform === 'darwin' ? 'open' :
416
- process.platform === 'win32' ? 'start' : 'xdg-open';
417
- (0, child_process_1.spawn)(start, [url], { shell: true });
418
- }, 3000);
860
+ const nextArgs = buildNextDevArgs(pm, port);
861
+ // Ensure .env.local points to bffTargetPort so Next.js reads the correct backend URL.
862
+ // When --mock-connectors is active, bffTargetPort = mock port (7072); otherwise = Functions port (7071).
863
+ // Next.js may load .env.local values that override spawn env vars, so we must keep them in sync.
864
+ envLocalPath = path.join(process.cwd(), '.env.local');
865
+ envLocalDefaultUrl = functionsBaseUrl;
866
+ const bffTargetUrl = buildFunctionsBaseUrl(options.host, bffTargetPort);
867
+ try {
868
+ if (fs.existsSync(envLocalPath)) {
869
+ const envContent = fs.readFileSync(envLocalPath, 'utf-8');
870
+ if (envContent.includes('BACKEND_FUNCTIONS_BASE_URL=') &&
871
+ !envContent.includes(`BACKEND_FUNCTIONS_BASE_URL=${bffTargetUrl}`)) {
872
+ const updated = envContent.replace(/^BACKEND_FUNCTIONS_BASE_URL=.*/m, `BACKEND_FUNCTIONS_BASE_URL=${bffTargetUrl}`);
873
+ fs.writeFileSync(envLocalPath, updated, 'utf-8');
874
+ }
875
+ }
419
876
  }
877
+ catch { /* ignore */ }
878
+ const nextEnv = {
879
+ ...process.env,
880
+ BACKEND_FUNCTIONS_BASE_URL: bffTargetUrl,
881
+ FUNCTIONS_BASE_URL: bffTargetUrl,
882
+ };
420
883
  const nextProcess = (0, child_process_1.spawn)(pm === 'pnpm' ? 'pnpm' : 'npx', nextArgs, {
421
884
  cwd: process.cwd(),
422
885
  shell: true,
423
886
  stdio: options.verbose ? 'inherit' : 'inherit',
887
+ env: nextEnv,
424
888
  });
425
889
  processes.push(nextProcess);
426
890
  nextProcess.on('error', (error) => {
@@ -432,6 +896,9 @@ async function startDevEnvironment(options) {
432
896
  console.log(`\n⏹️ Next.js exited (exit code: ${code})`);
433
897
  }
434
898
  // Exit all processes when Next.js exits
899
+ if (mockServer) {
900
+ mockServer.stop().catch(() => { });
901
+ }
435
902
  processes.forEach((proc) => {
436
903
  if (proc && !proc.killed) {
437
904
  proc.kill();
@@ -439,18 +906,90 @@ async function startDevEnvironment(options) {
439
906
  });
440
907
  process.exit(code || 0);
441
908
  });
909
+ if (functionsReadinessPromise) {
910
+ functionsReady = await functionsReadinessPromise;
911
+ console.log('');
912
+ if (functionsReady) {
913
+ console.log(`✅ Azure Functions ready (port: ${functionsPort})`);
914
+ }
915
+ else {
916
+ console.log(`⚠️ Azure Functions is still starting: ${functionsBaseUrl}`);
917
+ }
918
+ }
919
+ let swaReady = false;
920
+ if (useSwaEmulator && swaCommand) {
921
+ const nextUrl = `http://${options.host || 'localhost'}:${port}`;
922
+ console.log(`⏳ Waiting for Next.js before starting the SWA emulator at ${nextUrl}...`);
923
+ const nextReady = await waitForHttpServerReady(nextUrl);
924
+ if (!nextReady) {
925
+ throw new Error(`Next.js did not become ready at ${nextUrl}`);
926
+ }
927
+ console.log(`🔐 Starting SWA authentication emulator (port: ${swaPort})...`);
928
+ const swaProcess = (0, child_process_1.spawn)(swaCommand, buildSwaStartArgs(options.host, port, swaPort), {
929
+ cwd: process.cwd(),
930
+ shell: true,
931
+ stdio: 'inherit',
932
+ env: process.env,
933
+ });
934
+ processes.push(swaProcess);
935
+ swaProcess.on('error', (error) => {
936
+ console.error('❌ SWA CLI startup error:', error.message);
937
+ });
938
+ const swaUrl = `http://${options.host || 'localhost'}:${swaPort}`;
939
+ swaReady = await waitForHttpServerReady(swaUrl);
940
+ if (!swaReady) {
941
+ throw new Error(`SWA authentication emulator did not become ready at ${swaUrl}`);
942
+ }
943
+ }
442
944
  console.log('');
443
945
  console.log('✅ SwallowKit development environment is running!');
444
946
  console.log('');
445
- console.log(`📱 Next.js: http://${options.host || 'localhost'}:${port}`);
947
+ if (useSwaEmulator) {
948
+ const swaUrl = `http://${options.host || 'localhost'}:${swaPort}`;
949
+ console.log('🔐 SWA authenticated app:');
950
+ console.log(` ${swaUrl}`);
951
+ console.log('');
952
+ console.log('🔑 Local sign-in:');
953
+ console.log(` ${swaUrl}/.auth/login/aad`);
954
+ console.log(' Enter any username and select Login.');
955
+ console.log(' The "authenticated" role is added automatically.');
956
+ console.log('');
957
+ console.log('📱 Next.js direct access (authentication is not emulated):');
958
+ console.log(` http://${options.host || 'localhost'}:${port}`);
959
+ }
960
+ else {
961
+ console.log(`📱 Next.js: http://${options.host || 'localhost'}:${port}`);
962
+ }
446
963
  if (hasFunctions && !options.noFunctions) {
447
- console.log(`⚡ Azure Functions: http://${options.host || 'localhost'}:${functionsPort}`);
964
+ console.log(`${functionsReady ? '⚡ Azure Functions' : '⏳ Azure Functions (starting)'}: ${functionsBaseUrl}`);
965
+ }
966
+ if (mockServer) {
967
+ console.log(`🔌 Mock Proxy: ${bffTargetUrl} (BFF → here)`);
448
968
  }
449
969
  console.log('');
450
- if (hasFunctions && !options.noFunctions) {
970
+ if (hasFunctions && !options.noFunctions && functionsReady) {
451
971
  console.log('💡 Azure Functions and Next.js BFF are connected');
452
972
  }
973
+ else if (hasFunctions && !options.noFunctions) {
974
+ console.log('💡 Azure Functions is still warming up; BFF routes can fail until the backend responds.');
975
+ }
976
+ if (mockServer) {
977
+ console.log('💡 Connector models served from mock server (Zod-generated data)');
978
+ }
979
+ if (swaReady) {
980
+ console.log('💡 Open the app through the SWA port when testing authenticated pages and CRUD APIs.');
981
+ }
453
982
  console.log('');
983
+ if (options.open) {
984
+ const url = useSwaEmulator
985
+ ? `http://${options.host || 'localhost'}:${swaPort}`
986
+ : `http://${options.host || 'localhost'}:${port}`;
987
+ console.log(`🌐 Opening browser: ${url}`);
988
+ const start = process.platform === 'darwin' ? 'open' :
989
+ process.platform === 'win32' ? 'start' : 'xdg-open';
990
+ (0, child_process_1.spawn)(start, [url], { shell: true });
991
+ console.log('');
992
+ }
454
993
  console.log('🛑 Press Ctrl+C to stop');
455
994
  console.log('');
456
995
  }
@@ -464,4 +1003,43 @@ async function startDevEnvironment(options) {
464
1003
  process.exit(1);
465
1004
  }
466
1005
  }
1006
+ async function resolveSwaCliCommand(projectRoot) {
1007
+ const binaryName = process.platform === 'win32' ? 'swa.cmd' : 'swa';
1008
+ const localCommand = path.join(projectRoot, 'node_modules', '.bin', binaryName);
1009
+ if (fs.existsSync(localCommand) && await checkCommand(localCommand, ['--version'], {
1010
+ cwd: projectRoot,
1011
+ shell: process.platform === 'win32',
1012
+ })) {
1013
+ return localCommand;
1014
+ }
1015
+ if (await checkCommand('swa', ['--version'], { cwd: projectRoot })) {
1016
+ return 'swa';
1017
+ }
1018
+ return null;
1019
+ }
1020
+ async function runCommand(command, args, cwd, label, env, shell = true) {
1021
+ await new Promise((resolve, reject) => {
1022
+ const child = (0, child_process_1.spawn)(command, args, {
1023
+ cwd,
1024
+ shell,
1025
+ stdio: 'inherit',
1026
+ env,
1027
+ });
1028
+ child.on('close', (code) => {
1029
+ if (code === 0) {
1030
+ resolve();
1031
+ }
1032
+ else {
1033
+ reject(new Error(`${label} failed with code ${code}`));
1034
+ }
1035
+ });
1036
+ child.on('error', reject);
1037
+ });
1038
+ }
1039
+ function hasFunctionsProject(functionsDir, backendLanguage) {
1040
+ if (backendLanguage === 'typescript') {
1041
+ return fs.existsSync(path.join(functionsDir, 'package.json'));
1042
+ }
1043
+ return fs.existsSync(path.join(functionsDir, 'host.json'));
1044
+ }
467
1045
  //# sourceMappingURL=dev.js.map