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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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,482 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * TypeScript Compiler API index of Angular @Component metadata.
5
+ * Indexes every component declaration (multi-component files included):
6
+ * selector, template/templateUrl, class name, SCREEN / DEFAULT_SCREEN, imports.
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const { createRequire } = require('module');
12
+ const { duplicatePrimaryRank } = require('./id-rules');
13
+
14
+ function loadTypescript() {
15
+ try {
16
+ return require(require.resolve('typescript', { paths: [process.cwd(), __dirname] }));
17
+ } catch {
18
+ try {
19
+ return createRequire(path.join(process.cwd(), 'package.json'))('typescript');
20
+ } catch {
21
+ throw new Error(
22
+ 'access-control hierarchy scanner requires the `typescript` package (installed in generated Angular apps).'
23
+ );
24
+ }
25
+ }
26
+ }
27
+ const ts = loadTypescript();
28
+
29
+ const SKIP_DIR_RE = /(?:^|[/\\])(?:node_modules|\.git|dist|coverage|out-tsc)(?:[/\\]|$)/;
30
+ const SPEC_RE = /\.spec\.ts$/i;
31
+
32
+ function readSafe(filePath) {
33
+ try {
34
+ return fs.readFileSync(filePath, 'utf8');
35
+ } catch {
36
+ return '';
37
+ }
38
+ }
39
+
40
+ function walkTsFiles(dir, out = []) {
41
+ if (!dir || !fs.existsSync(dir)) {
42
+ return out;
43
+ }
44
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
45
+ const full = path.join(dir, entry.name);
46
+ if (entry.isDirectory()) {
47
+ if (SKIP_DIR_RE.test(full)) {
48
+ continue;
49
+ }
50
+ walkTsFiles(full, out);
51
+ } else if (entry.isFile() && full.endsWith('.ts') && !SPEC_RE.test(full)) {
52
+ out.push(full);
53
+ }
54
+ }
55
+ return out;
56
+ }
57
+
58
+ function decoratorName(decorator) {
59
+ const expr = decorator.expression;
60
+ if (ts.isCallExpression(expr)) {
61
+ if (ts.isIdentifier(expr.expression)) {
62
+ return expr.expression.text;
63
+ }
64
+ if (ts.isPropertyAccessExpression(expr.expression)) {
65
+ return expr.expression.name.text;
66
+ }
67
+ }
68
+ if (ts.isIdentifier(expr)) {
69
+ return expr.text;
70
+ }
71
+ return null;
72
+ }
73
+
74
+ function decoratorObjectLiteral(decorator) {
75
+ const expr = decorator.expression;
76
+ if (!ts.isCallExpression(expr) || !expr.arguments.length) {
77
+ return null;
78
+ }
79
+ const arg = expr.arguments[0];
80
+ return ts.isObjectLiteralExpression(arg) ? arg : null;
81
+ }
82
+
83
+ function objectProp(obj, name) {
84
+ if (!obj) {
85
+ return null;
86
+ }
87
+ for (const prop of obj.properties) {
88
+ if (!ts.isPropertyAssignment(prop)) {
89
+ continue;
90
+ }
91
+ const key =
92
+ (ts.isIdentifier(prop.name) && prop.name.text) ||
93
+ (ts.isStringLiteral(prop.name) && prop.name.text) ||
94
+ null;
95
+ if (key === name) {
96
+ return prop.initializer;
97
+ }
98
+ }
99
+ return null;
100
+ }
101
+
102
+ function stringValue(node) {
103
+ if (!node) {
104
+ return null;
105
+ }
106
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
107
+ return node.text;
108
+ }
109
+ return null;
110
+ }
111
+
112
+ function firstAppElementSelector(selectorText) {
113
+ if (!selectorText) {
114
+ return '';
115
+ }
116
+ return (
117
+ selectorText
118
+ .split(',')
119
+ .map((s) => s.trim())
120
+ .find((s) => /^app-[a-z0-9-]+$/i.test(s)) || ''
121
+ ).toLowerCase();
122
+ }
123
+
124
+ function importIdentifiers(initializer) {
125
+ const names = [];
126
+ if (!initializer || !ts.isArrayLiteralExpression(initializer)) {
127
+ return names;
128
+ }
129
+ for (const el of initializer.elements) {
130
+ if (ts.isIdentifier(el)) {
131
+ names.push(el.text);
132
+ }
133
+ }
134
+ return names;
135
+ }
136
+
137
+ function memberHasInputDecorator(member) {
138
+ const decorators = ts.canHaveDecorators(member) ? ts.getDecorators(member) : undefined;
139
+ return (decorators || []).some((d) => decoratorName(d) === 'Input');
140
+ }
141
+
142
+ /**
143
+ * Own-screen identity only — skip `@Input() SCREEN` (hostScreen passthrough on embeds).
144
+ */
145
+ function extractScreenId(classNode, sourceFile) {
146
+ // 1) readonly/field SCREEN / DEFAULT_SCREEN (not @Input)
147
+ for (const member of classNode.members) {
148
+ if (!ts.isPropertyDeclaration(member) || !member.name || !ts.isIdentifier(member.name)) {
149
+ continue;
150
+ }
151
+ const name = member.name.text;
152
+ if (name !== 'SCREEN' && name !== 'DEFAULT_SCREEN') {
153
+ continue;
154
+ }
155
+ if (memberHasInputDecorator(member)) {
156
+ // Embed cards often expose @Input() SCREEN for the host page — not their own id.
157
+ continue;
158
+ }
159
+ const value = stringValue(member.initializer);
160
+ if (value) {
161
+ return value;
162
+ }
163
+ }
164
+ // 2) get SCREEN() with @Input() screenId default
165
+ for (const member of classNode.members) {
166
+ if (
167
+ ts.isGetAccessorDeclaration(member) &&
168
+ member.name &&
169
+ ts.isIdentifier(member.name) &&
170
+ member.name.text === 'SCREEN'
171
+ ) {
172
+ break;
173
+ }
174
+ }
175
+ for (const member of classNode.members) {
176
+ if (!ts.isPropertyDeclaration(member) || !member.name || !ts.isIdentifier(member.name)) {
177
+ continue;
178
+ }
179
+ if (member.name.text !== 'screenId') {
180
+ continue;
181
+ }
182
+ if (!memberHasInputDecorator(member)) {
183
+ continue;
184
+ }
185
+ const value = stringValue(member.initializer);
186
+ if (value) {
187
+ return value;
188
+ }
189
+ }
190
+ return null;
191
+ }
192
+
193
+ /** Prefer real page/list hosts when duplicate SCREEN ids exist in the tree. */
194
+ function screenOwnerScore(meta) {
195
+ let score = 0;
196
+ const className = meta.className || '';
197
+ const file = (meta.tsFile || '').replace(/\\/g, '/');
198
+ const sourceType = meta.sourceType || '';
199
+ // Align with sync disambiguation: lower module rank → higher score
200
+ score += Math.max(0, 20 - duplicatePrimaryRank(file) * 2);
201
+ if (sourceType === 'pages' || file.includes('/pages/')) {
202
+ score += 8;
203
+ }
204
+ if (/-(list|page)$/.test(meta.screenId || '') || (meta.screenId || '').endsWith('-management-list')) {
205
+ score += 4;
206
+ }
207
+ if (/Card|Dialog|Drawer|Filter|Actions|Dropdown|Summary|Header/i.test(className)) {
208
+ score -= 6;
209
+ }
210
+ if (memberLooksLikeHostFile(file, meta.screenId)) {
211
+ score += 5;
212
+ }
213
+ if (meta.templateContent && /<app-[a-z0-9-]+\b/i.test(meta.templateContent)) {
214
+ score += 2;
215
+ }
216
+ return score;
217
+ }
218
+
219
+ function memberLooksLikeHostFile(file, screenId) {
220
+ if (!file || !screenId) {
221
+ return false;
222
+ }
223
+ const base = path.basename(file).replace(/\.ts$/i, '');
224
+ const normalized = screenId.replace(/-/g, '');
225
+ const fileNorm = base.replace(/\.component$/i, '').replace(/-/g, '');
226
+ return fileNorm.includes(normalized) || normalized.includes(fileNorm);
227
+ }
228
+
229
+ /**
230
+ * @returns {Array<{
231
+ * tsFile: string,
232
+ * className: string,
233
+ * selector: string,
234
+ * screenId: string|null,
235
+ * templateUrl: string|null,
236
+ * inlineTemplate: string|null,
237
+ * standalone: boolean|null,
238
+ * importNames: string[],
239
+ * sourceText: string
240
+ * }>}
241
+ */
242
+ function extractComponentsFromFile(tsFile, sourceText) {
243
+ if (!sourceText || !sourceText.includes('@Component')) {
244
+ return [];
245
+ }
246
+ const sourceFile = ts.createSourceFile(
247
+ tsFile,
248
+ sourceText,
249
+ ts.ScriptTarget.Latest,
250
+ true,
251
+ ts.ScriptKind.TS
252
+ );
253
+ const results = [];
254
+
255
+ const visit = (node) => {
256
+ if (ts.isClassDeclaration(node) && node.name) {
257
+ const decorators = ts.canHaveDecorators(node) ? ts.getDecorators(node) : undefined;
258
+ const componentDecorator = (decorators || []).find((d) => decoratorName(d) === 'Component');
259
+ if (componentDecorator) {
260
+ const obj = decoratorObjectLiteral(componentDecorator);
261
+ const selectorRaw = stringValue(objectProp(obj, 'selector'));
262
+ const templateUrl = stringValue(objectProp(obj, 'templateUrl'));
263
+ const inlineTemplate = stringValue(objectProp(obj, 'template'));
264
+ const standaloneInit = objectProp(obj, 'standalone');
265
+ let standalone = null;
266
+ if (standaloneInit && standaloneInit.kind === ts.SyntaxKind.TrueKeyword) {
267
+ standalone = true;
268
+ } else if (standaloneInit && standaloneInit.kind === ts.SyntaxKind.FalseKeyword) {
269
+ standalone = false;
270
+ }
271
+ results.push({
272
+ tsFile,
273
+ className: node.name.text,
274
+ selector: firstAppElementSelector(selectorRaw),
275
+ screenId: extractScreenId(node, sourceFile),
276
+ templateUrl,
277
+ inlineTemplate,
278
+ standalone,
279
+ importNames: importIdentifiers(objectProp(obj, 'imports')),
280
+ sourceText,
281
+ });
282
+ }
283
+ }
284
+ ts.forEachChild(node, visit);
285
+ };
286
+ visit(sourceFile);
287
+ return results;
288
+ }
289
+
290
+ function loadTemplate(component) {
291
+ if (component.inlineTemplate) {
292
+ return { html: component.inlineTemplate, htmlPath: component.tsFile };
293
+ }
294
+ if (component.templateUrl) {
295
+ const htmlPath = path.resolve(path.dirname(component.tsFile), component.templateUrl);
296
+ return { html: readSafe(htmlPath), htmlPath: fs.existsSync(htmlPath) ? htmlPath : '' };
297
+ }
298
+ return { html: '', htmlPath: '' };
299
+ }
300
+
301
+ /**
302
+ * Build a selector/class/screen index for Angular components under the given roots.
303
+ * @param {string[]} roots absolute directories
304
+ */
305
+ function buildComponentCatalog(roots) {
306
+ /** @type {Map<string, any>} */
307
+ const bySelector = new Map();
308
+ /** @type {Map<string, any>} */
309
+ const byClassName = new Map();
310
+ /** @type {Map<string, any>} */
311
+ const byScreenId = new Map();
312
+ /** @type {any[]} */
313
+ const components = [];
314
+
315
+ const files = [];
316
+ for (const root of roots) {
317
+ walkTsFiles(root, files);
318
+ }
319
+
320
+ for (const tsFile of files) {
321
+ const sourceText = readSafe(tsFile);
322
+ if (!sourceText.includes('@Component')) {
323
+ continue;
324
+ }
325
+ const extracted = extractComponentsFromFile(tsFile, sourceText);
326
+ for (const comp of extracted) {
327
+ const { html, htmlPath } = loadTemplate(comp);
328
+ const meta = {
329
+ ...comp,
330
+ templateContent: html,
331
+ htmlPath,
332
+ };
333
+ components.push(meta);
334
+ if (meta.selector) {
335
+ // Prefer the declaration that carries a SCREEN when selectors collide.
336
+ const existing = bySelector.get(meta.selector);
337
+ if (!existing || (!existing.screenId && meta.screenId)) {
338
+ bySelector.set(meta.selector, meta);
339
+ }
340
+ }
341
+ if (meta.className) {
342
+ byClassName.set(meta.className, meta);
343
+ }
344
+ if (meta.screenId) {
345
+ const prev = byScreenId.get(meta.screenId);
346
+ if (!prev || screenOwnerScore(meta) > screenOwnerScore(prev)) {
347
+ byScreenId.set(meta.screenId, meta);
348
+ }
349
+ }
350
+ }
351
+ }
352
+
353
+ return { components, bySelector, byClassName, byScreenId, fileCount: files.length };
354
+ }
355
+
356
+ /**
357
+ * Statically resolve loadComponent / loadChildren / component: ClassName in route files.
358
+ * @returns {Array<{ className: string, path: string, routesFile: string, kind: string }>}
359
+ */
360
+ function extractRouteComponentHits(routesFile, sourceText) {
361
+ const hits = [];
362
+ if (!sourceText) {
363
+ return hits;
364
+ }
365
+
366
+ // component: ClassName
367
+ const componentRe = /\{\s*path\s*:\s*['"]([^'"]*)['"]\s*,\s*component\s*:\s*(\w+)/g;
368
+ let match;
369
+ while ((match = componentRe.exec(sourceText)) !== null) {
370
+ hits.push({
371
+ path: match[1],
372
+ className: match[2],
373
+ routesFile,
374
+ kind: 'component',
375
+ });
376
+ }
377
+
378
+ // loadComponent: () => import('...').then(m => m.ClassName)
379
+ const loadComponentRe =
380
+ /path\s*:\s*['"]([^'"]*)['"]\s*,\s*loadComponent\s*:\s*\(\)\s*=>\s*import\(\s*['"]([^'"]+)['"]\s*\)(?:\s*\.then\(\s*(?:\(m\)|m)\s*=>\s*m\.(\w+)\s*\))?/g;
381
+ while ((match = loadComponentRe.exec(sourceText)) !== null) {
382
+ const routePath = match[1];
383
+ const importPath = match[2];
384
+ let className = match[3] || '';
385
+ if (!className) {
386
+ // Bare default import — resolve file and take first @Component class.
387
+ const resolved = resolveImportToTs(routesFile, importPath);
388
+ if (resolved) {
389
+ const comps = extractComponentsFromFile(resolved, readSafe(resolved));
390
+ if (comps[0]) {
391
+ className = comps[0].className;
392
+ }
393
+ }
394
+ }
395
+ if (className) {
396
+ hits.push({
397
+ path: routePath,
398
+ className,
399
+ routesFile,
400
+ kind: 'loadComponent',
401
+ importPath,
402
+ });
403
+ }
404
+ }
405
+
406
+ // loadChildren path markers (prefix resolution handled by hierarchy)
407
+ const loadChildrenRe =
408
+ /path\s*:\s*['"]([^'"]+)['"]\s*,\s*loadChildren\s*:\s*\(\)\s*=>\s*import\(\s*['"]([^'"]+)['"]\s*\)/g;
409
+ while ((match = loadChildrenRe.exec(sourceText)) !== null) {
410
+ hits.push({
411
+ path: match[1],
412
+ className: '',
413
+ routesFile,
414
+ kind: 'loadChildren',
415
+ importPath: match[2],
416
+ });
417
+ }
418
+
419
+ return hits;
420
+ }
421
+
422
+ function resolveImportToTs(fromFile, importPath) {
423
+ const base = path.resolve(path.dirname(fromFile), importPath);
424
+ const candidates = [`${base}.ts`, path.join(base, 'index.ts'), base];
425
+ for (const c of candidates) {
426
+ if (fs.existsSync(c) && c.endsWith('.ts')) {
427
+ return c;
428
+ }
429
+ }
430
+ return null;
431
+ }
432
+
433
+ /**
434
+ * Classify why a selector was historically unresolved / has no SCREEN embed.
435
+ */
436
+ function classifyRootCause(opts) {
437
+ const { selector, childMeta, hostFile } = opts;
438
+ if (!childMeta) {
439
+ return {
440
+ code: 'selector_not_found',
441
+ detail: `No @Component with selector '${selector}' under scanned apps trees`,
442
+ };
443
+ }
444
+ const childFile = (childMeta.tsFile || '').replace(/\\/g, '/');
445
+ if (!childMeta.screenId) {
446
+ if (childFile.includes('/shared/')) {
447
+ return {
448
+ code: 'shared_component_without_screen',
449
+ detail: 'Resolved under shared/; no SCREEN / DEFAULT_SCREEN — not an RBAC embed',
450
+ };
451
+ }
452
+ if (!path.basename(childFile).endsWith('.component.ts')) {
453
+ return {
454
+ code: 'non_component_filename_without_screen',
455
+ detail: 'Resolved via AST; filename is not *.component.ts and has no SCREEN',
456
+ };
457
+ }
458
+ return {
459
+ code: 'component_without_screen',
460
+ detail: 'Component resolved but declares no SCREEN / DEFAULT_SCREEN',
461
+ };
462
+ }
463
+ // Has screen — if parent somehow still failed historically, multi-component index
464
+ if (childFile.includes('damage-loss-record-form.component.ts') || hostFile) {
465
+ return {
466
+ code: 'multi_component_file',
467
+ detail: 'Multiple @Component declarations in one file; selector now bound to SCREEN class',
468
+ };
469
+ }
470
+ return { code: 'resolved', detail: 'Indexed via TypeScript AST' };
471
+ }
472
+
473
+ module.exports = {
474
+ buildComponentCatalog,
475
+ extractComponentsFromFile,
476
+ extractRouteComponentHits,
477
+ resolveImportToTs,
478
+ classifyRootCause,
479
+ loadTemplate,
480
+ walkTsFiles,
481
+ screenOwnerScore,
482
+ };
@@ -0,0 +1,223 @@
1
+ #!/usr/bin/env bash
2
+ # Access Control + QA ID runner
3
+ # Scans micro-functions and pages (custom screens), injects SCREEN / accessCtrl / QA ids,
4
+ # and updates screen-components.csv.
5
+ #
6
+ # Usage (from anywhere):
7
+ # ./run.sh # uses MODE preference below (default: apply)
8
+ # ./run.sh dry-run # report only
9
+ # ./run.sh apply # write ids + CSV
10
+ # ./run.sh all # dry-run, then apply
11
+ # ./run.sh --help
12
+ #
13
+ # Examples:
14
+ # bash src/app/domains/admin/modules/apps/shared/access-control-tools/run.sh
15
+ # bash .../run.sh dry-run
16
+ # MODE=dry-run bash .../run.sh
17
+
18
+ set -euo pipefail
19
+
20
+ # ── Preferences (override via env or CLI) ─────────────────────────────────────
21
+ # MODE: dry-run | apply | all
22
+ # Default dry-run so accidental apply cannot thrash Keycloak-facing SCREEN ids.
23
+ MODE="${MODE:-dry-run}"
24
+
25
+ # When applying, normalize legacy noisy ids to qa-{screen}__{action}
26
+ REWRITE_IDS="${REWRITE_IDS:-true}"
27
+
28
+ # When applying, drop CSV rows not rediscovered in this scan
29
+ # Keep false by default after product renames (regional-office-*).
30
+ PRUNE_CSV="${PRUNE_CSV:-false}"
31
+
32
+ # Rebuild SCREEN from template heading + view.
33
+ # Default false for CRUDForge Fuse: generated entities keep nav-aligned
34
+ # `{micro-functions|microservice}/{entity}` SCREEN values. Set true when
35
+ # stamping custom pages under apps/pages with heading-based ids.
36
+ FORCE_CONTENT_IDS="${FORCE_CONTENT_IDS:-false}"
37
+
38
+ # Scope: all apps/* trees except shared/enums (micro-functions, microservice, pages, …)
39
+ # ─────────────────────────────────────────────────────────────────────────────
40
+
41
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
42
+
43
+ # Resolve repo root (directory that contains package.json)
44
+ find_repo_root() {
45
+ local dir="$SCRIPT_DIR"
46
+ while [[ "$dir" != "/" ]]; do
47
+ if [[ -f "$dir/package.json" ]]; then
48
+ echo "$dir"
49
+ return 0
50
+ fi
51
+ dir="$(dirname "$dir")"
52
+ done
53
+ return 1
54
+ }
55
+
56
+ REPO_ROOT="$(find_repo_root)" || {
57
+ echo "error: could not find repo root (package.json) from $SCRIPT_DIR" >&2
58
+ exit 1
59
+ }
60
+
61
+ SYNC_JS="$SCRIPT_DIR/access-control-sync.js"
62
+ REPORT_REL="src/app/domains/admin/modules/apps/shared/access-control-tools/output/sync-report.json"
63
+ CSV_REL="src/app/domains/admin/modules/apps/shared/access-control-tools/output/screen-components.csv"
64
+ HIER_REL="src/app/domains/admin/modules/apps/shared/access-control-tools/output/screen-navigation-map.json"
65
+
66
+ print_help() {
67
+ cat <<'EOF'
68
+ Access Control & QA ID runner
69
+
70
+ Scans (under modules/apps, excluding shared/enums):
71
+ • micro-functions / microservice (generated + custom entities)
72
+ • pages (custom screens)
73
+
74
+ Usage:
75
+ ./run.sh Use MODE preference (default: dry-run)
76
+ ./run.sh dry-run Report only → output/sync-report.json
77
+ ./run.sh apply Write SCREEN, accessCtrl, QA ids, CSV
78
+ ./run.sh all Dry-run first, then apply
79
+ ./run.sh -h | --help
80
+
81
+ Environment preferences:
82
+ MODE=dry-run|apply|all Default run mode (default: dry-run)
83
+ REWRITE_IDS=true|false Normalize legacy ids on apply (default: true)
84
+ PRUNE_CSV=true|false Prune stale CSV rows on apply (default: false)
85
+ FORCE_CONTENT_IDS=true|false Rebuild SCREEN from page heading (default: false)
86
+
87
+ Id / description rules:
88
+ • Generated entities use {folder}/{entity} SCREEN (nav ACL). Keep FORCE_CONTENT_IDS=false.
89
+ • Custom pages: FORCE_CONTENT_IDS=true → heading + view (e.g. book-list)
90
+ • componentId = semantic verb (create, open-filter, toggle-view, open-columns, …)
91
+ • Prefix CSV description with [manual] to lock a hand-written blurb
92
+
93
+ Examples:
94
+ ./run.sh dry-run
95
+ ./run.sh apply
96
+ FORCE_CONTENT_IDS=true ./run.sh apply # heading-based ids for custom pages
97
+ ./run.sh all
98
+ EOF
99
+ }
100
+
101
+ parse_cli() {
102
+ if [[ $# -eq 0 ]]; then
103
+ return 0
104
+ fi
105
+
106
+ case "$1" in
107
+ -h|--help|help)
108
+ print_help
109
+ exit 0
110
+ ;;
111
+ dry-run|--dry-run)
112
+ MODE="dry-run"
113
+ ;;
114
+ apply|--apply|--write|write)
115
+ MODE="apply"
116
+ ;;
117
+ all|--all)
118
+ MODE="all"
119
+ ;;
120
+ *)
121
+ echo "error: unknown argument: $1" >&2
122
+ echo "Run ./run.sh --help for usage." >&2
123
+ exit 1
124
+ ;;
125
+ esac
126
+ }
127
+
128
+ require_node() {
129
+ if ! command -v node >/dev/null 2>&1; then
130
+ echo "error: node is required but not found on PATH" >&2
131
+ exit 1
132
+ fi
133
+ if [[ ! -f "$SYNC_JS" ]]; then
134
+ echo "error: missing sync script: $SYNC_JS" >&2
135
+ exit 1
136
+ fi
137
+ }
138
+
139
+ banner() {
140
+ echo ""
141
+ echo "══════════════════════════════════════════════════════════"
142
+ echo " Access Control / QA ID Sync"
143
+ echo "══════════════════════════════════════════════════════════"
144
+ echo " Repo: $REPO_ROOT"
145
+ echo " Mode: $MODE"
146
+ echo " Rewrite: $REWRITE_IDS"
147
+ echo " Prune: $PRUNE_CSV"
148
+ echo " Content ids: $FORCE_CONTENT_IDS"
149
+ echo " Targets: apps/* (micro-functions, microservice, pages, …)"
150
+ echo "══════════════════════════════════════════════════════════"
151
+ echo ""
152
+ }
153
+
154
+ run_sync() {
155
+ local -a args=("$@")
156
+ echo "→ node access-control-sync.js ${args[*]}"
157
+ echo ""
158
+ (
159
+ cd "$REPO_ROOT"
160
+ node "$SYNC_JS" "${args[@]}"
161
+ )
162
+ }
163
+
164
+ content_id_flags() {
165
+ case "${FORCE_CONTENT_IDS:-true}" in
166
+ true|1|yes|YES|True) echo --force-content-ids ;;
167
+ *) echo --keep-existing-screen ;;
168
+ esac
169
+ }
170
+
171
+ run_apply() {
172
+ local -a args=(--write)
173
+ case "${REWRITE_IDS:-true}" in
174
+ true|1|yes|YES|True) args+=(--rewrite-ids) ;;
175
+ esac
176
+ case "${PRUNE_CSV:-false}" in
177
+ true|1|yes|YES|True) args+=(--prune-csv) ;;
178
+ esac
179
+ args+=("$(content_id_flags)")
180
+ run_sync "${args[@]}"
181
+ }
182
+
183
+ print_report_hint() {
184
+ echo ""
185
+ echo "Report: $REPO_ROOT/$REPORT_REL"
186
+ echo "CSV: $REPO_ROOT/$CSV_REL"
187
+ echo "Nav map:$REPO_ROOT/$HIER_REL"
188
+ echo ""
189
+ }
190
+
191
+ main() {
192
+ parse_cli "$@"
193
+ require_node
194
+ banner
195
+
196
+ case "$MODE" in
197
+ dry-run)
198
+ run_sync --dry-run "$(content_id_flags)"
199
+ print_report_hint
200
+ echo "Dry-run complete. Re-run with: ./run.sh apply"
201
+ ;;
202
+ apply)
203
+ run_apply
204
+ print_report_hint
205
+ echo "Apply complete. IDs written for micro-functions + pages."
206
+ ;;
207
+ all)
208
+ echo "── Step 1/2: dry-run ───────────────────────────────────"
209
+ run_sync --dry-run "$(content_id_flags)"
210
+ print_report_hint
211
+ echo "── Step 2/2: apply ─────────────────────────────────────"
212
+ run_apply
213
+ print_report_hint
214
+ echo "All done. Dry-run + apply finished."
215
+ ;;
216
+ *)
217
+ echo "error: invalid MODE='$MODE' (use dry-run | apply | all)" >&2
218
+ exit 1
219
+ ;;
220
+ esac
221
+ }
222
+
223
+ main "$@"