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