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,205 @@
1
+ /**
2
+ * Zod スキーマ(ModelInfo)からモックデータを自動生成する
3
+ * フィールド名・型のヒューリスティクスで現実的な値を生成
4
+ */
5
+
6
+ import { ModelInfo, FieldInfo, toCamelCase, toKebabCase } from "../scaffold/model-parser";
7
+
8
+ /**
9
+ * 単一のモックドキュメントを生成
10
+ */
11
+ export function generateMockDocument(
12
+ model: ModelInfo,
13
+ index: number,
14
+ allModels: ModelInfo[] = [model],
15
+ seenModels: Set<string> = new Set()
16
+ ): Record<string, unknown> {
17
+ const nextSeen = new Set(seenModels);
18
+ nextSeen.add(model.name);
19
+
20
+ const doc: Record<string, unknown> = {};
21
+ for (const field of model.fields) {
22
+ doc[field.name] = generateFieldValue(model, field, index, allModels, nextSeen);
23
+ }
24
+ return doc;
25
+ }
26
+
27
+ /**
28
+ * 複数のモックドキュメントを生成
29
+ */
30
+ export function generateMockDocuments(
31
+ model: ModelInfo,
32
+ count: number = 5,
33
+ allModels: ModelInfo[] = [model]
34
+ ): Record<string, unknown>[] {
35
+ const docs: Record<string, unknown>[] = [];
36
+ for (let i = 0; i < count; i++) {
37
+ docs.push(generateMockDocument(model, i + 1, allModels));
38
+ }
39
+ return docs;
40
+ }
41
+
42
+ // ─── Field Value Generation ─────────────────────────────────
43
+
44
+ function generateFieldValue(
45
+ model: ModelInfo,
46
+ field: FieldInfo,
47
+ index: number,
48
+ allModels: ModelInfo[],
49
+ seenModels: Set<string>
50
+ ): unknown {
51
+ // ネストされたスキーマ
52
+ if (field.isNestedSchema && field.nestedModelName) {
53
+ const nestedModel = allModels.find((m) => m.name === field.nestedModelName);
54
+ if (nestedModel && !seenModels.has(nestedModel.name)) {
55
+ const nested = generateMockDocument(nestedModel, index, allModels, seenModels);
56
+ return field.isArray ? [nested] : nested;
57
+ }
58
+ const fallback = { id: `${toKebabCase(field.nestedModelName!)}-${padIndex(index)}` };
59
+ return field.isArray ? [fallback] : fallback;
60
+ }
61
+
62
+ if (field.isArray) {
63
+ return [generateScalarValue(model, field, index)];
64
+ }
65
+
66
+ return generateScalarValue(model, field, index);
67
+ }
68
+
69
+ function generateScalarValue(
70
+ model: ModelInfo,
71
+ field: FieldInfo,
72
+ index: number
73
+ ): unknown {
74
+ const name = field.name.toLowerCase();
75
+ const modelKebab = toKebabCase(model.name);
76
+
77
+ // id フィールド
78
+ if (field.name === "id") {
79
+ return `${modelKebab}-${padIndex(index)}`;
80
+ }
81
+
82
+ // enum フィールド — ラウンドロビン
83
+ if (field.enumValues && field.enumValues.length > 0) {
84
+ return field.enumValues[(index - 1) % field.enumValues.length];
85
+ }
86
+
87
+ // boolean フィールド
88
+ if (field.type === "boolean") {
89
+ return index % 2 === 0;
90
+ }
91
+
92
+ // number フィールド
93
+ if (field.type === "number") {
94
+ return generateNumberValue(name, index);
95
+ }
96
+
97
+ // date フィールド / *At パターン
98
+ if (field.type === "date" || name.endsWith("at")) {
99
+ return generateDateValue(index);
100
+ }
101
+
102
+ // object フィールド
103
+ if (field.type === "object") {
104
+ return {};
105
+ }
106
+
107
+ // string フィールド — 名前パターンで分岐
108
+ return generateStringValue(model, field, index);
109
+ }
110
+
111
+ function generateStringValue(model: ModelInfo, field: FieldInfo, index: number): string {
112
+ const name = field.name.toLowerCase();
113
+ const modelKebab = toKebabCase(model.name);
114
+
115
+ // email
116
+ if (name.includes("email") || name.includes("mail")) {
117
+ return `${modelKebab}-${padIndex(index)}@example.com`;
118
+ }
119
+
120
+ // URL / ウェブサイト
121
+ if (name.includes("url") || name.includes("website") || name.includes("link") || name === "href") {
122
+ return `https://example.com/${modelKebab}/${padIndex(index)}`;
123
+ }
124
+
125
+ // 電話番号
126
+ if (name.includes("phone") || name.includes("tel")) {
127
+ return `090-0000-${String(index).padStart(4, "0")}`;
128
+ }
129
+
130
+ // 名前系
131
+ if (name === "name" || name === "displayname" || name === "fullname" || name === "username") {
132
+ return `${model.name} ${index}`;
133
+ }
134
+ if (name === "firstname" || name === "givenname") {
135
+ return `FirstName${index}`;
136
+ }
137
+ if (name === "lastname" || name === "familyname" || name === "surname") {
138
+ return `LastName${index}`;
139
+ }
140
+
141
+ // タイトル / 件名
142
+ if (name === "title" || name === "subject" || name === "summary") {
143
+ return `Sample ${model.displayName || model.name} ${index}`;
144
+ }
145
+
146
+ // 説明 / 本文
147
+ if (name === "description" || name === "body" || name === "content" || name === "text") {
148
+ return `This is a sample ${toCamelCase(model.name)} description for item ${index}.`;
149
+ }
150
+
151
+ // コード / キー
152
+ if (name.includes("code") || name.includes("key") && !name.includes("api")) {
153
+ return `${modelKebab.toUpperCase().replace(/-/g, "_")}-${String(index).padStart(3, "0")}`;
154
+ }
155
+
156
+ // ステータス
157
+ if (name === "status") {
158
+ const statuses = ["active", "inactive", "pending"];
159
+ return statuses[(index - 1) % statuses.length];
160
+ }
161
+
162
+ // タイプ / カテゴリ
163
+ if (name === "type" || name === "category") {
164
+ return `type-${((index - 1) % 3) + 1}`;
165
+ }
166
+
167
+ // 住所
168
+ if (name.includes("address") || name.includes("city") || name.includes("country")) {
169
+ return `Address ${index}`;
170
+ }
171
+
172
+ // 部署
173
+ if (name === "department" || name === "team" || name === "group") {
174
+ const depts = ["Engineering", "Sales", "Marketing", "Support", "HR"];
175
+ return depts[(index - 1) % depts.length];
176
+ }
177
+
178
+ // *Id パターン(外部キー)
179
+ if (name.endsWith("id") && name !== "id") {
180
+ const refName = field.name.replace(/Id$/, "");
181
+ return `${toKebabCase(refName)}-${padIndex(((index - 1) % 3) + 1)}`;
182
+ }
183
+
184
+ // デフォルト
185
+ return `${modelKebab}-${toKebabCase(field.name)}-${index}`;
186
+ }
187
+
188
+ function generateNumberValue(name: string, index: number): number {
189
+ if (name.includes("age")) return 20 + index;
190
+ if (name.includes("price") || name.includes("cost") || name.includes("amount")) return index * 1000;
191
+ if (name.includes("count") || name.includes("quantity") || name.includes("qty")) return index * 5;
192
+ if (name.includes("priority") || name.includes("order") || name.includes("sort")) return index;
193
+ if (name.includes("rating") || name.includes("score")) return Math.min(index, 5);
194
+ return index;
195
+ }
196
+
197
+ function generateDateValue(index: number): string {
198
+ const baseDate = new Date("2026-01-01T00:00:00.000Z");
199
+ baseDate.setDate(baseDate.getDate() + index);
200
+ return baseDate.toISOString();
201
+ }
202
+
203
+ function padIndex(index: number): string {
204
+ return String(index).padStart(3, "0");
205
+ }
@@ -0,0 +1,360 @@
1
+ /**
2
+ * Plan / Apply Auth — add-auth を collect モードで実行して差分計画を作成し、
3
+ * 承認・鮮度チェックを経て実際に適用する。
4
+ */
5
+
6
+ import * as crypto from "crypto";
7
+ import * as path from "path";
8
+ import { addAuthCommand } from "../../cli/commands/add-auth";
9
+ import { MachineCommandError } from "../../machine/errors";
10
+ import { getSwallowKitVersion } from "../../version";
11
+ import { findArtifactRecord, loadArtifactLedger } from "../project/artifacts";
12
+ import { deletePlanState, loadPlanState, savePlanState } from "../project/state";
13
+ import {
14
+ ArtifactOwnership,
15
+ FileOperationSession,
16
+ hashFileIfExists,
17
+ runWithFileSession,
18
+ } from "./file-session";
19
+ import {
20
+ captureConsoleMessagesWithError,
21
+ deriveCapturedErrorMessage,
22
+ interceptProcessExit,
23
+ ProcessExitInterceptError,
24
+ trackFileMutations,
25
+ } from "./runtime";
26
+
27
+ export interface PlanAuthOptions {
28
+ provider: string;
29
+ scheme?: string;
30
+ /** SWA の許可 identity provider。未指定時は add-auth 側の既定 (['aad'])。 */
31
+ allowedProviders?: string[];
32
+ }
33
+
34
+ export type AuthPlanAction = "create" | "update" | "overwrite" | "append" | "delete" | "skip";
35
+
36
+ export interface AuthPlanOperation {
37
+ path: string;
38
+ action: AuthPlanAction;
39
+ ownership: ArtifactOwnership;
40
+ conflict: boolean;
41
+ conflictReason?: string;
42
+ }
43
+
44
+ export interface AuthPlanData {
45
+ planId: string;
46
+ planType: "auth";
47
+ provider: string;
48
+ scheme?: string;
49
+ allowedProviders?: string[];
50
+ createdAt: string;
51
+ swallowkitVersion: string;
52
+ operations: AuthPlanOperation[];
53
+ warnings: string[];
54
+ conflicts: AuthPlanOperation[];
55
+ requiresApproval: boolean;
56
+ /** project-relative path -> pre-plan content hash (null = did not exist). Used for stale-plan detection. */
57
+ fingerprints: Record<string, string | null>;
58
+ }
59
+
60
+ function deriveAuthErrorMessage(messages: { errors: string[]; warnings: string[]; logs: string[] }): string {
61
+ return deriveCapturedErrorMessage(messages, "Auth operation failed.");
62
+ }
63
+
64
+ /**
65
+ * add-auth を collect セッションで実行し、記録された操作を返す。ディスクには書き込まない。
66
+ */
67
+ async function collectAuthOperations(options: PlanAuthOptions): Promise<FileOperationSession> {
68
+ const session = new FileOperationSession("collect");
69
+ const originalMachineOutput = process.env.SWALLOWKIT_MACHINE_OUTPUT;
70
+ process.env.SWALLOWKIT_MACHINE_OUTPUT = "1";
71
+
72
+ try {
73
+ const captured = await captureConsoleMessagesWithError(async () => {
74
+ await interceptProcessExit(async () => {
75
+ await runWithFileSession(session, async () => {
76
+ await addAuthCommand({ provider: options.provider, scheme: options.scheme, allowedProviders: options.allowedProviders });
77
+ });
78
+ });
79
+ });
80
+
81
+ if (captured.error) {
82
+ if (captured.error instanceof ProcessExitInterceptError) {
83
+ throw new Error(deriveAuthErrorMessage(captured.messages));
84
+ }
85
+ if (captured.error instanceof Error) {
86
+ throw captured.error;
87
+ }
88
+ throw new Error(deriveAuthErrorMessage(captured.messages));
89
+ }
90
+
91
+ return session;
92
+ } finally {
93
+ if (originalMachineOutput === undefined) {
94
+ delete process.env.SWALLOWKIT_MACHINE_OUTPUT;
95
+ } else {
96
+ process.env.SWALLOWKIT_MACHINE_OUTPUT = originalMachineOutput;
97
+ }
98
+ }
99
+ }
100
+
101
+ export async function planAuthOperation(options: PlanAuthOptions): Promise<AuthPlanData> {
102
+ const projectRoot = process.cwd();
103
+ const session = await collectAuthOperations(options);
104
+ const ledger = loadArtifactLedger(projectRoot);
105
+
106
+ const operations: AuthPlanOperation[] = [];
107
+ const fingerprints: Record<string, string | null> = {};
108
+ const warnings = [...session.warnings];
109
+
110
+ for (const op of session.operations) {
111
+ fingerprints[op.path] = op.previousHash;
112
+
113
+ let action: AuthPlanAction;
114
+ let conflict = false;
115
+ let conflictReason: string | undefined;
116
+
117
+ switch (op.action) {
118
+ case "create":
119
+ action = "create";
120
+ break;
121
+ case "skip":
122
+ action = "skip";
123
+ break;
124
+ case "append":
125
+ action = "append";
126
+ break;
127
+ case "delete":
128
+ action = "delete";
129
+ break;
130
+ case "modify": {
131
+ if (op.ownership === "extension-point") {
132
+ // 設定ファイル・登録ポイントへの追記型変更は想定内の更新として扱う
133
+ action = "update";
134
+ break;
135
+ }
136
+ const record = ledger ? findArtifactRecord(ledger, op.path) : undefined;
137
+ if (record && record.contentHash === op.previousHash) {
138
+ action = "update";
139
+ } else if (record) {
140
+ action = "overwrite";
141
+ conflict = true;
142
+ conflictReason = "modified-after-generation";
143
+ } else {
144
+ action = "overwrite";
145
+ conflict = true;
146
+ conflictReason = "unknown-provenance";
147
+ warnings.push(
148
+ `unknown-provenance: ${op.path} exists but is not tracked in .swallowkit/artifacts.json; regenerating will overwrite it.`
149
+ );
150
+ }
151
+ break;
152
+ }
153
+ }
154
+
155
+ operations.push({
156
+ path: op.path,
157
+ action,
158
+ ownership: op.ownership,
159
+ conflict,
160
+ ...(conflictReason ? { conflictReason } : {}),
161
+ });
162
+ }
163
+
164
+ const conflicts = operations.filter((op) => op.conflict);
165
+
166
+ const planId = crypto
167
+ .createHash("sha256")
168
+ .update(
169
+ JSON.stringify({
170
+ provider: options.provider,
171
+ scheme: options.scheme,
172
+ allowedProviders: options.allowedProviders,
173
+ operations: operations.map((op) => ({ path: op.path, action: op.action })),
174
+ fingerprints,
175
+ })
176
+ )
177
+ .digest("hex")
178
+ .slice(0, 12);
179
+
180
+ const plan: AuthPlanData = {
181
+ planId,
182
+ planType: "auth",
183
+ provider: options.provider,
184
+ ...(options.scheme ? { scheme: options.scheme } : {}),
185
+ ...(options.allowedProviders && options.allowedProviders.length > 0 ? { allowedProviders: options.allowedProviders } : {}),
186
+ createdAt: new Date().toISOString(),
187
+ swallowkitVersion: getSwallowKitVersion(),
188
+ operations,
189
+ warnings,
190
+ conflicts,
191
+ requiresApproval: conflicts.length > 0,
192
+ fingerprints,
193
+ };
194
+
195
+ savePlanState(plan, projectRoot);
196
+ return plan;
197
+ }
198
+
199
+ export function verifyAuthPlanFingerprints(
200
+ plan: AuthPlanData,
201
+ projectRoot: string = process.cwd()
202
+ ): { fresh: boolean; changedFiles: string[] } {
203
+ const changedFiles: string[] = [];
204
+ for (const [relativePath, expectedHash] of Object.entries(plan.fingerprints)) {
205
+ const currentHash = hashFileIfExists(path.join(projectRoot, relativePath));
206
+ if (currentHash !== expectedHash) {
207
+ changedFiles.push(relativePath);
208
+ }
209
+ }
210
+ return { fresh: changedFiles.length === 0, changedFiles };
211
+ }
212
+
213
+ export interface ApplyAuthOptions {
214
+ provider?: string;
215
+ scheme?: string;
216
+ allowedProviders?: string[];
217
+ planId?: string;
218
+ approve?: boolean;
219
+ }
220
+
221
+ export interface ApplyAuthResult {
222
+ provider: string;
223
+ scheme?: string;
224
+ planId: string;
225
+ createdFiles: string[];
226
+ updatedFiles: string[];
227
+ appendedFiles: string[];
228
+ deletedFiles: string[];
229
+ createdDirectories: string[];
230
+ diagnostics: string[];
231
+ approvedConflicts: AuthPlanOperation[];
232
+ warnings: string[];
233
+ }
234
+
235
+ async function runCommitAuthOperation(options: PlanAuthOptions): Promise<{
236
+ createdFiles: string[];
237
+ updatedFiles: string[];
238
+ appendedFiles: string[];
239
+ deletedFiles: string[];
240
+ createdDirectories: string[];
241
+ diagnostics: string[];
242
+ }> {
243
+ const originalMachineOutput = process.env.SWALLOWKIT_MACHINE_OUTPUT;
244
+ process.env.SWALLOWKIT_MACHINE_OUTPUT = "1";
245
+
246
+ try {
247
+ const tracked = await trackFileMutations(async () => {
248
+ const captured = await captureConsoleMessagesWithError(async () => {
249
+ await interceptProcessExit(async () => {
250
+ await addAuthCommand({ provider: options.provider, scheme: options.scheme, allowedProviders: options.allowedProviders });
251
+ });
252
+ });
253
+
254
+ if (captured.error) {
255
+ if (captured.error instanceof ProcessExitInterceptError) {
256
+ throw new Error(deriveAuthErrorMessage(captured.messages));
257
+ }
258
+ if (captured.error instanceof Error) {
259
+ throw captured.error;
260
+ }
261
+ throw new Error(deriveAuthErrorMessage(captured.messages));
262
+ }
263
+
264
+ return captured.messages;
265
+ });
266
+
267
+ return {
268
+ createdFiles: tracked.mutations.createdFiles,
269
+ updatedFiles: tracked.mutations.updatedFiles,
270
+ appendedFiles: tracked.mutations.appendedFiles,
271
+ deletedFiles: tracked.mutations.deletedFiles,
272
+ createdDirectories: tracked.mutations.createdDirectories,
273
+ diagnostics: [
274
+ ...tracked.result.warnings.map((warning) => `warning:${warning}`),
275
+ ...tracked.result.errors.map((error) => `error:${error}`),
276
+ ],
277
+ };
278
+ } finally {
279
+ if (originalMachineOutput === undefined) {
280
+ delete process.env.SWALLOWKIT_MACHINE_OUTPUT;
281
+ } else {
282
+ process.env.SWALLOWKIT_MACHINE_OUTPUT = originalMachineOutput;
283
+ }
284
+ }
285
+ }
286
+
287
+ export async function applyAuthOperation(options: ApplyAuthOptions): Promise<ApplyAuthResult> {
288
+ const projectRoot = process.cwd();
289
+
290
+ let basePlan: AuthPlanData | null = null;
291
+
292
+ if (options.planId) {
293
+ basePlan = loadPlanState<AuthPlanData>(options.planId, projectRoot);
294
+ if (!basePlan) {
295
+ throw new MachineCommandError(
296
+ "plan-not-found",
297
+ `Plan "${options.planId}" was not found. Run "plan auth" to create a new plan.`,
298
+ { planId: options.planId },
299
+ "blocked"
300
+ );
301
+ }
302
+
303
+ const freshness = verifyAuthPlanFingerprints(basePlan, projectRoot);
304
+ if (!freshness.fresh) {
305
+ throw new MachineCommandError(
306
+ "stale-plan",
307
+ `Plan "${options.planId}" is stale: ${freshness.changedFiles.length} file(s) changed after the plan was created. Re-run "plan auth".`,
308
+ { planId: options.planId, changedFiles: freshness.changedFiles },
309
+ "blocked"
310
+ );
311
+ }
312
+ }
313
+
314
+ const provider = options.provider ?? basePlan?.provider;
315
+ if (!provider) {
316
+ throw new MachineCommandError(
317
+ "invalid-arguments",
318
+ "Either --provider or --plan <planId> must be provided.",
319
+ undefined,
320
+ "failed"
321
+ );
322
+ }
323
+
324
+ const effectiveOptions: PlanAuthOptions = {
325
+ provider,
326
+ scheme: options.scheme ?? basePlan?.scheme,
327
+ allowedProviders: options.allowedProviders ?? basePlan?.allowedProviders,
328
+ };
329
+
330
+ // 常に最新状態で collect し、競合を再確認する
331
+ const currentPlan = await planAuthOperation(effectiveOptions);
332
+
333
+ if (currentPlan.requiresApproval && !options.approve) {
334
+ throw new MachineCommandError(
335
+ "approval-required",
336
+ `${currentPlan.conflicts.length} file(s) were modified after generation and would be overwritten. Review the conflicts and re-run with --approve to proceed.`,
337
+ {
338
+ planId: currentPlan.planId,
339
+ conflicts: currentPlan.conflicts,
340
+ },
341
+ "requires-human"
342
+ );
343
+ }
344
+
345
+ const result = await runCommitAuthOperation(effectiveOptions);
346
+
347
+ deletePlanState(currentPlan.planId, projectRoot);
348
+ if (options.planId && options.planId !== currentPlan.planId) {
349
+ deletePlanState(options.planId, projectRoot);
350
+ }
351
+
352
+ return {
353
+ ...result,
354
+ provider,
355
+ ...(effectiveOptions.scheme ? { scheme: effectiveOptions.scheme } : {}),
356
+ planId: currentPlan.planId,
357
+ approvedConflicts: options.approve ? currentPlan.conflicts : [],
358
+ warnings: currentPlan.warnings,
359
+ };
360
+ }