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