swallowkit 1.1.2 → 2.0.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +61 -7
- package/README.md +61 -7
- package/dist/cli/commands/add-auth.d.ts.map +1 -1
- package/dist/cli/commands/add-auth.js +103 -27
- package/dist/cli/commands/add-auth.js.map +1 -1
- package/dist/cli/commands/configure-mcp.d.ts +12 -0
- package/dist/cli/commands/configure-mcp.d.ts.map +1 -0
- package/dist/cli/commands/configure-mcp.js +42 -0
- package/dist/cli/commands/configure-mcp.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +5 -2
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +143 -67
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/init.d.ts +14 -7
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +149 -170
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/migrations.d.ts.map +1 -1
- package/dist/cli/commands/migrations.js +25 -0
- package/dist/cli/commands/migrations.js.map +1 -1
- package/dist/cli/commands/provision.d.ts +5 -0
- package/dist/cli/commands/provision.d.ts.map +1 -1
- package/dist/cli/commands/provision.js +278 -55
- package/dist/cli/commands/provision.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +5 -1
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +123 -18
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +49 -8
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config.d.ts +2 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +19 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/operations/create-model.d.ts +2 -0
- package/dist/core/operations/create-model.d.ts.map +1 -1
- package/dist/core/operations/create-model.js +60 -8
- package/dist/core/operations/create-model.js.map +1 -1
- package/dist/core/operations/migration-generator.d.ts.map +1 -1
- package/dist/core/operations/migration-generator.js +12 -1
- package/dist/core/operations/migration-generator.js.map +1 -1
- package/dist/core/operations/model-migration.d.ts +22 -0
- package/dist/core/operations/model-migration.d.ts.map +1 -0
- package/dist/core/operations/model-migration.js +36 -0
- package/dist/core/operations/model-migration.js.map +1 -0
- package/dist/core/operations/provision-deployment.d.ts +52 -0
- package/dist/core/operations/provision-deployment.d.ts.map +1 -0
- package/dist/core/operations/provision-deployment.js +164 -0
- package/dist/core/operations/provision-deployment.js.map +1 -0
- package/dist/core/operations/provision-operations.d.ts +62 -6
- package/dist/core/operations/provision-operations.d.ts.map +1 -1
- package/dist/core/operations/provision-operations.js +653 -115
- package/dist/core/operations/provision-operations.js.map +1 -1
- package/dist/core/operations/scaffold-apply.d.ts +1 -0
- package/dist/core/operations/scaffold-apply.d.ts.map +1 -1
- package/dist/core/operations/scaffold-apply.js +1 -0
- package/dist/core/operations/scaffold-apply.js.map +1 -1
- package/dist/core/operations/scaffold-machine.d.ts +1 -0
- package/dist/core/operations/scaffold-machine.d.ts.map +1 -1
- package/dist/core/operations/scaffold-machine.js.map +1 -1
- package/dist/core/operations/scaffold-plan.d.ts +2 -0
- package/dist/core/operations/scaffold-plan.d.ts.map +1 -1
- package/dist/core/operations/scaffold-plan.js +3 -1
- package/dist/core/operations/scaffold-plan.js.map +1 -1
- package/dist/core/project/agent-instructions.d.ts +2 -1
- package/dist/core/project/agent-instructions.d.ts.map +1 -1
- package/dist/core/project/agent-instructions.js +15 -0
- package/dist/core/project/agent-instructions.js.map +1 -1
- package/dist/core/project/boundaries.d.ts +5 -2
- package/dist/core/project/boundaries.d.ts.map +1 -1
- package/dist/core/project/boundaries.js +51 -20
- package/dist/core/project/boundaries.js.map +1 -1
- package/dist/core/project/capabilities.d.ts +17 -0
- package/dist/core/project/capabilities.d.ts.map +1 -1
- package/dist/core/project/capabilities.js +45 -6
- package/dist/core/project/capabilities.js.map +1 -1
- package/dist/core/project/home-page.d.ts +4 -0
- package/dist/core/project/home-page.d.ts.map +1 -0
- package/dist/core/project/home-page.js +113 -0
- package/dist/core/project/home-page.js.map +1 -0
- package/dist/core/project/manifest.d.ts +3 -1
- package/dist/core/project/manifest.d.ts.map +1 -1
- package/dist/core/project/manifest.js +47 -25
- package/dist/core/project/manifest.js.map +1 -1
- package/dist/core/project/mcp-config.d.ts +26 -0
- package/dist/core/project/mcp-config.d.ts.map +1 -0
- package/dist/core/project/mcp-config.js +117 -0
- package/dist/core/project/mcp-config.js.map +1 -0
- package/dist/core/project/react-project.d.ts +26 -0
- package/dist/core/project/react-project.d.ts.map +1 -0
- package/dist/core/project/react-project.js +443 -0
- package/dist/core/project/react-project.js.map +1 -0
- package/dist/core/project/state.d.ts +7 -0
- package/dist/core/project/state.d.ts.map +1 -1
- package/dist/core/project/state.js +86 -0
- package/dist/core/project/state.js.map +1 -1
- package/dist/core/project/validation.d.ts.map +1 -1
- package/dist/core/project/validation.js +32 -10
- package/dist/core/project/validation.js.map +1 -1
- package/dist/core/project/workflows.d.ts.map +1 -1
- package/dist/core/project/workflows.js +53 -7
- package/dist/core/project/workflows.js.map +1 -1
- package/dist/core/scaffold/auth-generator.d.ts.map +1 -1
- package/dist/core/scaffold/auth-generator.js +26 -27
- package/dist/core/scaffold/auth-generator.js.map +1 -1
- package/dist/core/scaffold/model-parser.d.ts +23 -0
- package/dist/core/scaffold/model-parser.d.ts.map +1 -1
- package/dist/core/scaffold/model-parser.js +407 -137
- package/dist/core/scaffold/model-parser.js.map +1 -1
- package/dist/core/scaffold/native-schema-generator.d.ts +13 -0
- package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -1
- package/dist/core/scaffold/native-schema-generator.js +294 -91
- package/dist/core/scaffold/native-schema-generator.js.map +1 -1
- package/dist/core/scaffold/openapi-generator.d.ts +5 -0
- package/dist/core/scaffold/openapi-generator.d.ts.map +1 -1
- package/dist/core/scaffold/openapi-generator.js +53 -3
- package/dist/core/scaffold/openapi-generator.js.map +1 -1
- package/dist/core/scaffold/react-bff-generator.d.ts +10 -0
- package/dist/core/scaffold/react-bff-generator.d.ts.map +1 -0
- package/dist/core/scaffold/react-bff-generator.js +252 -0
- package/dist/core/scaffold/react-bff-generator.js.map +1 -0
- package/dist/core/scaffold/react-ui-generator.d.ts +6 -0
- package/dist/core/scaffold/react-ui-generator.d.ts.map +1 -0
- package/dist/core/scaffold/react-ui-generator.js +41 -0
- package/dist/core/scaffold/react-ui-generator.js.map +1 -0
- package/dist/core/scaffold/ui-generator.d.ts +0 -3
- package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
- package/dist/core/scaffold/ui-generator.js +68 -7
- package/dist/core/scaffold/ui-generator.js.map +1 -1
- package/dist/core/verify/index.d.ts +4 -3
- package/dist/core/verify/index.d.ts.map +1 -1
- package/dist/core/verify/index.js +122 -5
- package/dist/core/verify/index.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/machine/contracts.d.ts +1 -0
- package/dist/machine/contracts.d.ts.map +1 -1
- package/dist/machine/errors.d.ts +3 -2
- package/dist/machine/errors.d.ts.map +1 -1
- package/dist/machine/errors.js +2 -1
- package/dist/machine/errors.js.map +1 -1
- package/dist/machine/index.d.ts.map +1 -1
- package/dist/machine/index.js +61 -30
- package/dist/machine/index.js.map +1 -1
- package/dist/mcp/index.d.ts +6 -4
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +210 -25
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/version-selection.d.ts +55 -0
- package/dist/mcp/version-selection.d.ts.map +1 -0
- package/dist/mcp/version-selection.js +152 -0
- package/dist/mcp/version-selection.js.map +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/azure-cli.d.ts +56 -0
- package/dist/utils/azure-cli.d.ts.map +1 -0
- package/dist/utils/azure-cli.js +326 -0
- package/dist/utils/azure-cli.js.map +1 -0
- package/dist/utils/azure-diagnostics.d.ts +16 -0
- package/dist/utils/azure-diagnostics.d.ts.map +1 -0
- package/dist/utils/azure-diagnostics.js +68 -0
- package/dist/utils/azure-diagnostics.js.map +1 -0
- package/dist/utils/package-manager.d.ts +2 -2
- package/dist/utils/package-manager.d.ts.map +1 -1
- package/dist/utils/package-manager.js +6 -3
- package/dist/utils/package-manager.js.map +1 -1
- package/dist/utils/python-uv.d.ts +5 -7
- package/dist/utils/python-uv.d.ts.map +1 -1
- package/dist/utils/python-uv.js +50 -24
- package/dist/utils/python-uv.js.map +1 -1
- package/dist/utils/react-dev.d.ts +4 -0
- package/dist/utils/react-dev.d.ts.map +1 -0
- package/dist/utils/react-dev.js +35 -0
- package/dist/utils/react-dev.js.map +1 -0
- package/dist/utils/semver.d.ts +14 -0
- package/dist/utils/semver.d.ts.map +1 -0
- package/dist/utils/semver.js +61 -0
- package/dist/utils/semver.js.map +1 -0
- package/package.json +7 -4
- package/src/__tests__/__snapshots__/boundaries.test.ts.snap +154 -0
- package/src/__tests__/agent-instructions.test.ts +6 -1
- package/src/__tests__/agent-loop-phase34.test.ts +143 -8
- package/src/__tests__/auth.test.ts +9 -0
- package/src/__tests__/azure-cli.test.ts +202 -0
- package/src/__tests__/boundaries.test.ts +42 -0
- package/src/__tests__/csharp-schema.test.ts +261 -0
- package/src/__tests__/dev.test.ts +13 -6
- package/src/__tests__/fixtures/az wrapper.cmd +2 -0
- package/src/__tests__/fixtures/azure-cli.cjs +153 -0
- package/src/__tests__/fixtures/provision-runner.cjs +28 -0
- package/src/__tests__/generated-project-matrix.test.ts +789 -0
- package/src/__tests__/home-page.test.ts +50 -0
- package/src/__tests__/init.test.ts +145 -5
- package/src/__tests__/mcp-version.test.ts +262 -0
- package/src/__tests__/mcp.test.ts +120 -3
- package/src/__tests__/multi-auth.test.ts +115 -4
- package/src/__tests__/package-manager.test.ts +17 -2
- package/src/__tests__/plan-apply.test.ts +179 -0
- package/src/__tests__/provision-runtime.test.ts +722 -0
- package/src/__tests__/python-uv.test.ts +50 -14
- package/src/__tests__/python-venv.test.ts +48 -0
- package/src/__tests__/react-dev.test.ts +72 -0
- package/src/__tests__/react.test.ts +838 -0
- package/src/__tests__/release-metadata.test.ts +121 -0
- package/src/__tests__/schema-analysis.test.ts +329 -0
- package/src/cli/commands/add-auth.ts +103 -29
- package/src/cli/commands/configure-mcp.ts +54 -0
- package/src/cli/commands/dev.ts +145 -73
- package/src/cli/commands/init.ts +147 -172
- package/src/cli/commands/migrations.ts +22 -0
- package/src/cli/commands/provision.ts +294 -56
- package/src/cli/commands/scaffold.ts +145 -22
- package/src/cli/index.ts +46 -10
- package/src/core/config.ts +21 -1
- package/src/core/operations/create-model.ts +51 -13
- package/src/core/operations/migration-generator.ts +14 -1
- package/src/core/operations/model-migration.ts +52 -0
- package/src/core/operations/provision-deployment.ts +183 -0
- package/src/core/operations/provision-operations.ts +662 -154
- package/src/core/operations/scaffold-apply.ts +2 -0
- package/src/core/operations/scaffold-machine.ts +1 -0
- package/src/core/operations/scaffold-plan.ts +5 -1
- package/src/core/project/agent-instructions.ts +17 -1
- package/src/core/project/agent-templates/AGENTS.md +113 -15
- package/src/core/project/agent-templates/CLAUDE.md +14 -1
- package/src/core/project/agent-templates/bff-routes.instructions.md +5 -3
- package/src/core/project/agent-templates/copilot-instructions.md +15 -1
- package/src/core/project/agent-templates/shared-models.instructions.md +11 -3
- package/src/core/project/boundaries.ts +62 -20
- package/src/core/project/capabilities.ts +63 -6
- package/src/core/project/home-page.ts +114 -0
- package/src/core/project/manifest.ts +52 -27
- package/src/core/project/mcp-config.ts +84 -0
- package/src/core/project/react-project.ts +529 -0
- package/src/core/project/state.ts +70 -0
- package/src/core/project/validation.ts +32 -10
- package/src/core/project/workflows.ts +53 -7
- package/src/core/scaffold/auth-generator.ts +26 -27
- package/src/core/scaffold/model-parser.ts +413 -129
- package/src/core/scaffold/native-schema-generator.ts +319 -103
- package/src/core/scaffold/openapi-generator.ts +63 -5
- package/src/core/scaffold/react-bff-generator.ts +292 -0
- package/src/core/scaffold/react-ui-generator.ts +51 -0
- package/src/core/scaffold/ui-generator.ts +70 -8
- package/src/core/verify/index.ts +118 -6
- package/src/index.ts +3 -1
- package/src/machine/contracts.ts +1 -0
- package/src/machine/errors.ts +4 -2
- package/src/machine/index.ts +67 -36
- package/src/mcp/index.ts +221 -31
- package/src/mcp/version-selection.ts +163 -0
- package/src/types/index.ts +4 -0
- package/src/utils/azure-cli.ts +315 -0
- package/src/utils/azure-diagnostics.ts +75 -0
- package/src/utils/package-manager.ts +7 -4
- package/src/utils/python-uv.ts +53 -33
- package/src/utils/react-dev.ts +31 -0
- package/src/utils/semver.ts +59 -0
package/src/cli/commands/init.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { spawn, execSync } from "child_process";
|
|
4
4
|
import prompts from "prompts";
|
|
5
|
-
import { BackendLanguage } from "../../types";
|
|
5
|
+
import { BackendLanguage, FrontendFramework } from "../../types";
|
|
6
|
+
import { buildReactReadme, buildReactSwaConfig, createReactBffProject, createReactProject, buildReactGitHubSwaWorkflow, buildReactAzureSwaPipeline } from "../../core/project/react-project";
|
|
6
7
|
import {
|
|
7
8
|
type PackageManager,
|
|
8
9
|
detectFromUserAgent,
|
|
@@ -24,11 +25,14 @@ import {
|
|
|
24
25
|
buildCSharpCodegenToolManifestSource,
|
|
25
26
|
buildPythonCodegenRequirementsSource,
|
|
26
27
|
} from "../../core/scaffold/native-schema-generator";
|
|
27
|
-
import {
|
|
28
|
+
import { DEFAULT_AZURE_PYTHON_VERSION } from "../../utils/python-uv";
|
|
29
|
+
import { buildHomePage } from "../../core/project/home-page";
|
|
30
|
+
import { MCP_LATEST, McpPinDecision, lookupPublishedLatestVersion, resolveMcpPinVersion } from "../../core/project/mcp-config";
|
|
28
31
|
|
|
29
32
|
interface InitOptions {
|
|
30
33
|
name: string;
|
|
31
34
|
template: string;
|
|
35
|
+
frontend?: FrontendFramework;
|
|
32
36
|
nextVersion?: string;
|
|
33
37
|
cicd?: CiCdProvider;
|
|
34
38
|
backendLanguage?: BackendLanguage;
|
|
@@ -142,7 +146,7 @@ function getFunctionsRuntimeConfig(backendLanguage: BackendLanguage): { name: st
|
|
|
142
146
|
return { name: "dotnet-isolated", version: "10.0" };
|
|
143
147
|
}
|
|
144
148
|
if (backendLanguage === "python") {
|
|
145
|
-
return { name: "python", version:
|
|
149
|
+
return { name: "python", version: DEFAULT_AZURE_PYTHON_VERSION };
|
|
146
150
|
}
|
|
147
151
|
return { name: "node", version: "22" };
|
|
148
152
|
}
|
|
@@ -257,6 +261,9 @@ const VALID_VNET: VNetOption[] = ['none', 'outbound'];
|
|
|
257
261
|
const VALID_SWA_PLAN: StaticWebAppPlan[] = ['free', 'standard'];
|
|
258
262
|
|
|
259
263
|
function validateInitFlags(options: InitOptions): void {
|
|
264
|
+
if (options.frontend && !["nextjs", "react"].includes(options.frontend)) {
|
|
265
|
+
throw new Error("Invalid --frontend: expected nextjs or react");
|
|
266
|
+
}
|
|
260
267
|
if (options.packageManager && !['npm', 'pnpm'].includes(options.packageManager)) {
|
|
261
268
|
throw new Error('Invalid --package-manager: expected npm or pnpm');
|
|
262
269
|
}
|
|
@@ -322,17 +329,28 @@ export async function initCommand(options: InitOptions) {
|
|
|
322
329
|
}
|
|
323
330
|
}
|
|
324
331
|
|
|
332
|
+
// Preserve existing non-interactive invocations while offering a frontend choice in the wizard.
|
|
333
|
+
const frontend: FrontendFramework = options.frontend ?? (interactive
|
|
334
|
+
? (await prompts({
|
|
335
|
+
type: "select", name: "frontend", message: "Frontend framework:",
|
|
336
|
+
choices: [{ title: "Next.js (App Router)", value: "nextjs" }, { title: "React (Vite SPA + SWA managed Functions BFF)", value: "react" }],
|
|
337
|
+
initial: 0,
|
|
338
|
+
})).frontend || "nextjs"
|
|
339
|
+
: "nextjs");
|
|
340
|
+
options = { ...options, frontend };
|
|
341
|
+
|
|
325
342
|
// Use flag values if provided, otherwise prompt interactively
|
|
326
343
|
const cicdProvider: CiCdProvider = options.cicd || await promptCiCd();
|
|
327
344
|
const backendLanguage: BackendLanguage = options.backendLanguage || await promptBackendLanguage();
|
|
328
345
|
|
|
329
346
|
const azureConfig = await promptAzureConfig(options);
|
|
330
347
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
348
|
+
if (frontend === "react") {
|
|
349
|
+
createReactProject(projectDir, options.name, pm);
|
|
350
|
+
} else {
|
|
351
|
+
await createNextJsProject(options.name, pm);
|
|
352
|
+
await upgradeNextJs(projectDir, options.nextVersion || 'latest', pm);
|
|
353
|
+
}
|
|
336
354
|
|
|
337
355
|
// Add SwallowKit specific files
|
|
338
356
|
await addSwallowKitFiles(projectDir, options, cicdProvider, azureConfig, pm, backendLanguage);
|
|
@@ -342,9 +360,9 @@ export async function initCommand(options: InitOptions) {
|
|
|
342
360
|
|
|
343
361
|
// Create CI/CD files based on choice
|
|
344
362
|
if (cicdProvider === 'github') {
|
|
345
|
-
await createGitHubActionsWorkflows(projectDir, azureConfig, pm, backendLanguage);
|
|
363
|
+
await createGitHubActionsWorkflows(projectDir, azureConfig, pm, backendLanguage, frontend);
|
|
346
364
|
} else if (cicdProvider === 'azure') {
|
|
347
|
-
await createAzurePipelines(projectDir, pm, backendLanguage);
|
|
365
|
+
await createAzurePipelines(projectDir, pm, backendLanguage, frontend);
|
|
348
366
|
}
|
|
349
367
|
|
|
350
368
|
await syncProjectManifest(projectDir);
|
|
@@ -651,9 +669,13 @@ export function parseIgnoredBuilds(output: string): string[] {
|
|
|
651
669
|
}
|
|
652
670
|
|
|
653
671
|
export function injectSwallowKitNextConfig(nextConfigContent: string, projectName: string): string {
|
|
654
|
-
|
|
672
|
+
const compatibleConfig = nextConfigContent.replace(/(\b(?:cacheComponents|partialPrefetching)\s*:\s*)true\b/g, '$1false');
|
|
673
|
+
const cacheComponentsSetting = /\bcacheComponents\s*:/.test(compatibleConfig)
|
|
674
|
+
? ''
|
|
675
|
+
: ' cacheComponents: false,\n';
|
|
676
|
+
return compatibleConfig.replace(
|
|
655
677
|
/(const\s+nextConfig[:\s]*(?::\s*NextConfig\s*)?=\s*\{)(\s*\/\*[^*]*\*\/)?/,
|
|
656
|
-
`$1\n output: 'standalone',\n agentRules: false, // SwallowKit owns the canonical root AGENTS.md contract\n transpilePackages: ['@${projectName}/shared'],\n serverExternalPackages: ['applicationinsights', 'diagnostic-channel-publishers'],$2`
|
|
678
|
+
`$1\n output: 'standalone',\n${cacheComponentsSetting} agentRules: false, // SwallowKit owns the canonical root AGENTS.md contract\n transpilePackages: ['@${projectName}/shared'],\n serverExternalPackages: ['applicationinsights', 'diagnostic-channel-publishers'],$2`
|
|
657
679
|
);
|
|
658
680
|
}
|
|
659
681
|
|
|
@@ -691,7 +713,7 @@ export function buildCSharpFunctionsProjectSource(): string {
|
|
|
691
713
|
<None Remove="generated\\**\\bin\\**;generated\\**\\obj\\**" />
|
|
692
714
|
</ItemGroup>
|
|
693
715
|
<ItemGroup>
|
|
694
|
-
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.
|
|
716
|
+
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.63.2" />
|
|
695
717
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
|
696
718
|
<PackageReference Include="Azure.Identity" Version="1.17.0" />
|
|
697
719
|
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.52.0" />
|
|
@@ -704,8 +726,11 @@ export function buildCSharpFunctionsProjectSource(): string {
|
|
|
704
726
|
`;
|
|
705
727
|
}
|
|
706
728
|
|
|
707
|
-
export function buildSwallowKitConfigSource(backendLanguage: BackendLanguage): string {
|
|
729
|
+
export function buildSwallowKitConfigSource(backendLanguage: BackendLanguage, frontend: FrontendFramework = "nextjs"): string {
|
|
708
730
|
return `module.exports = {
|
|
731
|
+
frontend: {
|
|
732
|
+
framework: '${frontend}',
|
|
733
|
+
},
|
|
709
734
|
backend: {
|
|
710
735
|
language: '${backendLanguage}',
|
|
711
736
|
},
|
|
@@ -723,10 +748,10 @@ export function buildSwallowKitConfigSource(backendLanguage: BackendLanguage): s
|
|
|
723
748
|
export function buildGeneratedProjectDependencies(
|
|
724
749
|
projectName: string,
|
|
725
750
|
pm: PackageManager = 'pnpm',
|
|
751
|
+
frontend: FrontendFramework = "nextjs",
|
|
726
752
|
): Record<string, string> {
|
|
727
753
|
return {
|
|
728
|
-
'@azure/cosmos': '^4.0.0',
|
|
729
|
-
'applicationinsights': '^3.3.0',
|
|
754
|
+
...(frontend === "nextjs" ? { '@azure/cosmos': '^4.0.0', 'applicationinsights': '^3.3.0' } : {}),
|
|
730
755
|
'zod': '^4.0.0',
|
|
731
756
|
[`@${projectName}/shared`]: pm === 'pnpm' ? 'workspace:*' : 'file:shared',
|
|
732
757
|
};
|
|
@@ -755,14 +780,30 @@ function getSwallowKitPackageMetadata(): { name: string; version: string } {
|
|
|
755
780
|
};
|
|
756
781
|
}
|
|
757
782
|
|
|
758
|
-
export function buildGeneratedProjectDevDependencies(): Record<string, string> {
|
|
783
|
+
export function buildGeneratedProjectDevDependencies(frontend: FrontendFramework = "nextjs"): Record<string, string> {
|
|
759
784
|
const { name, version } = getSwallowKitPackageMetadata();
|
|
760
785
|
return {
|
|
761
786
|
[name]: version,
|
|
787
|
+
...(frontend === "nextjs" ? { "@tailwindcss/postcss": "^4.0.0" } : {}),
|
|
762
788
|
};
|
|
763
789
|
}
|
|
764
790
|
|
|
765
|
-
export function
|
|
791
|
+
export function ensureNextTailwindPostcssConfig(projectDir: string): void {
|
|
792
|
+
if (["postcss.config.mjs", "postcss.config.js", "postcss.config.cjs", "postcss.config.json"]
|
|
793
|
+
.some(file => fs.existsSync(path.join(projectDir, file)))) {
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// New create-next-app templates can configure Tailwind for Turbopack only.
|
|
798
|
+
fs.writeFileSync(path.join(projectDir, "postcss.config.mjs"), `export default {
|
|
799
|
+
plugins: {
|
|
800
|
+
"@tailwindcss/postcss": {},
|
|
801
|
+
},
|
|
802
|
+
};
|
|
803
|
+
`);
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
export function buildSwallowKitMcpProjectConfigSource(pm: PackageManager = 'pnpm', mcpVersion: string = MCP_LATEST): string {
|
|
766
807
|
const { name } = getSwallowKitPackageMetadata();
|
|
767
808
|
return JSON.stringify(
|
|
768
809
|
{
|
|
@@ -774,7 +815,7 @@ export function buildSwallowKitMcpProjectConfigSource(pm: PackageManager = 'pnpm
|
|
|
774
815
|
: ["--yes", "--package", `${name}@\${SWALLOWKIT_MCP_VERSION}`, "swallowkit-mcp"],
|
|
775
816
|
cwd: ".",
|
|
776
817
|
env: {
|
|
777
|
-
SWALLOWKIT_MCP_VERSION:
|
|
818
|
+
SWALLOWKIT_MCP_VERSION: mcpVersion,
|
|
778
819
|
},
|
|
779
820
|
},
|
|
780
821
|
},
|
|
@@ -784,6 +825,15 @@ export function buildSwallowKitMcpProjectConfigSource(pm: PackageManager = 'pnpm
|
|
|
784
825
|
);
|
|
785
826
|
}
|
|
786
827
|
|
|
828
|
+
/** latest by default; pinned to this CLI version only when it is newer than published latest. */
|
|
829
|
+
export function selectInitialMcpVersion(
|
|
830
|
+
lookupLatest: (packageName: string) => string | null = lookupPublishedLatestVersion
|
|
831
|
+
): McpPinDecision {
|
|
832
|
+
const { name, version } = getSwallowKitPackageMetadata();
|
|
833
|
+
if (version === MCP_LATEST) return { version: MCP_LATEST, reason: "SwallowKit package metadata is unavailable." };
|
|
834
|
+
return resolveMcpPinVersion(version, lookupLatest(name));
|
|
835
|
+
}
|
|
836
|
+
|
|
787
837
|
async function addSwallowKitFiles(
|
|
788
838
|
projectDir: string,
|
|
789
839
|
options: InitOptions,
|
|
@@ -795,6 +845,7 @@ async function addSwallowKitFiles(
|
|
|
795
845
|
console.log('📦 Adding SwallowKit files...\n');
|
|
796
846
|
|
|
797
847
|
const projectName = options.name;
|
|
848
|
+
const frontend = options.frontend ?? "nextjs";
|
|
798
849
|
|
|
799
850
|
// 1. Update package.json to add runtime dependencies for generated projects
|
|
800
851
|
const packageJsonPath = path.join(projectDir, 'package.json');
|
|
@@ -803,17 +854,21 @@ async function addSwallowKitFiles(
|
|
|
803
854
|
// Root BFF/auth code imports Zod directly; declare it as well as the shared package.
|
|
804
855
|
packageJson.dependencies = {
|
|
805
856
|
...packageJson.dependencies,
|
|
806
|
-
...buildGeneratedProjectDependencies(projectName, pm),
|
|
857
|
+
...buildGeneratedProjectDependencies(projectName, pm, frontend),
|
|
807
858
|
};
|
|
808
859
|
packageJson.devDependencies = {
|
|
809
860
|
...packageJson.devDependencies,
|
|
810
|
-
...buildGeneratedProjectDevDependencies(),
|
|
861
|
+
...buildGeneratedProjectDevDependencies(frontend),
|
|
811
862
|
};
|
|
812
863
|
|
|
813
864
|
packageJson.scripts = {
|
|
814
865
|
...packageJson.scripts,
|
|
815
|
-
'build': getBuildScript(pm),
|
|
816
|
-
'start': 'next start',
|
|
866
|
+
'build': getBuildScript(pm, frontend),
|
|
867
|
+
'start': frontend === "react" ? "vite preview" : 'next start',
|
|
868
|
+
...(frontend === "react" ? {
|
|
869
|
+
typecheck: `tsc --noEmit && ${getCommands(pm).exec} tsc --noEmit -p bff`,
|
|
870
|
+
"bff:start": `${getCommands(pm).runFilter("bff")} start`,
|
|
871
|
+
} : {}),
|
|
817
872
|
'functions:start': getFunctionsStartScript(pm, backendLanguage),
|
|
818
873
|
};
|
|
819
874
|
|
|
@@ -827,7 +882,7 @@ async function addSwallowKitFiles(
|
|
|
827
882
|
delete packageJson.packageManager;
|
|
828
883
|
|
|
829
884
|
// Keep package.json workspaces for npm-based CI/CD even when local development uses pnpm.
|
|
830
|
-
const workspacePackages = usesNodeFunctionsProject(backendLanguage) ? ['shared', 'functions'] : ['shared'];
|
|
885
|
+
const workspacePackages = [...(usesNodeFunctionsProject(backendLanguage) ? ['shared', 'functions'] : ['shared']), ...(frontend === "react" ? ["bff"] : [])];
|
|
831
886
|
const wsConfig = getWorkspaceConfig(pm, workspacePackages);
|
|
832
887
|
packageJson.workspaces = workspacePackages;
|
|
833
888
|
|
|
@@ -837,9 +892,11 @@ async function addSwallowKitFiles(
|
|
|
837
892
|
|
|
838
893
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
839
894
|
|
|
895
|
+
if (frontend === "nextjs") ensureNextTailwindPostcssConfig(projectDir);
|
|
896
|
+
|
|
840
897
|
// Create pnpm-workspace.yaml for local pnpm development when needed.
|
|
841
898
|
if (wsConfig.type === 'file') {
|
|
842
|
-
fs.writeFileSync(path.join(projectDir, wsConfig.filename), wsConfig.content);
|
|
899
|
+
fs.writeFileSync(path.join(projectDir, wsConfig.filename), wsConfig.content + (frontend === "react" ? " esbuild: true\n" : ""));
|
|
843
900
|
}
|
|
844
901
|
|
|
845
902
|
// Don't install yet — wait until all workspace packages (shared, functions) are created
|
|
@@ -878,12 +935,13 @@ async function addSwallowKitFiles(
|
|
|
878
935
|
}
|
|
879
936
|
|
|
880
937
|
// 3. Create SwallowKit config
|
|
881
|
-
const swallowkitConfig = buildSwallowKitConfigSource(backendLanguage);
|
|
938
|
+
const swallowkitConfig = buildSwallowKitConfigSource(backendLanguage, frontend);
|
|
882
939
|
fs.writeFileSync(path.join(projectDir, 'swallowkit.config.js'), swallowkitConfig);
|
|
883
940
|
|
|
884
941
|
// 4. Create shared workspace package for Zod models (Single Source of Truth)
|
|
885
942
|
await createSharedPackage(projectDir, projectName);
|
|
886
943
|
|
|
944
|
+
if (frontend === "nextjs") {
|
|
887
945
|
// Create lib directory for Next.js-specific utilities
|
|
888
946
|
const libDir = path.join(projectDir, 'lib');
|
|
889
947
|
|
|
@@ -1003,6 +1061,7 @@ export const api = {
|
|
|
1003
1061
|
};
|
|
1004
1062
|
`;
|
|
1005
1063
|
fs.writeFileSync(path.join(apiLibDir, 'backend.ts'), backendUtilContent);
|
|
1064
|
+
}
|
|
1006
1065
|
|
|
1007
1066
|
// 5. Create components directory
|
|
1008
1067
|
const componentsDir = path.join(projectDir, 'components');
|
|
@@ -1091,7 +1150,7 @@ export async function register() {
|
|
|
1091
1150
|
}
|
|
1092
1151
|
}
|
|
1093
1152
|
`;
|
|
1094
|
-
fs.writeFileSync(path.join(projectDir, 'instrumentation.ts'), instrumentationContent);
|
|
1153
|
+
if (frontend === "nextjs") fs.writeFileSync(path.join(projectDir, 'instrumentation.ts'), instrumentationContent);
|
|
1095
1154
|
|
|
1096
1155
|
// 8. Create .env.local for local development
|
|
1097
1156
|
const envLocalContent = [
|
|
@@ -1124,17 +1183,19 @@ export async function register() {
|
|
|
1124
1183
|
};
|
|
1125
1184
|
fs.writeFileSync(
|
|
1126
1185
|
path.join(projectDir, 'staticwebapp.config.json'),
|
|
1127
|
-
JSON.stringify(swaConfig, null, 2)
|
|
1186
|
+
JSON.stringify(frontend === "react" ? buildReactSwaConfig() : swaConfig, null, 2)
|
|
1128
1187
|
);
|
|
1129
1188
|
|
|
1130
1189
|
// 14. Create Azure Functions project
|
|
1131
1190
|
await createAzureFunctionsProject(projectDir, pm, backendLanguage);
|
|
1132
1191
|
|
|
1133
1192
|
// 15. Create BFF API route to call Azure Functions
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1193
|
+
if (frontend === "react") {
|
|
1194
|
+
createReactBffProject(projectDir, projectName, pm);
|
|
1195
|
+
} else {
|
|
1196
|
+
await createBffApiRoute(projectDir);
|
|
1197
|
+
await createHomePage(projectDir, pm);
|
|
1198
|
+
}
|
|
1138
1199
|
|
|
1139
1200
|
// 17. Install all workspace dependencies (root + shared + functions)
|
|
1140
1201
|
console.log('📦 Installing workspace dependencies...\n');
|
|
@@ -1143,13 +1204,17 @@ export async function register() {
|
|
|
1143
1204
|
console.log('✅ Project structure created\n');
|
|
1144
1205
|
|
|
1145
1206
|
// 18. Create README.md
|
|
1146
|
-
|
|
1207
|
+
if (frontend === "react") {
|
|
1208
|
+
fs.writeFileSync(path.join(projectDir, "README.md"), buildReactReadme(projectName, pm, backendLanguage));
|
|
1209
|
+
} else {
|
|
1210
|
+
createReadme(projectDir, projectName, cicdChoice, azureConfig, pm, backendLanguage);
|
|
1211
|
+
}
|
|
1147
1212
|
|
|
1148
1213
|
// 19. Create AI agent instruction files (AGENTS.md, CLAUDE.md, .github/copilot-instructions.md, etc.)
|
|
1149
|
-
createAiAgentFiles(projectDir, projectName, backendLanguage, pm);
|
|
1214
|
+
createAiAgentFiles(projectDir, projectName, backendLanguage, pm, frontend);
|
|
1150
1215
|
}
|
|
1151
1216
|
|
|
1152
|
-
async function createSharedPackage(projectDir: string, projectName: string) {
|
|
1217
|
+
export async function createSharedPackage(projectDir: string, projectName: string) {
|
|
1153
1218
|
console.log('📦 Creating shared workspace package for Zod models...\n');
|
|
1154
1219
|
|
|
1155
1220
|
const sharedDir = path.join(projectDir, 'shared');
|
|
@@ -1189,7 +1254,7 @@ async function createSharedPackage(projectDir: string, projectName: string) {
|
|
|
1189
1254
|
// shared/index.ts (empty re-export file, scaffold will add entries)
|
|
1190
1255
|
fs.writeFileSync(
|
|
1191
1256
|
path.join(sharedDir, 'index.ts'),
|
|
1192
|
-
`// Shared Zod models
|
|
1257
|
+
`// Shared Zod models. SwallowKit create-model/scaffold append missing model exports.\n// You may add exports manually; existing exports and comments are preserved. No managed markers.\n`
|
|
1193
1258
|
);
|
|
1194
1259
|
|
|
1195
1260
|
// shared/.gitignore
|
|
@@ -1201,7 +1266,7 @@ async function createSharedPackage(projectDir: string, projectName: string) {
|
|
|
1201
1266
|
console.log('✅ Shared package created\n');
|
|
1202
1267
|
}
|
|
1203
1268
|
|
|
1204
|
-
async function createAzureFunctionsProject(
|
|
1269
|
+
export async function createAzureFunctionsProject(
|
|
1205
1270
|
projectDir: string,
|
|
1206
1271
|
pm: PackageManager = 'pnpm',
|
|
1207
1272
|
backendLanguage: BackendLanguage = 'typescript'
|
|
@@ -1456,7 +1521,6 @@ public sealed class GreetFunction
|
|
|
1456
1521
|
}
|
|
1457
1522
|
|
|
1458
1523
|
function createPythonFunctionsProject(projectDir: string, functionsDir: string): void {
|
|
1459
|
-
fs.writeFileSync(path.join(projectDir, '.python-version'), '3.11\n');
|
|
1460
1524
|
ensureProjectGitignoreEntry(projectDir, '.uv');
|
|
1461
1525
|
|
|
1462
1526
|
fs.writeFileSync(path.join(functionsDir, 'requirements.txt'), `azure-functions>=1.20.0
|
|
@@ -1596,117 +1660,8 @@ export async function POST(request: NextRequest) {
|
|
|
1596
1660
|
|
|
1597
1661
|
async function createHomePage(projectDir: string, pm: PackageManager = 'pnpm') {
|
|
1598
1662
|
console.log('📦 Creating home page...\n');
|
|
1599
|
-
|
|
1600
|
-
const pmCmd = getCommands(pm);
|
|
1601
|
-
|
|
1602
|
-
const pageContent = `'use client'
|
|
1603
|
-
|
|
1604
|
-
export const dynamic = 'force-dynamic';
|
|
1605
|
-
|
|
1606
|
-
import { useState } from 'react';
|
|
1607
|
-
import { scaffoldConfig } from '@/lib/scaffold-config';
|
|
1608
|
-
|
|
1609
|
-
export default function Home() {
|
|
1610
|
-
const [greetingStatus, setGreetingStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
|
|
1611
|
-
const [message, setMessage] = useState('');
|
|
1612
|
-
|
|
1613
|
-
const testConnection = async () => {
|
|
1614
|
-
setGreetingStatus('loading');
|
|
1615
|
-
try {
|
|
1616
|
-
const response = await fetch('/api/greet?name=SwallowKit');
|
|
1617
|
-
const data = await response.json();
|
|
1618
|
-
if (!response.ok) {
|
|
1619
|
-
throw new Error(data.error || \`Server error: \${response.status}\`);
|
|
1620
|
-
}
|
|
1621
|
-
setMessage(data.message);
|
|
1622
|
-
setGreetingStatus('success');
|
|
1623
|
-
} catch (error) {
|
|
1624
|
-
setMessage(error instanceof Error ? error.message : 'Failed to connect to Azure Functions');
|
|
1625
|
-
setGreetingStatus('error');
|
|
1626
|
-
}
|
|
1627
|
-
};
|
|
1628
1663
|
|
|
1629
|
-
|
|
1630
|
-
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-50 dark:from-gray-900 dark:to-gray-800">
|
|
1631
|
-
<div className="container mx-auto px-4 py-12">
|
|
1632
|
-
<header className="text-center mb-16">
|
|
1633
|
-
<h1 className="text-5xl font-bold text-gray-800 dark:text-white mb-4">
|
|
1634
|
-
Welcome to SwallowKit
|
|
1635
|
-
</h1>
|
|
1636
|
-
<p className="text-xl text-gray-600 dark:text-gray-400">
|
|
1637
|
-
Next.js on Azure Static Web Apps + Functions + Cosmos DB — Zod schema sharing
|
|
1638
|
-
</p>
|
|
1639
|
-
</header>
|
|
1640
|
-
|
|
1641
|
-
{/* Connection Test */}
|
|
1642
|
-
<section className="max-w-2xl mx-auto mb-12">
|
|
1643
|
-
<div className="bg-white dark:bg-gray-800 rounded-xl p-8 border border-gray-200 dark:border-gray-700">
|
|
1644
|
-
<h2 className="text-2xl font-semibold mb-4 text-gray-900 dark:text-gray-100">
|
|
1645
|
-
Test BFF → Functions Connection
|
|
1646
|
-
</h2>
|
|
1647
|
-
<button
|
|
1648
|
-
onClick={testConnection}
|
|
1649
|
-
disabled={greetingStatus === 'loading'}
|
|
1650
|
-
className="px-6 py-3 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white rounded-lg font-medium transition-colors"
|
|
1651
|
-
>
|
|
1652
|
-
{greetingStatus === 'loading' ? 'Testing...' : 'Test Connection'}
|
|
1653
|
-
</button>
|
|
1654
|
-
{greetingStatus === 'success' && (
|
|
1655
|
-
<div className="mt-4 p-4 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg">
|
|
1656
|
-
<p className="text-green-800 dark:text-green-200 font-medium">✅ Connection successful!</p>
|
|
1657
|
-
<p className="text-green-700 dark:text-green-300 text-sm mt-1">{message}</p>
|
|
1658
|
-
</div>
|
|
1659
|
-
)}
|
|
1660
|
-
{greetingStatus === 'error' && (
|
|
1661
|
-
<div className="mt-4 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg">
|
|
1662
|
-
<p className="text-red-800 dark:text-red-200 font-medium">❌ Connection failed</p>
|
|
1663
|
-
<p className="text-red-700 dark:text-red-300 text-sm mt-1">{message}</p>
|
|
1664
|
-
</div>
|
|
1665
|
-
)}
|
|
1666
|
-
</div>
|
|
1667
|
-
</section>
|
|
1668
|
-
|
|
1669
|
-
{/* Scaffolded Models Menu */}
|
|
1670
|
-
{scaffoldConfig.models.length > 0 ? (
|
|
1671
|
-
<section className="max-w-6xl mx-auto">
|
|
1672
|
-
<h2 className="text-3xl font-bold mb-8 text-gray-900 dark:text-gray-100">Your Models</h2>
|
|
1673
|
-
<div className="grid gap-6 grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
|
|
1674
|
-
{scaffoldConfig.models.map((model) => (
|
|
1675
|
-
<a
|
|
1676
|
-
key={model.name}
|
|
1677
|
-
href={model.path}
|
|
1678
|
-
className="block p-8 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl hover:shadow-lg hover:border-blue-400 dark:hover:border-blue-600 transition-all"
|
|
1679
|
-
>
|
|
1680
|
-
<h3 className="text-2xl font-semibold mb-2 text-gray-900 dark:text-gray-100">{model.label}</h3>
|
|
1681
|
-
<p className="text-gray-600 dark:text-gray-400">Manage {model.label.toLowerCase()}</p>
|
|
1682
|
-
</a>
|
|
1683
|
-
))}
|
|
1684
|
-
</div>
|
|
1685
|
-
</section>
|
|
1686
|
-
) : (
|
|
1687
|
-
<section className="max-w-2xl mx-auto text-center">
|
|
1688
|
-
<div className="bg-white dark:bg-gray-800 rounded-xl p-12 border border-gray-200 dark:border-gray-700">
|
|
1689
|
-
<h2 className="text-2xl font-semibold mb-4 text-gray-900 dark:text-gray-100">Get Started</h2>
|
|
1690
|
-
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
|
1691
|
-
Create your first model with Zod and generate CRUD operations automatically.
|
|
1692
|
-
</p>
|
|
1693
|
-
<code className="block bg-gray-100 dark:bg-gray-900 p-4 rounded text-left text-sm">
|
|
1694
|
-
${pmCmd.exec} swallowkit scaffold shared/models/your-model.ts
|
|
1695
|
-
</code>
|
|
1696
|
-
</div>
|
|
1697
|
-
</section>
|
|
1698
|
-
)}
|
|
1699
|
-
|
|
1700
|
-
<footer className="mt-16 text-center text-gray-600 dark:text-gray-400 text-sm">
|
|
1701
|
-
<p>Built with SwallowKit</p>
|
|
1702
|
-
</footer>
|
|
1703
|
-
</div>
|
|
1704
|
-
</div>
|
|
1705
|
-
);
|
|
1706
|
-
}
|
|
1707
|
-
`;
|
|
1708
|
-
|
|
1709
|
-
fs.writeFileSync(path.join(projectDir, 'app', 'page.tsx'), pageContent);
|
|
1664
|
+
fs.writeFileSync(path.join(projectDir, 'app', 'page.tsx'), buildHomePage("nextjs", pm));
|
|
1710
1665
|
|
|
1711
1666
|
console.log('✅ Home page created\n');
|
|
1712
1667
|
|
|
@@ -1760,9 +1715,8 @@ function createReadme(
|
|
|
1760
1715
|
const backendScaffoldNote = backendLanguage === 'typescript'
|
|
1761
1716
|
? '- Azure Functions CRUD endpoints'
|
|
1762
1717
|
: `- Azure Functions ${backendLanguageLabel} CRUD handlers\n- OpenAPI export + native-generated ${backendLanguageLabel} schema assets`;
|
|
1763
|
-
const pythonUvPaths = getProjectLocalUvPaths(projectDir);
|
|
1764
1718
|
const pythonLocalDevNote = backendLanguage === 'python'
|
|
1765
|
-
? `\n**Python local dev note**: SwallowKit uses \`uv\`
|
|
1719
|
+
? `\n**Python local dev note**: SwallowKit uses normal \`uv\` interpreter discovery and settings, including installed Python, \`.python-version\`, and \`UV_PYTHON\`. It does not pin local Python to the Azure deployment version (${DEFAULT_AZURE_PYTHON_VERSION}). Local Functions uses \`functions/.venv\` and schema generation uses \`functions/.codegen-venv\`. Existing environments are preserved; explicitly recreate them with uv when switching interpreters. A missing uv binary is bootstrapped under \`.uv/bin\` without changing PATH. Keep \`functions/requirements.txt\` and \`functions/requirements.codegen.txt\` as the dependency sources of truth.\n`
|
|
1766
1720
|
: '';
|
|
1767
1721
|
|
|
1768
1722
|
const readme = `# ${projectName}
|
|
@@ -1871,10 +1825,12 @@ ${pmCmd.exec} swallowkit provision --resource-group <rg-name>
|
|
|
1871
1825
|
|
|
1872
1826
|
This creates:
|
|
1873
1827
|
- Static Web App (\`swa-${projectName}\`)
|
|
1874
|
-
- Azure Functions (\`func-${projectName}
|
|
1875
|
-
- Cosmos DB (\`cosmos-${projectName}
|
|
1828
|
+
- Azure Functions (\`func-${projectName}-<suffix>\`)
|
|
1829
|
+
- Cosmos DB (\`cosmos-${projectName}-<suffix>\`)
|
|
1876
1830
|
- Storage Account
|
|
1877
1831
|
|
|
1832
|
+
Cosmos DB and Function App names must be globally unique, so they include a short suffix derived from the resource group. When provisioning completes, the command prints the actual resource names and the CI/CD settings to use.
|
|
1833
|
+
|
|
1878
1834
|
You will be prompted to select Azure regions:
|
|
1879
1835
|
1. **Primary location**: For Functions and Cosmos DB (default: Japan East)
|
|
1880
1836
|
2. **Static Web App location**: Limited availability (default: East Asia)
|
|
@@ -1890,12 +1846,12 @@ ${cicdChoice === 'github' ? `#### GitHub Actions
|
|
|
1890
1846
|
|
|
1891
1847
|
2. Get Function App publish profile:
|
|
1892
1848
|
\`\`\`bash
|
|
1893
|
-
az webapp deployment list-publishing-profiles --name
|
|
1849
|
+
az webapp deployment list-publishing-profiles --name <functions-app-name> --resource-group <rg-name> --xml
|
|
1894
1850
|
\`\`\`
|
|
1895
1851
|
|
|
1896
1852
|
3. Add secrets to GitHub repository:
|
|
1897
1853
|
- \`AZURE_STATIC_WEB_APPS_API_TOKEN\`: SWA deployment token (from step 1)
|
|
1898
|
-
- \`AZURE_FUNCTIONAPP_NAME\`: \`func-${projectName}
|
|
1854
|
+
- \`AZURE_FUNCTIONAPP_NAME\`: Functions app name printed by \`swallowkit provision\` (\`func-${projectName}-<suffix>\`)
|
|
1899
1855
|
- \`AZURE_FUNCTIONAPP_PUBLISH_PROFILE\`: Functions publish profile (from step 2)
|
|
1900
1856
|
|
|
1901
1857
|
4. Push to \`main\` branch to trigger deployment (or use **Actions** → **Run workflow** for manual deployment)` : cicdChoice === 'azure' ? `#### Azure Pipelines
|
|
@@ -1917,8 +1873,10 @@ az staticwebapp deploy --name swa-${projectName} --resource-group <rg-name> --ap
|
|
|
1917
1873
|
\`\`\`bash
|
|
1918
1874
|
cd functions
|
|
1919
1875
|
${pmCmd.run} build
|
|
1920
|
-
func azure functionapp publish
|
|
1921
|
-
|
|
1876
|
+
func azure functionapp publish <functions-app-name>
|
|
1877
|
+
\`\`\`
|
|
1878
|
+
|
|
1879
|
+
Use the Functions app name printed by \`swallowkit provision\`.`}
|
|
1922
1880
|
|
|
1923
1881
|
## 🔧 Available Commands
|
|
1924
1882
|
|
|
@@ -1978,7 +1936,8 @@ function createAiAgentFiles(
|
|
|
1978
1936
|
projectDir: string,
|
|
1979
1937
|
projectName: string,
|
|
1980
1938
|
backendLanguage: BackendLanguage,
|
|
1981
|
-
pm: PackageManager
|
|
1939
|
+
pm: PackageManager,
|
|
1940
|
+
frontend: FrontendFramework = "nextjs"
|
|
1982
1941
|
) {
|
|
1983
1942
|
console.log('🤖 Creating AI agent instruction files...\n');
|
|
1984
1943
|
const runCmd = getCommands(pm).exec;
|
|
@@ -1987,17 +1946,23 @@ function createAiAgentFiles(
|
|
|
1987
1946
|
projectName,
|
|
1988
1947
|
backendLanguage,
|
|
1989
1948
|
packageManager: pm,
|
|
1949
|
+
frontend,
|
|
1990
1950
|
});
|
|
1991
1951
|
console.log(' ✅ AGENTS.md (canonical project contract)');
|
|
1992
1952
|
console.log(' ✅ CLAUDE.md (Claude Code adapter)');
|
|
1993
1953
|
console.log(' ✅ .github/copilot-instructions.md (GitHub Copilot adapter)');
|
|
1994
1954
|
console.log(' ✅ .github/instructions/ (path-specific instructions)');
|
|
1995
1955
|
|
|
1956
|
+
const mcpVersion = selectInitialMcpVersion();
|
|
1996
1957
|
fs.writeFileSync(
|
|
1997
1958
|
path.join(projectDir, '.mcp.json'),
|
|
1998
|
-
buildSwallowKitMcpProjectConfigSource(pm)
|
|
1959
|
+
buildSwallowKitMcpProjectConfigSource(pm, mcpVersion.version)
|
|
1960
|
+
);
|
|
1961
|
+
console.log(
|
|
1962
|
+
mcpVersion.version === MCP_LATEST
|
|
1963
|
+
? ' ✅ .mcp.json (project-scoped MCP bootstrap, resolves latest)'
|
|
1964
|
+
: ` ✅ .mcp.json (project-scoped MCP bootstrap, pinned to ${mcpVersion.version}: ${mcpVersion.reason} Run "${runCmd} swallowkit configure-mcp" to re-select later.)`
|
|
1999
1965
|
);
|
|
2000
|
-
console.log(' ✅ .mcp.json (project-scoped MCP bootstrap)');
|
|
2001
1966
|
|
|
2002
1967
|
writeWorkflowDocs(projectDir, runCmd);
|
|
2003
1968
|
console.log(' ✅ .swallowkit/workflows/ (agent-agnostic autonomous-loop runbooks)');
|
|
@@ -2088,6 +2053,14 @@ param cosmosDbMode string = '${azureConfig.cosmosDbMode}'
|
|
|
2088
2053
|
@description('Enable VNet integration')
|
|
2089
2054
|
param enableVNet bool = ${enableVNet}
|
|
2090
2055
|
|
|
2056
|
+
// Cosmos DB account and Function App names are globally unique across Azure.
|
|
2057
|
+
// A suffix derived from the resource group keeps names stable for redeployments
|
|
2058
|
+
// to the same resource group while avoiding collisions with other environments
|
|
2059
|
+
// and with names Azure still reserves after a previous resource group is deleted.
|
|
2060
|
+
var resourceSuffix = take(uniqueString(resourceGroup().id, projectName), 6)
|
|
2061
|
+
var cosmosAccountName = 'cosmos-\${take(toLower(projectName), 30)}-\${resourceSuffix}'
|
|
2062
|
+
var functionsAppName = 'func-\${take(projectName, 40)}-\${resourceSuffix}'
|
|
2063
|
+
|
|
2091
2064
|
// Shared Log Analytics Workspace (in Functions region for data residency)
|
|
2092
2065
|
module logAnalytics 'modules/loganalytics.bicep' = {
|
|
2093
2066
|
name: 'logAnalytics'
|
|
@@ -2141,7 +2114,7 @@ module vnet 'modules/vnet.bicep' = if (enableVNet) {
|
|
|
2141
2114
|
module cosmosDbFreeTier 'modules/cosmosdb-freetier.bicep' = if (cosmosDbMode == 'freetier') {
|
|
2142
2115
|
name: 'cosmosDb'
|
|
2143
2116
|
params: {
|
|
2144
|
-
accountName:
|
|
2117
|
+
accountName: cosmosAccountName
|
|
2145
2118
|
databaseName: '\${projectName}Database'
|
|
2146
2119
|
location: location
|
|
2147
2120
|
publicNetworkAccess: enableVNet ? 'Disabled' : 'Enabled'
|
|
@@ -2151,7 +2124,7 @@ module cosmosDbFreeTier 'modules/cosmosdb-freetier.bicep' = if (cosmosDbMode ==
|
|
|
2151
2124
|
module cosmosDbServerless 'modules/cosmosdb-serverless.bicep' = if (cosmosDbMode == 'serverless') {
|
|
2152
2125
|
name: 'cosmosDb'
|
|
2153
2126
|
params: {
|
|
2154
|
-
accountName:
|
|
2127
|
+
accountName: cosmosAccountName
|
|
2155
2128
|
databaseName: '\${projectName}Database'
|
|
2156
2129
|
location: location
|
|
2157
2130
|
publicNetworkAccess: enableVNet ? 'Disabled' : 'Enabled'
|
|
@@ -2175,7 +2148,7 @@ module cosmosPrivateEndpoint 'modules/private-endpoint-cosmos.bicep' = if (enabl
|
|
|
2175
2148
|
module functionsFlex 'modules/functions-flex.bicep' = {
|
|
2176
2149
|
name: 'functionsApp'
|
|
2177
2150
|
params: {
|
|
2178
|
-
name:
|
|
2151
|
+
name: functionsAppName
|
|
2179
2152
|
location: location
|
|
2180
2153
|
storageAccountName: 'stg\${uniqueString(resourceGroup().id, projectName)}'
|
|
2181
2154
|
appInsightsConnectionString: appInsightsFunctions.outputs.connectionString
|
|
@@ -2911,7 +2884,7 @@ jobs:
|
|
|
2911
2884
|
${commonSetup} - name: Setup Python
|
|
2912
2885
|
uses: actions/setup-python@v5
|
|
2913
2886
|
with:
|
|
2914
|
-
python-version: '
|
|
2887
|
+
python-version: '${DEFAULT_AZURE_PYTHON_VERSION}'
|
|
2915
2888
|
|
|
2916
2889
|
- name: Install Functions dependencies
|
|
2917
2890
|
run: |
|
|
@@ -3100,7 +3073,7 @@ variables:
|
|
|
3100
3073
|
steps:
|
|
3101
3074
|
${commonSetup} - task: UsePythonVersion@0
|
|
3102
3075
|
inputs:
|
|
3103
|
-
versionSpec: '
|
|
3076
|
+
versionSpec: '${DEFAULT_AZURE_PYTHON_VERSION}'
|
|
3104
3077
|
displayName: 'Install Python'
|
|
3105
3078
|
|
|
3106
3079
|
- script: |
|
|
@@ -3133,7 +3106,8 @@ export function getSwaNodeVersionCommand(): string {
|
|
|
3133
3106
|
return `node -e "const fs=require('fs');const p=JSON.parse(fs.readFileSync('package.json','utf8'));p.engines={...p.engines,node:'>=22.14.0 <23'};fs.writeFileSync('package.json',JSON.stringify(p,null,2)+String.fromCharCode(10));"`;
|
|
3134
3107
|
}
|
|
3135
3108
|
|
|
3136
|
-
export function buildAzureSwaPipeline(pm: PackageManager, projectPm: PackageManager = pm): string {
|
|
3109
|
+
export function buildAzureSwaPipeline(pm: PackageManager, projectPm: PackageManager = pm, frontend: FrontendFramework = "nextjs"): string {
|
|
3110
|
+
if (frontend === "react") return buildReactAzureSwaPipeline(projectPm);
|
|
3137
3111
|
// SWA/Oryx also invokes npm internally; normalize only the disposable CI checkout.
|
|
3138
3112
|
pm = 'npm';
|
|
3139
3113
|
const pmCmd = getCommands(pm);
|
|
@@ -3321,7 +3295,8 @@ async function createGitHubActionsWorkflows(
|
|
|
3321
3295
|
projectDir: string,
|
|
3322
3296
|
azureConfig: AzureConfig,
|
|
3323
3297
|
pm: PackageManager,
|
|
3324
|
-
backendLanguage: BackendLanguage
|
|
3298
|
+
backendLanguage: BackendLanguage,
|
|
3299
|
+
frontend: FrontendFramework = "nextjs"
|
|
3325
3300
|
) {
|
|
3326
3301
|
console.log('📦 Creating GitHub Actions workflows...\n');
|
|
3327
3302
|
const workflowsDir = path.join(projectDir, '.github', 'workflows');
|
|
@@ -3329,7 +3304,7 @@ async function createGitHubActionsWorkflows(
|
|
|
3329
3304
|
|
|
3330
3305
|
const actionsPm: PackageManager = pm;
|
|
3331
3306
|
|
|
3332
|
-
const swaWorkflow = buildGitHubSwaWorkflow(actionsPm, pm);
|
|
3307
|
+
const swaWorkflow = buildGitHubSwaWorkflow(actionsPm, pm, frontend);
|
|
3333
3308
|
fs.writeFileSync(path.join(workflowsDir, 'deploy-swa.yml'), swaWorkflow);
|
|
3334
3309
|
|
|
3335
3310
|
// deploy-functions.yml
|
|
@@ -3339,7 +3314,8 @@ async function createGitHubActionsWorkflows(
|
|
|
3339
3314
|
console.log('✅ GitHub Actions workflows created\n');
|
|
3340
3315
|
}
|
|
3341
3316
|
|
|
3342
|
-
export function buildGitHubSwaWorkflow(pm: PackageManager, projectPm: PackageManager = pm): string {
|
|
3317
|
+
export function buildGitHubSwaWorkflow(pm: PackageManager, projectPm: PackageManager = pm, frontend: FrontendFramework = "nextjs"): string {
|
|
3318
|
+
if (frontend === "react") return buildReactGitHubSwaWorkflow(projectPm);
|
|
3343
3319
|
// SWA/Oryx also invokes npm internally; normalize only the disposable CI checkout.
|
|
3344
3320
|
pm = 'npm';
|
|
3345
3321
|
const npmWorkspaceNormalizationStep = getGitHubNpmWorkspaceNormalizationStep(pm, projectPm);
|
|
@@ -3417,7 +3393,7 @@ ${npmWorkspaceNormalizationStep ? `\n${npmWorkspaceNormalizationStep}` : ''}
|
|
|
3417
3393
|
`;
|
|
3418
3394
|
}
|
|
3419
3395
|
|
|
3420
|
-
async function createAzurePipelines(projectDir: string, pm: PackageManager, backendLanguage: BackendLanguage) {
|
|
3396
|
+
async function createAzurePipelines(projectDir: string, pm: PackageManager, backendLanguage: BackendLanguage, frontend: FrontendFramework = "nextjs") {
|
|
3421
3397
|
console.log('📦 Creating Azure Pipelines...\n');
|
|
3422
3398
|
|
|
3423
3399
|
const pipelinesPm: PackageManager = pm;
|
|
@@ -3425,7 +3401,7 @@ async function createAzurePipelines(projectDir: string, pm: PackageManager, back
|
|
|
3425
3401
|
fs.mkdirSync(pipelinesDir, { recursive: true });
|
|
3426
3402
|
|
|
3427
3403
|
// swa.yml
|
|
3428
|
-
const swaPipeline = buildAzureSwaPipeline(pipelinesPm, pm);
|
|
3404
|
+
const swaPipeline = buildAzureSwaPipeline(pipelinesPm, pm, frontend);
|
|
3429
3405
|
fs.writeFileSync(path.join(pipelinesDir, 'swa.yml'), swaPipeline);
|
|
3430
3406
|
|
|
3431
3407
|
// functions.yml
|
|
@@ -3434,4 +3410,3 @@ async function createAzurePipelines(projectDir: string, pm: PackageManager, back
|
|
|
3434
3410
|
|
|
3435
3411
|
console.log('✅ Azure Pipelines created\n');
|
|
3436
3412
|
}
|
|
3437
|
-
|