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.
- package/LICENSE +21 -21
- package/README.ja.md +254 -183
- package/README.md +311 -184
- package/dist/__tests__/fixtures.d.ts +14 -0
- package/dist/__tests__/fixtures.d.ts.map +1 -0
- package/dist/__tests__/fixtures.js +85 -0
- package/dist/__tests__/fixtures.js.map +1 -0
- package/dist/cli/commands/create-model.d.ts.map +1 -1
- package/dist/cli/commands/create-model.js +16 -15
- package/dist/cli/commands/create-model.js.map +1 -1
- package/dist/cli/commands/dev-seeds.d.ts +35 -0
- package/dist/cli/commands/dev-seeds.d.ts.map +1 -0
- package/dist/cli/commands/dev-seeds.js +292 -0
- package/dist/cli/commands/dev-seeds.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +8 -0
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +308 -87
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/index.d.ts +1 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +3 -1
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/init.d.ts +13 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +2639 -1708
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +3 -0
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +283 -118
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.js +17 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config.d.ts +2 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +31 -1
- package/dist/core/config.js.map +1 -1
- package/dist/core/scaffold/functions-generator.d.ts +5 -0
- package/dist/core/scaffold/functions-generator.d.ts.map +1 -1
- package/dist/core/scaffold/functions-generator.js +649 -211
- package/dist/core/scaffold/functions-generator.js.map +1 -1
- package/dist/core/scaffold/model-parser.d.ts +1 -1
- package/dist/core/scaffold/model-parser.d.ts.map +1 -1
- package/dist/core/scaffold/model-parser.js +105 -101
- package/dist/core/scaffold/model-parser.js.map +1 -1
- package/dist/core/scaffold/nextjs-generator.js +181 -181
- package/dist/core/scaffold/openapi-generator.d.ts +3 -0
- package/dist/core/scaffold/openapi-generator.d.ts.map +1 -0
- package/dist/core/scaffold/openapi-generator.js +190 -0
- package/dist/core/scaffold/openapi-generator.js.map +1 -0
- package/dist/core/scaffold/ui-generator.js +656 -656
- package/dist/database/base-model.d.ts +3 -3
- package/dist/database/base-model.js +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/package-manager.d.ts +109 -0
- package/dist/utils/package-manager.d.ts.map +1 -0
- package/dist/utils/package-manager.js +215 -0
- package/dist/utils/package-manager.js.map +1 -0
- package/package.json +81 -73
- package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +445 -0
- package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +194 -0
- package/src/__tests__/__snapshots__/ui-generator.test.ts.snap +524 -0
- package/src/__tests__/config.test.ts +122 -0
- package/src/__tests__/dev-seeds.test.ts +112 -0
- package/src/__tests__/dev.test.ts +42 -0
- package/src/__tests__/fixtures.ts +83 -0
- package/src/__tests__/functions-generator.test.ts +101 -0
- package/src/__tests__/init.test.ts +80 -0
- package/src/__tests__/nextjs-generator.test.ts +97 -0
- package/src/__tests__/openapi-generator.test.ts +43 -0
- package/src/__tests__/package-manager.test.ts +189 -0
- package/src/__tests__/scaffold.test.ts +39 -0
- package/src/__tests__/string-utils.test.ts +75 -0
- package/src/__tests__/ui-generator.test.ts +144 -0
- package/src/cli/commands/create-model.ts +141 -0
- package/src/cli/commands/dev-seeds.ts +358 -0
- package/src/cli/commands/dev.ts +805 -0
- package/src/cli/commands/index.ts +9 -0
- package/src/cli/commands/init.ts +3370 -0
- package/src/cli/commands/provision.ts +193 -0
- package/src/cli/commands/scaffold.ts +786 -0
- package/src/cli/index.ts +74 -0
- package/src/core/config.ts +244 -0
- package/src/core/scaffold/functions-generator.ts +674 -0
- package/src/core/scaffold/model-parser.ts +627 -0
- package/src/core/scaffold/nextjs-generator.ts +217 -0
- package/src/core/scaffold/openapi-generator.ts +212 -0
- package/src/core/scaffold/ui-generator.ts +945 -0
- package/src/database/base-model.ts +184 -0
- package/src/database/client.ts +140 -0
- package/src/database/repository.ts +104 -0
- package/src/database/runtime-check.ts +25 -0
- package/src/index.ts +27 -0
- package/src/types/index.ts +45 -0
- package/src/utils/package-manager.ts +229 -0
- package/dist/cli/commands/build.d.ts +0 -6
- package/dist/cli/commands/build.d.ts.map +0 -1
- package/dist/cli/commands/build.js +0 -177
- package/dist/cli/commands/build.js.map +0 -1
- package/dist/cli/commands/deploy.d.ts +0 -3
- package/dist/cli/commands/deploy.d.ts.map +0 -1
- package/dist/cli/commands/deploy.js +0 -147
- package/dist/cli/commands/deploy.js.map +0 -1
- package/dist/cli/commands/setup.d.ts +0 -6
- package/dist/cli/commands/setup.d.ts.map +0 -1
- package/dist/cli/commands/setup.js +0 -254
- package/dist/cli/commands/setup.js.map +0 -1
package/dist/cli/commands/dev.js
CHANGED
|
@@ -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)(
|
|
49
|
-
shell:
|
|
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
|
-
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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)('
|
|
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(
|
|
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(
|
|
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
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
await
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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
|
|
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
|
|
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:
|
|
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(
|
|
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 =
|
|
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
|
|
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
|