swallowkit 1.1.2 → 2.0.0-alpha.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/README.ja.md +61 -7
- package/README.md +61 -7
- package/dist/cli/commands/add-auth.d.ts.map +1 -1
- package/dist/cli/commands/add-auth.js +103 -27
- package/dist/cli/commands/add-auth.js.map +1 -1
- package/dist/cli/commands/configure-mcp.d.ts +12 -0
- package/dist/cli/commands/configure-mcp.d.ts.map +1 -0
- package/dist/cli/commands/configure-mcp.js +42 -0
- package/dist/cli/commands/configure-mcp.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +5 -2
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +143 -67
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/init.d.ts +14 -7
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +149 -170
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/migrations.d.ts.map +1 -1
- package/dist/cli/commands/migrations.js +25 -0
- package/dist/cli/commands/migrations.js.map +1 -1
- package/dist/cli/commands/provision.d.ts +5 -0
- package/dist/cli/commands/provision.d.ts.map +1 -1
- package/dist/cli/commands/provision.js +278 -55
- package/dist/cli/commands/provision.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +5 -1
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +123 -18
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +49 -8
- 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 +19 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/operations/create-model.d.ts +2 -0
- package/dist/core/operations/create-model.d.ts.map +1 -1
- package/dist/core/operations/create-model.js +60 -8
- package/dist/core/operations/create-model.js.map +1 -1
- package/dist/core/operations/migration-generator.d.ts.map +1 -1
- package/dist/core/operations/migration-generator.js +12 -1
- package/dist/core/operations/migration-generator.js.map +1 -1
- package/dist/core/operations/model-migration.d.ts +22 -0
- package/dist/core/operations/model-migration.d.ts.map +1 -0
- package/dist/core/operations/model-migration.js +36 -0
- package/dist/core/operations/model-migration.js.map +1 -0
- package/dist/core/operations/provision-deployment.d.ts +52 -0
- package/dist/core/operations/provision-deployment.d.ts.map +1 -0
- package/dist/core/operations/provision-deployment.js +164 -0
- package/dist/core/operations/provision-deployment.js.map +1 -0
- package/dist/core/operations/provision-operations.d.ts +62 -6
- package/dist/core/operations/provision-operations.d.ts.map +1 -1
- package/dist/core/operations/provision-operations.js +653 -115
- package/dist/core/operations/provision-operations.js.map +1 -1
- package/dist/core/operations/scaffold-apply.d.ts +1 -0
- package/dist/core/operations/scaffold-apply.d.ts.map +1 -1
- package/dist/core/operations/scaffold-apply.js +1 -0
- package/dist/core/operations/scaffold-apply.js.map +1 -1
- package/dist/core/operations/scaffold-machine.d.ts +1 -0
- package/dist/core/operations/scaffold-machine.d.ts.map +1 -1
- package/dist/core/operations/scaffold-machine.js.map +1 -1
- package/dist/core/operations/scaffold-plan.d.ts +2 -0
- package/dist/core/operations/scaffold-plan.d.ts.map +1 -1
- package/dist/core/operations/scaffold-plan.js +3 -1
- package/dist/core/operations/scaffold-plan.js.map +1 -1
- package/dist/core/project/agent-instructions.d.ts +2 -1
- package/dist/core/project/agent-instructions.d.ts.map +1 -1
- package/dist/core/project/agent-instructions.js +15 -0
- package/dist/core/project/agent-instructions.js.map +1 -1
- package/dist/core/project/boundaries.d.ts +5 -2
- package/dist/core/project/boundaries.d.ts.map +1 -1
- package/dist/core/project/boundaries.js +51 -20
- package/dist/core/project/boundaries.js.map +1 -1
- package/dist/core/project/capabilities.d.ts +17 -0
- package/dist/core/project/capabilities.d.ts.map +1 -1
- package/dist/core/project/capabilities.js +45 -6
- package/dist/core/project/capabilities.js.map +1 -1
- package/dist/core/project/home-page.d.ts +4 -0
- package/dist/core/project/home-page.d.ts.map +1 -0
- package/dist/core/project/home-page.js +113 -0
- package/dist/core/project/home-page.js.map +1 -0
- package/dist/core/project/manifest.d.ts +3 -1
- package/dist/core/project/manifest.d.ts.map +1 -1
- package/dist/core/project/manifest.js +47 -25
- package/dist/core/project/manifest.js.map +1 -1
- package/dist/core/project/mcp-config.d.ts +26 -0
- package/dist/core/project/mcp-config.d.ts.map +1 -0
- package/dist/core/project/mcp-config.js +117 -0
- package/dist/core/project/mcp-config.js.map +1 -0
- package/dist/core/project/react-project.d.ts +26 -0
- package/dist/core/project/react-project.d.ts.map +1 -0
- package/dist/core/project/react-project.js +443 -0
- package/dist/core/project/react-project.js.map +1 -0
- package/dist/core/project/state.d.ts +7 -0
- package/dist/core/project/state.d.ts.map +1 -1
- package/dist/core/project/state.js +86 -0
- package/dist/core/project/state.js.map +1 -1
- package/dist/core/project/validation.d.ts.map +1 -1
- package/dist/core/project/validation.js +32 -10
- package/dist/core/project/validation.js.map +1 -1
- package/dist/core/project/workflows.d.ts.map +1 -1
- package/dist/core/project/workflows.js +53 -7
- package/dist/core/project/workflows.js.map +1 -1
- package/dist/core/scaffold/auth-generator.d.ts.map +1 -1
- package/dist/core/scaffold/auth-generator.js +26 -27
- package/dist/core/scaffold/auth-generator.js.map +1 -1
- package/dist/core/scaffold/model-parser.d.ts +23 -0
- package/dist/core/scaffold/model-parser.d.ts.map +1 -1
- package/dist/core/scaffold/model-parser.js +407 -137
- package/dist/core/scaffold/model-parser.js.map +1 -1
- package/dist/core/scaffold/native-schema-generator.d.ts +13 -0
- package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -1
- package/dist/core/scaffold/native-schema-generator.js +294 -91
- package/dist/core/scaffold/native-schema-generator.js.map +1 -1
- package/dist/core/scaffold/openapi-generator.d.ts +5 -0
- package/dist/core/scaffold/openapi-generator.d.ts.map +1 -1
- package/dist/core/scaffold/openapi-generator.js +53 -3
- package/dist/core/scaffold/openapi-generator.js.map +1 -1
- package/dist/core/scaffold/react-bff-generator.d.ts +10 -0
- package/dist/core/scaffold/react-bff-generator.d.ts.map +1 -0
- package/dist/core/scaffold/react-bff-generator.js +252 -0
- package/dist/core/scaffold/react-bff-generator.js.map +1 -0
- package/dist/core/scaffold/react-ui-generator.d.ts +6 -0
- package/dist/core/scaffold/react-ui-generator.d.ts.map +1 -0
- package/dist/core/scaffold/react-ui-generator.js +41 -0
- package/dist/core/scaffold/react-ui-generator.js.map +1 -0
- package/dist/core/scaffold/ui-generator.d.ts +0 -3
- package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
- package/dist/core/scaffold/ui-generator.js +68 -7
- package/dist/core/scaffold/ui-generator.js.map +1 -1
- package/dist/core/verify/index.d.ts +4 -3
- package/dist/core/verify/index.d.ts.map +1 -1
- package/dist/core/verify/index.js +122 -5
- package/dist/core/verify/index.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/machine/contracts.d.ts +1 -0
- package/dist/machine/contracts.d.ts.map +1 -1
- package/dist/machine/errors.d.ts +3 -2
- package/dist/machine/errors.d.ts.map +1 -1
- package/dist/machine/errors.js +2 -1
- package/dist/machine/errors.js.map +1 -1
- package/dist/machine/index.d.ts.map +1 -1
- package/dist/machine/index.js +61 -30
- package/dist/machine/index.js.map +1 -1
- package/dist/mcp/index.d.ts +6 -4
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +210 -25
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/version-selection.d.ts +55 -0
- package/dist/mcp/version-selection.d.ts.map +1 -0
- package/dist/mcp/version-selection.js +152 -0
- package/dist/mcp/version-selection.js.map +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/azure-cli.d.ts +56 -0
- package/dist/utils/azure-cli.d.ts.map +1 -0
- package/dist/utils/azure-cli.js +326 -0
- package/dist/utils/azure-cli.js.map +1 -0
- package/dist/utils/azure-diagnostics.d.ts +16 -0
- package/dist/utils/azure-diagnostics.d.ts.map +1 -0
- package/dist/utils/azure-diagnostics.js +68 -0
- package/dist/utils/azure-diagnostics.js.map +1 -0
- package/dist/utils/package-manager.d.ts +2 -2
- package/dist/utils/package-manager.d.ts.map +1 -1
- package/dist/utils/package-manager.js +6 -3
- package/dist/utils/package-manager.js.map +1 -1
- package/dist/utils/python-uv.d.ts +5 -7
- package/dist/utils/python-uv.d.ts.map +1 -1
- package/dist/utils/python-uv.js +50 -24
- package/dist/utils/python-uv.js.map +1 -1
- package/dist/utils/react-dev.d.ts +4 -0
- package/dist/utils/react-dev.d.ts.map +1 -0
- package/dist/utils/react-dev.js +35 -0
- package/dist/utils/react-dev.js.map +1 -0
- package/dist/utils/semver.d.ts +14 -0
- package/dist/utils/semver.d.ts.map +1 -0
- package/dist/utils/semver.js +61 -0
- package/dist/utils/semver.js.map +1 -0
- package/package.json +7 -4
- package/src/__tests__/__snapshots__/boundaries.test.ts.snap +154 -0
- package/src/__tests__/agent-instructions.test.ts +6 -1
- package/src/__tests__/agent-loop-phase34.test.ts +143 -8
- package/src/__tests__/auth.test.ts +9 -0
- package/src/__tests__/azure-cli.test.ts +202 -0
- package/src/__tests__/boundaries.test.ts +42 -0
- package/src/__tests__/csharp-schema.test.ts +261 -0
- package/src/__tests__/dev.test.ts +13 -6
- package/src/__tests__/fixtures/az wrapper.cmd +2 -0
- package/src/__tests__/fixtures/azure-cli.cjs +153 -0
- package/src/__tests__/fixtures/provision-runner.cjs +28 -0
- package/src/__tests__/generated-project-matrix.test.ts +789 -0
- package/src/__tests__/home-page.test.ts +50 -0
- package/src/__tests__/init.test.ts +145 -5
- package/src/__tests__/mcp-version.test.ts +262 -0
- package/src/__tests__/mcp.test.ts +120 -3
- package/src/__tests__/multi-auth.test.ts +115 -4
- package/src/__tests__/package-manager.test.ts +17 -2
- package/src/__tests__/plan-apply.test.ts +179 -0
- package/src/__tests__/provision-runtime.test.ts +722 -0
- package/src/__tests__/python-uv.test.ts +50 -14
- package/src/__tests__/python-venv.test.ts +48 -0
- package/src/__tests__/react-dev.test.ts +72 -0
- package/src/__tests__/react.test.ts +838 -0
- package/src/__tests__/release-metadata.test.ts +121 -0
- package/src/__tests__/schema-analysis.test.ts +329 -0
- package/src/cli/commands/add-auth.ts +103 -29
- package/src/cli/commands/configure-mcp.ts +54 -0
- package/src/cli/commands/dev.ts +145 -73
- package/src/cli/commands/init.ts +147 -172
- package/src/cli/commands/migrations.ts +22 -0
- package/src/cli/commands/provision.ts +294 -56
- package/src/cli/commands/scaffold.ts +145 -22
- package/src/cli/index.ts +46 -10
- package/src/core/config.ts +21 -1
- package/src/core/operations/create-model.ts +51 -13
- package/src/core/operations/migration-generator.ts +14 -1
- package/src/core/operations/model-migration.ts +52 -0
- package/src/core/operations/provision-deployment.ts +183 -0
- package/src/core/operations/provision-operations.ts +662 -154
- package/src/core/operations/scaffold-apply.ts +2 -0
- package/src/core/operations/scaffold-machine.ts +1 -0
- package/src/core/operations/scaffold-plan.ts +5 -1
- package/src/core/project/agent-instructions.ts +17 -1
- package/src/core/project/agent-templates/AGENTS.md +113 -15
- package/src/core/project/agent-templates/CLAUDE.md +14 -1
- package/src/core/project/agent-templates/bff-routes.instructions.md +5 -3
- package/src/core/project/agent-templates/copilot-instructions.md +15 -1
- package/src/core/project/agent-templates/shared-models.instructions.md +11 -3
- package/src/core/project/boundaries.ts +62 -20
- package/src/core/project/capabilities.ts +63 -6
- package/src/core/project/home-page.ts +114 -0
- package/src/core/project/manifest.ts +52 -27
- package/src/core/project/mcp-config.ts +84 -0
- package/src/core/project/react-project.ts +529 -0
- package/src/core/project/state.ts +70 -0
- package/src/core/project/validation.ts +32 -10
- package/src/core/project/workflows.ts +53 -7
- package/src/core/scaffold/auth-generator.ts +26 -27
- package/src/core/scaffold/model-parser.ts +413 -129
- package/src/core/scaffold/native-schema-generator.ts +319 -103
- package/src/core/scaffold/openapi-generator.ts +63 -5
- package/src/core/scaffold/react-bff-generator.ts +292 -0
- package/src/core/scaffold/react-ui-generator.ts +51 -0
- package/src/core/scaffold/ui-generator.ts +70 -8
- package/src/core/verify/index.ts +118 -6
- package/src/index.ts +3 -1
- package/src/machine/contracts.ts +1 -0
- package/src/machine/errors.ts +4 -2
- package/src/machine/index.ts +67 -36
- package/src/mcp/index.ts +221 -31
- package/src/mcp/version-selection.ts +163 -0
- package/src/types/index.ts +4 -0
- package/src/utils/azure-cli.ts +315 -0
- package/src/utils/azure-diagnostics.ts +75 -0
- package/src/utils/package-manager.ts +7 -4
- package/src/utils/python-uv.ts +53 -33
- package/src/utils/react-dev.ts +31 -0
- package/src/utils/semver.ts +59 -0
package/src/cli/commands/dev.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { spawn, ChildProcess } from 'child_process';
|
|
2
|
+
import { spawn, spawnSync, ChildProcess } from 'child_process';
|
|
3
3
|
import * as http from 'http';
|
|
4
4
|
import * as https from 'https';
|
|
5
5
|
import * as path from 'path';
|
|
@@ -7,7 +7,7 @@ import * as fs from 'fs';
|
|
|
7
7
|
import * as os from 'os';
|
|
8
8
|
import * as net from 'net';
|
|
9
9
|
import { CosmosClient, PartitionKeyKind } from '@azure/cosmos';
|
|
10
|
-
import { ensureSwallowKitProject, getBackendLanguage, getAuthConfig, normalizeAuthConfig } from '../../core/config';
|
|
10
|
+
import { ensureSwallowKitProject, getBackendLanguage, getFrontendFramework, getAuthConfig, normalizeAuthConfig } from '../../core/config';
|
|
11
11
|
import { ModelInfo } from '../../core/scaffold/model-parser';
|
|
12
12
|
import {
|
|
13
13
|
applyDevSeedEnvironment,
|
|
@@ -19,19 +19,20 @@ import {
|
|
|
19
19
|
import { BackendLanguage } from '../../types';
|
|
20
20
|
import { detectFromProject, getCommands } from '../../utils/package-manager';
|
|
21
21
|
import {
|
|
22
|
-
buildProjectLocalUvEnv,
|
|
23
22
|
buildProjectLocalUvInstallerEnv,
|
|
24
23
|
buildUvPipInstallArgs,
|
|
25
|
-
|
|
24
|
+
ensureUvVirtualEnvironment,
|
|
26
25
|
getProjectLocalUvInstallerCommand,
|
|
27
26
|
getProjectLocalUvPaths,
|
|
28
27
|
getPythonProjectRoot,
|
|
29
28
|
} from '../../utils/python-uv';
|
|
30
29
|
import { ConnectorMockServer } from '../../core/mock/connector-mock-server';
|
|
30
|
+
import { buildReactDevConfigSource } from '../../utils/react-dev';
|
|
31
31
|
|
|
32
32
|
export interface DevOptions {
|
|
33
33
|
port?: string;
|
|
34
34
|
functionsPort?: string;
|
|
35
|
+
bffPort?: string;
|
|
35
36
|
host?: string;
|
|
36
37
|
open?: boolean;
|
|
37
38
|
verbose?: boolean;
|
|
@@ -64,8 +65,8 @@ function normalizeParsedDevOptions(options: ParsedDevActionOptions): DevOptions
|
|
|
64
65
|
};
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
export function buildFunctionsStartArgs(functionsPort: string): string[] {
|
|
68
|
-
return ['start', '--port', functionsPort];
|
|
68
|
+
export function buildFunctionsStartArgs(functionsPort: string, backendLanguage: BackendLanguage = 'typescript'): string[] {
|
|
69
|
+
return [...(backendLanguage === 'csharp' ? [] : ['start']), '--port', functionsPort];
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
export function parseCoreToolsVersion(output: string): string | null {
|
|
@@ -104,15 +105,15 @@ export function buildFunctionsCoreToolsCommand(
|
|
|
104
105
|
|
|
105
106
|
return {
|
|
106
107
|
command: 'npm',
|
|
107
|
-
argsPrefix: ['exec', '--yes', NPM_CORE_TOOLS_PACKAGE, '--'],
|
|
108
|
-
label: `npm
|
|
108
|
+
argsPrefix: ['exec', '--yes', '--package', NPM_CORE_TOOLS_PACKAGE, '--', 'dotnet', 'run', '--no-launch-profile', '--'],
|
|
109
|
+
label: `dotnet run with npm ${NPM_CORE_TOOLS_PACKAGE} (${reason})`,
|
|
109
110
|
};
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
return {
|
|
113
|
-
command: 'func',
|
|
114
|
-
argsPrefix: [],
|
|
115
|
-
label: installedVersion ? `func ${installedVersion}` : 'func',
|
|
114
|
+
command: backendLanguage === 'csharp' ? 'dotnet' : 'func',
|
|
115
|
+
argsPrefix: backendLanguage === 'csharp' ? ['run', '--no-launch-profile', '--'] : [],
|
|
116
|
+
label: backendLanguage === 'csharp' ? 'dotnet run' : installedVersion ? `func ${installedVersion}` : 'func',
|
|
116
117
|
};
|
|
117
118
|
}
|
|
118
119
|
|
|
@@ -125,7 +126,7 @@ export function buildFunctionsBaseUrl(host: string | undefined, functionsPort: s
|
|
|
125
126
|
return `http://${host || 'localhost'}:${functionsPort}`;
|
|
126
127
|
}
|
|
127
128
|
|
|
128
|
-
export function buildSwaStartArgs(host: string | undefined, nextPort: string, swaPort: string): string[] {
|
|
129
|
+
export function buildSwaStartArgs(host: string | undefined, nextPort: string, swaPort: string, bffPort?: string): string[] {
|
|
129
130
|
return [
|
|
130
131
|
'start',
|
|
131
132
|
`http://${host || 'localhost'}:${nextPort}`,
|
|
@@ -133,9 +134,15 @@ export function buildSwaStartArgs(host: string | undefined, nextPort: string, sw
|
|
|
133
134
|
'.',
|
|
134
135
|
'--port',
|
|
135
136
|
swaPort,
|
|
137
|
+
...(bffPort ? ['--api-devserver-url', buildFunctionsBaseUrl(host, bffPort)] : []),
|
|
136
138
|
];
|
|
137
139
|
}
|
|
138
140
|
|
|
141
|
+
export function buildReactDevArgs(pm: string, port: string, host = 'localhost', configPath?: string): string[] {
|
|
142
|
+
const args = ['vite', '--port', port, '--host', host, '--strictPort', ...(configPath ? ['--config', configPath] : [])];
|
|
143
|
+
return pm === 'pnpm' ? ['exec', ...args] : args;
|
|
144
|
+
}
|
|
145
|
+
|
|
139
146
|
export function getSwaCliInstallCommand(pm: string): string {
|
|
140
147
|
return pm === 'pnpm'
|
|
141
148
|
? 'pnpm add -Dw @azure/static-web-apps-cli'
|
|
@@ -263,7 +270,7 @@ async function checkCommand(
|
|
|
263
270
|
}
|
|
264
271
|
|
|
265
272
|
async function resolveProjectLocalUvCommand(projectRoot: string): Promise<{ command: string; env: NodeJS.ProcessEnv }> {
|
|
266
|
-
const uvEnv =
|
|
273
|
+
const uvEnv = { ...process.env };
|
|
267
274
|
const { localUvExecutable } = getProjectLocalUvPaths(projectRoot);
|
|
268
275
|
|
|
269
276
|
if (await checkCommand('uv', ['--version'], { shell: false })) {
|
|
@@ -464,22 +471,8 @@ async function bridgePythonCoreToolsForWindowsArm64(
|
|
|
464
471
|
async function preparePythonFunctionsEnvironment(functionsDir: string): Promise<NodeJS.ProcessEnv> {
|
|
465
472
|
const projectRoot = getPythonProjectRoot(functionsDir);
|
|
466
473
|
const { command: uvCommand, env: uvEnv } = await resolveProjectLocalUvCommand(projectRoot);
|
|
467
|
-
const { venvDir
|
|
468
|
-
const
|
|
469
|
-
cwd: functionsDir,
|
|
470
|
-
env: uvEnv,
|
|
471
|
-
shell: false,
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
if (!hasUsableVirtualEnv) {
|
|
475
|
-
const venvArgs = buildUvVenvArgs('.venv');
|
|
476
|
-
if (fs.existsSync(venvDir)) {
|
|
477
|
-
venvArgs.push('--clear');
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
console.log('📦 Creating Python virtual environment with uv...');
|
|
481
|
-
await runCommand(uvCommand, venvArgs, functionsDir, 'python virtual environment setup', uvEnv, false);
|
|
482
|
-
}
|
|
474
|
+
const { venvDir } = getPythonVirtualEnvPaths(functionsDir);
|
|
475
|
+
const pythonExecutable = await ensureUvVirtualEnvironment(uvCommand, venvDir, functionsDir, uvEnv);
|
|
483
476
|
|
|
484
477
|
console.log('📦 Installing Python Azure Functions dependencies with uv...');
|
|
485
478
|
await runCommand(
|
|
@@ -522,23 +515,48 @@ async function checkCosmosDBEmulator(): Promise<boolean> {
|
|
|
522
515
|
});
|
|
523
516
|
}
|
|
524
517
|
|
|
518
|
+
export async function assertDevPortAvailable(port: number, host: string): Promise<void> {
|
|
519
|
+
await new Promise<void>((resolve, reject) => {
|
|
520
|
+
const server = net.createServer();
|
|
521
|
+
server.once('error', error => reject(new Error(`Development port ${port} on ${host} is unavailable: ${error.message}`)));
|
|
522
|
+
server.listen({ port, host, exclusive: true }, () => server.close(error => error ? reject(error) : resolve()));
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function stopDevProcesses(processes: ChildProcess[]): void {
|
|
527
|
+
const active = processes.filter(child => child.pid && !child.killed && child.exitCode === null && child.signalCode === null);
|
|
528
|
+
if (process.platform === 'win32' && active.length) {
|
|
529
|
+
// Windows shell termination alone leaves Vite, Core Tools, and SWA running.
|
|
530
|
+
const ids = active.map(child => child.pid).join(',');
|
|
531
|
+
const result = spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command',
|
|
532
|
+
`$all = @(Get-CimInstance Win32_Process); function Stop-OwnedTree([int]$id) { foreach ($child in ($all | Where-Object ParentProcessId -eq $id)) { Stop-OwnedTree $child.ProcessId }; Stop-Process -Id $id -Force -ErrorAction SilentlyContinue }; foreach ($id in @(${ids})) { Stop-OwnedTree $id }`,
|
|
533
|
+
], { windowsHide: true, encoding: 'utf8' });
|
|
534
|
+
if (result.error || result.status !== 0) {
|
|
535
|
+
console.error('❌ Could not stop all development subprocesses:', result.error?.message || result.stderr);
|
|
536
|
+
}
|
|
537
|
+
} else {
|
|
538
|
+
for (const child of active) child.kill();
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
525
542
|
export function buildDevCommand(
|
|
526
543
|
runDevEnvironment: (options: DevOptions) => Promise<void> = startDevEnvironment,
|
|
527
544
|
verifyProject: (commandName: string, projectRoot?: string) => void = ensureSwallowKitProject
|
|
528
545
|
): Command {
|
|
529
546
|
return new Command()
|
|
530
547
|
.name('dev')
|
|
531
|
-
.description('Start SwallowKit
|
|
532
|
-
.option('-p, --port <port>', '
|
|
548
|
+
.description('Start SwallowKit frontend, BFF, and standalone Azure Functions')
|
|
549
|
+
.option('-p, --port <port>', 'Frontend dev server port', '3000')
|
|
533
550
|
.option('-f, --functions-port <port>', 'Azure Functions port', '7071')
|
|
551
|
+
.option('--bff-port <port>', 'React managed BFF Functions port', '7073')
|
|
534
552
|
.option('--host <host>', 'Host name', 'localhost')
|
|
535
553
|
.option('--open', 'Open browser automatically', false)
|
|
536
554
|
.option('--verbose', 'Show verbose logs', false)
|
|
537
555
|
.option('--no-functions', 'Skip Azure Functions startup')
|
|
538
556
|
.option('--seed-env <environment>', 'Replace Cosmos DB Emulator data from dev-seeds/<environment> before startup')
|
|
539
557
|
.option('--mock-connectors', 'Start mock server for connector models (serves Zod-generated data)', false)
|
|
540
|
-
.option('--swa-port <port>', 'SWA authentication emulator port', '4280')
|
|
541
|
-
.option('--no-swa', 'Skip
|
|
558
|
+
.option('--swa-port <port>', 'SWA routing/authentication emulator port', '4280')
|
|
559
|
+
.option('--no-swa', 'Skip SWA routing/authentication emulation (React direct access has no same-origin API)')
|
|
542
560
|
.action(async (options: ParsedDevActionOptions) => {
|
|
543
561
|
const normalizedOptions = normalizeParsedDevOptions(options);
|
|
544
562
|
|
|
@@ -656,19 +674,24 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
656
674
|
const port = options.port || '3000';
|
|
657
675
|
const functionsPort = options.functionsPort || '7071';
|
|
658
676
|
const swaPort = options.swaPort || '4280';
|
|
677
|
+
const bffPort = options.bffPort || '7073';
|
|
659
678
|
|
|
660
679
|
// Detect package manager from project lockfile
|
|
661
680
|
const pm = detectFromProject();
|
|
662
681
|
const pmCmd = getCommands(pm);
|
|
663
682
|
const backendLanguage = getBackendLanguage();
|
|
683
|
+
const react = getFrontendFramework() === 'react';
|
|
684
|
+
const frontendLabel = react ? 'React/Vite' : 'Next.js';
|
|
664
685
|
const authConfig = getAuthConfig();
|
|
665
686
|
const normalizedAuth = normalizeAuthConfig(authConfig);
|
|
666
|
-
const
|
|
687
|
+
const authSchemes = Object.values(normalizedAuth?.schemes ?? {});
|
|
688
|
+
const swaScheme = authSchemes.find(scheme => scheme.provider === 'swa');
|
|
689
|
+
const useSwaEmulator = (react || Boolean(swaScheme)) && !options.noSwa;
|
|
667
690
|
const swaCommand = useSwaEmulator ? await resolveSwaCliCommand(process.cwd()) : null;
|
|
668
691
|
|
|
669
692
|
if (useSwaEmulator && !swaCommand) {
|
|
670
693
|
const installCommand = getSwaCliInstallCommand(pm);
|
|
671
|
-
console.error('❌ SWA authentication requires Azure Static Web Apps CLI for local development.');
|
|
694
|
+
console.error('❌ SWA routing/authentication requires Azure Static Web Apps CLI for local development.');
|
|
672
695
|
console.error(' Install it in this project and retry:');
|
|
673
696
|
console.error(`\n ${installCommand}\n`);
|
|
674
697
|
process.exitCode = 1;
|
|
@@ -684,6 +707,14 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
684
707
|
const functionsBaseUrl = buildFunctionsBaseUrl(options.host, functionsPort);
|
|
685
708
|
let functionsReadinessPromise: Promise<boolean> | null = null;
|
|
686
709
|
let functionsReady = !!options.noFunctions;
|
|
710
|
+
let reactDevConfigDir: string | undefined;
|
|
711
|
+
const cleanupReactDevConfig = () => {
|
|
712
|
+
if (reactDevConfigDir) {
|
|
713
|
+
fs.rmSync(reactDevConfigDir, { recursive: true, force: true });
|
|
714
|
+
reactDevConfigDir = undefined;
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
process.once('exit', cleanupReactDevConfig);
|
|
687
718
|
|
|
688
719
|
// Cleanup processes on Ctrl+C
|
|
689
720
|
process.on('SIGINT', async () => {
|
|
@@ -707,15 +738,22 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
707
738
|
if (mockServer) {
|
|
708
739
|
await mockServer.stop();
|
|
709
740
|
}
|
|
710
|
-
processes
|
|
711
|
-
if (proc && !proc.killed) {
|
|
712
|
-
proc.kill();
|
|
713
|
-
}
|
|
714
|
-
});
|
|
741
|
+
stopDevProcesses(processes);
|
|
715
742
|
process.exit(0);
|
|
716
743
|
});
|
|
717
744
|
|
|
718
745
|
try {
|
|
746
|
+
if (react) {
|
|
747
|
+
const ports = [port, bffPort, ...(!options.noFunctions ? [functionsPort] : []), ...(useSwaEmulator ? [swaPort] : []), ...(options.mockConnectors ? [String(Number(functionsPort) + 1)] : [])];
|
|
748
|
+
if (ports.some(value => !/^\d+$/.test(value) || Number(value) < 1 || Number(value) > 65535) || new Set(ports).size !== ports.length) {
|
|
749
|
+
throw new Error('Frontend, BFF, standalone Functions, mock, and SWA ports must be valid and distinct');
|
|
750
|
+
}
|
|
751
|
+
for (const value of ports) await assertDevPortAvailable(Number(value), options.host || 'localhost');
|
|
752
|
+
const coreToolsVersion = await resolveInstalledCoreToolsVersion();
|
|
753
|
+
if (!coreToolsVersion || compareVersionNumbers(coreToolsVersion, '4.0.0') < 0) {
|
|
754
|
+
throw new Error('React BFF requires Azure Functions Core Tools v4. Install it explicitly before running dev.');
|
|
755
|
+
}
|
|
756
|
+
}
|
|
719
757
|
// 1. Check for Next.js project
|
|
720
758
|
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
721
759
|
const nextConfigPathJs = path.join(process.cwd(), 'next.config.js');
|
|
@@ -724,11 +762,11 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
724
762
|
|
|
725
763
|
if (!fs.existsSync(packageJsonPath)) {
|
|
726
764
|
console.log('❌ package.json not found.');
|
|
727
|
-
console.log('💡 Please run this command in the root directory of a
|
|
765
|
+
console.log('💡 Please run this command in the root directory of a SwallowKit project.');
|
|
728
766
|
process.exit(1);
|
|
729
767
|
}
|
|
730
768
|
|
|
731
|
-
if (!fs.existsSync(nextConfigPathJs) && !fs.existsSync(nextConfigPathTs) && !fs.existsSync(nextConfigPathMjs)) {
|
|
769
|
+
if (!react && !fs.existsSync(nextConfigPathJs) && !fs.existsSync(nextConfigPathTs) && !fs.existsSync(nextConfigPathMjs)) {
|
|
732
770
|
console.log('⚠️ next.config file not found. Is this a Next.js project?');
|
|
733
771
|
}
|
|
734
772
|
|
|
@@ -895,7 +933,7 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
895
933
|
|
|
896
934
|
// Azure Functions を起動
|
|
897
935
|
const functionsCommand = functionsCoreToolsCommand ?? buildFunctionsCoreToolsCommand(backendLanguage, installedCoreToolsVersion);
|
|
898
|
-
const funcProcess = spawn(functionsCommand.command, [...functionsCommand.argsPrefix, ...buildFunctionsStartArgs(functionsPort)], {
|
|
936
|
+
const funcProcess = spawn(functionsCommand.command, [...functionsCommand.argsPrefix, ...buildFunctionsStartArgs(functionsPort, backendLanguage)], {
|
|
899
937
|
cwd: functionsDir,
|
|
900
938
|
shell: true,
|
|
901
939
|
stdio: 'pipe', // Always pipe to capture output
|
|
@@ -943,6 +981,8 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
943
981
|
console.error('⚠️ Azure Functions startup error:', error.message);
|
|
944
982
|
console.log('💡 Please ensure Azure Functions Core Tools is installed');
|
|
945
983
|
console.log(` ${pmCmd.addGlobal} azure-functions-core-tools@4`);
|
|
984
|
+
stopDevProcesses(processes);
|
|
985
|
+
process.exit(1);
|
|
946
986
|
});
|
|
947
987
|
|
|
948
988
|
funcProcess.on('close', (code) => {
|
|
@@ -953,6 +993,8 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
953
993
|
console.log(' Reinstall a matching Core Tools v4 package (Windows users should prefer the official x64/x86 MSI for their machine).');
|
|
954
994
|
console.log(' SwallowKit local Python dev uses functions/.venv and requirements.txt, but Core Tools still needs its own bundled Python worker.');
|
|
955
995
|
}
|
|
996
|
+
stopDevProcesses(processes);
|
|
997
|
+
process.exit(code || 1);
|
|
956
998
|
}
|
|
957
999
|
});
|
|
958
1000
|
|
|
@@ -963,14 +1005,14 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
963
1005
|
);
|
|
964
1006
|
} else if (!hasFunctions) {
|
|
965
1007
|
console.log('');
|
|
966
|
-
console.log(
|
|
1008
|
+
console.log(`ℹ️ functions/ directory not found. Starting ${frontendLabel}${react ? ' and managed BFF' : ''} only.`);
|
|
967
1009
|
} else if (options.noFunctions) {
|
|
968
1010
|
console.log('');
|
|
969
1011
|
console.log('ℹ️ --no-functions specified. Skipping Azure Functions.');
|
|
970
1012
|
}
|
|
971
1013
|
|
|
972
1014
|
console.log('');
|
|
973
|
-
console.log(
|
|
1015
|
+
console.log(`🚀 Starting ${frontendLabel} development server...`);
|
|
974
1016
|
|
|
975
1017
|
// Mock connector server — start proxy if --mock-connectors is enabled
|
|
976
1018
|
let bffTargetPort = functionsPort;
|
|
@@ -1039,7 +1081,17 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
1039
1081
|
}
|
|
1040
1082
|
|
|
1041
1083
|
// 5. Start Next.js development server
|
|
1042
|
-
|
|
1084
|
+
let reactDevConfigPath: string | undefined;
|
|
1085
|
+
if (react) {
|
|
1086
|
+
const stateDir = path.join(process.cwd(), '.swallowkit');
|
|
1087
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
1088
|
+
reactDevConfigDir = fs.mkdtempSync(path.join(stateDir, 'vite-dev-'));
|
|
1089
|
+
reactDevConfigPath = path.join(reactDevConfigDir, 'config.mjs');
|
|
1090
|
+
fs.writeFileSync(reactDevConfigPath, buildReactDevConfigSource());
|
|
1091
|
+
}
|
|
1092
|
+
const nextArgs = react
|
|
1093
|
+
? buildReactDevArgs(pm, port, options.host, reactDevConfigPath && path.relative(process.cwd(), reactDevConfigPath))
|
|
1094
|
+
: buildNextDevArgs(pm, port);
|
|
1043
1095
|
|
|
1044
1096
|
// Ensure .env.local points to bffTargetPort so Next.js reads the correct backend URL.
|
|
1045
1097
|
// When --mock-connectors is active, bffTargetPort = mock port (7072); otherwise = Functions port (7071).
|
|
@@ -1067,6 +1119,25 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
1067
1119
|
FUNCTIONS_BASE_URL: bffTargetUrl,
|
|
1068
1120
|
};
|
|
1069
1121
|
|
|
1122
|
+
if (react) {
|
|
1123
|
+
await runCommand(pm, ['run', 'build'], path.join(process.cwd(), 'shared'), 'Shared schema build');
|
|
1124
|
+
const bffDir = path.join(process.cwd(), 'bff');
|
|
1125
|
+
await runCommand(pm, ['run', 'build'], bffDir, 'Managed BFF build');
|
|
1126
|
+
const bffProcess = spawn('func', buildFunctionsStartArgs(bffPort), {
|
|
1127
|
+
cwd: bffDir, shell: true, stdio: 'inherit', env: nextEnv,
|
|
1128
|
+
});
|
|
1129
|
+
processes.push(bffProcess);
|
|
1130
|
+
bffProcess.on('error', error => console.error('❌ Managed BFF startup error:', error.message));
|
|
1131
|
+
bffProcess.on('close', code => {
|
|
1132
|
+
console.error(`⏹️ Managed BFF exited (exit code: ${code})`);
|
|
1133
|
+
stopDevProcesses(processes);
|
|
1134
|
+
process.exit(code || 1);
|
|
1135
|
+
});
|
|
1136
|
+
if (!await waitForHttpServerReady(buildFunctionsBaseUrl(options.host, bffPort))) {
|
|
1137
|
+
throw new Error(`Managed BFF did not become ready on port ${bffPort}`);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1070
1141
|
const nextProcess = spawn(pm === 'pnpm' ? 'pnpm' : 'npx', nextArgs, {
|
|
1071
1142
|
cwd: process.cwd(),
|
|
1072
1143
|
shell: true,
|
|
@@ -1077,23 +1148,20 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
1077
1148
|
processes.push(nextProcess);
|
|
1078
1149
|
|
|
1079
1150
|
nextProcess.on('error', (error) => {
|
|
1080
|
-
console.error(
|
|
1151
|
+
console.error(`❌ ${frontendLabel} startup error:`, error.message);
|
|
1152
|
+
stopDevProcesses(processes);
|
|
1081
1153
|
process.exit(1);
|
|
1082
1154
|
});
|
|
1083
1155
|
|
|
1084
1156
|
nextProcess.on('close', (code) => {
|
|
1085
1157
|
if (code !== 0) {
|
|
1086
|
-
console.log(`\n⏹️
|
|
1158
|
+
console.log(`\n⏹️ ${frontendLabel} exited (exit code: ${code})`);
|
|
1087
1159
|
}
|
|
1088
|
-
// Exit all processes when
|
|
1160
|
+
// Exit all owned processes when the frontend exits.
|
|
1089
1161
|
if (mockServer) {
|
|
1090
1162
|
mockServer.stop().catch(() => {});
|
|
1091
1163
|
}
|
|
1092
|
-
processes
|
|
1093
|
-
if (proc && !proc.killed) {
|
|
1094
|
-
proc.kill();
|
|
1095
|
-
}
|
|
1096
|
-
});
|
|
1164
|
+
stopDevProcesses(processes);
|
|
1097
1165
|
process.exit(code || 0);
|
|
1098
1166
|
});
|
|
1099
1167
|
|
|
@@ -1110,14 +1178,14 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
1110
1178
|
let swaReady = false;
|
|
1111
1179
|
if (useSwaEmulator && swaCommand) {
|
|
1112
1180
|
const nextUrl = `http://${options.host || 'localhost'}:${port}`;
|
|
1113
|
-
console.log(`⏳ Waiting for
|
|
1181
|
+
console.log(`⏳ Waiting for ${frontendLabel} before starting the SWA emulator at ${nextUrl}...`);
|
|
1114
1182
|
const nextReady = await waitForHttpServerReady(nextUrl);
|
|
1115
1183
|
if (!nextReady) {
|
|
1116
|
-
throw new Error(
|
|
1184
|
+
throw new Error(`${frontendLabel} did not become ready at ${nextUrl}`);
|
|
1117
1185
|
}
|
|
1118
1186
|
|
|
1119
|
-
console.log(`🔐 Starting SWA authentication emulator (port: ${swaPort})...`);
|
|
1120
|
-
const swaProcess = spawn(swaCommand, buildSwaStartArgs(options.host, port, swaPort), {
|
|
1187
|
+
console.log(`🔐 Starting SWA routing/authentication emulator (port: ${swaPort})...`);
|
|
1188
|
+
const swaProcess = spawn(swaCommand, buildSwaStartArgs(options.host, port, swaPort, react ? bffPort : undefined), {
|
|
1121
1189
|
cwd: process.cwd(),
|
|
1122
1190
|
shell: true,
|
|
1123
1191
|
stdio: 'inherit',
|
|
@@ -1131,7 +1199,7 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
1131
1199
|
const swaUrl = `http://${options.host || 'localhost'}:${swaPort}`;
|
|
1132
1200
|
swaReady = await waitForHttpServerReady(swaUrl);
|
|
1133
1201
|
if (!swaReady) {
|
|
1134
|
-
throw new Error(`SWA authentication emulator did not become ready at ${swaUrl}`);
|
|
1202
|
+
throw new Error(`SWA routing/authentication emulator did not become ready at ${swaUrl}`);
|
|
1135
1203
|
}
|
|
1136
1204
|
}
|
|
1137
1205
|
|
|
@@ -1140,18 +1208,25 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
1140
1208
|
console.log('');
|
|
1141
1209
|
if (useSwaEmulator) {
|
|
1142
1210
|
const swaUrl = `http://${options.host || 'localhost'}:${swaPort}`;
|
|
1143
|
-
console.log('🔐 SWA
|
|
1211
|
+
console.log('🔐 SWA app (same-origin frontend and BFF):');
|
|
1144
1212
|
console.log(` ${swaUrl}`);
|
|
1145
1213
|
console.log('');
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1214
|
+
if (swaScheme) {
|
|
1215
|
+
const provider = swaScheme.swa?.allowedProviders?.[0] || 'aad';
|
|
1216
|
+
console.log('🔑 Local SWA sign-in:');
|
|
1217
|
+
console.log(` ${swaUrl}/.auth/login/${provider}`);
|
|
1218
|
+
console.log(' Enter any username and select Login.');
|
|
1219
|
+
console.log(' The "authenticated" role is added automatically.');
|
|
1220
|
+
console.log('');
|
|
1221
|
+
}
|
|
1222
|
+
if (authSchemes.some(scheme => scheme.provider === 'custom-jwt')) {
|
|
1223
|
+
console.log(`🔑 Application sign-in: ${swaUrl}/login`);
|
|
1224
|
+
console.log('');
|
|
1225
|
+
}
|
|
1226
|
+
console.log(`📱 ${frontendLabel} direct access (SWA routing/authentication is not emulated):`);
|
|
1152
1227
|
console.log(` http://${options.host || 'localhost'}:${port}`);
|
|
1153
1228
|
} else {
|
|
1154
|
-
console.log(`📱
|
|
1229
|
+
console.log(`📱 ${frontendLabel}: http://${options.host || 'localhost'}:${port}`);
|
|
1155
1230
|
}
|
|
1156
1231
|
if (hasFunctions && !options.noFunctions) {
|
|
1157
1232
|
console.log(`${functionsReady ? '⚡ Azure Functions' : '⏳ Azure Functions (starting)'}: ${functionsBaseUrl}`);
|
|
@@ -1161,7 +1236,7 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
1161
1236
|
}
|
|
1162
1237
|
console.log('');
|
|
1163
1238
|
if (hasFunctions && !options.noFunctions && functionsReady) {
|
|
1164
|
-
console.log(
|
|
1239
|
+
console.log(`💡 Azure Functions and ${react ? 'managed Functions' : 'Next.js'} BFF are connected`);
|
|
1165
1240
|
} else if (hasFunctions && !options.noFunctions) {
|
|
1166
1241
|
console.log('💡 Azure Functions is still warming up; BFF routes can fail until the backend responds.');
|
|
1167
1242
|
}
|
|
@@ -1171,6 +1246,7 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
1171
1246
|
if (swaReady) {
|
|
1172
1247
|
console.log('💡 Open the app through the SWA port when testing authenticated pages and CRUD APIs.');
|
|
1173
1248
|
}
|
|
1249
|
+
if (react) console.log(`🔀 Managed BFF: ${buildFunctionsBaseUrl(options.host, bffPort)} (not the standalone Functions endpoint)`);
|
|
1174
1250
|
console.log('');
|
|
1175
1251
|
|
|
1176
1252
|
if (options.open) {
|
|
@@ -1189,11 +1265,7 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
1189
1265
|
|
|
1190
1266
|
} catch (error) {
|
|
1191
1267
|
console.error('❌ Failed to start development environment:', error instanceof Error ? error.message : error);
|
|
1192
|
-
processes
|
|
1193
|
-
if (proc && !proc.killed) {
|
|
1194
|
-
proc.kill();
|
|
1195
|
-
}
|
|
1196
|
-
});
|
|
1268
|
+
stopDevProcesses(processes);
|
|
1197
1269
|
process.exit(1);
|
|
1198
1270
|
}
|
|
1199
1271
|
}
|