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,2035 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/platform/cli/web/index.ts
31
+ var web_exports = {};
32
+ __export(web_exports, {
33
+ registerWebCommands: () => registerWebCommands
34
+ });
35
+ module.exports = __toCommonJS(web_exports);
36
+
37
+ // src/platform/cli/web/commands/web-sync.ts
38
+ var import_node_fs3 = __toESM(require("fs"));
39
+ var import_node_path4 = __toESM(require("path"));
40
+
41
+ // src/platform/cli/web/utils/config.ts
42
+ var import_node_fs = __toESM(require("fs"));
43
+ var import_node_path = __toESM(require("path"));
44
+ var WEB_CONFIG_FILE = "fragment.web.json";
45
+ function resolveCwd(cwd) {
46
+ return cwd ? import_node_path.default.resolve(cwd) : process.cwd();
47
+ }
48
+ function webConfigPath(cwd) {
49
+ return import_node_path.default.join(resolveCwd(cwd), WEB_CONFIG_FILE);
50
+ }
51
+ function readJsonFile(filePath) {
52
+ if (!import_node_fs.default.existsSync(filePath)) {
53
+ return null;
54
+ }
55
+ const raw = import_node_fs.default.readFileSync(filePath, "utf8");
56
+ return JSON.parse(raw);
57
+ }
58
+ function readWebConfig(cwd) {
59
+ return readJsonFile(webConfigPath(cwd));
60
+ }
61
+ function ensureWebConfig(cwd) {
62
+ const config2 = readWebConfig(cwd);
63
+ if (!config2) {
64
+ throw new Error("fragment.web.json was not found in the current project");
65
+ }
66
+ return config2;
67
+ }
68
+
69
+ // src/platform/cli/web/utils/header.ts
70
+ function autoGeneratedSyncHeader(source) {
71
+ return `// \u26A0\uFE0F AUTO-GENERATED by frg web:sync \u2014 do not edit manually
72
+ // Source: ${source}
73
+ // Synced at: ${(/* @__PURE__ */ new Date()).toISOString()}`;
74
+ }
75
+
76
+ // src/platform/cli/web/utils/logger.ts
77
+ var import_picocolors = __toESM(require("picocolors"));
78
+ var import_node_path2 = __toESM(require("path"));
79
+ function line(symbol, label, message) {
80
+ return ` ${symbol} ${label.padEnd(8, " ")} ${message}`;
81
+ }
82
+ function displayPath(value) {
83
+ if (!value) return value;
84
+ if (!import_node_path2.default.isAbsolute(value)) return value;
85
+ const rel = import_node_path2.default.relative(process.cwd(), value);
86
+ if (!rel) return ".";
87
+ if (rel.startsWith("..")) return value;
88
+ return rel;
89
+ }
90
+ var logger = {
91
+ section(title, subtitle) {
92
+ process.stdout.write(`
93
+ ${import_picocolors.default.bold(title)}${subtitle ? ` ${subtitle}` : ""}
94
+
95
+ `);
96
+ },
97
+ created(path19) {
98
+ process.stdout.write(`${line(import_picocolors.default.green("\u2714"), "created", displayPath(path19))}
99
+ `);
100
+ },
101
+ updated(path19, detail) {
102
+ process.stdout.write(
103
+ `${line(import_picocolors.default.cyan("\u2714"), "updated", `${displayPath(path19)}${detail ? ` (${detail})` : ""}`)}
104
+ `
105
+ );
106
+ },
107
+ exists(path19, detail = "use --force to overwrite") {
108
+ process.stdout.write(`${line(import_picocolors.default.yellow("\u26A0"), "exists", `${displayPath(path19)} (${detail})`)}
109
+ `);
110
+ },
111
+ dryRun(path19) {
112
+ process.stdout.write(`${line(import_picocolors.default.blue("\u2139"), "dry-run", displayPath(path19))}
113
+ `);
114
+ },
115
+ error(message) {
116
+ process.stderr.write(`${line(import_picocolors.default.red("\u2716"), "error", message)}
117
+ `);
118
+ },
119
+ done(message) {
120
+ process.stdout.write(`
121
+ ${import_picocolors.default.bold("Done.")} ${message}
122
+ `);
123
+ }
124
+ };
125
+
126
+ // src/platform/cli/web/utils/write-file.ts
127
+ var import_node_fs2 = __toESM(require("fs"));
128
+ var import_node_path3 = __toESM(require("path"));
129
+ function writeFileIfAllowed(filePath, content, options = {}) {
130
+ const exists = import_node_fs2.default.existsSync(filePath);
131
+ if (options.dryRun) {
132
+ logger.dryRun(filePath);
133
+ return "dry-run";
134
+ }
135
+ if (exists && !options.force) {
136
+ logger.exists(filePath);
137
+ return "exists";
138
+ }
139
+ import_node_fs2.default.mkdirSync(import_node_path3.default.dirname(filePath), { recursive: true });
140
+ import_node_fs2.default.writeFileSync(filePath, content, "utf8");
141
+ const mode = options.mode ?? (exists ? "updated" : "created");
142
+ if (mode === "updated") {
143
+ logger.updated(filePath);
144
+ } else {
145
+ logger.created(filePath);
146
+ }
147
+ return mode;
148
+ }
149
+
150
+ // src/platform/cli/web/commands/web-sync.ts
151
+ function toTypeName(raw) {
152
+ return raw.replace(/[^a-zA-Z0-9]+/g, " ").split(" ").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
153
+ }
154
+ function toIdentifier(raw) {
155
+ const cleaned = raw.replace(/[^a-zA-Z0-9]+/g, " ").split(" ").filter(Boolean).map(
156
+ (part, idx) => idx === 0 ? part.charAt(0).toLowerCase() + part.slice(1) : part.charAt(0).toUpperCase() + part.slice(1)
157
+ ).join("");
158
+ return cleaned || "value";
159
+ }
160
+ function toConstKey(value) {
161
+ return value.replace(/[{}]/g, "").replace(/[^a-zA-Z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toUpperCase();
162
+ }
163
+ function toFileName(raw) {
164
+ const out = raw.trim().replace(/([a-z\d])([A-Z])/g, "$1-$2").replace(/[^a-zA-Z0-9]+/g, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "").toLowerCase();
165
+ return out || "default";
166
+ }
167
+ function resolveRefName(ref) {
168
+ const parts = ref.split("/");
169
+ return parts[parts.length - 1] || "UnknownRef";
170
+ }
171
+ function schemaToTs(schema) {
172
+ if (!schema) return "unknown";
173
+ if (schema.$ref) {
174
+ return toTypeName(resolveRefName(schema.$ref));
175
+ }
176
+ if (schema.oneOf?.length) {
177
+ return schema.oneOf.map((s) => schemaToTs(s)).join(" | ");
178
+ }
179
+ if (schema.anyOf?.length) {
180
+ return schema.anyOf.map((s) => schemaToTs(s)).join(" | ");
181
+ }
182
+ if (schema.allOf?.length) {
183
+ return schema.allOf.map((s) => schemaToTs(s)).join(" & ");
184
+ }
185
+ if (schema.enum?.length) {
186
+ return schema.enum.map((v) => JSON.stringify(v)).join(" | ");
187
+ }
188
+ switch (schema.type) {
189
+ case "integer":
190
+ case "number":
191
+ return "number";
192
+ case "boolean":
193
+ return "boolean";
194
+ case "array":
195
+ return `${schemaToTs(schema.items)}[]`;
196
+ case "object": {
197
+ const properties = schema.properties || {};
198
+ const required = new Set(schema.required || []);
199
+ const fields = Object.entries(properties).map(
200
+ ([key, prop]) => `${key}${required.has(key) ? "" : "?"}: ${schemaToTs(prop)};`
201
+ );
202
+ if (schema.additionalProperties && typeof schema.additionalProperties === "object") {
203
+ fields.push(`[key: string]: ${schemaToTs(schema.additionalProperties)};`);
204
+ }
205
+ if (fields.length === 0) {
206
+ return "Record<string, unknown>";
207
+ }
208
+ return `{ ${fields.join(" ")} }`;
209
+ }
210
+ case "string":
211
+ default:
212
+ return "string";
213
+ }
214
+ }
215
+ function buildDtos(doc, source) {
216
+ const schemas = doc.components?.schemas || {};
217
+ const blocks = Object.entries(schemas).map(([rawName, schema]) => {
218
+ const name = toTypeName(rawName);
219
+ if (schema.type === "object" || schema.properties || schema.allOf?.length) {
220
+ const properties = schema.properties || {};
221
+ const required = new Set(schema.required || []);
222
+ const fields = Object.entries(properties).map(([key, prop]) => ` ${key}${required.has(key) ? "" : "?"}: ${schemaToTs(prop)};`).join("\n");
223
+ if (fields) {
224
+ return `export interface ${name} {
225
+ ${fields}
226
+ }`;
227
+ }
228
+ return `export type ${name} = ${schemaToTs(schema)};`;
229
+ }
230
+ return `export type ${name} = ${schemaToTs(schema)};`;
231
+ });
232
+ return `${autoGeneratedSyncHeader(source)}
233
+
234
+ ${blocks.join("\n\n")}
235
+ `;
236
+ }
237
+ function getPreferredSchema(response) {
238
+ if (!response?.content) return void 0;
239
+ const appJson = response.content["application/json"]?.schema;
240
+ if (appJson) return appJson;
241
+ const first = Object.values(response.content)[0];
242
+ return first?.schema;
243
+ }
244
+ function getSuccessResponseTypes(operation) {
245
+ const responses = operation.responses || {};
246
+ const types = [];
247
+ Object.entries(responses).forEach(([code, response]) => {
248
+ if (!/^2\d\d$/.test(code)) return;
249
+ const schema = getPreferredSchema(response);
250
+ types.push(schemaToTs(schema));
251
+ });
252
+ const unique = Array.from(new Set(types.filter(Boolean)));
253
+ return unique.length ? unique : ["unknown"];
254
+ }
255
+ function getRequestBodySchema(operation) {
256
+ const content = operation.requestBody?.content;
257
+ if (!content) return void 0;
258
+ const appJson = content["application/json"]?.schema;
259
+ if (appJson) return appJson;
260
+ const first = Object.values(content)[0];
261
+ return first?.schema;
262
+ }
263
+ function toFunctionName(route, method, operationId) {
264
+ if (operationId) return toIdentifier(operationId);
265
+ const routePart = route.replace(/[{}]/g, "").split("/").filter(Boolean).map((part) => toTypeName(part)).join("");
266
+ return toIdentifier(`${method}${routePart || "Root"}`);
267
+ }
268
+ function buildPathTemplate(route) {
269
+ return "`" + route.replace(/\{([^}]+)\}/g, (_, token) => `\${${toIdentifier(token)}}`) + "`";
270
+ }
271
+ function buildServiceFunction(descriptor) {
272
+ const { method, route, operation } = descriptor;
273
+ const fn = toFunctionName(route, method, operation.operationId);
274
+ const responseTypeName = `${toTypeName(fn)}Response`;
275
+ const responseUnion = getSuccessResponseTypes(operation).join(" | ");
276
+ const requestType = schemaToTs(getRequestBodySchema(operation));
277
+ const allParams = operation.parameters || [];
278
+ const pathParams = allParams.filter((p) => p.in === "path");
279
+ const queryParams = allParams.filter((p) => p.in === "query");
280
+ const args = [];
281
+ pathParams.forEach((param) => {
282
+ args.push(`${toIdentifier(param.name)}: ${schemaToTs(param.schema)}`);
283
+ });
284
+ if (requestType !== "unknown" && ["post", "put", "patch"].includes(method)) {
285
+ args.push(`payload${operation.requestBody?.required ? "" : "?"}: ${requestType}`);
286
+ }
287
+ if (queryParams.length) {
288
+ const queryType = `{ ${queryParams.map((p) => `${toIdentifier(p.name)}${p.required ? "" : "?"}: ${schemaToTs(p.schema)};`).join(" ")} }`;
289
+ args.push(`query?: ${queryType}`);
290
+ }
291
+ const pathExpr = buildPathTemplate(route);
292
+ if (method === "get") {
293
+ return `export type ${responseTypeName} = ${responseUnion};
294
+
295
+ export async function ${fn}(${args.join(", ")}): Promise<${responseTypeName}> {
296
+ return FragmentFetch.get<${responseTypeName}>(${pathExpr}${queryParams.length ? ", { params: query }" : ""});
297
+ }`;
298
+ }
299
+ if (method === "delete") {
300
+ return `export type ${responseTypeName} = ${responseUnion};
301
+
302
+ export async function ${fn}(${args.join(", ")}): Promise<${responseTypeName}> {
303
+ return FragmentFetch.delete<${responseTypeName}>(${pathExpr}${queryParams.length ? ", { params: query }" : ""});
304
+ }`;
305
+ }
306
+ const methodCall = method === "post" ? "post" : method === "put" ? "put" : "patch";
307
+ const bodyArg = requestType !== "unknown" ? "payload" : "undefined";
308
+ return `export type ${responseTypeName} = ${responseUnion};
309
+
310
+ export async function ${fn}(${args.join(", ")}): Promise<${responseTypeName}> {
311
+ return FragmentFetch.${methodCall}<${responseTypeName}>(${pathExpr}, ${bodyArg}${queryParams.length ? ", { params: query }" : ""});
312
+ }`;
313
+ }
314
+ function groupOperationsByTag(doc) {
315
+ const byTag = {};
316
+ Object.entries(doc.paths || {}).forEach(([route, methods]) => {
317
+ ["get", "post", "put", "patch", "delete"].forEach((method) => {
318
+ const operation = methods?.[method];
319
+ if (!operation) return;
320
+ const tags = operation.tags?.length ? operation.tags : ["default"];
321
+ tags.forEach((tag) => {
322
+ byTag[tag] = byTag[tag] || [];
323
+ byTag[tag].push({ route, method, operation });
324
+ });
325
+ });
326
+ });
327
+ return byTag;
328
+ }
329
+ function buildTagServiceFile(tag, operations, source) {
330
+ const fns = operations.map((op) => buildServiceFunction(op));
331
+ return `${autoGeneratedSyncHeader(source)}
332
+
333
+ import { FragmentFetch } from 'fragment-web';
334
+
335
+ ${fns.join("\n\n")}
336
+ `;
337
+ }
338
+ function buildRootServiceBarrel(tags, source) {
339
+ const exports2 = tags.map((tag) => `export * from './services/${toFileName(tag)}.service';`).join("\n");
340
+ return `${autoGeneratedSyncHeader(source)}
341
+
342
+ ${exports2}
343
+ `;
344
+ }
345
+ function buildServicesIndex(tags, source) {
346
+ const exports2 = tags.map((tag) => `export * from './${toFileName(tag)}.service';`).join("\n");
347
+ return `${autoGeneratedSyncHeader(source)}
348
+
349
+ ${exports2}
350
+ `;
351
+ }
352
+ function buildRoutes(doc, source) {
353
+ const lines = Object.keys(doc.paths || {}).map((route) => ` ${toConstKey(route)}: '${route}',`);
354
+ return `${autoGeneratedSyncHeader(source)}
355
+
356
+ export const ApiRoutes = {
357
+ ${lines.join("\n")}
358
+ } as const;
359
+ `;
360
+ }
361
+ async function fetchOpenApi(source) {
362
+ const response = await fetch(source);
363
+ if (!response.ok) {
364
+ throw new Error(`Could not connect to ${source}`);
365
+ }
366
+ return await response.json();
367
+ }
368
+ async function syncOnce(options) {
369
+ const config2 = ensureWebConfig();
370
+ const base = (options.url || config2.fetch.baseUrl).replace(/\/$/, "");
371
+ const source = `${base}${config2.serverRoot}/docs/api-json`;
372
+ const generatedDir = import_node_path4.default.join(process.cwd(), "src/generated");
373
+ const servicesDir = import_node_path4.default.join(generatedDir, "services");
374
+ const doc = await fetchOpenApi(source);
375
+ writeFileIfAllowed(import_node_path4.default.join(generatedDir, "routes.ts"), buildRoutes(doc, source), {
376
+ dryRun: options.dryRun,
377
+ force: true
378
+ });
379
+ writeFileIfAllowed(import_node_path4.default.join(generatedDir, "dtos.ts"), buildDtos(doc, source), {
380
+ dryRun: options.dryRun,
381
+ force: true
382
+ });
383
+ const grouped = groupOperationsByTag(doc);
384
+ const tags = Object.keys(grouped).sort();
385
+ const expectedServiceFiles = new Set(tags.map((tag) => `${toFileName(tag)}.service.ts`));
386
+ if (import_node_fs3.default.existsSync(servicesDir)) {
387
+ const existing = import_node_fs3.default.readdirSync(servicesDir);
388
+ existing.filter((file) => file.endsWith(".service.ts")).filter((file) => !expectedServiceFiles.has(file)).forEach((staleFile) => {
389
+ const stalePath = import_node_path4.default.join(servicesDir, staleFile);
390
+ if (options.dryRun) {
391
+ logger.dryRun(stalePath);
392
+ return;
393
+ }
394
+ import_node_fs3.default.unlinkSync(stalePath);
395
+ logger.updated(stalePath, "removed stale generated file");
396
+ });
397
+ }
398
+ tags.forEach((tag) => {
399
+ writeFileIfAllowed(import_node_path4.default.join(servicesDir, `${toFileName(tag)}.service.ts`), buildTagServiceFile(tag, grouped[tag], source), {
400
+ dryRun: options.dryRun,
401
+ force: true
402
+ });
403
+ });
404
+ writeFileIfAllowed(import_node_path4.default.join(servicesDir, "index.ts"), buildServicesIndex(tags, source), {
405
+ dryRun: options.dryRun,
406
+ force: true
407
+ });
408
+ writeFileIfAllowed(import_node_path4.default.join(generatedDir, "services.ts"), buildRootServiceBarrel(tags, source), {
409
+ dryRun: options.dryRun,
410
+ force: true
411
+ });
412
+ }
413
+ async function runWebSync(options = {}) {
414
+ await syncOnce(options);
415
+ if (options.watch) {
416
+ logger.section("fragment-web", "web:sync --watch");
417
+ setInterval(() => {
418
+ syncOnce(options).catch((error) => {
419
+ logger.error(error instanceof Error ? error.message : "web:sync watch failed");
420
+ });
421
+ }, 5e3);
422
+ }
423
+ }
424
+ function registerWebSync(program) {
425
+ 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) => {
426
+ runWebSync(opts).catch((error) => {
427
+ logger.error(error instanceof Error ? error.message : "web:sync failed");
428
+ process.exitCode = 1;
429
+ });
430
+ });
431
+ }
432
+
433
+ // src/platform/cli/web/commands/make/component.ts
434
+ var import_node_path6 = __toESM(require("path"));
435
+
436
+ // src/platform/cli/web/utils/names.ts
437
+ function normalizeName(input) {
438
+ return input.trim().replace(/([a-z\d])([A-Z])/g, "$1-$2").replace(/[^a-zA-Z0-9]+/g, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "").toLowerCase();
439
+ }
440
+ function toKebabCase(input) {
441
+ const normalized = normalizeName(input);
442
+ return normalized || "app";
443
+ }
444
+ function toPascalCase(input) {
445
+ return toKebabCase(input).split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
446
+ }
447
+
448
+ // src/platform/cli/web/utils/resolve-paths.ts
449
+ var import_node_path5 = __toESM(require("path"));
450
+ function domainOrName(input) {
451
+ return toKebabCase(input.domain || input.name);
452
+ }
453
+ function resolveOutputDir(input) {
454
+ const dom = domainOrName(input);
455
+ const leaf = toKebabCase(input.name);
456
+ switch (input.mode) {
457
+ case "layered":
458
+ switch (input.kind) {
459
+ case "component":
460
+ return import_node_path5.default.join("src", "components", leaf);
461
+ case "page":
462
+ return import_node_path5.default.join("src", "pages", leaf);
463
+ case "service":
464
+ return import_node_path5.default.join("src", "services");
465
+ case "store":
466
+ return import_node_path5.default.join("src", "stores");
467
+ case "guard":
468
+ return import_node_path5.default.join("src", "guards");
469
+ case "layout":
470
+ return import_node_path5.default.join("src", "layouts");
471
+ default:
472
+ throw new Error(`${input.kind} is not available in layered mode`);
473
+ }
474
+ case "module":
475
+ switch (input.kind) {
476
+ case "component":
477
+ return import_node_path5.default.join("src", "modules", dom, "components");
478
+ case "page":
479
+ case "service":
480
+ case "store":
481
+ return import_node_path5.default.join("src", "modules", dom);
482
+ case "guard":
483
+ return import_node_path5.default.join("src", "shared", "guards");
484
+ case "layout":
485
+ return import_node_path5.default.join("src", "shared", "layouts");
486
+ default:
487
+ throw new Error(`${input.kind} is not available in module mode`);
488
+ }
489
+ case "feature":
490
+ switch (input.kind) {
491
+ case "component":
492
+ return import_node_path5.default.join("src", "features", dom, "components");
493
+ case "page":
494
+ return import_node_path5.default.join("src", "features", dom, "pages");
495
+ case "service":
496
+ return import_node_path5.default.join("src", "features", dom, "services");
497
+ case "store":
498
+ return import_node_path5.default.join("src", "features", dom, "stores");
499
+ case "guard":
500
+ return import_node_path5.default.join("src", "shared", "guards");
501
+ case "layout":
502
+ return import_node_path5.default.join("src", "shared", "layouts");
503
+ default:
504
+ throw new Error(`${input.kind} is not available in feature mode`);
505
+ }
506
+ case "mvvm":
507
+ switch (input.kind) {
508
+ case "component":
509
+ case "page":
510
+ return import_node_path5.default.join("src", "app", dom, "view");
511
+ case "service":
512
+ case "store":
513
+ case "viewmodel":
514
+ return import_node_path5.default.join("src", "app", dom, "viewmodel");
515
+ case "model":
516
+ return import_node_path5.default.join("src", "app", dom, "model");
517
+ case "guard":
518
+ return import_node_path5.default.join("src", "shared", "guards");
519
+ case "layout":
520
+ return import_node_path5.default.join("src", "shared", "layouts");
521
+ default:
522
+ throw new Error(`${input.kind} is not available in mvvm mode`);
523
+ }
524
+ default:
525
+ return import_node_path5.default.join("src");
526
+ }
527
+ }
528
+
529
+ // src/platform/cli/web/commands/make/component.ts
530
+ function parseState(state) {
531
+ if (!state) return [];
532
+ return state.split(",").map((it) => it.trim()).filter(Boolean).map((pair) => {
533
+ const [key, type] = pair.split(":").map((v) => v.trim());
534
+ return { key, type: type || "unknown" };
535
+ });
536
+ }
537
+ async function runMakeComponent(name, options = {}) {
538
+ const config2 = ensureWebConfig();
539
+ const mode = options.structure || config2.structure;
540
+ const domain = options.feature || options.module;
541
+ const outDir = resolveOutputDir({ mode, kind: "component", name, domain });
542
+ const fileName = `${toKebabCase(name)}.component.tsx`;
543
+ const className = `${toPascalCase(name)}Component`;
544
+ const stateFields = parseState(options.state);
545
+ const injected = (options.inject || "").split(",").map((v) => v.trim()).filter(Boolean);
546
+ const decoratorArgs = [
547
+ `tag: '${toKebabCase(name)}'`,
548
+ options.memo ? "memo: true" : "",
549
+ `displayName: '${className}'`
550
+ ].filter(Boolean).join(", ");
551
+ const stateDecl = stateFields.map((field) => ` @State(undefined as ${field.type})
552
+ ${field.key}!: ${field.type};`).join("\n\n");
553
+ const injectDecl = injected.map((token) => ` @Inject('${token}')
554
+ ${toKebabCase(token).replace(/-([a-z])/g, (_, c) => c.toUpperCase())}: unknown;`).join("\n\n");
555
+ const content = `// Generated by: frg make:component ${name}
556
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
557
+ // fragment-web v0.1.0
558
+ import React from 'react';
559
+ import { Component, Inject, State } from 'fragment-web';
560
+
561
+ @Component({ ${decoratorArgs} })
562
+ export class ${className} {
563
+ ${injectDecl ? `${injectDecl}
564
+
565
+ ` : ""}${stateDecl ? `${stateDecl}
566
+
567
+ ` : ""} render(): JSX.Element {
568
+ return <div>${toPascalCase(name)} component</div>;
569
+ }
570
+ }
571
+ `;
572
+ writeFileIfAllowed(import_node_path6.default.join(process.cwd(), outDir, fileName), content, options);
573
+ }
574
+ function registerMakeComponent(program) {
575
+ program.command("make:component <name>").description("Generate a frontend component").option("--memo", "Enable component memo hint").option("--state <fields>", "CSV field:type list").option("--inject <services>", "CSV of injected service tokens").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) => {
576
+ runMakeComponent(name, opts).catch((error) => {
577
+ logger.error(error instanceof Error ? error.message : "make:component failed");
578
+ process.exitCode = 1;
579
+ });
580
+ });
581
+ }
582
+
583
+ // src/platform/cli/web/commands/make/page.ts
584
+ var import_node_path7 = __toESM(require("path"));
585
+ function parseCsv(value) {
586
+ if (!value) return [];
587
+ return value.split(",").map((v) => v.trim()).filter(Boolean);
588
+ }
589
+ async function runMakePage(name, options = {}) {
590
+ const config2 = ensureWebConfig();
591
+ const mode = options.structure || config2.structure;
592
+ const domain = options.feature || options.module;
593
+ const outDir = resolveOutputDir({ mode, kind: "page", name, domain });
594
+ const fileName = `${toKebabCase(name)}.page.tsx`;
595
+ const className = `${toPascalCase(name)}Page`;
596
+ const route = options.path || `/${toKebabCase(name)}`;
597
+ const guard = options.guard?.trim();
598
+ const params = parseCsv(options.params);
599
+ const query = (options.query || "").split(",").map((v) => v.trim()).filter(Boolean);
600
+ const paramsDecl = params.map((p) => ` @Param('${p}')
601
+ ${p}!: string;`).join("\n\n");
602
+ const queryDecl = query.map((q) => {
603
+ const [key, def] = q.split(":").map((v) => v.trim());
604
+ return ` @Query('${key}'${def ? `, '${def}'` : ""})
605
+ ${key}!: string;`;
606
+ }).join("\n\n");
607
+ const content = `// Generated by: frg make:page ${name}
608
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
609
+ // fragment-web v0.1.0
610
+ import React from 'react';
611
+ import { Guard, Page, Param, Query } from 'fragment-web';
612
+ ${guard ? `import { ${guard} } from '../guards/${toKebabCase(guard)}.guard';
613
+ ` : ""}
614
+ @Page({ path: '${route}', title: '${toPascalCase(name)}' })
615
+ ${guard ? `@Guard(${guard})
616
+ ` : ""}export class ${className} {
617
+ ${paramsDecl ? `${paramsDecl}
618
+
619
+ ` : ""}${queryDecl ? `${queryDecl}
620
+
621
+ ` : ""} render(): JSX.Element {
622
+ return <div>${toPascalCase(name)} page</div>;
623
+ }
624
+ }
625
+ `;
626
+ writeFileIfAllowed(import_node_path7.default.join(process.cwd(), outDir, fileName), content, options);
627
+ }
628
+ function registerMakePage(program) {
629
+ program.command("make:page <name>").description("Generate a frontend page").option("--path <path>", "Route path").option("--guard <guard>", "Guard class name").option("--params <keys>", "CSV route param names").option("--query <pairs>", "CSV queryKey[:default] list").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) => {
630
+ runMakePage(name, opts).catch((error) => {
631
+ logger.error(error instanceof Error ? error.message : "make:page failed");
632
+ process.exitCode = 1;
633
+ });
634
+ });
635
+ }
636
+
637
+ // src/platform/cli/web/commands/make/service.ts
638
+ var import_node_fs4 = __toESM(require("fs"));
639
+ var import_node_path8 = __toESM(require("path"));
640
+
641
+ // src/shared/config.utils.ts
642
+ var fs6 = __toESM(require("fs"));
643
+ var path10 = __toESM(require("path"));
644
+
645
+ // src/shared/env.utils.ts
646
+ var fs4 = __toESM(require("fs"));
647
+ var path8 = __toESM(require("path"));
648
+ var dotenv = __toESM(require("dotenv"));
649
+ var EnvUtils = class {
650
+ static loadEnvironment(cwd = process.cwd()) {
651
+ if (this.loaded) return;
652
+ const envPath = path8.join(cwd, ".env");
653
+ if (fs4.existsSync(envPath)) {
654
+ dotenv.config({ path: envPath, override: true });
655
+ }
656
+ this.loaded = true;
657
+ }
658
+ static resolveMode(cwd = process.cwd()) {
659
+ if (this.resolvedMode) return this.resolvedMode;
660
+ const projectRoot = path8.resolve(cwd).replace(/\\/g, "/");
661
+ const entryArg = process.argv[1] ? path8.resolve(process.argv[1]) : "";
662
+ const entryPath = entryArg.replace(/\\/g, "/");
663
+ const entryInsideProject = entryPath.length > 0 && (entryPath === projectRoot || entryPath.startsWith(`${projectRoot}/`));
664
+ if (entryInsideProject) {
665
+ if (entryPath.includes("/dist/")) {
666
+ this.resolvedMode = "production";
667
+ return this.resolvedMode;
668
+ }
669
+ if (entryPath.includes("/src/") || entryPath.endsWith(".ts")) {
670
+ this.resolvedMode = "development";
671
+ return this.resolvedMode;
672
+ }
673
+ }
674
+ const hasSrc = fs4.existsSync(path8.join(projectRoot, "src"));
675
+ const hasDist = fs4.existsSync(path8.join(projectRoot, "dist"));
676
+ if (hasSrc && !hasDist) {
677
+ this.resolvedMode = "development";
678
+ return this.resolvedMode;
679
+ }
680
+ if (!hasSrc && hasDist) {
681
+ this.resolvedMode = "production";
682
+ return this.resolvedMode;
683
+ }
684
+ if (hasSrc && hasDist) {
685
+ this.resolvedMode = "development";
686
+ return this.resolvedMode;
687
+ }
688
+ this.resolvedMode = "production";
689
+ return this.resolvedMode;
690
+ }
691
+ static getString(key, defaultValue) {
692
+ this.loadEnvironment();
693
+ const value = process.env[key];
694
+ return value !== void 0 ? value : defaultValue ?? "";
695
+ }
696
+ static getBoolean(key, defaultValue = false) {
697
+ this.loadEnvironment();
698
+ const value = process.env[key];
699
+ if (value === void 0) return defaultValue;
700
+ return ["true", "1", "yes", "on"].includes(value.toLowerCase());
701
+ }
702
+ static getNumber(key, defaultValue = 0) {
703
+ this.loadEnvironment();
704
+ const value = process.env[key];
705
+ if (value === void 0) return defaultValue;
706
+ const num = Number(value);
707
+ return isNaN(num) ? defaultValue : num;
708
+ }
709
+ static getJson(key, defaultValue) {
710
+ this.loadEnvironment();
711
+ const value = process.env[key];
712
+ if (value === void 0) return defaultValue;
713
+ try {
714
+ return JSON.parse(value);
715
+ } catch {
716
+ console.warn(`\u26A0\uFE0F Invalid JSON in env var ${key}, using default`);
717
+ return defaultValue;
718
+ }
719
+ }
720
+ /**
721
+ * Check if running in development mode.
722
+ * Mode is inferred from runtime/project layout, not from env file variants.
723
+ */
724
+ static isDevelopmentMode() {
725
+ this.loadEnvironment();
726
+ return this.resolveMode() === "development";
727
+ }
728
+ /**
729
+ * Get current environment mode ('development' or 'production')
730
+ */
731
+ static getEnvironmentMode() {
732
+ this.loadEnvironment();
733
+ return this.resolveMode();
734
+ }
735
+ };
736
+ EnvUtils.loaded = false;
737
+ EnvUtils.resolvedMode = null;
738
+
739
+ // src/shared/tsconfig.utils.ts
740
+ var fs5 = __toESM(require("fs"));
741
+ var path9 = __toESM(require("path"));
742
+ var TsConfigUtils = class {
743
+ static loadConfig() {
744
+ if (this.configCache) return this.configCache;
745
+ for (const file of this.DEFAULT_PATHS) {
746
+ const fullPath = path9.join(process.cwd(), file);
747
+ if (fs5.existsSync(fullPath)) {
748
+ try {
749
+ this.configCache = JSON.parse(fs5.readFileSync(fullPath, "utf8"));
750
+ if (!this.configCache) return {};
751
+ return this.configCache;
752
+ } catch {
753
+ }
754
+ }
755
+ }
756
+ this.configCache = {};
757
+ return this.configCache;
758
+ }
759
+ static exists() {
760
+ return this.DEFAULT_PATHS.some(
761
+ (f) => fs5.existsSync(path9.join(process.cwd(), f))
762
+ );
763
+ }
764
+ static hasDecoratorSupport() {
765
+ const config2 = this.loadConfig();
766
+ const opts = config2.compilerOptions || {};
767
+ return !!opts.experimentalDecorators && !!opts.emitDecoratorMetadata;
768
+ }
769
+ static getRootDir() {
770
+ const config2 = this.loadConfig();
771
+ const rootDir = config2.compilerOptions?.rootDir || "src";
772
+ return path9.isAbsolute(rootDir) ? rootDir : path9.join(process.cwd(), rootDir);
773
+ }
774
+ static getOutDir() {
775
+ const config2 = this.loadConfig();
776
+ const outDir = config2.compilerOptions?.outDir || "dist";
777
+ return path9.isAbsolute(outDir) ? outDir : path9.join(process.cwd(), outDir);
778
+ }
779
+ static getIncludePatterns() {
780
+ const config2 = this.loadConfig();
781
+ return config2.include || ["src/**/*"];
782
+ }
783
+ };
784
+ TsConfigUtils.configCache = null;
785
+ TsConfigUtils.DEFAULT_PATHS = ["tsconfig.json"];
786
+
787
+ // src/shared/config.utils.ts
788
+ var ConfigUtils = class {
789
+ static loadConfig() {
790
+ if (this.configCache) return this.configCache;
791
+ EnvUtils.loadEnvironment();
792
+ const configPath = path10.join(process.cwd(), "fragment.json");
793
+ const cachePath = path10.join(process.cwd(), "fragment.cache.json");
794
+ const sourcePath = fs6.existsSync(cachePath) ? cachePath : configPath;
795
+ if (!fs6.existsSync(sourcePath)) {
796
+ this.configCache = {};
797
+ return this.configCache;
798
+ }
799
+ try {
800
+ this.configCache = JSON.parse(fs6.readFileSync(sourcePath, "utf8")) || {};
801
+ if (!this.configCache) return {};
802
+ return this.configCache;
803
+ } catch (error) {
804
+ console.error("\u274C Error loading fragment config:", error);
805
+ this.configCache = {};
806
+ return this.configCache;
807
+ }
808
+ }
809
+ static interpolateEnvVars(value) {
810
+ if (typeof value === "string") {
811
+ return value.replace(
812
+ /\$\{([^}]+)\}/g,
813
+ (_match, expression) => {
814
+ const [rawKey, ...fallbackParts] = String(expression).split(":");
815
+ const key = rawKey.trim();
816
+ const fallback = fallbackParts.length > 0 ? fallbackParts.join(":").trim() : void 0;
817
+ return EnvUtils.getString(key, fallback) || "";
818
+ }
819
+ );
820
+ }
821
+ if (Array.isArray(value)) {
822
+ return value.map((item) => this.interpolateEnvVars(item));
823
+ }
824
+ if (value !== null && typeof value === "object") {
825
+ const result = {};
826
+ for (const key in value) {
827
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
828
+ result[key] = this.interpolateEnvVars(value[key]);
829
+ }
830
+ }
831
+ return result;
832
+ }
833
+ return value;
834
+ }
835
+ static getDatabaseConfig() {
836
+ const config2 = this.loadConfig();
837
+ if (!config2.database) return {};
838
+ let dbConfig = this.interpolateEnvVars(config2.database);
839
+ const isProduction = EnvUtils.getEnvironmentMode() === "production";
840
+ const rootDir = TsConfigUtils.getRootDir().replace(/\\/g, "/");
841
+ const outDir = TsConfigUtils.getOutDir().replace(/\\/g, "/");
842
+ if (Array.isArray(dbConfig.entities)) {
843
+ dbConfig.entities = dbConfig.entities.map(
844
+ (pattern) => this.toRuntimePathPattern(pattern, isProduction, rootDir, outDir)
845
+ );
846
+ }
847
+ if (Array.isArray(dbConfig.migrations)) {
848
+ dbConfig.migrations = dbConfig.migrations.map(
849
+ (pattern) => this.toRuntimePathPattern(pattern, isProduction, rootDir, outDir)
850
+ );
851
+ }
852
+ return dbConfig;
853
+ }
854
+ static toRuntimePathPattern(pattern, isProduction, rootDir, outDir) {
855
+ if (typeof pattern !== "string") return pattern;
856
+ const normalized = pattern.replace(/\\/g, "/");
857
+ if (!isProduction) return normalized;
858
+ const jsPattern = normalized.replace(/\.ts$/i, ".js");
859
+ const normalizedRoot = rootDir.replace(/\\/g, "/").replace(/\/+$/, "");
860
+ const normalizedOut = outDir.replace(/\\/g, "/").replace(/\/+$/, "");
861
+ const relativeRoot = path10.relative(process.cwd(), normalizedRoot).replace(/\\/g, "/").replace(/\/+$/, "");
862
+ const prefixCandidates = Array.from(
863
+ new Set(
864
+ [
865
+ `${normalizedRoot}/`,
866
+ relativeRoot ? `${relativeRoot}/` : "",
867
+ relativeRoot ? `./${relativeRoot}/` : "",
868
+ "src/",
869
+ "./src/"
870
+ ].filter(Boolean)
871
+ )
872
+ );
873
+ for (const prefix of prefixCandidates) {
874
+ if (jsPattern.startsWith(prefix)) {
875
+ return `${normalizedOut}/${jsPattern.slice(prefix.length)}`;
876
+ }
877
+ }
878
+ return jsPattern;
879
+ }
880
+ static getDatabaseProperty(property, defaultValue) {
881
+ const dbConfig = this.getDatabaseConfig();
882
+ return dbConfig[property] !== void 0 ? dbConfig[property] : defaultValue;
883
+ }
884
+ static getFullConfig() {
885
+ return this.loadConfig();
886
+ }
887
+ static getResolvedConfig() {
888
+ const config2 = this.getFullConfig();
889
+ return this.interpolateEnvVars(config2);
890
+ }
891
+ static isDatabaseEnabled() {
892
+ return !!this.getFullConfig().database;
893
+ }
894
+ static getVerbose() {
895
+ return this.getFullConfig().verbose === true;
896
+ }
897
+ static getLoggingConfig() {
898
+ return this.getFullConfig().logging || {};
899
+ }
900
+ static getMailConfig() {
901
+ return this.getResolvedConfig().mail || {};
902
+ }
903
+ static getNotificationsConfig() {
904
+ return this.getResolvedConfig().notifications || {};
905
+ }
906
+ static getDocsConfig() {
907
+ return this.getResolvedConfig().docs || {};
908
+ }
909
+ static getMailers() {
910
+ const mail = this.getMailConfig();
911
+ if (mail.mailers) {
912
+ if (Array.isArray(mail.mailers)) {
913
+ return mail.mailers.map((entry) => ({
914
+ ...entry,
915
+ name: entry.name || entry.qualifier
916
+ }));
917
+ }
918
+ return Object.entries(mail.mailers).map(([name, entry]) => ({
919
+ ...entry,
920
+ name
921
+ }));
922
+ }
923
+ const hasSingleConfig = mail.driver || mail.host || mail.port || mail.username || mail.password || mail.from || mail.apiKey;
924
+ if (hasSingleConfig) {
925
+ const { mailers, default: defaultName, ...single } = mail;
926
+ return [
927
+ {
928
+ name: defaultName || "default",
929
+ ...single
930
+ }
931
+ ];
932
+ }
933
+ return [];
934
+ }
935
+ static getDefaultMailer() {
936
+ const mail = this.getMailConfig();
937
+ if (mail.default) return mail.default;
938
+ const mailers = this.getMailers();
939
+ if (mailers.length === 1) return mailers[0].name || mailers[0].qualifier;
940
+ return void 0;
941
+ }
942
+ static getMailerConfig(name) {
943
+ const mailers = this.getMailers();
944
+ if (mailers.length === 0) return void 0;
945
+ if (name) {
946
+ return mailers.find(
947
+ (mailer) => mailer.name === name || mailer.qualifier === name
948
+ );
949
+ }
950
+ const defaultName = this.getDefaultMailer();
951
+ if (defaultName) {
952
+ return mailers.find(
953
+ (mailer) => mailer.name === defaultName || mailer.qualifier === defaultName
954
+ );
955
+ }
956
+ if (mailers.length === 1) return mailers[0];
957
+ return void 0;
958
+ }
959
+ static getPlugins() {
960
+ const config2 = this.getFullConfig();
961
+ const list = config2.plugins || [];
962
+ return list.map((entry) => {
963
+ if (typeof entry === "string") {
964
+ return { name: entry };
965
+ }
966
+ return entry;
967
+ }).filter((entry) => entry && entry.enabled !== false);
968
+ }
969
+ static getPluginAutoQualify() {
970
+ return this.getFullConfig().pluginAutoQualify === true;
971
+ }
972
+ static getStructureType() {
973
+ const config2 = this.loadConfig();
974
+ return config2.structure?.type || "layered";
975
+ }
976
+ static updateConfig(updates) {
977
+ const current = this.loadConfig();
978
+ const merged = this.mergeDeep(current, updates);
979
+ this.writeConfig(merged);
980
+ return merged;
981
+ }
982
+ static setVerbose(verbose) {
983
+ return this.updateConfig({ verbose });
984
+ }
985
+ static clearCache() {
986
+ this.configCache = null;
987
+ }
988
+ static clearCacheFile() {
989
+ const cachePath = path10.join(process.cwd(), "fragment.cache.json");
990
+ if (fs6.existsSync(cachePath)) {
991
+ fs6.unlinkSync(cachePath);
992
+ }
993
+ }
994
+ static writeConfig(config2) {
995
+ const configPath = path10.join(process.cwd(), "fragment.json");
996
+ const payload = JSON.stringify(config2, null, 2);
997
+ fs6.writeFileSync(configPath, payload, "utf8");
998
+ this.configCache = config2;
999
+ }
1000
+ static mergeDeep(target, source) {
1001
+ const output = { ...target };
1002
+ for (const [key, value] of Object.entries(source)) {
1003
+ if (value === void 0) continue;
1004
+ if (value && typeof value === "object" && !Array.isArray(value) && typeof target[key] === "object" && target[key] !== null) {
1005
+ output[key] = this.mergeDeep(target[key], value);
1006
+ } else {
1007
+ output[key] = value;
1008
+ }
1009
+ }
1010
+ return output;
1011
+ }
1012
+ };
1013
+ ConfigUtils.configCache = null;
1014
+
1015
+ // src/platform/cli/scaffold/generate/component-generator.ts
1016
+ var fs7 = __toESM(require("fs-extra"));
1017
+ var path11 = __toESM(require("path"));
1018
+
1019
+ // src/shared/errors.ts
1020
+ var FragmentError = class extends Error {
1021
+ constructor(message, statusCode, details) {
1022
+ super(message);
1023
+ this.name = this.constructor.name;
1024
+ this.statusCode = statusCode;
1025
+ this.details = details;
1026
+ Error.captureStackTrace?.(this, this.constructor);
1027
+ }
1028
+ };
1029
+ var GenerateTypeError = class extends FragmentError {
1030
+ constructor(message = "Unknown generate type", details) {
1031
+ super(message, 400, details);
1032
+ }
1033
+ };
1034
+
1035
+ // src/platform/cli/scaffold/naming.ts
1036
+ var NamingUtils = class {
1037
+ static capitalize(value) {
1038
+ if (!value) return value;
1039
+ return value.charAt(0).toUpperCase() + value.slice(1);
1040
+ }
1041
+ static camelCase(value) {
1042
+ if (!value) return value;
1043
+ const normalized = this.normalize(value);
1044
+ return normalized.charAt(0).toLowerCase() + normalized.slice(1);
1045
+ }
1046
+ static pascalCase(value) {
1047
+ if (!value) return value;
1048
+ const normalized = this.normalize(value);
1049
+ return this.capitalize(normalized);
1050
+ }
1051
+ static kebabCase(value) {
1052
+ if (!value) return value;
1053
+ return value.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").replace(/-+/g, "-").toLowerCase();
1054
+ }
1055
+ static normalize(value) {
1056
+ return value.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[\-_]+/g, " ").split(" ").filter(Boolean).map((part) => this.capitalize(part.toLowerCase())).join("");
1057
+ }
1058
+ };
1059
+
1060
+ // src/platform/cli/scaffold/generate/component-generator.ts
1061
+ var ComponentGenerator = class {
1062
+ constructor(structureType = ConfigUtils.getStructureType()) {
1063
+ this.structureType = structureType;
1064
+ }
1065
+ async generate(type, name, options) {
1066
+ switch (type) {
1067
+ case "controller":
1068
+ await this.generateController(name, options);
1069
+ break;
1070
+ case "service":
1071
+ await this.generateService(
1072
+ name,
1073
+ options?.withRepository ?? false,
1074
+ options
1075
+ );
1076
+ break;
1077
+ case "resource":
1078
+ await this.generateResource(name, options);
1079
+ break;
1080
+ case "entity":
1081
+ await this.generateEntity(name);
1082
+ break;
1083
+ case "dto":
1084
+ await this.generateDto(name);
1085
+ break;
1086
+ case "repository":
1087
+ await this.generateRepository(name, options);
1088
+ break;
1089
+ case "middleware":
1090
+ await this.generateMiddleware(name);
1091
+ break;
1092
+ case "guard":
1093
+ await this.generateGuard(name);
1094
+ break;
1095
+ case "interceptor":
1096
+ await this.generateInterceptor(name);
1097
+ break;
1098
+ case "filter":
1099
+ await this.generateFilter(name);
1100
+ break;
1101
+ case "module":
1102
+ await this.generateModule(name);
1103
+ break;
1104
+ case "feature":
1105
+ await this.generateFeature(name);
1106
+ break;
1107
+ case "job":
1108
+ await this.generateJob(name, options);
1109
+ break;
1110
+ case "mail":
1111
+ await this.generateMail(name, options);
1112
+ break;
1113
+ case "notification":
1114
+ await this.generateNotification(name, options);
1115
+ break;
1116
+ default:
1117
+ throw new GenerateTypeError(`Unknown generate type: ${type}`);
1118
+ }
1119
+ }
1120
+ getBasePath(subdir, options) {
1121
+ const srcDir = TsConfigUtils.getRootDir();
1122
+ let basePath = srcDir;
1123
+ switch (this.structureType) {
1124
+ case "module": {
1125
+ const moduleName = options?.module || "app";
1126
+ if (subdir && ["controllers", "services", "repositories"].includes(subdir)) {
1127
+ basePath = path11.join(
1128
+ srcDir,
1129
+ "modules",
1130
+ NamingUtils.kebabCase(moduleName),
1131
+ subdir
1132
+ );
1133
+ } else if (subdir && ["jobs", "mail", "notifications"].includes(subdir)) {
1134
+ basePath = path11.join(srcDir, "modules", subdir);
1135
+ } else if (subdir && ["guards", "interceptors", "filters", "middlewares"].includes(subdir)) {
1136
+ basePath = path11.join(srcDir, "common", subdir);
1137
+ } else if (subdir) {
1138
+ basePath = path11.join(srcDir, subdir);
1139
+ }
1140
+ break;
1141
+ }
1142
+ case "feature": {
1143
+ const featureName = options?.feature || "app";
1144
+ if (subdir && ["controllers", "services", "repositories"].includes(subdir)) {
1145
+ basePath = path11.join(
1146
+ srcDir,
1147
+ "features",
1148
+ NamingUtils.kebabCase(featureName)
1149
+ );
1150
+ } else if (subdir && ["jobs", "mail", "notifications"].includes(subdir)) {
1151
+ basePath = path11.join(srcDir, "features", subdir);
1152
+ } else if (subdir && ["guards", "interceptors", "filters", "middlewares"].includes(subdir)) {
1153
+ basePath = path11.join(srcDir, "shared", subdir);
1154
+ } else if (subdir) {
1155
+ basePath = path11.join(srcDir, subdir);
1156
+ }
1157
+ break;
1158
+ }
1159
+ case "layered":
1160
+ default: {
1161
+ if (subdir) {
1162
+ basePath = path11.join(srcDir, subdir);
1163
+ }
1164
+ break;
1165
+ }
1166
+ }
1167
+ return basePath;
1168
+ }
1169
+ async generateController(name, options) {
1170
+ const className = `${NamingUtils.pascalCase(name)}Controller`;
1171
+ const fileName = `${NamingUtils.kebabCase(name)}.controller.ts`;
1172
+ let filePath;
1173
+ let serviceImport = "";
1174
+ if (this.structureType === "feature") {
1175
+ filePath = path11.join(this.getBasePath("controllers", options), fileName);
1176
+ serviceImport = `import { ${NamingUtils.pascalCase(name)}Service } from './${NamingUtils.kebabCase(name)}.service';`;
1177
+ } else {
1178
+ filePath = path11.join(this.getBasePath("controllers", options), fileName);
1179
+ serviceImport = `import { ${NamingUtils.pascalCase(name)}Service } from '../services/${NamingUtils.kebabCase(name)}.service';`;
1180
+ }
1181
+ const content = `import { Controller, Get, Post, Put, Delete, Body, Param, Autowired } from 'fragment-ts';
1182
+ ${serviceImport}
1183
+
1184
+ @Controller('/${NamingUtils.kebabCase(name)}')
1185
+ export class ${className} {
1186
+ // @Autowired()
1187
+ // private ${NamingUtils.camelCase(name)}Service!: ${NamingUtils.pascalCase(name)}Service;
1188
+
1189
+ @Get()
1190
+ findAll() {
1191
+ return [];
1192
+ }
1193
+
1194
+ @Get('/:id')
1195
+ findOne(@Param('id') id: string) {
1196
+ return { id };
1197
+ }
1198
+
1199
+ @Post()
1200
+ create(@Body() body: any) {
1201
+ return body;
1202
+ }
1203
+
1204
+ @Put('/:id')
1205
+ update(@Param('id') id: string, @Body() body: any) {
1206
+ return { id, ...body };
1207
+ }
1208
+
1209
+ @Delete('/:id')
1210
+ delete(@Param('id') id: string) {
1211
+ return { deleted: true, id };
1212
+ }
1213
+ }
1214
+ `;
1215
+ await fs7.ensureDir(path11.dirname(filePath));
1216
+ await fs7.writeFile(filePath, content);
1217
+ }
1218
+ async generateService(name, withRepository, options) {
1219
+ const className = `${NamingUtils.pascalCase(name)}Service`;
1220
+ const fileName = `${NamingUtils.kebabCase(name)}.service.ts`;
1221
+ let filePath;
1222
+ let imports = "import { Service, Autowired } from 'fragment-ts';\n";
1223
+ let repositoryProp = "";
1224
+ filePath = path11.join(this.getBasePath("services", options), fileName);
1225
+ if (withRepository) {
1226
+ const repoName = `${NamingUtils.pascalCase(name)}Repository`;
1227
+ let repoPath;
1228
+ if (this.structureType === "module") {
1229
+ repoPath = `../repositories/${NamingUtils.kebabCase(name)}.repository`;
1230
+ } else if (this.structureType === "feature") {
1231
+ repoPath = `./${NamingUtils.kebabCase(name)}.repository`;
1232
+ } else {
1233
+ repoPath = `../repositories/${NamingUtils.kebabCase(name)}.repository`;
1234
+ }
1235
+ imports += `import { ${repoName} } from '${repoPath}';
1236
+ `;
1237
+ repositoryProp = `
1238
+ @Autowired()
1239
+ private ${NamingUtils.camelCase(name)}Repository!: ${repoName};
1240
+ `;
1241
+ }
1242
+ const content = `${imports}
1243
+ @Service()
1244
+ export class ${className} {${repositoryProp}
1245
+ async findAll() {
1246
+ return [];
1247
+ }
1248
+
1249
+ async findOne(id: string) {
1250
+ return { id };
1251
+ }
1252
+
1253
+ async create(data: any) {
1254
+ return data;
1255
+ }
1256
+
1257
+ async update(id: string, data: any) {
1258
+ return { id, ...data };
1259
+ }
1260
+
1261
+ async delete(id: string) {
1262
+ return { deleted: true, id };
1263
+ }
1264
+ }
1265
+ `;
1266
+ await fs7.ensureDir(path11.dirname(filePath));
1267
+ await fs7.writeFile(filePath, content);
1268
+ if (withRepository) {
1269
+ await this.generateRepository(name, options);
1270
+ }
1271
+ }
1272
+ async generateResource(name, options) {
1273
+ await this.generateController(name, options);
1274
+ await this.generateService(name, true, options);
1275
+ await this.generateEntity(name);
1276
+ await this.generateDto(name);
1277
+ }
1278
+ async generateModule(name) {
1279
+ const srcDir = TsConfigUtils.getRootDir();
1280
+ const modulePath = path11.join(
1281
+ srcDir,
1282
+ "modules",
1283
+ NamingUtils.kebabCase(name)
1284
+ );
1285
+ await fs7.ensureDir(path11.join(modulePath, "controllers"));
1286
+ await fs7.ensureDir(path11.join(modulePath, "services"));
1287
+ const controllerContent = `import { Controller, Get, Autowired } from 'fragment-ts';
1288
+ import { ${NamingUtils.pascalCase(name)}Service } from '../services/${NamingUtils.kebabCase(name)}.service';
1289
+
1290
+ @Controller('/${NamingUtils.kebabCase(name)}')
1291
+ export class ${NamingUtils.pascalCase(name)}Controller {
1292
+ @Autowired()
1293
+ private ${NamingUtils.camelCase(name)}Service!: ${NamingUtils.pascalCase(name)}Service;
1294
+
1295
+ @Get()
1296
+ findAll() {
1297
+ return this.${NamingUtils.camelCase(name)}Service.findAll();
1298
+ }
1299
+ }
1300
+ `;
1301
+ const serviceContent = `import { Service } from 'fragment-ts';
1302
+
1303
+ @Service()
1304
+ export class ${NamingUtils.pascalCase(name)}Service {
1305
+ async findAll() {
1306
+ return [];
1307
+ }
1308
+ }
1309
+ `;
1310
+ await fs7.writeFile(
1311
+ path11.join(
1312
+ modulePath,
1313
+ "controllers",
1314
+ `${NamingUtils.kebabCase(name)}.controller.ts`
1315
+ ),
1316
+ controllerContent
1317
+ );
1318
+ await fs7.writeFile(
1319
+ path11.join(
1320
+ modulePath,
1321
+ "services",
1322
+ `${NamingUtils.kebabCase(name)}.service.ts`
1323
+ ),
1324
+ serviceContent
1325
+ );
1326
+ }
1327
+ async generateFeature(name) {
1328
+ const srcDir = TsConfigUtils.getRootDir();
1329
+ const featurePath = path11.join(
1330
+ srcDir,
1331
+ "features",
1332
+ NamingUtils.kebabCase(name)
1333
+ );
1334
+ await fs7.ensureDir(featurePath);
1335
+ const controllerContent = `import { Controller, Get, Autowired } from 'fragment-ts';
1336
+ import { ${NamingUtils.pascalCase(name)}Service } from './${NamingUtils.kebabCase(name)}.service';
1337
+
1338
+ @Controller('/${NamingUtils.kebabCase(name)}')
1339
+ export class ${NamingUtils.pascalCase(name)}Controller {
1340
+ @Autowired()
1341
+ private ${NamingUtils.camelCase(name)}Service!: ${NamingUtils.pascalCase(name)}Service;
1342
+
1343
+ @Get()
1344
+ findAll() {
1345
+ return this.${NamingUtils.camelCase(name)}Service.findAll();
1346
+ }
1347
+ }
1348
+ `;
1349
+ const serviceContent = `import { Service } from 'fragment-ts';
1350
+
1351
+ @Service()
1352
+ export class ${NamingUtils.pascalCase(name)}Service {
1353
+ async findAll() {
1354
+ return [];
1355
+ }
1356
+ }
1357
+ `;
1358
+ await fs7.writeFile(
1359
+ path11.join(featurePath, `${NamingUtils.kebabCase(name)}.controller.ts`),
1360
+ controllerContent
1361
+ );
1362
+ await fs7.writeFile(
1363
+ path11.join(featurePath, `${NamingUtils.kebabCase(name)}.service.ts`),
1364
+ serviceContent
1365
+ );
1366
+ }
1367
+ async generateEntity(name) {
1368
+ const className = NamingUtils.pascalCase(name);
1369
+ const fileName = `${NamingUtils.kebabCase(name)}.entity.ts`;
1370
+ const srcDir = TsConfigUtils.getRootDir();
1371
+ const filePath = path11.join(srcDir, "entities", fileName);
1372
+ const content = `import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'fragment-ts';
1373
+
1374
+ @Entity()
1375
+ export class ${className} {
1376
+ @PrimaryGeneratedColumn()
1377
+ id: number;
1378
+
1379
+ @Column()
1380
+ name: string;
1381
+
1382
+ @CreateDateColumn()
1383
+ createdAt: Date;
1384
+
1385
+ @UpdateDateColumn()
1386
+ updatedAt: Date;
1387
+ }
1388
+ `;
1389
+ await fs7.ensureDir(path11.dirname(filePath));
1390
+ await fs7.writeFile(filePath, content);
1391
+ }
1392
+ async generateDto(name) {
1393
+ const className = `Create${NamingUtils.pascalCase(name)}Dto`;
1394
+ const fileName = `create-${NamingUtils.kebabCase(name)}.dto.ts`;
1395
+ const srcDir = TsConfigUtils.getRootDir();
1396
+ const filePath = path11.join(srcDir, "dto", fileName);
1397
+ const content = `export class ${className} {
1398
+ name: string;
1399
+ }
1400
+ `;
1401
+ await fs7.ensureDir(path11.dirname(filePath));
1402
+ await fs7.writeFile(filePath, content);
1403
+ }
1404
+ async generateRepository(name, options) {
1405
+ const className = `${NamingUtils.pascalCase(name)}Repository`;
1406
+ const entityName = NamingUtils.pascalCase(name);
1407
+ const fileName = `${NamingUtils.kebabCase(name)}.repository.ts`;
1408
+ let filePath;
1409
+ let entityImport;
1410
+ if (this.structureType === "module") {
1411
+ filePath = path11.join(this.getBasePath("repositories", options), fileName);
1412
+ entityImport = `import { ${entityName} } from '../../../entities/${NamingUtils.kebabCase(name)}.entity';`;
1413
+ } else if (this.structureType === "feature") {
1414
+ filePath = path11.join(this.getBasePath("repositories", options), fileName);
1415
+ entityImport = `import { ${entityName} } from '../../entities/${NamingUtils.kebabCase(name)}.entity';`;
1416
+ } else {
1417
+ filePath = path11.join(this.getBasePath("repositories", options), fileName);
1418
+ entityImport = `import { ${entityName} } from '../entities/${NamingUtils.kebabCase(name)}.entity';`;
1419
+ }
1420
+ const content = `import { Repository, InjectRepository } from 'fragment-ts';
1421
+ ${entityImport}
1422
+ import { Repository as TypeOrmRepository } from 'fragment-ts';
1423
+
1424
+ @Repository()
1425
+ export class ${className} {
1426
+ @InjectRepository(${entityName})
1427
+ private repo!: TypeOrmRepository<${entityName}>;
1428
+
1429
+ async findAll(): Promise<${entityName}[]> {
1430
+ return this.repo.find();
1431
+ }
1432
+
1433
+ async findById(id: number): Promise<${entityName} | null> {
1434
+ return this.repo.findOneBy({ id });
1435
+ }
1436
+
1437
+ async create(data: Partial<${entityName}>): Promise<${entityName}> {
1438
+ const entity = this.repo.create(data);
1439
+ return this.repo.save(entity);
1440
+ }
1441
+
1442
+ async update(id: number, data: Partial<${entityName}>): Promise<${entityName} | null> {
1443
+ await this.repo.update(id, data);
1444
+ return this.findById(id);
1445
+ }
1446
+
1447
+ async delete(id: number): Promise<boolean> {
1448
+ const result = await this.repo.delete(id);
1449
+ return result.affected > 0;
1450
+ }
1451
+ }
1452
+ `;
1453
+ await fs7.ensureDir(path11.dirname(filePath));
1454
+ await fs7.writeFile(filePath, content);
1455
+ }
1456
+ async generateMiddleware(name) {
1457
+ const className = `${NamingUtils.pascalCase(name)}Middleware`;
1458
+ const fileName = `${NamingUtils.kebabCase(name)}.middleware.ts`;
1459
+ const filePath = path11.join(this.getBasePath("middlewares"), fileName);
1460
+ const content = `import { Injectable, express } from 'fragment-ts';
1461
+
1462
+ @Injectable()
1463
+ export class ${className} {
1464
+ use(req: express.Request, res: express.Response, next: express.NextFunction): void {
1465
+ // Add your middleware logic here
1466
+ next();
1467
+ }
1468
+ }
1469
+ `;
1470
+ await fs7.ensureDir(path11.dirname(filePath));
1471
+ await fs7.writeFile(filePath, content);
1472
+ }
1473
+ async generateGuard(name) {
1474
+ const className = `${NamingUtils.pascalCase(name)}Guard`;
1475
+ const fileName = `${NamingUtils.kebabCase(name)}.guard.ts`;
1476
+ const filePath = path11.join(this.getBasePath("guards"), fileName);
1477
+ const content = `import { Injectable, express } from 'fragment-ts';
1478
+
1479
+ @Injectable()
1480
+ export class ${className} {
1481
+ canActivate(req: express.Request): boolean {
1482
+ // Add your authorization logic here
1483
+ // Return true to allow access, false to deny
1484
+ return true;
1485
+ }
1486
+ }
1487
+ `;
1488
+ await fs7.ensureDir(path11.dirname(filePath));
1489
+ await fs7.writeFile(filePath, content);
1490
+ }
1491
+ async generateInterceptor(name) {
1492
+ const className = `${NamingUtils.pascalCase(name)}Interceptor`;
1493
+ const fileName = `${NamingUtils.kebabCase(name)}.interceptor.ts`;
1494
+ const filePath = path11.join(this.getBasePath("interceptors"), fileName);
1495
+ const content = `import { Injectable, express } from 'fragment-ts';
1496
+
1497
+ @Injectable()
1498
+ export class ${className} {
1499
+ intercept(req: express.Request, res: express.Response, result: any): any {
1500
+ // Add your transformation logic here
1501
+ return result;
1502
+ }
1503
+ }
1504
+ `;
1505
+ await fs7.ensureDir(path11.dirname(filePath));
1506
+ await fs7.writeFile(filePath, content);
1507
+ }
1508
+ async generateFilter(name) {
1509
+ const className = `${NamingUtils.pascalCase(name)}ExceptionFilter`;
1510
+ const fileName = `${NamingUtils.kebabCase(name)}.exception.filter.ts`;
1511
+ const filePath = path11.join(this.getBasePath("filters"), fileName);
1512
+ const content = `import { Injectable, express } from 'fragment-ts';
1513
+
1514
+ @Injectable()
1515
+ export class ${className} {
1516
+ catch(exception: Error, req: express.Request, res: express.Response): void {
1517
+ // Add your error handling logic here
1518
+ res.status(500).json({
1519
+ statusCode: 500,
1520
+ error: 'Internal Server Error',
1521
+ message: exception.message || 'Something went wrong'
1522
+ });
1523
+ }
1524
+ }
1525
+ `;
1526
+ await fs7.ensureDir(path11.dirname(filePath));
1527
+ await fs7.writeFile(filePath, content);
1528
+ }
1529
+ async generateJob(name, options) {
1530
+ const className = `${NamingUtils.pascalCase(name)}Job`;
1531
+ const fileName = `${NamingUtils.kebabCase(name)}.job.ts`;
1532
+ const filePath = path11.join(this.getBasePath("jobs", options), fileName);
1533
+ const content = `import { Job, ScheduledJob } from 'fragment-ts';
1534
+ import type { JobContext } from 'fragment-ts';
1535
+
1536
+ @Job()
1537
+ export class ${className} extends ScheduledJob {
1538
+ protected async handle(payload: any, context: JobContext): Promise<void> {
1539
+ // Add your job logic here
1540
+ }
1541
+ }
1542
+ `;
1543
+ await fs7.ensureDir(path11.dirname(filePath));
1544
+ await fs7.writeFile(filePath, content);
1545
+ }
1546
+ async generateMail(name, options) {
1547
+ const className = `${NamingUtils.pascalCase(name)}Mailer`;
1548
+ const fileName = `${NamingUtils.kebabCase(name)}.mail.ts`;
1549
+ const filePath = path11.join(this.getBasePath("mail", options), fileName);
1550
+ const qualifier = NamingUtils.kebabCase(name);
1551
+ const content = `import { Mailer } from 'fragment-ts';
1552
+ import type { MailMessage, MailProvider, MailSendResult } from 'fragment-ts';
1553
+
1554
+ @Mailer({ name: '${qualifier}' })
1555
+ export class ${className} implements MailProvider {
1556
+ async send(message: MailMessage): Promise<MailSendResult | void> {
1557
+ // Implement provider send logic here
1558
+ return {
1559
+ id: 'example',
1560
+ accepted: Array.isArray(message.to) ? message.to.map(String) : [String(message.to)],
1561
+ rejected: [],
1562
+ };
1563
+ }
1564
+ }
1565
+ `;
1566
+ await fs7.ensureDir(path11.dirname(filePath));
1567
+ await fs7.writeFile(filePath, content);
1568
+ }
1569
+ async generateNotification(name, options) {
1570
+ const className = `${NamingUtils.pascalCase(name)}Notification`;
1571
+ const fileName = `${NamingUtils.kebabCase(name)}.notification.ts`;
1572
+ const filePath = path11.join(
1573
+ this.getBasePath("notifications", options),
1574
+ fileName
1575
+ );
1576
+ const content = `import type { MailMessage, MailNotification } from 'fragment-ts';
1577
+
1578
+ export class ${className} implements MailNotification {
1579
+ via(): string[] {
1580
+ return ['mail'];
1581
+ }
1582
+
1583
+ toMail(): MailMessage {
1584
+ return {
1585
+ subject: '${NamingUtils.pascalCase(name)} Notification',
1586
+ html: '<p>Notification content</p>'
1587
+ };
1588
+ }
1589
+ }
1590
+ `;
1591
+ await fs7.ensureDir(path11.dirname(filePath));
1592
+ await fs7.writeFile(filePath, content);
1593
+ }
1594
+ };
1595
+
1596
+ // src/platform/cli/web/commands/make/service.ts
1597
+ async function resolveTarget(options) {
1598
+ if (options.target) return options.target;
1599
+ const hasApi = import_node_fs4.default.existsSync(import_node_path8.default.join(process.cwd(), "fragment.json"));
1600
+ const hasWeb = import_node_fs4.default.existsSync(import_node_path8.default.join(process.cwd(), "fragment.web.json"));
1601
+ if (!(hasApi && hasWeb)) return "web";
1602
+ const inquirer = await import("inquirer");
1603
+ const answer = await inquirer.default.prompt([
1604
+ {
1605
+ type: "list",
1606
+ name: "target",
1607
+ message: "Both backend and frontend configs were found. Which service do you want to generate?",
1608
+ choices: [
1609
+ { name: "Frontend (@FragmentService) in src/web", value: "web" },
1610
+ { name: "Backend (@Service) in fragment-ts", value: "api" }
1611
+ ],
1612
+ default: "web"
1613
+ }
1614
+ ]);
1615
+ return answer.target;
1616
+ }
1617
+ async function runBackendService(name, options) {
1618
+ const structureType = ConfigUtils.getStructureType();
1619
+ const generator = new ComponentGenerator(structureType);
1620
+ await generator.generate("service", name, {
1621
+ module: options.module,
1622
+ feature: options.feature
1623
+ });
1624
+ }
1625
+ async function runMakeService(name, options = {}) {
1626
+ const target = await resolveTarget(options);
1627
+ if (target === "api") {
1628
+ await runBackendService(name, options);
1629
+ return;
1630
+ }
1631
+ const config2 = ensureWebConfig();
1632
+ const mode = options.structure || config2.structure;
1633
+ const domain = options.feature || options.module;
1634
+ const outDir = resolveOutputDir({ mode, kind: "service", name, domain });
1635
+ const fileName = `${toKebabCase(name)}.service.ts`;
1636
+ const className = `${toPascalCase(name)}Service`;
1637
+ const baseUrl = (options.baseUrl || `/${toKebabCase(name)}`).replace(/\/$/, "");
1638
+ const methods = (options.methods || "findAll,findById,create,update,delete").split(",").map((v) => v.trim()).filter(Boolean);
1639
+ const body = methods.map((m) => {
1640
+ switch (m) {
1641
+ case "findAll":
1642
+ return ` async findAll(): Promise<unknown> {
1643
+ return FragmentFetch.get('${baseUrl}');
1644
+ }`;
1645
+ case "findById":
1646
+ return ` async findById(id: string): Promise<unknown> {
1647
+ return FragmentFetch.get(\`${baseUrl}/\${id}\`);
1648
+ }`;
1649
+ case "create":
1650
+ return ` async create(payload: unknown): Promise<unknown> {
1651
+ return FragmentFetch.post('${baseUrl}', payload);
1652
+ }`;
1653
+ case "update":
1654
+ return ` async update(id: string, payload: unknown): Promise<unknown> {
1655
+ return FragmentFetch.put(\`${baseUrl}/\${id}\`, payload);
1656
+ }`;
1657
+ case "delete":
1658
+ return ` async delete(id: string): Promise<unknown> {
1659
+ return FragmentFetch.delete(\`${baseUrl}/\${id}\`);
1660
+ }`;
1661
+ default:
1662
+ return ` async ${m}(): Promise<unknown> {
1663
+ return FragmentFetch.get('${baseUrl}');
1664
+ }`;
1665
+ }
1666
+ }).join("\n\n");
1667
+ const content = `// Generated by: frg make:service ${name}
1668
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
1669
+ // fragment-web v0.1.0
1670
+ import { FragmentFetch, FragmentService } from 'fragment-web';
1671
+
1672
+ @FragmentService()
1673
+ export class ${className} {
1674
+ ${body}
1675
+ }
1676
+ `;
1677
+ writeFileIfAllowed(import_node_path8.default.join(process.cwd(), outDir, fileName), content, options);
1678
+ }
1679
+ function registerMakeService(program) {
1680
+ program.command("make:service <name>").description("Generate a service (frontend or backend when both contexts exist)").option("--base-url <path>", "Base URL path for FragmentFetch calls").option("--methods <list>", "CSV of service methods").option("--target <target>", "web|api").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) => {
1681
+ runMakeService(name, opts).catch((error) => {
1682
+ logger.error(error instanceof Error ? error.message : "make:service failed");
1683
+ process.exitCode = 1;
1684
+ });
1685
+ });
1686
+ }
1687
+
1688
+ // src/platform/cli/web/commands/make/guard.ts
1689
+ var import_node_path9 = __toESM(require("path"));
1690
+ async function runMakeGuard(name, options = {}) {
1691
+ const config2 = ensureWebConfig();
1692
+ const mode = options.structure || config2.structure;
1693
+ const outDir = resolveOutputDir({ mode, kind: "guard", name });
1694
+ const fileName = `${toKebabCase(name)}.guard.ts`;
1695
+ const className = `${toPascalCase(name)}Guard`;
1696
+ const redirect = options.redirect || "/login";
1697
+ const bodyByType = {
1698
+ auth: ` canActivate(): boolean {
1699
+ return Boolean(FragmentFetch.getToken());
1700
+ }`,
1701
+ role: ` canActivate(): boolean {
1702
+ // Replace with role claim checks from your auth provider
1703
+ return Boolean(FragmentFetch.getToken());
1704
+ }`,
1705
+ custom: ` canActivate(): boolean {
1706
+ return true;
1707
+ }`
1708
+ };
1709
+ const type = options.type || "custom";
1710
+ const content = `// Generated by: frg make:guard ${name}
1711
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
1712
+ // fragment-web v0.1.0
1713
+ import { FragmentFetch } from 'fragment-web';
1714
+
1715
+ export class ${className} {
1716
+ ${bodyByType[type]}
1717
+
1718
+ redirectTo(): string {
1719
+ return '${redirect}';
1720
+ }
1721
+ }
1722
+ `;
1723
+ writeFileIfAllowed(import_node_path9.default.join(process.cwd(), outDir, fileName), content, options);
1724
+ }
1725
+ function registerMakeGuard(program) {
1726
+ program.command("make:guard <name>").description("Generate a frontend guard").option("--type <type>", "auth|role|custom").option("--redirect <path>", "Redirect path").option("--structure <mode>", "Override structure mode").option("--force", "Overwrite existing files").option("--dry-run", "Preview changes").action((name, opts) => {
1727
+ runMakeGuard(name, opts).catch((error) => {
1728
+ logger.error(error instanceof Error ? error.message : "make:guard failed");
1729
+ process.exitCode = 1;
1730
+ });
1731
+ });
1732
+ }
1733
+
1734
+ // src/platform/cli/web/commands/make/layout.ts
1735
+ var import_node_path10 = __toESM(require("path"));
1736
+ async function runMakeLayout(name, options = {}) {
1737
+ const config2 = ensureWebConfig();
1738
+ const mode = options.structure || config2.structure;
1739
+ const outDir = resolveOutputDir({ mode, kind: "layout", name });
1740
+ const fileName = `${toKebabCase(name)}.layout.tsx`;
1741
+ const className = `${toPascalCase(name)}Layout`;
1742
+ const slots = (options.slots || "header,footer").split(",").map((v) => v.trim()).filter(Boolean);
1743
+ const slotProps = slots.map((s) => `${s}?: React.ReactNode;`).join("\n ");
1744
+ const slotUsage = slots.map((s) => `{${s}}`).join("\n ");
1745
+ const content = `// Generated by: frg make:layout ${name}
1746
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
1747
+ // fragment-web v0.1.0
1748
+ import React from 'react';
1749
+
1750
+ interface ${className}Props {
1751
+ children?: React.ReactNode;
1752
+ ${slotProps}
1753
+ }
1754
+
1755
+ export function ${className}({ children, ${slots.join(", ")} }: ${className}Props): JSX.Element {
1756
+ return <section>
1757
+ ${slotUsage}
1758
+ {children}
1759
+ </section>;
1760
+ }
1761
+ `;
1762
+ writeFileIfAllowed(import_node_path10.default.join(process.cwd(), outDir, fileName), content, options);
1763
+ }
1764
+ function registerMakeLayout(program) {
1765
+ program.command("make:layout <name>").description("Generate a frontend layout").option("--slots <slots>", "CSV slot names").option("--structure <mode>", "Override structure mode").option("--force", "Overwrite existing files").option("--dry-run", "Preview changes").action((name, opts) => {
1766
+ runMakeLayout(name, opts).catch((error) => {
1767
+ logger.error(error instanceof Error ? error.message : "make:layout failed");
1768
+ process.exitCode = 1;
1769
+ });
1770
+ });
1771
+ }
1772
+
1773
+ // src/platform/cli/web/commands/make/store.ts
1774
+ var import_node_path11 = __toESM(require("path"));
1775
+ function parseFields(fields) {
1776
+ const raw = (fields || "loading:boolean=false").split(",").map((v) => v.trim()).filter(Boolean);
1777
+ return raw.map((item) => {
1778
+ const [left, defaultValue] = item.split("=").map((v) => v.trim());
1779
+ const [key, type] = left.split(":").map((v) => v.trim());
1780
+ return {
1781
+ key,
1782
+ type: type || "unknown",
1783
+ initial: defaultValue ?? (type === "number" ? "0" : type === "boolean" ? "false" : type === "string" ? "''" : "null")
1784
+ };
1785
+ });
1786
+ }
1787
+ async function runMakeStore(name, options = {}) {
1788
+ const config2 = ensureWebConfig();
1789
+ const mode = options.structure || config2.structure;
1790
+ const adapter = options.adapter || config2.app.stateAdapter;
1791
+ const domain = options.feature || options.module;
1792
+ const outDir = resolveOutputDir({ mode, kind: "store", name, domain });
1793
+ const fileName = `${toKebabCase(name)}.store.ts`;
1794
+ const className = toPascalCase(name);
1795
+ const fields = parseFields(options.fields);
1796
+ const actions = (options.actions || "setState").split(",").map((v) => v.trim()).filter(Boolean);
1797
+ const contentByAdapter = {
1798
+ zustand: `// Generated by: frg make:store ${name}
1799
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
1800
+ // fragment-web v0.1.0
1801
+ import { create } from 'zustand';
1802
+
1803
+ interface ${className}State {
1804
+ ${fields.map((f) => ` ${f.key}: ${f.type};`).join("\n")}
1805
+ ${actions.map((a) => ` ${a}: (payload?: unknown) => void;`).join("\n")}
1806
+ }
1807
+
1808
+ export const use${className}Store = create<${className}State>((set) => ({
1809
+ ${fields.map((f) => ` ${f.key}: ${f.initial},`).join("\n")}
1810
+ ${actions.map((a) => ` ${a}: (payload) => set((s) => ({ ...s })),`).join("\n")}
1811
+ }));
1812
+ `,
1813
+ jotai: `// Generated by: frg make:store ${name}
1814
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
1815
+ // fragment-web v0.1.0
1816
+ import { atom } from 'jotai';
1817
+
1818
+ export const ${toKebabCase(name).replace(/-/g, "_")}Store = atom({
1819
+ ${fields.map((f) => ` ${f.key}: ${f.initial},`).join("\n")}
1820
+ });
1821
+ `,
1822
+ redux: `// Generated by: frg make:store ${name}
1823
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
1824
+ // fragment-web v0.1.0
1825
+ export interface ${className}State {
1826
+ ${fields.map((f) => ` ${f.key}: ${f.type};`).join("\n")}
1827
+ }
1828
+
1829
+ export const initial${className}State: ${className}State = {
1830
+ ${fields.map((f) => ` ${f.key}: ${f.initial},`).join("\n")}
1831
+ };
1832
+ `,
1833
+ mobx: `// Generated by: frg make:store ${name}
1834
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
1835
+ // fragment-web v0.1.0
1836
+ import { makeAutoObservable } from 'mobx';
1837
+
1838
+ export class ${className}Store {
1839
+ ${fields.map((f) => ` ${f.key}: ${f.type} = ${f.initial};`).join("\n")}
1840
+
1841
+ constructor() {
1842
+ makeAutoObservable(this);
1843
+ }
1844
+
1845
+ ${actions.map((a) => ` ${a}(payload?: unknown): void {}`).join("\n\n")}
1846
+ }
1847
+ `
1848
+ };
1849
+ writeFileIfAllowed(import_node_path11.default.join(process.cwd(), outDir, fileName), contentByAdapter[adapter], options);
1850
+ }
1851
+ function registerMakeStore(program) {
1852
+ program.command("make:store <name>").description("Generate a frontend store").option("--adapter <adapter>", "zustand|jotai|redux|mobx").option("--fields <fields>", "CSV key:type[=default] list").option("--actions <actions>", "CSV action names").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) => {
1853
+ runMakeStore(name, opts).catch((error) => {
1854
+ logger.error(error instanceof Error ? error.message : "make:store failed");
1855
+ process.exitCode = 1;
1856
+ });
1857
+ });
1858
+ }
1859
+
1860
+ // src/platform/cli/web/commands/make/resource.ts
1861
+ async function runMakeResource(name, options = {}) {
1862
+ await runMakeComponent(`${name}-card`, options);
1863
+ await runMakePage(`${name}-list`, { ...options, path: options.path || `/${name}` });
1864
+ await runMakeService(name, options);
1865
+ await runMakeStore(name, options);
1866
+ }
1867
+ function registerMakeResource(program) {
1868
+ program.command("make:resource <name>").description("Generate page + service + store + component in one shot").option("--path <path>", "List page route path").option("--methods <list>", "Service methods CSV").option("--adapter <adapter>", "Store adapter").option("--fields <fields>", "Store fields").option("--actions <actions>", "Store actions").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) => {
1869
+ runMakeResource(name, opts).catch((error) => {
1870
+ logger.error(error instanceof Error ? error.message : "make:resource failed");
1871
+ process.exitCode = 1;
1872
+ });
1873
+ });
1874
+ }
1875
+
1876
+ // src/platform/cli/web/commands/create/module.ts
1877
+ var import_node_path12 = __toESM(require("path"));
1878
+ async function runCreateModule(name, options = {}) {
1879
+ const config2 = ensureWebConfig();
1880
+ const mode = options.structure || config2.structure;
1881
+ if (mode !== "module") {
1882
+ throw new Error("create:module requires structure mode 'module' (or pass --structure module)");
1883
+ }
1884
+ await runMakePage(`${name}-list`, { ...options, structure: mode, module: name });
1885
+ await runMakePage(`${name}-detail`, { ...options, structure: mode, module: name });
1886
+ await runMakeService(name, { ...options, structure: mode, module: name });
1887
+ await runMakeStore(name, { ...options, structure: mode, module: name });
1888
+ await runMakeComponent(`${name}-card`, { ...options, structure: mode, module: name });
1889
+ writeFileIfAllowed(
1890
+ import_node_path12.default.join(process.cwd(), "src/modules", toKebabCase(name), "index.ts"),
1891
+ `// Generated by: frg create:module ${name}
1892
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
1893
+ // fragment-web v0.1.0
1894
+ export * from './${toKebabCase(name)}-list.page';
1895
+ export * from './${toKebabCase(name)}-detail.page';
1896
+ export * from './${toKebabCase(name)}.service';
1897
+ export * from './${toKebabCase(name)}.store';
1898
+ export * from './components/${toKebabCase(name)}-card.component';
1899
+ `,
1900
+ options
1901
+ );
1902
+ }
1903
+ function registerCreateModule(program) {
1904
+ 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) => {
1905
+ runCreateModule(name, opts).catch((error) => {
1906
+ logger.error(error instanceof Error ? error.message : "create:module failed");
1907
+ process.exitCode = 1;
1908
+ });
1909
+ });
1910
+ }
1911
+
1912
+ // src/platform/cli/web/commands/create/feature.ts
1913
+ var import_node_path13 = __toESM(require("path"));
1914
+ async function runCreateFeature(name, options = {}) {
1915
+ const config2 = ensureWebConfig();
1916
+ const mode = options.structure || config2.structure;
1917
+ if (mode !== "feature") {
1918
+ throw new Error("create:feature requires structure mode 'feature' (or pass --structure feature)");
1919
+ }
1920
+ await runMakePage(`${name}-list`, { ...options, structure: mode, feature: name });
1921
+ await runMakeService(name, { ...options, structure: mode, feature: name });
1922
+ await runMakeStore(name, { ...options, structure: mode, feature: name });
1923
+ await runMakeComponent(`${name}-card`, { ...options, structure: mode, feature: name });
1924
+ writeFileIfAllowed(
1925
+ import_node_path13.default.join(process.cwd(), "src/features", toKebabCase(name), "index.ts"),
1926
+ `// Generated by: frg create:feature ${name}
1927
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
1928
+ // fragment-web v0.1.0
1929
+ export * from './pages/${toKebabCase(name)}-list.page';
1930
+ export * from './services/${toKebabCase(name)}.service';
1931
+ export * from './stores/${toKebabCase(name)}.store';
1932
+ export * from './components/${toKebabCase(name)}-card.component';
1933
+ `,
1934
+ options
1935
+ );
1936
+ }
1937
+ function registerCreateFeature(program) {
1938
+ 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) => {
1939
+ runCreateFeature(name, opts).catch((error) => {
1940
+ logger.error(error instanceof Error ? error.message : "create:feature failed");
1941
+ process.exitCode = 1;
1942
+ });
1943
+ });
1944
+ }
1945
+
1946
+ // src/platform/cli/web/commands/create/mvvm.ts
1947
+ var import_node_path14 = __toESM(require("path"));
1948
+ async function runCreateMvvm(name, options = {}) {
1949
+ const config2 = ensureWebConfig();
1950
+ const mode = options.structure || config2.structure;
1951
+ if (mode !== "mvvm") {
1952
+ throw new Error("create:mvvm requires structure mode 'mvvm' (or pass --structure mvvm)");
1953
+ }
1954
+ const dom = toKebabCase(name);
1955
+ const className = toPascalCase(name);
1956
+ await runMakePage(`${name}-list`, { ...options, structure: mode, feature: name });
1957
+ await runMakeComponent(`${name}-card`, { ...options, structure: mode, feature: name });
1958
+ writeFileIfAllowed(
1959
+ import_node_path14.default.join(process.cwd(), "src/app", dom, "viewmodel", `${dom}.vm.ts`),
1960
+ `// Generated by: frg create:mvvm ${name}
1961
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
1962
+ // fragment-web v0.1.0
1963
+ import { FragmentFetch, FragmentService } from 'fragment-web';
1964
+
1965
+ @FragmentService()
1966
+ export class ${className}Vm {
1967
+ async load(): Promise<unknown> {
1968
+ return FragmentFetch.get('/${dom}');
1969
+ }
1970
+ }
1971
+ `,
1972
+ options
1973
+ );
1974
+ writeFileIfAllowed(
1975
+ import_node_path14.default.join(process.cwd(), "src/app", dom, "model", `${dom}.model.ts`),
1976
+ `// Generated by: frg create:mvvm ${name}
1977
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
1978
+ // fragment-web v0.1.0
1979
+ export interface ${className}Model {
1980
+ id: string;
1981
+ }
1982
+ `,
1983
+ options
1984
+ );
1985
+ writeFileIfAllowed(
1986
+ import_node_path14.default.join(process.cwd(), "src/app", dom, "index.ts"),
1987
+ `// Generated by: frg create:mvvm ${name}
1988
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
1989
+ // fragment-web v0.1.0
1990
+ export * from './model/${dom}.model';
1991
+ export * from './viewmodel/${dom}.vm';
1992
+ export * from './view/${dom}-list.page';
1993
+ export * from './view/${dom}-card.component';
1994
+ `,
1995
+ options
1996
+ );
1997
+ }
1998
+ function registerCreateMvvm(program) {
1999
+ 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) => {
2000
+ runCreateMvvm(name, opts).catch((error) => {
2001
+ logger.error(error instanceof Error ? error.message : "create:mvvm failed");
2002
+ process.exitCode = 1;
2003
+ });
2004
+ });
2005
+ }
2006
+
2007
+ // src/platform/cli/web/index.ts
2008
+ function removeCommandIfExists(registry, name) {
2009
+ const commands = registry.commands;
2010
+ if (!commands) return;
2011
+ const idx = commands.findIndex((cmd) => cmd.name() === name);
2012
+ if (idx >= 0) {
2013
+ commands.splice(idx, 1);
2014
+ }
2015
+ }
2016
+ function registerWebCommands(registry) {
2017
+ removeCommandIfExists(registry, "make:service");
2018
+ removeCommandIfExists(registry, "make:guard");
2019
+ removeCommandIfExists(registry, "make:resource");
2020
+ registerWebSync(registry);
2021
+ registerMakeComponent(registry);
2022
+ registerMakePage(registry);
2023
+ registerMakeService(registry);
2024
+ registerMakeGuard(registry);
2025
+ registerMakeLayout(registry);
2026
+ registerMakeStore(registry);
2027
+ registerMakeResource(registry);
2028
+ registerCreateModule(registry);
2029
+ registerCreateFeature(registry);
2030
+ registerCreateMvvm(registry);
2031
+ }
2032
+ // Annotate the CommonJS export names for ESM import in node:
2033
+ 0 && (module.exports = {
2034
+ registerWebCommands
2035
+ });