@cicore/cli 1.0.0

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 (337) hide show
  1. package/bin/ci.js +13 -0
  2. package/dist/commands/addon/api-actions.d.ts +45 -0
  3. package/dist/commands/addon/api-actions.d.ts.map +1 -0
  4. package/dist/commands/addon/api-actions.js +281 -0
  5. package/dist/commands/addon/api-actions.js.map +1 -0
  6. package/dist/commands/addon/build.d.ts +11 -0
  7. package/dist/commands/addon/build.d.ts.map +1 -0
  8. package/dist/commands/addon/build.js +182 -0
  9. package/dist/commands/addon/build.js.map +1 -0
  10. package/dist/commands/addon/create.d.ts +11 -0
  11. package/dist/commands/addon/create.d.ts.map +1 -0
  12. package/dist/commands/addon/create.js +1186 -0
  13. package/dist/commands/addon/create.js.map +1 -0
  14. package/dist/commands/addon/delete.d.ts +13 -0
  15. package/dist/commands/addon/delete.d.ts.map +1 -0
  16. package/dist/commands/addon/delete.js +83 -0
  17. package/dist/commands/addon/delete.js.map +1 -0
  18. package/dist/commands/addon/deploy.d.ts +27 -0
  19. package/dist/commands/addon/deploy.d.ts.map +1 -0
  20. package/dist/commands/addon/deploy.js +459 -0
  21. package/dist/commands/addon/deploy.js.map +1 -0
  22. package/dist/commands/addon/dev-deploy.d.ts +31 -0
  23. package/dist/commands/addon/dev-deploy.d.ts.map +1 -0
  24. package/dist/commands/addon/dev-deploy.js +128 -0
  25. package/dist/commands/addon/dev-deploy.js.map +1 -0
  26. package/dist/commands/addon/dev.d.ts +36 -0
  27. package/dist/commands/addon/dev.d.ts.map +1 -0
  28. package/dist/commands/addon/dev.js +323 -0
  29. package/dist/commands/addon/dev.js.map +1 -0
  30. package/dist/commands/addon/extract-classes.d.ts +23 -0
  31. package/dist/commands/addon/extract-classes.d.ts.map +1 -0
  32. package/dist/commands/addon/extract-classes.js +281 -0
  33. package/dist/commands/addon/extract-classes.js.map +1 -0
  34. package/dist/commands/addon/generate-safelist.d.ts +24 -0
  35. package/dist/commands/addon/generate-safelist.d.ts.map +1 -0
  36. package/dist/commands/addon/generate-safelist.js +276 -0
  37. package/dist/commands/addon/generate-safelist.js.map +1 -0
  38. package/dist/commands/addon/index.d.ts +19 -0
  39. package/dist/commands/addon/index.d.ts.map +1 -0
  40. package/dist/commands/addon/index.js +296 -0
  41. package/dist/commands/addon/index.js.map +1 -0
  42. package/dist/commands/addon/init-repo.d.ts +25 -0
  43. package/dist/commands/addon/init-repo.d.ts.map +1 -0
  44. package/dist/commands/addon/init-repo.js +171 -0
  45. package/dist/commands/addon/init-repo.js.map +1 -0
  46. package/dist/commands/addon/install.d.ts +23 -0
  47. package/dist/commands/addon/install.d.ts.map +1 -0
  48. package/dist/commands/addon/install.js +84 -0
  49. package/dist/commands/addon/install.js.map +1 -0
  50. package/dist/commands/addon/list.d.ts +10 -0
  51. package/dist/commands/addon/list.d.ts.map +1 -0
  52. package/dist/commands/addon/list.js +102 -0
  53. package/dist/commands/addon/list.js.map +1 -0
  54. package/dist/commands/addon/manifest-refresh.d.ts +17 -0
  55. package/dist/commands/addon/manifest-refresh.d.ts.map +1 -0
  56. package/dist/commands/addon/manifest-refresh.js +48 -0
  57. package/dist/commands/addon/manifest-refresh.js.map +1 -0
  58. package/dist/commands/addon/migrate.d.ts +40 -0
  59. package/dist/commands/addon/migrate.d.ts.map +1 -0
  60. package/dist/commands/addon/migrate.js +236 -0
  61. package/dist/commands/addon/migrate.js.map +1 -0
  62. package/dist/commands/addon/publish.d.ts +33 -0
  63. package/dist/commands/addon/publish.d.ts.map +1 -0
  64. package/dist/commands/addon/publish.js +236 -0
  65. package/dist/commands/addon/publish.js.map +1 -0
  66. package/dist/commands/addon/scaffold-quality.d.ts +21 -0
  67. package/dist/commands/addon/scaffold-quality.d.ts.map +1 -0
  68. package/dist/commands/addon/scaffold-quality.js +90 -0
  69. package/dist/commands/addon/scaffold-quality.js.map +1 -0
  70. package/dist/commands/addon/sign.d.ts +9 -0
  71. package/dist/commands/addon/sign.d.ts.map +1 -0
  72. package/dist/commands/addon/sign.js +83 -0
  73. package/dist/commands/addon/sign.js.map +1 -0
  74. package/dist/commands/addon/toggle.d.ts +6 -0
  75. package/dist/commands/addon/toggle.d.ts.map +1 -0
  76. package/dist/commands/addon/toggle.js +46 -0
  77. package/dist/commands/addon/toggle.js.map +1 -0
  78. package/dist/commands/agent/index.d.ts +34 -0
  79. package/dist/commands/agent/index.d.ts.map +1 -0
  80. package/dist/commands/agent/index.js +564 -0
  81. package/dist/commands/agent/index.js.map +1 -0
  82. package/dist/commands/brand/index.d.ts +54 -0
  83. package/dist/commands/brand/index.d.ts.map +1 -0
  84. package/dist/commands/brand/index.js +367 -0
  85. package/dist/commands/brand/index.js.map +1 -0
  86. package/dist/commands/build/index.d.ts +53 -0
  87. package/dist/commands/build/index.d.ts.map +1 -0
  88. package/dist/commands/build/index.js +726 -0
  89. package/dist/commands/build/index.js.map +1 -0
  90. package/dist/commands/cache/flush-local.d.ts +31 -0
  91. package/dist/commands/cache/flush-local.d.ts.map +1 -0
  92. package/dist/commands/cache/flush-local.js +161 -0
  93. package/dist/commands/cache/flush-local.js.map +1 -0
  94. package/dist/commands/cache/index.d.ts +14 -0
  95. package/dist/commands/cache/index.d.ts.map +1 -0
  96. package/dist/commands/cache/index.js +453 -0
  97. package/dist/commands/cache/index.js.map +1 -0
  98. package/dist/commands/check/index.d.ts +8 -0
  99. package/dist/commands/check/index.d.ts.map +1 -0
  100. package/dist/commands/check/index.js +1316 -0
  101. package/dist/commands/check/index.js.map +1 -0
  102. package/dist/commands/cloudflare/index.d.ts +8 -0
  103. package/dist/commands/cloudflare/index.d.ts.map +1 -0
  104. package/dist/commands/cloudflare/index.js +453 -0
  105. package/dist/commands/cloudflare/index.js.map +1 -0
  106. package/dist/commands/core/create.d.ts +12 -0
  107. package/dist/commands/core/create.d.ts.map +1 -0
  108. package/dist/commands/core/create.js +206 -0
  109. package/dist/commands/core/create.js.map +1 -0
  110. package/dist/commands/core/delete.d.ts +11 -0
  111. package/dist/commands/core/delete.d.ts.map +1 -0
  112. package/dist/commands/core/delete.js +64 -0
  113. package/dist/commands/core/delete.js.map +1 -0
  114. package/dist/commands/core/env.d.ts +12 -0
  115. package/dist/commands/core/env.d.ts.map +1 -0
  116. package/dist/commands/core/env.js +95 -0
  117. package/dist/commands/core/env.js.map +1 -0
  118. package/dist/commands/core/health.d.ts +6 -0
  119. package/dist/commands/core/health.d.ts.map +1 -0
  120. package/dist/commands/core/health.js +215 -0
  121. package/dist/commands/core/health.js.map +1 -0
  122. package/dist/commands/core/index.d.ts +15 -0
  123. package/dist/commands/core/index.d.ts.map +1 -0
  124. package/dist/commands/core/index.js +86 -0
  125. package/dist/commands/core/index.js.map +1 -0
  126. package/dist/commands/core/list.d.ts +11 -0
  127. package/dist/commands/core/list.d.ts.map +1 -0
  128. package/dist/commands/core/list.js +58 -0
  129. package/dist/commands/core/list.js.map +1 -0
  130. package/dist/commands/core/rebuild.d.ts +13 -0
  131. package/dist/commands/core/rebuild.d.ts.map +1 -0
  132. package/dist/commands/core/rebuild.js +119 -0
  133. package/dist/commands/core/rebuild.js.map +1 -0
  134. package/dist/commands/db/index.d.ts +23 -0
  135. package/dist/commands/db/index.d.ts.map +1 -0
  136. package/dist/commands/db/index.js +355 -0
  137. package/dist/commands/db/index.js.map +1 -0
  138. package/dist/commands/db/promote-silo.d.ts +320 -0
  139. package/dist/commands/db/promote-silo.d.ts.map +1 -0
  140. package/dist/commands/db/promote-silo.js +930 -0
  141. package/dist/commands/db/promote-silo.js.map +1 -0
  142. package/dist/commands/db/relocate.d.ts +41 -0
  143. package/dist/commands/db/relocate.d.ts.map +1 -0
  144. package/dist/commands/db/relocate.js +482 -0
  145. package/dist/commands/db/relocate.js.map +1 -0
  146. package/dist/commands/db/rollback-silo.d.ts +44 -0
  147. package/dist/commands/db/rollback-silo.d.ts.map +1 -0
  148. package/dist/commands/db/rollback-silo.js +402 -0
  149. package/dist/commands/db/rollback-silo.js.map +1 -0
  150. package/dist/commands/deploy/index.d.ts +26 -0
  151. package/dist/commands/deploy/index.d.ts.map +1 -0
  152. package/dist/commands/deploy/index.js +107 -0
  153. package/dist/commands/deploy/index.js.map +1 -0
  154. package/dist/commands/devops/index.d.ts +6 -0
  155. package/dist/commands/devops/index.d.ts.map +1 -0
  156. package/dist/commands/devops/index.js +220 -0
  157. package/dist/commands/devops/index.js.map +1 -0
  158. package/dist/commands/domain/index.d.ts +8 -0
  159. package/dist/commands/domain/index.d.ts.map +1 -0
  160. package/dist/commands/domain/index.js +386 -0
  161. package/dist/commands/domain/index.js.map +1 -0
  162. package/dist/commands/image/index.d.ts +8 -0
  163. package/dist/commands/image/index.d.ts.map +1 -0
  164. package/dist/commands/image/index.js +308 -0
  165. package/dist/commands/image/index.js.map +1 -0
  166. package/dist/commands/install/factory-reset.d.ts +21 -0
  167. package/dist/commands/install/factory-reset.d.ts.map +1 -0
  168. package/dist/commands/install/factory-reset.js +83 -0
  169. package/dist/commands/install/factory-reset.js.map +1 -0
  170. package/dist/commands/install/index.d.ts +17 -0
  171. package/dist/commands/install/index.d.ts.map +1 -0
  172. package/dist/commands/install/index.js +44 -0
  173. package/dist/commands/install/index.js.map +1 -0
  174. package/dist/commands/install/install.d.ts +35 -0
  175. package/dist/commands/install/install.d.ts.map +1 -0
  176. package/dist/commands/install/install.js +171 -0
  177. package/dist/commands/install/install.js.map +1 -0
  178. package/dist/commands/login/index.d.ts +15 -0
  179. package/dist/commands/login/index.d.ts.map +1 -0
  180. package/dist/commands/login/index.js +58 -0
  181. package/dist/commands/login/index.js.map +1 -0
  182. package/dist/commands/nginx/index.d.ts +11 -0
  183. package/dist/commands/nginx/index.d.ts.map +1 -0
  184. package/dist/commands/nginx/index.js +580 -0
  185. package/dist/commands/nginx/index.js.map +1 -0
  186. package/dist/commands/server/bootstrap.d.ts +25 -0
  187. package/dist/commands/server/bootstrap.d.ts.map +1 -0
  188. package/dist/commands/server/bootstrap.js +260 -0
  189. package/dist/commands/server/bootstrap.js.map +1 -0
  190. package/dist/commands/server/index.d.ts +8 -0
  191. package/dist/commands/server/index.d.ts.map +1 -0
  192. package/dist/commands/server/index.js +2524 -0
  193. package/dist/commands/server/index.js.map +1 -0
  194. package/dist/commands/setup/index.d.ts +34 -0
  195. package/dist/commands/setup/index.d.ts.map +1 -0
  196. package/dist/commands/setup/index.js +423 -0
  197. package/dist/commands/setup/index.js.map +1 -0
  198. package/dist/commands/ssl/index.d.ts +8 -0
  199. package/dist/commands/ssl/index.d.ts.map +1 -0
  200. package/dist/commands/ssl/index.js +275 -0
  201. package/dist/commands/ssl/index.js.map +1 -0
  202. package/dist/commands/superadmin/index.d.ts +16 -0
  203. package/dist/commands/superadmin/index.d.ts.map +1 -0
  204. package/dist/commands/superadmin/index.js +81 -0
  205. package/dist/commands/superadmin/index.js.map +1 -0
  206. package/dist/commands/tenant/index.d.ts +6 -0
  207. package/dist/commands/tenant/index.d.ts.map +1 -0
  208. package/dist/commands/tenant/index.js +192 -0
  209. package/dist/commands/tenant/index.js.map +1 -0
  210. package/dist/index.d.ts +11 -0
  211. package/dist/index.d.ts.map +1 -0
  212. package/dist/index.js +107 -0
  213. package/dist/index.js.map +1 -0
  214. package/dist/lib/addon-sign.d.ts +23 -0
  215. package/dist/lib/addon-sign.d.ts.map +1 -0
  216. package/dist/lib/addon-sign.js +39 -0
  217. package/dist/lib/addon-sign.js.map +1 -0
  218. package/dist/lib/addon-sign.test.d.ts +2 -0
  219. package/dist/lib/addon-sign.test.d.ts.map +1 -0
  220. package/dist/lib/addon-sign.test.js +27 -0
  221. package/dist/lib/addon-sign.test.js.map +1 -0
  222. package/dist/lib/cdn.d.ts +25 -0
  223. package/dist/lib/cdn.d.ts.map +1 -0
  224. package/dist/lib/cdn.js +131 -0
  225. package/dist/lib/cdn.js.map +1 -0
  226. package/dist/lib/cloudflare.d.ts +133 -0
  227. package/dist/lib/cloudflare.d.ts.map +1 -0
  228. package/dist/lib/cloudflare.js +435 -0
  229. package/dist/lib/cloudflare.js.map +1 -0
  230. package/dist/lib/config.d.ts +96 -0
  231. package/dist/lib/config.d.ts.map +1 -0
  232. package/dist/lib/config.js +132 -0
  233. package/dist/lib/config.js.map +1 -0
  234. package/dist/lib/env.d.ts +8 -0
  235. package/dist/lib/env.d.ts.map +1 -0
  236. package/dist/lib/env.js +64 -0
  237. package/dist/lib/env.js.map +1 -0
  238. package/dist/lib/hosts.d.ts +194 -0
  239. package/dist/lib/hosts.d.ts.map +1 -0
  240. package/dist/lib/hosts.js +183 -0
  241. package/dist/lib/hosts.js.map +1 -0
  242. package/dist/lib/logger.d.ts +68 -0
  243. package/dist/lib/logger.d.ts.map +1 -0
  244. package/dist/lib/logger.js +130 -0
  245. package/dist/lib/logger.js.map +1 -0
  246. package/dist/lib/nginx-config.d.ts +78 -0
  247. package/dist/lib/nginx-config.d.ts.map +1 -0
  248. package/dist/lib/nginx-config.js +736 -0
  249. package/dist/lib/nginx-config.js.map +1 -0
  250. package/dist/lib/ops/addon-dev.d.ts +93 -0
  251. package/dist/lib/ops/addon-dev.d.ts.map +1 -0
  252. package/dist/lib/ops/addon-dev.js +237 -0
  253. package/dist/lib/ops/addon-dev.js.map +1 -0
  254. package/dist/lib/ops/addon-quality.d.ts +38 -0
  255. package/dist/lib/ops/addon-quality.d.ts.map +1 -0
  256. package/dist/lib/ops/addon-quality.js +338 -0
  257. package/dist/lib/ops/addon-quality.js.map +1 -0
  258. package/dist/lib/ops/addon-routes.d.ts +49 -0
  259. package/dist/lib/ops/addon-routes.d.ts.map +1 -0
  260. package/dist/lib/ops/addon-routes.js +189 -0
  261. package/dist/lib/ops/addon-routes.js.map +1 -0
  262. package/dist/lib/ops/addon.d.ts +120 -0
  263. package/dist/lib/ops/addon.d.ts.map +1 -0
  264. package/dist/lib/ops/addon.js +260 -0
  265. package/dist/lib/ops/addon.js.map +1 -0
  266. package/dist/lib/ops/cdn.d.ts +87 -0
  267. package/dist/lib/ops/cdn.d.ts.map +1 -0
  268. package/dist/lib/ops/cdn.js +170 -0
  269. package/dist/lib/ops/cdn.js.map +1 -0
  270. package/dist/lib/ops/cf.d.ts +36 -0
  271. package/dist/lib/ops/cf.d.ts.map +1 -0
  272. package/dist/lib/ops/cf.js +114 -0
  273. package/dist/lib/ops/cf.js.map +1 -0
  274. package/dist/lib/ops/compose.d.ts +95 -0
  275. package/dist/lib/ops/compose.d.ts.map +1 -0
  276. package/dist/lib/ops/compose.js +165 -0
  277. package/dist/lib/ops/compose.js.map +1 -0
  278. package/dist/lib/ops/core.d.ts +117 -0
  279. package/dist/lib/ops/core.d.ts.map +1 -0
  280. package/dist/lib/ops/core.js +322 -0
  281. package/dist/lib/ops/core.js.map +1 -0
  282. package/dist/lib/ops/db.d.ts +116 -0
  283. package/dist/lib/ops/db.d.ts.map +1 -0
  284. package/dist/lib/ops/db.js +351 -0
  285. package/dist/lib/ops/db.js.map +1 -0
  286. package/dist/lib/ops/dns.d.ts +111 -0
  287. package/dist/lib/ops/dns.d.ts.map +1 -0
  288. package/dist/lib/ops/dns.js +306 -0
  289. package/dist/lib/ops/dns.js.map +1 -0
  290. package/dist/lib/ops/image.d.ts +94 -0
  291. package/dist/lib/ops/image.d.ts.map +1 -0
  292. package/dist/lib/ops/image.js +159 -0
  293. package/dist/lib/ops/image.js.map +1 -0
  294. package/dist/lib/ops/nginx.d.ts +114 -0
  295. package/dist/lib/ops/nginx.d.ts.map +1 -0
  296. package/dist/lib/ops/nginx.js +388 -0
  297. package/dist/lib/ops/nginx.js.map +1 -0
  298. package/dist/lib/ops/redis.d.ts +7 -0
  299. package/dist/lib/ops/redis.d.ts.map +1 -0
  300. package/dist/lib/ops/redis.js +35 -0
  301. package/dist/lib/ops/redis.js.map +1 -0
  302. package/dist/lib/ops/ssh.d.ts +127 -0
  303. package/dist/lib/ops/ssh.d.ts.map +1 -0
  304. package/dist/lib/ops/ssh.js +269 -0
  305. package/dist/lib/ops/ssh.js.map +1 -0
  306. package/dist/lib/prompts.d.ts +46 -0
  307. package/dist/lib/prompts.d.ts.map +1 -0
  308. package/dist/lib/prompts.js +113 -0
  309. package/dist/lib/prompts.js.map +1 -0
  310. package/dist/lib/sast.d.ts +43 -0
  311. package/dist/lib/sast.d.ts.map +1 -0
  312. package/dist/lib/sast.js +79 -0
  313. package/dist/lib/sast.js.map +1 -0
  314. package/dist/lib/sast.test.d.ts +2 -0
  315. package/dist/lib/sast.test.d.ts.map +1 -0
  316. package/dist/lib/sast.test.js +33 -0
  317. package/dist/lib/sast.test.js.map +1 -0
  318. package/dist/lib/shell.d.ts +61 -0
  319. package/dist/lib/shell.d.ts.map +1 -0
  320. package/dist/lib/shell.js +183 -0
  321. package/dist/lib/shell.js.map +1 -0
  322. package/dist/lib/ssh-config.d.ts +37 -0
  323. package/dist/lib/ssh-config.d.ts.map +1 -0
  324. package/dist/lib/ssh-config.js +122 -0
  325. package/dist/lib/ssh-config.js.map +1 -0
  326. package/dist/lib/tenant-scope.d.ts +38 -0
  327. package/dist/lib/tenant-scope.d.ts.map +1 -0
  328. package/dist/lib/tenant-scope.js +129 -0
  329. package/dist/lib/tenant-scope.js.map +1 -0
  330. package/dist/lib/tenant-scope.test.d.ts +2 -0
  331. package/dist/lib/tenant-scope.test.d.ts.map +1 -0
  332. package/dist/lib/tenant-scope.test.js +223 -0
  333. package/dist/lib/tenant-scope.test.js.map +1 -0
  334. package/package.json +58 -0
  335. package/templates/bootstrap/.env.template +54 -0
  336. package/templates/bootstrap/docker-compose.yml +145 -0
  337. package/templates/vhost.conf.tmpl +446 -0
@@ -0,0 +1,1186 @@
1
+ /**
2
+ * vu addon create <name>
3
+ *
4
+ * ciCore HelloWorld şablonundan doğrulanmış pattern'lerle
5
+ * sıfırdan çalışır addon iskeleti oluşturur.
6
+ *
7
+ * Kullanım:
8
+ * vu addon create HesapMakinasi
9
+ */
10
+ import { log, spinner } from '../../lib/logger.js';
11
+ import { paths } from '../../lib/config.js';
12
+ import { findShellRoot } from '../../lib/ops/addon-dev.js';
13
+ import { genEslintConfig, genPrettierConfig, genEditorConfig, genTsConfig, genGitlabCi, genAmbientTypes, } from '../../lib/ops/addon-quality.js';
14
+ import fs from 'fs-extra';
15
+ import path from 'path';
16
+ // ─── İsim türetme yardımcıları ────────────────────────────────────────────────
17
+ /** HesapMakinasi → hesap-makinasi */
18
+ function toKebab(name) {
19
+ return name
20
+ .replace(/([A-Z])/g, '-$1')
21
+ .toLowerCase()
22
+ .replace(/^-/, '');
23
+ }
24
+ /** HesapMakinasi → hesapMakinasi */
25
+ function toCamel(name) {
26
+ return name.charAt(0).toLowerCase() + name.slice(1);
27
+ }
28
+ /**
29
+ * HesapMakinasi → hm (her PascalCase kelimenin ilk harfi, max 4 karakter)
30
+ * HelloWorld → hw | ProductManager → pm | AdminMenuManager → amm
31
+ */
32
+ function toPrefix(name) {
33
+ const words = name.match(/[A-Z][a-z0-9]*/g) ?? [name];
34
+ return words
35
+ .map(w => w[0].toLowerCase())
36
+ .join('')
37
+ .slice(0, 4);
38
+ }
39
+ /** HesapMakinasi → Hesap Makinasi */
40
+ function toDisplay(name) {
41
+ const words = name.match(/[A-Z][a-z0-9]*/g) ?? [name];
42
+ return words.join(' ');
43
+ }
44
+ // ─── Ana fonksiyon ────────────────────────────────────────────────────────────
45
+ export async function createAddon(name) {
46
+ log.title(`ciCore Addon Oluşturucu: ${name}`);
47
+ // 1. İsim doğrulama (PascalCase zorunlu)
48
+ if (!/^[A-Z][a-zA-Z0-9]+$/.test(name)) {
49
+ log.error('Addon adı PascalCase olmalı. Örnek: HesapMakinasi, NotePad, TaskFlow');
50
+ process.exit(1);
51
+ }
52
+ // Pick the right addons dir for the layout we're in.
53
+ // - dev-shell (personnel laptop): cores/dev/addons/<Name>
54
+ // - monorepo (Mustafa): addons/<Name> (paths.dev.addons default)
55
+ // Auto-detect dev-shell by walking up from cwd; fall back to the
56
+ // monorepo-style path when we're not inside dev-shell.
57
+ const shellRoot = findShellRoot(process.cwd());
58
+ const addonsRoot = shellRoot ? path.join(shellRoot, 'cores', 'dev', 'addons') : paths.dev.addons;
59
+ const addonPath = path.join(addonsRoot, name);
60
+ // 2. Mevcut addon kontrolü
61
+ if (await fs.pathExists(addonPath)) {
62
+ log.error(`"${name}" addon'u zaten mevcut: ${addonPath}`);
63
+ process.exit(1);
64
+ }
65
+ // 3. Değerleri türet
66
+ const slug = toKebab(name); // hesap-makinasi
67
+ const camel = toCamel(name); // hesapMakinasi
68
+ const prefix = toPrefix(name); // hm
69
+ const display = toDisplay(name); // Hesap Makinasi
70
+ log.blank();
71
+ log.kv('Klasör adı', name);
72
+ log.kv('URL slug', slug);
73
+ log.kv('i18n namespace', camel);
74
+ log.kv('Tablo prefix', prefix);
75
+ log.kv('Görünen isim', display);
76
+ log.blank();
77
+ // 4. Dosyaları oluştur
78
+ const s = spinner('Addon iskelet dosyaları oluşturuluyor...').start();
79
+ try {
80
+ // Klasörler
81
+ await fs.ensureDir(path.join(addonPath, 'Backend', 'Controllers'));
82
+ await fs.ensureDir(path.join(addonPath, 'Backend', 'Services'));
83
+ await fs.ensureDir(path.join(addonPath, 'Backend', 'Routes'));
84
+ await fs.ensureDir(path.join(addonPath, 'Backend', 'Migrations'));
85
+ await fs.ensureDir(path.join(addonPath, 'pages', 'admin'));
86
+ await fs.ensureDir(path.join(addonPath, 'pages', 'web'));
87
+ await fs.ensureDir(path.join(addonPath, 'components'));
88
+ await fs.ensureDir(path.join(addonPath, 'composables'));
89
+ await fs.ensureDir(path.join(addonPath, 'locales'));
90
+ await fs.ensureDir(path.join(addonPath, 'ui'));
91
+ await fs.ensureDir(path.join(addonPath, 'Assets', 'img'));
92
+ // Dosyalar
93
+ await write(addonPath, 'addon.json', genAddonJson(name, slug, camel, prefix, display));
94
+ await write(addonPath, 'package.json', genPackageJson(name));
95
+ await write(addonPath, '.signature', '');
96
+ await write(addonPath, 'empty-devtools.js', genEmptyDevtools());
97
+ await write(addonPath, 'vite.config.js', genViteConfig(name));
98
+ await write(addonPath, 'Backend/Routes/routes.php', genRoutes(name, slug));
99
+ await write(addonPath, `Backend/Controllers/${name}Controller.php`, genController(name, slug));
100
+ await write(addonPath, `Backend/Services/${name}Service.php`, genService(name, prefix));
101
+ await write(addonPath, `Backend/Migrations/001_Create${prefix.toUpperCase()}Table.php`, genMigration(name, prefix));
102
+ await write(addonPath, 'pages/admin/index.vue', genAdminPage(name, slug, camel));
103
+ await write(addonPath, 'pages/web/index.vue', genWebPage(name, slug, camel));
104
+ await write(addonPath, 'ui/entry.ts', genEntryTs(name));
105
+ await write(addonPath, 'ui/style.css', genStyleCss(name));
106
+ await write(addonPath, 'locales/en.json', genLocaleEn(camel, display));
107
+ await write(addonPath, 'locales/tr.json', genLocaleTr(camel, display));
108
+ // Faz 5 quality template — every new addon ships with lint + format
109
+ // + typecheck config out of the box. Tooling lives in dev-shell's
110
+ // top-level node_modules; the configs here just point the addon's
111
+ // file types at the shared toolchain.
112
+ await write(addonPath, '.eslintrc.json', genEslintConfig());
113
+ await write(addonPath, '.prettierrc.json', genPrettierConfig());
114
+ await write(addonPath, '.editorconfig', genEditorConfig());
115
+ await write(addonPath, 'tsconfig.json', genTsConfig());
116
+ await write(addonPath, '.gitlab-ci.yml', genGitlabCi(name));
117
+ // Ambient type declarations so vue-tsc doesn't choke on the
118
+ // platform-provided globals (`$fetch` from Nuxt, `@cicore/shared`
119
+ // imports resolved at runtime via the import map).
120
+ await write(addonPath, 'env.d.ts', genAmbientTypes());
121
+ await write(addonPath, '.gitignore', genGitignore());
122
+ s.succeed('Tüm dosyalar oluşturuldu');
123
+ }
124
+ catch (err) {
125
+ s.fail('Dosya oluşturma hatası');
126
+ log.error(err.message);
127
+ await fs.remove(addonPath).catch(() => { });
128
+ process.exit(1);
129
+ }
130
+ // 5. Özet
131
+ log.blank();
132
+ log.success(`"${name}" addon'u başarıyla oluşturuldu!`);
133
+ log.blank();
134
+ log.info('Oluşturulan dosyalar:');
135
+ log.item(`addons/${name}/addon.json`);
136
+ log.item(`addons/${name}/Backend/Routes/routes.php`);
137
+ log.item(`addons/${name}/Backend/Controllers/${name}Controller.php`);
138
+ log.item(`addons/${name}/Backend/Services/${name}Service.php`);
139
+ log.item(`addons/${name}/Backend/Migrations/001_Create${prefix.toUpperCase()}Table.php`);
140
+ log.item(`addons/${name}/pages/admin/index.vue`);
141
+ log.item(`addons/${name}/pages/web/index.vue`);
142
+ log.item(`addons/${name}/ui/entry.ts`);
143
+ log.item(`addons/${name}/locales/en.json + tr.json`);
144
+ log.blank();
145
+ log.info('Sonraki adımlar:');
146
+ log.item(`1. addons/${name}/addon.json → icon ve menü sırasını düzenle`);
147
+ log.item(`2. Backend/Services/${name}Service.php → iş mantığını yaz`);
148
+ log.item(`3. pages/admin/index.vue → UI'ı geliştir`);
149
+ log.item(`4. ci addon dev ${name} → vite HMR session (Faz 2A)`);
150
+ log.item(`5. http://localhost:3080/admin/${slug} adresini aç`);
151
+ log.blank();
152
+ log.info('Quality stack (kendi devDeps — npm ci, sonra):');
153
+ log.item(`Lint: npm run lint`);
154
+ log.item(`Typecheck: npm run typecheck`);
155
+ log.item(`Build: npm run build`);
156
+ log.item(`(\`ci addon deploy --core <X>\` runs lint + typecheck before upload)`);
157
+ log.blank();
158
+ log.info('Git başlatmak için (S4-T23):');
159
+ log.item(`cd ${addonPath} && git init && git add . && git commit -m "init: ${name} addon scaffold"`);
160
+ }
161
+ // ─── Yardımcı: dosya yaz ──────────────────────────────────────────────────────
162
+ async function write(base, rel, content) {
163
+ const fullPath = path.join(base, rel);
164
+ await fs.ensureDir(path.dirname(fullPath));
165
+ if (typeof content === 'object') {
166
+ await fs.writeJson(fullPath, content, { spaces: 2 });
167
+ }
168
+ else {
169
+ await fs.writeFile(fullPath, content, 'utf8');
170
+ }
171
+ }
172
+ // ─── Şablon üreticiler ────────────────────────────────────────────────────────
173
+ function genAddonJson(name, slug, camel, prefix, display) {
174
+ // `name` MUST be a string matching the addon directory — `ci addon deploy`
175
+ // and the install-addon.sh receiver both validate the directory ↔ name
176
+ // pair against /^[A-Za-z0-9_-]+$/. The i18n display name lives in
177
+ // `display_name` and the menu title, so multilingual surfaces still
178
+ // render in Turkish/English without breaking the deploy pipeline.
179
+ return {
180
+ name,
181
+ display_name: { en: display, tr: display },
182
+ version: '1.0.0',
183
+ // Plain-string description matches the install-addon.sh receiver's
184
+ // schema (it grep's a single-line summary for the audit log).
185
+ // Multilingual descriptions live in `display_name` and any per-page
186
+ // i18n the addon ships in locales/*.json.
187
+ description: `${display} addon for ciCore`,
188
+ author: 'ciCore Team',
189
+ enabled: true,
190
+ admin: {
191
+ enabled: true,
192
+ locales: true,
193
+ route_prefix: `/admin/${slug}`,
194
+ menu: {
195
+ title: display,
196
+ title_i18n: { en: display, tr: display },
197
+ icon: 'i-lucide-puzzle',
198
+ order: 50,
199
+ to: `/admin/${slug}`,
200
+ },
201
+ },
202
+ web: {
203
+ enabled: true,
204
+ locales: true,
205
+ route_prefix: `/dashboard/${slug}`,
206
+ menu: {
207
+ title: display,
208
+ title_i18n: { en: display, tr: display },
209
+ icon: 'i-lucide-puzzle',
210
+ order: 50,
211
+ to: `/dashboard/${slug}`,
212
+ },
213
+ },
214
+ backend: {
215
+ enabled: true,
216
+ route_prefix: `/api/${slug}`,
217
+ namespace: `VuCore\\Addons\\${name}`,
218
+ routes_file: 'Backend/Routes/routes.php',
219
+ migrations_dir: 'Backend/Migrations',
220
+ table_prefix: prefix,
221
+ },
222
+ i18n: { namespace: camel },
223
+ is_system: false,
224
+ licensing: { model: 'free' },
225
+ };
226
+ }
227
+ // ─────────────────────────────────────────────────────────────────────────────
228
+ function genPackageJson(name) {
229
+ return JSON.stringify({
230
+ name: `@cicore-addon/${name.toLowerCase()}`,
231
+ version: '1.0.0',
232
+ private: true,
233
+ type: 'module',
234
+ scripts: {
235
+ build: 'vite build',
236
+ lint: 'eslint . --max-warnings 0',
237
+ typecheck: 'vue-tsc --noEmit',
238
+ },
239
+ devDependencies: {
240
+ vite: '^7.1.9',
241
+ '@vitejs/plugin-vue': '^6.0.1',
242
+ vue: '^3.5.22',
243
+ 'vue-tsc': '^2.1.10',
244
+ typescript: '^5.7.2',
245
+ '@typescript-eslint/eslint-plugin': '^7.18.0',
246
+ '@typescript-eslint/parser': '^7.18.0',
247
+ eslint: '^8.57.1',
248
+ 'eslint-plugin-vue': '^9.31.0',
249
+ 'vue-eslint-parser': '^9.4.3',
250
+ },
251
+ }, null, 2) + '\n';
252
+ }
253
+ function genGitignore() {
254
+ return `node_modules/
255
+ dist/
256
+ *.local
257
+ .DS_Store
258
+ *.log
259
+ `;
260
+ }
261
+ function genEmptyDevtools() {
262
+ return `// Stub for @vue/devtools-api — not needed in production ESM bundles
263
+ export const setupDevtoolsPlugin = () => {};
264
+ export default { setupDevtoolsPlugin: () => {} };
265
+ `;
266
+ }
267
+ // ─────────────────────────────────────────────────────────────────────────────
268
+ function genRoutes(name, slug) {
269
+ // PHP değişkenlerini ($app, $group vs.) template literal interpolasyon olarak
270
+ // algılamaması için satırları birleştirerek string oluşturuyoruz.
271
+ const lines = [
272
+ '<?php',
273
+ `/**`,
274
+ ` * ${name} Routes`,
275
+ ` *`,
276
+ ` * KRİTİK: addon.json backend.route_prefix otomatik uygulanmaz!`,
277
+ ` * AddonLoader: $routeHandler($app) — prefix eklenmez, tam path yaz.`,
278
+ ` */`,
279
+ ``,
280
+ `use Slim\\App;`,
281
+ `use Slim\\Routing\\RouteCollectorProxy;`,
282
+ `use VuCore\\Addons\\${name}\\Controllers\\${name}Controller;`,
283
+ `use VuCore\\Core\\Middleware\\DualAuthMiddleware;`,
284
+ ``,
285
+ `return function (App $app) {`,
286
+ ` $controller = new ${name}Controller();`,
287
+ ` $auth = new DualAuthMiddleware('${name}');`,
288
+ ``,
289
+ ` // Admin API — JWT veya API Key ile korumalı`,
290
+ ` $app->group('/api/${slug}', function (RouteCollectorProxy $group) use ($controller) {`,
291
+ ` $group->get('/ping', [$controller, 'ping']);`,
292
+ ` $group->get('/items', [$controller, 'index']);`,
293
+ ` $group->get('/items/{id}', [$controller, 'show']);`,
294
+ ` $group->post('/items', [$controller, 'store']);`,
295
+ ` $group->patch('/items/{id}', [$controller, 'update']);`,
296
+ ` $group->delete('/items/{id}',[$controller, 'destroy']);`,
297
+ ` })->add($auth);`,
298
+ ``,
299
+ ` // Public Web API — auth gerektirmez`,
300
+ ` $app->group('/api/${slug}/web', function (RouteCollectorProxy $group) use ($controller) {`,
301
+ ` $group->get('/message', [$controller, 'publicMessage'])`,
302
+ ` ->setArgument('__public', 'true');`,
303
+ ` });`,
304
+ `};`,
305
+ ``,
306
+ ];
307
+ return lines.join('\n');
308
+ }
309
+ // ─────────────────────────────────────────────────────────────────────────────
310
+ function genController(name, slug) {
311
+ const lines = [
312
+ `<?php`,
313
+ `declare(strict_types=1);`,
314
+ ``,
315
+ `namespace VuCore\\Addons\\${name}\\Controllers;`,
316
+ ``,
317
+ `use Psr\\Http\\Message\\ResponseInterface as Response;`,
318
+ `use Psr\\Http\\Message\\ServerRequestInterface as Request;`,
319
+ `use VuCore\\Core\\Controllers\\BaseController;`,
320
+ `use VuCore\\Addons\\${name}\\Services\\${name}Service;`,
321
+ ``,
322
+ `class ${name}Controller extends BaseController`,
323
+ `{`,
324
+ ` private ?${name}Service $service;`,
325
+ ``,
326
+ ` public function __construct()`,
327
+ ` {`,
328
+ ` try {`,
329
+ ` $this->service = new ${name}Service();`,
330
+ ` } catch (\\Exception $e) {`,
331
+ ` error_log("[${name}] Controller init failed: " . $e->getMessage());`,
332
+ ` $this->service = null;`,
333
+ ` }`,
334
+ ` }`,
335
+ ``,
336
+ ` private function unavailable(Response $response): Response`,
337
+ ` {`,
338
+ ` return $this->json($response, ['success' => false, 'message' => 'Service unavailable'], 503);`,
339
+ ` }`,
340
+ ``,
341
+ ` /** GET /${slug}/ping */`,
342
+ ` public function ping(Request $request, Response $response): Response`,
343
+ ` {`,
344
+ ` return $this->json($response, [`,
345
+ ` 'success' => true,`,
346
+ ` 'message' => 'pong',`,
347
+ ` 'addon' => '${name}',`,
348
+ ` 'timestamp' => date('c'),`,
349
+ ` ]);`,
350
+ ` }`,
351
+ ``,
352
+ ` /** GET /${slug}/items */`,
353
+ ` public function index(Request $request, Response $response): Response`,
354
+ ` {`,
355
+ ` if (!$this->service) return $this->unavailable($response);`,
356
+ ` try {`,
357
+ ` $result = $this->service->getAll($request->getQueryParams());`,
358
+ ` return $this->json($response, ['success' => true, 'data' => $result]);`,
359
+ ` } catch (\\Exception $e) {`,
360
+ ` return $this->json($response, ['success' => false, 'message' => $e->getMessage()], 500);`,
361
+ ` }`,
362
+ ` }`,
363
+ ``,
364
+ ` /** GET /${slug}/items/{id} */`,
365
+ ` public function show(Request $request, Response $response, array $args): Response`,
366
+ ` {`,
367
+ ` if (!$this->service) return $this->unavailable($response);`,
368
+ ` $item = $this->service->getById($args['id']);`,
369
+ ` if (!$item) return $this->json($response, ['success' => false, 'message' => 'Not found'], 404);`,
370
+ ` return $this->json($response, ['success' => true, 'data' => $item]);`,
371
+ ` }`,
372
+ ``,
373
+ ` /** POST /${slug}/items */`,
374
+ ` public function store(Request $request, Response $response): Response`,
375
+ ` {`,
376
+ ` if (!$this->service) return $this->unavailable($response);`,
377
+ ` try {`,
378
+ ` $item = $this->service->create($request->getParsedBody() ?? []);`,
379
+ ` return $this->json($response, ['success' => true, 'data' => $item], 201);`,
380
+ ` } catch (\\Exception $e) {`,
381
+ ` return $this->json($response, ['success' => false, 'message' => $e->getMessage()], 400);`,
382
+ ` }`,
383
+ ` }`,
384
+ ``,
385
+ ` /** PATCH /${slug}/items/{id} */`,
386
+ ` public function update(Request $request, Response $response, array $args): Response`,
387
+ ` {`,
388
+ ` if (!$this->service) return $this->unavailable($response);`,
389
+ ` try {`,
390
+ ` $item = $this->service->update($args['id'], $request->getParsedBody() ?? []);`,
391
+ ` if (!$item) return $this->json($response, ['success' => false, 'message' => 'Not found'], 404);`,
392
+ ` return $this->json($response, ['success' => true, 'data' => $item]);`,
393
+ ` } catch (\\Exception $e) {`,
394
+ ` return $this->json($response, ['success' => false, 'message' => $e->getMessage()], 400);`,
395
+ ` }`,
396
+ ` }`,
397
+ ``,
398
+ ` /** DELETE /${slug}/items/{id} */`,
399
+ ` public function destroy(Request $request, Response $response, array $args): Response`,
400
+ ` {`,
401
+ ` if (!$this->service) return $this->unavailable($response);`,
402
+ ` $this->service->delete($args['id']);`,
403
+ ` return $this->json($response, ['success' => true]);`,
404
+ ` }`,
405
+ ``,
406
+ ` /** GET /${slug}/web/message — public */`,
407
+ ` public function publicMessage(Request $request, Response $response): Response`,
408
+ ` {`,
409
+ ` return $this->json($response, ['success' => true, 'message' => 'Hello from ${name}!']);`,
410
+ ` }`,
411
+ `}`,
412
+ ``,
413
+ ];
414
+ return lines.join('\n');
415
+ }
416
+ // ─────────────────────────────────────────────────────────────────────────────
417
+ function genService(name, prefix) {
418
+ const tbl = `${prefix}_items`;
419
+ const lines = [
420
+ `<?php`,
421
+ `declare(strict_types=1);`,
422
+ ``,
423
+ `namespace VuCore\\Addons\\${name}\\Services;`,
424
+ ``,
425
+ `use VuCore\\Core\\Services\\DatabaseService;`,
426
+ `use PDO;`,
427
+ ``,
428
+ `class ${name}Service`,
429
+ `{`,
430
+ ` private PDO $db;`,
431
+ ` private string $table = '${tbl}';`,
432
+ ``,
433
+ ` public function __construct()`,
434
+ ` {`,
435
+ ` $this->db = DatabaseService::getConnection();`,
436
+ ` }`,
437
+ ``,
438
+ ` public function getAll(array $params = []): array`,
439
+ ` {`,
440
+ ` $page = (int)($params['page'] ?? 1);`,
441
+ ` $perPage = (int)($params['per_page'] ?? 20);`,
442
+ ` $offset = ($page - 1) * $perPage;`,
443
+ ``,
444
+ ` $stmt = $this->db->prepare(`,
445
+ ` "SELECT * FROM {$this->table} ORDER BY created_at DESC LIMIT :limit OFFSET :offset"`,
446
+ ` );`,
447
+ ` $stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);`,
448
+ ` $stmt->bindValue(':offset', $offset, PDO::PARAM_INT);`,
449
+ ` $stmt->execute();`,
450
+ ` $items = $stmt->fetchAll(PDO::FETCH_ASSOC);`,
451
+ ``,
452
+ ` $total = (int)$this->db->query("SELECT COUNT(*) FROM {$this->table}")->fetchColumn();`,
453
+ ``,
454
+ ` return [`,
455
+ ` 'items' => $items,`,
456
+ ` 'pagination' => [`,
457
+ ` 'total' => $total,`,
458
+ ` 'per_page' => $perPage,`,
459
+ ` 'current_page' => $page,`,
460
+ ` 'last_page' => (int)ceil($total / $perPage),`,
461
+ ` ],`,
462
+ ` ];`,
463
+ ` }`,
464
+ ``,
465
+ ` public function getById(string $id): ?array`,
466
+ ` {`,
467
+ ` $stmt = $this->db->prepare("SELECT * FROM {$this->table} WHERE id = :id");`,
468
+ ` $stmt->execute(['id' => $id]);`,
469
+ ` return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;`,
470
+ ` }`,
471
+ ``,
472
+ ` public function create(array $data): array`,
473
+ ` {`,
474
+ ` if (empty($data['name'])) throw new \\InvalidArgumentException('name is required');`,
475
+ ``,
476
+ ` $stmt = $this->db->prepare("`,
477
+ ` INSERT INTO {$this->table} (id, name, description, is_active, created_at, updated_at)`,
478
+ ` VALUES (gen_random_uuid(), :name, :description, true, NOW(), NOW())`,
479
+ ` RETURNING *`,
480
+ ` ");`,
481
+ ` $stmt->execute(['name' => $data['name'], 'description' => $data['description'] ?? null]);`,
482
+ ` return $stmt->fetch(PDO::FETCH_ASSOC);`,
483
+ ` }`,
484
+ ``,
485
+ ` public function update(string $id, array $data): ?array`,
486
+ ` {`,
487
+ ` $existing = $this->getById($id);`,
488
+ ` if (!$existing) return null;`,
489
+ ``,
490
+ ` $stmt = $this->db->prepare("`,
491
+ ` UPDATE {$this->table}`,
492
+ ` SET name = :name, description = :description, updated_at = NOW()`,
493
+ ` WHERE id = :id RETURNING *`,
494
+ ` ");`,
495
+ ` $stmt->execute([`,
496
+ ` 'id' => $id,`,
497
+ ` 'name' => $data['name'] ?? $existing['name'],`,
498
+ ` 'description' => $data['description'] ?? $existing['description'],`,
499
+ ` ]);`,
500
+ ` return $stmt->fetch(PDO::FETCH_ASSOC);`,
501
+ ` }`,
502
+ ``,
503
+ ` public function delete(string $id): void`,
504
+ ` {`,
505
+ ` $this->db->prepare("DELETE FROM {$this->table} WHERE id = :id")->execute(['id' => $id]);`,
506
+ ` }`,
507
+ `}`,
508
+ ``,
509
+ ];
510
+ return lines.join('\n');
511
+ }
512
+ // ─────────────────────────────────────────────────────────────────────────────
513
+ function genMigration(name, prefix) {
514
+ const UP = prefix.toUpperCase();
515
+ const cls = `Migration_001_Create${UP}Table`;
516
+ const tbl = `${prefix}_items`;
517
+ const lines = [
518
+ `<?php`,
519
+ `declare(strict_types=1);`,
520
+ ``,
521
+ `namespace VuCore\\Addons\\${name}\\Migrations;`,
522
+ ``,
523
+ `use VuCore\\Core\\Services\\Migration;`,
524
+ `use PDO;`,
525
+ ``,
526
+ `class ${cls} implements Migration`,
527
+ `{`,
528
+ ` private string $prefix = '${prefix}';`,
529
+ ``,
530
+ ` public function name(): string { return '001_Create${UP}Table'; }`,
531
+ ``,
532
+ ` public function up(PDO $db): void`,
533
+ ` {`,
534
+ ` $exists = $db->query("`,
535
+ ` SELECT EXISTS (`,
536
+ ` SELECT FROM information_schema.tables WHERE table_name = '${tbl}'`,
537
+ ` )`,
538
+ ` ")->fetchColumn();`,
539
+ ` if ($exists) return;`,
540
+ ``,
541
+ ` $db->exec("`,
542
+ ` CREATE TABLE ${tbl} (`,
543
+ ` id UUID PRIMARY KEY DEFAULT gen_random_uuid(),`,
544
+ ` name VARCHAR(255) NOT NULL,`,
545
+ ` description TEXT,`,
546
+ ` is_active BOOLEAN DEFAULT TRUE,`,
547
+ ` metadata JSONB DEFAULT '{}',`,
548
+ ` created_at TIMESTAMPTZ DEFAULT NOW(),`,
549
+ ` updated_at TIMESTAMPTZ DEFAULT NOW()`,
550
+ ` )`,
551
+ ` ");`,
552
+ ` $db->exec("CREATE INDEX idx_${prefix}_items_name ON ${tbl}(name)");`,
553
+ ` $db->exec("CREATE INDEX idx_${prefix}_items_is_active ON ${tbl}(is_active)");`,
554
+ ` }`,
555
+ ``,
556
+ ` public function down(PDO $db): void`,
557
+ ` {`,
558
+ ` $db->exec("DROP TABLE IF EXISTS ${tbl} CASCADE");`,
559
+ ` }`,
560
+ `}`,
561
+ ``,
562
+ ];
563
+ return lines.join('\n');
564
+ }
565
+ // ─────────────────────────────────────────────────────────────────────────────
566
+ function genAdminPage(name, slug, camel) {
567
+ return `<template>
568
+ <div class="space-y-6">
569
+
570
+ <div class="flex items-center justify-between">
571
+ <div>
572
+ <h1 class="text-2xl font-bold text-gray-900 dark:text-white">
573
+ {{ t('${camel}.title') }}
574
+ </h1>
575
+ <p class="text-sm text-gray-500 dark:text-gray-400 mt-1">
576
+ {{ t('${camel}.description') }}
577
+ </p>
578
+ </div>
579
+ <UButton icon="i-lucide-plus" @click="openCreate">
580
+ {{ t('${camel}.actions.create') }}
581
+ </UButton>
582
+ </div>
583
+
584
+ <UCard>
585
+ <div v-if="loading" class="flex justify-center py-12">
586
+ <Icon name="i-lucide-loader-2" class="size-8 animate-spin text-primary" />
587
+ </div>
588
+
589
+ <div v-else-if="items.length === 0" class="text-center py-12">
590
+ <Icon name="i-lucide-inbox" class="size-12 text-gray-300 dark:text-gray-700 mx-auto mb-3" />
591
+ <p class="text-gray-500">{{ t('${camel}.empty') }}</p>
592
+ <UButton class="mt-4" @click="openCreate">
593
+ {{ t('${camel}.actions.create') }}
594
+ </UButton>
595
+ </div>
596
+
597
+ <div v-else class="divide-y divide-gray-100 dark:divide-gray-800">
598
+ <div
599
+ v-for="item in items"
600
+ :key="item.id"
601
+ class="flex items-center justify-between py-3"
602
+ >
603
+ <div>
604
+ <p class="font-medium text-gray-900 dark:text-white">{{ item.name }}</p>
605
+ <p class="text-xs text-gray-400 mt-0.5">{{ formatDate(item.created_at) }}</p>
606
+ </div>
607
+ <div class="flex gap-1">
608
+ <UButton size="xs" variant="ghost" color="neutral" icon="i-lucide-pencil"
609
+ @click="openEdit(item)" />
610
+ <UButton size="xs" variant="ghost" color="error" icon="i-lucide-trash-2"
611
+ @click="removeItem(item.id)" />
612
+ </div>
613
+ </div>
614
+ </div>
615
+ </UCard>
616
+
617
+ <!-- Oluştur / Düzenle Modal -->
618
+ <ClientOnly>
619
+ <UModal v-model:open="showModal">
620
+ <template #header>
621
+ <h3 class="text-lg font-semibold">
622
+ {{ editing ? t('${camel}.actions.edit') : t('${camel}.actions.create') }}
623
+ </h3>
624
+ </template>
625
+ <template #body>
626
+ <div class="space-y-4">
627
+ <UFormField :label="t('${camel}.form.name')" required>
628
+ <UInput v-model="form.name" autofocus />
629
+ </UFormField>
630
+ <UFormField :label="t('${camel}.form.description')">
631
+ <UTextarea v-model="form.description" :rows="3" />
632
+ </UFormField>
633
+ </div>
634
+ </template>
635
+ <template #footer>
636
+ <div class="flex justify-end gap-2">
637
+ <UButton variant="ghost" color="neutral" @click="showModal = false">
638
+ {{ t('${camel}.actions.cancel') }}
639
+ </UButton>
640
+ <UButton :loading="saving" @click="saveItem">
641
+ {{ t('${camel}.actions.save') }}
642
+ </UButton>
643
+ </div>
644
+ </template>
645
+ </UModal>
646
+ </ClientOnly>
647
+
648
+ </div>
649
+ </template>
650
+
651
+ <script setup lang="ts">
652
+ import { ref, onMounted } from 'vue'
653
+ import { useRuntimeConfig, useToast, useI18n } from '@cicore/shared'
654
+ import enLocale from '../../locales/en.json'
655
+ import trLocale from '../../locales/tr.json'
656
+
657
+ // KRİTİK: useApi() değil — $fetch + config.public.apiBase kullan
658
+ const config = useRuntimeConfig()
659
+ const toast = useToast()
660
+ const { locale } = useI18n()
661
+
662
+ // Addon i18n — locale dosyalarını doğrudan import et
663
+ const t = (key: string): string => {
664
+ const map: Record<string, any> = { en: enLocale, tr: trLocale }
665
+ let val: any = map[locale.value] ?? map['en']
666
+ for (const k of key.split('.')) val = val?.[k]
667
+ return typeof val === 'string' ? val : key
668
+ }
669
+
670
+ const items = ref<any[]>([])
671
+ const loading = ref(false)
672
+ const saving = ref(false)
673
+ const showModal = ref(false)
674
+ const editing = ref<any>(null)
675
+ const form = ref({ name: '', description: '' })
676
+
677
+ async function loadItems() {
678
+ loading.value = true
679
+ try {
680
+ const res = await $fetch<any>(\`\${config.public.apiBase}/${slug}/items\`)
681
+ if (res.success) items.value = res.data?.items ?? res.data ?? []
682
+ } catch (e: any) {
683
+ toast.add({ title: t('${camel}.messages.error'), color: 'error' })
684
+ } finally {
685
+ loading.value = false
686
+ }
687
+ }
688
+
689
+ function openCreate() {
690
+ editing.value = null
691
+ form.value = { name: '', description: '' }
692
+ showModal.value = true
693
+ }
694
+
695
+ function openEdit(item: any) {
696
+ editing.value = item
697
+ form.value = { name: item.name, description: item.description ?? '' }
698
+ showModal.value = true
699
+ }
700
+
701
+ async function saveItem() {
702
+ if (!form.value.name.trim()) return
703
+ saving.value = true
704
+ try {
705
+ if (editing.value) {
706
+ await $fetch(\`\${config.public.apiBase}/${slug}/items/\${editing.value.id}\`, {
707
+ method: 'PATCH', body: form.value,
708
+ })
709
+ toast.add({ title: t('${camel}.messages.updated'), color: 'success' })
710
+ } else {
711
+ await $fetch(\`\${config.public.apiBase}/${slug}/items\`, {
712
+ method: 'POST', body: form.value,
713
+ })
714
+ toast.add({ title: t('${camel}.messages.created'), color: 'success' })
715
+ }
716
+ showModal.value = false
717
+ await loadItems()
718
+ } catch (e: any) {
719
+ toast.add({ title: t('${camel}.messages.error'), color: 'error' })
720
+ } finally {
721
+ saving.value = false
722
+ }
723
+ }
724
+
725
+ async function removeItem(id: string) {
726
+ try {
727
+ await $fetch(\`\${config.public.apiBase}/${slug}/items/\${id}\`, { method: 'DELETE' })
728
+ toast.add({ title: t('${camel}.messages.deleted'), color: 'success' })
729
+ await loadItems()
730
+ } catch {
731
+ toast.add({ title: t('${camel}.messages.error'), color: 'error' })
732
+ }
733
+ }
734
+
735
+ function formatDate(d: string): string {
736
+ return new Date(d).toLocaleDateString(locale.value === 'tr' ? 'tr-TR' : 'en-US', {
737
+ year: 'numeric', month: 'short', day: 'numeric',
738
+ })
739
+ }
740
+
741
+ onMounted(() => loadItems())
742
+ </script>
743
+ `;
744
+ }
745
+ // ─────────────────────────────────────────────────────────────────────────────
746
+ function genWebPage(name, slug, camel) {
747
+ return `<template>
748
+ <div class="space-y-6">
749
+ <div>
750
+ <h1 class="text-2xl font-bold text-gray-900 dark:text-white">
751
+ {{ t('${camel}.title') }}
752
+ </h1>
753
+ <p class="text-sm text-gray-500 dark:text-gray-400 mt-1">
754
+ {{ t('${camel}.description') }}
755
+ </p>
756
+ </div>
757
+
758
+ <UCard>
759
+ <div class="text-center py-8">
760
+ <Icon name="i-lucide-puzzle" class="size-14 text-primary mx-auto mb-4" />
761
+ <h2 class="text-lg font-semibold mb-2">{{ t('${camel}.web.greeting') }}</h2>
762
+ <p class="text-gray-500 mb-6">{{ t('${camel}.web.subtitle') }}</p>
763
+
764
+ <UButton :loading="loading" icon="i-lucide-send" @click="fetchPublic">
765
+ {{ t('${camel}.web.fetch_button') }}
766
+ </UButton>
767
+
768
+ <div v-if="message"
769
+ class="mt-4 p-3 rounded-lg bg-green-50 dark:bg-green-900/20
770
+ text-green-700 dark:text-green-400 text-sm font-medium">
771
+ {{ message }}
772
+ </div>
773
+ </div>
774
+ </UCard>
775
+ </div>
776
+ </template>
777
+
778
+ <script setup lang="ts">
779
+ import { ref } from 'vue'
780
+ import { useRuntimeConfig, useI18n } from '@cicore/shared'
781
+ import enLocale from '../../locales/en.json'
782
+ import trLocale from '../../locales/tr.json'
783
+
784
+ const config = useRuntimeConfig()
785
+ const { locale } = useI18n()
786
+
787
+ const t = (key: string): string => {
788
+ const map: Record<string, any> = { en: enLocale, tr: trLocale }
789
+ let val: any = map[locale.value] ?? map['en']
790
+ for (const k of key.split('.')) val = val?.[k]
791
+ return typeof val === 'string' ? val : key
792
+ }
793
+
794
+ const loading = ref(false)
795
+ const message = ref('')
796
+
797
+ async function fetchPublic() {
798
+ loading.value = true
799
+ try {
800
+ const res = await $fetch<any>(\`\${config.public.apiBase}/${slug}/web/message\`)
801
+ if (res.success) message.value = res.message
802
+ } catch {
803
+ message.value = 'API bağlantı hatası'
804
+ } finally {
805
+ loading.value = false
806
+ }
807
+ }
808
+ </script>
809
+ `;
810
+ }
811
+ // ─────────────────────────────────────────────────────────────────────────────
812
+ function genEntryTs(name) {
813
+ return `// ${name} Addon — ESM Entry Point
814
+ import './style.css'
815
+
816
+ const route_admin_index = () => import('../pages/admin/index.vue')
817
+ const route_web_index = () => import('../pages/web/index.vue')
818
+
819
+ const locale_en = () => import('../locales/en.json')
820
+ const locale_tr = () => import('../locales/tr.json')
821
+
822
+ const entry = {
823
+ routes: {
824
+ 'admin/index': route_admin_index,
825
+ 'web/index': route_web_index,
826
+ },
827
+ locales: {
828
+ en: locale_en,
829
+ tr: locale_tr,
830
+ },
831
+ }
832
+
833
+ export { entry }
834
+ export default entry
835
+ `;
836
+ }
837
+ // ─────────────────────────────────────────────────────────────────────────────
838
+ function genStyleCss(name) {
839
+ return `/* ${name} Addon Styles */
840
+ /* Addon'a özel global CSS buraya yazılır */
841
+ /* Tailwind sınıfları Vue dosyalarında direkt kullanılır */
842
+ `;
843
+ }
844
+ // ─────────────────────────────────────────────────────────────────────────────
845
+ function genViteConfig(name) {
846
+ return `import { defineConfig } from 'vite'
847
+ import vue from '@vitejs/plugin-vue'
848
+ import { resolve } from 'path'
849
+
850
+ export default defineConfig({
851
+ base: './',
852
+ define: {
853
+ process: JSON.stringify({ env: { NODE_ENV: 'production' } }),
854
+ 'process.env': JSON.stringify({ NODE_ENV: 'production' }),
855
+ 'process.env.NODE_ENV': JSON.stringify('production'),
856
+ global: 'globalThis',
857
+ },
858
+ plugins: [vue({ script: { defineModel: true } })],
859
+ resolve: {
860
+ alias: {
861
+ '@': resolve(__dirname),
862
+ vue: '@cicore/shared',
863
+ '#imports': '@cicore/shared',
864
+ '@vue/devtools-api': resolve(__dirname, 'empty-devtools.js'),
865
+ },
866
+ },
867
+ build: {
868
+ lib: {
869
+ entry: resolve(__dirname, 'ui/entry.ts'),
870
+ formats: ['es'],
871
+ fileName: () => 'entry.js',
872
+ },
873
+ outDir: 'dist',
874
+ emptyOutDir: true,
875
+ minify: 'esbuild',
876
+ sourcemap: false,
877
+ modulePreload: false,
878
+ rollupOptions: {
879
+ preserveEntrySignatures: 'strict',
880
+ external(id, _parentId, isResolved) {
881
+ if (id.includes('entry.ts') || id.includes('entry.js')) return false
882
+ if (id.includes('?vue&type=')) return false
883
+ if (id.startsWith('./') || id.startsWith('../')) return false
884
+ if (isResolved && (id.includes('/addons/') || id.includes('\\\\addons\\\\'))) return false
885
+ if (id.includes('/addons/') || id.includes('\\\\addons\\\\')) return false
886
+ if (id === 'vue' || id.startsWith('vue/')) return true
887
+ if (id === '@vue/devtools-api' || id.startsWith('@vue/devtools-api/')) return false
888
+ if (id.startsWith('@vue/')) return true
889
+ if (id === '@cicore/shared' || id.startsWith('@cicore/')) return true
890
+ if (id === 'pinia' || id.startsWith('pinia/')) return true
891
+ if (!id.startsWith('.') && !id.startsWith('/') && !id.includes(':')) return true
892
+ return false
893
+ },
894
+ output: {
895
+ format: 'es',
896
+ entryFileNames: 'entry.js',
897
+ chunkFileNames: 'chunks/[name]-[hash].js',
898
+ paths: { vue: '@cicore/shared' },
899
+ assetFileNames: (a) =>
900
+ /\\.(png|jpe?g|gif|svg|webp|ico)$/i.test(a.name ?? '')
901
+ ? 'assets/img/[name]-[hash][extname]'
902
+ : 'assets/[name]-[hash][extname]',
903
+ exports: 'named',
904
+ manualChunks(id) {
905
+ if (id.includes('/pages/')) {
906
+ const m = id.match(/pages\\/(.+?)\\.vue/)
907
+ if (m) return m[1].replace(/\\//g, '-')
908
+ }
909
+ if (id.includes('/locales/')) {
910
+ const m = id.match(/locales\\/(.+?)\\.json/)
911
+ if (m) return m[1]
912
+ }
913
+ },
914
+ },
915
+ },
916
+ },
917
+ })
918
+ `;
919
+ }
920
+ // ─────────────────────────────────────────────────────────────────────────────
921
+ function genLocaleEn(camel, display) {
922
+ return JSON.stringify({
923
+ [camel]: {
924
+ title: display,
925
+ description: `${display} addon for ciCore`,
926
+ empty: 'No items yet',
927
+ form: {
928
+ name: 'Name',
929
+ description: 'Description',
930
+ },
931
+ actions: {
932
+ create: 'Create New',
933
+ edit: 'Edit',
934
+ delete: 'Delete',
935
+ save: 'Save',
936
+ cancel: 'Cancel',
937
+ },
938
+ web: {
939
+ greeting: `Hello from ${display}!`,
940
+ subtitle: 'Public web interface',
941
+ fetch_button: 'Fetch Message',
942
+ },
943
+ messages: {
944
+ created: 'Created successfully',
945
+ updated: 'Updated successfully',
946
+ deleted: 'Deleted successfully',
947
+ error: 'An error occurred',
948
+ },
949
+ },
950
+ }, null, 2) + '\n';
951
+ }
952
+ function genLocaleTr(camel, display) {
953
+ return JSON.stringify({
954
+ [camel]: {
955
+ title: display,
956
+ description: `ciCore için${display} addon'u`,
957
+ empty: 'Henüz öğe yok',
958
+ form: {
959
+ name: 'İsim',
960
+ description: 'Açıklama',
961
+ },
962
+ actions: {
963
+ create: 'Yeni Oluştur',
964
+ edit: 'Düzenle',
965
+ delete: 'Sil',
966
+ save: 'Kaydet',
967
+ cancel: 'İptal',
968
+ },
969
+ web: {
970
+ greeting: `${display}'dan merhaba!`,
971
+ subtitle: 'Genel web arayüzü',
972
+ fetch_button: 'Mesajı Getir',
973
+ },
974
+ messages: {
975
+ created: 'Başarıyla oluşturuldu',
976
+ updated: 'Başarıyla güncellendi',
977
+ deleted: 'Başarıyla silindi',
978
+ error: 'Bir hata oluştu',
979
+ },
980
+ },
981
+ }, null, 2) + '\n';
982
+ }
983
+ // ─── Faz 5 quality stack generators ─────────────────────────────────
984
+ // Moved to lib/ops/addon-quality.ts (Faz 5B) so the scaffold-quality
985
+ // command can reuse them. Local definitions below remain for now to
986
+ // avoid breaking the diff — they're now dead code and will be removed
987
+ // in a follow-up cleanup.
988
+ function _unused_genEslintConfig() {
989
+ return {
990
+ root: true,
991
+ env: { browser: true, es2022: true, node: true },
992
+ parser: 'vue-eslint-parser',
993
+ parserOptions: {
994
+ parser: '@typescript-eslint/parser',
995
+ ecmaVersion: 'latest',
996
+ sourceType: 'module',
997
+ extraFileExtensions: ['.vue'],
998
+ },
999
+ plugins: ['vue', '@typescript-eslint'],
1000
+ extends: [
1001
+ 'eslint:recommended',
1002
+ 'plugin:vue/vue3-recommended',
1003
+ 'plugin:@typescript-eslint/recommended',
1004
+ ],
1005
+ rules: {
1006
+ // Single-word component names are common in addon page files
1007
+ // (index.vue, list.vue). Vue's default rule would force PascalCase
1008
+ // multi-word; ergonomically wrong for our routing layout.
1009
+ 'vue/multi-word-component-names': 'off',
1010
+ 'vue/html-self-closing': ['error', { html: { void: 'always', normal: 'always', component: 'always' } }],
1011
+ // any-warnings rather than errors so addon authors can ship a TODO
1012
+ // explicitly; the warning surfaces in the deploy quality gate.
1013
+ '@typescript-eslint/no-explicit-any': 'warn',
1014
+ 'no-unused-vars': 'off',
1015
+ '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
1016
+ 'prefer-const': 'error',
1017
+ 'no-var': 'error',
1018
+ },
1019
+ ignorePatterns: ['dist/', 'node_modules/', '.vite-cache/', '*.d.ts'],
1020
+ };
1021
+ }
1022
+ function _unused_genPrettierConfig() {
1023
+ return {
1024
+ semi: false,
1025
+ singleQuote: true,
1026
+ tabWidth: 2,
1027
+ printWidth: 100,
1028
+ trailingComma: 'es5',
1029
+ vueIndentScriptAndStyle: false,
1030
+ endOfLine: 'lf',
1031
+ };
1032
+ }
1033
+ function _unused_genEditorConfig() {
1034
+ return `root = true
1035
+
1036
+ [*]
1037
+ charset = utf-8
1038
+ end_of_line = lf
1039
+ indent_style = space
1040
+ indent_size = 2
1041
+ insert_final_newline = true
1042
+ trim_trailing_whitespace = true
1043
+
1044
+ [*.md]
1045
+ trim_trailing_whitespace = false
1046
+ `;
1047
+ }
1048
+ function _unused_genTsConfig() {
1049
+ return {
1050
+ compilerOptions: {
1051
+ target: 'ES2022',
1052
+ module: 'ESNext',
1053
+ moduleResolution: 'Bundler',
1054
+ strict: true,
1055
+ noUncheckedIndexedAccess: true,
1056
+ noImplicitOverride: true,
1057
+ esModuleInterop: true,
1058
+ skipLibCheck: true,
1059
+ isolatedModules: true,
1060
+ resolveJsonModule: true,
1061
+ noEmit: true,
1062
+ jsx: 'preserve',
1063
+ lib: ['ES2022', 'DOM', 'DOM.Iterable'],
1064
+ // vue types resolved from dev-shell/node_modules/vue (parent
1065
+ // lookup) so the addon doesn't need its own node_modules.
1066
+ types: ['vue'],
1067
+ paths: {
1068
+ '@/*': ['./*'],
1069
+ },
1070
+ },
1071
+ include: ['**/*.ts', '**/*.vue', 'ui/entry.ts'],
1072
+ exclude: ['node_modules', 'dist', '.vite-cache'],
1073
+ };
1074
+ }
1075
+ /**
1076
+ * Ambient type declarations for the addon source. The runtime resolves
1077
+ * `@cicore/shared` via the host's import map and `$fetch` is Nuxt's
1078
+ * global, but vue-tsc has no way to find either without these shims.
1079
+ * Declared as `any` for now — Faz 6 (SDK docs) ships richer type
1080
+ * surfaces alongside the API reference and replaces these stubs.
1081
+ */
1082
+ function _unused_genAmbientTypes() {
1083
+ return `// Ambient types for ciCore addon runtime. Replaced by the real
1084
+ // @cicore/shared types when the SDK package ships (Faz 6 FUTURE).
1085
+
1086
+ declare module '@cicore/shared' {
1087
+ // Runtime exports come from the host's import map — every Vue Composition
1088
+ // API name, the useApi/useAuth/useI18n composables, and the Nuxt UI
1089
+ // components are re-exported here. Typed as any to keep new addons
1090
+ // unblocked; Faz 6 (SDK docs) replaces this stub with typed surfaces.
1091
+ //
1092
+ // NOTE: TypeScript doesn't have a "catch-all named exports" syntax, so
1093
+ // every name an addon imports must appear here. If vue-tsc rejects an
1094
+ // import you need, add it to this list (and to docs/addon-sdk in Faz 6).
1095
+ export const ref: any
1096
+ export const reactive: any
1097
+ export const computed: any
1098
+ export const watch: any
1099
+ export const watchEffect: any
1100
+ export const onMounted: any
1101
+ export const onUnmounted: any
1102
+ export const onBeforeMount: any
1103
+ export const onBeforeUnmount: any
1104
+ export const nextTick: any
1105
+ export const defineComponent: any
1106
+ export const defineProps: any
1107
+ export const defineEmits: any
1108
+ export const useI18n: any
1109
+ export const useAuth: any
1110
+ export const useApi: any
1111
+ export const useAddons: any
1112
+ export const useTenant: any
1113
+ export const usePermissions: any
1114
+ export const useIcon: any
1115
+ export const useNuxtUI: any
1116
+ export const useRuntimeConfig: any
1117
+ export const useToast: any
1118
+ export const useRouter: any
1119
+ export const useRoute: any
1120
+ export const useGsap: any
1121
+ export const Icon: any
1122
+ export const UButton: any
1123
+ export const UCard: any
1124
+ export const UInput: any
1125
+ export const UInputMenu: any
1126
+ export const UModal: any
1127
+ export const USelectMenu: any
1128
+ export const USwitch: any
1129
+ export const UAlert: any
1130
+ export const UBadge: any
1131
+ export const UTooltip: any
1132
+ export const UFieldGroup: any
1133
+ export const UForm: any
1134
+ export const UFormField: any
1135
+ export const UTextarea: any
1136
+ export const UCheckbox: any
1137
+ export const UTable: any
1138
+ export const UPagination: any
1139
+ export const UAvatar: any
1140
+ export const UDropdownMenu: any
1141
+ const _default: any
1142
+ export default _default
1143
+ }
1144
+
1145
+ // Nuxt global available to addon pages without explicit import.
1146
+ declare const $fetch: <T = unknown>(url: string, opts?: Record<string, unknown>) => Promise<T>
1147
+
1148
+ // Vue SFC module shim so .vue imports resolve to a component type.
1149
+ declare module '*.vue' {
1150
+ import type { DefineComponent } from 'vue'
1151
+ const component: DefineComponent<Record<string, unknown>, Record<string, unknown>, unknown>
1152
+ export default component
1153
+ }
1154
+ `;
1155
+ }
1156
+ function _unused_genGitlabCi(addonName) {
1157
+ return `# Auto-generated by \`ci addon create ${addonName}\`. Faz 5 quality template.
1158
+ # Runs lint + typecheck on every MR + on default branch. Tooling resolves
1159
+ # from the addon-dev-shell repo's node_modules (cached across pipelines).
1160
+
1161
+ stages:
1162
+ - check
1163
+
1164
+ quality:
1165
+ stage: check
1166
+ image: node:22-alpine
1167
+ cache:
1168
+ key: addon-dev-shell-tools
1169
+ paths: [dev-shell-node_modules/]
1170
+ before_script:
1171
+ # The addon ships with no local node_modules — clone the shared
1172
+ # toolchain from cicore-addons/addon-dev-shell at the same commit
1173
+ # personnel uses locally. Pin to a tag if you need reproducibility.
1174
+ - apk add --no-cache git
1175
+ - git clone --depth 1 http://10.20.20.28/cicore-addons/addon-dev-shell tmp-shell
1176
+ - mv tmp-shell/package.json tmp-shell/package-lock.json ./
1177
+ - npm ci --no-audit --no-fund --prefer-offline
1178
+ script:
1179
+ - ./node_modules/.bin/eslint . --max-warnings 0
1180
+ - ./node_modules/.bin/vue-tsc --noEmit
1181
+ rules:
1182
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event"
1183
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
1184
+ `;
1185
+ }
1186
+ //# sourceMappingURL=create.js.map