swallowkit 1.0.0-beta.9 → 1.0.1
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 +87 -181
- package/README.md +87 -238
- 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 +164 -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 +20 -82
- package/dist/cli/commands/create-model.js.map +1 -1
- package/dist/cli/commands/dev-seeds.d.ts +27 -0
- package/dist/cli/commands/dev-seeds.d.ts.map +1 -1
- package/dist/cli/commands/dev-seeds.js +188 -6
- package/dist/cli/commands/dev-seeds.js.map +1 -1
- package/dist/cli/commands/dev.d.ts +30 -0
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +495 -129
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/init.d.ts +69 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +788 -766
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +24 -3
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +408 -189
- 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 +335 -58
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config.d.ts +13 -3
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +151 -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 +160 -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 +190 -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 +4 -3
- package/dist/core/scaffold/functions-generator.d.ts.map +1 -1
- package/dist/core/scaffold/functions-generator.js +405 -134
- package/dist/core/scaffold/functions-generator.js.map +1 -1
- package/dist/core/scaffold/model-parser.d.ts +20 -0
- package/dist/core/scaffold/model-parser.d.ts.map +1 -1
- package/dist/core/scaffold/model-parser.js +229 -78
- 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 +152 -8
- package/dist/core/scaffold/nextjs-generator.js.map +1 -1
- 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/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 +105 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/package-manager.d.ts +7 -11
- package/dist/utils/package-manager.d.ts.map +1 -1
- package/dist/utils/package-manager.js +42 -17
- 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 +15 -17
- package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +716 -22
- package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +19 -7
- package/src/__tests__/__snapshots__/ui-generator.test.ts.snap +20 -12
- package/src/__tests__/agent-instructions.test.ts +237 -0
- package/src/__tests__/agent-loop-phase34.test.ts +619 -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 +176 -3
- package/src/__tests__/connector-functions-generator.test.ts +288 -0
- package/src/__tests__/connector-mock-server.test.ts +459 -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 +124 -0
- package/src/__tests__/dev.test.ts +254 -0
- package/src/__tests__/file-session.test.ts +98 -0
- package/src/__tests__/fixtures.ts +101 -0
- package/src/__tests__/functions-generator.test.ts +137 -1
- package/src/__tests__/init.test.ts +366 -2
- 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 +142 -0
- package/src/__tests__/multi-auth.test.ts +160 -0
- package/src/__tests__/nextjs-generator.test.ts +8 -0
- package/src/__tests__/package-manager.test.ts +59 -4
- package/src/__tests__/plan-apply.test.ts +499 -0
- package/src/__tests__/python-uv.test.ts +48 -0
- package/src/__tests__/scaffold.test.ts +287 -24
- package/src/__tests__/tsconfig.json +15 -0
- package/src/__tests__/ui-generator.test.ts +104 -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 +159 -0
- package/src/cli/commands/cosmos-worktree-isolation.ts +130 -0
- package/src/cli/commands/create-model.ts +21 -100
- package/src/cli/commands/dev-seeds.ts +278 -7
- package/src/cli/commands/dev.ts +607 -160
- package/src/cli/commands/init.ts +851 -786
- package/src/cli/commands/scaffold.ts +531 -236
- package/src/cli/index.ts +338 -60
- package/src/core/config.ts +145 -9
- 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 +504 -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 +203 -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 +432 -134
- package/src/core/scaffold/model-parser.ts +244 -90
- package/src/core/scaffold/native-schema-generator.ts +904 -0
- package/src/core/scaffold/nextjs-generator.ts +174 -9
- package/src/core/scaffold/ui-generator.ts +191 -36
- package/src/core/verify/index.ts +468 -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 +137 -0
- package/src/utils/package-manager.ts +41 -19
- package/src/utils/python-uv.ts +96 -0
- package/src/version.ts +13 -0
package/dist/cli/commands/dev.js
CHANGED
|
@@ -35,25 +35,119 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.devCommand = void 0;
|
|
37
37
|
exports.buildFunctionsStartArgs = buildFunctionsStartArgs;
|
|
38
|
+
exports.parseCoreToolsVersion = parseCoreToolsVersion;
|
|
39
|
+
exports.compareVersionNumbers = compareVersionNumbers;
|
|
40
|
+
exports.buildFunctionsCoreToolsCommand = buildFunctionsCoreToolsCommand;
|
|
38
41
|
exports.buildNextDevArgs = buildNextDevArgs;
|
|
42
|
+
exports.buildFunctionsBaseUrl = buildFunctionsBaseUrl;
|
|
43
|
+
exports.buildSwaStartArgs = buildSwaStartArgs;
|
|
44
|
+
exports.getSwaCliInstallCommand = getSwaCliInstallCommand;
|
|
45
|
+
exports.getFunctionsReadinessTimeoutMs = getFunctionsReadinessTimeoutMs;
|
|
46
|
+
exports.waitForHttpServerReady = waitForHttpServerReady;
|
|
39
47
|
exports.getPythonVirtualEnvPaths = getPythonVirtualEnvPaths;
|
|
48
|
+
exports.getCSharpFunctionsBuildArtifactPaths = getCSharpFunctionsBuildArtifactPaths;
|
|
40
49
|
exports.buildPythonFunctionsEnv = buildPythonFunctionsEnv;
|
|
50
|
+
exports.buildFunctionsCosmosEnvironment = buildFunctionsCosmosEnvironment;
|
|
51
|
+
exports.buildDevCommand = buildDevCommand;
|
|
41
52
|
const commander_1 = require("commander");
|
|
42
53
|
const child_process_1 = require("child_process");
|
|
54
|
+
const http = __importStar(require("http"));
|
|
55
|
+
const https = __importStar(require("https"));
|
|
43
56
|
const path = __importStar(require("path"));
|
|
44
57
|
const fs = __importStar(require("fs"));
|
|
45
58
|
const os = __importStar(require("os"));
|
|
59
|
+
const net = __importStar(require("net"));
|
|
46
60
|
const cosmos_1 = require("@azure/cosmos");
|
|
47
61
|
const config_1 = require("../../core/config");
|
|
48
62
|
const dev_seeds_1 = require("./dev-seeds");
|
|
49
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
|
+
}
|
|
50
75
|
function buildFunctionsStartArgs(functionsPort) {
|
|
51
76
|
return ['start', '--port', functionsPort];
|
|
52
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
|
+
}
|
|
53
113
|
function buildNextDevArgs(pm, port) {
|
|
54
114
|
const baseArgs = ['next', 'dev', '--port', port, '--webpack'];
|
|
55
115
|
return pm === 'pnpm' ? ['exec', ...baseArgs] : baseArgs;
|
|
56
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
|
+
}
|
|
57
151
|
function getPythonVirtualEnvPaths(functionsDir) {
|
|
58
152
|
const venvDir = path.join(functionsDir, '.venv');
|
|
59
153
|
const binDir = process.platform === 'win32'
|
|
@@ -64,6 +158,12 @@ function getPythonVirtualEnvPaths(functionsDir) {
|
|
|
64
158
|
: path.join(binDir, 'python');
|
|
65
159
|
return { venvDir, binDir, pythonExecutable };
|
|
66
160
|
}
|
|
161
|
+
function getCSharpFunctionsBuildArtifactPaths(functionsDir) {
|
|
162
|
+
return [
|
|
163
|
+
path.join(functionsDir, 'bin'),
|
|
164
|
+
path.join(functionsDir, 'obj'),
|
|
165
|
+
];
|
|
166
|
+
}
|
|
67
167
|
function buildPythonFunctionsEnv(baseEnv, functionsDir) {
|
|
68
168
|
const { venvDir, binDir, pythonExecutable } = getPythonVirtualEnvPaths(functionsDir);
|
|
69
169
|
const pathKey = getPathEnvKey(baseEnv);
|
|
@@ -75,6 +175,12 @@ function buildPythonFunctionsEnv(baseEnv, functionsDir) {
|
|
|
75
175
|
languageWorkers__python__defaultExecutablePath: pythonExecutable,
|
|
76
176
|
};
|
|
77
177
|
}
|
|
178
|
+
function buildFunctionsCosmosEnvironment(baseEnv, databaseName) {
|
|
179
|
+
return {
|
|
180
|
+
...baseEnv,
|
|
181
|
+
COSMOS_DB_DATABASE_NAME: databaseName,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
78
184
|
function getPathEnvKey(env) {
|
|
79
185
|
return Object.keys(env).find((key) => key.toUpperCase() === 'PATH') || 'PATH';
|
|
80
186
|
}
|
|
@@ -92,10 +198,12 @@ function prependToPathEnv(env, entry) {
|
|
|
92
198
|
async function checkCoreTools() {
|
|
93
199
|
return checkCommand('func', ['--version']);
|
|
94
200
|
}
|
|
95
|
-
async function checkCommand(command, args = ['--version']) {
|
|
201
|
+
async function checkCommand(command, args = ['--version'], options) {
|
|
96
202
|
return new Promise((resolve) => {
|
|
97
203
|
const checkProcess = (0, child_process_1.spawn)(command, args, {
|
|
98
|
-
|
|
204
|
+
cwd: options?.cwd,
|
|
205
|
+
env: options?.env ?? process.env,
|
|
206
|
+
shell: options?.shell ?? true,
|
|
99
207
|
stdio: 'pipe',
|
|
100
208
|
});
|
|
101
209
|
checkProcess.on('close', (code) => {
|
|
@@ -106,22 +214,22 @@ async function checkCommand(command, args = ['--version']) {
|
|
|
106
214
|
});
|
|
107
215
|
});
|
|
108
216
|
}
|
|
109
|
-
async function
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
{ command: 'python', argsPrefix: [], label: 'python' },
|
|
118
|
-
];
|
|
119
|
-
for (const candidate of candidates) {
|
|
120
|
-
if (await checkCommand(candidate.command, [...candidate.argsPrefix, '--version'])) {
|
|
121
|
-
return candidate;
|
|
122
|
-
}
|
|
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 };
|
|
123
225
|
}
|
|
124
|
-
|
|
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 };
|
|
125
233
|
}
|
|
126
234
|
async function getCommandPath(command) {
|
|
127
235
|
const locator = process.platform === 'win32' ? 'where' : 'which';
|
|
@@ -132,6 +240,17 @@ async function getCommandPath(command) {
|
|
|
132
240
|
.find(Boolean);
|
|
133
241
|
return firstLine || null;
|
|
134
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
|
+
}
|
|
135
254
|
async function captureCommandOutput(command, args, cwd, env) {
|
|
136
255
|
return new Promise((resolve, reject) => {
|
|
137
256
|
const child = (0, child_process_1.spawn)(command, args, {
|
|
@@ -159,6 +278,35 @@ async function captureCommandOutput(command, args, cwd, env) {
|
|
|
159
278
|
child.on('error', reject);
|
|
160
279
|
});
|
|
161
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
|
+
}
|
|
162
310
|
async function resolvePythonRuntimeDetails(functionsDir, env) {
|
|
163
311
|
const { pythonExecutable } = getPythonVirtualEnvPaths(functionsDir);
|
|
164
312
|
const output = await captureCommandOutput(pythonExecutable, [
|
|
@@ -207,28 +355,25 @@ async function bridgePythonCoreToolsForWindowsArm64(functionsDir, env) {
|
|
|
207
355
|
return prependToPathEnv(env, patchedRoot);
|
|
208
356
|
}
|
|
209
357
|
async function preparePythonFunctionsEnvironment(functionsDir) {
|
|
210
|
-
const
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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');
|
|
221
370
|
}
|
|
371
|
+
console.log('📦 Creating Python virtual environment with uv...');
|
|
372
|
+
await runCommand(uvCommand, venvArgs, functionsDir, 'python virtual environment setup', uvEnv, false);
|
|
222
373
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
await runCommand('uv', ['pip', 'install', '--python', pythonExecutable, '-r', 'requirements.txt'], functionsDir, 'python dependency installation', pythonEnv);
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
await runCommand('python', ['-m', 'pip', 'install', '--upgrade', 'pip'], functionsDir, 'python pip upgrade', pythonEnv);
|
|
230
|
-
await runCommand('python', ['-m', 'pip', 'install', '-r', 'requirements.txt'], functionsDir, 'python dependency installation', pythonEnv);
|
|
231
|
-
}
|
|
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);
|
|
232
377
|
return bridgePythonCoreToolsForWindowsArm64(functionsDir, pythonEnv);
|
|
233
378
|
}
|
|
234
379
|
/**
|
|
@@ -236,7 +381,6 @@ async function preparePythonFunctionsEnvironment(functionsDir) {
|
|
|
236
381
|
*/
|
|
237
382
|
async function checkCosmosDBEmulator() {
|
|
238
383
|
return new Promise((resolve) => {
|
|
239
|
-
const net = require('net');
|
|
240
384
|
const socket = new net.Socket();
|
|
241
385
|
const timeout = setTimeout(() => {
|
|
242
386
|
socket.destroy();
|
|
@@ -254,53 +398,56 @@ async function checkCosmosDBEmulator() {
|
|
|
254
398
|
socket.connect(8081, 'localhost');
|
|
255
399
|
});
|
|
256
400
|
}
|
|
257
|
-
|
|
258
|
-
.
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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();
|
|
276
427
|
async function initializeCosmosDB(databaseName) {
|
|
277
428
|
try {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
console.log('⚠️ CosmosDBConnection not found in local.settings.json. Skipping Cosmos DB initialization.');
|
|
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
|
+
}
|
|
290
440
|
return null;
|
|
291
441
|
}
|
|
292
442
|
console.log('🗄️ Initializing Cosmos DB...');
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
console.log('⚠️ Invalid CosmosDB connection string format.');
|
|
298
|
-
return null;
|
|
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');
|
|
299
447
|
}
|
|
300
|
-
const endpoint = endpointMatch[1];
|
|
301
448
|
const client = new cosmos_1.CosmosClient({
|
|
302
449
|
endpoint: endpoint,
|
|
303
|
-
key
|
|
450
|
+
key,
|
|
304
451
|
});
|
|
305
452
|
// Create database if not exists
|
|
306
453
|
const { database } = await client.databases.createIfNotExists({ id: dbName });
|
|
@@ -311,11 +458,11 @@ async function initializeCosmosDB(databaseName) {
|
|
|
311
458
|
// Try creating container with full partition key definition first
|
|
312
459
|
let containerCreated = false;
|
|
313
460
|
try {
|
|
314
|
-
console.log(`🔧 Creating container "${containerName}" with partition key
|
|
461
|
+
console.log(`🔧 Creating container "${containerName}" with partition key ${model.partitionKey}...`);
|
|
315
462
|
const containerResponse = await database.containers.createIfNotExists({
|
|
316
463
|
id: containerName,
|
|
317
464
|
partitionKey: {
|
|
318
|
-
paths: [
|
|
465
|
+
paths: [model.partitionKey],
|
|
319
466
|
kind: cosmos_1.PartitionKeyKind.Hash,
|
|
320
467
|
version: 2
|
|
321
468
|
}
|
|
@@ -334,7 +481,7 @@ async function initializeCosmosDB(databaseName) {
|
|
|
334
481
|
const containerResponse = await database.containers.createIfNotExists({
|
|
335
482
|
id: containerName,
|
|
336
483
|
partitionKey: {
|
|
337
|
-
paths: [
|
|
484
|
+
paths: [model.partitionKey]
|
|
338
485
|
}
|
|
339
486
|
});
|
|
340
487
|
console.log(`✅ Container "${containerName}" ready (status: ${containerResponse.statusCode})`);
|
|
@@ -350,7 +497,7 @@ async function initializeCosmosDB(databaseName) {
|
|
|
350
497
|
}
|
|
351
498
|
}
|
|
352
499
|
console.log('✅ Cosmos DB initialization complete\n');
|
|
353
|
-
return { endpoint, key
|
|
500
|
+
return { endpoint, key, databaseName: dbName, models };
|
|
354
501
|
}
|
|
355
502
|
catch (error) {
|
|
356
503
|
console.error('⚠️ Cosmos DB initialization failed:', error.message);
|
|
@@ -364,16 +511,52 @@ async function initializeCosmosDB(databaseName) {
|
|
|
364
511
|
async function startDevEnvironment(options) {
|
|
365
512
|
const port = options.port || '3000';
|
|
366
513
|
const functionsPort = options.functionsPort || '7071';
|
|
514
|
+
const swaPort = options.swaPort || '4280';
|
|
367
515
|
// Detect package manager from project lockfile
|
|
368
516
|
const pm = (0, package_manager_1.detectFromProject)();
|
|
369
517
|
const pmCmd = (0, package_manager_1.getCommands)(pm);
|
|
370
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
|
+
}
|
|
371
531
|
// プロセスを管理する配列
|
|
372
532
|
const processes = [];
|
|
373
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;
|
|
374
540
|
// Cleanup processes on Ctrl+C
|
|
375
|
-
process.on('SIGINT', () => {
|
|
541
|
+
process.on('SIGINT', async () => {
|
|
376
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
|
+
}
|
|
377
560
|
processes.forEach((proc) => {
|
|
378
561
|
if (proc && !proc.killed) {
|
|
379
562
|
proc.kill();
|
|
@@ -398,10 +581,12 @@ async function startDevEnvironment(options) {
|
|
|
398
581
|
// 2. Check if Azure Functions exists
|
|
399
582
|
const functionsDir = path.join(process.cwd(), 'functions');
|
|
400
583
|
const hasFunctions = fs.existsSync(functionsDir) && hasFunctionsProject(functionsDir, backendLanguage);
|
|
584
|
+
let functionsCoreToolsCommand = null;
|
|
585
|
+
let installedCoreToolsVersion = null;
|
|
401
586
|
if (hasFunctions && !options.noFunctions) {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
if (!
|
|
587
|
+
installedCoreToolsVersion = await resolveInstalledCoreToolsVersion();
|
|
588
|
+
functionsCoreToolsCommand = buildFunctionsCoreToolsCommand(backendLanguage, installedCoreToolsVersion);
|
|
589
|
+
if (functionsCoreToolsCommand.command === 'func' && !installedCoreToolsVersion) {
|
|
405
590
|
console.log('');
|
|
406
591
|
console.log('⚠️ Azure Functions Core Tools not found.');
|
|
407
592
|
console.log('');
|
|
@@ -448,6 +633,9 @@ async function startDevEnvironment(options) {
|
|
|
448
633
|
options.noFunctions = true;
|
|
449
634
|
}
|
|
450
635
|
}
|
|
636
|
+
else if (functionsCoreToolsCommand.command !== 'func') {
|
|
637
|
+
console.log(`ℹ️ Using ${functionsCoreToolsCommand.label}.`);
|
|
638
|
+
}
|
|
451
639
|
if (!options.noFunctions) {
|
|
452
640
|
// Check if Cosmos DB Emulator is running
|
|
453
641
|
const cosmosRunning = await checkCosmosDBEmulator();
|
|
@@ -463,9 +651,7 @@ async function startDevEnvironment(options) {
|
|
|
463
651
|
process.exit(1);
|
|
464
652
|
}
|
|
465
653
|
// Initialize Cosmos DB before starting Functions
|
|
466
|
-
const
|
|
467
|
-
const appName = packageJson.name || 'App';
|
|
468
|
-
const databaseName = `${appName.charAt(0).toUpperCase() + appName.slice(1)}Database`;
|
|
654
|
+
const databaseName = (0, dev_seeds_1.getDefaultCosmosDatabaseName)();
|
|
469
655
|
const cosmosInitialization = await initializeCosmosDB(databaseName);
|
|
470
656
|
if (options.seedEnv && cosmosInitialization) {
|
|
471
657
|
await (0, dev_seeds_1.applyDevSeedEnvironment)({
|
|
@@ -488,42 +674,63 @@ async function startDevEnvironment(options) {
|
|
|
488
674
|
}
|
|
489
675
|
}
|
|
490
676
|
else if (backendLanguage === 'csharp') {
|
|
491
|
-
console.log('
|
|
492
|
-
await runCommand('dotnet', ['build'], functionsDir, 'dotnet build');
|
|
677
|
+
console.log('ℹ️ C# Azure Functions can take longer on cold start while the worker builds.');
|
|
493
678
|
}
|
|
494
679
|
else {
|
|
495
680
|
functionsEnv = await preparePythonFunctionsEnvironment(functionsDir);
|
|
496
681
|
}
|
|
682
|
+
if (cosmosInitialization) {
|
|
683
|
+
functionsEnv = buildFunctionsCosmosEnvironment(functionsEnv, cosmosInitialization.databaseName);
|
|
684
|
+
}
|
|
497
685
|
}
|
|
498
686
|
}
|
|
499
687
|
if (hasFunctions && !options.noFunctions) {
|
|
500
688
|
// Build shared package before starting Functions
|
|
501
689
|
const sharedDir = path.join(process.cwd(), 'shared');
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
const
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
if (code === 0) {
|
|
515
|
-
console.log('✅ Shared package built successfully');
|
|
516
|
-
resolve();
|
|
517
|
-
}
|
|
518
|
-
else {
|
|
519
|
-
reject(new Error(`Shared package build failed with code ${code}`));
|
|
520
|
-
}
|
|
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(),
|
|
700
|
+
shell: true,
|
|
701
|
+
stdio: 'inherit',
|
|
521
702
|
});
|
|
522
|
-
|
|
523
|
-
|
|
703
|
+
await new Promise((resolve, reject) => {
|
|
704
|
+
sharedBuild.on('close', (code) => {
|
|
705
|
+
if (code === 0) {
|
|
706
|
+
console.log('✅ Shared package built successfully');
|
|
707
|
+
resolve();
|
|
708
|
+
}
|
|
709
|
+
else {
|
|
710
|
+
reject(new Error(`Shared package build failed with code ${code}`));
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
sharedBuild.on('error', reject);
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
717
|
+
console.log('⚠️ Shared package has no build script — skipping build. Run "swallowkit add-auth" to fix.');
|
|
718
|
+
}
|
|
719
|
+
}
|
|
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
|
+
}
|
|
524
730
|
}
|
|
525
731
|
// Azure Functions を起動
|
|
526
|
-
const
|
|
732
|
+
const functionsCommand = functionsCoreToolsCommand ?? buildFunctionsCoreToolsCommand(backendLanguage, installedCoreToolsVersion);
|
|
733
|
+
const funcProcess = (0, child_process_1.spawn)(functionsCommand.command, [...functionsCommand.argsPrefix, ...buildFunctionsStartArgs(functionsPort)], {
|
|
527
734
|
cwd: functionsDir,
|
|
528
735
|
shell: true,
|
|
529
736
|
stdio: 'pipe', // Always pipe to capture output
|
|
@@ -574,7 +781,8 @@ async function startDevEnvironment(options) {
|
|
|
574
781
|
}
|
|
575
782
|
}
|
|
576
783
|
});
|
|
577
|
-
console.log(
|
|
784
|
+
console.log(`⏳ Waiting for Azure Functions to accept requests at ${functionsBaseUrl}...`);
|
|
785
|
+
functionsReadinessPromise = waitForHttpServerReady(functionsBaseUrl, getFunctionsReadinessTimeoutMs(backendLanguage));
|
|
578
786
|
}
|
|
579
787
|
else if (!hasFunctions) {
|
|
580
788
|
console.log('');
|
|
@@ -586,22 +794,91 @@ async function startDevEnvironment(options) {
|
|
|
586
794
|
}
|
|
587
795
|
console.log('');
|
|
588
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
|
+
}
|
|
589
859
|
// 5. Start Next.js development server
|
|
590
860
|
const nextArgs = buildNextDevArgs(pm, port);
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
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
|
+
}
|
|
600
876
|
}
|
|
877
|
+
catch { /* ignore */ }
|
|
601
878
|
const nextEnv = {
|
|
602
879
|
...process.env,
|
|
603
|
-
BACKEND_FUNCTIONS_BASE_URL:
|
|
604
|
-
FUNCTIONS_BASE_URL:
|
|
880
|
+
BACKEND_FUNCTIONS_BASE_URL: bffTargetUrl,
|
|
881
|
+
FUNCTIONS_BASE_URL: bffTargetUrl,
|
|
605
882
|
};
|
|
606
883
|
const nextProcess = (0, child_process_1.spawn)(pm === 'pnpm' ? 'pnpm' : 'npx', nextArgs, {
|
|
607
884
|
cwd: process.cwd(),
|
|
@@ -619,6 +896,9 @@ async function startDevEnvironment(options) {
|
|
|
619
896
|
console.log(`\n⏹️ Next.js exited (exit code: ${code})`);
|
|
620
897
|
}
|
|
621
898
|
// Exit all processes when Next.js exits
|
|
899
|
+
if (mockServer) {
|
|
900
|
+
mockServer.stop().catch(() => { });
|
|
901
|
+
}
|
|
622
902
|
processes.forEach((proc) => {
|
|
623
903
|
if (proc && !proc.killed) {
|
|
624
904
|
proc.kill();
|
|
@@ -626,18 +906,90 @@ async function startDevEnvironment(options) {
|
|
|
626
906
|
});
|
|
627
907
|
process.exit(code || 0);
|
|
628
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
|
+
}
|
|
629
944
|
console.log('');
|
|
630
945
|
console.log('✅ SwallowKit development environment is running!');
|
|
631
946
|
console.log('');
|
|
632
|
-
|
|
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
|
+
}
|
|
633
963
|
if (hasFunctions && !options.noFunctions) {
|
|
634
|
-
console.log(
|
|
964
|
+
console.log(`${functionsReady ? '⚡ Azure Functions' : '⏳ Azure Functions (starting)'}: ${functionsBaseUrl}`);
|
|
965
|
+
}
|
|
966
|
+
if (mockServer) {
|
|
967
|
+
console.log(`🔌 Mock Proxy: ${bffTargetUrl} (BFF → here)`);
|
|
635
968
|
}
|
|
636
969
|
console.log('');
|
|
637
|
-
if (hasFunctions && !options.noFunctions) {
|
|
970
|
+
if (hasFunctions && !options.noFunctions && functionsReady) {
|
|
638
971
|
console.log('💡 Azure Functions and Next.js BFF are connected');
|
|
639
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
|
+
}
|
|
640
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
|
+
}
|
|
641
993
|
console.log('🛑 Press Ctrl+C to stop');
|
|
642
994
|
console.log('');
|
|
643
995
|
}
|
|
@@ -651,11 +1003,25 @@ async function startDevEnvironment(options) {
|
|
|
651
1003
|
process.exit(1);
|
|
652
1004
|
}
|
|
653
1005
|
}
|
|
654
|
-
async function
|
|
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) {
|
|
655
1021
|
await new Promise((resolve, reject) => {
|
|
656
1022
|
const child = (0, child_process_1.spawn)(command, args, {
|
|
657
1023
|
cwd,
|
|
658
|
-
shell
|
|
1024
|
+
shell,
|
|
659
1025
|
stdio: 'inherit',
|
|
660
1026
|
env,
|
|
661
1027
|
});
|