swallowkit 0.4.0-beta.3 → 1.0.0-beta.10

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 (111) hide show
  1. package/LICENSE +21 -21
  2. package/README.ja.md +254 -183
  3. package/README.md +311 -184
  4. package/dist/__tests__/fixtures.d.ts +14 -0
  5. package/dist/__tests__/fixtures.d.ts.map +1 -0
  6. package/dist/__tests__/fixtures.js +85 -0
  7. package/dist/__tests__/fixtures.js.map +1 -0
  8. package/dist/cli/commands/create-model.d.ts.map +1 -1
  9. package/dist/cli/commands/create-model.js +16 -15
  10. package/dist/cli/commands/create-model.js.map +1 -1
  11. package/dist/cli/commands/dev-seeds.d.ts +35 -0
  12. package/dist/cli/commands/dev-seeds.d.ts.map +1 -0
  13. package/dist/cli/commands/dev-seeds.js +292 -0
  14. package/dist/cli/commands/dev-seeds.js.map +1 -0
  15. package/dist/cli/commands/dev.d.ts +8 -0
  16. package/dist/cli/commands/dev.d.ts.map +1 -1
  17. package/dist/cli/commands/dev.js +308 -87
  18. package/dist/cli/commands/dev.js.map +1 -1
  19. package/dist/cli/commands/index.d.ts +1 -0
  20. package/dist/cli/commands/index.d.ts.map +1 -1
  21. package/dist/cli/commands/index.js +3 -1
  22. package/dist/cli/commands/index.js.map +1 -1
  23. package/dist/cli/commands/init.d.ts +13 -0
  24. package/dist/cli/commands/init.d.ts.map +1 -1
  25. package/dist/cli/commands/init.js +2639 -1708
  26. package/dist/cli/commands/init.js.map +1 -1
  27. package/dist/cli/commands/scaffold.d.ts +3 -0
  28. package/dist/cli/commands/scaffold.d.ts.map +1 -1
  29. package/dist/cli/commands/scaffold.js +283 -118
  30. package/dist/cli/commands/scaffold.js.map +1 -1
  31. package/dist/cli/index.js +17 -1
  32. package/dist/cli/index.js.map +1 -1
  33. package/dist/core/config.d.ts +2 -1
  34. package/dist/core/config.d.ts.map +1 -1
  35. package/dist/core/config.js +31 -1
  36. package/dist/core/config.js.map +1 -1
  37. package/dist/core/scaffold/functions-generator.d.ts +5 -0
  38. package/dist/core/scaffold/functions-generator.d.ts.map +1 -1
  39. package/dist/core/scaffold/functions-generator.js +649 -211
  40. package/dist/core/scaffold/functions-generator.js.map +1 -1
  41. package/dist/core/scaffold/model-parser.d.ts +1 -1
  42. package/dist/core/scaffold/model-parser.d.ts.map +1 -1
  43. package/dist/core/scaffold/model-parser.js +105 -101
  44. package/dist/core/scaffold/model-parser.js.map +1 -1
  45. package/dist/core/scaffold/nextjs-generator.js +181 -181
  46. package/dist/core/scaffold/openapi-generator.d.ts +3 -0
  47. package/dist/core/scaffold/openapi-generator.d.ts.map +1 -0
  48. package/dist/core/scaffold/openapi-generator.js +190 -0
  49. package/dist/core/scaffold/openapi-generator.js.map +1 -0
  50. package/dist/core/scaffold/ui-generator.js +656 -656
  51. package/dist/database/base-model.d.ts +3 -3
  52. package/dist/database/base-model.js +3 -3
  53. package/dist/index.d.ts +2 -2
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +2 -1
  56. package/dist/index.js.map +1 -1
  57. package/dist/types/index.d.ts +4 -0
  58. package/dist/types/index.d.ts.map +1 -1
  59. package/dist/utils/package-manager.d.ts +109 -0
  60. package/dist/utils/package-manager.d.ts.map +1 -0
  61. package/dist/utils/package-manager.js +215 -0
  62. package/dist/utils/package-manager.js.map +1 -0
  63. package/package.json +81 -73
  64. package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +445 -0
  65. package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +194 -0
  66. package/src/__tests__/__snapshots__/ui-generator.test.ts.snap +524 -0
  67. package/src/__tests__/config.test.ts +122 -0
  68. package/src/__tests__/dev-seeds.test.ts +112 -0
  69. package/src/__tests__/dev.test.ts +42 -0
  70. package/src/__tests__/fixtures.ts +83 -0
  71. package/src/__tests__/functions-generator.test.ts +101 -0
  72. package/src/__tests__/init.test.ts +80 -0
  73. package/src/__tests__/nextjs-generator.test.ts +97 -0
  74. package/src/__tests__/openapi-generator.test.ts +43 -0
  75. package/src/__tests__/package-manager.test.ts +189 -0
  76. package/src/__tests__/scaffold.test.ts +39 -0
  77. package/src/__tests__/string-utils.test.ts +75 -0
  78. package/src/__tests__/ui-generator.test.ts +144 -0
  79. package/src/cli/commands/create-model.ts +141 -0
  80. package/src/cli/commands/dev-seeds.ts +358 -0
  81. package/src/cli/commands/dev.ts +805 -0
  82. package/src/cli/commands/index.ts +9 -0
  83. package/src/cli/commands/init.ts +3370 -0
  84. package/src/cli/commands/provision.ts +193 -0
  85. package/src/cli/commands/scaffold.ts +786 -0
  86. package/src/cli/index.ts +74 -0
  87. package/src/core/config.ts +244 -0
  88. package/src/core/scaffold/functions-generator.ts +674 -0
  89. package/src/core/scaffold/model-parser.ts +627 -0
  90. package/src/core/scaffold/nextjs-generator.ts +217 -0
  91. package/src/core/scaffold/openapi-generator.ts +212 -0
  92. package/src/core/scaffold/ui-generator.ts +945 -0
  93. package/src/database/base-model.ts +184 -0
  94. package/src/database/client.ts +140 -0
  95. package/src/database/repository.ts +104 -0
  96. package/src/database/runtime-check.ts +25 -0
  97. package/src/index.ts +27 -0
  98. package/src/types/index.ts +45 -0
  99. package/src/utils/package-manager.ts +229 -0
  100. package/dist/cli/commands/build.d.ts +0 -6
  101. package/dist/cli/commands/build.d.ts.map +0 -1
  102. package/dist/cli/commands/build.js +0 -177
  103. package/dist/cli/commands/build.js.map +0 -1
  104. package/dist/cli/commands/deploy.d.ts +0 -3
  105. package/dist/cli/commands/deploy.d.ts.map +0 -1
  106. package/dist/cli/commands/deploy.js +0 -147
  107. package/dist/cli/commands/deploy.js.map +0 -1
  108. package/dist/cli/commands/setup.d.ts +0 -6
  109. package/dist/cli/commands/setup.d.ts.map +0 -1
  110. package/dist/cli/commands/setup.js +0 -254
  111. package/dist/cli/commands/setup.js.map +0 -1
@@ -34,19 +34,68 @@ 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.buildNextDevArgs = buildNextDevArgs;
39
+ exports.getPythonVirtualEnvPaths = getPythonVirtualEnvPaths;
40
+ exports.buildPythonFunctionsEnv = buildPythonFunctionsEnv;
37
41
  const commander_1 = require("commander");
38
42
  const child_process_1 = require("child_process");
39
43
  const path = __importStar(require("path"));
40
44
  const fs = __importStar(require("fs"));
45
+ const os = __importStar(require("os"));
41
46
  const cosmos_1 = require("@azure/cosmos");
42
47
  const config_1 = require("../../core/config");
48
+ const dev_seeds_1 = require("./dev-seeds");
49
+ const package_manager_1 = require("../../utils/package-manager");
50
+ function buildFunctionsStartArgs(functionsPort) {
51
+ return ['start', '--port', functionsPort];
52
+ }
53
+ function buildNextDevArgs(pm, port) {
54
+ const baseArgs = ['next', 'dev', '--port', port, '--webpack'];
55
+ return pm === 'pnpm' ? ['exec', ...baseArgs] : baseArgs;
56
+ }
57
+ function getPythonVirtualEnvPaths(functionsDir) {
58
+ const venvDir = path.join(functionsDir, '.venv');
59
+ const binDir = process.platform === 'win32'
60
+ ? path.join(venvDir, 'Scripts')
61
+ : path.join(venvDir, 'bin');
62
+ const pythonExecutable = process.platform === 'win32'
63
+ ? path.join(binDir, 'python.exe')
64
+ : path.join(binDir, 'python');
65
+ return { venvDir, binDir, pythonExecutable };
66
+ }
67
+ function buildPythonFunctionsEnv(baseEnv, functionsDir) {
68
+ const { venvDir, binDir, pythonExecutable } = getPythonVirtualEnvPaths(functionsDir);
69
+ const pathKey = getPathEnvKey(baseEnv);
70
+ const currentPath = baseEnv[pathKey] || '';
71
+ return {
72
+ ...baseEnv,
73
+ [pathKey]: currentPath ? `${binDir}${path.delimiter}${currentPath}` : binDir,
74
+ VIRTUAL_ENV: venvDir,
75
+ languageWorkers__python__defaultExecutablePath: pythonExecutable,
76
+ };
77
+ }
78
+ function getPathEnvKey(env) {
79
+ return Object.keys(env).find((key) => key.toUpperCase() === 'PATH') || 'PATH';
80
+ }
81
+ function prependToPathEnv(env, entry) {
82
+ const pathKey = getPathEnvKey(env);
83
+ const currentPath = env[pathKey] || '';
84
+ return {
85
+ ...env,
86
+ [pathKey]: currentPath ? `${entry}${path.delimiter}${currentPath}` : entry,
87
+ };
88
+ }
43
89
  /**
44
90
  * Check if Azure Functions Core Tools is installed
45
91
  */
46
92
  async function checkCoreTools() {
93
+ return checkCommand('func', ['--version']);
94
+ }
95
+ async function checkCommand(command, args = ['--version']) {
47
96
  return new Promise((resolve) => {
48
- const checkProcess = (0, child_process_1.spawn)('func', ['--version'], {
49
- shell: true,
97
+ const checkProcess = (0, child_process_1.spawn)(command, args, {
98
+ shell: false,
50
99
  stdio: 'pipe',
51
100
  });
52
101
  checkProcess.on('close', (code) => {
@@ -57,6 +106,131 @@ async function checkCoreTools() {
57
106
  });
58
107
  });
59
108
  }
109
+ async function resolvePythonBootstrapCommand() {
110
+ const candidates = process.platform === 'win32'
111
+ ? [
112
+ { command: 'py', argsPrefix: ['-3.11'], label: 'py -3.11' },
113
+ { command: 'python', argsPrefix: [], label: 'python' },
114
+ ]
115
+ : [
116
+ { command: 'python3', argsPrefix: [], label: 'python3' },
117
+ { command: 'python', argsPrefix: [], label: 'python' },
118
+ ];
119
+ for (const candidate of candidates) {
120
+ if (await checkCommand(candidate.command, [...candidate.argsPrefix, '--version'])) {
121
+ return candidate;
122
+ }
123
+ }
124
+ throw new Error('Python 3.11 was not found. Install Python 3.11 and make sure `python`, `python3`, or `py -3.11` is available.');
125
+ }
126
+ async function getCommandPath(command) {
127
+ const locator = process.platform === 'win32' ? 'where' : 'which';
128
+ const result = await captureCommandOutput(locator, [command]);
129
+ const firstLine = result
130
+ .split(/\r?\n/)
131
+ .map((line) => line.trim())
132
+ .find(Boolean);
133
+ return firstLine || null;
134
+ }
135
+ async function captureCommandOutput(command, args, cwd, env) {
136
+ return new Promise((resolve, reject) => {
137
+ const child = (0, child_process_1.spawn)(command, args, {
138
+ cwd,
139
+ env,
140
+ shell: false,
141
+ stdio: ['ignore', 'pipe', 'pipe'],
142
+ });
143
+ let stdout = '';
144
+ let stderr = '';
145
+ child.stdout?.on('data', (data) => {
146
+ stdout += data.toString();
147
+ });
148
+ child.stderr?.on('data', (data) => {
149
+ stderr += data.toString();
150
+ });
151
+ child.on('close', (code) => {
152
+ if (code === 0) {
153
+ resolve(stdout);
154
+ }
155
+ else {
156
+ reject(new Error(stderr || stdout || `${command} exited with code ${code}`));
157
+ }
158
+ });
159
+ child.on('error', reject);
160
+ });
161
+ }
162
+ async function resolvePythonRuntimeDetails(functionsDir, env) {
163
+ const { pythonExecutable } = getPythonVirtualEnvPaths(functionsDir);
164
+ const output = await captureCommandOutput(pythonExecutable, [
165
+ '-c',
166
+ 'import platform; import sys; print(str(sys.version_info.major) + "." + str(sys.version_info.minor)); print(platform.machine())',
167
+ ], functionsDir, env);
168
+ const [version, architecture] = output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
169
+ if (!version || !architecture) {
170
+ throw new Error('Failed to determine Python runtime details.');
171
+ }
172
+ return { version, architecture };
173
+ }
174
+ async function bridgePythonCoreToolsForWindowsArm64(functionsDir, env) {
175
+ if (process.platform !== 'win32' || process.arch !== 'arm64') {
176
+ return env;
177
+ }
178
+ const funcPath = await getCommandPath('func');
179
+ if (!funcPath) {
180
+ return env;
181
+ }
182
+ const { version, architecture } = await resolvePythonRuntimeDetails(functionsDir, env);
183
+ if (architecture.toUpperCase() !== 'AMD64') {
184
+ return env;
185
+ }
186
+ const coreToolsRoot = path.dirname(funcPath);
187
+ const armWorkerDir = path.join(coreToolsRoot, 'workers', 'python', version, 'WINDOWS', 'Arm64');
188
+ if (fs.existsSync(armWorkerDir)) {
189
+ return env;
190
+ }
191
+ const x64WorkerDir = path.join(coreToolsRoot, 'workers', 'python', version, 'WINDOWS', 'X64');
192
+ if (!fs.existsSync(x64WorkerDir)) {
193
+ return env;
194
+ }
195
+ const patchedRoot = path.join(process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'), 'SwallowKit', 'azure-functions-core-tools-python-bridge');
196
+ if (!fs.existsSync(path.join(patchedRoot, 'func.exe'))) {
197
+ console.log('🩹 Creating a local Azure Functions Core Tools bridge for Windows Arm64 Python...');
198
+ fs.mkdirSync(path.dirname(patchedRoot), { recursive: true });
199
+ fs.cpSync(coreToolsRoot, patchedRoot, { recursive: true });
200
+ }
201
+ const patchedArmWorkerDir = path.join(patchedRoot, 'workers', 'python', version, 'WINDOWS', 'Arm64');
202
+ const patchedX64WorkerDir = path.join(patchedRoot, 'workers', 'python', version, 'WINDOWS', 'X64');
203
+ if (!fs.existsSync(patchedArmWorkerDir) && fs.existsSync(patchedX64WorkerDir)) {
204
+ fs.cpSync(patchedX64WorkerDir, patchedArmWorkerDir, { recursive: true });
205
+ }
206
+ console.log(`🩹 Using bridged Azure Functions Core Tools from ${patchedRoot}`);
207
+ return prependToPathEnv(env, patchedRoot);
208
+ }
209
+ async function preparePythonFunctionsEnvironment(functionsDir) {
210
+ const { pythonExecutable } = getPythonVirtualEnvPaths(functionsDir);
211
+ const hasUv = await checkCommand('uv', ['--version']);
212
+ if (!fs.existsSync(pythonExecutable)) {
213
+ if (hasUv) {
214
+ console.log('šŸ“¦ Creating Python virtual environment with uv...');
215
+ await runCommand('uv', ['venv', '.venv', '--python', '3.11'], functionsDir, 'python virtual environment setup');
216
+ }
217
+ else {
218
+ const bootstrap = await resolvePythonBootstrapCommand();
219
+ console.log(`šŸ“¦ Creating Python virtual environment with ${bootstrap.label}...`);
220
+ await runCommand(bootstrap.command, [...bootstrap.argsPrefix, '-m', 'venv', '.venv'], functionsDir, 'python virtual environment setup');
221
+ }
222
+ }
223
+ const pythonEnv = buildPythonFunctionsEnv(process.env, functionsDir);
224
+ console.log(`šŸ“¦ Installing Python Azure Functions dependencies${hasUv ? ' with uv' : ''}...`);
225
+ if (hasUv) {
226
+ await runCommand('uv', ['pip', 'install', '--python', pythonExecutable, '-r', 'requirements.txt'], functionsDir, 'python dependency installation', pythonEnv);
227
+ }
228
+ else {
229
+ await runCommand('python', ['-m', 'pip', 'install', '--upgrade', 'pip'], functionsDir, 'python pip upgrade', pythonEnv);
230
+ await runCommand('python', ['-m', 'pip', 'install', '-r', 'requirements.txt'], functionsDir, 'python dependency installation', pythonEnv);
231
+ }
232
+ return bridgePythonCoreToolsForWindowsArm64(functionsDir, pythonEnv);
233
+ }
60
234
  /**
61
235
  * Check if Cosmos DB Emulator is running by checking if port 8081 is open
62
236
  */
@@ -89,6 +263,7 @@ exports.devCommand = new commander_1.Command()
89
263
  .option('--open', 'Open browser automatically', false)
90
264
  .option('--verbose', 'Show verbose logs', false)
91
265
  .option('--no-functions', 'Skip Azure Functions startup', false)
266
+ .option('--seed-env <environment>', 'Replace Cosmos DB Emulator data from dev-seeds/<environment> before startup')
92
267
  .action(async (options) => {
93
268
  // SwallowKit ćƒ—ćƒ­ć‚øć‚§ć‚Æćƒˆćƒ‡ć‚£ćƒ¬ć‚ÆćƒˆćƒŖć‹ć©ć†ć‹ć‚’ę¤œčØ¼
94
269
  (0, config_1.ensureSwallowKitProject)("dev");
@@ -105,14 +280,14 @@ async function initializeCosmosDB(databaseName) {
105
280
  const localSettingsPath = path.join(functionsDir, 'local.settings.json');
106
281
  if (!fs.existsSync(localSettingsPath)) {
107
282
  console.log('āš ļø local.settings.json not found. Skipping Cosmos DB initialization.');
108
- return;
283
+ return null;
109
284
  }
110
285
  const localSettings = JSON.parse(fs.readFileSync(localSettingsPath, 'utf-8'));
111
286
  const connectionString = localSettings.Values?.CosmosDBConnection;
112
287
  const dbName = localSettings.Values?.COSMOS_DB_DATABASE_NAME || databaseName;
113
288
  if (!connectionString) {
114
289
  console.log('āš ļø CosmosDBConnection not found in local.settings.json. Skipping Cosmos DB initialization.');
115
- return;
290
+ return null;
116
291
  }
117
292
  console.log('šŸ—„ļø Initializing Cosmos DB...');
118
293
  // Parse connection string
@@ -120,7 +295,7 @@ async function initializeCosmosDB(databaseName) {
120
295
  const keyMatch = connectionString.match(/AccountKey=([^;]+)/);
121
296
  if (!endpointMatch || !keyMatch) {
122
297
  console.log('āš ļø Invalid CosmosDB connection string format.');
123
- return;
298
+ return null;
124
299
  }
125
300
  const endpoint = endpointMatch[1];
126
301
  const client = new cosmos_1.CosmosClient({
@@ -130,62 +305,52 @@ async function initializeCosmosDB(databaseName) {
130
305
  // Create database if not exists
131
306
  const { database } = await client.databases.createIfNotExists({ id: dbName });
132
307
  console.log(`āœ… Database "${dbName}" ready`);
133
- // Read lib/scaffold-config.ts to get list of models
134
- const scaffoldConfigPath = path.join(process.cwd(), 'lib', 'scaffold-config.ts');
135
- if (fs.existsSync(scaffoldConfigPath)) {
136
- const scaffoldConfigContent = fs.readFileSync(scaffoldConfigPath, 'utf-8');
137
- // Parse TypeScript file to extract models array
138
- const modelsMatch = scaffoldConfigContent.match(/models:\s*\[([\s\S]*?)\]\s*as\s*ScaffoldModel\[\]/);
139
- if (modelsMatch) {
140
- const modelsArrayContent = modelsMatch[1];
141
- // Extract model names from objects like { name: 'Task', path: '/task', label: 'Task' }
142
- const modelMatches = modelsArrayContent.matchAll(/\{\s*name:\s*['"](\w+)['"]/g);
143
- const models = Array.from(modelMatches, m => m[1]);
144
- for (const modelName of models) {
145
- const containerName = `${modelName}s`; // Pluralize model name
146
- // Try creating container with full partition key definition first
147
- let containerCreated = false;
148
- try {
149
- console.log(`šŸ”§ Creating container "${containerName}" with partition key /id...`);
150
- const containerResponse = await database.containers.createIfNotExists({
151
- id: containerName,
152
- partitionKey: {
153
- paths: ['/id'],
154
- kind: cosmos_1.PartitionKeyKind.Hash,
155
- version: 2
156
- }
157
- });
158
- console.log(`āœ… Container "${containerName}" ready (status: ${containerResponse.statusCode})`);
159
- containerCreated = true;
160
- }
161
- catch (error) {
162
- console.log(`āš ļø Failed with full partition key definition: ${error.message}`);
163
- console.log(`šŸ”„ Retrying with simple partition key...`);
308
+ const models = await (0, dev_seeds_1.loadProjectModels)();
309
+ for (const model of models) {
310
+ const containerName = (0, dev_seeds_1.getContainerNameForModel)(model);
311
+ // Try creating container with full partition key definition first
312
+ let containerCreated = false;
313
+ try {
314
+ console.log(`šŸ”§ Creating container "${containerName}" with partition key /id...`);
315
+ const containerResponse = await database.containers.createIfNotExists({
316
+ id: containerName,
317
+ partitionKey: {
318
+ paths: ['/id'],
319
+ kind: cosmos_1.PartitionKeyKind.Hash,
320
+ version: 2
164
321
  }
165
- // If first attempt failed, try with simple partition key definition
166
- if (!containerCreated) {
167
- try {
168
- const containerResponse = await database.containers.createIfNotExists({
169
- id: containerName,
170
- partitionKey: {
171
- paths: ['/id']
172
- }
173
- });
174
- console.log(`āœ… Container "${containerName}" ready (status: ${containerResponse.statusCode})`);
175
- }
176
- catch (containerError) {
177
- console.error(`āŒ Failed to create container "${containerName}":`, containerError.message);
178
- console.error(`Error code: ${containerError.code}`);
179
- if (containerError.body) {
180
- console.error(`Response body:`, JSON.stringify(containerError.body, null, 2));
181
- }
182
- // Continue with other containers
322
+ });
323
+ console.log(`āœ… Container "${containerName}" ready (status: ${containerResponse.statusCode})`);
324
+ containerCreated = true;
325
+ }
326
+ catch (error) {
327
+ const message = error instanceof Error ? error.message : String(error);
328
+ console.log(`āš ļø Failed with full partition key definition: ${message}`);
329
+ console.log(`šŸ”„ Retrying with simple partition key...`);
330
+ }
331
+ // If first attempt failed, try with simple partition key definition
332
+ if (!containerCreated) {
333
+ try {
334
+ const containerResponse = await database.containers.createIfNotExists({
335
+ id: containerName,
336
+ partitionKey: {
337
+ paths: ['/id']
183
338
  }
339
+ });
340
+ console.log(`āœ… Container "${containerName}" ready (status: ${containerResponse.statusCode})`);
341
+ }
342
+ catch (containerError) {
343
+ console.error(`āŒ Failed to create container "${containerName}":`, containerError.message);
344
+ console.error(`Error code: ${containerError.code}`);
345
+ if (containerError.body) {
346
+ console.error(`Response body:`, JSON.stringify(containerError.body, null, 2));
184
347
  }
348
+ // Continue with other containers
185
349
  }
186
350
  }
187
351
  }
188
352
  console.log('āœ… Cosmos DB initialization complete\n');
353
+ return { endpoint, key: keyMatch[1], databaseName: dbName, models };
189
354
  }
190
355
  catch (error) {
191
356
  console.error('āš ļø Cosmos DB initialization failed:', error.message);
@@ -193,13 +358,19 @@ async function initializeCosmosDB(databaseName) {
193
358
  console.error('Stack trace:', error.stack);
194
359
  }
195
360
  console.log('šŸ’” Make sure Cosmos DB Emulator is running');
361
+ return null;
196
362
  }
197
363
  }
198
364
  async function startDevEnvironment(options) {
199
365
  const port = options.port || '3000';
200
366
  const functionsPort = options.functionsPort || '7071';
367
+ // Detect package manager from project lockfile
368
+ const pm = (0, package_manager_1.detectFromProject)();
369
+ const pmCmd = (0, package_manager_1.getCommands)(pm);
370
+ const backendLanguage = (0, config_1.getBackendLanguage)();
201
371
  // ćƒ—ćƒ­ć‚»ć‚¹ć‚’ē®”ē†ć™ć‚‹é…åˆ—
202
372
  const processes = [];
373
+ let functionsEnv = process.env;
203
374
  // Cleanup processes on Ctrl+C
204
375
  process.on('SIGINT', () => {
205
376
  console.log('\nšŸ›‘ Stopping development servers...');
@@ -226,8 +397,7 @@ async function startDevEnvironment(options) {
226
397
  }
227
398
  // 2. Check if Azure Functions exists
228
399
  const functionsDir = path.join(process.cwd(), 'functions');
229
- const hasFunctions = fs.existsSync(functionsDir) &&
230
- fs.existsSync(path.join(functionsDir, 'package.json'));
400
+ const hasFunctions = fs.existsSync(functionsDir) && hasFunctionsProject(functionsDir, backendLanguage);
231
401
  if (hasFunctions && !options.noFunctions) {
232
402
  // Check if Azure Functions Core Tools is installed
233
403
  const coreToolsInstalled = await checkCoreTools();
@@ -248,7 +418,7 @@ async function startDevEnvironment(options) {
248
418
  if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
249
419
  console.log('šŸ“¦ Installing Azure Functions Core Tools...');
250
420
  console.log(' This may take a few minutes.');
251
- const installProcess = (0, child_process_1.spawn)('npm', ['install', '-g', 'azure-functions-core-tools@4'], {
421
+ const installProcess = (0, child_process_1.spawn)(pm, pm === 'pnpm' ? ['add', '-g', 'azure-functions-core-tools@4'] : ['install', '-g', 'azure-functions-core-tools@4'], {
252
422
  shell: true,
253
423
  stdio: 'inherit',
254
424
  });
@@ -261,7 +431,7 @@ async function startDevEnvironment(options) {
261
431
  else {
262
432
  console.error('āŒ Installation failed.');
263
433
  console.log('šŸ’” Please install manually:');
264
- console.log(' npm install -g azure-functions-core-tools@4');
434
+ console.log(` ${pmCmd.addGlobal} azure-functions-core-tools@4`);
265
435
  reject(new Error(`Installation failed with code ${code}`));
266
436
  }
267
437
  });
@@ -272,7 +442,7 @@ async function startDevEnvironment(options) {
272
442
  console.log('');
273
443
  console.log('ā„¹ļø Skipping Azure Functions startup.');
274
444
  console.log('šŸ’” To install later:');
275
- console.log(' npm install -g azure-functions-core-tools@4');
445
+ console.log(` ${pmCmd.addGlobal} azure-functions-core-tools@4`);
276
446
  console.log('');
277
447
  // Skip Azure Functions startup
278
448
  options.noFunctions = true;
@@ -296,29 +466,33 @@ async function startDevEnvironment(options) {
296
466
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
297
467
  const appName = packageJson.name || 'App';
298
468
  const databaseName = `${appName.charAt(0).toUpperCase() + appName.slice(1)}Database`;
299
- await initializeCosmosDB(databaseName);
469
+ const cosmosInitialization = await initializeCosmosDB(databaseName);
470
+ if (options.seedEnv && cosmosInitialization) {
471
+ await (0, dev_seeds_1.applyDevSeedEnvironment)({
472
+ client: new cosmos_1.CosmosClient({
473
+ endpoint: cosmosInitialization.endpoint,
474
+ key: cosmosInitialization.key,
475
+ }),
476
+ databaseName: cosmosInitialization.databaseName,
477
+ environment: options.seedEnv,
478
+ models: cosmosInitialization.models,
479
+ });
480
+ }
300
481
  console.log('');
301
482
  console.log('šŸš€ Starting Azure Functions...');
302
- // Check if npm install has been run in functions directory
303
- const functionsNodeModules = path.join(functionsDir, 'node_modules');
304
- if (!fs.existsSync(functionsNodeModules)) {
305
- console.log('šŸ“¦ Installing Azure Functions dependencies...');
306
- const npmInstall = (0, child_process_1.spawn)('npm', ['install'], {
307
- cwd: functionsDir,
308
- shell: true,
309
- stdio: 'inherit',
310
- });
311
- await new Promise((resolve, reject) => {
312
- npmInstall.on('close', (code) => {
313
- if (code === 0) {
314
- resolve();
315
- }
316
- else {
317
- reject(new Error(`npm install failed with code ${code}`));
318
- }
319
- });
320
- npmInstall.on('error', reject);
321
- });
483
+ if (backendLanguage === 'typescript') {
484
+ const functionsNodeModules = path.join(functionsDir, 'node_modules');
485
+ if (!fs.existsSync(functionsNodeModules)) {
486
+ console.log('šŸ“¦ Installing Azure Functions dependencies...');
487
+ await runCommand(pm, ['install'], functionsDir, `${pm} install`);
488
+ }
489
+ }
490
+ else if (backendLanguage === 'csharp') {
491
+ console.log('šŸ“¦ Building C# Azure Functions project...');
492
+ await runCommand('dotnet', ['build'], functionsDir, 'dotnet build');
493
+ }
494
+ else {
495
+ functionsEnv = await preparePythonFunctionsEnvironment(functionsDir);
322
496
  }
323
497
  }
324
498
  }
@@ -327,7 +501,10 @@ async function startDevEnvironment(options) {
327
501
  const sharedDir = path.join(process.cwd(), 'shared');
328
502
  if (fs.existsSync(sharedDir) && fs.existsSync(path.join(sharedDir, 'package.json'))) {
329
503
  console.log('šŸ“¦ Building shared package...');
330
- const sharedBuild = (0, child_process_1.spawn)('npm', ['run', 'build', '-w', 'shared'], {
504
+ const filterArgs = pm === 'pnpm'
505
+ ? ['run', '--filter', 'shared', 'build']
506
+ : ['run', '--workspace=shared', 'build'];
507
+ const sharedBuild = (0, child_process_1.spawn)(pm, filterArgs, {
331
508
  cwd: process.cwd(),
332
509
  shell: true,
333
510
  stdio: 'inherit',
@@ -346,13 +523,13 @@ async function startDevEnvironment(options) {
346
523
  });
347
524
  }
348
525
  // Azure Functions ć‚’čµ·å‹•
349
- const funcEnv = { ...process.env, FUNCTIONS_PORT: functionsPort };
350
- const funcProcess = (0, child_process_1.spawn)('npm', ['start'], {
526
+ const funcProcess = (0, child_process_1.spawn)('func', buildFunctionsStartArgs(functionsPort), {
351
527
  cwd: functionsDir,
352
528
  shell: true,
353
529
  stdio: 'pipe', // Always pipe to capture output
354
- env: funcEnv
530
+ env: functionsEnv
355
531
  });
532
+ let pythonWorkerMissing = false;
356
533
  // Functions ć®å‡ŗåŠ›ć‚’ćć®ć¾ć¾č”Øē¤ŗļ¼ˆćƒ—ćƒ¬ćƒ•ć‚£ćƒƒć‚Æć‚¹ä»˜ćļ¼‰
357
534
  if (funcProcess.stdout) {
358
535
  funcProcess.stdout.on('data', (data) => {
@@ -360,6 +537,10 @@ async function startDevEnvironment(options) {
360
537
  // å„č”Œć«ćƒ—ćƒ¬ćƒ•ć‚£ćƒƒć‚Æć‚¹ć‚’ä»˜ć‘ć¦å‡ŗåŠ›
361
538
  const lines = output.split('\n').filter((line) => line.trim());
362
539
  lines.forEach((line) => {
540
+ if (backendLanguage === 'python' &&
541
+ (line.includes('WorkerConfig for runtime: python not found') || line.includes('DefaultWorkerPath:'))) {
542
+ pythonWorkerMissing = true;
543
+ }
363
544
  console.log(`[Functions] ${line}`);
364
545
  });
365
546
  });
@@ -369,6 +550,10 @@ async function startDevEnvironment(options) {
369
550
  const output = data.toString();
370
551
  const lines = output.split('\n').filter((line) => line.trim());
371
552
  lines.forEach((line) => {
553
+ if (backendLanguage === 'python' &&
554
+ (line.includes('WorkerConfig for runtime: python not found') || line.includes('DefaultWorkerPath:'))) {
555
+ pythonWorkerMissing = true;
556
+ }
372
557
  console.error(`[Functions Error] ${line}`);
373
558
  });
374
559
  });
@@ -377,11 +562,16 @@ async function startDevEnvironment(options) {
377
562
  funcProcess.on('error', (error) => {
378
563
  console.error('āš ļø Azure Functions startup error:', error.message);
379
564
  console.log('šŸ’” Please ensure Azure Functions Core Tools is installed');
380
- console.log(' npm install -g azure-functions-core-tools@4');
565
+ console.log(` ${pmCmd.addGlobal} azure-functions-core-tools@4`);
381
566
  });
382
567
  funcProcess.on('close', (code) => {
383
568
  if (code !== 0) {
384
569
  console.log(`\nā¹ļø Azure Functions exited (exit code: ${code})`);
570
+ if (backendLanguage === 'python' && pythonWorkerMissing) {
571
+ console.log('šŸ’” Your Azure Functions Core Tools installation is missing the Python worker for this OS/architecture.');
572
+ console.log(' Reinstall a matching Core Tools v4 package (Windows users should prefer the official x64/x86 MSI for their machine).');
573
+ console.log(' SwallowKit local Python dev uses functions/.venv and requirements.txt, but Core Tools still needs its own bundled Python worker.');
574
+ }
385
575
  }
386
576
  });
387
577
  console.log(`āœ… Azure Functions started (port: ${functionsPort})`);
@@ -397,7 +587,7 @@ async function startDevEnvironment(options) {
397
587
  console.log('');
398
588
  console.log('šŸš€ Starting Next.js development server...');
399
589
  // 5. Start Next.js development server
400
- const nextArgs = ['next', 'dev', '--port', port];
590
+ const nextArgs = buildNextDevArgs(pm, port);
401
591
  if (options.open) {
402
592
  // Next.js 14+ deprecated --open option, so we open browser manually
403
593
  setTimeout(() => {
@@ -408,10 +598,16 @@ async function startDevEnvironment(options) {
408
598
  (0, child_process_1.spawn)(start, [url], { shell: true });
409
599
  }, 3000);
410
600
  }
411
- const nextProcess = (0, child_process_1.spawn)('npx', nextArgs, {
601
+ const nextEnv = {
602
+ ...process.env,
603
+ BACKEND_FUNCTIONS_BASE_URL: `http://${options.host || 'localhost'}:${functionsPort}`,
604
+ FUNCTIONS_BASE_URL: `http://${options.host || 'localhost'}:${functionsPort}`,
605
+ };
606
+ const nextProcess = (0, child_process_1.spawn)(pm === 'pnpm' ? 'pnpm' : 'npx', nextArgs, {
412
607
  cwd: process.cwd(),
413
608
  shell: true,
414
609
  stdio: options.verbose ? 'inherit' : 'inherit',
610
+ env: nextEnv,
415
611
  });
416
612
  processes.push(nextProcess);
417
613
  nextProcess.on('error', (error) => {
@@ -455,4 +651,29 @@ async function startDevEnvironment(options) {
455
651
  process.exit(1);
456
652
  }
457
653
  }
654
+ async function runCommand(command, args, cwd, label, env) {
655
+ await new Promise((resolve, reject) => {
656
+ const child = (0, child_process_1.spawn)(command, args, {
657
+ cwd,
658
+ shell: true,
659
+ stdio: 'inherit',
660
+ env,
661
+ });
662
+ child.on('close', (code) => {
663
+ if (code === 0) {
664
+ resolve();
665
+ }
666
+ else {
667
+ reject(new Error(`${label} failed with code ${code}`));
668
+ }
669
+ });
670
+ child.on('error', reject);
671
+ });
672
+ }
673
+ function hasFunctionsProject(functionsDir, backendLanguage) {
674
+ if (backendLanguage === 'typescript') {
675
+ return fs.existsSync(path.join(functionsDir, 'package.json'));
676
+ }
677
+ return fs.existsSync(path.join(functionsDir, 'host.json'));
678
+ }
458
679
  //# sourceMappingURL=dev.js.map