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.
Files changed (262) hide show
  1. package/README.ja.md +61 -7
  2. package/README.md +61 -7
  3. package/dist/cli/commands/add-auth.d.ts.map +1 -1
  4. package/dist/cli/commands/add-auth.js +103 -27
  5. package/dist/cli/commands/add-auth.js.map +1 -1
  6. package/dist/cli/commands/configure-mcp.d.ts +12 -0
  7. package/dist/cli/commands/configure-mcp.d.ts.map +1 -0
  8. package/dist/cli/commands/configure-mcp.js +42 -0
  9. package/dist/cli/commands/configure-mcp.js.map +1 -0
  10. package/dist/cli/commands/dev.d.ts +5 -2
  11. package/dist/cli/commands/dev.d.ts.map +1 -1
  12. package/dist/cli/commands/dev.js +143 -67
  13. package/dist/cli/commands/dev.js.map +1 -1
  14. package/dist/cli/commands/init.d.ts +14 -7
  15. package/dist/cli/commands/init.d.ts.map +1 -1
  16. package/dist/cli/commands/init.js +149 -170
  17. package/dist/cli/commands/init.js.map +1 -1
  18. package/dist/cli/commands/migrations.d.ts.map +1 -1
  19. package/dist/cli/commands/migrations.js +25 -0
  20. package/dist/cli/commands/migrations.js.map +1 -1
  21. package/dist/cli/commands/provision.d.ts +5 -0
  22. package/dist/cli/commands/provision.d.ts.map +1 -1
  23. package/dist/cli/commands/provision.js +278 -55
  24. package/dist/cli/commands/provision.js.map +1 -1
  25. package/dist/cli/commands/scaffold.d.ts +5 -1
  26. package/dist/cli/commands/scaffold.d.ts.map +1 -1
  27. package/dist/cli/commands/scaffold.js +123 -18
  28. package/dist/cli/commands/scaffold.js.map +1 -1
  29. package/dist/cli/index.d.ts +2 -0
  30. package/dist/cli/index.d.ts.map +1 -1
  31. package/dist/cli/index.js +49 -8
  32. package/dist/cli/index.js.map +1 -1
  33. package/dist/core/config.d.ts +2 -1
  34. package/dist/core/config.d.ts.map +1 -1
  35. package/dist/core/config.js +19 -0
  36. package/dist/core/config.js.map +1 -1
  37. package/dist/core/operations/create-model.d.ts +2 -0
  38. package/dist/core/operations/create-model.d.ts.map +1 -1
  39. package/dist/core/operations/create-model.js +60 -8
  40. package/dist/core/operations/create-model.js.map +1 -1
  41. package/dist/core/operations/migration-generator.d.ts.map +1 -1
  42. package/dist/core/operations/migration-generator.js +12 -1
  43. package/dist/core/operations/migration-generator.js.map +1 -1
  44. package/dist/core/operations/model-migration.d.ts +22 -0
  45. package/dist/core/operations/model-migration.d.ts.map +1 -0
  46. package/dist/core/operations/model-migration.js +36 -0
  47. package/dist/core/operations/model-migration.js.map +1 -0
  48. package/dist/core/operations/provision-deployment.d.ts +52 -0
  49. package/dist/core/operations/provision-deployment.d.ts.map +1 -0
  50. package/dist/core/operations/provision-deployment.js +164 -0
  51. package/dist/core/operations/provision-deployment.js.map +1 -0
  52. package/dist/core/operations/provision-operations.d.ts +62 -6
  53. package/dist/core/operations/provision-operations.d.ts.map +1 -1
  54. package/dist/core/operations/provision-operations.js +653 -115
  55. package/dist/core/operations/provision-operations.js.map +1 -1
  56. package/dist/core/operations/scaffold-apply.d.ts +1 -0
  57. package/dist/core/operations/scaffold-apply.d.ts.map +1 -1
  58. package/dist/core/operations/scaffold-apply.js +1 -0
  59. package/dist/core/operations/scaffold-apply.js.map +1 -1
  60. package/dist/core/operations/scaffold-machine.d.ts +1 -0
  61. package/dist/core/operations/scaffold-machine.d.ts.map +1 -1
  62. package/dist/core/operations/scaffold-machine.js.map +1 -1
  63. package/dist/core/operations/scaffold-plan.d.ts +2 -0
  64. package/dist/core/operations/scaffold-plan.d.ts.map +1 -1
  65. package/dist/core/operations/scaffold-plan.js +3 -1
  66. package/dist/core/operations/scaffold-plan.js.map +1 -1
  67. package/dist/core/project/agent-instructions.d.ts +2 -1
  68. package/dist/core/project/agent-instructions.d.ts.map +1 -1
  69. package/dist/core/project/agent-instructions.js +15 -0
  70. package/dist/core/project/agent-instructions.js.map +1 -1
  71. package/dist/core/project/boundaries.d.ts +5 -2
  72. package/dist/core/project/boundaries.d.ts.map +1 -1
  73. package/dist/core/project/boundaries.js +51 -20
  74. package/dist/core/project/boundaries.js.map +1 -1
  75. package/dist/core/project/capabilities.d.ts +17 -0
  76. package/dist/core/project/capabilities.d.ts.map +1 -1
  77. package/dist/core/project/capabilities.js +45 -6
  78. package/dist/core/project/capabilities.js.map +1 -1
  79. package/dist/core/project/home-page.d.ts +4 -0
  80. package/dist/core/project/home-page.d.ts.map +1 -0
  81. package/dist/core/project/home-page.js +113 -0
  82. package/dist/core/project/home-page.js.map +1 -0
  83. package/dist/core/project/manifest.d.ts +3 -1
  84. package/dist/core/project/manifest.d.ts.map +1 -1
  85. package/dist/core/project/manifest.js +47 -25
  86. package/dist/core/project/manifest.js.map +1 -1
  87. package/dist/core/project/mcp-config.d.ts +26 -0
  88. package/dist/core/project/mcp-config.d.ts.map +1 -0
  89. package/dist/core/project/mcp-config.js +117 -0
  90. package/dist/core/project/mcp-config.js.map +1 -0
  91. package/dist/core/project/react-project.d.ts +26 -0
  92. package/dist/core/project/react-project.d.ts.map +1 -0
  93. package/dist/core/project/react-project.js +443 -0
  94. package/dist/core/project/react-project.js.map +1 -0
  95. package/dist/core/project/state.d.ts +7 -0
  96. package/dist/core/project/state.d.ts.map +1 -1
  97. package/dist/core/project/state.js +86 -0
  98. package/dist/core/project/state.js.map +1 -1
  99. package/dist/core/project/validation.d.ts.map +1 -1
  100. package/dist/core/project/validation.js +32 -10
  101. package/dist/core/project/validation.js.map +1 -1
  102. package/dist/core/project/workflows.d.ts.map +1 -1
  103. package/dist/core/project/workflows.js +53 -7
  104. package/dist/core/project/workflows.js.map +1 -1
  105. package/dist/core/scaffold/auth-generator.d.ts.map +1 -1
  106. package/dist/core/scaffold/auth-generator.js +26 -27
  107. package/dist/core/scaffold/auth-generator.js.map +1 -1
  108. package/dist/core/scaffold/model-parser.d.ts +23 -0
  109. package/dist/core/scaffold/model-parser.d.ts.map +1 -1
  110. package/dist/core/scaffold/model-parser.js +407 -137
  111. package/dist/core/scaffold/model-parser.js.map +1 -1
  112. package/dist/core/scaffold/native-schema-generator.d.ts +13 -0
  113. package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -1
  114. package/dist/core/scaffold/native-schema-generator.js +294 -91
  115. package/dist/core/scaffold/native-schema-generator.js.map +1 -1
  116. package/dist/core/scaffold/openapi-generator.d.ts +5 -0
  117. package/dist/core/scaffold/openapi-generator.d.ts.map +1 -1
  118. package/dist/core/scaffold/openapi-generator.js +53 -3
  119. package/dist/core/scaffold/openapi-generator.js.map +1 -1
  120. package/dist/core/scaffold/react-bff-generator.d.ts +10 -0
  121. package/dist/core/scaffold/react-bff-generator.d.ts.map +1 -0
  122. package/dist/core/scaffold/react-bff-generator.js +252 -0
  123. package/dist/core/scaffold/react-bff-generator.js.map +1 -0
  124. package/dist/core/scaffold/react-ui-generator.d.ts +6 -0
  125. package/dist/core/scaffold/react-ui-generator.d.ts.map +1 -0
  126. package/dist/core/scaffold/react-ui-generator.js +41 -0
  127. package/dist/core/scaffold/react-ui-generator.js.map +1 -0
  128. package/dist/core/scaffold/ui-generator.d.ts +0 -3
  129. package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
  130. package/dist/core/scaffold/ui-generator.js +68 -7
  131. package/dist/core/scaffold/ui-generator.js.map +1 -1
  132. package/dist/core/verify/index.d.ts +4 -3
  133. package/dist/core/verify/index.d.ts.map +1 -1
  134. package/dist/core/verify/index.js +122 -5
  135. package/dist/core/verify/index.js.map +1 -1
  136. package/dist/index.d.ts +3 -3
  137. package/dist/index.d.ts.map +1 -1
  138. package/dist/index.js +3 -2
  139. package/dist/index.js.map +1 -1
  140. package/dist/machine/contracts.d.ts +1 -0
  141. package/dist/machine/contracts.d.ts.map +1 -1
  142. package/dist/machine/errors.d.ts +3 -2
  143. package/dist/machine/errors.d.ts.map +1 -1
  144. package/dist/machine/errors.js +2 -1
  145. package/dist/machine/errors.js.map +1 -1
  146. package/dist/machine/index.d.ts.map +1 -1
  147. package/dist/machine/index.js +61 -30
  148. package/dist/machine/index.js.map +1 -1
  149. package/dist/mcp/index.d.ts +6 -4
  150. package/dist/mcp/index.d.ts.map +1 -1
  151. package/dist/mcp/index.js +210 -25
  152. package/dist/mcp/index.js.map +1 -1
  153. package/dist/mcp/version-selection.d.ts +55 -0
  154. package/dist/mcp/version-selection.d.ts.map +1 -0
  155. package/dist/mcp/version-selection.js +152 -0
  156. package/dist/mcp/version-selection.js.map +1 -0
  157. package/dist/types/index.d.ts +4 -0
  158. package/dist/types/index.d.ts.map +1 -1
  159. package/dist/utils/azure-cli.d.ts +56 -0
  160. package/dist/utils/azure-cli.d.ts.map +1 -0
  161. package/dist/utils/azure-cli.js +326 -0
  162. package/dist/utils/azure-cli.js.map +1 -0
  163. package/dist/utils/azure-diagnostics.d.ts +16 -0
  164. package/dist/utils/azure-diagnostics.d.ts.map +1 -0
  165. package/dist/utils/azure-diagnostics.js +68 -0
  166. package/dist/utils/azure-diagnostics.js.map +1 -0
  167. package/dist/utils/package-manager.d.ts +2 -2
  168. package/dist/utils/package-manager.d.ts.map +1 -1
  169. package/dist/utils/package-manager.js +6 -3
  170. package/dist/utils/package-manager.js.map +1 -1
  171. package/dist/utils/python-uv.d.ts +5 -7
  172. package/dist/utils/python-uv.d.ts.map +1 -1
  173. package/dist/utils/python-uv.js +50 -24
  174. package/dist/utils/python-uv.js.map +1 -1
  175. package/dist/utils/react-dev.d.ts +4 -0
  176. package/dist/utils/react-dev.d.ts.map +1 -0
  177. package/dist/utils/react-dev.js +35 -0
  178. package/dist/utils/react-dev.js.map +1 -0
  179. package/dist/utils/semver.d.ts +14 -0
  180. package/dist/utils/semver.d.ts.map +1 -0
  181. package/dist/utils/semver.js +61 -0
  182. package/dist/utils/semver.js.map +1 -0
  183. package/package.json +7 -4
  184. package/src/__tests__/__snapshots__/boundaries.test.ts.snap +154 -0
  185. package/src/__tests__/agent-instructions.test.ts +6 -1
  186. package/src/__tests__/agent-loop-phase34.test.ts +143 -8
  187. package/src/__tests__/auth.test.ts +9 -0
  188. package/src/__tests__/azure-cli.test.ts +202 -0
  189. package/src/__tests__/boundaries.test.ts +42 -0
  190. package/src/__tests__/csharp-schema.test.ts +261 -0
  191. package/src/__tests__/dev.test.ts +13 -6
  192. package/src/__tests__/fixtures/az wrapper.cmd +2 -0
  193. package/src/__tests__/fixtures/azure-cli.cjs +153 -0
  194. package/src/__tests__/fixtures/provision-runner.cjs +28 -0
  195. package/src/__tests__/generated-project-matrix.test.ts +789 -0
  196. package/src/__tests__/home-page.test.ts +50 -0
  197. package/src/__tests__/init.test.ts +145 -5
  198. package/src/__tests__/mcp-version.test.ts +262 -0
  199. package/src/__tests__/mcp.test.ts +120 -3
  200. package/src/__tests__/multi-auth.test.ts +115 -4
  201. package/src/__tests__/package-manager.test.ts +17 -2
  202. package/src/__tests__/plan-apply.test.ts +179 -0
  203. package/src/__tests__/provision-runtime.test.ts +722 -0
  204. package/src/__tests__/python-uv.test.ts +50 -14
  205. package/src/__tests__/python-venv.test.ts +48 -0
  206. package/src/__tests__/react-dev.test.ts +72 -0
  207. package/src/__tests__/react.test.ts +838 -0
  208. package/src/__tests__/release-metadata.test.ts +121 -0
  209. package/src/__tests__/schema-analysis.test.ts +329 -0
  210. package/src/cli/commands/add-auth.ts +103 -29
  211. package/src/cli/commands/configure-mcp.ts +54 -0
  212. package/src/cli/commands/dev.ts +145 -73
  213. package/src/cli/commands/init.ts +147 -172
  214. package/src/cli/commands/migrations.ts +22 -0
  215. package/src/cli/commands/provision.ts +294 -56
  216. package/src/cli/commands/scaffold.ts +145 -22
  217. package/src/cli/index.ts +46 -10
  218. package/src/core/config.ts +21 -1
  219. package/src/core/operations/create-model.ts +51 -13
  220. package/src/core/operations/migration-generator.ts +14 -1
  221. package/src/core/operations/model-migration.ts +52 -0
  222. package/src/core/operations/provision-deployment.ts +183 -0
  223. package/src/core/operations/provision-operations.ts +662 -154
  224. package/src/core/operations/scaffold-apply.ts +2 -0
  225. package/src/core/operations/scaffold-machine.ts +1 -0
  226. package/src/core/operations/scaffold-plan.ts +5 -1
  227. package/src/core/project/agent-instructions.ts +17 -1
  228. package/src/core/project/agent-templates/AGENTS.md +113 -15
  229. package/src/core/project/agent-templates/CLAUDE.md +14 -1
  230. package/src/core/project/agent-templates/bff-routes.instructions.md +5 -3
  231. package/src/core/project/agent-templates/copilot-instructions.md +15 -1
  232. package/src/core/project/agent-templates/shared-models.instructions.md +11 -3
  233. package/src/core/project/boundaries.ts +62 -20
  234. package/src/core/project/capabilities.ts +63 -6
  235. package/src/core/project/home-page.ts +114 -0
  236. package/src/core/project/manifest.ts +52 -27
  237. package/src/core/project/mcp-config.ts +84 -0
  238. package/src/core/project/react-project.ts +529 -0
  239. package/src/core/project/state.ts +70 -0
  240. package/src/core/project/validation.ts +32 -10
  241. package/src/core/project/workflows.ts +53 -7
  242. package/src/core/scaffold/auth-generator.ts +26 -27
  243. package/src/core/scaffold/model-parser.ts +413 -129
  244. package/src/core/scaffold/native-schema-generator.ts +319 -103
  245. package/src/core/scaffold/openapi-generator.ts +63 -5
  246. package/src/core/scaffold/react-bff-generator.ts +292 -0
  247. package/src/core/scaffold/react-ui-generator.ts +51 -0
  248. package/src/core/scaffold/ui-generator.ts +70 -8
  249. package/src/core/verify/index.ts +118 -6
  250. package/src/index.ts +3 -1
  251. package/src/machine/contracts.ts +1 -0
  252. package/src/machine/errors.ts +4 -2
  253. package/src/machine/index.ts +67 -36
  254. package/src/mcp/index.ts +221 -31
  255. package/src/mcp/version-selection.ts +163 -0
  256. package/src/types/index.ts +4 -0
  257. package/src/utils/azure-cli.ts +315 -0
  258. package/src/utils/azure-diagnostics.ts +75 -0
  259. package/src/utils/package-manager.ts +7 -4
  260. package/src/utils/python-uv.ts +53 -33
  261. package/src/utils/react-dev.ts +31 -0
  262. package/src/utils/semver.ts +59 -0
@@ -1,20 +1,22 @@
1
1
  import * as path from "path";
2
+ import * as fs from "fs";
3
+ import * as os from "os";
2
4
  import {
3
- buildProjectLocalUvEnv,
5
+ buildProjectLocalUvInstallerEnv,
4
6
  buildUvPipInstallArgs,
5
7
  buildUvVenvArgs,
6
8
  getProjectLocalUvPaths,
7
9
  getPythonProjectRoot,
10
+ ensureUvVirtualEnvironment,
11
+ buildPythonCodegenInstallEnv,
8
12
  } from "../utils/python-uv";
9
13
 
10
14
  describe("python uv helpers", () => {
11
- it("keeps uv-managed state inside the project", () => {
15
+ it("keeps only the fallback uv binary inside the project", () => {
12
16
  const projectRoot = "C:\\repo";
13
17
  const uvPaths = getProjectLocalUvPaths(projectRoot);
14
18
 
15
19
  expect(uvPaths.stateDir).toBe(path.join(projectRoot, ".uv"));
16
- expect(uvPaths.cacheDir).toBe(path.join(projectRoot, ".uv", "cache"));
17
- expect(uvPaths.pythonInstallDir).toBe(path.join(projectRoot, ".uv", "python"));
18
20
  expect(uvPaths.localUvExecutable.endsWith(process.platform === "win32" ? path.join(".uv", "bin", "uv.exe") : path.join(".uv", "bin", "uv"))).toBe(true);
19
21
  });
20
22
 
@@ -22,20 +24,47 @@ describe("python uv helpers", () => {
22
24
  expect(getPythonProjectRoot(path.join("C:\\repo", "functions"))).toBe("C:\\repo");
23
25
  });
24
26
 
25
- it("builds project-local uv environment variables", () => {
26
- const env = buildProjectLocalUvEnv({ PATH: "C:\\Windows\\System32" }, "C:\\repo");
27
+ it("leaves uv interpreter discovery, cache paths and user settings unchanged during bootstrap", () => {
28
+ const configured = {
29
+ PATH: "system-path",
30
+ UV_PYTHON: "3.15",
31
+ UV_CACHE_DIR: "user-cache",
32
+ UV_PYTHON_INSTALL_DIR: "user-python",
33
+ UV_PYTHON_PREFERENCE: "system",
34
+ UV_PYTHON_DOWNLOADS: "never",
35
+ UV_PYTHON_NO_REGISTRY: "false",
36
+ };
37
+ expect(buildProjectLocalUvInstallerEnv(configured, "C:\\repo")).toMatchObject(configured);
38
+ expect(buildProjectLocalUvInstallerEnv({}, "C:\\repo").UV_PYTHON_PREFERENCE).toBeUndefined();
39
+ expect(buildProjectLocalUvInstallerEnv({}, "C:\\repo").UV_PYTHON_NO_REGISTRY).toBeUndefined();
40
+ });
41
+
42
+ it("limits bootstrap changes to a project-local uv binary and its child environment", () => {
43
+ const base = { PSModulePath: "powershell-7-modules", UV_PYTHON: "3.15" };
44
+ const env = buildProjectLocalUvInstallerEnv(base, "C:\\repo");
45
+ expect(env.UV_UNMANAGED_INSTALL).toBe(path.join("C:\\repo", ".uv", "bin"));
46
+ expect(env.UV_NO_MODIFY_PATH).toBe("1");
47
+ expect(env.UV_PYTHON).toBe("3.15");
48
+ expect(env.PSModulePath).toBe(process.platform === "win32" ? undefined : base.PSModulePath);
49
+ expect(base.PSModulePath).toBe("powershell-7-modules");
50
+ });
27
51
 
28
- expect(env.UV_CACHE_DIR).toBe(path.join("C:\\repo", ".uv", "cache"));
29
- expect(env.UV_PYTHON_INSTALL_DIR).toBe(path.join("C:\\repo", ".uv", "python"));
30
- expect(env.UV_TOOL_DIR).toBe(path.join("C:\\repo", ".uv", "tools"));
31
- expect(env.UV_TOOL_BIN_DIR).toBe(path.join("C:\\repo", ".uv", "tools", "bin"));
32
- expect(env.UV_MANAGED_PYTHON).toBeUndefined();
33
- expect(env.UV_PYTHON_PREFERENCE).toBe("only-managed");
34
- expect(env.UV_PYTHON_NO_REGISTRY).toBe(process.platform === "win32" ? "true" : undefined);
52
+ it("preserves an existing unusable virtual environment instead of clearing it", async () => {
53
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "swallowkit-uv-test-"));
54
+ const marker = path.join(root, "keep.txt");
55
+ fs.writeFileSync(marker, "user data");
56
+ try {
57
+ await expect(
58
+ ensureUvVirtualEnvironment("uv-must-not-be-called", root, root, process.env)
59
+ ).rejects.toThrow("The virtual environment was preserved");
60
+ expect(fs.readFileSync(marker, "utf8")).toBe("user data");
61
+ } finally {
62
+ fs.rmSync(root, { recursive: true, force: true });
63
+ }
35
64
  });
36
65
 
37
66
  it("builds uv commands for venv creation and requirements installation", () => {
38
- expect(buildUvVenvArgs(".venv")).toEqual(["venv", ".venv", "--python", "3.11"]);
67
+ expect(buildUvVenvArgs(".venv")).toEqual(["venv", ".venv"]);
39
68
  expect(buildUvPipInstallArgs(path.join("C:\\repo", "functions", ".venv", "Scripts", "python.exe"), "requirements.txt")).toEqual([
40
69
  "pip",
41
70
  "install",
@@ -45,4 +74,11 @@ describe("python uv helpers", () => {
45
74
  "requirements.txt",
46
75
  ]);
47
76
  });
77
+
78
+ it("does not require a C compiler for PyYAML's optional codegen accelerator", () => {
79
+ expect(buildPythonCodegenInstallEnv({ UV_PYTHON: "3.15" })).toEqual({
80
+ UV_PYTHON: "3.15", PYYAML_FORCE_LIBYAML: "0",
81
+ });
82
+ expect(buildPythonCodegenInstallEnv({ PYYAML_FORCE_LIBYAML: "1" }).PYYAML_FORCE_LIBYAML).toBe("1");
83
+ });
48
84
  });
@@ -0,0 +1,48 @@
1
+ import { execFile } from "child_process";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import { ensureUvVirtualEnvironment, getVirtualEnvPythonPath } from "../utils/python-uv";
5
+
6
+ let mockInterpreterError: Error | null = null;
7
+ jest.mock("child_process", () => ({
8
+ execFile: jest.fn((
9
+ _command: string,
10
+ args: string[],
11
+ _options: object,
12
+ callback: (error: Error | null, stdout: string, stderr: string) => void
13
+ ) => callback(args[0] === "--version" ? mockInterpreterError : null, "Python 3.15.1\n", "")),
14
+ }));
15
+ jest.mock("fs", () => ({ existsSync: jest.fn() }));
16
+
17
+ describe("uv virtual environment lifecycle", () => {
18
+ const cwd = path.resolve("python-fixture", "functions");
19
+ const venv = path.join(cwd, ".venv");
20
+ const env = { UV_PYTHON: "3.15", UV_PYTHON_DOWNLOADS: "never" };
21
+
22
+ beforeEach(() => {
23
+ jest.clearAllMocks();
24
+ mockInterpreterError = null;
25
+ });
26
+
27
+ it("creates with normal uv selection, then checks that Python can actually execute", async () => {
28
+ jest.mocked(fs.existsSync).mockReturnValue(false);
29
+ await expect(ensureUvVirtualEnvironment("uv", venv, cwd, env)).resolves.toBe(getVirtualEnvPythonPath(venv));
30
+ expect(execFile).toHaveBeenNthCalledWith(1, "uv", ["venv", venv], { cwd, env }, expect.any(Function));
31
+ expect(execFile).toHaveBeenNthCalledWith(2, getVirtualEnvPythonPath(venv), ["--version"], { cwd, env }, expect.any(Function));
32
+ });
33
+
34
+ it("reuses an existing executable environment without changing its interpreter", async () => {
35
+ jest.mocked(fs.existsSync).mockReturnValue(true);
36
+ await ensureUvVirtualEnvironment("uv", venv, cwd, env);
37
+ expect(execFile).toHaveBeenCalledTimes(1);
38
+ expect(execFile).toHaveBeenCalledWith(getVirtualEnvPythonPath(venv), ["--version"], { cwd, env }, expect.any(Function));
39
+ });
40
+
41
+ it.each([true, false])("reports blocked Python after checking the environment (existing=%s)", async exists => {
42
+ jest.mocked(fs.existsSync).mockReturnValue(exists);
43
+ mockInterpreterError = new Error("Application control blocked this file (os error 4551)");
44
+ await expect(ensureUvVirtualEnvironment("uv", venv, cwd, env)).rejects.toThrow("os error 4551");
45
+ expect(jest.mocked(execFile).mock.calls.some(call => call[1]?.includes("--clear"))).toBe(false);
46
+ expect(execFile).toHaveBeenCalledTimes(exists ? 1 : 2);
47
+ });
48
+ });
@@ -0,0 +1,72 @@
1
+ import ts from "typescript";
2
+ import {
3
+ buildReactDevConfigSource,
4
+ buildReactDevWatchIgnoredSource,
5
+ } from "../utils/react-dev";
6
+ import { buildReactDevArgs } from "../cli/commands/dev";
7
+
8
+ describe("React dev watch boundaries", () => {
9
+ it.each(["C:\\repo\\", "C:\\repo", "/repo/"])(
10
+ "ignores backend artifacts without excluding frontend or shared sources under %s",
11
+ root => {
12
+ const ignored: string[] = new Function("root", `return ${buildReactDevWatchIgnoredSource("root")}`)(root);
13
+ const normalized = root.replace(/\\/g, "/").replace(/\/$/, "");
14
+ expect(ignored).toEqual([
15
+ `${normalized}/functions/**`,
16
+ `${normalized}/bff/**`,
17
+ `${normalized}/.uv/**`,
18
+ `${normalized}/.swallowkit/**`,
19
+ `${normalized}/shared/dist/**`,
20
+ ]);
21
+ expect(ignored.some(value => value.includes("/src/") || value.includes("/shared/models/"))).toBe(false);
22
+ }
23
+ );
24
+
25
+ function loadWrapper(config: object) {
26
+ const loadConfigFromFile = jest.fn(async () => ({
27
+ config,
28
+ dependencies: ["/repo/vite.config.mts", "/repo/vite-settings.ts"],
29
+ }));
30
+ const mergeConfig = jest.fn((base: object, overrides: object) => ({ base, overrides }));
31
+ const result = { exports: {} as { default: (env: object) => Promise<unknown> } };
32
+ const source = ts.transpileModule(buildReactDevConfigSource(), {
33
+ compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 },
34
+ }).outputText;
35
+ new Function("require", "exports", source)(
36
+ () => ({ defineConfig: (value: unknown) => value, loadConfigFromFile, mergeConfig }),
37
+ result.exports
38
+ );
39
+ return { load: result.exports.default, loadConfigFromFile, mergeConfig };
40
+ }
41
+
42
+ it("loads the user's Vite config and merges rather than replacing their watch options", async () => {
43
+ const config = { plugins: ["user-plugin"], server: { watch: { ignored: ["**/custom/**"], usePolling: true } } };
44
+ const wrapper = loadWrapper(config);
45
+ const env = { command: "serve", mode: "development" };
46
+ await wrapper.load(env);
47
+ expect(wrapper.loadConfigFromFile).toHaveBeenCalledWith(env, undefined, process.cwd());
48
+ expect(wrapper.mergeConfig).toHaveBeenCalledWith(config, {
49
+ plugins: [expect.objectContaining({ name: "swallowkit-dev-watch-boundaries" })],
50
+ server: { watch: { ignored: expect.arrayContaining([expect.stringContaining("/functions/**")]) } },
51
+ });
52
+ const plugin = wrapper.mergeConfig.mock.calls[0][1] as {
53
+ plugins: Array<{ configResolved: (config: { configFileDependencies: string[] }) => void }>;
54
+ };
55
+ const resolved = { configFileDependencies: ["/repo/wrapper.mjs"] };
56
+ plugin.plugins[0].configResolved(resolved);
57
+ expect(resolved.configFileDependencies).toContain("/repo/vite.config.mts");
58
+ });
59
+
60
+ it("respects an explicitly disabled watcher", async () => {
61
+ const config = { server: { watch: null } };
62
+ const wrapper = loadWrapper(config);
63
+ await expect(wrapper.load({})).resolves.toBe(config);
64
+ expect(wrapper.mergeConfig).not.toHaveBeenCalled();
65
+ });
66
+
67
+ it("passes the override config to existing projects without editing their Vite config", () => {
68
+ expect(buildReactDevArgs("pnpm", "3100", "127.0.0.1", ".swallowkit/config.mjs")).toEqual([
69
+ "exec", "vite", "--port", "3100", "--host", "127.0.0.1", "--strictPort", "--config", ".swallowkit/config.mjs",
70
+ ]);
71
+ });
72
+ });