fragment-ts 2.0.3 → 2.0.4

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 (258) hide show
  1. package/dist/platform/cli/commands/install/command.d.ts +14 -0
  2. package/dist/platform/cli/commands/install/command.d.ts.map +1 -0
  3. package/dist/platform/cli/commands/install/command.js +145 -0
  4. package/dist/platform/cli/commands/install/command.js.map +1 -0
  5. package/dist/platform/cli/commands/register.d.ts.map +1 -1
  6. package/dist/platform/cli/commands/register.js +37 -28
  7. package/dist/platform/cli/commands/register.js.map +1 -1
  8. package/dist/platform/cli/index.js +61 -1
  9. package/dist/platform/cli/index.js.map +1 -1
  10. package/dist/platform/cli/web/commands/create/feature.d.ts +11 -0
  11. package/dist/platform/cli/web/commands/create/feature.d.ts.map +1 -0
  12. package/dist/platform/cli/web/commands/create/feature.js +43 -0
  13. package/dist/platform/cli/web/commands/create/feature.js.map +1 -0
  14. package/dist/platform/cli/web/commands/create/module.d.ts +11 -0
  15. package/dist/platform/cli/web/commands/create/module.d.ts.map +1 -0
  16. package/dist/platform/cli/web/commands/create/module.js +44 -0
  17. package/dist/platform/cli/web/commands/create/module.js.map +1 -0
  18. package/dist/platform/cli/web/commands/create/mvvm.d.ts +11 -0
  19. package/dist/platform/cli/web/commands/create/mvvm.d.ts.map +1 -0
  20. package/dist/platform/cli/web/commands/create/mvvm.js +43 -0
  21. package/dist/platform/cli/web/commands/create/mvvm.js.map +1 -0
  22. package/dist/platform/cli/web/commands/init-fullstack.d.ts +12 -0
  23. package/dist/platform/cli/web/commands/init-fullstack.d.ts.map +1 -0
  24. package/dist/platform/cli/web/commands/init-fullstack.js +42 -0
  25. package/dist/platform/cli/web/commands/init-fullstack.js.map +1 -0
  26. package/dist/platform/cli/web/commands/init-web.d.ts +12 -0
  27. package/dist/platform/cli/web/commands/init-web.d.ts.map +1 -0
  28. package/dist/platform/cli/web/commands/init-web.js +70 -0
  29. package/dist/platform/cli/web/commands/init-web.js.map +1 -0
  30. package/dist/platform/cli/web/commands/install-api.d.ts +11 -0
  31. package/dist/platform/cli/web/commands/install-api.d.ts.map +1 -0
  32. package/dist/platform/cli/web/commands/install-api.js +92 -0
  33. package/dist/platform/cli/web/commands/install-api.js.map +1 -0
  34. package/dist/platform/cli/web/commands/install-web.d.ts +13 -0
  35. package/dist/platform/cli/web/commands/install-web.d.ts.map +1 -0
  36. package/dist/platform/cli/web/commands/install-web.js +102 -0
  37. package/dist/platform/cli/web/commands/install-web.js.map +1 -0
  38. package/dist/platform/cli/web/commands/install.d.ts +3 -0
  39. package/dist/platform/cli/web/commands/install.d.ts.map +1 -0
  40. package/dist/platform/cli/web/commands/install.js +23 -0
  41. package/dist/platform/cli/web/commands/install.js.map +1 -0
  42. package/dist/platform/cli/web/commands/make/component.d.ts +16 -0
  43. package/dist/platform/cli/web/commands/make/component.d.ts.map +1 -0
  44. package/dist/platform/cli/web/commands/make/component.js +73 -0
  45. package/dist/platform/cli/web/commands/make/component.js.map +1 -0
  46. package/dist/platform/cli/web/commands/make/guard.d.ts +13 -0
  47. package/dist/platform/cli/web/commands/make/guard.d.ts.map +1 -0
  48. package/dist/platform/cli/web/commands/make/guard.js +46 -0
  49. package/dist/platform/cli/web/commands/make/guard.js.map +1 -0
  50. package/dist/platform/cli/web/commands/make/layout.d.ts +12 -0
  51. package/dist/platform/cli/web/commands/make/layout.d.ts.map +1 -0
  52. package/dist/platform/cli/web/commands/make/layout.js +44 -0
  53. package/dist/platform/cli/web/commands/make/layout.js.map +1 -0
  54. package/dist/platform/cli/web/commands/make/page.d.ts +17 -0
  55. package/dist/platform/cli/web/commands/make/page.d.ts.map +1 -0
  56. package/dist/platform/cli/web/commands/make/page.js +66 -0
  57. package/dist/platform/cli/web/commands/make/page.js.map +1 -0
  58. package/dist/platform/cli/web/commands/make/resource.d.ts +17 -0
  59. package/dist/platform/cli/web/commands/make/resource.d.ts.map +1 -0
  60. package/dist/platform/cli/web/commands/make/resource.js +37 -0
  61. package/dist/platform/cli/web/commands/make/resource.js.map +1 -0
  62. package/dist/platform/cli/web/commands/make/service.d.ts +16 -0
  63. package/dist/platform/cli/web/commands/make/service.d.ts.map +1 -0
  64. package/dist/platform/cli/web/commands/make/service.js +137 -0
  65. package/dist/platform/cli/web/commands/make/service.js.map +1 -0
  66. package/dist/platform/cli/web/commands/make/store.d.ts +16 -0
  67. package/dist/platform/cli/web/commands/make/store.d.ts.map +1 -0
  68. package/dist/platform/cli/web/commands/make/store.js +72 -0
  69. package/dist/platform/cli/web/commands/make/store.js.map +1 -0
  70. package/dist/platform/cli/web/commands/web-sync.d.ts +10 -0
  71. package/dist/platform/cli/web/commands/web-sync.d.ts.map +1 -0
  72. package/dist/platform/cli/web/commands/web-sync.js +307 -0
  73. package/dist/platform/cli/web/commands/web-sync.js.map +1 -0
  74. package/dist/platform/cli/web/index.d.ts +3 -0
  75. package/dist/platform/cli/web/index.d.ts.map +1 -0
  76. package/dist/platform/cli/web/index.js +40 -0
  77. package/dist/platform/cli/web/index.js.map +1 -0
  78. package/dist/platform/cli/web/utils/config.d.ts +52 -0
  79. package/dist/platform/cli/web/utils/config.d.ts.map +1 -0
  80. package/dist/platform/cli/web/utils/config.js +89 -0
  81. package/dist/platform/cli/web/utils/config.js.map +1 -0
  82. package/dist/platform/cli/web/utils/format.d.ts +2 -0
  83. package/dist/platform/cli/web/utils/format.d.ts.map +1 -0
  84. package/dist/platform/cli/web/utils/format.js +28 -0
  85. package/dist/platform/cli/web/utils/format.js.map +1 -0
  86. package/dist/platform/cli/web/utils/header.d.ts +3 -0
  87. package/dist/platform/cli/web/utils/header.d.ts.map +1 -0
  88. package/dist/platform/cli/web/utils/header.js +11 -0
  89. package/dist/platform/cli/web/utils/header.js.map +1 -0
  90. package/dist/platform/cli/web/utils/logger.d.ts +10 -0
  91. package/dist/platform/cli/web/utils/logger.d.ts.map +1 -0
  92. package/dist/platform/cli/web/utils/logger.js +47 -0
  93. package/dist/platform/cli/web/utils/logger.js.map +1 -0
  94. package/dist/platform/cli/web/utils/names.d.ts +7 -0
  95. package/dist/platform/cli/web/utils/names.d.ts.map +1 -0
  96. package/dist/platform/cli/web/utils/names.js +43 -0
  97. package/dist/platform/cli/web/utils/names.js.map +1 -0
  98. package/dist/platform/cli/web/utils/resolve-paths.d.ts +10 -0
  99. package/dist/platform/cli/web/utils/resolve-paths.d.ts.map +1 -0
  100. package/dist/platform/cli/web/utils/resolve-paths.js +87 -0
  101. package/dist/platform/cli/web/utils/resolve-paths.js.map +1 -0
  102. package/dist/platform/cli/web/utils/write-file.d.ts +7 -0
  103. package/dist/platform/cli/web/utils/write-file.d.ts.map +1 -0
  104. package/dist/platform/cli/web/utils/write-file.js +31 -0
  105. package/dist/platform/cli/web/utils/write-file.js.map +1 -0
  106. package/dist/web/cli/commands/create/feature.d.ts +11 -0
  107. package/dist/web/cli/commands/create/feature.d.ts.map +1 -0
  108. package/dist/web/cli/commands/create/feature.js +43 -0
  109. package/dist/web/cli/commands/create/feature.js.map +1 -0
  110. package/dist/web/cli/commands/create/module.d.ts +11 -0
  111. package/dist/web/cli/commands/create/module.d.ts.map +1 -0
  112. package/dist/web/cli/commands/create/module.js +44 -0
  113. package/dist/web/cli/commands/create/module.js.map +1 -0
  114. package/dist/web/cli/commands/create/mvvm.d.ts +11 -0
  115. package/dist/web/cli/commands/create/mvvm.d.ts.map +1 -0
  116. package/dist/web/cli/commands/create/mvvm.js +43 -0
  117. package/dist/web/cli/commands/create/mvvm.js.map +1 -0
  118. package/dist/web/cli/commands/init-fullstack.d.ts +12 -0
  119. package/dist/web/cli/commands/init-fullstack.d.ts.map +1 -0
  120. package/dist/web/cli/commands/init-fullstack.js +42 -0
  121. package/dist/web/cli/commands/init-fullstack.js.map +1 -0
  122. package/dist/web/cli/commands/init-web.d.ts +12 -0
  123. package/dist/web/cli/commands/init-web.d.ts.map +1 -0
  124. package/dist/web/cli/commands/init-web.js +70 -0
  125. package/dist/web/cli/commands/init-web.js.map +1 -0
  126. package/dist/web/cli/commands/install-api.d.ts +10 -0
  127. package/dist/web/cli/commands/install-api.d.ts.map +1 -0
  128. package/dist/web/cli/commands/install-api.js +91 -0
  129. package/dist/web/cli/commands/install-api.js.map +1 -0
  130. package/dist/web/cli/commands/install-web.d.ts +12 -0
  131. package/dist/web/cli/commands/install-web.d.ts.map +1 -0
  132. package/dist/web/cli/commands/install-web.js +101 -0
  133. package/dist/web/cli/commands/install-web.js.map +1 -0
  134. package/dist/web/cli/commands/make/component.d.ts +13 -0
  135. package/dist/web/cli/commands/make/component.d.ts.map +1 -0
  136. package/dist/web/cli/commands/make/component.js +40 -0
  137. package/dist/web/cli/commands/make/component.js.map +1 -0
  138. package/dist/web/cli/commands/make/guard.d.ts +11 -0
  139. package/dist/web/cli/commands/make/guard.d.ts.map +1 -0
  140. package/dist/web/cli/commands/make/guard.js +37 -0
  141. package/dist/web/cli/commands/make/guard.js.map +1 -0
  142. package/dist/web/cli/commands/make/layout.d.ts +11 -0
  143. package/dist/web/cli/commands/make/layout.d.ts.map +1 -0
  144. package/dist/web/cli/commands/make/layout.js +37 -0
  145. package/dist/web/cli/commands/make/layout.js.map +1 -0
  146. package/dist/web/cli/commands/make/page.d.ts +14 -0
  147. package/dist/web/cli/commands/make/page.d.ts.map +1 -0
  148. package/dist/web/cli/commands/make/page.js +42 -0
  149. package/dist/web/cli/commands/make/page.js.map +1 -0
  150. package/dist/web/cli/commands/make/resource.d.ts +13 -0
  151. package/dist/web/cli/commands/make/resource.d.ts.map +1 -0
  152. package/dist/web/cli/commands/make/resource.js +33 -0
  153. package/dist/web/cli/commands/make/resource.js.map +1 -0
  154. package/dist/web/cli/commands/make/service.d.ts +14 -0
  155. package/dist/web/cli/commands/make/service.d.ts.map +1 -0
  156. package/dist/web/cli/commands/make/service.js +56 -0
  157. package/dist/web/cli/commands/make/service.js.map +1 -0
  158. package/dist/web/cli/commands/make/store.d.ts +14 -0
  159. package/dist/web/cli/commands/make/store.d.ts.map +1 -0
  160. package/dist/web/cli/commands/make/store.js +47 -0
  161. package/dist/web/cli/commands/make/store.js.map +1 -0
  162. package/dist/web/cli/commands/web-sync.d.ts +10 -0
  163. package/dist/web/cli/commands/web-sync.d.ts.map +1 -0
  164. package/dist/web/cli/commands/web-sync.js +48 -0
  165. package/dist/web/cli/commands/web-sync.js.map +1 -0
  166. package/dist/web/cli/index.d.mts +5 -0
  167. package/dist/web/cli/index.d.ts +5 -0
  168. package/dist/web/cli/index.d.ts.map +1 -0
  169. package/dist/web/cli/index.js +36 -0
  170. package/dist/web/cli/index.js.map +1 -0
  171. package/dist/web/cli/index.mjs +984 -0
  172. package/dist/web/cli/utils/config.d.ts +52 -0
  173. package/dist/web/cli/utils/config.d.ts.map +1 -0
  174. package/dist/web/cli/utils/config.js +89 -0
  175. package/dist/web/cli/utils/config.js.map +1 -0
  176. package/dist/web/cli/utils/format.d.ts +2 -0
  177. package/dist/web/cli/utils/format.d.ts.map +1 -0
  178. package/dist/web/cli/utils/format.js +28 -0
  179. package/dist/web/cli/utils/format.js.map +1 -0
  180. package/dist/web/cli/utils/logger.d.ts +10 -0
  181. package/dist/web/cli/utils/logger.d.ts.map +1 -0
  182. package/dist/web/cli/utils/logger.js +34 -0
  183. package/dist/web/cli/utils/logger.js.map +1 -0
  184. package/dist/web/cli/utils/names.d.ts +7 -0
  185. package/dist/web/cli/utils/names.d.ts.map +1 -0
  186. package/dist/web/cli/utils/names.js +43 -0
  187. package/dist/web/cli/utils/names.js.map +1 -0
  188. package/dist/web/cli/utils/resolve-paths.d.ts +10 -0
  189. package/dist/web/cli/utils/resolve-paths.d.ts.map +1 -0
  190. package/dist/web/cli/utils/resolve-paths.js +87 -0
  191. package/dist/web/cli/utils/resolve-paths.js.map +1 -0
  192. package/dist/web/cli/utils/write-file.d.ts +7 -0
  193. package/dist/web/cli/utils/write-file.d.ts.map +1 -0
  194. package/dist/web/cli/utils/write-file.js +31 -0
  195. package/dist/web/cli/utils/write-file.js.map +1 -0
  196. package/dist/web/platform/cli/web/index.d.mts +5 -0
  197. package/dist/web/platform/cli/web/index.d.ts +5 -0
  198. package/dist/web/platform/cli/web/index.js +2035 -0
  199. package/dist/web/platform/cli/web/index.mjs +1998 -0
  200. package/dist/web/src/adapters/jotai.d.ts +2 -0
  201. package/dist/web/src/adapters/jotai.d.ts.map +1 -0
  202. package/dist/web/src/adapters/jotai.js +7 -0
  203. package/dist/web/src/adapters/jotai.js.map +1 -0
  204. package/dist/web/src/adapters/mobx.d.ts +2 -0
  205. package/dist/web/src/adapters/mobx.d.ts.map +1 -0
  206. package/dist/web/src/adapters/mobx.js +7 -0
  207. package/dist/web/src/adapters/mobx.js.map +1 -0
  208. package/dist/web/src/adapters/redux.d.ts +2 -0
  209. package/dist/web/src/adapters/redux.d.ts.map +1 -0
  210. package/dist/web/src/adapters/redux.js +7 -0
  211. package/dist/web/src/adapters/redux.js.map +1 -0
  212. package/dist/web/src/adapters/zustand.d.ts +2 -0
  213. package/dist/web/src/adapters/zustand.d.ts.map +1 -0
  214. package/dist/web/src/adapters/zustand.js +7 -0
  215. package/dist/web/src/adapters/zustand.js.map +1 -0
  216. package/dist/web/src/config/web-config.d.ts +34 -0
  217. package/dist/web/src/config/web-config.d.ts.map +1 -0
  218. package/dist/web/src/config/web-config.js +73 -0
  219. package/dist/web/src/config/web-config.js.map +1 -0
  220. package/dist/web/src/core/application.d.ts +8 -0
  221. package/dist/web/src/core/application.d.ts.map +1 -0
  222. package/dist/web/src/core/application.js +45 -0
  223. package/dist/web/src/core/application.js.map +1 -0
  224. package/dist/web/src/core/decorators/component.d.ts +20 -0
  225. package/dist/web/src/core/decorators/component.d.ts.map +1 -0
  226. package/dist/web/src/core/decorators/component.js +83 -0
  227. package/dist/web/src/core/decorators/component.js.map +1 -0
  228. package/dist/web/src/core/decorators/page.d.ts +27 -0
  229. package/dist/web/src/core/decorators/page.d.ts.map +1 -0
  230. package/dist/web/src/core/decorators/page.js +58 -0
  231. package/dist/web/src/core/decorators/page.js.map +1 -0
  232. package/dist/web/src/core/di/container.d.ts +8 -0
  233. package/dist/web/src/core/di/container.d.ts.map +1 -0
  234. package/dist/web/src/core/di/container.js +26 -0
  235. package/dist/web/src/core/di/container.js.map +1 -0
  236. package/dist/web/src/core/runtime.d.ts +3 -0
  237. package/dist/web/src/core/runtime.d.ts.map +1 -0
  238. package/dist/web/src/core/runtime.js +15 -0
  239. package/dist/web/src/core/runtime.js.map +1 -0
  240. package/dist/web/src/http/fragment-fetch.d.ts +55 -0
  241. package/dist/web/src/http/fragment-fetch.d.ts.map +1 -0
  242. package/dist/web/src/http/fragment-fetch.js +290 -0
  243. package/dist/web/src/http/fragment-fetch.js.map +1 -0
  244. package/dist/web/src/index.d.mts +137 -0
  245. package/dist/web/src/index.d.ts +7 -0
  246. package/dist/web/src/index.d.ts.map +1 -0
  247. package/dist/web/src/index.js +31 -0
  248. package/dist/web/src/index.js.map +1 -0
  249. package/dist/web/src/index.mjs +643 -0
  250. package/dist/web/src/router/fragment-router.d.ts +14 -0
  251. package/dist/web/src/router/fragment-router.d.ts.map +1 -0
  252. package/dist/web/src/router/fragment-router.js +94 -0
  253. package/dist/web/src/router/fragment-router.js.map +1 -0
  254. package/dist/web/web/src/index.d.mts +137 -0
  255. package/dist/web/web/src/index.d.ts +137 -0
  256. package/dist/web/web/src/index.js +702 -0
  257. package/dist/web/web/src/index.mjs +644 -0
  258. package/package.json +15 -3
@@ -0,0 +1,984 @@
1
+ // src/web/cli/commands/install-web.ts
2
+ import fs3 from "fs";
3
+ import path3 from "path";
4
+ import { spawnSync } from "child_process";
5
+
6
+ // src/web/cli/utils/config.ts
7
+ import fs from "fs";
8
+ import path from "path";
9
+ var WEB_CONFIG_FILE = "fragment.web.json";
10
+ var API_CONFIG_FILE = "fragment.json";
11
+ function resolveCwd(cwd) {
12
+ return cwd ? path.resolve(cwd) : process.cwd();
13
+ }
14
+ function webConfigPath(cwd) {
15
+ return path.join(resolveCwd(cwd), WEB_CONFIG_FILE);
16
+ }
17
+ function apiConfigPath(cwd) {
18
+ return path.join(resolveCwd(cwd), API_CONFIG_FILE);
19
+ }
20
+ function createDefaultWebConfig(partial) {
21
+ return {
22
+ webRoot: "/",
23
+ serverRoot: partial?.serverRoot ?? "/api",
24
+ publicRoutes: ["/", "/login", "/register"],
25
+ authenticatedRoutes: ["/dashboard", "/profile", "/settings"],
26
+ app: {
27
+ name: partial?.app?.name ?? "My App",
28
+ rootElement: "#root",
29
+ strict: true,
30
+ router: "react-router",
31
+ stateAdapter: partial?.app?.stateAdapter ?? "zustand"
32
+ },
33
+ fetch: {
34
+ baseUrl: partial?.fetch?.baseUrl ?? "${VITE_API_URL:http://localhost:3000}",
35
+ timeout: 1e4,
36
+ retries: 2,
37
+ retryDelay: 300,
38
+ tokenTransport: "cookie",
39
+ tokenKey: "fragment_token",
40
+ onUnauthorized: "redirect:/login",
41
+ onForbidden: "redirect:/403"
42
+ },
43
+ structure: partial?.structure ?? "layered",
44
+ di: {
45
+ autoScan: true,
46
+ scanPaths: ["src/components", "src/pages", "src/services"]
47
+ },
48
+ theme: {
49
+ mode: "system",
50
+ primaryColor: "#6366f1",
51
+ fontFamily: "Geist, system-ui"
52
+ },
53
+ devtools: {
54
+ enabled: true
55
+ }
56
+ };
57
+ }
58
+ function readJsonFile(filePath) {
59
+ if (!fs.existsSync(filePath)) {
60
+ return null;
61
+ }
62
+ const raw = fs.readFileSync(filePath, "utf8");
63
+ return JSON.parse(raw);
64
+ }
65
+ function writeJsonFile(filePath, payload) {
66
+ fs.writeFileSync(filePath, `${JSON.stringify(payload, null, 2)}
67
+ `, "utf8");
68
+ }
69
+ function readWebConfig(cwd) {
70
+ return readJsonFile(webConfigPath(cwd));
71
+ }
72
+ function readApiConfig(cwd) {
73
+ return readJsonFile(apiConfigPath(cwd));
74
+ }
75
+ function ensureWebConfig(cwd) {
76
+ const config = readWebConfig(cwd);
77
+ if (!config) {
78
+ throw new Error("fragment.web.json was not found in the current project");
79
+ }
80
+ return config;
81
+ }
82
+
83
+ // src/web/cli/utils/logger.ts
84
+ import pc from "picocolors";
85
+ function line(symbol, label, message) {
86
+ return ` ${symbol} ${label.padEnd(8, " ")} ${message}`;
87
+ }
88
+ var logger = {
89
+ section(title, subtitle) {
90
+ process.stdout.write(`
91
+ ${pc.bold(title)}${subtitle ? ` ${subtitle}` : ""}
92
+
93
+ `);
94
+ },
95
+ created(path18) {
96
+ process.stdout.write(`${line(pc.green("\u2714"), "created", path18)}
97
+ `);
98
+ },
99
+ updated(path18, detail) {
100
+ process.stdout.write(
101
+ `${line(pc.cyan("\u2714"), "updated", `${path18}${detail ? ` (${detail})` : ""}`)}
102
+ `
103
+ );
104
+ },
105
+ exists(path18, detail = "use --force to overwrite") {
106
+ process.stdout.write(`${line(pc.yellow("\u26A0"), "exists", `${path18} (${detail})`)}
107
+ `);
108
+ },
109
+ dryRun(path18) {
110
+ process.stdout.write(`${line(pc.blue("\u2139"), "dry-run", path18)}
111
+ `);
112
+ },
113
+ error(message) {
114
+ process.stderr.write(`${line(pc.red("\u2716"), "error", message)}
115
+ `);
116
+ },
117
+ done(message) {
118
+ process.stdout.write(`
119
+ ${pc.bold("Done.")} ${message}
120
+ `);
121
+ }
122
+ };
123
+
124
+ // src/web/cli/utils/write-file.ts
125
+ import fs2 from "fs";
126
+ import path2 from "path";
127
+ function writeFileIfAllowed(filePath, content, options = {}) {
128
+ const exists = fs2.existsSync(filePath);
129
+ if (options.dryRun) {
130
+ logger.dryRun(filePath);
131
+ return "dry-run";
132
+ }
133
+ if (exists && !options.force) {
134
+ logger.exists(filePath);
135
+ return "exists";
136
+ }
137
+ fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
138
+ fs2.writeFileSync(filePath, content, "utf8");
139
+ const mode = options.mode ?? (exists ? "updated" : "created");
140
+ if (mode === "updated") {
141
+ logger.updated(filePath);
142
+ } else {
143
+ logger.created(filePath);
144
+ }
145
+ return mode;
146
+ }
147
+
148
+ // src/web/cli/commands/install-web.ts
149
+ function detectPackageManager(cwd) {
150
+ if (fs3.existsSync(path3.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
151
+ if (fs3.existsSync(path3.join(cwd, "yarn.lock"))) return "yarn";
152
+ return "npm";
153
+ }
154
+ function installDeps(cwd, manager, dryRun) {
155
+ if (dryRun) return;
156
+ const args = manager === "yarn" ? ["add", "fragment-web", "react", "react-dom", "react-router-dom", "reflect-metadata", "vite"] : manager === "pnpm" ? ["add", "fragment-web", "react", "react-dom", "react-router-dom", "reflect-metadata", "vite"] : ["install", "fragment-web", "react", "react-dom", "react-router-dom", "reflect-metadata", "vite"];
157
+ const result = spawnSync(manager, args, { cwd, stdio: "ignore" });
158
+ if (result.status !== 0) {
159
+ throw new Error(`Dependency install failed using ${manager}`);
160
+ }
161
+ }
162
+ function updatePackageScripts(cwd, dryRun) {
163
+ const pkgPath = path3.join(cwd, "package.json");
164
+ if (!fs3.existsSync(pkgPath)) return;
165
+ const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf8"));
166
+ pkg.scripts = pkg.scripts || {};
167
+ pkg.scripts["dev:web"] = "vite";
168
+ pkg.scripts["build:web"] = "vite build";
169
+ if (dryRun) {
170
+ logger.dryRun("package.json");
171
+ return;
172
+ }
173
+ fs3.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
174
+ `, "utf8");
175
+ logger.updated("package.json", "scripts: dev:web, build:web");
176
+ }
177
+ async function runInstallWeb(options = {}) {
178
+ const cwd = process.cwd();
179
+ logger.section("fragment-web", "install:web");
180
+ const apiConfig = readApiConfig(cwd);
181
+ const serverRoot = typeof apiConfig?.serverRoot === "string" ? apiConfig.serverRoot : "/api";
182
+ const webConfig = createDefaultWebConfig({
183
+ serverRoot,
184
+ structure: options.structure,
185
+ app: {
186
+ name: "My App",
187
+ rootElement: "#root",
188
+ strict: true,
189
+ router: "react-router",
190
+ stateAdapter: options.adapter || "zustand"
191
+ }
192
+ });
193
+ const configPath = webConfigPath(cwd);
194
+ if (options.dryRun) {
195
+ logger.dryRun(configPath);
196
+ } else if (fs3.existsSync(configPath) && !options.force) {
197
+ logger.exists(configPath);
198
+ } else {
199
+ writeJsonFile(configPath, webConfig);
200
+ logger.created("fragment.web.json");
201
+ }
202
+ writeFileIfAllowed(
203
+ path3.join(cwd, "index.html"),
204
+ '<!doctype html>\n<html><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0"/><title>Fragment Web</title></head><body><div id="root"></div><script type="module" src="/src/web/main.tsx"></script></body></html>\n',
205
+ options
206
+ );
207
+ writeFileIfAllowed(
208
+ path3.join(cwd, "vite.config.ts"),
209
+ "import { defineConfig } from 'vite';\nimport webConfig from './fragment.web.json';\n\nexport default defineConfig({\n server: {\n proxy: {\n [webConfig.serverRoot]: {\n target: webConfig.fetch.baseUrl,\n changeOrigin: true,\n },\n },\n },\n});\n",
210
+ options
211
+ );
212
+ writeFileIfAllowed(
213
+ path3.join(cwd, "src/web/main.tsx"),
214
+ "import 'reflect-metadata';\nimport { FragmentWebApplication, FragmentWebUIApplication } from 'fragment-web';\n\n@FragmentWebApplication({ rootElement: '#root', strict: true })\nclass Application {}\n\nasync function bootstrap() {\n const app = new FragmentWebUIApplication();\n await app.bootstrap(Application);\n}\n\nbootstrap();\n",
215
+ options
216
+ );
217
+ writeFileIfAllowed(
218
+ path3.join(cwd, "src/web/app.component.tsx"),
219
+ "import React from 'react';\n\nexport function AppComponent(): JSX.Element {\n return <div>Fragment Web App</div>;\n}\n",
220
+ options
221
+ );
222
+ writeFileIfAllowed(
223
+ path3.join(cwd, "src/web/pages/home.page.tsx"),
224
+ "import React from 'react';\nimport { Page } from 'fragment-web';\n\n@Page({ path: '/', title: 'Home' })\nexport class HomePage {\n render(): JSX.Element {\n return <div>Home</div>;\n }\n}\n",
225
+ options
226
+ );
227
+ writeFileIfAllowed(
228
+ path3.join(cwd, "src/web/pages/login.page.tsx"),
229
+ "import React from 'react';\nimport { Page, RedirectIfAuthenticated } from 'fragment-web';\n\n@Page({ path: '/login', title: 'Login' })\n@RedirectIfAuthenticated('/')\nexport class LoginPage {\n render(): JSX.Element {\n return <div>Login</div>;\n }\n}\n",
230
+ options
231
+ );
232
+ updatePackageScripts(cwd, options.dryRun);
233
+ installDeps(cwd, detectPackageManager(cwd), options.dryRun);
234
+ logger.done("Run: npm run dev:web");
235
+ }
236
+ function registerInstallWeb(program) {
237
+ program.command("install:web").description("Install fragment-web into an existing fragment-ts project").option("--structure <mode>", "Folder structure mode").option("--adapter <adapter>", "State adapter").option("--force", "Overwrite existing files").option("--dry-run", "Preview changes").action((opts) => {
238
+ runInstallWeb(opts).catch((error) => {
239
+ logger.error(error instanceof Error ? error.message : "install:web failed");
240
+ process.exitCode = 1;
241
+ });
242
+ });
243
+ }
244
+
245
+ // src/web/cli/commands/install-api.ts
246
+ import fs4 from "fs";
247
+ import path4 from "path";
248
+ import { spawnSync as spawnSync2 } from "child_process";
249
+ function detectPackageManager2(cwd) {
250
+ if (fs4.existsSync(path4.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
251
+ if (fs4.existsSync(path4.join(cwd, "yarn.lock"))) return "yarn";
252
+ return "npm";
253
+ }
254
+ function installDeps2(cwd, manager, dryRun) {
255
+ if (dryRun) return;
256
+ const args = manager === "yarn" ? ["add", "fragment-ts", "reflect-metadata"] : manager === "pnpm" ? ["add", "fragment-ts", "reflect-metadata"] : ["install", "fragment-ts", "reflect-metadata"];
257
+ const result = spawnSync2(manager, args, { cwd, stdio: "ignore" });
258
+ if (result.status !== 0) {
259
+ throw new Error(`Dependency install failed using ${manager}`);
260
+ }
261
+ }
262
+ function updatePackageScripts2(cwd, dryRun) {
263
+ const pkgPath = path4.join(cwd, "package.json");
264
+ if (!fs4.existsSync(pkgPath)) return;
265
+ const pkg = JSON.parse(fs4.readFileSync(pkgPath, "utf8"));
266
+ pkg.scripts = pkg.scripts || {};
267
+ pkg.scripts["dev:api"] = "frg serve";
268
+ pkg.scripts["build:api"] = "frg build";
269
+ if (dryRun) {
270
+ logger.dryRun("package.json");
271
+ return;
272
+ }
273
+ fs4.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
274
+ `, "utf8");
275
+ logger.updated("package.json", "scripts: dev:api, build:api");
276
+ }
277
+ async function runInstallApi(options = {}) {
278
+ const cwd = process.cwd();
279
+ logger.section("fragment-web", "install:api");
280
+ const webConfig = ensureWebConfig(cwd);
281
+ const fragmentConfig = {
282
+ serverRoot: webConfig.serverRoot,
283
+ database: {
284
+ driver: options.db ?? "sqlite"
285
+ }
286
+ };
287
+ const fragmentPath = path4.join(cwd, "fragment.json");
288
+ if (options.dryRun) {
289
+ logger.dryRun(fragmentPath);
290
+ } else if (fs4.existsSync(fragmentPath) && !options.force) {
291
+ logger.exists(fragmentPath);
292
+ } else {
293
+ writeJsonFile(fragmentPath, fragmentConfig);
294
+ logger.created("fragment.json");
295
+ }
296
+ writeFileIfAllowed(
297
+ path4.join(cwd, "src/api/main.ts"),
298
+ "import 'reflect-metadata';\nimport { FragmentApplication } from 'fragment-ts';\n\n@FragmentApplication()\nclass App {}\n\nexport default App;\n",
299
+ options
300
+ );
301
+ writeFileIfAllowed(
302
+ path4.join(cwd, "src/api/modules/app/controllers/app.controller.ts"),
303
+ "import { Controller, Get } from 'fragment-ts';\nimport { AppService } from '../services/app.service';\n\n@Controller('/app')\nexport class AppController {\n constructor(private readonly appService: AppService) {}\n\n @Get('/')\n getHello(): string {\n return this.appService.getHello();\n }\n}\n",
304
+ options
305
+ );
306
+ writeFileIfAllowed(
307
+ path4.join(cwd, "src/api/modules/app/services/app.service.ts"),
308
+ "import { Service } from 'fragment-ts';\n\n@Service()\nexport class AppService {\n getHello(): string {\n return 'Hello from fragment-ts';\n }\n}\n",
309
+ options
310
+ );
311
+ updatePackageScripts2(cwd, options.dryRun);
312
+ installDeps2(cwd, detectPackageManager2(cwd), options.dryRun);
313
+ logger.done("Run: npm run dev:api");
314
+ }
315
+ function registerInstallApi(program) {
316
+ program.command("install:api").description("Install fragment-ts into an existing fragment-web project").option("--db <db>", "Database adapter").option("--force", "Overwrite existing files").option("--dry-run", "Preview changes").action((opts) => {
317
+ runInstallApi(opts).catch((error) => {
318
+ logger.error(error instanceof Error ? error.message : "install:api failed");
319
+ process.exitCode = 1;
320
+ });
321
+ });
322
+ }
323
+
324
+ // src/web/cli/commands/init-web.ts
325
+ import fs5 from "fs";
326
+ import path5 from "path";
327
+ async function runInitWeb(name, options = {}) {
328
+ const root = path5.resolve(process.cwd(), name);
329
+ logger.section("fragment-web", `init:web ${name}`);
330
+ if (!options.dryRun) {
331
+ fs5.mkdirSync(root, { recursive: true });
332
+ }
333
+ const config = createDefaultWebConfig({
334
+ app: {
335
+ name,
336
+ rootElement: "#root",
337
+ strict: true,
338
+ router: "react-router",
339
+ stateAdapter: options.adapter || "zustand"
340
+ },
341
+ structure: options.structure
342
+ });
343
+ writeFileIfAllowed(path5.join(root, "fragment.web.json"), `${JSON.stringify(config, null, 2)}
344
+ `, options);
345
+ writeFileIfAllowed(
346
+ path5.join(root, "package.json"),
347
+ `${JSON.stringify(
348
+ {
349
+ name,
350
+ version: "0.1.0",
351
+ private: true,
352
+ scripts: {
353
+ "dev:web": "vite",
354
+ "build:web": "vite build"
355
+ }
356
+ },
357
+ null,
358
+ 2
359
+ )}
360
+ `,
361
+ options
362
+ );
363
+ writeFileIfAllowed(
364
+ path5.join(root, "tsconfig.json"),
365
+ JSON.stringify(
366
+ {
367
+ compilerOptions: {
368
+ target: "ES2020",
369
+ module: "ESNext",
370
+ moduleResolution: "bundler",
371
+ jsx: "react-jsx",
372
+ strict: true,
373
+ experimentalDecorators: true,
374
+ emitDecoratorMetadata: true
375
+ },
376
+ include: ["src/**/*"]
377
+ },
378
+ null,
379
+ 2
380
+ ) + "\n",
381
+ options
382
+ );
383
+ writeFileIfAllowed(
384
+ path5.join(root, "vite.config.ts"),
385
+ "import { defineConfig } from 'vite';\nimport webConfig from './fragment.web.json';\n\nexport default defineConfig({\n server: {\n proxy: {\n [webConfig.serverRoot]: {\n target: webConfig.fetch.baseUrl,\n changeOrigin: true,\n },\n },\n },\n});\n",
386
+ options
387
+ );
388
+ writeFileIfAllowed(
389
+ path5.join(root, "src/web/main.tsx"),
390
+ "import 'reflect-metadata';\nimport { FragmentWebApplication, FragmentWebUIApplication } from 'fragment-web';\n\n@FragmentWebApplication({ rootElement: '#root', strict: true })\nclass Application {}\n\nnew FragmentWebUIApplication().bootstrap(Application);\n",
391
+ options
392
+ );
393
+ logger.done(`Created ${name}`);
394
+ }
395
+ function registerInitWeb(program) {
396
+ program.command("init:web <name>").description("Create a new fragment-web project").option("--structure <mode>", "Folder structure mode").option("--adapter <adapter>", "State adapter").option("--force", "Overwrite existing files").option("--dry-run", "Preview changes").action((name, opts) => {
397
+ runInitWeb(name, opts).catch((error) => {
398
+ logger.error(error instanceof Error ? error.message : "init:web failed");
399
+ process.exitCode = 1;
400
+ });
401
+ });
402
+ }
403
+
404
+ // src/web/cli/commands/init-fullstack.ts
405
+ import path6 from "path";
406
+ async function runInitFullstack(name, options = {}) {
407
+ await runInitWeb(name, options);
408
+ const root = path6.resolve(process.cwd(), name);
409
+ writeFileIfAllowed(
410
+ path6.join(root, "fragment.json"),
411
+ `${JSON.stringify(
412
+ {
413
+ serverRoot: "/api",
414
+ database: {
415
+ driver: options.db ?? "sqlite"
416
+ }
417
+ },
418
+ null,
419
+ 2
420
+ )}
421
+ `,
422
+ options
423
+ );
424
+ writeFileIfAllowed(
425
+ path6.join(root, "src/api/main.ts"),
426
+ "import 'reflect-metadata';\nimport { FragmentApplication } from 'fragment-ts';\n\n@FragmentApplication()\nclass App {}\n\nexport default App;\n",
427
+ options
428
+ );
429
+ writeFileIfAllowed(
430
+ path6.join(root, "src/api/modules/app/controllers/app.controller.ts"),
431
+ "import { Controller, Get } from 'fragment-ts';\n\n@Controller('/app')\nexport class AppController {\n @Get('/')\n hello(): string {\n return 'Hello';\n }\n}\n",
432
+ options
433
+ );
434
+ writeFileIfAllowed(
435
+ path6.join(root, "src/api/modules/app/services/app.service.ts"),
436
+ "import { Service } from 'fragment-ts';\n\n@Service()\nexport class AppService {}\n",
437
+ options
438
+ );
439
+ logger.done(`Created fullstack project ${name}`);
440
+ }
441
+ function registerInitFullstack(program) {
442
+ program.command("init:fullstack <name>").description("Create a new fullstack fragment project").option("--structure <mode>", "Folder structure mode").option("--adapter <adapter>", "State adapter").option("--db <db>", "Database adapter").option("--force", "Overwrite existing files").option("--dry-run", "Preview changes").action((name, opts) => {
443
+ runInitFullstack(name, opts).catch((error) => {
444
+ logger.error(error instanceof Error ? error.message : "init:fullstack failed");
445
+ process.exitCode = 1;
446
+ });
447
+ });
448
+ }
449
+
450
+ // src/web/cli/commands/web-sync.ts
451
+ import path7 from "path";
452
+ async function syncOnce(options) {
453
+ const config = ensureWebConfig();
454
+ const base = options.url || config.fetch.baseUrl;
455
+ const source = `${base}${config.serverRoot}/docs/api-json`;
456
+ const generatedDir = path7.join(process.cwd(), "src/generated");
457
+ const content = `// \u26A0\uFE0F AUTO-GENERATED by frg web:sync \u2014 do not edit manually
458
+ // Source: ${source}
459
+ // Synced at: ${(/* @__PURE__ */ new Date()).toISOString()}
460
+ export const API_BASE = '${config.serverRoot}';
461
+ export const API_DOCS_URL = '${source}';
462
+ `;
463
+ writeFileIfAllowed(path7.join(generatedDir, "routes.ts"), content, {
464
+ dryRun: options.dryRun,
465
+ force: true
466
+ });
467
+ }
468
+ async function runWebSync(options = {}) {
469
+ await syncOnce(options);
470
+ if (options.watch) {
471
+ logger.section("fragment-web", "web:sync --watch");
472
+ setInterval(() => {
473
+ syncOnce(options).catch((error) => {
474
+ logger.error(error instanceof Error ? error.message : "web:sync watch failed");
475
+ });
476
+ }, 5e3);
477
+ }
478
+ }
479
+ function registerWebSync(program) {
480
+ program.command("web:sync").description("Generate typed web API artifacts into src/generated").option("--url <url>", "Backend base URL").option("--watch", "Watch and resync").option("--dry-run", "Preview changes").action((opts) => {
481
+ runWebSync(opts).catch((error) => {
482
+ logger.error(error instanceof Error ? error.message : "web:sync failed");
483
+ process.exitCode = 1;
484
+ });
485
+ });
486
+ }
487
+
488
+ // src/web/cli/commands/make/component.ts
489
+ import path9 from "path";
490
+
491
+ // src/web/cli/utils/names.ts
492
+ function normalizeName(input) {
493
+ return input.trim().replace(/([a-z\d])([A-Z])/g, "$1-$2").replace(/[^a-zA-Z0-9]+/g, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "").toLowerCase();
494
+ }
495
+ function toKebabCase(input) {
496
+ const normalized = normalizeName(input);
497
+ return normalized || "app";
498
+ }
499
+ function toPascalCase(input) {
500
+ return toKebabCase(input).split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
501
+ }
502
+
503
+ // src/web/cli/utils/resolve-paths.ts
504
+ import path8 from "path";
505
+ function domainOrName(input) {
506
+ return toKebabCase(input.domain || input.name);
507
+ }
508
+ function resolveOutputDir(input) {
509
+ const dom = domainOrName(input);
510
+ const leaf = toKebabCase(input.name);
511
+ switch (input.mode) {
512
+ case "layered":
513
+ switch (input.kind) {
514
+ case "component":
515
+ return path8.join("src", "components", leaf);
516
+ case "page":
517
+ return path8.join("src", "pages", leaf);
518
+ case "service":
519
+ return path8.join("src", "services");
520
+ case "store":
521
+ return path8.join("src", "stores");
522
+ case "guard":
523
+ return path8.join("src", "guards");
524
+ case "layout":
525
+ return path8.join("src", "layouts");
526
+ default:
527
+ throw new Error(`${input.kind} is not available in layered mode`);
528
+ }
529
+ case "module":
530
+ switch (input.kind) {
531
+ case "component":
532
+ return path8.join("src", "modules", dom, "components");
533
+ case "page":
534
+ case "service":
535
+ case "store":
536
+ return path8.join("src", "modules", dom);
537
+ case "guard":
538
+ return path8.join("src", "shared", "guards");
539
+ case "layout":
540
+ return path8.join("src", "shared", "layouts");
541
+ default:
542
+ throw new Error(`${input.kind} is not available in module mode`);
543
+ }
544
+ case "feature":
545
+ switch (input.kind) {
546
+ case "component":
547
+ return path8.join("src", "features", dom, "components");
548
+ case "page":
549
+ return path8.join("src", "features", dom, "pages");
550
+ case "service":
551
+ return path8.join("src", "features", dom, "services");
552
+ case "store":
553
+ return path8.join("src", "features", dom, "stores");
554
+ case "guard":
555
+ return path8.join("src", "shared", "guards");
556
+ case "layout":
557
+ return path8.join("src", "shared", "layouts");
558
+ default:
559
+ throw new Error(`${input.kind} is not available in feature mode`);
560
+ }
561
+ case "mvvm":
562
+ switch (input.kind) {
563
+ case "component":
564
+ case "page":
565
+ return path8.join("src", "app", dom, "view");
566
+ case "service":
567
+ case "store":
568
+ case "viewmodel":
569
+ return path8.join("src", "app", dom, "viewmodel");
570
+ case "model":
571
+ return path8.join("src", "app", dom, "model");
572
+ case "guard":
573
+ return path8.join("src", "shared", "guards");
574
+ case "layout":
575
+ return path8.join("src", "shared", "layouts");
576
+ default:
577
+ throw new Error(`${input.kind} is not available in mvvm mode`);
578
+ }
579
+ default:
580
+ return path8.join("src");
581
+ }
582
+ }
583
+
584
+ // src/web/cli/commands/make/component.ts
585
+ async function runMakeComponent(name, options = {}) {
586
+ const config = ensureWebConfig();
587
+ const mode = options.structure || config.structure;
588
+ const domain = options.feature || options.module;
589
+ const outDir = resolveOutputDir({ mode, kind: "component", name, domain });
590
+ const fileName = `${toKebabCase(name)}.component.tsx`;
591
+ const className = `${toPascalCase(name)}Component`;
592
+ const content = `// Generated by: frg make:component ${name}
593
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
594
+ // fragment-web v0.1.0
595
+ import React from 'react';
596
+
597
+ export function ${className}(): JSX.Element {
598
+ return <div>${toPascalCase(name)} component</div>;
599
+ }
600
+ `;
601
+ writeFileIfAllowed(path9.join(process.cwd(), outDir, fileName), content, options);
602
+ }
603
+ function registerMakeComponent(program) {
604
+ program.command("make:component <name>").description("Generate a frontend component").option("--structure <mode>", "Override structure mode").option("--module <name>", "Module name").option("--feature <name>", "Feature name").option("--force", "Overwrite existing files").option("--dry-run", "Preview changes").action((name, opts) => {
605
+ runMakeComponent(name, opts).catch((error) => {
606
+ logger.error(error instanceof Error ? error.message : "make:component failed");
607
+ process.exitCode = 1;
608
+ });
609
+ });
610
+ }
611
+
612
+ // src/web/cli/commands/make/page.ts
613
+ import path10 from "path";
614
+ async function runMakePage(name, options = {}) {
615
+ const config = ensureWebConfig();
616
+ const mode = options.structure || config.structure;
617
+ const domain = options.feature || options.module;
618
+ const outDir = resolveOutputDir({ mode, kind: "page", name, domain });
619
+ const fileName = `${toKebabCase(name)}.page.tsx`;
620
+ const className = `${toPascalCase(name)}Page`;
621
+ const route = options.path || `/${toKebabCase(name)}`;
622
+ const content = `// Generated by: frg make:page ${name}
623
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
624
+ // fragment-web v0.1.0
625
+ import React from 'react';
626
+ import { Page } from 'fragment-web';
627
+
628
+ @Page({ path: '${route}', title: '${toPascalCase(name)}' })
629
+ export class ${className} {
630
+ render(): JSX.Element {
631
+ return <div>${toPascalCase(name)} page</div>;
632
+ }
633
+ }
634
+ `;
635
+ writeFileIfAllowed(path10.join(process.cwd(), outDir, fileName), content, options);
636
+ }
637
+ function registerMakePage(program) {
638
+ program.command("make:page <name>").description("Generate a frontend page").option("--path <path>", "Route path").option("--structure <mode>", "Override structure mode").option("--module <name>", "Module name").option("--feature <name>", "Feature name").option("--force", "Overwrite existing files").option("--dry-run", "Preview changes").action((name, opts) => {
639
+ runMakePage(name, opts).catch((error) => {
640
+ logger.error(error instanceof Error ? error.message : "make:page failed");
641
+ process.exitCode = 1;
642
+ });
643
+ });
644
+ }
645
+
646
+ // src/web/cli/commands/make/service.ts
647
+ import fs6 from "fs";
648
+ import path11 from "path";
649
+ async function runMakeService(name, options = {}) {
650
+ const hasApi = fs6.existsSync(path11.join(process.cwd(), "fragment.json"));
651
+ const hasWeb = fs6.existsSync(path11.join(process.cwd(), "fragment.web.json"));
652
+ if (hasApi && hasWeb) {
653
+ logger.error("Both fragment.json and fragment.web.json found. Use explicit backend generator for API services.");
654
+ process.exitCode = 1;
655
+ return;
656
+ }
657
+ const config = ensureWebConfig();
658
+ const mode = options.structure || config.structure;
659
+ const domain = options.feature || options.module;
660
+ const outDir = resolveOutputDir({ mode, kind: "service", name, domain });
661
+ const fileName = `${toKebabCase(name)}.service.ts`;
662
+ const className = `${toPascalCase(name)}Service`;
663
+ const methods = (options.methods || "findAll,findById,create,update,delete").split(",").map((v) => v.trim()).filter(Boolean);
664
+ const body = methods.map(
665
+ (m) => ` async ${m}(): Promise<unknown> {
666
+ return FragmentFetch.get('/${toKebabCase(name)}');
667
+ }`
668
+ ).join("\n\n");
669
+ const content = `// Generated by: frg make:service ${name}
670
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
671
+ // fragment-web v0.1.0
672
+ import { FragmentFetch, FragmentService } from 'fragment-web';
673
+
674
+ @FragmentService()
675
+ export class ${className} {
676
+ ${body}
677
+ }
678
+ `;
679
+ writeFileIfAllowed(path11.join(process.cwd(), outDir, fileName), content, options);
680
+ }
681
+ function registerMakeService(program) {
682
+ program.command("make:service <name>").description("Generate a frontend service").option("--methods <list>", "CSV of service methods").option("--structure <mode>", "Override structure mode").option("--module <name>", "Module name").option("--feature <name>", "Feature name").option("--force", "Overwrite existing files").option("--dry-run", "Preview changes").action((name, opts) => {
683
+ runMakeService(name, opts).catch((error) => {
684
+ logger.error(error instanceof Error ? error.message : "make:service failed");
685
+ process.exitCode = 1;
686
+ });
687
+ });
688
+ }
689
+
690
+ // src/web/cli/commands/make/guard.ts
691
+ import path12 from "path";
692
+ async function runMakeGuard(name, options = {}) {
693
+ const config = ensureWebConfig();
694
+ const mode = options.structure || config.structure;
695
+ const outDir = resolveOutputDir({ mode, kind: "guard", name });
696
+ const fileName = `${toKebabCase(name)}.guard.ts`;
697
+ const className = `${toPascalCase(name)}Guard`;
698
+ const content = `// Generated by: frg make:guard ${name}
699
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
700
+ // fragment-web v0.1.0
701
+ export class ${className} {
702
+ canActivate(): boolean {
703
+ return true;
704
+ }
705
+ }
706
+ `;
707
+ writeFileIfAllowed(path12.join(process.cwd(), outDir, fileName), content, options);
708
+ }
709
+ function registerMakeGuard(program) {
710
+ program.command("make:guard <name>").description("Generate a frontend guard").option("--structure <mode>", "Override structure mode").option("--force", "Overwrite existing files").option("--dry-run", "Preview changes").action((name, opts) => {
711
+ runMakeGuard(name, opts).catch((error) => {
712
+ logger.error(error instanceof Error ? error.message : "make:guard failed");
713
+ process.exitCode = 1;
714
+ });
715
+ });
716
+ }
717
+
718
+ // src/web/cli/commands/make/layout.ts
719
+ import path13 from "path";
720
+ async function runMakeLayout(name, options = {}) {
721
+ const config = ensureWebConfig();
722
+ const mode = options.structure || config.structure;
723
+ const outDir = resolveOutputDir({ mode, kind: "layout", name });
724
+ const fileName = `${toKebabCase(name)}.layout.tsx`;
725
+ const className = `${toPascalCase(name)}Layout`;
726
+ const content = `// Generated by: frg make:layout ${name}
727
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
728
+ // fragment-web v0.1.0
729
+ import React from 'react';
730
+
731
+ export function ${className}({ children }: { children?: React.ReactNode }): JSX.Element {
732
+ return <section>{children}</section>;
733
+ }
734
+ `;
735
+ writeFileIfAllowed(path13.join(process.cwd(), outDir, fileName), content, options);
736
+ }
737
+ function registerMakeLayout(program) {
738
+ program.command("make:layout <name>").description("Generate a frontend layout").option("--structure <mode>", "Override structure mode").option("--force", "Overwrite existing files").option("--dry-run", "Preview changes").action((name, opts) => {
739
+ runMakeLayout(name, opts).catch((error) => {
740
+ logger.error(error instanceof Error ? error.message : "make:layout failed");
741
+ process.exitCode = 1;
742
+ });
743
+ });
744
+ }
745
+
746
+ // src/web/cli/commands/make/store.ts
747
+ import path14 from "path";
748
+ async function runMakeStore(name, options = {}) {
749
+ const config = ensureWebConfig();
750
+ const mode = options.structure || config.structure;
751
+ const adapter = options.adapter || config.app.stateAdapter;
752
+ const domain = options.feature || options.module;
753
+ const outDir = resolveOutputDir({ mode, kind: "store", name, domain });
754
+ const fileName = `${toKebabCase(name)}.store.ts`;
755
+ const className = toPascalCase(name);
756
+ const contentByAdapter = {
757
+ zustand: `// Generated by: frg make:store ${name}
758
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
759
+ // fragment-web v0.1.0
760
+ import { create } from 'zustand';
761
+
762
+ interface ${className}State {
763
+ loading: boolean;
764
+ setLoading: (value: boolean) => void;
765
+ }
766
+
767
+ export const use${className}Store = create<${className}State>((set) => ({
768
+ loading: false,
769
+ setLoading: (value) => set({ loading: value }),
770
+ }));
771
+ `,
772
+ jotai: `// Generated by: frg make:store ${name}
773
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
774
+ // fragment-web v0.1.0
775
+ import { atom } from 'jotai';
776
+
777
+ export const ${toKebabCase(name).replace(/-/g, "_")}Store = atom({ loading: false });
778
+ `,
779
+ redux: `// Generated by: frg make:store ${name}
780
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
781
+ // fragment-web v0.1.0
782
+ export interface ${className}State {
783
+ loading: boolean;
784
+ }
785
+
786
+ export const initial${className}State: ${className}State = { loading: false };
787
+ `,
788
+ mobx: `// Generated by: frg make:store ${name}
789
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
790
+ // fragment-web v0.1.0
791
+ import { makeAutoObservable } from 'mobx';
792
+
793
+ export class ${className}Store {
794
+ loading = false;
795
+
796
+ constructor() {
797
+ makeAutoObservable(this);
798
+ }
799
+
800
+ setLoading(value: boolean): void {
801
+ this.loading = value;
802
+ }
803
+ }
804
+ `
805
+ };
806
+ writeFileIfAllowed(path14.join(process.cwd(), outDir, fileName), contentByAdapter[adapter], options);
807
+ }
808
+ function registerMakeStore(program) {
809
+ program.command("make:store <name>").description("Generate a frontend store").option("--adapter <adapter>", "zustand|jotai|redux|mobx").option("--structure <mode>", "Override structure mode").option("--module <name>", "Module name").option("--feature <name>", "Feature name").option("--force", "Overwrite existing files").option("--dry-run", "Preview changes").action((name, opts) => {
810
+ runMakeStore(name, opts).catch((error) => {
811
+ logger.error(error instanceof Error ? error.message : "make:store failed");
812
+ process.exitCode = 1;
813
+ });
814
+ });
815
+ }
816
+
817
+ // src/web/cli/commands/make/resource.ts
818
+ async function runMakeResource(name, options = {}) {
819
+ await runMakeComponent(`${name}-card`, options);
820
+ await runMakePage(`${name}-list`, options);
821
+ await runMakeService(name, options);
822
+ await runMakeStore(name, options);
823
+ }
824
+ function registerMakeResource(program) {
825
+ program.command("make:resource <name>").description("Generate page + service + store + component in one shot").option("--structure <mode>", "Override structure mode").option("--module <name>", "Module name").option("--feature <name>", "Feature name").option("--adapter <adapter>", "Store adapter").option("--force", "Overwrite existing files").option("--dry-run", "Preview changes").action((name, opts) => {
826
+ runMakeResource(name, opts).catch((error) => {
827
+ logger.error(error instanceof Error ? error.message : "make:resource failed");
828
+ process.exitCode = 1;
829
+ });
830
+ });
831
+ }
832
+
833
+ // src/web/cli/commands/create/module.ts
834
+ import path15 from "path";
835
+ async function runCreateModule(name, options = {}) {
836
+ const config = ensureWebConfig();
837
+ const mode = options.structure || config.structure;
838
+ if (mode !== "module") {
839
+ throw new Error("create:module requires structure mode 'module' (or pass --structure module)");
840
+ }
841
+ await runMakePage(`${name}-list`, { ...options, structure: mode, module: name });
842
+ await runMakePage(`${name}-detail`, { ...options, structure: mode, module: name });
843
+ await runMakeService(name, { ...options, structure: mode, module: name });
844
+ await runMakeStore(name, { ...options, structure: mode, module: name });
845
+ await runMakeComponent(`${name}-card`, { ...options, structure: mode, module: name });
846
+ writeFileIfAllowed(
847
+ path15.join(process.cwd(), "src/modules", toKebabCase(name), "index.ts"),
848
+ `// Generated by: frg create:module ${name}
849
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
850
+ // fragment-web v0.1.0
851
+ export * from './${toKebabCase(name)}-list.page';
852
+ export * from './${toKebabCase(name)}-detail.page';
853
+ export * from './${toKebabCase(name)}.service';
854
+ export * from './${toKebabCase(name)}.store';
855
+ export * from './components/${toKebabCase(name)}-card.component';
856
+ `,
857
+ options
858
+ );
859
+ }
860
+ function registerCreateModule(program) {
861
+ program.command("create:module <name>").description("Generate a full module slice").option("--structure <mode>", "Override structure mode").option("--force", "Overwrite existing files").option("--dry-run", "Preview changes").action((name, opts) => {
862
+ runCreateModule(name, opts).catch((error) => {
863
+ logger.error(error instanceof Error ? error.message : "create:module failed");
864
+ process.exitCode = 1;
865
+ });
866
+ });
867
+ }
868
+
869
+ // src/web/cli/commands/create/feature.ts
870
+ import path16 from "path";
871
+ async function runCreateFeature(name, options = {}) {
872
+ const config = ensureWebConfig();
873
+ const mode = options.structure || config.structure;
874
+ if (mode !== "feature") {
875
+ throw new Error("create:feature requires structure mode 'feature' (or pass --structure feature)");
876
+ }
877
+ await runMakePage(`${name}-list`, { ...options, structure: mode, feature: name });
878
+ await runMakeService(name, { ...options, structure: mode, feature: name });
879
+ await runMakeStore(name, { ...options, structure: mode, feature: name });
880
+ await runMakeComponent(`${name}-card`, { ...options, structure: mode, feature: name });
881
+ writeFileIfAllowed(
882
+ path16.join(process.cwd(), "src/features", toKebabCase(name), "index.ts"),
883
+ `// Generated by: frg create:feature ${name}
884
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
885
+ // fragment-web v0.1.0
886
+ export * from './pages/${toKebabCase(name)}-list.page';
887
+ export * from './services/${toKebabCase(name)}.service';
888
+ export * from './stores/${toKebabCase(name)}.store';
889
+ export * from './components/${toKebabCase(name)}-card.component';
890
+ `,
891
+ options
892
+ );
893
+ }
894
+ function registerCreateFeature(program) {
895
+ program.command("create:feature <name>").description("Generate a full feature slice").option("--structure <mode>", "Override structure mode").option("--force", "Overwrite existing files").option("--dry-run", "Preview changes").action((name, opts) => {
896
+ runCreateFeature(name, opts).catch((error) => {
897
+ logger.error(error instanceof Error ? error.message : "create:feature failed");
898
+ process.exitCode = 1;
899
+ });
900
+ });
901
+ }
902
+
903
+ // src/web/cli/commands/create/mvvm.ts
904
+ import path17 from "path";
905
+ async function runCreateMvvm(name, options = {}) {
906
+ const config = ensureWebConfig();
907
+ const mode = options.structure || config.structure;
908
+ if (mode !== "mvvm") {
909
+ throw new Error("create:mvvm requires structure mode 'mvvm' (or pass --structure mvvm)");
910
+ }
911
+ const dom = toKebabCase(name);
912
+ const className = toPascalCase(name);
913
+ await runMakePage(`${name}-list`, { ...options, structure: mode, feature: name });
914
+ await runMakeComponent(`${name}-card`, { ...options, structure: mode, feature: name });
915
+ writeFileIfAllowed(
916
+ path17.join(process.cwd(), "src/app", dom, "viewmodel", `${dom}.vm.ts`),
917
+ `// Generated by: frg create:mvvm ${name}
918
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
919
+ // fragment-web v0.1.0
920
+ import { FragmentFetch, FragmentService } from 'fragment-web';
921
+
922
+ @FragmentService()
923
+ export class ${className}Vm {
924
+ async load(): Promise<unknown> {
925
+ return FragmentFetch.get('/${dom}');
926
+ }
927
+ }
928
+ `,
929
+ options
930
+ );
931
+ writeFileIfAllowed(
932
+ path17.join(process.cwd(), "src/app", dom, "model", `${dom}.model.ts`),
933
+ `// Generated by: frg create:mvvm ${name}
934
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
935
+ // fragment-web v0.1.0
936
+ export interface ${className}Model {
937
+ id: string;
938
+ }
939
+ `,
940
+ options
941
+ );
942
+ writeFileIfAllowed(
943
+ path17.join(process.cwd(), "src/app", dom, "index.ts"),
944
+ `// Generated by: frg create:mvvm ${name}
945
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
946
+ // fragment-web v0.1.0
947
+ export * from './model/${dom}.model';
948
+ export * from './viewmodel/${dom}.vm';
949
+ export * from './view/${dom}-list.page';
950
+ export * from './view/${dom}-card.component';
951
+ `,
952
+ options
953
+ );
954
+ }
955
+ function registerCreateMvvm(program) {
956
+ program.command("create:mvvm <name>").description("Generate a full MVVM slice").option("--structure <mode>", "Override structure mode").option("--force", "Overwrite existing files").option("--dry-run", "Preview changes").action((name, opts) => {
957
+ runCreateMvvm(name, opts).catch((error) => {
958
+ logger.error(error instanceof Error ? error.message : "create:mvvm failed");
959
+ process.exitCode = 1;
960
+ });
961
+ });
962
+ }
963
+
964
+ // src/web/cli/index.ts
965
+ function registerWebCommands(registry) {
966
+ registerInstallWeb(registry);
967
+ registerInstallApi(registry);
968
+ registerInitWeb(registry);
969
+ registerInitFullstack(registry);
970
+ registerWebSync(registry);
971
+ registerMakeComponent(registry);
972
+ registerMakePage(registry);
973
+ registerMakeService(registry);
974
+ registerMakeGuard(registry);
975
+ registerMakeLayout(registry);
976
+ registerMakeStore(registry);
977
+ registerMakeResource(registry);
978
+ registerCreateModule(registry);
979
+ registerCreateFeature(registry);
980
+ registerCreateMvvm(registry);
981
+ }
982
+ export {
983
+ registerWebCommands
984
+ };