@team-codeme/crud-forge 0.0.0-stage → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (415) hide show
  1. package/.cursor/agents/generator-engineer.md +31 -0
  2. package/.cursor/agents/keycloak-engineer.md +33 -0
  3. package/.cursor/agents/qa-engineer.md +32 -0
  4. package/.cursor/agents/security-reviewer.md +30 -0
  5. package/.cursor/mcp.json +13 -0
  6. package/.cursor/rules/crudforge-precautions.mdc +16 -0
  7. package/.cursor/rules/playwright-browser.mdc +30 -0
  8. package/.cursor/rules/readme-todo-sync.mdc +13 -0
  9. package/.env.qa.example +19 -0
  10. package/.github/workflows/qa-live.yml +32 -0
  11. package/.github/workflows/qa.yml +27 -0
  12. package/.vscode/settings.json +32 -0
  13. package/QA/IAM-PLAN.md +25 -0
  14. package/QA/IAM-RESULTS.md +39 -0
  15. package/QA/IAM-TESTCASES.md +36 -0
  16. package/QA/IAM-TODO.md +7 -0
  17. package/QA/KEYCLOAK-BOOTSTRAP.md +105 -0
  18. package/QA/PLAN.md +43 -0
  19. package/QA/RESULTS.md +59 -0
  20. package/QA/TESTCASES.md +65 -0
  21. package/QA/TODO.md +14 -0
  22. package/QA/pmb-dev/README.md +49 -0
  23. package/QA/pmb-dev/config/config.yml +152 -0
  24. package/QA/pmb-dev/gdl/i.gdl +17 -0
  25. package/README.md +851 -2
  26. package/Todo.md +207 -0
  27. package/assets/branding/crudforge-logo.png +0 -0
  28. package/assets/branding/crudforge-mark.png +0 -0
  29. package/branding/poster-crudforge.jpg +0 -0
  30. package/chat/last-chat/Investigating Mock Data Configuration.md +428 -0
  31. package/cli.js +167 -0
  32. package/config/default-config.yml +245 -0
  33. package/docs/performance/README.md +217 -0
  34. package/docs/performance/after.json +111 -0
  35. package/docs/performance/before.json +111 -0
  36. package/docs/quality/README.md +276 -0
  37. package/e2e/crudforge.e2e.yml +47 -0
  38. package/e2e/crudforge.field-profile.yml +33 -0
  39. package/e2e/crudforge.iam-live.yml +124 -0
  40. package/e2e/crudforge.iam.yml +62 -0
  41. package/e2e/tests/field-profile.spec.js +152 -0
  42. package/e2e/tests/generated-crud.spec.js +251 -0
  43. package/e2e/tests/iam-live.spec.js +149 -0
  44. package/e2e/tests/iam-pack.spec.js +151 -0
  45. package/e2e/tests/list-state.spec.js +73 -0
  46. package/features.csv +27 -0
  47. package/fuse-starter-pack/branding/crudforge-mark-source.jpg +0 -0
  48. package/fuse-starter-pack/branding/crudforge-mark-source.png +0 -0
  49. package/fuse-starter-pack/branding/crudforge-mark.png +0 -0
  50. package/fuse-starter-pack/branding/crudforge-wordmark-source.jpg +0 -0
  51. package/fuse-starter-pack/branding/crudforge-wordmark-source.png +0 -0
  52. package/fuse-starter-pack/branding/crudforge-wordmark.png +0 -0
  53. package/fuse-starter-pack/fuse-v21.1/Dockerfile +14 -0
  54. package/fuse-starter-pack/fuse-v21.1/LICENSE.md +6 -0
  55. package/fuse-starter-pack/fuse-v21.1/README.md +59 -0
  56. package/fuse-starter-pack/fuse-v21.1/angular.json +113 -0
  57. package/fuse-starter-pack/fuse-v21.1/eslint.config.ts +150 -0
  58. package/fuse-starter-pack/fuse-v21.1/nginx.conf +23 -0
  59. package/fuse-starter-pack/fuse-v21.1/nginx.dev.conf +18 -0
  60. package/fuse-starter-pack/fuse-v21.1/package-lock.json +11513 -0
  61. package/fuse-starter-pack/fuse-v21.1/package.json +69 -0
  62. package/fuse-starter-pack/fuse-v21.1/postcss.config.json +5 -0
  63. package/fuse-starter-pack/fuse-v21.1/prettier.config.ts +15 -0
  64. package/fuse-starter-pack/fuse-v21.1/proxy.conf.json +8 -0
  65. package/fuse-starter-pack/fuse-v21.1/proxy.dev.conf.json +8 -0
  66. package/fuse-starter-pack/fuse-v21.1/public/favicon.png +0 -0
  67. package/fuse-starter-pack/fuse-v21.1/public/fonts/geist/Geist-Italic.woff2 +0 -0
  68. package/fuse-starter-pack/fuse-v21.1/public/fonts/geist/Geist.woff2 +0 -0
  69. package/fuse-starter-pack/fuse-v21.1/public/fonts/geist/GeistMono-Italic.woff2 +0 -0
  70. package/fuse-starter-pack/fuse-v21.1/public/fonts/geist/GeistMono.woff2 +0 -0
  71. package/fuse-starter-pack/fuse-v21.1/public/i18n/en.json +8 -0
  72. package/fuse-starter-pack/fuse-v21.1/public/i18n/es.json +8 -0
  73. package/fuse-starter-pack/fuse-v21.1/public/images/flags/ES.svg +79 -0
  74. package/fuse-starter-pack/fuse-v21.1/public/images/flags/US.svg +14 -0
  75. package/fuse-starter-pack/fuse-v21.1/public/images/flags/where-to-find-other-flags.txt +5 -0
  76. package/fuse-starter-pack/fuse-v21.1/public/images/logo/logo-mark.png +0 -0
  77. package/fuse-starter-pack/fuse-v21.1/public/images/logo/logo.png +0 -0
  78. package/fuse-starter-pack/fuse-v21.1/push.sh +27 -0
  79. package/fuse-starter-pack/fuse-v21.1/src/app/app.config.server.ts +10 -0
  80. package/fuse-starter-pack/fuse-v21.1/src/app/app.config.ts +64 -0
  81. package/fuse-starter-pack/fuse-v21.1/src/app/app.routes.server.ts +8 -0
  82. package/fuse-starter-pack/fuse-v21.1/src/app/app.routes.ts +39 -0
  83. package/fuse-starter-pack/fuse-v21.1/src/app/app.ts +22 -0
  84. package/fuse-starter-pack/fuse-v21.1/src/app/core/icons/index.ts +1 -0
  85. package/fuse-starter-pack/fuse-v21.1/src/app/core/icons/lucide.ts +1 -0
  86. package/fuse-starter-pack/fuse-v21.1/src/app/core/icons/provider.ts +23 -0
  87. package/fuse-starter-pack/fuse-v21.1/src/app/core/local-storage/index.ts +2 -0
  88. package/fuse-starter-pack/fuse-v21.1/src/app/core/local-storage/local-storage.ts +109 -0
  89. package/fuse-starter-pack/fuse-v21.1/src/app/core/local-storage/provider.ts +15 -0
  90. package/fuse-starter-pack/fuse-v21.1/src/app/core/media/index.ts +2 -0
  91. package/fuse-starter-pack/fuse-v21.1/src/app/core/media/media.ts +64 -0
  92. package/fuse-starter-pack/fuse-v21.1/src/app/core/media/provider.ts +15 -0
  93. package/fuse-starter-pack/fuse-v21.1/src/app/core/theming/index.ts +3 -0
  94. package/fuse-starter-pack/fuse-v21.1/src/app/core/theming/models/theming.ts +13 -0
  95. package/fuse-starter-pack/fuse-v21.1/src/app/core/theming/palette.ts +209 -0
  96. package/fuse-starter-pack/fuse-v21.1/src/app/core/theming/provider.ts +24 -0
  97. package/fuse-starter-pack/fuse-v21.1/src/app/core/theming/theming.ts +129 -0
  98. package/fuse-starter-pack/fuse-v21.1/src/app/core/transloco/transloco-http-loader.ts +12 -0
  99. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/layout/data/navigation.ts +176 -0
  100. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/layout/layout.ts +77 -0
  101. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/layout/ui/language-switcher.ts +91 -0
  102. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/layout/ui/navigation.ts +238 -0
  103. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/layout/ui/notifications.ts +252 -0
  104. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/layout/ui/scheme-switcher.ts +58 -0
  105. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/layout/ui/sidebar.ts +31 -0
  106. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/layout/ui/user.ts +132 -0
  107. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/academy/data/academy.ts +753 -0
  108. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/academy/data/model.ts +27 -0
  109. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/academy/features/course.ts +318 -0
  110. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/academy/features/courses.ts +228 -0
  111. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/academy/routes.ts +14 -0
  112. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/contacts/data/contacts.ts +4372 -0
  113. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/contacts/data/model.ts +34 -0
  114. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/contacts/features/contact.ts +239 -0
  115. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/contacts/features/contacts.ts +185 -0
  116. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/contacts/routes.ts +16 -0
  117. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/file-manager/data/file-manager.ts +261 -0
  118. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/file-manager/data/model.ts +12 -0
  119. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/file-manager/features/file-manager.ts +131 -0
  120. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/file-manager/routes.ts +15 -0
  121. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/file-manager/ui/list.ts +164 -0
  122. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/help-center/features/faq.ts +288 -0
  123. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/help-center/features/guide.ts +134 -0
  124. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/help-center/features/home.ts +219 -0
  125. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/help-center/features/support.ts +161 -0
  126. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/help-center/routes.ts +33 -0
  127. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/notes/data/model.ts +18 -0
  128. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/notes/data/notes.ts +324 -0
  129. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/notes/features/note.ts +152 -0
  130. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/notes/features/notes.ts +118 -0
  131. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/notes/routes.ts +16 -0
  132. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/tasks/data/model.ts +17 -0
  133. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/tasks/data/tasks.ts +562 -0
  134. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/tasks/features/task.ts +195 -0
  135. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/tasks/features/tasks.ts +271 -0
  136. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/tasks/routes.ts +16 -0
  137. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/dashboards/data/analytics.ts +1620 -0
  138. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/dashboards/data/finance.ts +1065 -0
  139. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/dashboards/data/project.ts +376 -0
  140. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/dashboards/features/analytics/analytics.ts +1096 -0
  141. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/dashboards/features/finance/finance.ts +658 -0
  142. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/dashboards/features/project/budget-table.ts +188 -0
  143. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/dashboards/features/project/project.ts +565 -0
  144. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/dashboards/routes.ts +23 -0
  145. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/documentation/features/changelog/changelog.ts +932 -0
  146. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/documentation/features/getting-started/building.ts +33 -0
  147. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/documentation/features/getting-started/development.ts +42 -0
  148. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/documentation/features/getting-started/installation.ts +47 -0
  149. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/documentation/layout.ts +9 -0
  150. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/documentation/routes.ts +45 -0
  151. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/error/features/error-page.ts +70 -0
  152. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/error/routes.ts +21 -0
  153. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/notifications/features/notifications.ts +262 -0
  154. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/notifications/routes.ts +10 -0
  155. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/settings/features/account.ts +238 -0
  156. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/settings/features/notifications.ts +134 -0
  157. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/settings/features/plan-and-billing.ts +238 -0
  158. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/settings/features/security.ts +211 -0
  159. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/settings/features/team.ts +188 -0
  160. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/settings/layout.ts +109 -0
  161. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/settings/routes.ts +37 -0
  162. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/welcome/welcome.component.ts +22 -0
  163. package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/routes.ts +95 -0
  164. package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/features/forgot-password/forgot-password.html +54 -0
  165. package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/features/forgot-password/forgot-password.ts +51 -0
  166. package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/features/reset-password/reset-password.html +82 -0
  167. package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/features/reset-password/reset-password.ts +69 -0
  168. package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/features/sign-in/sign-in.html +132 -0
  169. package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/features/sign-in/sign-in.ts +54 -0
  170. package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/features/sign-up/sign-up.html +183 -0
  171. package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/features/sign-up/sign-up.ts +56 -0
  172. package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/layout.ts +9 -0
  173. package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/routes.ts +34 -0
  174. package/fuse-starter-pack/fuse-v21.1/src/app/domains/coming-soon/features/coming-soon.ts +156 -0
  175. package/fuse-starter-pack/fuse-v21.1/src/app/domains/coming-soon/layout.ts +9 -0
  176. package/fuse-starter-pack/fuse-v21.1/src/app/domains/coming-soon/routes.ts +16 -0
  177. package/fuse-starter-pack/fuse-v21.1/src/app/domains/website/layout/layout.ts +9 -0
  178. package/fuse-starter-pack/fuse-v21.1/src/app/domains/website/modules/home/features/home.ts +23 -0
  179. package/fuse-starter-pack/fuse-v21.1/src/app/domains/website/modules/home/routes.ts +10 -0
  180. package/fuse-starter-pack/fuse-v21.1/src/app/domains/website/routes.ts +30 -0
  181. package/fuse-starter-pack/fuse-v21.1/src/environments/environment.mock.ts +5 -0
  182. package/fuse-starter-pack/fuse-v21.1/src/environments/environment.prod.ts +5 -0
  183. package/fuse-starter-pack/fuse-v21.1/src/environments/environment.ts +5 -0
  184. package/fuse-starter-pack/fuse-v21.1/src/index.html +66 -0
  185. package/fuse-starter-pack/fuse-v21.1/src/main.server.ts +11 -0
  186. package/fuse-starter-pack/fuse-v21.1/src/main.ts +5 -0
  187. package/fuse-starter-pack/fuse-v21.1/src/server.ts +70 -0
  188. package/fuse-starter-pack/fuse-v21.1/src/styles/base/base.css +63 -0
  189. package/fuse-starter-pack/fuse-v21.1/src/styles/base/typography.css +54 -0
  190. package/fuse-starter-pack/fuse-v21.1/src/styles/components/a.css +18 -0
  191. package/fuse-starter-pack/fuse-v21.1/src/styles/components/input.css +22 -0
  192. package/fuse-starter-pack/fuse-v21.1/src/styles/components/kbd.css +55 -0
  193. package/fuse-starter-pack/fuse-v21.1/src/styles/components/material.css +1149 -0
  194. package/fuse-starter-pack/fuse-v21.1/src/styles/styles.css +31 -0
  195. package/fuse-starter-pack/fuse-v21.1/src/styles/tailwind/theme.css +147 -0
  196. package/fuse-starter-pack/fuse-v21.1/src/styles/tailwind/utilities.css +563 -0
  197. package/fuse-starter-pack/fuse-v21.1/src/styles/tailwind/variants.css +4 -0
  198. package/fuse-starter-pack/fuse-v21.1/src/styles/third-party/lucide-icons.css +12 -0
  199. package/fuse-starter-pack/fuse-v21.1/tsconfig.app.json +16 -0
  200. package/fuse-starter-pack/fuse-v21.1/tsconfig.json +37 -0
  201. package/fuse-starter-pack/fuse-v21.1/tsconfig.spec.json +10 -0
  202. package/generator/README.md +12 -0
  203. package/generator/issues/.gitkeep +0 -0
  204. package/package.json +56 -4
  205. package/playwright.config.js +29 -0
  206. package/playwright.field-profile.config.js +6 -0
  207. package/playwright.iam-live.config.js +31 -0
  208. package/playwright.iam.config.js +29 -0
  209. package/playwright.list-state.config.js +6 -0
  210. package/playwright.performance.config.js +1 -0
  211. package/run-config-tests.sh +5 -0
  212. package/scripts/e2e-paths.js +89 -0
  213. package/scripts/e2e-prepare-field-profile.js +17 -0
  214. package/scripts/e2e-prepare-iam-live.js +135 -0
  215. package/scripts/e2e-prepare-iam.js +65 -0
  216. package/scripts/e2e-prepare.js +72 -0
  217. package/scripts/e2e-serve-iam-live.js +28 -0
  218. package/scripts/e2e-serve-iam.js +28 -0
  219. package/scripts/e2e-serve.js +28 -0
  220. package/scripts/performance/generate.js +17 -0
  221. package/scripts/performance/measure.js +55 -0
  222. package/scripts/postinstall.js +9 -0
  223. package/scripts/prepare-quality-apps.js +34 -0
  224. package/scripts/run-config-tests.js +63 -0
  225. package/src/assets/logo-with-lable.jpeg +0 -0
  226. package/src/assets/logo.jpeg +0 -0
  227. package/src/commands/bootstrap.js +92 -0
  228. package/src/commands/doctor.js +403 -0
  229. package/src/commands/generate.js +301 -0
  230. package/src/commands/init-config.js +310 -0
  231. package/src/commands/keycloak-options.js +182 -0
  232. package/src/commands/validate.js +52 -0
  233. package/src/config/auth-config.js +183 -0
  234. package/src/config/default-values.js +235 -0
  235. package/src/config/env-refs.js +71 -0
  236. package/src/config/field-preferences.js +99 -0
  237. package/src/config/input-sources.js +104 -0
  238. package/src/config/load-config.js +166 -0
  239. package/src/config/proxy-routes.js +44 -0
  240. package/src/generator/access-control-tools.js +334 -0
  241. package/src/generator/ai-docs.js +691 -0
  242. package/src/generator/application-tests.js +252 -0
  243. package/src/generator/auth-pack.js +965 -0
  244. package/src/generator/embed-config.js +431 -0
  245. package/src/generator/entity-layout.js +30 -0
  246. package/src/generator/fuse-build.js +57 -0
  247. package/src/generator/fuse-runtime.js +344 -0
  248. package/src/generator/generated-banner.js +137 -0
  249. package/src/generator/index.js +874 -0
  250. package/src/generator/project-output.js +126 -0
  251. package/src/generator/team-pack.js +220 -0
  252. package/src/index.js +68 -0
  253. package/src/parser/gdl-parser.js +169 -0
  254. package/src/parser/jdl-parser.js +171 -0
  255. package/src/presets/pmb-test.js +139 -0
  256. package/src/presets/static-source.js +73 -0
  257. package/src/presets/store.js +96 -0
  258. package/src/provision/admin-service.js +148 -0
  259. package/src/provision/bootstrap.js +147 -0
  260. package/src/provision/http.js +123 -0
  261. package/src/provision/keycloak-admin-rest.js +335 -0
  262. package/src/provision/state.js +51 -0
  263. package/src/provision/token.js +52 -0
  264. package/src/templates/angular/auth/access-control.service.ts.ejs +179 -0
  265. package/src/templates/angular/auth/access-ctrl.pipe.ts.ejs +20 -0
  266. package/src/templates/angular/auth/auth.guard.ts.ejs +27 -0
  267. package/src/templates/angular/auth/auth.interceptor.ts.ejs +53 -0
  268. package/src/templates/angular/auth/auth.service.ts.ejs +594 -0
  269. package/src/templates/angular/auth/auth.utils.ts.ejs +28 -0
  270. package/src/templates/angular/auth/current-user.ts.ejs +24 -0
  271. package/src/templates/angular/auth/disabled-account-overlay.component.css.ejs +95 -0
  272. package/src/templates/angular/auth/disabled-account-overlay.component.html.ejs +33 -0
  273. package/src/templates/angular/auth/disabled-account-overlay.component.ts.ejs +23 -0
  274. package/src/templates/angular/auth/fuse-account.ts.ejs +234 -0
  275. package/src/templates/angular/auth/fuse-user.ts.ejs +264 -0
  276. package/src/templates/angular/auth/has-permission.directive.ts.ejs +51 -0
  277. package/src/templates/angular/auth/index.ts.ejs +24 -0
  278. package/src/templates/angular/auth/keycloak-admin.service.ts.ejs +670 -0
  279. package/src/templates/angular/auth/landing.guard.ts.ejs +61 -0
  280. package/src/templates/angular/auth/no-auth.guard.ts.ejs +19 -0
  281. package/src/templates/angular/auth/permission-attribute.util.ts.ejs +182 -0
  282. package/src/templates/angular/auth/sign-in.ts.ejs +60 -0
  283. package/src/templates/angular/entity/_entity_.model.ts.ejs +64 -0
  284. package/src/templates/angular/entity/_entity_.routes.ts.ejs +9 -0
  285. package/src/templates/angular/entity/components/forms/form/_entity_-form.component.html.ejs +168 -0
  286. package/src/templates/angular/entity/components/forms/form/_entity_-form.component.ts.ejs +175 -0
  287. package/src/templates/angular/entity/components/forms/form-page/_entity_-form-page.component.html.ejs +10 -0
  288. package/src/templates/angular/entity/components/forms/form-page/_entity_-form-page.component.ts.ejs +86 -0
  289. package/src/templates/angular/entity/components/forms/update/_entity_-form.service.ts.ejs +136 -0
  290. package/src/templates/angular/entity/components/interaction/actions/_entity_-actions.component.html.ejs +68 -0
  291. package/src/templates/angular/entity/components/interaction/actions/_entity_-actions.component.ts.ejs +69 -0
  292. package/src/templates/angular/entity/components/interaction/filter/_entity_-filter.component.html.ejs +111 -0
  293. package/src/templates/angular/entity/components/interaction/filter/_entity_-filter.component.ts.ejs +102 -0
  294. package/src/templates/angular/entity/components/presentation/card/_entity_-card.component.css.ejs +33 -0
  295. package/src/templates/angular/entity/components/presentation/card/_entity_-card.component.html.ejs +29 -0
  296. package/src/templates/angular/entity/components/presentation/card/_entity_-card.component.ts.ejs +93 -0
  297. package/src/templates/angular/entity/components/presentation/detail/_entity_-detail.component.css.ejs +54 -0
  298. package/src/templates/angular/entity/components/presentation/detail/_entity_-detail.component.html.ejs +58 -0
  299. package/src/templates/angular/entity/components/presentation/detail/_entity_-detail.component.ts.ejs +47 -0
  300. package/src/templates/angular/entity/components/presentation/list/_entity_-list.component.html.ejs +419 -0
  301. package/src/templates/angular/entity/components/presentation/list/_entity_-list.component.scss.ejs +48 -0
  302. package/src/templates/angular/entity/components/presentation/list/_entity_-list.component.ts.ejs +592 -0
  303. package/src/templates/angular/entity/service/_entity_.service.ts.ejs +153 -0
  304. package/src/templates/angular/entity/settings/_entity_.settings.ts.ejs +49 -0
  305. package/src/templates/angular/enums/_enum_.model.ts.ejs +11 -0
  306. package/src/templates/angular/iam/change-password.component.html.ejs +39 -0
  307. package/src/templates/angular/iam/change-password.component.ts.ejs +44 -0
  308. package/src/templates/angular/iam/no-access.component.ts.ejs +17 -0
  309. package/src/templates/angular/iam/role-list.component.html.ejs +331 -0
  310. package/src/templates/angular/iam/role-list.component.scss.ejs +107 -0
  311. package/src/templates/angular/iam/role-list.component.ts.ejs +597 -0
  312. package/src/templates/angular/iam/role-management/role-header.component.html.ejs +51 -0
  313. package/src/templates/angular/iam/role-management/role-header.component.ts.ejs +45 -0
  314. package/src/templates/angular/iam/role-management/role-inheritance.component.html.ejs +15 -0
  315. package/src/templates/angular/iam/role-management/role-inheritance.component.ts.ejs +19 -0
  316. package/src/templates/angular/iam/role-management/role-permissions-panel.component.css.ejs +24 -0
  317. package/src/templates/angular/iam/role-management/role-permissions-panel.component.html.ejs +108 -0
  318. package/src/templates/angular/iam/role-management/role-permissions-panel.component.ts.ejs +122 -0
  319. package/src/templates/angular/iam/role-management/role-users.component.html.ejs +104 -0
  320. package/src/templates/angular/iam/role-management/role-users.component.ts.ejs +53 -0
  321. package/src/templates/angular/iam/role-management/user-select-dialog.component.html.ejs +113 -0
  322. package/src/templates/angular/iam/role-management/user-select-dialog.component.ts.ejs +157 -0
  323. package/src/templates/angular/iam/role-permission-matrix.component.html.ejs +355 -0
  324. package/src/templates/angular/iam/role-permission-matrix.component.scss.ejs +217 -0
  325. package/src/templates/angular/iam/role-permission-matrix.component.ts.ejs +800 -0
  326. package/src/templates/angular/iam/screen-catalog.ts.ejs +147 -0
  327. package/src/templates/angular/iam/shared/confirm-dialog.component.ts.ejs +83 -0
  328. package/src/templates/angular/iam/shared/matrix-hierarchy.util.ts.ejs +196 -0
  329. package/src/templates/angular/iam/shared/permission-matrix-sound.ts.ejs +59 -0
  330. package/src/templates/angular/iam/shared/role-display.util.ts.ejs +43 -0
  331. package/src/templates/angular/iam/shared/role-permission-skeleton.component.ts.ejs +54 -0
  332. package/src/templates/angular/iam/shared/screen-matrix.util.ts.ejs +554 -0
  333. package/src/templates/angular/iam/shared/screen-navigation-map.ts.ejs +187 -0
  334. package/src/templates/angular/iam/shared/screen-navigation.registry.ts.ejs +64 -0
  335. package/src/templates/angular/iam/shared/text-highlight.pipe.ts.ejs +39 -0
  336. package/src/templates/angular/iam/user-access-matrix.component.html.ejs +212 -0
  337. package/src/templates/angular/iam/user-access-matrix.component.scss.ejs +34 -0
  338. package/src/templates/angular/iam/user-access-matrix.component.ts.ejs +239 -0
  339. package/src/templates/angular/iam/user-detail.component.html.ejs +143 -0
  340. package/src/templates/angular/iam/user-detail.component.ts.ejs +108 -0
  341. package/src/templates/angular/iam/user-form.component.html.ejs +63 -0
  342. package/src/templates/angular/iam/user-form.component.ts.ejs +100 -0
  343. package/src/templates/angular/iam/user-list.component.html.ejs +369 -0
  344. package/src/templates/angular/iam/user-list.component.scss.ejs +242 -0
  345. package/src/templates/angular/iam/user-list.component.ts.ejs +446 -0
  346. package/src/templates/angular/iam/user-management.routes.ts.ejs +47 -0
  347. package/src/templates/angular/iam/user-permission-matrix.component.html.ejs +231 -0
  348. package/src/templates/angular/iam/user-permission-matrix.component.scss.ejs +216 -0
  349. package/src/templates/angular/iam/user-permission-matrix.component.ts.ejs +516 -0
  350. package/src/templates/angular/shared/base/audit-user-name.pipe.ts.ejs +69 -0
  351. package/src/templates/angular/shared/base/base-filter.component.ts.ejs +245 -0
  352. package/src/templates/angular/shared/base/base-form.component.ts.ejs +109 -0
  353. package/src/templates/angular/shared/base/base-list.component.ts.ejs +507 -0
  354. package/src/templates/angular/shared/base/filter-types.ts.ejs +55 -0
  355. package/src/templates/angular/shared/base/input-format.directive.ts.ejs +47 -0
  356. package/src/templates/angular/shared/base/input-format.ts.ejs +15 -0
  357. package/src/templates/angular/shared/confirm-with-input-dialog.component.ts.ejs +91 -0
  358. package/src/templates/angular/shared/confirmation-dialog.component.ts.ejs +80 -0
  359. package/src/templates/angular/shared/list-styles.css.ejs +103 -0
  360. package/src/templates/angular/shared/loading-overlay.component.ts.ejs +24 -0
  361. package/src/templates/angular/shared/resizable.directive.ts.ejs +115 -0
  362. package/src/templates/angular/shared/snackbar-styles.css.ejs +11 -0
  363. package/src/templates/fuse-packs/access-control-tools/access-control-sync.js +819 -0
  364. package/src/templates/fuse-packs/access-control-tools/build-rename-map.js +67 -0
  365. package/src/templates/fuse-packs/access-control-tools/inject-qa-ids.js +14 -0
  366. package/src/templates/fuse-packs/access-control-tools/lib/hierarchy-scanner.js +628 -0
  367. package/src/templates/fuse-packs/access-control-tools/lib/hierarchy-scanner.test.js +323 -0
  368. package/src/templates/fuse-packs/access-control-tools/lib/html-scanner.js +362 -0
  369. package/src/templates/fuse-packs/access-control-tools/lib/id-rules.duplicate.test.js +136 -0
  370. package/src/templates/fuse-packs/access-control-tools/lib/id-rules.js +1095 -0
  371. package/src/templates/fuse-packs/access-control-tools/lib/ts-component-index.js +482 -0
  372. package/src/templates/fuse-packs/access-control-tools/output/.gitkeep +0 -0
  373. package/src/templates/fuse-packs/access-control-tools/run.sh +223 -0
  374. package/src/templates/fuse-packs/access-control-tools/scan-access-control.js +14 -0
  375. package/src/templates/fuse-packs/iam-assets/sound/apple-play.mp3 +0 -0
  376. package/src/templates/fuse-packs/iam-assets/sound/remove.mp3 +0 -0
  377. package/src/templates/generator/README.md +12 -0
  378. package/src/templates/testing/ci.cjs +45 -0
  379. package/src/templates/testing/mock.cjs +149 -0
  380. package/src/templates/testing/playwright.config.cjs +25 -0
  381. package/src/templates/testing/serve.cjs +22 -0
  382. package/src/templates/testing/smoke.spec.cjs +180 -0
  383. package/src/utils/branding.js +39 -0
  384. package/src/utils/colors.js +18 -0
  385. package/src/utils/diff.js +109 -0
  386. package/src/utils/merger.js +115 -0
  387. package/src/validator/backend-diagnostics.js +30 -0
  388. package/src/validator/blueprint-validator.js +276 -0
  389. package/src/validator/diagnostics.js +608 -0
  390. package/src/validator/pattern.js +28 -0
  391. package/test/auth-interceptor.js +84 -0
  392. package/test/auth-provision-checks.js +297 -0
  393. package/test/field-preferences.js +145 -0
  394. package/test/fixtures/field-profile.gdl +7 -0
  395. package/test/fixtures/mock.jdl +17 -0
  396. package/test/fixtures/mock.png +0 -0
  397. package/test/fixtures/performance/config.yml +33 -0
  398. package/test/fixtures/performance/large.gdl +199 -0
  399. package/test/fixtures/performance/small.gdl +7 -0
  400. package/test/fixtures/quality/custom.yml +22 -0
  401. package/test/fixtures/quality/go-duck.yml +22 -0
  402. package/test/fixtures/quality/jhipster.yml +30 -0
  403. package/test/fixtures/quality/representative.gdl +14 -0
  404. package/test/list-state.js +138 -0
  405. package/test/micro-functions-layout.js +95 -0
  406. package/test/performance-generation.js +32 -0
  407. package/test/pmb-preset.js +127 -0
  408. package/test/project-workflow.js +111 -0
  409. package/test/quality-features.js +250 -0
  410. package/test/verify-commands.js +83 -0
  411. package/test/verify.js +959 -0
  412. package/test-files/GDL/example.gdl +15 -0
  413. package/test-files/JDL/example.jdl +15 -0
  414. package/test-files/config/crudforge.config.yml +127 -0
  415. package/test-run.sh +272 -0
@@ -0,0 +1,149 @@
1
+ const { test, expect } = require('@playwright/test');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const { loadEnvFile } = require('../../src/config/env-refs');
5
+
6
+ const root = path.resolve(__dirname, '../..');
7
+ const envFile = process.env.CRUDFORGE_ENV_FILE || path.join(root, '.env.qa');
8
+ if (fs.existsSync(envFile)) {
9
+ loadEnvFile(envFile);
10
+ }
11
+
12
+ function requiredEnv(name) {
13
+ const value = process.env[name];
14
+ if (!value) {
15
+ throw new Error(`${name} is required for live Keycloak tests. Copy .env.qa.example to .env.qa.`);
16
+ }
17
+ return value;
18
+ }
19
+
20
+ const adminUser = process.env.CRUDFORGE_LIVE_ADMIN_USERNAME || 'crudforge-qa-admin';
21
+ const adminPass = process.env.CRUDFORGE_QA_ADMIN_PASSWORD;
22
+ const limitedUser = process.env.CRUDFORGE_LIVE_USER_USERNAME || 'crudforge-qa-user';
23
+ const limitedPass = process.env.CRUDFORGE_QA_USER_PASSWORD;
24
+ const realmAdminUser = process.env.KEYCLOAK_ADMIN_USERNAME;
25
+ const realmAdminPass = process.env.KEYCLOAK_ADMIN_PASSWORD;
26
+
27
+ async function fillControl(locator, value) {
28
+ await locator.click();
29
+ await locator.evaluate((el, next) => {
30
+ const descriptor = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value');
31
+ descriptor.set.call(el, next);
32
+ el.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true, inputType: 'insertFromPaste', data: next }));
33
+ el.dispatchEvent(new Event('change', { bubbles: true }));
34
+ }, value);
35
+ await locator.fill(value);
36
+ }
37
+
38
+ async function signIn(page, username, password) {
39
+ await page.goto('/auth/sign-in');
40
+ const submit = page.getByRole('button', { name: 'Sign in' });
41
+ await expect(submit).toBeVisible();
42
+ await fillControl(page.locator('#email'), username);
43
+ await fillControl(page.locator('#password'), password);
44
+ await expect(page.locator('#email')).toHaveValue(username);
45
+ await expect(page.locator('#password')).toHaveValue(password);
46
+ await expect(submit).toBeEnabled({ timeout: 10000 });
47
+ await submit.click();
48
+ }
49
+
50
+ test.describe.configure({ mode: 'serial' });
51
+
52
+ test.describe('IAM LIVE (real Keycloak)', () => {
53
+ test.beforeAll(() => {
54
+ requiredEnv('CRUDFORGE_KC_SERVER_URL');
55
+ if (!adminPass || !limitedPass) {
56
+ throw new Error('CRUDFORGE_QA_ADMIN_PASSWORD and CRUDFORGE_QA_USER_PASSWORD are required.');
57
+ }
58
+ });
59
+
60
+ test('LIVE-AUTH-01 Seeded admin can sign in', async ({ page }) => {
61
+ await signIn(page, adminUser, adminPass);
62
+ await page.waitForURL(/\/admin(\/|$)/, { timeout: 20000 });
63
+ await expect(page).toHaveURL(/\/admin\/welcome|\/admin\/no-access|\/admin\/?$/);
64
+ });
65
+
66
+ test('LIVE-AUTH-02 Bad password stays on sign-in', async ({ page }) => {
67
+ await signIn(page, 'crudforge-qa-unknown-user', 'definitely-wrong-password');
68
+ await expect(page).toHaveURL(/\/auth\/sign-in/);
69
+ await expect(page.getByText(/invalid username or password/i)).toBeVisible({ timeout: 15000 });
70
+ });
71
+
72
+ test('LIVE-AUTH-03 Anonymous users are redirected to sign-in', async ({ page }) => {
73
+ await page.goto('/admin/user-management');
74
+ await expect(page).toHaveURL(/\/auth\/sign-in/);
75
+ });
76
+
77
+ test('LIVE-AUTH-04 Logout ends the session', async ({ page }) => {
78
+ await signIn(page, adminUser, adminPass);
79
+ await page.waitForURL(/\/admin(\/|$)/, { timeout: 20000 });
80
+ await page.getByRole('img', { name: 'User avatar' }).first().click();
81
+ await page.getByRole('menuitem', { name: 'Sign out' }).click();
82
+ await expect(page).toHaveURL(/\/auth\/sign-in/, { timeout: 15000 });
83
+ await page.goto('/admin/user-management');
84
+ await expect(page).toHaveURL(/\/auth\/sign-in/);
85
+ });
86
+
87
+ test('LIVE-RBAC-01 Limited user is not an admin landing', async ({ page }) => {
88
+ await signIn(page, limitedUser, limitedPass);
89
+ await page.waitForURL(/\/admin(\/|$)/, { timeout: 20000 });
90
+ await expect(page).toHaveURL(/\/admin\/no-access/);
91
+ });
92
+
93
+ test('LIVE-API-01 Limited user is denied Keycloak admin users API', async ({ request }) => {
94
+ const tokenRes = await request.post('/realms/CRUD-FORDGE/protocol/openid-connect/token', {
95
+ form: {
96
+ grant_type: 'password',
97
+ client_id: process.env.CRUDFORGE_KC_CLIENT_ID || 'crudforge-client-QA',
98
+ username: limitedUser,
99
+ password: limitedPass,
100
+ scope: 'openid',
101
+ },
102
+ });
103
+ expect(tokenRes.ok(), `token HTTP ${tokenRes.status()}`).toBeTruthy();
104
+ const tokenJson = await tokenRes.json();
105
+ expect(tokenJson.access_token).toBeTruthy();
106
+ const usersRes = await request.get('/admin/realms/CRUD-FORDGE/users?max=1', {
107
+ headers: { Authorization: `Bearer ${tokenJson.access_token}` },
108
+ });
109
+ expect(usersRes.status()).toBeGreaterThanOrEqual(400);
110
+ });
111
+
112
+ test('LIVE-API-02 Realm admin can query users via proxied Admin REST', async ({ request }) => {
113
+ test.skip(!realmAdminUser || !realmAdminPass, 'KEYCLOAK_ADMIN_* not set');
114
+ const tokenRes = await request.post('/realms/CRUD-FORDGE/protocol/openid-connect/token', {
115
+ form: {
116
+ grant_type: 'password',
117
+ client_id: process.env.CRUDFORGE_KC_CLIENT_ID || 'crudforge-client-QA',
118
+ username: realmAdminUser,
119
+ password: realmAdminPass,
120
+ scope: 'openid',
121
+ },
122
+ });
123
+ expect(tokenRes.ok(), `admin token HTTP ${tokenRes.status()}`).toBeTruthy();
124
+ const tokenJson = await tokenRes.json();
125
+ const usersRes = await request.get('/admin/realms/CRUD-FORDGE/users?username=crudforge-qa-admin&exact=true', {
126
+ headers: { Authorization: `Bearer ${tokenJson.access_token}` },
127
+ });
128
+ expect(usersRes.ok(), `users HTTP ${usersRes.status()}`).toBeTruthy();
129
+ const users = await usersRes.json();
130
+ expect(Array.isArray(users)).toBeTruthy();
131
+ expect(users.some((user) => user.username === 'crudforge-qa-admin')).toBeTruthy();
132
+ });
133
+
134
+ test('LIVE-IAM-01 Seeded ROLE_ADMIN can open User Management UI', async ({ page }) => {
135
+ await signIn(page, adminUser, adminPass);
136
+ await page.waitForURL(/\/admin(\/|$)/, { timeout: 20000 });
137
+ await expect(page).not.toHaveURL(/\/admin\/no-access/);
138
+ await page.getByRole('treeitem', { name: 'Users' }).click();
139
+ await expect(page).toHaveURL(/\/admin\/user-management/, { timeout: 15000 });
140
+ await expect(page.getByRole('heading', { name: 'User Management' })).toBeVisible({ timeout: 20000 });
141
+ });
142
+
143
+ test('LIVE-IAM-02 Realm operator is denied Angular IAM screens without ROLE_ADMIN', async ({ page }) => {
144
+ test.skip(!realmAdminUser || !realmAdminPass, 'KEYCLOAK_ADMIN_* not set');
145
+ await signIn(page, realmAdminUser, realmAdminPass);
146
+ await page.waitForURL(/\/admin(\/|$)/, { timeout: 20000 });
147
+ await expect(page.getByText(/no screens assigned/i)).toBeVisible();
148
+ });
149
+ });
@@ -0,0 +1,151 @@
1
+ const { test, expect } = require('@playwright/test');
2
+
3
+ async function signIn(page, username = 'demo@crudforge.com', password = 'admin') {
4
+ await page.goto('/auth/sign-in');
5
+ await expect(page.getByRole('button', { name: 'Sign in' })).toBeVisible();
6
+ const userField = page.locator('#email');
7
+ await userField.fill(username);
8
+ await page.locator('input[type="password"]').fill(password);
9
+ await page.getByRole('button', { name: 'Sign in' }).click();
10
+ }
11
+
12
+ async function signInAsAdmin(page) {
13
+ await signIn(page);
14
+ await page.waitForURL(/\/admin(\/|$)/);
15
+ }
16
+
17
+ test.describe('IAM AUTH', () => {
18
+ test('IAM-AUTH-01 Mock admin sign-in reaches welcome', async ({ page }) => {
19
+ await signInAsAdmin(page);
20
+ await expect(page).toHaveURL(/\/admin\/welcome|\/admin\/?$/);
21
+ });
22
+
23
+ test('IAM-AUTH-02 Bad password stays on sign-in', async ({ page }) => {
24
+ await signIn(page, 'demo@crudforge.com', 'wrong-password');
25
+ await expect(page).toHaveURL(/\/auth\/sign-in/);
26
+ await expect(page.getByText('Invalid username or password')).toBeVisible();
27
+ });
28
+
29
+ test('IAM-AUTH-03 Guard sends anonymous users to sign-in', async ({ page }) => {
30
+ await page.goto('/admin/user-management');
31
+ await expect(page).toHaveURL(/\/auth\/sign-in/);
32
+ });
33
+ });
34
+
35
+ test.describe('IAM NAV', () => {
36
+ test('IAM-NAV-01 User Management nav is visible after admin sign-in', async ({ page }) => {
37
+ await signInAsAdmin(page);
38
+ await expect(page.getByText('User Management', { exact: true }).first()).toBeVisible();
39
+ await expect(page.getByText('Users, roles, and access')).toHaveCount(0);
40
+ await expect(page.getByRole('treeitem', { name: 'Users' })).toBeVisible();
41
+ await expect(page.getByRole('treeitem', { name: 'Roles' })).toBeVisible();
42
+ await expect(page.getByRole('treeitem', { name: 'Role permissions' })).toBeVisible();
43
+ });
44
+
45
+ test('IAM-NAV-02 Admin landing stays on welcome', async ({ page }) => {
46
+ await signInAsAdmin(page);
47
+ await expect(page).toHaveURL(/\/admin\/welcome/);
48
+ await expect(page.getByText('Welcome to CRUDForge')).toBeVisible();
49
+ await expect(page.getByRole('treeitem', { name: /Books?/i })).toBeVisible();
50
+ });
51
+ });
52
+
53
+ test.describe('IAM USERS', () => {
54
+ test('IAM-USER-01 Users list shows mock admin', async ({ page }) => {
55
+ await signInAsAdmin(page);
56
+ await page.goto('/admin/user-management');
57
+ await expect(page.getByRole('heading', { name: 'User Management' })).toBeVisible();
58
+ await expect(page.getByRole('cell', { name: 'admin', exact: true })).toBeVisible();
59
+ await expect(page.getByRole('cell', { name: 'demo@crudforge.com' })).toBeVisible();
60
+ });
61
+
62
+ test('IAM-USER-02 Create user from dialog', async ({ page }) => {
63
+ await signInAsAdmin(page);
64
+ await page.goto('/admin/user-management');
65
+ await page.getByRole('button', { name: 'New' }).click();
66
+ await expect(page.getByRole('heading', { name: 'New User' })).toBeVisible();
67
+ await page.getByLabel('Username').fill('officer1');
68
+ await page.getByLabel('Email').fill('officer1@example.com');
69
+ await page.getByLabel('First name').fill('Office');
70
+ await page.getByLabel('Last name').fill('One');
71
+ await page.getByLabel('Temporary password').fill('Temp-123');
72
+ await page.getByRole('button', { name: 'Save' }).click();
73
+ await expect(page.getByRole('heading', { name: 'New User' })).toBeHidden();
74
+ await expect(page.getByRole('cell', { name: 'officer1', exact: true })).toBeVisible();
75
+ });
76
+
77
+ test('IAM-USER-03 View user details', async ({ page }) => {
78
+ await signInAsAdmin(page);
79
+ await page.goto('/admin/user-management');
80
+ await page.getByRole('button', { name: 'View', exact: true }).first().click();
81
+ await expect(page.getByRole('heading', { name: 'User Details' })).toBeVisible();
82
+ await expect(page.getByText('admin').first()).toBeVisible();
83
+ });
84
+
85
+ test('IAM-USER-04 Change password dialog', async ({ page }) => {
86
+ await signInAsAdmin(page);
87
+ await page.goto('/admin/user-management/mock-admin/view');
88
+ await page.getByRole('button', { name: 'Change password' }).click();
89
+ await expect(page.getByRole('heading', { name: 'Change password' })).toBeVisible();
90
+ await page.getByLabel('New password').fill('Next-Pass-1');
91
+ await page.getByRole('button', { name: 'Save' }).click();
92
+ await expect(page.getByRole('heading', { name: 'Change password' })).toBeHidden();
93
+ });
94
+ });
95
+
96
+ test.describe('IAM ROLES', () => {
97
+ test('IAM-ROLE-01 Roles list shows ROLE_ADMIN', async ({ page }) => {
98
+ await signInAsAdmin(page);
99
+ await page.goto('/admin/user-management/roles');
100
+ await expect(page.getByRole('heading', { name: 'Roles' })).toBeVisible();
101
+ await expect(page.getByRole('button', { name: /ROLE_ADMIN/ })).toBeVisible();
102
+ });
103
+
104
+ test('IAM-ROLE-02 Create role', async ({ page }) => {
105
+ await signInAsAdmin(page);
106
+ await page.goto('/admin/user-management/roles');
107
+ await page.getByRole('button', { name: 'Create Role' }).click();
108
+ await page.locator('#iam-role-name').fill('ROLE_OFFICER');
109
+ await page.locator('#iam-role-description').fill('Field officer');
110
+ await page.getByRole('button', { name: 'Create' }).click();
111
+ await expect(page.getByRole('heading', { name: 'ROLE_OFFICER' })).toBeVisible();
112
+ });
113
+ });
114
+
115
+ test.describe('IAM MATRICES', () => {
116
+ test('IAM-PERM-01 Role permission matrix lists screens', async ({ page }) => {
117
+ await signInAsAdmin(page);
118
+ await page.goto('/admin/user-management/role-permissions');
119
+ await expect(page.getByRole('heading', { name: 'Role permissions' })).toBeVisible();
120
+ await expect(page.getByRole('columnheader', { name: 'ROLE_ADMIN' })).toBeVisible();
121
+ await expect(page.getByText('user management', { exact: false }).first()).toBeVisible();
122
+ });
123
+
124
+ test('IAM-ACCESS-01 User roles matrix can assign a role', async ({ page }) => {
125
+ await signInAsAdmin(page);
126
+ await page.goto('/admin/user-management/roles');
127
+ await page.getByRole('button', { name: 'Create Role' }).click();
128
+ await page.locator('#iam-role-name').fill('ROLE_READER');
129
+ await page.getByRole('button', { name: 'Create' }).click();
130
+ await expect(page.getByRole('heading', { name: 'ROLE_READER' })).toBeVisible();
131
+
132
+ await page.goto('/admin/user-management/mock-admin/access');
133
+ await expect(page.getByRole('heading', { name: 'User roles' })).toBeVisible();
134
+ const readerRow = page.locator('label').filter({ hasText: 'ROLE_READER' });
135
+ await expect(readerRow).toBeVisible();
136
+ await readerRow.getByRole('switch').click();
137
+ await expect(readerRow.getByRole('switch')).toBeChecked();
138
+ });
139
+
140
+ test('IAM-UPERM-01 User screen grant persists on the matrix', async ({ page }) => {
141
+ await signInAsAdmin(page);
142
+ await page.goto('/admin/user-management/mock-admin/permissions');
143
+ await expect(page.getByRole('heading', { name: 'User screens' })).toBeVisible();
144
+ const row = page.locator('label').filter({ hasText: /book/i }).first();
145
+ await expect(row).toBeVisible();
146
+ const toggle = row.getByRole('switch');
147
+ const wasChecked = await toggle.isChecked();
148
+ await toggle.click();
149
+ await expect(toggle).toBeChecked({ checked: !wasChecked });
150
+ });
151
+ });
@@ -0,0 +1,73 @@
1
+ const { test, expect } = require('@playwright/test');
2
+
3
+ async function openBooks(page) {
4
+ await page.goto('/auth/sign-in');
5
+ await page.getByRole('button', { name: 'Sign in', exact: true }).click();
6
+ await page.waitForURL(/\/admin(\/|$)/);
7
+ await page.goto('/admin/book');
8
+ await expect(page.getByRole('cell', { name: 'Mock title 1', exact: true })).toBeVisible();
9
+ }
10
+
11
+ async function editRow(page, index) {
12
+ await page.locator('button[matTooltip="Edit Book"]').nth(index).click();
13
+ await page.getByRole('button', { name: 'Confirm', exact: true }).click();
14
+ const form = page.locator('mat-drawer.cforge-form-drawer');
15
+ await expect(form.getByRole('heading', { name: 'Edit Book' })).toBeVisible();
16
+ return form;
17
+ }
18
+
19
+ test('Edit → close → New resets values, then Edit A → Edit B loads the right record', async ({ page }) => {
20
+ const errors = [];
21
+ page.on('pageerror', error => errors.push(error.message));
22
+ await openBooks(page);
23
+ const form = await editRow(page, 0);
24
+ await expect(form.locator('[formControlName="title"]')).toHaveValue('Mock title 1');
25
+ await form.getByRole('button', { name: 'Cancel' }).click();
26
+ await page.getByRole('button', { name: 'New', exact: true }).click();
27
+ await expect(form.getByRole('heading', { name: 'New Book' })).toBeVisible();
28
+ await expect(form.locator('[formControlName="title"]')).toHaveValue('');
29
+ await expect(form.locator('[formControlName="pages"]')).toHaveValue('');
30
+ await expect(form.getByRole('button', { name: 'Save', exact: true })).toBeDisabled();
31
+ await form.getByRole('button', { name: 'Cancel' }).click();
32
+ await editRow(page, 0);
33
+ await expect(form.locator('[formControlName="title"]')).toHaveValue('Mock title 1');
34
+ await form.getByRole('button', { name: 'Cancel' }).click();
35
+ await editRow(page, 1);
36
+ await expect(form.locator('[formControlName="title"]')).toHaveValue('Mock title 2');
37
+ expect(errors).toEqual([]);
38
+ });
39
+
40
+ test('list responds to available width and restores the saved view across reloads', async ({ page }) => {
41
+ await openBooks(page);
42
+ await page.getByRole('button', { name: 'Switch to Card View' }).click();
43
+ await expect.poll(() => page.evaluate(() => localStorage.getItem('book-view-mode'))).toBe('card');
44
+ await page.reload();
45
+ await expect(page.locator('app-book-card').first()).toBeVisible();
46
+ await page.getByRole('button', { name: 'Switch to Table View' }).click();
47
+ await expect(page.locator('table')).toBeVisible();
48
+ // Change the container alone, as happens with side navigation or an embedded list.
49
+ const viewport = page.locator('app-book-list .absolute.inset-0').first();
50
+ await viewport.evaluate(el => { el.style.width = '500px'; });
51
+ await expect(page.locator('app-book-card').first()).toBeVisible();
52
+ await expect.poll(() => page.evaluate(() => localStorage.getItem('book-view-mode'))).toBe('table');
53
+ await viewport.evaluate(el => { el.style.width = ''; });
54
+ await expect(page.locator('table')).toBeVisible();
55
+ await page.reload();
56
+ await expect(page.locator('table')).toBeVisible();
57
+ });
58
+
59
+ test('audit columns start hidden, can be selected, and Reset hides them again', async ({ page }) => {
60
+ await openBooks(page);
61
+ await expect(page.locator('th.mat-column-createdBy')).toHaveCount(0);
62
+ await page.locator('app-book-actions button').filter({ has: page.locator('mat-icon[svgIcon="menu"]') }).click();
63
+ const row = page.locator('mat-slide-toggle').locator('..').filter({ hasText: 'Created By' });
64
+ await row.getByRole('switch').click();
65
+ await page.keyboard.press('Escape');
66
+ await expect(page.locator('th.mat-column-createdBy')).toBeVisible();
67
+ await page.reload();
68
+ await expect(page.locator('th.mat-column-createdBy')).toBeVisible();
69
+ await page.locator('app-book-actions button').filter({ has: page.locator('mat-icon[svgIcon="menu"]') }).click();
70
+ await page.getByRole('button', { name: 'Reset', exact: true }).click();
71
+ await page.keyboard.press('Escape');
72
+ await expect(page.locator('th.mat-column-createdBy')).toHaveCount(0);
73
+ });
package/features.csv ADDED
@@ -0,0 +1,27 @@
1
+ "Feature Name","Description","How It Works","How To Test It Out"
2
+ "Responsibility-Based Components","Structures entity templates into logical folders based on UI responsibility: card, detail, list under presentation; form, form-page, update under forms; actions, filter under interaction.","Maps template definitions and relative file generation outputs inside the engine to separate UI roles.","Run CLI generation and verify that output components are organized inside presentation/, forms/, and interaction/ subfolders."
3
+ "ASCII Branding Banner","Displays a customizable ASCII banner showing 'CRUDForge' and 'Developed by TeamCodeMe' during npm install and CLI completions. Supports customization of color and suppression.","Prints ASCII banner using formatting helpers, checking config.branding.banner and config.branding.bannerColor configuration settings.","Configure branding: { banner: false } or branding: { bannerColor: 'red' } in config.yml, run cli.js, and verify banner behavior."
4
+ "Smart Terminal Colors","Terminal colors formatting that fallbacks to plain text in environments where colors are not supported or disabled.","Detects TTY status, CI flags, and process.env.NO_COLOR. If present, disables ANSI escape sequences.","Run process.env.NO_COLOR=1 node cli.js and check that terminal logs contain no color escape characters."
5
+ "Execution Metrics & Stats","Calculates and returns execution metrics (file write counts, processed entities, elapsed time) in programmatic runs and CLI summaries.","Increments file counters during template processing and measures time duration using Date.now().","Execute node cli.js and check the detailed run summary section printed at the end of output."
6
+ "JDL/GDL Blueprint Validation","Analyzes schemas before executing generation to catch brace mismatches, duplicate structures, and type-incompatible validations.","Scans file contents line-by-line, runs syntactic and semantic validation checks, and formats error carets (^) pointing to the offending tokens.","Create a JDL with invalid validation rules (e.g. Integer minlength(5)) and run generation to check line-level console errors."
7
+ "Dry-Run & Diff Preview","Runs generation in-memory without making filesystem changes and displays proposed additions and unified diff previews.","Intercepts filesystem ensureDir/write calls when config.dryRun is active and computes line-level additions/deletions using a custom LCS diff algorithm.","Run node cli.js with the --dry-run or -d flag and inspect the proposed new files list and modification diffs."
8
+ "Overwrite Protection & Safe Merging","Protects custom code edits from being overwritten when regenerating schemas. Supports skip, fail, merge, and interactive ask policies.","Scans files on disk for custom code delimiter comments (cforge-custom-code-start / cforge-custom-code-end) and patches them into the new templates.","Seed a generated model file with custom zones, run generation using --overwrite-policy merge, and verify changes are preserved."
9
+ "Relationship Generation","Enables bidirectional entity linking and bindings across generated forms, details, and lists.","Injects related services, loads options arrays in OnInit, renders select/multi-select picker dropdowns, and generates active detail routerLink anchors.","Inspect generated forms for mat-select pickers and detail views for active routerLink navigation tags."
10
+ "Fuse Project Scaffolding","Scaffolds projects from a Fuse Angular starter pack, placing entities, enums, routes, and navigation items inside the Fuse directory layout.","Copies the starter pack, resolves subfolders dynamically, registers routes lazy loading, and adds menu items to navigation.ts with configurable labels, icons, orders, and permissions.","Run the CLI with --fuse flag, verify copied project files, check routes.ts and navigation.ts for registrations, and compile using npx tsc -p tsconfig.app.json --noEmit."
11
+ "Strict Angular Compiler Warning Fixes","Eliminates all NG8107 and NG8113 warnings when generating Angular code.","Uses safe optional chaining with @if directives and conditionally imports ES modules only when actually utilized within templates.","Run the CLI generation with --clean-layout flag, navigate to the output, run 'npm run build', and verify there are zero Angular compiler warnings in the terminal."
12
+ "Fuse Clean Layout Engine","Removes unneeded pre-built UI components like banners, trial alerts, and language switchers to provide a minimal dashboard.","String replaces specific HTML blocks and their backing ES module imports based on the cleanLayout boolean flag.","Run the CLI with --clean-layout flag, serve the Angular project, and verify the top banner and trial cards are gone."
13
+ "Dynamic Module Folder Typings","Allows structuring generation output paths under either 'microservice' or 'micro-functions' folder architectures.","Uses the --folder-type argument to dynamically map the base URL paths when scaffolding the entities into the admin module.","Run the CLI with --folder-type micro-functions, check the src/app/domains/admin/modules/apps directory structure."
14
+ "Automated Project Rebranding","Automatically strips Fuse logos, placeholder text, and demo emails and replaces them with CRUDForge specific branding across login and layouts.","Applies Regex and string replacements across signIn, layout, and sidebar templates during generation to substitute image sources and text copy.","Run the CLI generation, launch the app, and inspect the login screen and sidebar for the CRUDForge logo and copy."
15
+ "Environment & Deployment Setup","Generates dev and prod Angular environments mapped to ports 4200 and 4300, complete with NGINX routing and Docker push scripts.","Modifies angular.json, injects environment.ts files, sets up proxy.conf.json configs, and drops nginx/Docker assets into the output directory.","Run 'npm run dev' to serve on 4200, 'npm start' to serve on 4300, or execute push.sh to build the Docker image."
16
+ "Generic Fallback Error Handler","Implements a universal, reusable error-page fallback component mapped to both global and admin wildcard route handlers.","Generates a single error-page component and registers it as the '**' catch-all route, passing custom status codes and messages via route data instead of using redundant hardcoded templates.","Navigate to a non-existent route like /invalid in the running app and verify the generic error page gracefully catches the request."
17
+ "Sidebar Theming Configuration","Allows toggling the sidebar navigation visual style between light and dark themes.","Injects the scheme-dark class into the layout component if the --sidebar-theme flag is set to 'dark'.","Run the CLI with --sidebar-theme dark and inspect the generated sidebar."
18
+ "Microservice Routing","Supports prefixing generated API routes with a specific microservice name.","Uses the --microservice or -m flag to prepend the microservice name to service endpoint paths.","Run the CLI with -m auth and check the generated Angular service HTTP calls."
19
+ "Configuration Initialization","Generates a default crudforge.config.yml and example schema files to bootstrap new projects.","The init-config command creates standard template files in the current working directory.","Run 'crudforge init-config' and verify the creation of crudforge.config.yml and sample schemas."
20
+ "Custom Configuration Overrides","Allows overriding default generator behaviors via custom YAML/JSON configuration files.","Reads the file specified by the --preferences or -p flag and deep merges it with default internal settings.","Create a custom.yml with specific settings, run CLI with -p custom.yml, and verify the customized output."
21
+ "UI & Form Preferences","Configurable UI parameters such as default view mode (table vs grid), responsive grid columns, and form behaviors (uppercase inputs, error messages).","Parses the 'ui' and 'formBehavior' blocks from the configuration file and injects them as constants into the generated components.","Set formBehavior.uppercaseInputs: true in config, generate a form, and check if the generated inputs have uppercase directives."
22
+ "Mock Data Generation","Generates mock values for CRUD testing when a backend isn't ready.","Uses the 'mock' block to seed generated components with preset string prefixes and randomized numbers/booleans.","Set mock.enabled: true in config, generate a component, and inspect the mock records populated in lists."
23
+ "Audit Fields Handling","Automatically detects common audit fields (e.g. createdBy, createdDate) and handles them according to convention (hiding in forms, showing in details).","Uses the 'fields.audit' configuration block to match property names and adjust template generation visibility logic.","Add a 'createdBy' field to your schema, run generation, and observe that it doesn't appear in the creation form."
24
+ "Naming Conventions Override","Provides deep customization for generated code file prefixes, suffixes, interface prefixes, and enum casing.","Applies the 'namingConventions' configuration block globally to formatting functions used across EJS templates.","Set namingConventions.modelInterfacePrefix to 'Base' in config and check the generated TypeScript model definitions."
25
+ "API Endpoint Customization","Allows configuring REST API bases, microservice names, endpoint styles (like kebab-plural), and date payload libraries (like dayjs).","Injects the 'api' block settings into the generated services HTTP paths and date mapping interceptors.","Set api.endpointStyle to 'kebab-plural' and verify that service endpoints resolve to pluralized kebab-case strings."
26
+ "Dynamic Runtime Configuration","Provides a settings.config.ts pattern to allow runtime overriding of view modes (table vs card), form display modes (sidebar vs page route), snackbar positions, and UI colors without needing to recompile or regenerate the code.","Generates a settings module per entity that gets imported by the list, form, and form-page components. The components dynamically inject these variables into the CSS and component logic during initialization (ngOnInit) or interaction.","Run generation, edit the output settings.config.ts file in the settings directory, change the viewMode to 'card', the formMode to 'page', or the snackbar colors, and observe changes applied dynamically."
27
+ "Base Class Component Architecture","Extracts common Angular boilerplate for forms, lists, and filters into centralized generic base classes.","Moves redundant EJS logic for pagination, sorting, form saving, error handling, and reactive filtering into abstract BaseListComponent, BaseFormComponent, BaseFilterComponent, and filter-types.ts.","Run CLI generation and verify the shared/base folder is populated. Check generated component files to see they extend the base classes and are drastically reduced in size."
@@ -0,0 +1,14 @@
1
+ # Stage 1: Build the Angular application
2
+ FROM node:24-alpine AS build
3
+ WORKDIR /app
4
+ COPY package*.json ./
5
+ RUN npm ci
6
+ COPY . .
7
+ RUN npm run build
8
+
9
+ # Stage 2: Serve the application with Nginx
10
+ FROM nginx:alpine
11
+ COPY --from=build /app/dist/fuse/browser /usr/share/nginx/html
12
+ COPY nginx.conf /etc/nginx/conf.d/default.conf
13
+ EXPOSE 80
14
+ CMD ["nginx", "-g", "daemon off;"]
@@ -0,0 +1,6 @@
1
+ Envato Standard License
2
+
3
+ Copyright (c) Sercan Yemen <hi@srcn.dev>
4
+
5
+ This project is protected by Envato's Standard License. For more information,
6
+ check the official license page at [https://themeforest.net/licenses/standard](https://themeforest.net/licenses/standard)
@@ -0,0 +1,59 @@
1
+ # Fuse - Angular Starter Kit & Admin Template
2
+
3
+ This project was generated using [Angular CLI](https://github.com/angular/angular-cli).
4
+
5
+ ## Development server
6
+
7
+ To start a local development server, run:
8
+
9
+ ```bash
10
+ ng serve
11
+ ```
12
+
13
+ Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
14
+
15
+ ## Code scaffolding
16
+
17
+ Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
18
+
19
+ ```bash
20
+ ng generate component component-name
21
+ ```
22
+
23
+ For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
24
+
25
+ ```bash
26
+ ng generate --help
27
+ ```
28
+
29
+ ## Building
30
+
31
+ To build the project run:
32
+
33
+ ```bash
34
+ ng build
35
+ ```
36
+
37
+ This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
38
+
39
+ ## Running unit tests
40
+
41
+ To execute unit tests with the Vitest, use the following command:
42
+
43
+ ```bash
44
+ ng test
45
+ ```
46
+
47
+ ## Running end-to-end tests
48
+
49
+ For end-to-end (e2e) testing, run:
50
+
51
+ ```bash
52
+ ng e2e
53
+ ```
54
+
55
+ Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
56
+
57
+ ## Additional Resources
58
+
59
+ For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
@@ -0,0 +1,113 @@
1
+ {
2
+ "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3
+ "version": 1,
4
+ "cli": {
5
+ "schematicCollections": [
6
+ "angular-eslint"
7
+ ],
8
+ "analytics": false,
9
+ "cache": {
10
+ "enabled": true,
11
+ "environment": "local"
12
+ }
13
+ },
14
+ "newProjectRoot": "projects",
15
+ "projects": {
16
+ "fuse": {
17
+ "projectType": "application",
18
+ "schematics": {},
19
+ "root": "",
20
+ "sourceRoot": "src",
21
+ "prefix": "app",
22
+ "architect": {
23
+ "build": {
24
+ "builder": "@angular/build:application",
25
+ "options": {
26
+ "browser": "src/main.ts",
27
+ "tsConfig": "tsconfig.app.json",
28
+ "assets": [
29
+ {
30
+ "glob": "**/*",
31
+ "input": "public"
32
+ }
33
+ ],
34
+ "styles": [
35
+ "src/styles/styles.css"
36
+ ]
37
+ },
38
+ "configurations": {
39
+ "production": {
40
+ "budgets": [
41
+ {
42
+ "type": "initial",
43
+ "maximumWarning": "1Mb"
44
+ },
45
+ {
46
+ "type": "anyComponentStyle",
47
+ "maximumWarning": "4kB"
48
+ }
49
+ ],
50
+ "outputHashing": "all",
51
+ "fileReplacements": [
52
+ {
53
+ "replace": "src/environments/environment.ts",
54
+ "with": "src/environments/environment.prod.ts"
55
+ }
56
+ ]
57
+ },
58
+ "development": {
59
+ "optimization": false,
60
+ "extractLicenses": false,
61
+ "sourceMap": true
62
+ },
63
+ "mock": {
64
+ "optimization": false,
65
+ "extractLicenses": false,
66
+ "sourceMap": true,
67
+ "fileReplacements": [
68
+ {
69
+ "replace": "src/environments/environment.ts",
70
+ "with": "src/environments/environment.mock.ts"
71
+ }
72
+ ]
73
+ }
74
+ },
75
+ "defaultConfiguration": "production"
76
+ },
77
+ "serve": {
78
+ "builder": "@angular/build:dev-server",
79
+ "configurations": {
80
+ "production": {
81
+ "buildTarget": "fuse:build:production",
82
+ "port": 4300,
83
+ "proxyConfig": "proxy.conf.json"
84
+ },
85
+ "development": {
86
+ "buildTarget": "fuse:build:development",
87
+ "port": 4200,
88
+ "proxyConfig": "proxy.dev.conf.json"
89
+ },
90
+ "mock": {
91
+ "buildTarget": "fuse:build:mock",
92
+ "port": 4200,
93
+ "proxyConfig": "proxy.dev.conf.json"
94
+ }
95
+ },
96
+ "defaultConfiguration": "development"
97
+ },
98
+ "test": {
99
+ "builder": "@angular/build:unit-test"
100
+ },
101
+ "lint": {
102
+ "builder": "@angular-eslint/builder:lint",
103
+ "options": {
104
+ "lintFilePatterns": [
105
+ "src/**/*.ts",
106
+ "src/**/*.html"
107
+ ]
108
+ }
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }