@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,199 @@
1
+ @Audited
2
+ entity Province {
3
+ string index
4
+ string provinceCode
5
+ string provinceName
6
+ bool isActive
7
+ }
8
+
9
+ @Audited
10
+ entity Catalog01 {
11
+ string code
12
+ string name
13
+ int quantity
14
+ bool isActive
15
+ }
16
+
17
+ @Audited
18
+ entity Catalog02 {
19
+ string code
20
+ string name
21
+ int quantity
22
+ bool isActive
23
+ }
24
+
25
+ @Audited
26
+ entity Catalog03 {
27
+ string code
28
+ string name
29
+ int quantity
30
+ bool isActive
31
+ }
32
+
33
+ @Audited
34
+ entity Catalog04 {
35
+ string code
36
+ string name
37
+ int quantity
38
+ bool isActive
39
+ }
40
+
41
+ @Audited
42
+ entity Catalog05 {
43
+ string code
44
+ string name
45
+ int quantity
46
+ bool isActive
47
+ }
48
+
49
+ @Audited
50
+ entity Catalog06 {
51
+ string code
52
+ string name
53
+ int quantity
54
+ bool isActive
55
+ }
56
+
57
+ @Audited
58
+ entity Catalog07 {
59
+ string code
60
+ string name
61
+ int quantity
62
+ bool isActive
63
+ }
64
+
65
+ @Audited
66
+ entity Catalog08 {
67
+ string code
68
+ string name
69
+ int quantity
70
+ bool isActive
71
+ }
72
+
73
+ @Audited
74
+ entity Catalog09 {
75
+ string code
76
+ string name
77
+ int quantity
78
+ bool isActive
79
+ }
80
+
81
+ @Audited
82
+ entity Catalog10 {
83
+ string code
84
+ string name
85
+ int quantity
86
+ bool isActive
87
+ }
88
+
89
+ @Audited
90
+ entity Catalog11 {
91
+ string code
92
+ string name
93
+ int quantity
94
+ bool isActive
95
+ }
96
+
97
+ @Audited
98
+ entity Catalog12 {
99
+ string code
100
+ string name
101
+ int quantity
102
+ bool isActive
103
+ }
104
+
105
+ @Audited
106
+ entity Catalog13 {
107
+ string code
108
+ string name
109
+ int quantity
110
+ bool isActive
111
+ }
112
+
113
+ @Audited
114
+ entity Catalog14 {
115
+ string code
116
+ string name
117
+ int quantity
118
+ bool isActive
119
+ }
120
+
121
+ @Audited
122
+ entity Catalog15 {
123
+ string code
124
+ string name
125
+ int quantity
126
+ bool isActive
127
+ }
128
+
129
+ @Audited
130
+ entity Catalog16 {
131
+ string code
132
+ string name
133
+ int quantity
134
+ bool isActive
135
+ }
136
+
137
+ @Audited
138
+ entity Catalog17 {
139
+ string code
140
+ string name
141
+ int quantity
142
+ bool isActive
143
+ }
144
+
145
+ @Audited
146
+ entity Catalog18 {
147
+ string code
148
+ string name
149
+ int quantity
150
+ bool isActive
151
+ }
152
+
153
+ @Audited
154
+ entity Catalog19 {
155
+ string code
156
+ string name
157
+ int quantity
158
+ bool isActive
159
+ }
160
+
161
+ @Audited
162
+ entity Catalog20 {
163
+ string code
164
+ string name
165
+ int quantity
166
+ bool isActive
167
+ }
168
+
169
+ @Audited
170
+ entity Catalog21 {
171
+ string code
172
+ string name
173
+ int quantity
174
+ bool isActive
175
+ }
176
+
177
+ @Audited
178
+ entity Catalog22 {
179
+ string code
180
+ string name
181
+ int quantity
182
+ bool isActive
183
+ }
184
+
185
+ @Audited
186
+ entity Catalog23 {
187
+ string code
188
+ string name
189
+ int quantity
190
+ bool isActive
191
+ }
192
+
193
+ @Audited
194
+ entity Catalog24 {
195
+ string code
196
+ string name
197
+ int quantity
198
+ bool isActive
199
+ }
@@ -0,0 +1,7 @@
1
+ @Audited
2
+ entity Province {
3
+ string index
4
+ string provinceCode
5
+ string provinceName
6
+ bool isActive
7
+ }
@@ -0,0 +1,22 @@
1
+ backend:
2
+ kind: custom
3
+ input:
4
+ file: ./gdl/example.gdl
5
+ type: gdl
6
+ fuse:
7
+ enabled: true
8
+ cleanLayout: true
9
+ includePrebuilt: false
10
+ api:
11
+ microservice: catalog
12
+ baseUrl: /v1
13
+ generate:
14
+ globals:
15
+ tests: true
16
+ testing:
17
+ ci: true
18
+ fixtures:
19
+ Farmer:
20
+ referenceCode: AB12
21
+ auth:
22
+ provider: none
@@ -0,0 +1,22 @@
1
+ backend:
2
+ kind: go-duck
3
+ input:
4
+ file: ./gdl/example.gdl
5
+ type: gdl
6
+ fuse:
7
+ enabled: true
8
+ cleanLayout: true
9
+ includePrebuilt: false
10
+ api:
11
+ microservice: reference-service
12
+ baseUrl: /api
13
+ generate:
14
+ globals:
15
+ tests: true
16
+ testing:
17
+ ci: true
18
+ fixtures:
19
+ Farmer:
20
+ referenceCode: AB12
21
+ auth:
22
+ provider: none
@@ -0,0 +1,30 @@
1
+ backend:
2
+ kind: jhipster
3
+ input:
4
+ file: ./gdl/example.gdl
5
+ type: gdl
6
+ fuse:
7
+ enabled: true
8
+ cleanLayout: true
9
+ includePrebuilt: false
10
+ api:
11
+ microservice: registry
12
+ baseUrl: /api
13
+ generate:
14
+ globals:
15
+ tests: true
16
+ testing:
17
+ ci: true
18
+ fixtures:
19
+ Farmer:
20
+ referenceCode: AB12
21
+ auth:
22
+ provider: keycloak
23
+ realm: test
24
+ clientId: test-client
25
+ serverUrl: http://127.0.0.1:9999
26
+ useMockAuth: false
27
+ provision:
28
+ enabled: false
29
+ iam:
30
+ enabled: false
@@ -0,0 +1,14 @@
1
+ enum Status { ACTIVE, INACTIVE }
2
+ entity Province {
3
+ string name required minlength(2)
4
+ }
5
+ entity Farmer {
6
+ string name required maxlength(30)
7
+ Status status required
8
+ bool active
9
+ date birthDate required
10
+ string referenceCode required pattern(^[A-Z]{2}[0-9]{2}$)
11
+ }
12
+ relationship ManyToOne {
13
+ Farmer{province} to Province{farmer}
14
+ }
@@ -0,0 +1,138 @@
1
+ // Behavioral tests of shared generated UI logic. Requires Node 24+, like the Fuse app.
2
+ const assert = require('node:assert/strict');
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const vm = require('node:vm');
6
+ const { stripTypeScriptTypes } = require('node:module');
7
+ const ejs = require('ejs');
8
+ const rx = require('rxjs');
9
+
10
+ function load(relative, exported, context = {}, preferences = {}, locals = {}) {
11
+ let source = ejs.render(fs.readFileSync(path.join(__dirname, '../src/templates/angular', relative), 'utf8'), { preferences, ...locals });
12
+ source = source.replace(/^import[\s\S]*?;\n/gm, '')
13
+ .replace(/@(Component|Injectable|Pipe)\([\s\S]*?\)\s*/g, '')
14
+ .replace(/@(ViewChild|Input|Output)\([^\n]*?\)\s*/g, '')
15
+ .replace(/^export /gm, '');
16
+ const module = { exports: {} };
17
+ vm.runInNewContext(stripTypeScriptTypes(source) + `\nmodule.exports = ${exported};`, { module, ...rx, ...context });
18
+ return module.exports;
19
+ }
20
+
21
+ async function main() {
22
+ const id = '11111111-2222-3333-4444-555555555555';
23
+ let calls = 0;
24
+ let lookup = () => rx.of({ id, firstName: ' First ', lastName: 'Last' });
25
+ const NameService = load('shared/base/audit-user-name.pipe.ts.ejs', 'AuditUserNameService', {
26
+ KeycloakAdminService: {}, inject: () => ({ getUserById: () => { ++calls; return lookup(); } }),
27
+ }, { fuse: { enabled: true }, auth: { provider: 'keycloak' } });
28
+ const names = new NameService();
29
+ assert.equal(await rx.firstValueFrom(names.resolve('Already Named')), 'Already Named');
30
+ assert.equal(await rx.firstValueFrom(names.resolve(null)), '');
31
+ assert.equal(calls, 0);
32
+ assert.equal(await rx.firstValueFrom(names.resolve(id)), 'First Last');
33
+ assert.equal(await rx.firstValueFrom(names.resolve(id)), 'First Last');
34
+ assert.equal(calls, 1, 'Repeated actors reuse a single lookup');
35
+ lookup = () => rx.of({ data: { user: { id, attributes: { given_name: ['First'], family_name: ['Last'] } } } });
36
+ assert.equal(await rx.firstValueFrom(new NameService().resolve(id)), 'First Last');
37
+ lookup = () => rx.throwError(() => new Error('Forbidden'));
38
+ assert.equal(await rx.firstValueFrom(new NameService().resolve(id)), id);
39
+ lookup = () => rx.of({ id: 'different-user', firstName: 'Wrong' });
40
+ assert.equal(await rx.firstValueFrom(new NameService().resolve(id)), id);
41
+ const NoAuthNames = load('shared/base/audit-user-name.pipe.ts.ejs', 'AuditUserNameService');
42
+ assert.equal(await rx.firstValueFrom(new NoAuthNames().resolve(id)), id);
43
+
44
+ const storage = new Map();
45
+ let observer;
46
+ const cleanup = [];
47
+ const Base = load('shared/base/base-list.component.ts.ejs', 'BaseListComponent', {
48
+ localStorage: { getItem: k => storage.get(k) ?? null, setItem: (k, v) => storage.set(k, v) },
49
+ ResizeObserver: class { constructor(callback) { observer = callback; } observe() {} disconnect() { cleanup.push('disconnected'); } },
50
+ });
51
+ const list = Object.create(Base.prototype);
52
+ Object.assign(list, {
53
+ entityNameKebab: 'book', viewMode: 'table', cdr: { markForCheck() {} },
54
+ viewDestroyRef: { onDestroy(callback) { cleanup.push(callback); } },
55
+ listViewport: { nativeElement: { getBoundingClientRect: () => ({ width: 1000 }) } },
56
+ allColumnPreferences: [{key:'state',alwaysVisible:true},{key:'title'},{key:'createdBy',defaultHidden:true}],
57
+ });
58
+ list.restoreColumnPreferences();
59
+ assert.deepEqual(Array.from(list.displayedColumns), ['state', 'title']);
60
+ list.toggleColumn('createdBy');
61
+ list.restoreColumnPreferences();
62
+ assert.ok(list.displayedColumns.includes('createdBy'));
63
+ list.resetColumnPreferences();
64
+ assert.ok(!list.displayedColumns.includes('createdBy'));
65
+ storage.set(list.columnPreferenceStorageKey, '[]');
66
+ list.restoreColumnPreferences();
67
+ assert.deepEqual(Array.from(list.displayedColumns), ['state'], 'Empty saved selections are distinct from no saved preference');
68
+ storage.set(list.columnPreferenceStorageKey, '{broken');
69
+ list.restoreColumnPreferences();
70
+ assert.deepEqual(Array.from(list.displayedColumns), ['state', 'title']);
71
+ list.restoreViewMode();
72
+ list.observeListWidth();
73
+ observer([{ contentRect: { width: 500 } }]);
74
+ assert.equal(list.viewMode, 'card');
75
+ assert.equal(storage.has(list.storageKeyViewMode), false);
76
+ observer([{ contentRect: { width: 900 } }]);
77
+ assert.equal(list.viewMode, 'table');
78
+ list.onViewModeChange('card');
79
+ assert.equal(storage.get(list.storageKeyViewMode), 'card');
80
+ observer([{ contentRect: { width: 1000 } }]);
81
+ assert.equal(list.viewMode, 'card');
82
+ cleanup[0]();
83
+ assert.equal(cleanup[1], 'disconnected');
84
+ const locals = {
85
+ entity: { name: 'Book', fields: [], relationships: [] }, relationships: [], iamAcl: false,
86
+ toCamelCase: value => value[0].toLowerCase() + value.slice(1),
87
+ toKebabCase: value => value.toLowerCase(),
88
+ };
89
+ const FormBase = load('shared/base/base-form.component.ts.ejs', 'BaseFormComponent');
90
+ const Form = load('entity/components/forms/form/_entity_-form.component.ts.ejs', 'BookFormComponent',
91
+ { BaseFormComponent: FormBase }, {}, locals);
92
+ const form = Object.create(Form.prototype);
93
+ let resetValue;
94
+ let pristine = 0, untouched = 0;
95
+ Object.assign(form, {
96
+ isInitialized: true, entity: {id: 10}, errorMessage: 'old error', dialogData: { defaults: { title: 'Default' } },
97
+ form: { markAsPristine: () => pristine++, markAsUntouched: () => untouched++ },
98
+ formService: { resetForm: (_, entity) => { resetValue = entity; } },
99
+ });
100
+ form.ngOnChanges({ entity: { currentValue: {id: 10, title: 'Edited'} } });
101
+ assert.equal(resetValue.id, 10);
102
+ form.ngOnChanges({ entity: { currentValue: null } });
103
+ assert.equal(resetValue.id, null);
104
+ assert.equal(resetValue.title, 'Default');
105
+ assert.equal(form.entity, null);
106
+ assert.equal(form.errorMessage, null);
107
+ assert.equal(pristine, 1);
108
+ assert.equal(untouched, 1);
109
+ form.ngOnChanges({ entity: { currentValue: undefined } });
110
+ assert.equal(resetValue.id, null);
111
+
112
+ const List = load('entity/components/presentation/list/_entity_-list.component.ts.ejs', 'BookListComponent',
113
+ { BaseListComponent: Base, takeUntilDestroyed: () => source => source }, {}, locals);
114
+ const host = Object.create(List.prototype);
115
+ const pending = new Map();
116
+ const mounts = [];
117
+ Object.assign(host, {
118
+ formRequestVersion: 0, formMode: 'sidebar', destroyRef: {}, formDrawer: { open() {}, close() {} },
119
+ cdr: { detectChanges: () => mounts.push(host.formVisible), markForCheck() {} },
120
+ bookService: { find: id => { const request = new rx.Subject(); pending.set(id, request); return request; } },
121
+ });
122
+ host.openEdit(1);
123
+ host.openFormDrawer();
124
+ pending.get(1).next({body: {id: 1, title: 'Stale'}});
125
+ assert.equal(host.selectedBook, null, 'Late edit response must not overwrite New');
126
+ assert.equal(host.drawerMode, 'new');
127
+ host.openEdit(1);
128
+ host.openEdit(2);
129
+ pending.get(2).next({body: {id: 2}});
130
+ pending.get(1).next({body: {id: 1}});
131
+ assert.equal(host.selectedBook.id, 2);
132
+ assert.deepEqual(mounts, [false, true, false, true], 'Every opening destroys before mounting');
133
+ host.closeFormDrawer();
134
+ assert.equal(host.selectedBook, null);
135
+ assert.equal(host.formVisible, false);
136
+ console.log('List state tests passed: audit names/cache/fallback, column defaults/preferences, responsive saved view, observer cleanup, reused form defaults, and stale edit responses.');
137
+ }
138
+ main().catch(error => { console.error(error); process.exitCode = 1; });
@@ -0,0 +1,95 @@
1
+ const assert = require('assert/strict');
2
+ const fs = require('fs-extra');
3
+ const path = require('path');
4
+ const os = require('os');
5
+ const { generate, parseJdl } = require('../src');
6
+ const defaults = require('../src/config/default-values');
7
+
8
+ (async () => {
9
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'crudforge-micro-functions-'));
10
+ try {
11
+ const config = structuredClone(defaults);
12
+ config.output.directory = root;
13
+ config.fuse = { ...config.fuse, enabled: true, includePrebuilt: false,
14
+ starterPackPath: path.resolve(__dirname, '../fuse-starter-pack/fuse-v21.1') };
15
+ config.input.sources = [
16
+ { name: 'Identity', folder: 'identityAccessModule' },
17
+ { name: 'Reference', folder: 'referenceModule' },
18
+ ];
19
+ const parsed = parseJdl('enum State { ACTIVE, INACTIVE }\nentity Staff {\n name String\n state State\n}\nentity Province {\n name String\n}\nrelationship ManyToOne { Staff{province} to Province }\nrelationship OneToMany { Province{staff} to Staff }');
20
+ for (const entity of parsed.entities) {
21
+ for (const field of entity.fields) field.fieldTypeIsEnum = parsed.enums.some(en => en.name === field.fieldType);
22
+ const identity = entity.name === 'Staff';
23
+ entity.folderName = identity ? 'identityAccessModule' : 'referenceModule';
24
+ entity.sourceName = identity ? 'Identity' : 'Reference';
25
+ entity.microservice = identity ? 'identity-service' : 'reference-service';
26
+ }
27
+ await generate(parsed, config);
28
+ const apps = path.join(root, 'src/app/domains/admin/modules/apps');
29
+ const staff = path.join(apps, 'micro-functions/identityAccessModule/entities/staff');
30
+ const province = path.join(apps, 'micro-functions/referenceModule/entities/province');
31
+ assert(fs.existsSync(path.join(apps, 'micro-functions/referenceModule/enum')));
32
+ assert(!fs.existsSync(path.join(apps, 'enums')));
33
+ assert(fs.existsSync(path.join(staff, 'staff.model.ts')));
34
+ assert(fs.existsSync(path.join(province, 'province.model.ts')));
35
+ assert(!fs.existsSync(path.join(apps, 'identityAccessModule')));
36
+ assert(!fs.existsSync(path.join(apps, 'referenceModule')));
37
+ const routes = fs.readFileSync(path.join(root, 'src/app/domains/admin/routes.ts'), 'utf8');
38
+ assert(routes.includes('./modules/apps/micro-functions/identityAccessModule/entities/staff/staff.routes'));
39
+ assert(fs.readFileSync(path.join(staff, 'service/staff.service.ts'), 'utf8').includes('/services/identity-service/api/staffs'));
40
+ // Every relative TS import must resolve, including cross-service relationship imports.
41
+ function verify(dir) {
42
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
43
+ const file = path.join(dir, entry.name);
44
+ if (entry.isDirectory()) verify(file);
45
+ else if (file.endsWith('.ts')) {
46
+ const content = fs.readFileSync(file, 'utf8');
47
+ for (const match of content.matchAll(/(?:from\s*|import\s*\()['"](\.[^'"]+)['"]/g)) {
48
+ const target = path.resolve(path.dirname(file), match[1]);
49
+ assert(fs.existsSync(target + '.ts') || fs.existsSync(path.join(target, 'index.ts')), `${file}: ${match[1]}`);
50
+ }
51
+ }
52
+ }
53
+ }
54
+ verify(staff);
55
+ verify(province);
56
+ assert(fs.readFileSync(path.join(staff, 'staff.model.ts'), 'utf8').includes('../../enum/state.model'));
57
+ // Separate schemas may define the same enum name with different values.
58
+ const cliConfig = structuredClone(config);
59
+ cliConfig.output.directory = path.join(root, 'cli-output');
60
+ cliConfig.input.sources = [
61
+ { name: 'Identity Access', folder: 'identity-access', file: path.join(root, 'identity.jdl'), microservice: 'identity-service' },
62
+ { name: 'Reference', folder: 'reference', file: path.join(root, 'reference.jdl'), microservice: 'reference-service' },
63
+ { name: 'Inventory', folder: 'inventory', file: path.join(root, 'inventory.jdl'), microservice: 'inventory-service' },
64
+ ];
65
+ fs.writeFileSync(cliConfig.input.sources[0].file, 'enum State { ACTIVE, INACTIVE }\nentity Staff {\n state State\n}');
66
+ fs.writeFileSync(cliConfig.input.sources[1].file, 'enum State { OPEN, CLOSED }\nentity Province {\n state State\n}');
67
+ fs.writeFileSync(cliConfig.input.sources[2].file, 'entity Product {\n name String\n}');
68
+ const configPath = path.join(root, 'config.yml');
69
+ fs.writeFileSync(configPath, require('yaml').stringify(cliConfig));
70
+ const result = require('child_process').spawnSync(process.execPath,
71
+ [path.resolve(__dirname, '../cli.js'), 'generate', '--preferences', configPath, '--yes'],
72
+ { encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 });
73
+ assert.equal(result.status, 0, result.stdout + result.stderr);
74
+ assert(fs.readFileSync(path.join(cliConfig.output.directory, 'generator/README.md'), 'utf8')
75
+ .includes('Agents and developers should record generator issues'));
76
+ assert(fs.existsSync(path.join(cliConfig.output.directory, 'generator/issues/.gitkeep')));
77
+ const modules = path.join(cliConfig.output.directory, 'src/app/domains/admin/modules/apps/micro-functions');
78
+ assert(fs.statSync(path.join(modules, 'inventory/entities')).isDirectory());
79
+ assert(fs.statSync(path.join(modules, 'inventory/enum')).isDirectory());
80
+ assert.deepEqual(fs.readdirSync(path.join(modules, 'inventory/enum')), ['.gitkeep']);
81
+ const identityEnum = fs.readFileSync(path.join(modules, 'identity-access/enum/state.model.ts'), 'utf8');
82
+ const referenceEnum = fs.readFileSync(path.join(modules, 'reference/enum/state.model.ts'), 'utf8');
83
+ assert(identityEnum.includes('ACTIVE') && !identityEnum.includes('CLOSED'));
84
+ assert(referenceEnum.includes('CLOSED') && !referenceEnum.includes('ACTIVE'));
85
+ verify(path.join(modules, 'identity-access/entities'));
86
+ verify(path.join(modules, 'reference/entities'));
87
+ config.generate.enums = false;
88
+ config.dryRun = true;
89
+ config.output.directory = path.join(root, 'disabled-enums-preview');
90
+ const preview = await require('../src/generator/project-output').generateProject(parsed, config);
91
+ assert(preview.dryRunReport.newFiles.some(file => file.path.endsWith('referenceModule/enum/.gitkeep')));
92
+ assert(!fs.existsSync(config.output.directory));
93
+ console.log('Micro-functions layout passed: module grouping, routes, service APIs, enums, and cross-service imports.');
94
+ } finally { await fs.remove(root); }
95
+ })().catch(error => { console.error(error); process.exitCode = 1; });
@@ -0,0 +1,32 @@
1
+ const assert=require('node:assert/strict'),fs=require('fs-extra'),path=require('path'),os=require('os');
2
+ const {generateFromSource}=require('../src');
3
+ const {generateProject}=require('../src/generator/project-output');
4
+ const {validateConfig}=require('../src/config/load-config');
5
+ (async()=>{
6
+ const root=await fs.mkdtemp(path.join(os.tmpdir(),'crudforge-performance-check-'));
7
+ try {
8
+ const schema=await fs.readFile(path.join(__dirname,'fixtures/performance/large.gdl'),'utf8');
9
+ for(const mode of ['spa','ssr']) {
10
+ const output=path.join(root,mode);const result=await generateFromSource(schema,{configPath:path.join(__dirname,'fixtures/performance/config.yml'),fileType:'gdl',outputDirectory:output,fuse:{enabled:true,rendering:mode,starterPackPath:path.resolve('fuse-starter-pack/fuse-v21.1')}});
11
+ const angular=await fs.readJson(path.join(output,'angular.json'));const options=angular.projects.fuse.architect.build.options;
12
+ const ts=await fs.readJson(path.join(output,'tsconfig.app.json'));const strict=await fs.readJson(path.join(output,'tsconfig.json')).catch(()=>JSON.parse(fs.readFileSync(path.join(output,'tsconfig.json'),'utf8').replace(/\/\*[\s\S]*?\*\//g,'').replace(/^\/\/.*$/gm,'')));
13
+ assert.equal(strict.compilerOptions.strict,true);assert.equal(strict.angularCompilerOptions.strictTemplates,true);
14
+ assert.equal(!!options.ssr,mode==='ssr');assert.equal(ts.files.includes('src/server.ts'),mode==='ssr');assert.deepEqual(ts.include,['src/**/*.d.ts']);
15
+ const app=await fs.readFile(path.join(output,'src/app/app.config.ts'),'utf8');assert.equal(app.includes('provideClientHydration'),mode==='ssr');assert.ok(app.includes('withInterceptors([authInterceptor])'));
16
+ const pkg=await fs.readJson(path.join(output,'package.json'));const lock=await fs.readJson(path.join(output,'package-lock.json'));assert.deepEqual(lock.packages[''].dependencies,pkg.dependencies);assert.equal(!!pkg.dependencies['@angular/ssr'],mode==='ssr');
17
+ const docker=await fs.readFile(path.join(output,'Dockerfile'),'utf8');assert.equal(docker.includes('dist/fuse/server/server.mjs'),mode==='ssr');
18
+ const files=[];async function walk(dir){for(const entry of await fs.readdir(dir,{withFileTypes:true})){const p=path.join(dir,entry.name);if(entry.isDirectory())await walk(p);else files.push(p);}}await walk(path.join(output,'src'));
19
+ for(const name of ['confirmation-dialog.component.ts','resizable.directive.ts','list-styles.css','snackbar-styles.css'])assert.equal(files.filter(f=>path.basename(f)===name).length,1,name);
20
+ for(const file of files.filter(f=>f.endsWith('.ts'))){const code=await fs.readFile(file,'utf8');assert.ok(!code.includes('HttpClientModule'),file);}
21
+ const routes=files.filter(f=>/catalog-\d+\.routes\.ts$/.test(f));assert.equal(routes.length,24);for(const file of routes)assert.ok((await fs.readFile(file,'utf8')).includes('loadComponent: () => import('));
22
+ // Existing overwrite policy protects developer customization of a shared component.
23
+ if(mode==='spa') {
24
+ const shared=files.find(f=>path.basename(f)==='confirmation-dialog.component.ts');await fs.appendFile(shared,'\n// developer customization\n');result.config.output.overwritePolicy='fail';
25
+ await assert.rejects(generateProject(result.parsed,result.config), /conflict|existing|overwrite/i);
26
+ assert.ok((await fs.readFile(shared,'utf8')).includes('developer customization'));
27
+ }
28
+ }
29
+ assert.throws(()=>validateConfig({fuse:{rendering:'invalid'}},'config.yml'),/fuse.rendering/);
30
+ console.log('Performance generation passed: SPA/SSR coherence, strict checks, 25 entities, shared assets, lazy screens, root HTTP and overwrite protection.');
31
+ }finally{await fs.remove(root);}
32
+ })().catch(e=>{console.error(e);process.exitCode=1;});