swallowkit 1.0.0-beta.4 → 1.0.0-beta.40

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 (204) hide show
  1. package/LICENSE +21 -21
  2. package/README.ja.md +153 -215
  3. package/README.md +153 -216
  4. package/dist/__tests__/fixtures.d.ts +22 -0
  5. package/dist/__tests__/fixtures.d.ts.map +1 -0
  6. package/dist/__tests__/fixtures.js +146 -0
  7. package/dist/__tests__/fixtures.js.map +1 -0
  8. package/dist/cli/commands/add-auth.d.ts +10 -0
  9. package/dist/cli/commands/add-auth.d.ts.map +1 -0
  10. package/dist/cli/commands/add-auth.js +426 -0
  11. package/dist/cli/commands/add-auth.js.map +1 -0
  12. package/dist/cli/commands/add-connector.d.ts +20 -0
  13. package/dist/cli/commands/add-connector.d.ts.map +1 -0
  14. package/dist/cli/commands/add-connector.js +163 -0
  15. package/dist/cli/commands/add-connector.js.map +1 -0
  16. package/dist/cli/commands/create-model.d.ts +1 -4
  17. package/dist/cli/commands/create-model.d.ts.map +1 -1
  18. package/dist/cli/commands/create-model.js +21 -82
  19. package/dist/cli/commands/create-model.js.map +1 -1
  20. package/dist/cli/commands/dev-seeds.d.ts +57 -0
  21. package/dist/cli/commands/dev-seeds.d.ts.map +1 -0
  22. package/dist/cli/commands/dev-seeds.js +470 -0
  23. package/dist/cli/commands/dev-seeds.js.map +1 -0
  24. package/dist/cli/commands/dev.d.ts +33 -0
  25. package/dist/cli/commands/dev.d.ts.map +1 -1
  26. package/dist/cli/commands/dev.js +628 -146
  27. package/dist/cli/commands/dev.js.map +1 -1
  28. package/dist/cli/commands/index.d.ts +1 -0
  29. package/dist/cli/commands/index.d.ts.map +1 -1
  30. package/dist/cli/commands/index.js +3 -1
  31. package/dist/cli/commands/index.js.map +1 -1
  32. package/dist/cli/commands/init.d.ts +45 -0
  33. package/dist/cli/commands/init.d.ts.map +1 -1
  34. package/dist/cli/commands/init.js +2851 -1754
  35. package/dist/cli/commands/init.js.map +1 -1
  36. package/dist/cli/commands/scaffold.d.ts +4 -0
  37. package/dist/cli/commands/scaffold.d.ts.map +1 -1
  38. package/dist/cli/commands/scaffold.js +439 -130
  39. package/dist/cli/commands/scaffold.js.map +1 -1
  40. package/dist/cli/index.d.ts +5 -1
  41. package/dist/cli/index.d.ts.map +1 -1
  42. package/dist/cli/index.js +200 -42
  43. package/dist/cli/index.js.map +1 -1
  44. package/dist/core/config.d.ts +8 -2
  45. package/dist/core/config.d.ts.map +1 -1
  46. package/dist/core/config.js +94 -5
  47. package/dist/core/config.js.map +1 -1
  48. package/dist/core/mock/connector-mock-server.d.ts +101 -0
  49. package/dist/core/mock/connector-mock-server.d.ts.map +1 -0
  50. package/dist/core/mock/connector-mock-server.js +480 -0
  51. package/dist/core/mock/connector-mock-server.js.map +1 -0
  52. package/dist/core/mock/zod-mock-generator.d.ts +14 -0
  53. package/dist/core/mock/zod-mock-generator.d.ts.map +1 -0
  54. package/dist/core/mock/zod-mock-generator.js +163 -0
  55. package/dist/core/mock/zod-mock-generator.js.map +1 -0
  56. package/dist/core/operations/create-model.d.ts +15 -0
  57. package/dist/core/operations/create-model.d.ts.map +1 -0
  58. package/dist/core/operations/create-model.js +171 -0
  59. package/dist/core/operations/create-model.js.map +1 -0
  60. package/dist/core/operations/runtime.d.ts +32 -0
  61. package/dist/core/operations/runtime.d.ts.map +1 -0
  62. package/dist/core/operations/runtime.js +225 -0
  63. package/dist/core/operations/runtime.js.map +1 -0
  64. package/dist/core/operations/scaffold-machine.d.ts +16 -0
  65. package/dist/core/operations/scaffold-machine.d.ts.map +1 -0
  66. package/dist/core/operations/scaffold-machine.js +63 -0
  67. package/dist/core/operations/scaffold-machine.js.map +1 -0
  68. package/dist/core/project/manifest.d.ts +92 -0
  69. package/dist/core/project/manifest.d.ts.map +1 -0
  70. package/dist/core/project/manifest.js +321 -0
  71. package/dist/core/project/manifest.js.map +1 -0
  72. package/dist/core/project/validation.d.ts +20 -0
  73. package/dist/core/project/validation.d.ts.map +1 -0
  74. package/dist/core/project/validation.js +209 -0
  75. package/dist/core/project/validation.js.map +1 -0
  76. package/dist/core/scaffold/auth-generator.d.ts +38 -0
  77. package/dist/core/scaffold/auth-generator.d.ts.map +1 -0
  78. package/dist/core/scaffold/auth-generator.js +1244 -0
  79. package/dist/core/scaffold/auth-generator.js.map +1 -0
  80. package/dist/core/scaffold/connector-functions-generator.d.ts +41 -0
  81. package/dist/core/scaffold/connector-functions-generator.d.ts.map +1 -0
  82. package/dist/core/scaffold/connector-functions-generator.js +1027 -0
  83. package/dist/core/scaffold/connector-functions-generator.js.map +1 -0
  84. package/dist/core/scaffold/functions-generator.d.ts +7 -1
  85. package/dist/core/scaffold/functions-generator.d.ts.map +1 -1
  86. package/dist/core/scaffold/functions-generator.js +920 -220
  87. package/dist/core/scaffold/functions-generator.js.map +1 -1
  88. package/dist/core/scaffold/model-parser.d.ts +20 -1
  89. package/dist/core/scaffold/model-parser.d.ts.map +1 -1
  90. package/dist/core/scaffold/model-parser.js +328 -135
  91. package/dist/core/scaffold/model-parser.js.map +1 -1
  92. package/dist/core/scaffold/native-schema-generator.d.ts +13 -0
  93. package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -0
  94. package/dist/core/scaffold/native-schema-generator.js +696 -0
  95. package/dist/core/scaffold/native-schema-generator.js.map +1 -0
  96. package/dist/core/scaffold/nextjs-generator.d.ts +8 -0
  97. package/dist/core/scaffold/nextjs-generator.d.ts.map +1 -1
  98. package/dist/core/scaffold/nextjs-generator.js +314 -182
  99. package/dist/core/scaffold/nextjs-generator.js.map +1 -1
  100. package/dist/core/scaffold/openapi-generator.d.ts +3 -0
  101. package/dist/core/scaffold/openapi-generator.d.ts.map +1 -0
  102. package/dist/core/scaffold/openapi-generator.js +190 -0
  103. package/dist/core/scaffold/openapi-generator.js.map +1 -0
  104. package/dist/core/scaffold/ui-generator.d.ts +10 -4
  105. package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
  106. package/dist/core/scaffold/ui-generator.js +768 -663
  107. package/dist/core/scaffold/ui-generator.js.map +1 -1
  108. package/dist/database/base-model.d.ts +3 -3
  109. package/dist/database/base-model.js +3 -3
  110. package/dist/index.d.ts +2 -2
  111. package/dist/index.d.ts.map +1 -1
  112. package/dist/index.js +2 -1
  113. package/dist/index.js.map +1 -1
  114. package/dist/machine/contracts.d.ts +16 -0
  115. package/dist/machine/contracts.d.ts.map +1 -0
  116. package/dist/machine/contracts.js +3 -0
  117. package/dist/machine/contracts.js.map +1 -0
  118. package/dist/machine/errors.d.ts +11 -0
  119. package/dist/machine/errors.d.ts.map +1 -0
  120. package/dist/machine/errors.js +34 -0
  121. package/dist/machine/errors.js.map +1 -0
  122. package/dist/machine/index.d.ts +3 -0
  123. package/dist/machine/index.d.ts.map +1 -0
  124. package/dist/machine/index.js +156 -0
  125. package/dist/machine/index.js.map +1 -0
  126. package/dist/mcp/index.d.ts +25 -0
  127. package/dist/mcp/index.d.ts.map +1 -0
  128. package/dist/mcp/index.js +186 -0
  129. package/dist/mcp/index.js.map +1 -0
  130. package/dist/types/index.d.ts +65 -0
  131. package/dist/types/index.d.ts.map +1 -1
  132. package/dist/utils/package-manager.d.ts +110 -0
  133. package/dist/utils/package-manager.d.ts.map +1 -0
  134. package/dist/utils/package-manager.js +224 -0
  135. package/dist/utils/package-manager.js.map +1 -0
  136. package/dist/utils/python-uv.d.ts +21 -0
  137. package/dist/utils/python-uv.d.ts.map +1 -0
  138. package/dist/utils/python-uv.js +111 -0
  139. package/dist/utils/python-uv.js.map +1 -0
  140. package/package.json +84 -73
  141. package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +1139 -0
  142. package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +194 -0
  143. package/src/__tests__/__snapshots__/ui-generator.test.ts.snap +532 -0
  144. package/src/__tests__/auth.test.ts +654 -0
  145. package/src/__tests__/config.test.ts +274 -0
  146. package/src/__tests__/connector-functions-generator.test.ts +288 -0
  147. package/src/__tests__/connector-mock-server.test.ts +439 -0
  148. package/src/__tests__/connector-model-bff.test.ts +162 -0
  149. package/src/__tests__/dev-seeds.test.ts +173 -0
  150. package/src/__tests__/dev.test.ts +252 -0
  151. package/src/__tests__/fixtures.ts +144 -0
  152. package/src/__tests__/functions-generator.test.ts +237 -0
  153. package/src/__tests__/init.test.ts +197 -0
  154. package/src/__tests__/jest-globals.d.ts +8 -0
  155. package/src/__tests__/machine.test.ts +356 -0
  156. package/src/__tests__/mcp.test.ts +124 -0
  157. package/src/__tests__/model-parser.test.ts +52 -0
  158. package/src/__tests__/nextjs-generator.test.ts +97 -0
  159. package/src/__tests__/openapi-generator.test.ts +43 -0
  160. package/src/__tests__/package-manager.test.ts +201 -0
  161. package/src/__tests__/python-uv.test.ts +48 -0
  162. package/src/__tests__/scaffold.test.ts +191 -0
  163. package/src/__tests__/string-utils.test.ts +75 -0
  164. package/src/__tests__/tsconfig.json +15 -0
  165. package/src/__tests__/ui-generator.test.ts +144 -0
  166. package/src/__tests__/zod-mock-generator.test.ts +132 -0
  167. package/src/cli/commands/add-auth.ts +482 -0
  168. package/src/cli/commands/add-connector.ts +158 -0
  169. package/src/cli/commands/create-model.ts +62 -0
  170. package/src/cli/commands/dev-seeds.ts +614 -0
  171. package/src/cli/commands/dev.ts +1134 -0
  172. package/src/cli/commands/index.ts +9 -0
  173. package/src/cli/commands/init.ts +3600 -0
  174. package/src/cli/commands/provision.ts +193 -0
  175. package/src/cli/commands/scaffold.ts +991 -0
  176. package/src/cli/index.ts +196 -0
  177. package/src/core/config.ts +312 -0
  178. package/src/core/mock/connector-mock-server.ts +555 -0
  179. package/src/core/mock/zod-mock-generator.ts +205 -0
  180. package/src/core/operations/create-model.ts +174 -0
  181. package/src/core/operations/runtime.ts +235 -0
  182. package/src/core/operations/scaffold-machine.ts +91 -0
  183. package/src/core/project/manifest.ts +402 -0
  184. package/src/core/project/validation.ts +229 -0
  185. package/src/core/scaffold/auth-generator.ts +1284 -0
  186. package/src/core/scaffold/connector-functions-generator.ts +1128 -0
  187. package/src/core/scaffold/functions-generator.ts +970 -0
  188. package/src/core/scaffold/model-parser.ts +841 -0
  189. package/src/core/scaffold/native-schema-generator.ts +822 -0
  190. package/src/core/scaffold/nextjs-generator.ts +370 -0
  191. package/src/core/scaffold/openapi-generator.ts +212 -0
  192. package/src/core/scaffold/ui-generator.ts +1061 -0
  193. package/src/database/base-model.ts +184 -0
  194. package/src/database/client.ts +140 -0
  195. package/src/database/repository.ts +104 -0
  196. package/src/database/runtime-check.ts +25 -0
  197. package/src/index.ts +27 -0
  198. package/src/machine/contracts.ts +17 -0
  199. package/src/machine/errors.ts +34 -0
  200. package/src/machine/index.ts +173 -0
  201. package/src/mcp/index.ts +191 -0
  202. package/src/types/index.ts +134 -0
  203. package/src/utils/package-manager.ts +238 -0
  204. package/src/utils/python-uv.ts +96 -0
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Zod モックデータジェネレーターのテスト
3
+ */
4
+
5
+ import { generateMockDocument, generateMockDocuments } from "../core/mock/zod-mock-generator";
6
+ import {
7
+ createBasicModelInfo,
8
+ createRdbConnectorModelInfo,
9
+ createApiConnectorModelInfo,
10
+ createModelInfoWithEnum,
11
+ } from "./fixtures";
12
+
13
+ describe("generateMockDocument", () => {
14
+ it("generates a document with all model fields", () => {
15
+ const model = createBasicModelInfo();
16
+ const doc = generateMockDocument(model, 1);
17
+
18
+ expect(doc).toHaveProperty("id");
19
+ expect(doc).toHaveProperty("title");
20
+ expect(doc).toHaveProperty("description");
21
+ expect(doc).toHaveProperty("completed");
22
+ expect(doc).toHaveProperty("createdAt");
23
+ expect(doc).toHaveProperty("updatedAt");
24
+ });
25
+
26
+ it("generates id using model name and index", () => {
27
+ const model = createBasicModelInfo();
28
+ const doc = generateMockDocument(model, 1);
29
+ expect(doc.id).toBe("todo-001");
30
+ });
31
+
32
+ it("generates different values for different indices", () => {
33
+ const model = createBasicModelInfo();
34
+ const doc1 = generateMockDocument(model, 1);
35
+ const doc2 = generateMockDocument(model, 2);
36
+ expect(doc1.id).not.toBe(doc2.id);
37
+ expect(doc1.title).not.toBe(doc2.title);
38
+ });
39
+
40
+ it("generates boolean fields with alternating values", () => {
41
+ const model = createBasicModelInfo();
42
+ const doc1 = generateMockDocument(model, 1);
43
+ const doc2 = generateMockDocument(model, 2);
44
+ expect(typeof doc1.completed).toBe("boolean");
45
+ expect(doc1.completed).not.toBe(doc2.completed);
46
+ });
47
+
48
+ it("generates ISO date strings for *At fields", () => {
49
+ const model = createBasicModelInfo();
50
+ const doc = generateMockDocument(model, 1);
51
+ expect(typeof doc.createdAt).toBe("string");
52
+ // Should be parseable as ISO date
53
+ expect(new Date(doc.createdAt as string).toISOString()).toBe(doc.createdAt);
54
+ });
55
+
56
+ it("generates string values for title fields", () => {
57
+ const model = createBasicModelInfo();
58
+ const doc = generateMockDocument(model, 1);
59
+ expect(typeof doc.title).toBe("string");
60
+ expect(doc.title).toContain("1");
61
+ });
62
+
63
+ it("handles optional string fields", () => {
64
+ const model = createBasicModelInfo();
65
+ const doc = generateMockDocument(model, 1);
66
+ // description is optional but still gets a value for mock data
67
+ expect(typeof doc.description).toBe("string");
68
+ });
69
+
70
+ it("generates email-like values for email fields", () => {
71
+ const model = createRdbConnectorModelInfo();
72
+ const doc = generateMockDocument(model, 1);
73
+ expect(doc.email).toContain("@example.com");
74
+ });
75
+
76
+ it("generates department values for department fields", () => {
77
+ const model = createRdbConnectorModelInfo();
78
+ const doc = generateMockDocument(model, 1);
79
+ expect(typeof doc.department).toBe("string");
80
+ // Should be one of the department names
81
+ expect(["Engineering", "Sales", "Marketing", "Support", "HR"]).toContain(doc.department);
82
+ });
83
+
84
+ it("cycles enum values across indices", () => {
85
+ const model = createModelInfoWithEnum();
86
+ const doc1 = generateMockDocument(model, 1);
87
+ const doc2 = generateMockDocument(model, 2);
88
+ const doc3 = generateMockDocument(model, 3);
89
+ expect(doc1.status).toBe("open");
90
+ expect(doc2.status).toBe("in_progress");
91
+ expect(doc3.status).toBe("closed");
92
+ });
93
+
94
+ it("generates foreign key references for *Id fields", () => {
95
+ const model = createApiConnectorModelInfo();
96
+ const doc = generateMockDocument(model, 1);
97
+ expect(typeof doc.projectId).toBe("string");
98
+ expect(doc.projectId).toContain("project-");
99
+ });
100
+ });
101
+
102
+ describe("generateMockDocuments", () => {
103
+ it("generates the specified number of documents", () => {
104
+ const model = createBasicModelInfo();
105
+ const docs = generateMockDocuments(model, 3);
106
+ expect(docs).toHaveLength(3);
107
+ });
108
+
109
+ it("defaults to 5 documents", () => {
110
+ const model = createBasicModelInfo();
111
+ const docs = generateMockDocuments(model);
112
+ expect(docs).toHaveLength(5);
113
+ });
114
+
115
+ it("generates unique ids for each document", () => {
116
+ const model = createBasicModelInfo();
117
+ const docs = generateMockDocuments(model, 5);
118
+ const ids = docs.map((d) => d.id);
119
+ const uniqueIds = new Set(ids);
120
+ expect(uniqueIds.size).toBe(5);
121
+ });
122
+
123
+ it("works with connector models", () => {
124
+ const model = createRdbConnectorModelInfo();
125
+ const docs = generateMockDocuments(model, 3);
126
+ expect(docs).toHaveLength(3);
127
+ // All should have email fields with @example.com
128
+ for (const doc of docs) {
129
+ expect(doc.email).toContain("@example.com");
130
+ }
131
+ });
132
+ });
@@ -0,0 +1,482 @@
1
+ /**
2
+ * SwallowKit Add-Auth コマンド
3
+ * 認証認可基盤ファイルを生成する
4
+ */
5
+
6
+ import * as fs from "fs";
7
+ import * as path from "path";
8
+ import { ensureSwallowKitProject, getBackendLanguage, getConnectorDefinition, getFullConfig } from "../../core/config";
9
+ import { AuthProvider, BackendLanguage, CustomJwtConfig, RdbConnectorConfig } from "../../types";
10
+ import {
11
+ generateAuthModels,
12
+ generateAuthFunctionsTS,
13
+ generateJwtHelperTS,
14
+ generateAuthFunctionsCSharp,
15
+ generateJwtHelperCSharp,
16
+ generateAuthFunctionsPython,
17
+ generateJwtHelperPython,
18
+ generateBFFAuthLoginRoute,
19
+ generateBFFAuthLogoutRoute,
20
+ generateBFFAuthMeRoute,
21
+ generateProxy,
22
+ generateLoginPage,
23
+ generateAuthContext,
24
+ generateBFFCallFunctionWithAuth,
25
+ } from "../../core/scaffold/auth-generator";
26
+ import { syncProjectManifest } from "../../core/project/manifest";
27
+ import { buildSharedTsConfig } from "./init";
28
+
29
+ interface AddAuthOptions {
30
+ provider?: string;
31
+ }
32
+
33
+ export async function addAuthCommand(options: AddAuthOptions) {
34
+ ensureSwallowKitProject("add-auth");
35
+
36
+ console.log(" SwallowKit Add-Auth: Setting up authentication...\n");
37
+
38
+ const provider = (options.provider || "custom-jwt") as AuthProvider;
39
+ if (!["custom-jwt", "swa", "swa-custom", "none"].includes(provider)) {
40
+ console.error(` Unknown provider: ${provider}. Use: custom-jwt | swa | swa-custom | none`);
41
+ process.exit(1);
42
+ }
43
+
44
+ const backendLanguage = getBackendLanguage();
45
+ const config = getFullConfig();
46
+ const cwd = process.cwd();
47
+
48
+ // Read project name from package.json
49
+ const pkgPath = path.join(cwd, "package.json");
50
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
51
+ const projectName = pkg.name || "app";
52
+
53
+ // Read shared package name
54
+ const sharedPkgPath = path.join(cwd, "shared", "package.json");
55
+ let sharedPackageName = `@${projectName}/shared`;
56
+ if (fs.existsSync(sharedPkgPath)) {
57
+ const sharedPkg = JSON.parse(fs.readFileSync(sharedPkgPath, "utf-8"));
58
+ sharedPackageName = sharedPkg.name || sharedPackageName;
59
+ }
60
+
61
+ // Default custom-jwt config
62
+ const customJwtConfig: CustomJwtConfig = config.auth?.customJwt || {
63
+ userConnector: "mysql",
64
+ userTable: "users",
65
+ loginIdColumn: "login_id",
66
+ passwordHashColumn: "password_hash",
67
+ rolesColumn: "roles",
68
+ jwtSecretEnv: "JWT_SECRET",
69
+ tokenExpiry: "24h",
70
+ };
71
+
72
+ // 1. Generate shared/models/auth.ts
73
+ console.log(" Generating auth models...");
74
+ const modelsDir = path.join(cwd, "shared", "models");
75
+ fs.mkdirSync(modelsDir, { recursive: true });
76
+ const authModelPath = path.join(modelsDir, "auth.ts");
77
+ fs.writeFileSync(authModelPath, generateAuthModels(), "utf-8");
78
+ console.log(` Created: shared/models/auth.ts`);
79
+
80
+ // Ensure shared package has build infrastructure (tsconfig, build script)
81
+ ensureSharedBuildInfrastructure(cwd);
82
+
83
+ // Update shared/index.ts to re-export auth
84
+ updateSharedIndex(cwd);
85
+
86
+ // Resolve RDB provider for dependency installation
87
+ const connDef = getConnectorDefinition(customJwtConfig.userConnector);
88
+ const rdbProvider = (connDef as RdbConnectorConfig | undefined)?.provider ?? "mysql";
89
+
90
+ // 2. Generate Functions auth code
91
+ console.log("\n Generating auth functions...");
92
+ generateFunctionsAuth(cwd, backendLanguage, sharedPackageName, customJwtConfig);
93
+
94
+ // 3. Generate BFF auth routes
95
+ console.log("\n Generating BFF auth routes...");
96
+ generateBFFAuth(cwd, projectName, sharedPackageName);
97
+
98
+ // 4. Generate proxy
99
+ console.log("\n Generating proxy...");
100
+ const proxyPath = path.join(cwd, "proxy.ts");
101
+ fs.writeFileSync(proxyPath, generateProxy(projectName), "utf-8");
102
+ console.log(` Created: proxy.ts`);
103
+
104
+ // 5. Generate login page
105
+ console.log("\n Generating login page...");
106
+ const loginDir = path.join(cwd, "app", "login");
107
+ fs.mkdirSync(loginDir, { recursive: true });
108
+ fs.writeFileSync(path.join(loginDir, "page.tsx"), generateLoginPage(), "utf-8");
109
+ console.log(` Created: app/login/page.tsx`);
110
+
111
+ // 6. Generate auth context
112
+ console.log("\n Generating auth context...");
113
+ const authLibDir = path.join(cwd, "lib", "auth");
114
+ fs.mkdirSync(authLibDir, { recursive: true });
115
+ fs.writeFileSync(path.join(authLibDir, "auth-context.tsx"), generateAuthContext(), "utf-8");
116
+ console.log(`✅ Created: lib/auth/auth-context.tsx`);
117
+
118
+ // 7. Update callFunction with auth support
119
+ console.log("\n Updating callFunction with auth support...");
120
+ const callFnPath = path.join(cwd, "lib", "api", "call-function.ts");
121
+ const callFnDir = path.dirname(callFnPath);
122
+ fs.mkdirSync(callFnDir, { recursive: true });
123
+ fs.writeFileSync(callFnPath, generateBFFCallFunctionWithAuth(), "utf-8");
124
+ console.log(` Updated: lib/api/call-function.ts`);
125
+
126
+ // 8. Update swallowkit.config.js
127
+ console.log("\n Updating configuration...");
128
+ updateConfigWithAuth(cwd, provider, customJwtConfig);
129
+
130
+ // 9. Update environment files
131
+ console.log("\n Updating environment files...");
132
+ updateEnvironmentFiles(cwd);
133
+
134
+ // 10. Install dependencies
135
+ console.log("\n Installing auth dependencies...");
136
+ await installAuthDependencies(cwd, backendLanguage, rdbProvider);
137
+ await syncProjectManifest();
138
+
139
+ console.log("\n Authentication setup complete!");
140
+ console.log("\n Next steps:");
141
+ console.log(" 1. Review the generated files");
142
+ console.log(" 2. Set JWT_SECRET in functions/local.settings.json");
143
+ if (provider === "custom-jwt") {
144
+ console.log(" 3. Ensure your user database table matches the config");
145
+ console.log(" 4. Add authPolicy to your models for role-based access control:");
146
+ console.log(" export const authPolicy = { roles: ['admin'] };");
147
+ }
148
+ console.log(` 5. Run scaffold to regenerate functions with auth guards`);
149
+ }
150
+
151
+ /**
152
+ * Ensure the shared package has proper build infrastructure
153
+ * (tsconfig.json, build script, typescript devDependency).
154
+ * Required for `dev` command which runs `npm run --workspace=shared build`.
155
+ */
156
+ function ensureSharedBuildInfrastructure(cwd: string): void {
157
+ const sharedDir = path.join(cwd, "shared");
158
+ const pkgPath = path.join(sharedDir, "package.json");
159
+ if (!fs.existsSync(pkgPath)) return;
160
+
161
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
162
+ let updated = false;
163
+
164
+ // Ensure scripts.build exists
165
+ if (!pkg.scripts?.build) {
166
+ if (!pkg.scripts) pkg.scripts = {};
167
+ pkg.scripts.build = "tsc";
168
+ pkg.scripts.watch = "tsc --watch";
169
+ updated = true;
170
+ }
171
+
172
+ // Ensure main points to compiled output
173
+ if (!pkg.main || pkg.main === "index.ts") {
174
+ pkg.main = "dist/index.js";
175
+ pkg.types = "dist/index.d.ts";
176
+ updated = true;
177
+ }
178
+
179
+ // Ensure typescript devDependency
180
+ if (!pkg.devDependencies?.typescript) {
181
+ if (!pkg.devDependencies) pkg.devDependencies = {};
182
+ pkg.devDependencies.typescript = "^5.0.0";
183
+ updated = true;
184
+ }
185
+
186
+ if (updated) {
187
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2), "utf-8");
188
+ console.log(` Updated: shared/package.json (added build infrastructure)`);
189
+ }
190
+
191
+ // Ensure tsconfig.json exists
192
+ const tsconfigPath = path.join(sharedDir, "tsconfig.json");
193
+ if (!fs.existsSync(tsconfigPath)) {
194
+ fs.writeFileSync(tsconfigPath, JSON.stringify(buildSharedTsConfig(), null, 2), "utf-8");
195
+ console.log(` Created: shared/tsconfig.json`);
196
+ }
197
+ }
198
+
199
+ function updateSharedIndex(cwd: string): void {
200
+ const indexPath = path.join(cwd, "shared", "index.ts");
201
+ if (fs.existsSync(indexPath)) {
202
+ let content = fs.readFileSync(indexPath, "utf-8");
203
+ if (!content.includes("./models/auth")) {
204
+ content += `\nexport { LoginRequest, AuthUser, LoginResponse } from './models/auth';\n`;
205
+ fs.writeFileSync(indexPath, content, "utf-8");
206
+ console.log(` Updated: shared/index.ts`);
207
+ }
208
+ } else {
209
+ fs.writeFileSync(indexPath, `export { LoginRequest, AuthUser, LoginResponse } from './models/auth';\n`, "utf-8");
210
+ console.log(` Created: shared/index.ts`);
211
+ }
212
+ }
213
+
214
+ function generateFunctionsAuth(
215
+ cwd: string,
216
+ backendLanguage: BackendLanguage,
217
+ sharedPackageName: string,
218
+ config: CustomJwtConfig,
219
+ ): void {
220
+ const functionsDir = path.join(cwd, "functions");
221
+
222
+ // Resolve the RDB provider from the connector definition
223
+ const connDef = getConnectorDefinition(config.userConnector);
224
+ const provider = (connDef as RdbConnectorConfig | undefined)?.provider ?? "mysql";
225
+
226
+ if (backendLanguage === "typescript") {
227
+ // Auth functions
228
+ const srcDir = path.join(functionsDir, "src");
229
+ fs.mkdirSync(srcDir, { recursive: true });
230
+ fs.writeFileSync(
231
+ path.join(srcDir, "auth.ts"),
232
+ generateAuthFunctionsTS(sharedPackageName, config, provider),
233
+ "utf-8"
234
+ );
235
+ console.log(` Created: functions/src/auth.ts`);
236
+
237
+ // JWT helper
238
+ const authDir = path.join(srcDir, "auth");
239
+ fs.mkdirSync(authDir, { recursive: true });
240
+ fs.writeFileSync(
241
+ path.join(authDir, "jwt-helper.ts"),
242
+ generateJwtHelperTS(),
243
+ "utf-8"
244
+ );
245
+ console.log(` Created: functions/src/auth/jwt-helper.ts`);
246
+ } else if (backendLanguage === "csharp") {
247
+ const authDir = path.join(functionsDir, "Auth");
248
+ fs.mkdirSync(authDir, { recursive: true });
249
+ fs.writeFileSync(
250
+ path.join(authDir, "AuthFunctions.cs"),
251
+ generateAuthFunctionsCSharp(config, provider),
252
+ "utf-8"
253
+ );
254
+ console.log(` Created: functions/Auth/AuthFunctions.cs`);
255
+ fs.writeFileSync(
256
+ path.join(authDir, "JwtHelper.cs"),
257
+ generateJwtHelperCSharp(),
258
+ "utf-8"
259
+ );
260
+ console.log(` Created: functions/Auth/JwtHelper.cs`);
261
+ } else if (backendLanguage === "python") {
262
+ const blueprintsDir = path.join(functionsDir, "blueprints");
263
+ fs.mkdirSync(blueprintsDir, { recursive: true });
264
+ fs.writeFileSync(
265
+ path.join(blueprintsDir, "auth.py"),
266
+ generateAuthFunctionsPython(config, provider),
267
+ "utf-8"
268
+ );
269
+ console.log(` Created: functions/blueprints/auth.py`);
270
+
271
+ const authDir = path.join(functionsDir, "auth");
272
+ fs.mkdirSync(authDir, { recursive: true });
273
+ fs.writeFileSync(
274
+ path.join(authDir, "jwt_helper.py"),
275
+ generateJwtHelperPython(),
276
+ "utf-8"
277
+ );
278
+ console.log(` Created: functions/auth/jwt_helper.py`);
279
+
280
+ // __init__.py
281
+ fs.writeFileSync(path.join(authDir, "__init__.py"), "", "utf-8");
282
+
283
+ // Register auth blueprint in function_app.py
284
+ const functionAppPath = path.join(functionsDir, "function_app.py");
285
+ if (fs.existsSync(functionAppPath)) {
286
+ const content = fs.readFileSync(functionAppPath, "utf-8");
287
+ const authImport = "from blueprints.auth import bp as auth_bp";
288
+ const authRegister = "app.register_blueprint(auth_bp)";
289
+ if (!content.includes(authImport)) {
290
+ const marker = "# SwallowKit scaffold registrations";
291
+ if (content.includes(marker)) {
292
+ const updated = content.replace(
293
+ marker,
294
+ `${authImport}\n${authRegister}\n${marker}`
295
+ );
296
+ fs.writeFileSync(functionAppPath, updated, "utf-8");
297
+ console.log(` Updated: functions/function_app.py (registered auth blueprint)`);
298
+ }
299
+ }
300
+ }
301
+ }
302
+ }
303
+
304
+ function generateBFFAuth(cwd: string, projectName: string, sharedPackageName: string): void {
305
+ const authApiDir = path.join(cwd, "app", "api", "auth");
306
+
307
+ // Login route
308
+ const loginDir = path.join(authApiDir, "login");
309
+ fs.mkdirSync(loginDir, { recursive: true });
310
+ fs.writeFileSync(
311
+ path.join(loginDir, "route.ts"),
312
+ generateBFFAuthLoginRoute(projectName, sharedPackageName),
313
+ "utf-8"
314
+ );
315
+ console.log(` Created: app/api/auth/login/route.ts`);
316
+
317
+ // Logout route
318
+ const logoutDir = path.join(authApiDir, "logout");
319
+ fs.mkdirSync(logoutDir, { recursive: true });
320
+ fs.writeFileSync(
321
+ path.join(logoutDir, "route.ts"),
322
+ generateBFFAuthLogoutRoute(projectName),
323
+ "utf-8"
324
+ );
325
+ console.log(` Created: app/api/auth/logout/route.ts`);
326
+
327
+ // Me route
328
+ const meDir = path.join(authApiDir, "me");
329
+ fs.mkdirSync(meDir, { recursive: true });
330
+ fs.writeFileSync(
331
+ path.join(meDir, "route.ts"),
332
+ generateBFFAuthMeRoute(),
333
+ "utf-8"
334
+ );
335
+ console.log(` Created: app/api/auth/me/route.ts`);
336
+ }
337
+
338
+ function updateConfigWithAuth(cwd: string, provider: AuthProvider, config: CustomJwtConfig): void {
339
+ const configPath = path.join(cwd, "swallowkit.config.js");
340
+ if (!fs.existsSync(configPath)) {
341
+ console.warn(" swallowkit.config.js not found. Please add auth config manually.");
342
+ return;
343
+ }
344
+
345
+ const content = fs.readFileSync(configPath, "utf-8");
346
+
347
+ if (content.includes("auth:") || content.includes("auth :")) {
348
+ console.log(" 'auth' section already exists in swallowkit.config.js");
349
+ return;
350
+ }
351
+
352
+ // Find the last property before the closing of module.exports
353
+ const closingBraceIdx = content.lastIndexOf("}");
354
+ if (closingBraceIdx === -1) {
355
+ console.error(" Could not parse config file structure.");
356
+ return;
357
+ }
358
+
359
+ const beforeClosing = content.substring(0, closingBraceIdx).trimEnd();
360
+ const needsComma = !beforeClosing.endsWith(",") && !beforeClosing.endsWith("{");
361
+
362
+ const authBlock = `${needsComma ? "," : ""}
363
+ // 認証認可設定
364
+ auth: {
365
+ provider: '${provider}',
366
+ customJwt: {
367
+ userConnector: '${config.userConnector}',
368
+ userTable: '${config.userTable}',
369
+ loginIdColumn: '${config.loginIdColumn}',
370
+ passwordHashColumn: '${config.passwordHashColumn}',
371
+ rolesColumn: '${config.rolesColumn}',
372
+ jwtSecretEnv: '${config.jwtSecretEnv || "JWT_SECRET"}',
373
+ tokenExpiry: '${config.tokenExpiry || "24h"}',
374
+ },
375
+ authorization: {
376
+ defaultPolicy: 'authenticated',
377
+ },
378
+ },
379
+ `;
380
+
381
+ const newContent = content.substring(0, closingBraceIdx) + authBlock + content.substring(closingBraceIdx);
382
+ fs.writeFileSync(configPath, newContent, "utf-8");
383
+ console.log(` Updated: swallowkit.config.js`);
384
+ }
385
+
386
+ function updateEnvironmentFiles(cwd: string): void {
387
+ // Update functions/local.settings.json
388
+ const localSettingsPath = path.join(cwd, "functions", "local.settings.json");
389
+ if (fs.existsSync(localSettingsPath)) {
390
+ const settings = JSON.parse(fs.readFileSync(localSettingsPath, "utf-8"));
391
+ if (!settings.Values) settings.Values = {};
392
+ if (!settings.Values.JWT_SECRET) {
393
+ settings.Values.JWT_SECRET = "dev-jwt-secret-change-in-production-min-32-chars!!";
394
+ }
395
+ fs.writeFileSync(localSettingsPath, JSON.stringify(settings, null, 2), "utf-8");
396
+ console.log(` Updated: functions/local.settings.json`);
397
+ }
398
+
399
+ // Update .env.example
400
+ const envExamplePath = path.join(cwd, ".env.example");
401
+ if (fs.existsSync(envExamplePath)) {
402
+ let content = fs.readFileSync(envExamplePath, "utf-8");
403
+ if (!content.includes("JWT_SECRET")) {
404
+ content += "\n# Authentication\nJWT_SECRET=your-jwt-secret-key-at-least-32-chars\n";
405
+ fs.writeFileSync(envExamplePath, content, "utf-8");
406
+ console.log(` Updated: .env.example`);
407
+ }
408
+ }
409
+ }
410
+
411
+ async function installAuthDependencies(cwd: string, backendLanguage: BackendLanguage, provider: "mysql" | "postgres" | "sqlserver" = "mysql"): Promise<void> {
412
+ if (backendLanguage === "typescript") {
413
+ const funcPkgPath = path.join(cwd, "functions", "package.json");
414
+ if (fs.existsSync(funcPkgPath)) {
415
+ const funcPkg = JSON.parse(fs.readFileSync(funcPkgPath, "utf-8"));
416
+ if (!funcPkg.dependencies) funcPkg.dependencies = {};
417
+ funcPkg.dependencies["jsonwebtoken"] = "^9.0.0";
418
+ funcPkg.dependencies["bcryptjs"] = "^2.4.3";
419
+ // RDB driver based on provider
420
+ if (provider === "mysql") funcPkg.dependencies["mysql2"] = "^3.11.0";
421
+ else if (provider === "postgres") funcPkg.dependencies["pg"] = "^8.13.0";
422
+ else funcPkg.dependencies["mssql"] = "^11.0.0";
423
+ if (!funcPkg.devDependencies) funcPkg.devDependencies = {};
424
+ funcPkg.devDependencies["@types/jsonwebtoken"] = "^9.0.0";
425
+ funcPkg.devDependencies["@types/bcryptjs"] = "^2.4.0";
426
+ if (provider === "postgres") funcPkg.devDependencies["@types/pg"] = "^8.11.0";
427
+ fs.writeFileSync(funcPkgPath, JSON.stringify(funcPkg, null, 2), "utf-8");
428
+ console.log(` Updated: functions/package.json with auth dependencies (${provider})`);
429
+ }
430
+ } else if (backendLanguage === "csharp") {
431
+ // Add NuGet package references to .csproj
432
+ const functionsDir = path.join(cwd, "functions");
433
+ const csprojFiles = fs.readdirSync(functionsDir).filter((f: string) => f.endsWith(".csproj"));
434
+ if (csprojFiles.length > 0) {
435
+ const csprojPath = path.join(functionsDir, csprojFiles[0]);
436
+ let csprojContent = fs.readFileSync(csprojPath, "utf-8");
437
+ const nugetPackages: { name: string; version: string }[] = [
438
+ { name: "System.IdentityModel.Tokens.Jwt", version: "7.0.0" },
439
+ { name: "Microsoft.IdentityModel.Tokens", version: "7.0.0" },
440
+ { name: "BCrypt.Net-Next", version: "4.0.3" },
441
+ ];
442
+ // RDB driver based on provider
443
+ if (provider === "mysql") nugetPackages.push({ name: "MySqlConnector", version: "2.3.0" });
444
+ else if (provider === "postgres") nugetPackages.push({ name: "Npgsql", version: "8.0.0" });
445
+ else nugetPackages.push({ name: "Microsoft.Data.SqlClient", version: "5.2.0" });
446
+ for (const pkg of nugetPackages) {
447
+ if (!csprojContent.includes(`"${pkg.name}"`)) {
448
+ const insertPoint = csprojContent.lastIndexOf("</ItemGroup>");
449
+ if (insertPoint >= 0) {
450
+ csprojContent =
451
+ csprojContent.slice(0, insertPoint) +
452
+ ` <PackageReference Include="${pkg.name}" Version="${pkg.version}" />\n ` +
453
+ csprojContent.slice(insertPoint);
454
+ }
455
+ }
456
+ }
457
+ fs.writeFileSync(csprojPath, csprojContent, "utf-8");
458
+ console.log(` Updated: ${csprojFiles[0]} with auth NuGet packages (${provider})`);
459
+ }
460
+ } else if (backendLanguage === "python") {
461
+ // Add Python dependencies to requirements.txt
462
+ const requirementsPath = path.join(cwd, "functions", "requirements.txt");
463
+ const baseDeps = ["PyJWT>=2.8.0", "bcrypt>=4.1.0"];
464
+ // RDB driver based on provider
465
+ if (provider === "mysql") baseDeps.push("mysql-connector-python>=8.3.0");
466
+ else if (provider === "postgres") baseDeps.push("psycopg2-binary>=2.9.0");
467
+ else baseDeps.push("pymssql>=2.2.0");
468
+ if (fs.existsSync(requirementsPath)) {
469
+ let content = fs.readFileSync(requirementsPath, "utf-8");
470
+ for (const dep of baseDeps) {
471
+ const pkgName = dep.split(">=")[0].split("==")[0];
472
+ if (!content.includes(pkgName)) {
473
+ content += `${dep}\n`;
474
+ }
475
+ }
476
+ fs.writeFileSync(requirementsPath, content, "utf-8");
477
+ } else {
478
+ fs.writeFileSync(requirementsPath, baseDeps.join("\n") + "\n", "utf-8");
479
+ }
480
+ console.log(` Updated: functions/requirements.txt with auth dependencies`);
481
+ }
482
+ }