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.
Files changed (272) hide show
  1. package/README.ja.md +87 -181
  2. package/README.md +87 -238
  3. package/dist/__tests__/fixtures.d.ts +12 -0
  4. package/dist/__tests__/fixtures.d.ts.map +1 -1
  5. package/dist/__tests__/fixtures.js +101 -0
  6. package/dist/__tests__/fixtures.js.map +1 -1
  7. package/dist/cli/commands/add-auth.d.ts +15 -0
  8. package/dist/cli/commands/add-auth.d.ts.map +1 -0
  9. package/dist/cli/commands/add-auth.js +790 -0
  10. package/dist/cli/commands/add-auth.js.map +1 -0
  11. package/dist/cli/commands/add-connector.d.ts +20 -0
  12. package/dist/cli/commands/add-connector.d.ts.map +1 -0
  13. package/dist/cli/commands/add-connector.js +164 -0
  14. package/dist/cli/commands/add-connector.js.map +1 -0
  15. package/dist/cli/commands/cosmos-worktree-isolation.d.ts +25 -0
  16. package/dist/cli/commands/cosmos-worktree-isolation.d.ts.map +1 -0
  17. package/dist/cli/commands/cosmos-worktree-isolation.js +124 -0
  18. package/dist/cli/commands/cosmos-worktree-isolation.js.map +1 -0
  19. package/dist/cli/commands/create-model.d.ts +1 -4
  20. package/dist/cli/commands/create-model.d.ts.map +1 -1
  21. package/dist/cli/commands/create-model.js +20 -82
  22. package/dist/cli/commands/create-model.js.map +1 -1
  23. package/dist/cli/commands/dev-seeds.d.ts +27 -0
  24. package/dist/cli/commands/dev-seeds.d.ts.map +1 -1
  25. package/dist/cli/commands/dev-seeds.js +188 -6
  26. package/dist/cli/commands/dev-seeds.js.map +1 -1
  27. package/dist/cli/commands/dev.d.ts +30 -0
  28. package/dist/cli/commands/dev.d.ts.map +1 -1
  29. package/dist/cli/commands/dev.js +495 -129
  30. package/dist/cli/commands/dev.js.map +1 -1
  31. package/dist/cli/commands/init.d.ts +69 -1
  32. package/dist/cli/commands/init.d.ts.map +1 -1
  33. package/dist/cli/commands/init.js +788 -766
  34. package/dist/cli/commands/init.js.map +1 -1
  35. package/dist/cli/commands/scaffold.d.ts +24 -3
  36. package/dist/cli/commands/scaffold.d.ts.map +1 -1
  37. package/dist/cli/commands/scaffold.js +408 -189
  38. package/dist/cli/commands/scaffold.js.map +1 -1
  39. package/dist/cli/index.d.ts +5 -1
  40. package/dist/cli/index.d.ts.map +1 -1
  41. package/dist/cli/index.js +335 -58
  42. package/dist/cli/index.js.map +1 -1
  43. package/dist/core/config.d.ts +13 -3
  44. package/dist/core/config.d.ts.map +1 -1
  45. package/dist/core/config.js +151 -8
  46. package/dist/core/config.js.map +1 -1
  47. package/dist/core/mock/connector-mock-server.d.ts +101 -0
  48. package/dist/core/mock/connector-mock-server.d.ts.map +1 -0
  49. package/dist/core/mock/connector-mock-server.js +480 -0
  50. package/dist/core/mock/connector-mock-server.js.map +1 -0
  51. package/dist/core/mock/zod-mock-generator.d.ts +14 -0
  52. package/dist/core/mock/zod-mock-generator.d.ts.map +1 -0
  53. package/dist/core/mock/zod-mock-generator.js +163 -0
  54. package/dist/core/mock/zod-mock-generator.js.map +1 -0
  55. package/dist/core/operations/auth-operations.d.ts +61 -0
  56. package/dist/core/operations/auth-operations.d.ts.map +1 -0
  57. package/dist/core/operations/auth-operations.js +272 -0
  58. package/dist/core/operations/auth-operations.js.map +1 -0
  59. package/dist/core/operations/create-model.d.ts +15 -0
  60. package/dist/core/operations/create-model.d.ts.map +1 -0
  61. package/dist/core/operations/create-model.js +185 -0
  62. package/dist/core/operations/create-model.js.map +1 -0
  63. package/dist/core/operations/file-session.d.ts +56 -0
  64. package/dist/core/operations/file-session.d.ts.map +1 -0
  65. package/dist/core/operations/file-session.js +203 -0
  66. package/dist/core/operations/file-session.js.map +1 -0
  67. package/dist/core/operations/provision-operations.d.ts +52 -0
  68. package/dist/core/operations/provision-operations.d.ts.map +1 -0
  69. package/dist/core/operations/provision-operations.js +211 -0
  70. package/dist/core/operations/provision-operations.js.map +1 -0
  71. package/dist/core/operations/runtime.d.ts +37 -0
  72. package/dist/core/operations/runtime.d.ts.map +1 -0
  73. package/dist/core/operations/runtime.js +248 -0
  74. package/dist/core/operations/runtime.js.map +1 -0
  75. package/dist/core/operations/scaffold-apply.d.ts +21 -0
  76. package/dist/core/operations/scaffold-apply.d.ts.map +1 -0
  77. package/dist/core/operations/scaffold-apply.js +57 -0
  78. package/dist/core/operations/scaffold-apply.js.map +1 -0
  79. package/dist/core/operations/scaffold-machine.d.ts +16 -0
  80. package/dist/core/operations/scaffold-machine.d.ts.map +1 -0
  81. package/dist/core/operations/scaffold-machine.js +51 -0
  82. package/dist/core/operations/scaffold-machine.js.map +1 -0
  83. package/dist/core/operations/scaffold-plan.d.ts +43 -0
  84. package/dist/core/operations/scaffold-plan.d.ts.map +1 -0
  85. package/dist/core/operations/scaffold-plan.js +187 -0
  86. package/dist/core/operations/scaffold-plan.js.map +1 -0
  87. package/dist/core/project/agent-instructions.d.ts +29 -0
  88. package/dist/core/project/agent-instructions.d.ts.map +1 -0
  89. package/dist/core/project/agent-instructions.js +160 -0
  90. package/dist/core/project/agent-instructions.js.map +1 -0
  91. package/dist/core/project/artifacts.d.ts +58 -0
  92. package/dist/core/project/artifacts.d.ts.map +1 -0
  93. package/dist/core/project/artifacts.js +179 -0
  94. package/dist/core/project/artifacts.js.map +1 -0
  95. package/dist/core/project/boundaries.d.ts +50 -0
  96. package/dist/core/project/boundaries.d.ts.map +1 -0
  97. package/dist/core/project/boundaries.js +117 -0
  98. package/dist/core/project/boundaries.js.map +1 -0
  99. package/dist/core/project/capabilities.d.ts +58 -0
  100. package/dist/core/project/capabilities.d.ts.map +1 -0
  101. package/dist/core/project/capabilities.js +135 -0
  102. package/dist/core/project/capabilities.js.map +1 -0
  103. package/dist/core/project/drift.d.ts +29 -0
  104. package/dist/core/project/drift.d.ts.map +1 -0
  105. package/dist/core/project/drift.js +190 -0
  106. package/dist/core/project/drift.js.map +1 -0
  107. package/dist/core/project/infra.d.ts +49 -0
  108. package/dist/core/project/infra.d.ts.map +1 -0
  109. package/dist/core/project/infra.js +167 -0
  110. package/dist/core/project/infra.js.map +1 -0
  111. package/dist/core/project/manifest.d.ts +92 -0
  112. package/dist/core/project/manifest.d.ts.map +1 -0
  113. package/dist/core/project/manifest.js +321 -0
  114. package/dist/core/project/manifest.js.map +1 -0
  115. package/dist/core/project/state.d.ts +15 -0
  116. package/dist/core/project/state.d.ts.map +1 -0
  117. package/dist/core/project/state.js +106 -0
  118. package/dist/core/project/state.js.map +1 -0
  119. package/dist/core/project/validation.d.ts +20 -0
  120. package/dist/core/project/validation.d.ts.map +1 -0
  121. package/dist/core/project/validation.js +274 -0
  122. package/dist/core/project/validation.js.map +1 -0
  123. package/dist/core/project/workflows.d.ts +43 -0
  124. package/dist/core/project/workflows.d.ts.map +1 -0
  125. package/dist/core/project/workflows.js +299 -0
  126. package/dist/core/project/workflows.js.map +1 -0
  127. package/dist/core/scaffold/auth-generator.d.ts +63 -0
  128. package/dist/core/scaffold/auth-generator.d.ts.map +1 -0
  129. package/dist/core/scaffold/auth-generator.js +1774 -0
  130. package/dist/core/scaffold/auth-generator.js.map +1 -0
  131. package/dist/core/scaffold/connector-functions-generator.d.ts +41 -0
  132. package/dist/core/scaffold/connector-functions-generator.d.ts.map +1 -0
  133. package/dist/core/scaffold/connector-functions-generator.js +1029 -0
  134. package/dist/core/scaffold/connector-functions-generator.js.map +1 -0
  135. package/dist/core/scaffold/functions-generator.d.ts +4 -3
  136. package/dist/core/scaffold/functions-generator.d.ts.map +1 -1
  137. package/dist/core/scaffold/functions-generator.js +405 -134
  138. package/dist/core/scaffold/functions-generator.js.map +1 -1
  139. package/dist/core/scaffold/model-parser.d.ts +20 -0
  140. package/dist/core/scaffold/model-parser.d.ts.map +1 -1
  141. package/dist/core/scaffold/model-parser.js +229 -78
  142. package/dist/core/scaffold/model-parser.js.map +1 -1
  143. package/dist/core/scaffold/native-schema-generator.d.ts +19 -0
  144. package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -0
  145. package/dist/core/scaffold/native-schema-generator.js +753 -0
  146. package/dist/core/scaffold/native-schema-generator.js.map +1 -0
  147. package/dist/core/scaffold/nextjs-generator.d.ts +8 -0
  148. package/dist/core/scaffold/nextjs-generator.d.ts.map +1 -1
  149. package/dist/core/scaffold/nextjs-generator.js +152 -8
  150. package/dist/core/scaffold/nextjs-generator.js.map +1 -1
  151. package/dist/core/scaffold/ui-generator.d.ts +17 -4
  152. package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
  153. package/dist/core/scaffold/ui-generator.js +174 -34
  154. package/dist/core/scaffold/ui-generator.js.map +1 -1
  155. package/dist/core/verify/index.d.ts +69 -0
  156. package/dist/core/verify/index.d.ts.map +1 -0
  157. package/dist/core/verify/index.js +412 -0
  158. package/dist/core/verify/index.js.map +1 -0
  159. package/dist/machine/contracts.d.ts +24 -0
  160. package/dist/machine/contracts.d.ts.map +1 -0
  161. package/dist/machine/contracts.js +3 -0
  162. package/dist/machine/contracts.js.map +1 -0
  163. package/dist/machine/errors.d.ts +14 -0
  164. package/dist/machine/errors.d.ts.map +1 -0
  165. package/dist/machine/errors.js +42 -0
  166. package/dist/machine/errors.js.map +1 -0
  167. package/dist/machine/index.d.ts +3 -0
  168. package/dist/machine/index.d.ts.map +1 -0
  169. package/dist/machine/index.js +566 -0
  170. package/dist/machine/index.js.map +1 -0
  171. package/dist/mcp/index.d.ts +25 -0
  172. package/dist/mcp/index.d.ts.map +1 -0
  173. package/dist/mcp/index.js +425 -0
  174. package/dist/mcp/index.js.map +1 -0
  175. package/dist/types/index.d.ts +105 -0
  176. package/dist/types/index.d.ts.map +1 -1
  177. package/dist/utils/package-manager.d.ts +7 -11
  178. package/dist/utils/package-manager.d.ts.map +1 -1
  179. package/dist/utils/package-manager.js +42 -17
  180. package/dist/utils/package-manager.js.map +1 -1
  181. package/dist/utils/python-uv.d.ts +21 -0
  182. package/dist/utils/python-uv.d.ts.map +1 -0
  183. package/dist/utils/python-uv.js +111 -0
  184. package/dist/utils/python-uv.js.map +1 -0
  185. package/dist/version.d.ts +2 -0
  186. package/dist/version.d.ts.map +1 -0
  187. package/dist/version.js +49 -0
  188. package/dist/version.js.map +1 -0
  189. package/package.json +15 -17
  190. package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +716 -22
  191. package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +19 -7
  192. package/src/__tests__/__snapshots__/ui-generator.test.ts.snap +20 -12
  193. package/src/__tests__/agent-instructions.test.ts +237 -0
  194. package/src/__tests__/agent-loop-phase34.test.ts +619 -0
  195. package/src/__tests__/auth.test.ts +770 -0
  196. package/src/__tests__/bff-swa-auth.test.ts +275 -0
  197. package/src/__tests__/config.test.ts +176 -3
  198. package/src/__tests__/connector-functions-generator.test.ts +288 -0
  199. package/src/__tests__/connector-mock-server.test.ts +459 -0
  200. package/src/__tests__/connector-model-bff.test.ts +162 -0
  201. package/src/__tests__/cosmos-worktree-isolation.test.ts +149 -0
  202. package/src/__tests__/dev-seeds.test.ts +124 -0
  203. package/src/__tests__/dev.test.ts +254 -0
  204. package/src/__tests__/file-session.test.ts +98 -0
  205. package/src/__tests__/fixtures.ts +101 -0
  206. package/src/__tests__/functions-generator.test.ts +137 -1
  207. package/src/__tests__/init.test.ts +366 -2
  208. package/src/__tests__/jest-globals.d.ts +8 -0
  209. package/src/__tests__/machine.test.ts +371 -0
  210. package/src/__tests__/mcp.test.ts +253 -0
  211. package/src/__tests__/model-parser.test.ts +142 -0
  212. package/src/__tests__/multi-auth.test.ts +160 -0
  213. package/src/__tests__/nextjs-generator.test.ts +8 -0
  214. package/src/__tests__/package-manager.test.ts +59 -4
  215. package/src/__tests__/plan-apply.test.ts +499 -0
  216. package/src/__tests__/python-uv.test.ts +48 -0
  217. package/src/__tests__/scaffold.test.ts +287 -24
  218. package/src/__tests__/tsconfig.json +15 -0
  219. package/src/__tests__/ui-generator.test.ts +104 -0
  220. package/src/__tests__/zod-mock-generator.test.ts +132 -0
  221. package/src/cli/commands/add-auth.ts +853 -0
  222. package/src/cli/commands/add-connector.ts +159 -0
  223. package/src/cli/commands/cosmos-worktree-isolation.ts +130 -0
  224. package/src/cli/commands/create-model.ts +21 -100
  225. package/src/cli/commands/dev-seeds.ts +278 -7
  226. package/src/cli/commands/dev.ts +607 -160
  227. package/src/cli/commands/init.ts +851 -786
  228. package/src/cli/commands/scaffold.ts +531 -236
  229. package/src/cli/index.ts +338 -60
  230. package/src/core/config.ts +145 -9
  231. package/src/core/mock/connector-mock-server.ts +555 -0
  232. package/src/core/mock/zod-mock-generator.ts +205 -0
  233. package/src/core/operations/auth-operations.ts +360 -0
  234. package/src/core/operations/create-model.ts +191 -0
  235. package/src/core/operations/file-session.ts +222 -0
  236. package/src/core/operations/provision-operations.ts +297 -0
  237. package/src/core/operations/runtime.ts +258 -0
  238. package/src/core/operations/scaffold-apply.ts +106 -0
  239. package/src/core/operations/scaffold-machine.ts +77 -0
  240. package/src/core/operations/scaffold-plan.ts +215 -0
  241. package/src/core/project/agent-instructions.ts +167 -0
  242. package/src/core/project/agent-templates/AGENTS.md +504 -0
  243. package/src/core/project/agent-templates/CLAUDE.md +31 -0
  244. package/src/core/project/agent-templates/azure-functions.instructions.md +22 -0
  245. package/src/core/project/agent-templates/bff-routes.instructions.md +18 -0
  246. package/src/core/project/agent-templates/copilot-instructions.md +27 -0
  247. package/src/core/project/agent-templates/shared-models.instructions.md +72 -0
  248. package/src/core/project/artifacts.ts +205 -0
  249. package/src/core/project/boundaries.ts +170 -0
  250. package/src/core/project/capabilities.ts +193 -0
  251. package/src/core/project/drift.ts +203 -0
  252. package/src/core/project/infra.ts +196 -0
  253. package/src/core/project/manifest.ts +402 -0
  254. package/src/core/project/state.ts +77 -0
  255. package/src/core/project/validation.ts +295 -0
  256. package/src/core/project/workflows.ts +295 -0
  257. package/src/core/scaffold/auth-generator.ts +1824 -0
  258. package/src/core/scaffold/connector-functions-generator.ts +1133 -0
  259. package/src/core/scaffold/functions-generator.ts +432 -134
  260. package/src/core/scaffold/model-parser.ts +244 -90
  261. package/src/core/scaffold/native-schema-generator.ts +904 -0
  262. package/src/core/scaffold/nextjs-generator.ts +174 -9
  263. package/src/core/scaffold/ui-generator.ts +191 -36
  264. package/src/core/verify/index.ts +468 -0
  265. package/src/machine/contracts.ts +33 -0
  266. package/src/machine/errors.ts +45 -0
  267. package/src/machine/index.ts +690 -0
  268. package/src/mcp/index.ts +423 -0
  269. package/src/types/index.ts +137 -0
  270. package/src/utils/package-manager.ts +41 -19
  271. package/src/utils/python-uv.ts +96 -0
  272. package/src/version.ts +13 -0
@@ -0,0 +1,402 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import { getBackendLanguage, getFullConfig, normalizeAuthConfig, validateConfig } from "../config";
4
+ import { getAllModels, toCamelCase, toKebabCase } from "../scaffold/model-parser";
5
+ import { AuthConfig, BackendLanguage, ConnectorDefinition, ModelAuthPolicy, ModelConnectorConfig } from "../../types";
6
+ import { captureConsoleMessages, withWorkingDirectory } from "../operations/runtime";
7
+
8
+ const CONFIG_CANDIDATES = [
9
+ "swallowkit.config.js",
10
+ "swallowkit.config.json",
11
+ ".swallowkitrc.json",
12
+ ] as const;
13
+
14
+ export const SWALLOWKIT_MANIFEST_PATH = path.join(".swallowkit", "project.json");
15
+ export const SWALLOWKIT_MANIFEST_VERSION = 1;
16
+
17
+ export interface ProjectManifestField {
18
+ name: string;
19
+ type: string;
20
+ isOptional: boolean;
21
+ isArray: boolean;
22
+ }
23
+
24
+ export interface ProjectManifestEntity {
25
+ name: string;
26
+ displayName: string;
27
+ schemaName: string;
28
+ filePath: string;
29
+ partitionKey: string;
30
+ hasId: boolean;
31
+ hasCreatedAt: boolean;
32
+ hasUpdatedAt: boolean;
33
+ connectorConfig?: ModelConnectorConfig;
34
+ authPolicy?: ModelAuthPolicy;
35
+ nestedModels: string[];
36
+ fields: ProjectManifestField[];
37
+ }
38
+
39
+ export interface ProjectManifestRoute {
40
+ name: string;
41
+ kind: "entity" | "system";
42
+ surface: "bff" | "functions";
43
+ entityName?: string;
44
+ methods: string[];
45
+ publicPath: string;
46
+ filePath: string;
47
+ exists: boolean;
48
+ }
49
+
50
+ export interface ProjectManifestModule {
51
+ name: string;
52
+ kind: "shared-models" | "bff" | "functions" | "ui" | "infrastructure" | "auth";
53
+ rootPath: string;
54
+ exists: boolean;
55
+ }
56
+
57
+ export interface ProjectManifestArtifacts {
58
+ callFunctionHelperPath: string;
59
+ callFunctionHelperExists: boolean;
60
+ openApiSpecs: string[];
61
+ generatedSchemaDirectories: string[];
62
+ proxyPath: string;
63
+ proxyExists: boolean;
64
+ loginPagePath: string;
65
+ loginPageExists: boolean;
66
+ authContextPath: string;
67
+ authContextExists: boolean;
68
+ }
69
+
70
+ export interface ProjectManifestArchitecture {
71
+ pattern: string;
72
+ backendLanguage: BackendLanguage;
73
+ layerBoundaries: string[];
74
+ hasSharedWorkspace: boolean;
75
+ hasConnectors: boolean;
76
+ hasAuth: boolean;
77
+ }
78
+
79
+ export interface ProjectManifest {
80
+ version: number;
81
+ generatedAt: string;
82
+ configPath: string | null;
83
+ backendLanguage: BackendLanguage;
84
+ configValidation: {
85
+ valid: boolean;
86
+ errors: string[];
87
+ };
88
+ connectors: Array<{ name: string; definition: ConnectorDefinition }>;
89
+ auth: AuthConfig | null;
90
+ entities: ProjectManifestEntity[];
91
+ routes: ProjectManifestRoute[];
92
+ modules: ProjectManifestModule[];
93
+ artifacts: ProjectManifestArtifacts;
94
+ architecture: ProjectManifestArchitecture;
95
+ }
96
+
97
+ export interface LoadedProjectManifest {
98
+ manifest: ProjectManifest;
99
+ source: "file" | "reconstructed";
100
+ diagnostics: string[];
101
+ }
102
+
103
+ function resolveConfigPath(projectRoot: string): string | null {
104
+ for (const candidate of CONFIG_CANDIDATES) {
105
+ const fullPath = path.join(projectRoot, candidate);
106
+ if (fs.existsSync(fullPath)) {
107
+ return candidate;
108
+ }
109
+ }
110
+
111
+ return null;
112
+ }
113
+
114
+ function relativeProjectPath(projectRoot: string, targetPath: string): string {
115
+ return path.relative(projectRoot, targetPath).replace(/\\/g, "/");
116
+ }
117
+
118
+ function buildModules(projectRoot: string): ProjectManifestModule[] {
119
+ const candidates: ProjectManifestModule[] = [
120
+ {
121
+ name: "shared-models",
122
+ kind: "shared-models",
123
+ rootPath: "shared/models",
124
+ exists: fs.existsSync(path.join(projectRoot, "shared", "models")),
125
+ },
126
+ {
127
+ name: "bff",
128
+ kind: "bff",
129
+ rootPath: "app/api",
130
+ exists: fs.existsSync(path.join(projectRoot, "app", "api")),
131
+ },
132
+ {
133
+ name: "functions",
134
+ kind: "functions",
135
+ rootPath: "functions",
136
+ exists: fs.existsSync(path.join(projectRoot, "functions")),
137
+ },
138
+ {
139
+ name: "ui",
140
+ kind: "ui",
141
+ rootPath: "app",
142
+ exists: fs.existsSync(path.join(projectRoot, "app")),
143
+ },
144
+ {
145
+ name: "infrastructure",
146
+ kind: "infrastructure",
147
+ rootPath: "infra",
148
+ exists: fs.existsSync(path.join(projectRoot, "infra")),
149
+ },
150
+ {
151
+ name: "auth",
152
+ kind: "auth",
153
+ rootPath: "app/api/auth",
154
+ exists: fs.existsSync(path.join(projectRoot, "app", "api", "auth")),
155
+ },
156
+ ];
157
+
158
+ return candidates;
159
+ }
160
+
161
+ function buildArtifacts(projectRoot: string): ProjectManifestArtifacts {
162
+ const openApiDir = path.join(projectRoot, "functions", "openapi");
163
+ const generatedDir = path.join(projectRoot, "functions", "generated");
164
+
165
+ return {
166
+ callFunctionHelperPath: "lib/api/call-function.ts",
167
+ callFunctionHelperExists: fs.existsSync(path.join(projectRoot, "lib", "api", "call-function.ts")),
168
+ openApiSpecs: fs.existsSync(openApiDir)
169
+ ? fs.readdirSync(openApiDir)
170
+ .filter((entry) => entry.endsWith(".json"))
171
+ .map((entry) => `functions/openapi/${entry}`)
172
+ .sort()
173
+ : [],
174
+ generatedSchemaDirectories: fs.existsSync(generatedDir)
175
+ ? fs.readdirSync(generatedDir)
176
+ .map((entry) => `functions/generated/${entry}`)
177
+ .sort()
178
+ : [],
179
+ proxyPath: "proxy.ts",
180
+ proxyExists: fs.existsSync(path.join(projectRoot, "proxy.ts")),
181
+ loginPagePath: "app/login/page.tsx",
182
+ loginPageExists: fs.existsSync(path.join(projectRoot, "app", "login", "page.tsx")),
183
+ authContextPath: "lib/auth/auth-context.tsx",
184
+ authContextExists: fs.existsSync(path.join(projectRoot, "lib", "auth", "auth-context.tsx")),
185
+ };
186
+ }
187
+
188
+ function buildEntityRoutes(
189
+ projectRoot: string,
190
+ backendLanguage: BackendLanguage,
191
+ entity: ProjectManifestEntity
192
+ ): ProjectManifestRoute[] {
193
+ const modelCamel = toCamelCase(entity.name);
194
+ const modelKebab = toKebabCase(entity.name);
195
+ const isConnectorEntity = Boolean(entity.connectorConfig);
196
+ const functionFilePath = backendLanguage === "typescript"
197
+ ? `functions/src/${modelKebab}.ts`
198
+ : backendLanguage === "csharp"
199
+ ? isConnectorEntity
200
+ ? `functions/Connectors/${entity.name}ConnectorFunctions.cs`
201
+ : `functions/Crud/${entity.name}CrudFunctions.cs`
202
+ : `functions/blueprints/${modelKebab.replace(/-/g, "_")}.py`;
203
+
204
+ const operations = entity.connectorConfig?.operations ?? ["getAll", "getById", "create", "update", "delete"];
205
+ const listMethods: string[] = [];
206
+ const detailMethods: string[] = [];
207
+
208
+ if (operations.includes("getAll")) {
209
+ listMethods.push("GET");
210
+ }
211
+ if (operations.includes("create")) {
212
+ listMethods.push("POST");
213
+ }
214
+ if (operations.includes("getById")) {
215
+ detailMethods.push("GET");
216
+ }
217
+ if (operations.includes("update")) {
218
+ detailMethods.push("PUT");
219
+ }
220
+ if (operations.includes("delete")) {
221
+ detailMethods.push("DELETE");
222
+ }
223
+
224
+ return [
225
+ {
226
+ name: `${entity.name}-bff-list`,
227
+ kind: "entity",
228
+ surface: "bff",
229
+ entityName: entity.name,
230
+ methods: listMethods,
231
+ publicPath: `/api/${modelCamel}`,
232
+ filePath: `app/api/${modelCamel}/route.ts`,
233
+ exists: fs.existsSync(path.join(projectRoot, "app", "api", modelCamel, "route.ts")),
234
+ },
235
+ {
236
+ name: `${entity.name}-bff-detail`,
237
+ kind: "entity",
238
+ surface: "bff",
239
+ entityName: entity.name,
240
+ methods: detailMethods,
241
+ publicPath: `/api/${modelCamel}/{id}`,
242
+ filePath: `app/api/${modelCamel}/[id]/route.ts`,
243
+ exists: fs.existsSync(path.join(projectRoot, "app", "api", modelCamel, "[id]", "route.ts")),
244
+ },
245
+ {
246
+ name: `${entity.name}-functions`,
247
+ kind: "entity",
248
+ surface: "functions",
249
+ entityName: entity.name,
250
+ methods: Array.from(new Set([...listMethods, ...detailMethods])),
251
+ publicPath: `/api/${modelCamel}`,
252
+ filePath: functionFilePath,
253
+ exists: fs.existsSync(path.join(projectRoot, functionFilePath)),
254
+ },
255
+ ];
256
+ }
257
+
258
+ function buildSystemRoutes(projectRoot: string): ProjectManifestRoute[] {
259
+ return [
260
+ {
261
+ name: "auth-login",
262
+ kind: "system",
263
+ surface: "bff",
264
+ methods: ["POST"],
265
+ publicPath: "/api/auth/login",
266
+ filePath: "app/api/auth/login/route.ts",
267
+ exists: fs.existsSync(path.join(projectRoot, "app", "api", "auth", "login", "route.ts")),
268
+ },
269
+ {
270
+ name: "auth-logout",
271
+ kind: "system",
272
+ surface: "bff",
273
+ methods: ["POST"],
274
+ publicPath: "/api/auth/logout",
275
+ filePath: "app/api/auth/logout/route.ts",
276
+ exists: fs.existsSync(path.join(projectRoot, "app", "api", "auth", "logout", "route.ts")),
277
+ },
278
+ {
279
+ name: "auth-me",
280
+ kind: "system",
281
+ surface: "bff",
282
+ methods: ["GET"],
283
+ publicPath: "/api/auth/me",
284
+ filePath: "app/api/auth/me/route.ts",
285
+ exists: fs.existsSync(path.join(projectRoot, "app", "api", "auth", "me", "route.ts")),
286
+ },
287
+ ];
288
+ }
289
+
290
+ export async function buildProjectManifest(projectRoot: string = process.cwd()): Promise<{
291
+ manifest: ProjectManifest;
292
+ diagnostics: string[];
293
+ }> {
294
+ const diagnostics: string[] = [];
295
+
296
+ return withWorkingDirectory(projectRoot, async () => {
297
+ const configPath = resolveConfigPath(projectRoot);
298
+ const backendLanguage = getBackendLanguage(configPath || undefined);
299
+ const config = getFullConfig(configPath || undefined);
300
+ const configValidation = validateConfig(config);
301
+ const { result: entities, messages } = await captureConsoleMessages(async () => {
302
+ const models = await getAllModels("shared/models");
303
+ return models.map((model) => ({
304
+ name: model.name,
305
+ displayName: model.displayName,
306
+ schemaName: model.schemaName,
307
+ filePath: relativeProjectPath(projectRoot, model.filePath),
308
+ partitionKey: model.partitionKey,
309
+ hasId: model.hasId,
310
+ hasCreatedAt: model.hasCreatedAt,
311
+ hasUpdatedAt: model.hasUpdatedAt,
312
+ connectorConfig: model.connectorConfig,
313
+ authPolicy: model.authPolicy,
314
+ nestedModels: model.nestedSchemaRefs.map((ref) => ref.modelName),
315
+ fields: model.fields.map((field) => ({
316
+ name: field.name,
317
+ type: field.type,
318
+ isOptional: field.isOptional,
319
+ isArray: field.isArray,
320
+ })),
321
+ }));
322
+ });
323
+
324
+ diagnostics.push(
325
+ ...messages.warnings.map((warning) => `warning:${warning}`),
326
+ ...messages.errors.map((error) => `error:${error}`)
327
+ );
328
+
329
+ const routes = [
330
+ ...entities.flatMap((entity) => buildEntityRoutes(projectRoot, backendLanguage, entity)),
331
+ ...buildSystemRoutes(projectRoot),
332
+ ].sort((left, right) => left.name.localeCompare(right.name));
333
+
334
+ const manifest: ProjectManifest = {
335
+ version: SWALLOWKIT_MANIFEST_VERSION,
336
+ generatedAt: new Date().toISOString(),
337
+ configPath,
338
+ backendLanguage,
339
+ configValidation,
340
+ connectors: Object.entries(config.connectors || {})
341
+ .map(([name, definition]) => ({ name, definition }))
342
+ .sort((left, right) => left.name.localeCompare(right.name)),
343
+ auth: normalizeAuthConfig(config.auth) || null,
344
+ entities: entities.sort((left, right) => left.name.localeCompare(right.name)),
345
+ routes,
346
+ modules: buildModules(projectRoot),
347
+ artifacts: buildArtifacts(projectRoot),
348
+ architecture: {
349
+ pattern: "Next.js BFF + Azure Functions + shared Zod models",
350
+ backendLanguage,
351
+ layerBoundaries: [
352
+ "shared/models -> schema source of truth",
353
+ "app/api -> BFF layer only",
354
+ "functions -> backend logic and data access",
355
+ "app -> UI and pages",
356
+ ],
357
+ hasSharedWorkspace: fs.existsSync(path.join(projectRoot, "shared", "package.json")),
358
+ hasConnectors: Object.keys(config.connectors || {}).length > 0,
359
+ hasAuth: Boolean(config.auth && (config.auth.provider !== "none" || Object.keys(config.auth.schemes ?? {}).length > 0)),
360
+ },
361
+ };
362
+
363
+ return { manifest, diagnostics };
364
+ });
365
+ }
366
+
367
+ export function readProjectManifest(projectRoot: string = process.cwd()): ProjectManifest | null {
368
+ const manifestPath = path.join(projectRoot, SWALLOWKIT_MANIFEST_PATH);
369
+ if (!fs.existsSync(manifestPath)) {
370
+ return null;
371
+ }
372
+
373
+ const raw = fs.readFileSync(manifestPath, "utf-8");
374
+ return JSON.parse(raw) as ProjectManifest;
375
+ }
376
+
377
+ export async function loadProjectManifest(projectRoot: string = process.cwd()): Promise<LoadedProjectManifest> {
378
+ const manifestPath = path.join(projectRoot, SWALLOWKIT_MANIFEST_PATH);
379
+ if (fs.existsSync(manifestPath)) {
380
+ return {
381
+ manifest: readProjectManifest(projectRoot)!,
382
+ source: "file",
383
+ diagnostics: [],
384
+ };
385
+ }
386
+
387
+ const { manifest, diagnostics } = await buildProjectManifest(projectRoot);
388
+ diagnostics.unshift("manifest:not-found-reconstructed");
389
+ return {
390
+ manifest,
391
+ source: "reconstructed",
392
+ diagnostics,
393
+ };
394
+ }
395
+
396
+ export async function syncProjectManifest(projectRoot: string = process.cwd()): Promise<ProjectManifest> {
397
+ const { manifest } = await buildProjectManifest(projectRoot);
398
+ const manifestPath = path.join(projectRoot, SWALLOWKIT_MANIFEST_PATH);
399
+ fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
400
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n", "utf-8");
401
+ return manifest;
402
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * ローカル状態管理 — `.swallowkit/state/`
3
+ *
4
+ * Pending plan と最新の Verify 結果を保存する。Git 管理対象外
5
+ * (ディレクトリ内に自己 ignore する .gitignore を生成する)。
6
+ */
7
+
8
+ import * as fs from "fs";
9
+ import * as path from "path";
10
+
11
+ export const SWALLOWKIT_STATE_DIR = path.join(".swallowkit", "state");
12
+
13
+ function ensureStateDir(projectRoot: string): string {
14
+ const stateDir = path.join(projectRoot, SWALLOWKIT_STATE_DIR);
15
+ fs.mkdirSync(path.join(stateDir, "plans"), { recursive: true });
16
+
17
+ const gitignorePath = path.join(stateDir, ".gitignore");
18
+ if (!fs.existsSync(gitignorePath)) {
19
+ fs.writeFileSync(gitignorePath, "*\n", "utf-8");
20
+ }
21
+
22
+ return stateDir;
23
+ }
24
+
25
+ function planPath(projectRoot: string, planId: string): string {
26
+ if (!/^[A-Za-z0-9-]+$/.test(planId)) {
27
+ throw new Error(`Invalid plan id: ${planId}`);
28
+ }
29
+ return path.join(projectRoot, SWALLOWKIT_STATE_DIR, "plans", `${planId}.json`);
30
+ }
31
+
32
+ export function savePlanState<TPlan extends { planId: string }>(
33
+ plan: TPlan,
34
+ projectRoot: string = process.cwd()
35
+ ): void {
36
+ ensureStateDir(projectRoot);
37
+ fs.writeFileSync(planPath(projectRoot, plan.planId), JSON.stringify(plan, null, 2) + "\n", "utf-8");
38
+ }
39
+
40
+ export function loadPlanState<TPlan>(planId: string, projectRoot: string = process.cwd()): TPlan | null {
41
+ const filePath = planPath(projectRoot, planId);
42
+ if (!fs.existsSync(filePath)) {
43
+ return null;
44
+ }
45
+ try {
46
+ return JSON.parse(fs.readFileSync(filePath, "utf-8")) as TPlan;
47
+ } catch {
48
+ // 破損した plan は「存在しない」扱いに退化させ、呼び出し側の plan-not-found で再計画を促す
49
+ return null;
50
+ }
51
+ }
52
+
53
+ export function deletePlanState(planId: string, projectRoot: string = process.cwd()): void {
54
+ const filePath = planPath(projectRoot, planId);
55
+ if (fs.existsSync(filePath)) {
56
+ fs.unlinkSync(filePath);
57
+ }
58
+ }
59
+
60
+ const LAST_VERIFY_FILE = "last-verify.json";
61
+
62
+ export function saveLastVerifyState(result: unknown, projectRoot: string = process.cwd()): void {
63
+ const stateDir = ensureStateDir(projectRoot);
64
+ fs.writeFileSync(path.join(stateDir, LAST_VERIFY_FILE), JSON.stringify(result, null, 2) + "\n", "utf-8");
65
+ }
66
+
67
+ export function loadLastVerifyState<TResult>(projectRoot: string = process.cwd()): TResult | null {
68
+ const filePath = path.join(projectRoot, SWALLOWKIT_STATE_DIR, LAST_VERIFY_FILE);
69
+ if (!fs.existsSync(filePath)) {
70
+ return null;
71
+ }
72
+ try {
73
+ return JSON.parse(fs.readFileSync(filePath, "utf-8")) as TResult;
74
+ } catch {
75
+ return null;
76
+ }
77
+ }