swallowkit 2.0.0-alpha.4 → 2.0.0-alpha.5

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.
Files changed (76) hide show
  1. package/dist/cli/commands/add-auth.js +37 -6
  2. package/dist/cli/commands/add-auth.js.map +1 -1
  3. package/dist/cli/commands/init.d.ts +2 -0
  4. package/dist/cli/commands/init.d.ts.map +1 -1
  5. package/dist/cli/commands/init.js +3 -1
  6. package/dist/cli/commands/init.js.map +1 -1
  7. package/dist/cli/commands/scaffold.d.ts.map +1 -1
  8. package/dist/cli/commands/scaffold.js +45 -1
  9. package/dist/cli/commands/scaffold.js.map +1 -1
  10. package/dist/cli/index.js +2 -2
  11. package/dist/cli/index.js.map +1 -1
  12. package/dist/core/operations/create-model.d.ts +2 -0
  13. package/dist/core/operations/create-model.d.ts.map +1 -1
  14. package/dist/core/operations/create-model.js +60 -8
  15. package/dist/core/operations/create-model.js.map +1 -1
  16. package/dist/core/project/boundaries.js +3 -3
  17. package/dist/core/project/boundaries.js.map +1 -1
  18. package/dist/core/project/capabilities.d.ts +1 -0
  19. package/dist/core/project/capabilities.d.ts.map +1 -1
  20. package/dist/core/project/capabilities.js +9 -3
  21. package/dist/core/project/capabilities.js.map +1 -1
  22. package/dist/core/project/react-project.js +1 -1
  23. package/dist/core/project/workflows.d.ts.map +1 -1
  24. package/dist/core/project/workflows.js +15 -5
  25. package/dist/core/project/workflows.js.map +1 -1
  26. package/dist/core/scaffold/auth-generator.d.ts.map +1 -1
  27. package/dist/core/scaffold/auth-generator.js +18 -19
  28. package/dist/core/scaffold/auth-generator.js.map +1 -1
  29. package/dist/core/scaffold/native-schema-generator.d.ts +2 -0
  30. package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -1
  31. package/dist/core/scaffold/native-schema-generator.js +16 -1
  32. package/dist/core/scaffold/native-schema-generator.js.map +1 -1
  33. package/dist/core/verify/index.d.ts +4 -3
  34. package/dist/core/verify/index.d.ts.map +1 -1
  35. package/dist/core/verify/index.js +98 -4
  36. package/dist/core/verify/index.js.map +1 -1
  37. package/dist/machine/contracts.d.ts +1 -0
  38. package/dist/machine/contracts.d.ts.map +1 -1
  39. package/dist/machine/errors.d.ts +3 -2
  40. package/dist/machine/errors.d.ts.map +1 -1
  41. package/dist/machine/errors.js +2 -1
  42. package/dist/machine/errors.js.map +1 -1
  43. package/dist/machine/index.d.ts.map +1 -1
  44. package/dist/machine/index.js +3 -2
  45. package/dist/machine/index.js.map +1 -1
  46. package/dist/mcp/index.d.ts +1 -0
  47. package/dist/mcp/index.d.ts.map +1 -1
  48. package/dist/mcp/index.js +19 -4
  49. package/dist/mcp/index.js.map +1 -1
  50. package/package.json +6 -3
  51. package/src/__tests__/__snapshots__/boundaries.test.ts.snap +6 -6
  52. package/src/__tests__/agent-loop-phase34.test.ts +95 -0
  53. package/src/__tests__/generated-project-matrix.test.ts +789 -0
  54. package/src/__tests__/mcp.test.ts +15 -0
  55. package/src/__tests__/multi-auth.test.ts +115 -4
  56. package/src/__tests__/plan-apply.test.ts +111 -0
  57. package/src/cli/commands/add-auth.ts +36 -6
  58. package/src/cli/commands/init.ts +4 -4
  59. package/src/cli/commands/scaffold.ts +65 -1
  60. package/src/cli/index.ts +2 -2
  61. package/src/core/operations/create-model.ts +51 -13
  62. package/src/core/project/agent-templates/AGENTS.md +28 -3
  63. package/src/core/project/agent-templates/CLAUDE.md +4 -0
  64. package/src/core/project/agent-templates/copilot-instructions.md +4 -0
  65. package/src/core/project/agent-templates/shared-models.instructions.md +11 -3
  66. package/src/core/project/boundaries.ts +3 -3
  67. package/src/core/project/capabilities.ts +10 -3
  68. package/src/core/project/react-project.ts +1 -1
  69. package/src/core/project/workflows.ts +15 -5
  70. package/src/core/scaffold/auth-generator.ts +18 -19
  71. package/src/core/scaffold/native-schema-generator.ts +16 -0
  72. package/src/core/verify/index.ts +95 -5
  73. package/src/machine/contracts.ts +1 -0
  74. package/src/machine/errors.ts +4 -2
  75. package/src/machine/index.ts +3 -2
  76. package/src/mcp/index.ts +19 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swallowkit",
3
- "version": "2.0.0-alpha.4",
3
+ "version": "2.0.0-alpha.5",
4
4
  "description": "Type-safe schema-driven development toolkit for Next.js and React applications on Azure",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -46,21 +46,24 @@
46
46
  "zod": "^3.25.76"
47
47
  },
48
48
  "devDependencies": {
49
+ "@azure/functions": "~4.5.0",
49
50
  "@types/jest": "^29.0.0",
50
51
  "@types/jsonwebtoken": "^9.0.0",
51
52
  "@types/node": "^20.0.0",
52
53
  "@types/prompts": "^2.4.9",
53
54
  "@types/react": "^18.0.0",
55
+ "@types/react-dom": "^18.3.7",
54
56
  "@typescript-eslint/eslint-plugin": "^6.0.0",
55
57
  "@typescript-eslint/parser": "^6.0.0",
58
+ "azure-functions-v3": "npm:@azure/functions@^3.6.0",
56
59
  "eslint": "^8.0.0",
57
60
  "jest": "^29.0.0",
61
+ "next": "^16.2.6",
58
62
  "prettier": "^3.0.0",
59
63
  "react": "^18.3.1",
60
64
  "react-dom": "^18.3.1",
61
65
  "ts-jest": "^29.0.0",
62
- "vitepress": "^1.6.4",
63
- "next": "^16.2.6"
66
+ "vitepress": "^1.6.4"
64
67
  },
65
68
  "engines": {
66
69
  "node": ">=22.14.0"
@@ -33,8 +33,8 @@ exports[`inspect boundaries convention rules matches the csharp backend 1`] = `
33
33
  "zone": "deterministic",
34
34
  },
35
35
  {
36
- "description": "SwallowKit appends model exports; other exports may be added freely.",
37
- "editPolicy": "edit-outside-markers",
36
+ "description": "create-model and scaffold append missing model exports while preserving existing content. No managed markers; manual exports are allowed. Keep exports needed by generated code.",
37
+ "editPolicy": "free-edit",
38
38
  "pattern": "shared/index.ts",
39
39
  "zone": "shared",
40
40
  },
@@ -80,8 +80,8 @@ exports[`inspect boundaries convention rules matches the python backend 1`] = `
80
80
  "zone": "deterministic",
81
81
  },
82
82
  {
83
- "description": "SwallowKit appends model exports; other exports may be added freely.",
84
- "editPolicy": "edit-outside-markers",
83
+ "description": "create-model and scaffold append missing model exports while preserving existing content. No managed markers; manual exports are allowed. Keep exports needed by generated code.",
84
+ "editPolicy": "free-edit",
85
85
  "pattern": "shared/index.ts",
86
86
  "zone": "shared",
87
87
  },
@@ -139,8 +139,8 @@ exports[`inspect boundaries convention rules matches the typescript backend 1`]
139
139
  "zone": "deterministic",
140
140
  },
141
141
  {
142
- "description": "SwallowKit appends model exports; other exports may be added freely.",
143
- "editPolicy": "edit-outside-markers",
142
+ "description": "create-model and scaffold append missing model exports while preserving existing content. No managed markers; manual exports are allowed. Keep exports needed by generated code.",
143
+ "editPolicy": "free-edit",
144
144
  "pattern": "shared/index.ts",
145
145
  "zone": "shared",
146
146
  },
@@ -559,6 +559,101 @@ describe("agent loop phase 3/4", () => {
559
559
  );
560
560
  });
561
561
 
562
+ it("includes standalone C# compilation in default verification and explains source errors", async () => {
563
+ createProjectFixture(tempDir);
564
+ writeFile(path.join(tempDir, "swallowkit.config.json"), JSON.stringify({ backend: { language: "csharp" } }));
565
+ // The fixture's .js config otherwise takes precedence.
566
+ fs.unlinkSync(path.join(tempDir, "swallowkit.config.js"));
567
+ fs.mkdirSync(path.join(tempDir, "shared", "models"), { recursive: true });
568
+ fs.mkdirSync(path.join(tempDir, "app", "api"), { recursive: true });
569
+ writeFile(path.join(tempDir, "functions", "SwallowKit.Functions.csproj"), "<Project />\n");
570
+ const spawnMock = childProcess.spawnSync as unknown as ReturnType<typeof jest.fn>;
571
+ spawnMock.mockImplementation((command: string, args: string[]) => ({
572
+ status: command === "dotnet" && args[0] === "build" ? 1 : 0,
573
+ stdout: command === "dotnet" && args[0] === "build"
574
+ ? `${path.join(tempDir, "functions", "Crud", "ProbeItemCrudFunctions.cs")}(8,7): error CS0246: Functions namespace not found`
575
+ : "",
576
+ stderr: "",
577
+ }));
578
+ const result = await runVerify(undefined, tempDir);
579
+ expect(result.summary.done).toBe(false);
580
+ expect(result.checks).toContainEqual(expect.objectContaining({
581
+ id: "backend-build", status: "fail",
582
+ evidence: expect.objectContaining({ logTail: expect.arrayContaining([expect.stringContaining("(8,7): error CS0246")]) }),
583
+ }));
584
+ const explanation = await runMachine(["node", "swallowkit", "machine", "explain", "failure", "--check", "backend-build"]);
585
+ expect(JSON.stringify(explanation.response)).toContain("(8,7): error CS0246");
586
+ });
587
+
588
+ it.each(["csharp", "python"] as const)("skips %s backend compilation when its toolchain is missing", async (language) => {
589
+ createProjectFixture(tempDir);
590
+ writeFile(path.join(tempDir, "swallowkit.config.js"), `module.exports = { backend: { language: '${language}' } };`);
591
+ const missing = Object.assign(new Error("not found"), { code: "ENOENT" });
592
+ (childProcess.spawnSync as unknown as ReturnType<typeof jest.fn>).mockReturnValue({ status: null, error: missing });
593
+ const result = await runVerify(["backend-build"], tempDir);
594
+ expect(result.checks[0]).toMatchObject({ id: "backend-build", status: "skip" });
595
+ expect(result.checks[0].evidence.diagnostics?.[0]).toMatch(/not installed|not available/);
596
+ expect(result.summary).toMatchObject({ skipped: 1, failed: 0 });
597
+ });
598
+
599
+ it("does not hide an installed backend toolchain execution failure as a skip", async () => {
600
+ createProjectFixture(tempDir);
601
+ writeFile(path.join(tempDir, "swallowkit.config.js"), "module.exports = { backend: { language: 'csharp' } };");
602
+ const denied = Object.assign(new Error("spawn EPERM"), { code: "EPERM" });
603
+ (childProcess.spawnSync as unknown as ReturnType<typeof jest.fn>).mockReturnValue({ status: null, error: denied });
604
+ const result = await runVerify(["backend-build"], tempDir);
605
+ expect(result.checks[0]).toMatchObject({ status: "error", evidence: { errorCode: "verification-command-unavailable" } });
606
+ });
607
+
608
+ it("uses the project's compiler and functions tsconfig for TypeScript backend verification", async () => {
609
+ createProjectFixture(tempDir);
610
+ fs.symlinkSync(path.join(repoRoot, "node_modules", "typescript"), path.join(tempDir, "node_modules", "typescript"), "junction");
611
+ const spawnMock = childProcess.spawnSync as unknown as ReturnType<typeof jest.fn>;
612
+ spawnMock.mockReturnValue({ status: 2, stdout: "src/probe-item.ts(9,3): error TS2322: Invalid type", stderr: "" });
613
+ const result = await runVerify(["backend-build"], tempDir);
614
+ expect(result.summary.done).toBe(false);
615
+ expect(result.checks[0].evidence.diagnostics).toContain("src/probe-item.ts(9,3): error TS2322: Invalid type");
616
+ expect(spawnMock).toHaveBeenCalledWith(process.execPath, [
617
+ expect.stringContaining(path.join("typescript", "bin", "tsc")), "--project", "tsconfig.json", "--noEmit",
618
+ ], expect.objectContaining({ cwd: path.join(tempDir, "functions"), shell: false }));
619
+ });
620
+
621
+ it("prefers the existing Python dev virtualenv and includes source syntax errors", async () => {
622
+ createProjectFixture(tempDir);
623
+ writeFile(path.join(tempDir, "swallowkit.config.js"), "module.exports = { backend: { language: 'python' } };");
624
+ const python = path.join(tempDir, "functions", ".venv", ...(process.platform === "win32" ? ["Scripts", "python.exe"] : ["bin", "python"]));
625
+ writeFile(python, "mock interpreter");
626
+ const spawnMock = childProcess.spawnSync as unknown as ReturnType<typeof jest.fn>;
627
+ spawnMock.mockImplementation((_command: string, args: string[]) => args.includes("compileall")
628
+ ? { status: 1, stdout: '*** Error compiling "./blueprints/probe_item.py"...\n File "./blueprints/probe_item.py", line 12\nSyntaxError: invalid syntax', stderr: "" }
629
+ : { status: 0, stdout: "", stderr: "" });
630
+ const result = await runVerify(["backend-build"], tempDir);
631
+ expect(result.checks[0].status).toBe("fail");
632
+ expect(result.checks[0].evidence.diagnostics).toContain(' File "./blueprints/probe_item.py", line 12');
633
+ expect(spawnMock).toHaveBeenCalledWith(python, [
634
+ "-m", "compileall", "-q", "-f", "-x", expect.stringContaining(".codegen-venv"), ".",
635
+ ], expect.objectContaining({ cwd: path.join(tempDir, "functions"), shell: false }));
636
+ });
637
+
638
+ it.each([
639
+ ["python", "python"],
640
+ ["unsupported", "typescript"],
641
+ ])("resolves the backend like generation when SWALLOWKIT_BACKEND_LANGUAGE=%s", async (override, expected) => {
642
+ createProjectFixture(tempDir); // config file says typescript
643
+ fs.symlinkSync(path.join(repoRoot, "node_modules", "typescript"), path.join(tempDir, "node_modules", "typescript"), "junction");
644
+ const spawnMock = childProcess.spawnSync as unknown as ReturnType<typeof jest.fn>;
645
+ spawnMock.mockReturnValue({ status: 0, stdout: "", stderr: "" });
646
+ const previous = process.env.SWALLOWKIT_BACKEND_LANGUAGE;
647
+ process.env.SWALLOWKIT_BACKEND_LANGUAGE = override;
648
+ try {
649
+ const result = await runVerify(["backend-build"], tempDir);
650
+ expect(result.checks[0].evidence.command).toContain(expected === "python" ? "compileall" : "tsc");
651
+ } finally {
652
+ if (previous === undefined) delete process.env.SWALLOWKIT_BACKEND_LANGUAGE;
653
+ else process.env.SWALLOWKIT_BACKEND_LANGUAGE = previous;
654
+ }
655
+ });
656
+
562
657
  it("reports verification command startup failures as blocked", async () => {
563
658
  createProjectFixture(tempDir);
564
659
  writeFile(path.join(tempDir, "tsconfig.json"), JSON.stringify({ compilerOptions: { noEmit: true } }));