@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,691 @@
1
+ const { entityPath } = require('./entity-layout');
2
+ const path = require('path');
3
+ const { formatLocalTimestamp } = require('./embed-config');
4
+
5
+ /**
6
+ * GO-DUCK-style agent entrypoint + docs/ai maps for generated Fuse apps.
7
+ */
8
+ function applyAiDocsPack({ outputDir, config, parsedData, writeOrPreview }) {
9
+ const pack = config.output?.aiDocs;
10
+ if (pack && pack.enabled === false) {
11
+ return { applied: false, reason: 'output.aiDocs.enabled=false' };
12
+ }
13
+
14
+ const generatedAt = new Date();
15
+ const projectName = config.project?.name || 'CRUDForge App';
16
+ const entities = parsedData?.entities || [];
17
+ const enums = parsedData?.enums || [];
18
+ const relationships = parsedData?.relationships || [];
19
+ const written = [];
20
+
21
+ const files = {
22
+ 'AGENT_README.md': renderAgentReadme({ projectName, config, entities, enums, relationships, generatedAt }),
23
+ 'docs/ai/README.md': renderDocsIndex({ projectName, generatedAt }),
24
+ 'docs/ai/ARCHITECTURE.md': renderArchitecture({ projectName, config, generatedAt }),
25
+ 'docs/ai/ENTITIES.md': renderEntities({ projectName, entities, enums, relationships, generatedAt }),
26
+ 'docs/ai/ROUTES.md': renderRoutes({ projectName, config, entities, generatedAt }),
27
+ 'docs/ai/UI_CONTRACTS.md': renderUiContracts({ projectName, config, generatedAt }),
28
+ 'docs/ai/PRESERVATION.md': renderPreservation({ projectName, config, generatedAt }),
29
+ '.crudforge/README.md': renderCrudforgeStateReadme({ generatedAt }),
30
+ };
31
+
32
+ for (const [rel, content] of Object.entries(files)) {
33
+ const target = path.join(outputDir, rel);
34
+ writeOrPreview(target, content);
35
+ written.push(target);
36
+ }
37
+
38
+ // Generator state mirror (like .go-duck/config.yaml) — safe, no secrets.
39
+ const { formatEmbeddableYaml } = require('./embed-config');
40
+ const stateYaml = formatEmbeddableYaml(config, generatedAt);
41
+ const statePath = path.join(outputDir, '.crudforge', 'config.yaml');
42
+ writeOrPreview(statePath, stateYaml);
43
+ written.push(statePath);
44
+
45
+ return { applied: true, files: written };
46
+ }
47
+
48
+ function header(title, generatedAt) {
49
+ return [
50
+ `# ${title}`,
51
+ '',
52
+ `> **Generated by \`@crudforge/cli\`** (\`crudforge generate\`) at ${formatLocalTimestamp(generatedAt)} (\`${generatedAt.toISOString()}\`).`,
53
+ '> Do not treat this file as hand-authored source — regenerate after schema or config changes.',
54
+ '',
55
+ ].join('\n');
56
+ }
57
+
58
+ function fieldNameOf(field) {
59
+ return field?.fieldName || field?.name || null;
60
+ }
61
+
62
+ function fieldTypeOf(field) {
63
+ return field?.fieldType || field?.type || 'unknown';
64
+ }
65
+
66
+ function fieldRulesOf(field) {
67
+ return Array.isArray(field?.fieldValidateRules) ? field.fieldValidateRules : [];
68
+ }
69
+
70
+ function isRequiredField(field) {
71
+ return fieldRulesOf(field).some((r) => String(r).toLowerCase() === 'required');
72
+ }
73
+
74
+ function formatFieldRow(field) {
75
+ const name = fieldNameOf(field);
76
+ if (!name) {
77
+ return null;
78
+ }
79
+ const type = fieldTypeOf(field);
80
+ const rules = fieldRulesOf(field).filter(Boolean);
81
+ const flags = [];
82
+ if (isRequiredField(field)) flags.push('required');
83
+ if (field?.fieldTypeIsEnum) flags.push('enum');
84
+ if (field?.comment) flags.push(String(field.comment).replace(/\s+/g, ' ').trim());
85
+ const extras = [
86
+ flags.length ? flags.join(', ') : null,
87
+ rules.length ? `rules: ${rules.join(', ')}` : null,
88
+ ]
89
+ .filter(Boolean)
90
+ .join(' · ');
91
+ return `| \`${name}\` | \`${type}\` | ${extras || '—'} |`;
92
+ }
93
+
94
+ function relationshipsForEntity(entityName, relationships = []) {
95
+ const name = String(entityName || '');
96
+ return (relationships || []).filter(
97
+ (r) => r?.from?.name === name || r?.to?.name === name || r?.otherEntityName === name
98
+ );
99
+ }
100
+
101
+ function formatRelationshipRow(rel, entityName) {
102
+ if (!rel) return null;
103
+ if (rel.from?.name || rel.to?.name) {
104
+ const from = rel.from?.name || '?';
105
+ const to = rel.to?.name || '?';
106
+ const fromField = rel.from?.fieldName ? `{${rel.from.fieldName}}` : '';
107
+ const toField = rel.to?.fieldName ? `{${rel.to.fieldName}}` : '';
108
+ const arrow = from === entityName ? '→' : '←';
109
+ const other = from === entityName ? `${to}${toField}` : `${from}${fromField}`;
110
+ const ownField = from === entityName ? fromField : toField;
111
+ return `| \`${rel.type || 'rel'}\` | ${arrow} \`${other}\` | ${ownField ? `\`${ownField}\`` : '—'} |`;
112
+ }
113
+ const other = rel.otherEntityName || rel.target || '?';
114
+ return `| \`${rel.type || rel.relationshipType || 'rel'}\` | → \`${other}\` | — |`;
115
+ }
116
+
117
+ function renderDocsIndex({ projectName, generatedAt }) {
118
+ return `${header(`AI docs index: ${projectName}`, generatedAt)}
119
+ Maps under \`docs/ai/\` are produced by **\`@crudforge/cli\`** on every generate. Start at [AGENT_README.md](../AGENT_README.md), then open the topic file you need.
120
+
121
+ | Doc | Purpose |
122
+ |---|---|
123
+ | [ARCHITECTURE.md](ARCHITECTURE.md) | Shell, API, auth summary |
124
+ | [ENTITIES.md](ENTITIES.md) | Domain fields, validations, relationships |
125
+ | [ROUTES.md](ROUTES.md) | Admin + IAM + auth routes |
126
+ | [UI_CONTRACTS.md](UI_CONTRACTS.md) | formMode / searchMode / theme / ACL |
127
+ | [PRESERVATION.md](PRESERVATION.md) | Overwrite policy & \`cforge-\` merge zones |
128
+ `;
129
+ }
130
+
131
+ function renderAgentReadme({ projectName, config, entities = [], enums = [], relationships = [], generatedAt }) {
132
+ const auth = config.auth || {};
133
+ const iam = config.iam || {};
134
+ const ui = config.ui || {};
135
+ const api = config.api || {};
136
+ const fuse = config.fuse || {};
137
+ const mock = config.mock || {};
138
+ const proxy = config.proxy || {};
139
+ const project = config.project || {};
140
+ const keycloakOn = auth.provider === 'keycloak';
141
+ const iamOn = keycloakOn && iam.enabled !== false;
142
+
143
+ const entityRows = (entities || [])
144
+ .map((e) => {
145
+ const kebab = toKebab(e.name);
146
+ const fieldCount = (e.fields || []).filter((f) => fieldNameOf(f)).length;
147
+ const relCount = relationshipsForEntity(e.name, relationships).length;
148
+ return `| \`${e.name}\` | \`/admin/${kebab}\` | \`src/app/domains/admin/modules/apps/${entityPath(e, config)}/\` | ${fieldCount} fields, ${relCount} rels |`;
149
+ })
150
+ .join('\n');
151
+
152
+ const enumList = (enums || [])
153
+ .map((e) => {
154
+ const values = (e.values || e.vals || [])
155
+ .map((v) => (typeof v === 'string' ? v : v.name))
156
+ .filter(Boolean)
157
+ .join(', ');
158
+ return `- \`${e.name}\`${values ? `: ${values}` : ''}`;
159
+ })
160
+ .join('\n');
161
+
162
+ const serviceEntries = normalizeServiceEntries(api.services);
163
+ const serviceRows = serviceEntries
164
+ .map((s) => `| \`${s.name}\` | \`${s.proxyPath}\` | \`${s.prodTarget || s.devTarget || ''}\` |`)
165
+ .join('\n');
166
+
167
+ const authBlock = keycloakOn
168
+ ? `## 7. Auth & IAM (Keycloak overlay)
169
+
170
+ **Do not** model User or Role as JDL entities. Identity is an overlay.
171
+
172
+ | Setting | Value |
173
+ |---|---|
174
+ | provider | \`${auth.provider}\` |
175
+ | realm | \`${auth.realm || ''}\` |
176
+ | clientId | \`${auth.clientId || ''}\` |
177
+ | useMockAuth | \`${!!auth.useMockAuth}\` |
178
+ | serverUrl | \`${auth.serverUrl || '(empty — mock or relative proxy)'}\` |
179
+ | forwardedProto | \`${auth.forwardedProto || ''}\` |
180
+ | adminServiceUrl | \`${auth.provision?.adminServiceUrl || ''}\` |
181
+ | IAM enabled | \`${!!iamOn}\` |
182
+
183
+ ### Auth pack — \`src/app/core/auth/\`
184
+
185
+ See [\`src/app/core/auth/AGENT_README.md\`](src/app/core/auth/AGENT_README.md).
186
+
187
+ | Folder | Responsibility |
188
+ |---|---|
189
+ | \`services/\` | \`AuthService\`, \`AccessControlService\`, \`KeycloakAdminService\` |
190
+ | \`guards/\` | \`AuthGuard\`, \`NoAuthGuard\`, \`LandingGuard\` |
191
+ | \`interceptors/\` | Bearer + refresh + \`X-Forwarded-Proto\` |
192
+ | \`pipes/\` / \`directives/\` | \`accessCtrl\`, \`hasPermission\` |
193
+ | \`utils/\` | JWT + permission attribute helpers |
194
+ | \`index.ts\` | Public barrel — prefer \`import { … } from '@/app/core/auth'\` |
195
+
196
+ ### User Management — \`src/app/core/user-management/\`
197
+
198
+ ${iamOn
199
+ ? `See [\`src/app/core/user-management/AGENT_README.md\`](src/app/core/user-management/AGENT_README.md).
200
+
201
+ | Area | Path | Route |
202
+ |---|---|---|
203
+ | Users | \`users/\` | \`/admin/user-management\` |
204
+ | Roles | \`roles/\` | \`/admin/user-management/roles\` |
205
+ | Matrices | \`matrices/\` | \`/admin/user-management/role-permissions\`, \`…/:id/access\`, \`…/:id/permissions\` |
206
+
207
+ Components use separate \`.component.ts\` + \`.component.html\` files (\`templateUrl\`).`
208
+ : '_IAM overlay is off for this generate (`iam.enabled` false or auth not keycloak)._'}
209
+
210
+ ### Auth UI routes
211
+
212
+ | Screen | Route |
213
+ |---|---|
214
+ | Sign in | \`/auth/sign-in\` |
215
+ | No access | \`/admin/no-access\` |
216
+ `
217
+ : `## 7. Auth & IAM
218
+
219
+ \`auth.provider\` is \`${auth.provider || 'none'}\`. No Keycloak pack was generated.
220
+ Turn on with \`auth.provider: keycloak\` (+ optional \`iam.enabled\`) and regenerate.
221
+ `;
222
+
223
+ return `${header(`LLM / AI agent instructions: ${projectName}`, generatedAt)}
224
+ This is the **primary orientation document** for agents and developers working in this generated Fuse app.
225
+ Read **top to bottom** once, then use \`docs/ai/*\` and pack-level \`AGENT_README.md\` files for depth.
226
+
227
+ ---
228
+
229
+ ## 1. What this app is
230
+
231
+ | | |
232
+ |---|---|
233
+ | **Product / project** | ${projectName} |
234
+ | **Description** | ${project.description || '_n/a_'} |
235
+ | **Generator** | CRUDForge (Angular Fuse admin CRUD + optional Keycloak/IAM) |
236
+ | **Repository URL** | ${project.repositoryUrl || '_not set_'} |
237
+ | **Generated at** | ${formatLocalTimestamp(generatedAt)} |
238
+
239
+ CRUDForge produces a Fuse Angular admin shell, entity CRUD modules from JDL/GDL, shared UI primitives, optional Keycloak auth, and optional User Management (IAM) screens.
240
+ Treat **config + routes + entities + auth/IAM + UI contracts** as one coordinated system.
241
+
242
+ ---
243
+
244
+ ## 2. How to run
245
+
246
+ \`\`\`bash
247
+ npm install
248
+ npm start # ng serve (uses proxy.dev.conf.json in development)
249
+ npm run build # production build when configured
250
+ \`\`\`
251
+
252
+ Proxies:
253
+
254
+ | File | When |
255
+ |---|---|
256
+ | \`proxy.dev.conf.json\` | Dev / mock serve (\`proxy.write\` includes \`dev\`/\`both\`) |
257
+ | \`proxy.conf.json\` | Prod-oriented serve (\`proxy.write\` includes \`prod\`/\`both\`) |
258
+
259
+ Current \`proxy.write\`: \`${proxy.write || 'both'}\`.
260
+
261
+ From the **CRUDForge generator repo** (not this app folder):
262
+
263
+ \`\`\`bash
264
+ ./test-run.sh mock # verify + CRUD e2e + mock IAM
265
+ ./test-run.sh live # Keycloak bootstrap + live Playwright
266
+ \`\`\`
267
+
268
+ ---
269
+
270
+ ## 3. Source of truth (do this before hand-editing)
271
+
272
+ 1. **Domain schema** — original JDL/GDL used at generate time (re-run CRUDForge after changes).
273
+ 2. **App preferences** — [\`config/crudforge.config.yml\`](config/crudforge.config.yml) (readable, dated).
274
+ 3. **Generator state mirror** — [\`.crudforge/config.yaml\`](.crudforge/config.yaml) (no secrets).
275
+ 4. **Browser runtime** — \`src/environments/environment*.ts\` (Keycloak public fields + \`apiUrl\` / \`services\`; **never** provision passwords or \`clientSecret\`).
276
+ 5. **Secrets** — local \`.env\` / generator \`.env.qa\` only. \`generate\` must not mutate a remote Keycloak realm; use \`crudforge bootstrap\` for that.
277
+
278
+ Overwrite policy: \`${config.output?.overwritePolicy || 'overwrite'}\`. Preserve \`// cforge-\` merge zones.
279
+
280
+ ---
281
+
282
+ ## 4. Repository map (top → bottom)
283
+
284
+ \`\`\`text
285
+ .
286
+ ├── AGENT_README.md ← you are here (full orientation)
287
+ ├── config/crudforge.config.yml ← canonical preferences
288
+ ├── .crudforge/ ← generator state (+ optional provision-state)
289
+ ├── docs/ai/ ← deep blueprints (architecture, entities, routes, UI, preservation)
290
+ ├── proxy.conf.json / proxy.dev.conf.json
291
+ ├── src/
292
+ │ ├── environments/ ← browser env (apiUrl, Keycloak public fields, services)
293
+ │ ├── app/
294
+ │ │ ├── app.ts / app.config.ts ← theming + HTTP interceptors
295
+ │ │ ├── core/
296
+ │ │ │ ├── auth/ ← Keycloak pack (+ AGENT_README.md) when enabled
297
+ │ │ │ ├── user-management/ ← IAM UI (+ AGENT_README.md) when enabled
298
+ │ │ │ ├── theming/ media/ icons/ …
299
+ │ │ └── domains/
300
+ │ │ ├── auth/features/sign-in/
301
+ │ │ └── admin/ ← layout, routes, entity modules, extras
302
+ │ │ └── modules/apps/shared/access-control-tools/ ← RBAC/QA id sync (+ README)
303
+ │ └── …
304
+ └── .cursor/agents/ · .codex/agents/ · .agents/ ← optional keep-team agents
305
+ \`\`\`
306
+
307
+ ---
308
+
309
+ ## 5. Domain entities (this generate)
310
+
311
+ | Entity | Admin route | Module folder | Size |
312
+ |---|---|---|---|
313
+ ${entityRows || '| — | — | — | — |'}
314
+
315
+ ### Enums
316
+
317
+ ${enumList || '_No enums in this generate._'}
318
+
319
+ Each entity module typically contains: model, service, routes, list/card/detail, filter, actions, form + form-page, settings.
320
+ Shared primitives (confirm dialogs, loading overlay, base list/form/filter, snackbar CSS) live under \`modules/apps/shared/\` when the shared-central pack is used.
321
+
322
+ Deep field/relationship detail: [docs/ai/ENTITIES.md](docs/ai/ENTITIES.md).
323
+
324
+ ---
325
+
326
+ ## 6. API & backend services
327
+
328
+ | Setting | Value |
329
+ |---|---|
330
+ | api.baseUrl | \`${api.baseUrl || '/api'}\` |
331
+ | api.microservice | \`${api.microservice || ''}\` |
332
+ | endpointStyle | \`${api.endpointStyle || 'kebab-plural'}\` |
333
+ | dateLibrary | \`${api.dateLibrary || 'dayjs'}\` |
334
+ | mock data | enabled=${!!mock.enabled}, recordsPerEntity=${mock.recordsPerEntity ?? 'n/a'} |
335
+
336
+ Entity HTTP paths generally look like:
337
+
338
+ \`/services/{microservice}/api/{kebab-plural-entity}\`
339
+
340
+ ### \`api.services\` → environment + proxy
341
+
342
+ Browser code should call **relative** \`/services/{name}\` paths. Absolute origins belong only in proxy JSON.
343
+
344
+ | Service key | Browser path | Target (prod or shared) |
345
+ |---|---|---|
346
+ ${serviceRows || '| _(none configured)_ | | |'}
347
+
348
+ ---
349
+
350
+ ${authBlock}
351
+ ---
352
+
353
+ ## 8. UI contracts (non-negotiable)
354
+
355
+ | Contract | Active value | Rule |
356
+ |---|---|---|
357
+ | formMode | \`${ui.formMode || 'sidebar'}\` | **New and edit share the same mode.** Sidebar → drawer (\`mat-drawer\` / \`.cforge-form-drawer\`). Page → \`/new\` and \`/:id/edit\`. Never leave edit on a full page while new uses the drawer. |
358
+ | searchMode | \`${ui.searchMode || 'field'}\` | \`field\` = per-field \`contains\`. \`global\` only if the API accepts \`query\`. |
359
+ | viewMode | \`${ui.viewMode || 'table'}\` | Table/card; breakpoint \`${ui.cardBreakpointPx ?? 640}px\`. |
360
+ | Theme | scheme \`${fuse.theme?.scheme || 'system'}\`, primary \`${fuse.theme?.primary || ''}\` | Change colors only via root \`fuseTheme\` in \`app.ts\` / \`fuse.theme\` — never hardcode entity primary colors. |
361
+ | ACL stamps | \`data-screen-id\` / \`data-component-id\` + \`accessCtrl\` | Hidden UI is not authorization. |
362
+
363
+ Clean layout: \`${!!fuse.cleanLayout}\`.
364
+
365
+ More: [docs/ai/UI_CONTRACTS.md](docs/ai/UI_CONTRACTS.md).
366
+
367
+ ---
368
+
369
+ ## 9. Routing & navigation
370
+
371
+ - Admin shell: \`src/app/domains/admin/routes.ts\` (+ \`AuthGuard\` / \`LandingGuard\` when Keycloak is on).
372
+ - Navigation data: \`src/app/domains/admin/layout/data/navigation.ts\` (filtered by ACL when auth is on).
373
+ - Entity children register under \`// cforge-routes-start\` / \`// cforge-navigation-start\` needles when present.
374
+
375
+ Full table: [docs/ai/ROUTES.md](docs/ai/ROUTES.md).
376
+
377
+ ---
378
+
379
+ ## 10. Safe evolution workflow
380
+
381
+ 1. Change **JDL/GDL + \`config/crudforge.config.yml\`**, then re-run CRUDForge generate into this app folder.
382
+ 2. Hand-edit only inside \`// cforge-\` merge zones, or own files outside generated entity trees.
383
+ 3. Never commit secrets into config, environments, screenshots, or fixtures.
384
+ 4. After regenerate: \`npm start\` / build, then Playwright (\`test:e2e\`, \`test:e2e:iam\`, or live suite).
385
+ 5. Realm mutations: \`crudforge bootstrap --env-file .env\` only — not during \`generate\`.
386
+
387
+ Preservation details: [docs/ai/PRESERVATION.md](docs/ai/PRESERVATION.md).
388
+
389
+ ---
390
+
391
+ ## 11. Doc index (read before large edits)
392
+
393
+ | Doc | Purpose |
394
+ |---|---|
395
+ | [AGENT_README.md](AGENT_README.md) | **This file** — full top-to-bottom orientation |
396
+ | [docs/ai/ARCHITECTURE.md](docs/ai/ARCHITECTURE.md) | Shell, API, auth summary, layout |
397
+ | [docs/ai/README.md](docs/ai/README.md) | Index of AI maps |
398
+ | [docs/ai/ENTITIES.md](docs/ai/ENTITIES.md) | Fields, validations & relationships |
399
+ | [docs/ai/ROUTES.md](docs/ai/ROUTES.md) | Admin + IAM + auth routes |
400
+ | [docs/ai/UI_CONTRACTS.md](docs/ai/UI_CONTRACTS.md) | formMode / searchMode / ACL / theme |
401
+ | [docs/ai/PRESERVATION.md](docs/ai/PRESERVATION.md) | Overwrite policy & merge zones |
402
+ | [src/app/core/auth/AGENT_README.md](src/app/core/auth/AGENT_README.md) | Auth pack internals (when Keycloak on) |
403
+ | [src/app/core/user-management/AGENT_README.md](src/app/core/user-management/AGENT_README.md) | IAM UI internals (when IAM on) |
404
+ | [src/app/domains/admin/modules/apps/shared/access-control-tools/AGENT_README.md](src/app/domains/admin/modules/apps/shared/access-control-tools/AGENT_README.md) | RBAC / QA id sync for custom pages |
405
+ | [config/crudforge.config.yml](config/crudforge.config.yml) | Canonical preferences |
406
+ | [.crudforge/README.md](.crudforge/README.md) | Generator state folder |
407
+
408
+ Optional keep-team IDE agents may also exist under \`.cursor/agents/\`, \`.codex/agents/\`, and Antigravity \`.agents/\`.
409
+ `;
410
+ }
411
+
412
+ function toKebab(name) {
413
+ return String(name || '')
414
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
415
+ .replace(/[\s_]+/g, '-')
416
+ .toLowerCase();
417
+ }
418
+
419
+ function normalizeServiceEntries(services) {
420
+ if (!services || typeof services !== 'object' || Array.isArray(services)) {
421
+ return [];
422
+ }
423
+ return Object.entries(services)
424
+ .map(([name, value]) => {
425
+ const key = String(name || '').trim();
426
+ if (!key) return null;
427
+ const proxyPath = `/services/${key}`;
428
+ if (typeof value === 'string') {
429
+ const target = value.trim().replace(/\/+$/, '');
430
+ return target ? { name: key, proxyPath, prodTarget: target, devTarget: target } : null;
431
+ }
432
+ if (!value || typeof value !== 'object') return null;
433
+ const prodTarget = String(value.prod || value.target || value.url || '').trim().replace(/\/+$/, '');
434
+ const devTarget = String(value.dev || value.target || value.url || prodTarget).trim().replace(/\/+$/, '');
435
+ if (!prodTarget && !devTarget) return null;
436
+ return { name: key, proxyPath, prodTarget: prodTarget || devTarget, devTarget: devTarget || prodTarget };
437
+ })
438
+ .filter(Boolean);
439
+ }
440
+
441
+ function renderArchitecture({ projectName, config, generatedAt }) {
442
+ const fuse = config.fuse || {};
443
+ const api = config.api || {};
444
+ const auth = config.auth || {};
445
+ const iam = config.iam || {};
446
+ return `${header(`Architecture: ${projectName}`, generatedAt)}
447
+ ## Shell
448
+
449
+ - **UI kit**: Fuse Angular (\`fuse.enabled: ${!!fuse.enabled}\`)
450
+ - **Clean layout**: ${!!fuse.cleanLayout}
451
+ - **Theme**: scheme \`${fuse.theme?.scheme || 'system'}\`, primary \`${fuse.theme?.primary || ''}\`, error \`${fuse.theme?.error || ''}\`
452
+
453
+ ## API
454
+
455
+ - **baseUrl**: \`${api.baseUrl || '/api'}\`
456
+ - **microservice**: \`${api.microservice || ''}\`
457
+ - **endpointStyle**: \`${api.endpointStyle || 'kebab-plural'}\`
458
+ - **dateLibrary**: \`${api.dateLibrary || 'dayjs'}\`
459
+
460
+ ## Auth & IAM
461
+
462
+ - **provider**: \`${auth.provider || 'none'}\`
463
+ - **useMockAuth**: ${!!auth.useMockAuth}
464
+ - **realm / clientId**: \`${auth.realm || ''}\` / \`${auth.clientId || ''}\`
465
+ - **IAM overlay**: ${!!iam.enabled} (users=${iam.users !== false}, roles=${iam.roles !== false})
466
+ - **IAM code**: \`src/app/core/user-management/\` (see that folder's \`AGENT_README.md\`)
467
+ - **Auth code**: \`src/app/core/auth/\` (services / guards / interceptors — see \`AGENT_README.md\`)
468
+ - **Access-control tools**: \`modules/apps/shared/access-control-tools/\` (\`npm run access-control:sync\`)
469
+
470
+ ## Configuration locations
471
+
472
+ | Path | Role |
473
+ |---|---|
474
+ | \`config/crudforge.config.yml\` | Canonical app preferences (readable, dated) |
475
+ | \`.crudforge/config.yaml\` | Generator state mirror (incremental regen) |
476
+ | \`src/environments/environment*.ts\` | Browser runtime (no provision secrets) |
477
+
478
+ ## Layout map (Fuse)
479
+
480
+ \`\`\`text
481
+ src/app/
482
+ core/auth/ # Keycloak auth pack when enabled
483
+ domains/admin/ # layout, routes, entity modules, IAM screens
484
+ domains/auth/ # sign-in
485
+ \`\`\`
486
+ `;
487
+ }
488
+
489
+ function renderEntities({ projectName, entities, enums, relationships = [], generatedAt }) {
490
+ const entityBlocks = (entities || [])
491
+ .map((entity) => {
492
+ const fieldRows = (entity.fields || []).map(formatFieldRow).filter(Boolean);
493
+ const entityRels = relationshipsForEntity(entity.name, relationships);
494
+ const relRows = entityRels.map((r) => formatRelationshipRow(r, entity.name)).filter(Boolean);
495
+ const annotations = [
496
+ entity.searchable ? '`@Searchable`' : null,
497
+ entity.activeStatus ? '`@ActiveStatus`' : null,
498
+ entity.audited ? '`@Audited` / `@EnableAudit`' : null,
499
+ ].filter(Boolean);
500
+
501
+ const fieldsTable = fieldRows.length
502
+ ? `| Field | Type | Notes |\n|---|---|---|\n${fieldRows.join('\n')}`
503
+ : '_No fields._';
504
+ const relsTable = relRows.length
505
+ ? `| Kind | Other side | Local field |\n|---|---|---|\n${relRows.join('\n')}`
506
+ : '_No relationships involving this entity._';
507
+
508
+ return [
509
+ `### \`${entity.name}\``,
510
+ '',
511
+ annotations.length ? `Annotations: ${annotations.join(', ')}` : null,
512
+ annotations.length ? '' : null,
513
+ '**Fields**',
514
+ '',
515
+ fieldsTable,
516
+ '',
517
+ '**Relationships**',
518
+ '',
519
+ relsTable,
520
+ '',
521
+ ]
522
+ .filter((line) => line !== null)
523
+ .join('\n');
524
+ })
525
+ .join('\n');
526
+
527
+ const enumBlocks = (enums || [])
528
+ .map((e) => {
529
+ const values = (e.values || e.vals || [])
530
+ .map((v) => {
531
+ const name = typeof v === 'string' ? v : v?.name;
532
+ return name ? `| \`${name}\` |` : null;
533
+ })
534
+ .filter(Boolean);
535
+ const table = values.length ? `| Value |\n|---|\n${values.join('\n')}` : '_No values._';
536
+ return `### Enum \`${e.name}\`\n\n${table}\n`;
537
+ })
538
+ .join('\n');
539
+
540
+ const relSummary = (relationships || [])
541
+ .map((r) => {
542
+ if (r?.from?.name && r?.to?.name) {
543
+ const fromField = r.from.fieldName ? `{${r.from.fieldName}}` : '';
544
+ const toField = r.to.fieldName ? `{${r.to.fieldName}}` : '';
545
+ return `| \`${r.type || 'rel'}\` | \`${r.from.name}${fromField}\` | \`${r.to.name}${toField}\` |`;
546
+ }
547
+ return null;
548
+ })
549
+ .filter(Boolean);
550
+
551
+ return `${header(`Entities: ${projectName}`, generatedAt)}
552
+ Derived from the JDL/GDL blueprint used at generate time. Field names come from parser \`fieldName\` / \`fieldType\` (not hand-authored). Re-run \`crudforge generate\` after schema changes.
553
+
554
+ ## Summary
555
+
556
+ | | Count |
557
+ |---|---|
558
+ | Entities | ${(entities || []).length} |
559
+ | Enums | ${(enums || []).length} |
560
+ | Relationships | ${(relationships || []).length} |
561
+
562
+ ## Domain entities
563
+
564
+ ${entityBlocks || '_No entities in this generate run._'}
565
+
566
+ ## Relationship index
567
+
568
+ ${
569
+ relSummary.length
570
+ ? `| Kind | From | To |\n|---|---|---|\n${relSummary.join('\n')}`
571
+ : '_No relationship blocks in this generate run._'
572
+ }
573
+
574
+ ## Enums
575
+
576
+ ${enumBlocks || '_No enums in this generate run._'}
577
+ `;
578
+ }
579
+
580
+ function renderRoutes({ projectName, config, entities, generatedAt }) {
581
+ const rows = (entities || [])
582
+ .map((e) => {
583
+ const kebab = String(e.name || '')
584
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
585
+ .replace(/[\s_]+/g, '-')
586
+ .toLowerCase();
587
+ return `| ${e.name} | \`/admin/${kebab}\` | \`modules/apps/${entityPath(e, config)}/\` |`;
588
+ })
589
+ .join('\n');
590
+
591
+ const iam = config.iam?.enabled
592
+ ? `| User Management | \`/admin/user-management\` | IAM overlay |\n| Roles | \`/admin/user-management/roles\` | IAM overlay |\n| Role permissions | \`/admin/user-management/role-permissions\` | IAM overlay |`
593
+ : '';
594
+
595
+ return `${header(`Routes & navigation: ${projectName}`, generatedAt)}
596
+ Admin shell uses \`AuthGuard\` when Keycloak auth is enabled. Landing uses \`LandingGuard\` (first allowed screen or \`/admin/no-access\`).
597
+
598
+ ## Entity routes
599
+
600
+ | Entity | Route | Module folder |
601
+ |---|---|---|
602
+ ${rows || '| — | — | — |'}
603
+ ${iam ? `\n## IAM routes\n\n| Screen | Route | Notes |\n|---|---|---|\n${iam}\n` : ''}
604
+ ## Auth routes
605
+
606
+ | Screen | Route |
607
+ |---|---|
608
+ | Sign in | \`/auth/sign-in\` |
609
+ `;
610
+ }
611
+
612
+ function renderUiContracts({ projectName, config, generatedAt }) {
613
+ const ui = config.ui || {};
614
+ return `${header(`UI contracts: ${projectName}`, generatedAt)}
615
+ ## Form mode
616
+
617
+ - **Active**: \`${ui.formMode || 'sidebar'}\`
618
+ - **Rule**: New and Edit must behave the same. Sidebar → \`mat-drawer\` / \`.cforge-form-drawer\`. Page → \`/new\` and \`/:id/edit\`.
619
+
620
+ ## Search mode
621
+
622
+ - **Active**: \`${ui.searchMode || 'field'}\`
623
+ - **field**: per-field contains controls (JHipster-style).
624
+ - **global**: single query box only when the backend accepts \`query\`.
625
+
626
+ ## View mode
627
+
628
+ - **Default**: \`${ui.viewMode || 'table'}\`
629
+ - Card breakpoint: \`${ui.cardBreakpointPx ?? 640}px\`
630
+
631
+ ## Access control stamps
632
+
633
+ Entity actions should carry \`data-screen-id\` / \`data-component-id\` and \`accessCtrl\` pipes so ACL can hide/disable controls. Hidden UI alone is not authorization.
634
+
635
+ - **SCREEN** / \`data-screen-id\`: \`{folder}/{entity}\` (nav-aligned), e.g. \`micro-functions/book\`
636
+ - **componentId** (semantic): \`create\`, \`open-filter\`, \`toggle-view\`, \`open-columns\`, \`view\`, \`edit\`, \`delete\`, …
637
+ - Custom pages: run \`npm run access-control:sync\` / \`access-control:apply\` from \`modules/apps/shared/access-control-tools/\` (see that folder's README + AGENT docs)
638
+
639
+ ## Theme
640
+
641
+ Change colors only via root \`fuseTheme\` / \`fuse.theme\` — never scatter hardcoded primary colors in entity templates.
642
+ `;
643
+ }
644
+
645
+ function renderPreservation({ projectName, config, generatedAt }) {
646
+ const policy = config.output?.overwritePolicy || 'overwrite';
647
+ return `${header(`Preservation & overwrite: ${projectName}`, generatedAt)}
648
+ ## Overwrite policy
649
+
650
+ Current policy: \`${policy}\`.
651
+
652
+ | Policy | Behavior |
653
+ |---|---|
654
+ | overwrite | Replace generated files |
655
+ | skip | Keep existing file |
656
+ | fail | Abort on conflict |
657
+ | ask | Interactive resolution |
658
+ | merge | Preserve \`// cforge-\` custom zones |
659
+
660
+ ## Merge zones
661
+
662
+ Custom code belongs inside \`// cforge-*\` / \`// cforge-*-end\` markers. Do not delete those needles. Prefer merge policy when hand-editing generated files.
663
+
664
+ ## Do not hand-edit as source of truth
665
+
666
+ - Large entity trees under \`modules/apps/**\` — prefer JDL + regenerate
667
+ - Auth pack templates — edit generator \`src/templates/angular/auth\`
668
+ - Embedded config — edit \`config/crudforge.config.yml\` then regenerate if needed
669
+
670
+ ## Generator state
671
+
672
+ \`.crudforge/config.yaml\` mirrors the last generate preferences (no secrets). Provision tracking files under \`.crudforge/provision-state.json\` are local/CI only and should stay gitignored.
673
+ `;
674
+ }
675
+
676
+ function renderCrudforgeStateReadme({ generatedAt }) {
677
+ return `${header('CRUDForge generator state', generatedAt)}
678
+ This folder is the Fuse equivalent of GO-DUCK's \`.go-duck/\` directory.
679
+
680
+ | File | Purpose |
681
+ |---|---|
682
+ | \`config.yaml\` | Last generate preferences (safe mirror of \`config/crudforge.config.yml\`) |
683
+ | \`provision-state.json\` | Optional bootstrap tracking (gitignored) — only resources CRUDForge created |
684
+
685
+ Do not put passwords here.
686
+ `;
687
+ }
688
+
689
+ module.exports = {
690
+ applyAiDocsPack,
691
+ };