runlify 0.0.794 → 0.0.796

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 (57) hide show
  1. package/build/cli.js +8 -2
  2. package/build/projectsGeneration/builders/generationPaths.js +44 -1
  3. package/build/projectsGeneration/generateProject/back/src/generateBackSrc.js +11 -2
  4. package/build/projectsGeneration/generateProject/fileHandlers.js +6 -2
  5. package/build/projectsGeneration/generateProject/front/generateFrontSrc.js +10 -1
  6. package/build/projectsGeneration/generateProject/front/translations/generateFrontSrcTranslations.js +33 -1
  7. package/build/projectsGeneration/generateProject/generateBackClickHouseBootstrap.js +1 -4
  8. package/build/projectsGeneration/generateProject/utils.js +2 -2
  9. package/build/projectsGeneration/generators/fileTemplates/back/clickhouse/jobs.js +41 -4
  10. package/build/projectsGeneration/generators/fileTemplates/back/roles/customPermissions.js +12 -0
  11. package/build/projectsGeneration/generators/fileTemplates/back/roles/customUiPermissions.js +13 -0
  12. package/build/projectsGeneration/generators/fileTemplates/back/roles/initPermissions.js +31 -0
  13. package/build/projectsGeneration/generators/fileTemplates/back/roles/uiPermissions.js +24 -0
  14. package/build/projectsGeneration/generators/fileTemplates/ui/NotFoundPage.js +6 -0
  15. package/build/projectsGeneration/generators/fileTemplates/ui/PermissionPage.js +6 -0
  16. package/build/projectsGeneration/generators/fileTemplates/ui/environment/src/routes.js +5 -5
  17. package/build/projectsGeneration/generators/fileTemplates/ui/getDefaultMenu.js +5 -1
  18. package/build/projectsGeneration/generators/fileTemplates/ui/i18n/skeleton/uiI18nIndexTmpl.js +35 -0
  19. package/build/projectsGeneration/generators/fileTemplates/ui/i18n/skeleton/uiI18nTypesTmpl.js +33 -0
  20. package/build/projectsGeneration/generators/fileTemplates/ui/i18n/skeleton/uiI18nValidationTmpl.js +38 -0
  21. package/build/projectsGeneration/generators/fileTemplates/ui/pages/EntityCreate/DefaultEntityCreate.js +16 -5
  22. package/build/projectsGeneration/generators/fileTemplates/ui/pages/EntityEdit/DefaultEntityEdit.js +27 -11
  23. package/build/projectsGeneration/generators/fileTemplates/ui/pages/EntityList/DefaultEntityFilter.js +13 -4
  24. package/build/projectsGeneration/generators/fileTemplates/ui/pages/EntityList/DefaultEntityList.js +14 -7
  25. package/build/projectsGeneration/generators/fileTemplates/ui/pages/EntityShow/DefaultMainTab.js +13 -4
  26. package/build/projectsGeneration/generators/fileTemplates/ui/pages/EntityShow/DependencyTab.js +12 -3
  27. package/build/projectsGeneration/generators/fileTemplates/ui/resources.js +21 -8
  28. package/build/projectsGeneration/generators/fileTemplates/ui/utils/permissions.js +89 -0
  29. package/build/projectsGeneration/generators/ui/getShowComponent.js +2 -2
  30. package/build/types/projectsGeneration/builders/generationPaths.d.ts +42 -0
  31. package/build/types/projectsGeneration/generateProject/fileHandlers.d.ts +1 -0
  32. package/build/types/projectsGeneration/generateProject/types.d.ts +1 -1
  33. package/build/types/projectsGeneration/generateProject/utils.d.ts +1 -1
  34. package/build/types/projectsGeneration/generators/fileTemplates/back/roles/customPermissions.d.ts +2 -0
  35. package/build/types/projectsGeneration/generators/fileTemplates/back/roles/customUiPermissions.d.ts +2 -0
  36. package/build/types/projectsGeneration/generators/fileTemplates/back/roles/initPermissions.d.ts +2 -0
  37. package/build/types/projectsGeneration/generators/fileTemplates/back/roles/uiPermissions.d.ts +3 -0
  38. package/build/types/projectsGeneration/generators/fileTemplates/ui/NotFoundPage.d.ts +2 -0
  39. package/build/types/projectsGeneration/generators/fileTemplates/ui/PermissionPage.d.ts +2 -0
  40. package/build/types/projectsGeneration/generators/fileTemplates/ui/i18n/skeleton/uiI18nIndexTmpl.d.ts +2 -0
  41. package/build/types/projectsGeneration/generators/fileTemplates/ui/i18n/skeleton/uiI18nTypesTmpl.d.ts +3 -0
  42. package/build/types/projectsGeneration/generators/fileTemplates/ui/i18n/skeleton/uiI18nValidationTmpl.d.ts +2 -0
  43. package/build/types/projectsGeneration/generators/fileTemplates/ui/pages/EntityEdit/DefaultEntityEdit.d.ts +1 -0
  44. package/build/types/projectsGeneration/generators/fileTemplates/ui/utils/permissions.d.ts +2 -0
  45. package/docs/01-overview.md +172 -0
  46. package/docs/02-system-meta-builder.md +405 -0
  47. package/docs/03-entity-types.md +471 -0
  48. package/docs/04-fields.md +357 -0
  49. package/docs/05-custom-methods.md +249 -0
  50. package/docs/06-storage.md +129 -0
  51. package/docs/07-backend-file-graph.md +231 -0
  52. package/docs/08-frontend-file-graph.md +260 -0
  53. package/docs/09-options.md +213 -0
  54. package/docs/10-modules.md +164 -0
  55. package/docs/11-permissions.md +231 -0
  56. package/docs/INDEX.md +70 -0
  57. package/package.json +1 -1
@@ -0,0 +1,129 @@
1
+ # Storage
2
+
3
+ > **Load this file when:** you need to choose or change the storage engine for an entity.
4
+ >
5
+ > Related: [03-entity-types.md#storage](./03-entity-types.md#storage) ·
6
+ > [09-options.md](./09-options.md)
7
+
8
+ ---
9
+
10
+ ## StorageType values
11
+
12
+ Import: `import { Storage, StorageType } from 'runlify'`
13
+
14
+ | Constant | String value | What it means |
15
+ |----------|-------------|---------------|
16
+ | `Storage.POSTGRES` | `'postgres'` | PostgreSQL only. Default for all entities. |
17
+ | `Storage.ELASTIC` | `'elastic'` | Elasticsearch only. No Prisma model generated. |
18
+ | `Storage.CLICKHOUSE` | `'clickhouse'` | ClickHouse only. No Prisma model generated. |
19
+ | `Storage.POSTGRES_WITH_ELASTIC_SEARCH` | `'postgres_with_elastic_search'` | PostgreSQL as primary store + Elasticsearch for search queries |
20
+ | `Storage.POSTGRES_WITH_CLICKHOUSE_SEARCH` | `'postgres_with_clickhouse_search'` | PostgreSQL as primary store + ClickHouse for search queries |
21
+
22
+ ---
23
+
24
+ ## How to set storage
25
+
26
+ ```ts
27
+ entity.setStorage(Storage.POSTGRES_WITH_ELASTIC_SEARCH)
28
+ ```
29
+
30
+ The storage must be set **before** adding fields, because some storage types force
31
+ the `id` type to `string`.
32
+
33
+ ---
34
+
35
+ ## Side effects of `setStorage()`
36
+
37
+ | Storage | Forces `id` type | Creates external tracking entity | Notes |
38
+ |---------|-----------------|----------------------------------|-------|
39
+ | `POSTGRES` | No | No | Default |
40
+ | `ELASTIC` | `string` (cuid) | No | No Prisma schema |
41
+ | `CLICKHOUSE` | `string` (cuid) | No | No Prisma schema |
42
+ | `POSTGRES_WITH_ELASTIC_SEARCH` | `string` (cuid) | Yes: `external<Entity>SearchTrackings` | Dual-write: PG + ES |
43
+ | `POSTGRES_WITH_CLICKHOUSE_SEARCH` | `string` (cuid) | Yes: `external<Entity>SearchTrackings` | Dual-write: PG + CH |
44
+
45
+ The `external<Entity>SearchTrackings` catalog is auto-generated and tracks sync state
46
+ between Postgres and the external search engine.
47
+
48
+ ---
49
+
50
+ ## Infrastructure requirements
51
+
52
+ Setting a non-Postgres storage triggers bootstrap generation:
53
+
54
+ | Storage contains | Generated bootstrap |
55
+ |-----------------|---------------------|
56
+ | `elastic` or `postgres_with_elastic_search` | `generateBackElasticBootstrap` — ES client init, index creation jobs |
57
+ | `clickhouse` or `postgres_with_clickhouse_search` | `generateBackClickHouseBootstrap` — CH client init, table creation jobs |
58
+
59
+ These bootstraps require the corresponding config vars to be set:
60
+ - Elasticsearch: `es.enabled`, `es.node` (or `es.cloudId` + `es.username` + `es.password`)
61
+ - ClickHouse: `ch.enabled`, `ch.host`, `ch.port`, `ch.database`, `ch.username`, `ch.password`
62
+
63
+ ---
64
+
65
+ ## Choosing the right storage
66
+
67
+ | Scenario | Recommended storage |
68
+ |----------|---------------------|
69
+ | Standard CRUD entity | `Storage.POSTGRES` |
70
+ | Entity with full-text search needs, Postgres as source of truth | `Storage.POSTGRES_WITH_ELASTIC_SEARCH` |
71
+ | Analytical / time-series data with heavy aggregation | `Storage.POSTGRES_WITH_CLICKHOUSE_SEARCH` or `Storage.CLICKHOUSE` |
72
+ | Pure analytics table, no CRUD | `Storage.CLICKHOUSE` |
73
+ | Pure search index, no relational data | `Storage.ELASTIC` |
74
+
75
+ ---
76
+
77
+ ## Helper functions (exported from `runlify`)
78
+
79
+ ```ts
80
+ isStoragePostgres(storage) // true for POSTGRES, PG+ES, PG+CH
81
+ isStorageElasticOnly(storage) // true only for ELASTIC
82
+ isStorageClickHouseOnly(storage) // true only for CLICKHOUSE
83
+ isStorageExternalSearch(storage) // true for PG+ES, PG+CH
84
+ isStorageElasticSearch(storage) // true for ELASTIC, PG+ES
85
+ isStorageClickHouseSearch(storage) // true for CLICKHOUSE, PG+CH
86
+ getSearchEngine(storage) // returns 'elastic' | 'clickhouse' | null
87
+ usesPrismaDelegate(storage) // false only for ELASTIC and CLICKHOUSE
88
+ ```
89
+
90
+ ---
91
+
92
+ ## Anti-patterns
93
+
94
+ ### Setting `id` type after setting external search storage
95
+
96
+ **Wrong:**
97
+ ```ts
98
+ entity.setStorage(Storage.POSTGRES_WITH_ELASTIC_SEARCH)
99
+ entity.getKey().setType('int') // silently overrides forced 'string' type
100
+ ```
101
+
102
+ **Why:** `setStorage()` with external-search storage forces `id` to `string` (cuid).
103
+ Overriding it to `int` or `bigint` breaks the external search sync because search engine
104
+ IDs must be strings.
105
+
106
+ **Correct:** do not override the key type when using external search storage.
107
+
108
+ ---
109
+
110
+ ### Using `ELASTIC` or `CLICKHOUSE` for entities that need relational joins
111
+
112
+ **Wrong:**
113
+ ```ts
114
+ const orders = system.addDocument('orders')
115
+ orders.setStorage(Storage.ELASTIC) // no Prisma model, no FK support
116
+ orders.addLinkField('users', 'userId') // FK has nowhere to land
117
+ ```
118
+
119
+ **Why:** `ELASTIC` and `CLICKHOUSE` storages do not generate a Prisma model, so foreign
120
+ keys and relational queries are not supported.
121
+
122
+ **Correct:** use `POSTGRES_WITH_ELASTIC_SEARCH` to keep Postgres as the relational
123
+ source of truth while gaining search capabilities.
124
+
125
+ ---
126
+
127
+ > **Examples:** this file intentionally omits code examples. Add real usage examples
128
+ > from the consuming project here. A reference metadata file is typically at
129
+ > `src/meta/metadata.ts` in the meta project.
@@ -0,0 +1,231 @@
1
+ # Backend File Graph (`<prefix>-back`)
2
+
3
+ > **Load this file when:** you need to find a specific file in the generated backend,
4
+ > understand which files are safe to edit, or know what gets regenerated.
5
+ >
6
+ > Legend: `[gen]` = regenerated every `regen` (DO NOT EDIT) · `[yours]` = created once,
7
+ > yours to implement · `[once]` = created once by Runlify, rarely needs changes
8
+ >
9
+ > Related: [01-overview.md](./01-overview.md) · [08-frontend-file-graph.md](./08-frontend-file-graph.md)
10
+
11
+ ---
12
+
13
+ ## Full tree
14
+
15
+ ```
16
+ <prefix>-back/
17
+ │
18
+ ├── src/
19
+ │ │
20
+ │ ├── config/
21
+ │ │ └── config.ts [gen] Runtime config loader (reads env JSON)
22
+ │ │
23
+ │ ├── adm/
24
+ │ │ │
25
+ │ │ ├── graph/
26
+ │ │ │ ├── permissionsToGraphql.ts [gen] Permission → GraphQL mapping
27
+ │ │ │ ├── additionalResolvers.ts [gen] Wires additional service resolvers
28
+ │ │ │ ├── additionalTypes.ts [gen] Additional GraphQL type definitions
29
+ │ │ │ │
30
+ │ │ │ └── <EntityName>/ [gen] Per-entity GraphQL folder
31
+ │ │ │ ├── resolvers.ts [gen] CRUD resolvers
32
+ │ │ │ ├── types.ts [gen] GraphQL type definitions
33
+ │ │ │ └── permissionsToGraphql.ts [gen] Entity permission mappings
34
+ │ │ │
35
+ │ │ └── services/
36
+ │ │ └── <EntityName>Service/ One folder per entity
37
+ │ │ │
38
+ │ │ ├── <EntityName>Service.ts [gen] Base service class — DO NOT EDIT
39
+ │ │ ├── Additional<Entity>Service.ts [yours] Your custom methods + overrides
40
+ │ │ ├── config.ts [gen] Service configuration
41
+ │ │ ├── initBuiltInHooks.ts [gen] Wires built-in framework hooks
42
+ │ │ ├── initUserHooks.ts [yours] Wire your custom hooks here
43
+ │ │ │
44
+ │ │ └── hooks/
45
+ │ │ ├── beforeCreate.ts [yours] Runs before INSERT
46
+ │ │ ├── afterCreate.ts [yours] Runs after INSERT
47
+ │ │ ├── beforeUpdate.ts [yours] Runs before UPDATE
48
+ │ │ ├── afterUpdate.ts [yours] Runs after UPDATE
49
+ │ │ ├── beforeDelete.ts [yours] Runs before DELETE
50
+ │ │ ├── afterDelete.ts [yours] Runs after DELETE
51
+ │ │ ├── beforeUpsert.ts [yours] Runs before UPSERT
52
+ │ │ ├── additionalOperationsOnCreate.ts [yours] Side-effects after create
53
+ │ │ ├── additionalOperationsOnUpdate.ts [yours] Side-effects after update
54
+ │ │ ├── additionalOperationsOnDelete.ts [yours] Side-effects after delete
55
+ │ │ ├── changeListFilter.ts [yours] Mutate the list query filter at runtime
56
+ │ │ └── tenantIdRequiredHooks.ts [yours] Tenant isolation (only if multitenancy enabled)
57
+ │ │
58
+ │ ├── clients/
59
+ │ │ ├── createPgPrismaClient.ts [gen] Prisma client factory
60
+ │ │ └── getPrisma.ts [gen] Prisma client getter (per DB)
61
+ │ │
62
+ │ ├── rest/
63
+ │ │ └── restRouter.ts [once] Custom REST endpoints (yours)
64
+ │ │
65
+ │ ├── init/
66
+ │ │ └── roles/
67
+ │ │ ├── generated/
68
+ │ │ │ ├── initPermissions.ts [gen] Collects and seeds all permissions
69
+ │ │ │ └── uiPermissions.ts [gen] 'ui.<entity>.list' per entity + system pages
70
+ │ │ ├── customPermissions.ts [once] Your hand-written data-space permissions
71
+ │ │ └── customUiPermissions.ts [once] Your hand-written UI-space permissions
72
+ │ │
73
+ │ ├── enums/
74
+ │ │ ├── enums.ts [gen] All entity name enums
75
+ │ │ ├── devEnums.ts [gen] Dev-only enums
76
+ │ │ └── initEntities.ts [gen] Entity registry initialisation
77
+ │ │
78
+ │ └── index.ts [gen] Application entry point
79
+ │
80
+ ├── prisma/
81
+ │ └── schema.<dbName>.prisma [gen] Prisma schema (one per database)
82
+ │
83
+ ├── config/
84
+ │ ├── dev.json [gen] Dev environment config (values from meta)
85
+ │ └── prod.json [gen] Prod environment config (values from meta)
86
+ │
87
+ ├── docs/
88
+ │ ├── spec.md [gen] Auto-generated project specification
89
+ │ ├── configuration.md [gen] Config vars documentation
90
+ │ └── entities/
91
+ │ └── <EntityName>.md [gen] Per-entity documentation
92
+ │
93
+ ├── Dockerfile [gen] Backend Docker image
94
+ ├── .gitlab-ci.yml [gen] GitLab CI pipeline
95
+ │
96
+ └── chart/ Helm chart for Kubernetes
97
+ ├── Chart.yaml [gen]
98
+ ├── values.yaml [gen]
99
+ └── templates/
100
+ ├── back.yaml [gen] Deployment manifest
101
+ └── ingress.yaml [gen] Ingress manifest
102
+ ```
103
+
104
+ ---
105
+
106
+ ## Per-entity service: what to edit and what not to
107
+
108
+ ### `<EntityName>Service.ts` — DO NOT EDIT {#entity-service-generated}
109
+
110
+ Generated on every `regen`. Contains:
111
+ - Standard CRUD operations (`getAll`, `getById`, `create`, `update`, `delete`)
112
+ - Prisma query building, filter application, sorting, pagination
113
+ - Hook invocation calls (calls into `initUserHooks.ts`)
114
+
115
+ ### `Additional<EntityName>Service.ts` — yours {#additional-service}
116
+
117
+ Created once. Never overwritten. This is where you add:
118
+ - Custom methods that are too complex for the standard service
119
+ - Business logic that wraps or extends generated CRUD
120
+ - Helper methods used by hooks
121
+
122
+ ### `hooks/` — yours {#hooks}
123
+
124
+ All hook files are created once and never overwritten.
125
+
126
+ | Hook file | When called | Common uses |
127
+ |-----------|------------|-------------|
128
+ | `beforeCreate.ts` | Before DB INSERT | Validate, enrich input, set computed fields |
129
+ | `afterCreate.ts` | After DB INSERT | Send notifications, trigger side effects |
130
+ | `beforeUpdate.ts` | Before DB UPDATE | Validate new state, check transitions |
131
+ | `afterUpdate.ts` | After DB UPDATE | Sync caches, publish events |
132
+ | `beforeDelete.ts` | Before DB DELETE | Check dependencies, soft-delete fallback |
133
+ | `afterDelete.ts` | After DB DELETE | Clean up related data |
134
+ | `beforeUpsert.ts` | Before UPSERT | |
135
+ | `additionalOperationsOnCreate.ts` | After create, separate tx context | Audit log, async tasks |
136
+ | `additionalOperationsOnUpdate.ts` | After update, separate tx context | |
137
+ | `additionalOperationsOnDelete.ts` | After delete, separate tx context | |
138
+ | `changeListFilter.ts` | On every list query | Inject runtime filters (tenant, user scope) |
139
+ | `tenantIdRequiredHooks.ts` | On create (multitenancy only) | Ensure `tenantId` is set from user context |
140
+
141
+ ### `initUserHooks.ts` — yours {#init-user-hooks}
142
+
143
+ Registers your hook implementations with the framework. Created once.
144
+ When you implement a hook, register it here.
145
+
146
+ ### `initBuiltInHooks.ts` — DO NOT EDIT {#init-built-in-hooks}
147
+
148
+ Generated. Registers framework-level hooks (audit logging, search index sync, etc.).
149
+
150
+ ---
151
+
152
+ ## Prisma schema
153
+
154
+ One `.prisma` file per registered database:
155
+ - `schema.main.prisma` — always generated
156
+ - `schema.<name>.prisma` — generated for each additional database registered via
157
+ `system.addDatabase(name)`
158
+
159
+ The schema is fully generated. **Do not edit it** — all model definitions come from
160
+ the meta. Schema changes go in the meta, not in the `.prisma` file.
161
+
162
+ **Anti-pattern — editing the Prisma schema directly:**
163
+
164
+ ```
165
+ // WRONG: you add a column directly to schema.main.prisma
166
+ model Order {
167
+ id Int @id @default(autoincrement())
168
+ myField String // <-- added manually
169
+ }
170
+ ```
171
+
172
+ After the next `regen`, `myField` is gone.
173
+
174
+ **Correct:** add the field in `metadata.ts` via `entity.addField('myField')`, then regen.
175
+
176
+ ---
177
+
178
+ ## Config files (`config/`)
179
+
180
+ `dev.json` and `prod.json` are generated from the config vars defined in `metadata.ts`.
181
+ They contain placeholder / default values.
182
+
183
+ **Do not commit secrets** to these files. They are intended as value templates.
184
+ Actual secret values should be injected from CI/CD environment variables, not from these
185
+ files.
186
+
187
+ ---
188
+
189
+ ## Permissions (`init/roles/`)
190
+
191
+ `permissionsToGraphql.ts` and its per-entity counterparts map permission strings to
192
+ GraphQL methods. How permissions are named, structured, and seeded into the database
193
+ (`init/roles/initPermissions.ts` and friends) is covered in full in
194
+ [11-permissions.md](./11-permissions.md).
195
+
196
+ ---
197
+
198
+ ## Anti-patterns
199
+
200
+ ### Adding custom logic to `<EntityName>Service.ts`
201
+
202
+ **Wrong:** editing the generated service file directly.
203
+
204
+ **Why:** overwritten on every `regen`. Your changes are lost.
205
+
206
+ **Correct:** use `Additional<EntityName>Service.ts` for custom logic or `hooks/` for
207
+ lifecycle interception.
208
+
209
+ ---
210
+
211
+ ### Creating custom hooks outside the `hooks/` directory
212
+
213
+ **Wrong:** adding a `customHook.ts` file inside `<EntityName>Service/` and calling it
214
+ directly from `Additional<EntityName>Service.ts`.
215
+
216
+ **Why:** works, but bypasses the hook registration system. Framework hooks (audit,
217
+ search) won't compose correctly with custom hooks unless they go through `initUserHooks`.
218
+
219
+ **Correct:** implement hooks in the designated `hooks/` files and register them in
220
+ `initUserHooks.ts`.
221
+
222
+ ---
223
+
224
+ ### Modifying `initBuiltInHooks.ts`
225
+
226
+ **Wrong:** editing the generated file to remove audit logging or search sync hooks.
227
+
228
+ **Why:** the file is regenerated. Your changes are lost.
229
+
230
+ **Correct:** if you need to suppress a built-in hook, do it via the meta (e.g.
231
+ `entity.setAuditable(false)` to disable audit logging).
@@ -0,0 +1,260 @@
1
+ # Frontend File Graph (`<prefix>-ui`)
2
+
3
+ > **Load this file when:** you need to find a specific file in the generated frontend,
4
+ > understand which files are safe to edit, or know what gets regenerated.
5
+ >
6
+ > Legend: `[gen]` = regenerated every `regen` (DO NOT EDIT) · `[yours]` = created once,
7
+ > yours to implement · `[once]` = created once, rarely needs changes
8
+ >
9
+ > Related: [01-overview.md](./01-overview.md) · [07-backend-file-graph.md](./07-backend-file-graph.md) ·
10
+ > [11-permissions.md](./11-permissions.md)
11
+
12
+ ---
13
+
14
+ ## Full tree
15
+
16
+ ```
17
+ <prefix>-ui/
18
+ │
19
+ └── src/
20
+ │
21
+ ├── adm/
22
+ │ │
23
+ │ ├── pages/
24
+ │ │ └── <EntityName>/ One folder per entity
25
+ │ │ │
26
+ │ │ ├── <EntityName>List/
27
+ │ │ │ ├── index.tsx [gen] Entry point — re-exports Default or custom
28
+ │ │ │ ├── Default<Entity>List.tsx [gen] Generated list component
29
+ │ │ │ ├── <Entity>Filter.tsx [yours] Your custom filter (created once)
30
+ │ │ │ ├── Default<Entity>Filter.tsx [gen] Generated default filter
31
+ │ │ │ └── <Entity>ListBreadcrumbs.tsx [gen] Breadcrumb component
32
+ │ │ │
33
+ │ │ ├── <EntityName>Show/
34
+ │ │ │ ├── index.tsx [gen] Entry point
35
+ │ │ │ ├── Default<Entity>Show.tsx [gen] Generated show component
36
+ │ │ │ ├── MainTab.tsx [yours] Your custom main tab content
37
+ │ │ │ ├── DefaultMainTab.tsx [gen] Generated main tab
38
+ │ │ │ ├── DefaultActions.tsx [gen] Generated action buttons
39
+ │ │ │ ├── additionalTabs.tsx [yours] Your extra tabs in show view
40
+ │ │ │ └── tabs/
41
+ │ │ │ └── <RelatedEntity>Tab.tsx [gen] Auto-generated dependency tabs
42
+ │ │ │
43
+ │ │ ├── <EntityName>Create/
44
+ │ │ │ ├── index.tsx [gen] Entry point
45
+ │ │ │ └── Default<Entity>Create.tsx [gen] Generated create form
46
+ │ │ │
47
+ │ │ └── <EntityName>Edit/
48
+ │ │ ├── index.tsx [gen] Entry point
49
+ │ │ └── Default<Entity>Edit.tsx [gen] Generated edit form
50
+ │ │
51
+ │ ├── widgets/
52
+ │ │ └── <EntityName>/
53
+ │ │ ├── <Entity>CountWidget.tsx [gen] Count widget for dashboard
54
+ │ │ └── <Entity>ListWidget.tsx [gen] List widget for dashboard
55
+ │ │
56
+ │ ├── resources.tsx [gen] react-admin resource registry
57
+ │ ├── resourcesChunk0.tsx [gen] Chunked resource imports (code splitting)
58
+ │ ├── resourcesChunk1.tsx [gen] Chunked resource imports
59
+ │ ├── ResourcesPage.tsx [gen] /resources debug page
60
+ │ ├── MetaPage.tsx [gen] /meta debug page
61
+ │ ├── entityMapping.ts [gen] Entity name → component mapping
62
+ │ ├── routes.tsx [gen] All react-admin routes
63
+ │ ├── additionalRoutes.tsx [yours] Your custom routes/pages
64
+ │ ├── Dashboard.tsx [yours] Home page content
65
+ │ ├── PermissionPage.tsx [once] Shown when withPermission denies access
66
+ │ ├── NotFoundPage.tsx [once] Shown when an action doesn't exist on the entity
67
+ │ ├── getDefaultMenu.ts [gen] Auto-generated sidebar menu
68
+ │ ├── getAdditionalMenu.ts [yours] Your extra menu items
69
+ │ │
70
+ │ └── functions/
71
+ │ └── Functions.tsx [gen] System functions page
72
+ │
73
+ ├── i18n/
74
+ │ ├── types.ts [once] ValidationMessages type (one per project)
75
+ │ └── <lang>/
76
+ │ ├── <lang>Catalogs.ts [gen] Catalog translations for this language
77
+ │ ├── <lang>Docs.ts [gen] Document translations (exports `<lang>Documents`)
78
+ │ ├── <lang>InfoRegistries.ts [gen]
79
+ │ ├── <lang>SumRegistries.ts [gen]
80
+ │ ├── <lang>Reports.ts [gen]
81
+ │ ├── <lang>Validation.ts [once] Validation messages (one per language)
82
+ │ └── index.ts [once] Assembles the above + custom keys, per language
83
+ │
84
+ ├── utils/
85
+ │ └── permissions.ts [gen] hasPermission / hasAnyPermission / hasAllPermissions / withPermission
86
+ │
87
+ └── environment/
88
+ ├── src/
89
+ │ ├── App.tsx [gen] Root react-admin App component
90
+ │ ├── dataProvider/
91
+ │ │ ├── index.ts [gen] GraphQL data provider
92
+ │ │ └── getAdditionalMethods.ts [once] Hook for custom data provider methods
93
+ │ ├── i18nProvider/
94
+ │ │ └── index.ts [gen] react-admin i18n provider — merges src/i18n/<lang> per language
95
+ │ ├── layout/
96
+ │ │ ├── AppBar.tsx [gen] Top application bar
97
+ │ │ └── Menu.tsx [gen] Sidebar menu component
98
+ │ ├── routes.ts [gen] Route registration
99
+ │ └── contexts/
100
+ │ └── SpacesContext.ts [gen] Multi-space / tenant context
101
+ │
102
+ ├── .gitlab-ci.yml [gen] GitLab CI pipeline for UI
103
+ ├── Dockerfile [gen] nginx-based frontend Docker image
104
+ └── chart/ Helm chart for Kubernetes
105
+ ├── Chart.yaml [gen]
106
+ ├── values.yaml [gen]
107
+ └── templates/
108
+ ├── front.yaml [gen]
109
+ └── ingress.yaml [gen]
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Per-entity: what to edit and what not to
115
+
116
+ ### List page
117
+
118
+ | File | Owner | When to edit |
119
+ |------|-------|-------------|
120
+ | `index.tsx` | `[gen]` | Never — re-exports Default or your override |
121
+ | `Default<Entity>List.tsx` | `[gen]` | Never |
122
+ | `Default<Entity>Filter.tsx` | `[gen]` | Never |
123
+ | `<Entity>Filter.tsx` | `[yours]` | Customise filter fields, add new filters |
124
+ | `<Entity>ListBreadcrumbs.tsx` | `[gen]` | Never |
125
+
126
+ ### Show page
127
+
128
+ | File | Owner | When to edit |
129
+ |------|-------|-------------|
130
+ | `index.tsx` | `[gen]` | Never |
131
+ | `Default<Entity>Show.tsx` | `[gen]` | Never |
132
+ | `DefaultMainTab.tsx` | `[gen]` | Never |
133
+ | `MainTab.tsx` | `[yours]` | Override the main tab layout |
134
+ | `DefaultActions.tsx` | `[gen]` | Never |
135
+ | `additionalTabs.tsx` | `[yours]` | Add extra tabs to the show view |
136
+ | `tabs/<Related>Tab.tsx` | `[gen]` | Never — auto-generated dependency lists |
137
+
138
+ ### Create / Edit pages
139
+
140
+ All files under `Create/` and `Edit/` are `[gen]`. Form fields and their order are
141
+ controlled from the meta via `entity.getForms()`. See [03-entity-types.md](./03-entity-types.md).
142
+
143
+ ---
144
+
145
+ ## System-level files
146
+
147
+ ### `Dashboard.tsx` — yours {#dashboard}
148
+
149
+ The home page of the admin UI. Empty by default. Add widgets, stats, charts here.
150
+ Widget components are generated per-entity in `widgets/<EntityName>/`.
151
+
152
+ ### `additionalRoutes.tsx` — yours {#additional-routes}
153
+
154
+ Register custom react-admin `<Route>` components here. Used for non-entity pages like
155
+ reports, custom dashboards, or wizard flows.
156
+
157
+ ### `getAdditionalMenu.ts` — yours {#additional-menu}
158
+
159
+ Return extra menu items from this function. Appended to the auto-generated menu.
160
+ Menu items added via `system.addGroupMenuItem` / `addInternalMenuItem` etc. in the meta
161
+ are reflected in `getDefaultMenu.ts` — do not edit that file.
162
+
163
+ ### `PermissionPage.tsx` / `NotFoundPage.tsx` — once {#permission-fallbacks}
164
+
165
+ Fallback components rendered instead of a page's real content. Both re-export a
166
+ react-admin built-in by default and are safe to replace. See
167
+ [11-permissions.md](./11-permissions.md#fallback-components) for when each one is used.
168
+
169
+ ---
170
+
171
+ ## How the menu is built
172
+
173
+ ```
174
+ getDefaultMenu.ts [gen] ← from system.addGroupMenuItem / addInternalMenuItem in meta
175
+ getAdditionalMenu.ts [yours] ← your custom extra items
176
+
177
+ Menu.tsx [gen] ← combines both
178
+ ```
179
+
180
+ **Anti-pattern — editing `getDefaultMenu.ts` to add menu items:**
181
+
182
+ ```ts
183
+ // WRONG: overwritten on next regen
184
+ export const getDefaultMenu = () => [
185
+ ...generatedItems,
186
+ { label: 'My Custom Page', path: '/custom' } // lost on regen
187
+ ]
188
+ ```
189
+
190
+ **Correct:** add menu items in `getAdditionalMenu.ts` (for frontend-only custom items)
191
+ or via `system.addInternalMenuItem` / `addGroupMenuItem` in the meta (for items
192
+ backed by a page registered in the system).
193
+
194
+ ---
195
+
196
+ ## i18n {#i18n}
197
+
198
+ Entity/field-derived translation files (`<lang>Catalogs.ts`, `<lang>Docs.ts`,
199
+ `<lang>InfoRegistries.ts`, `<lang>SumRegistries.ts`, `<lang>Reports.ts`) are `[gen]` —
200
+ fully generated from entity titles and field titles defined in the meta. To change one
201
+ of these translations:
202
+
203
+ 1. Update the title in `metadata.ts`: `entity.setTitle({ singular: 'Order', plural: 'Orders' }, 'en')`
204
+ 2. Run `regen`
205
+
206
+ Three other files exist per project/language and are `[once]` — created only if
207
+ missing, then yours to edit freely:
208
+
209
+ | File | Scope | Purpose |
210
+ |------|-------|---------|
211
+ | `i18n/types.ts` | One per project | The `ValidationMessages` type — add fields here as you add validation messages |
212
+ | `i18n/<lang>/<lang>Validation.ts` | One per language | Validation message strings, typed by `ValidationMessages` |
213
+ | `i18n/<lang>/index.ts` | One per language | Assembles all of the above into the object `i18nProvider` imports; the place to add any translation key that isn't derived from an entity/field (UI labels, error messages, etc.) |
214
+
215
+ `i18n/<lang>/index.ts` is what `environment/src/i18nProvider/index.ts` (`[gen]`)
216
+ actually imports — it merges the entity-derived generated files, `<lang>Validation.ts`,
217
+ and falls back to `ra-language-english` for anything not overridden. Since it's created
218
+ once, it's safe to add project-specific keys directly into it.
219
+
220
+ **Anti-pattern — editing an entity/field-derived translation file directly:**
221
+
222
+ Editing `<lang>Catalogs.ts`, `<lang>Docs.ts`, or any other `[gen]` lang file is
223
+ pointless — changes are overwritten on regen. Add custom keys to `i18n/<lang>/index.ts`
224
+ instead.
225
+
226
+ ---
227
+
228
+ ## Data provider
229
+
230
+ `dataProvider/index.ts` is generated. It wires the react-admin data provider to the
231
+ GraphQL backend.
232
+
233
+ `dataProvider/getAdditionalMethods.ts` is created once. Use it to add custom data
234
+ provider methods for non-standard GraphQL queries.
235
+
236
+ ---
237
+
238
+ ## Anti-patterns
239
+
240
+ ### Editing `Default<Entity>*.tsx` components
241
+
242
+ **Wrong:** modifying generated list/show/create/edit components directly.
243
+
244
+ **Why:** overwritten on every `regen`. Your changes vanish.
245
+
246
+ **Correct:**
247
+ - For list: customise `<Entity>Filter.tsx`
248
+ - For show: implement `MainTab.tsx` or add tabs in `additionalTabs.tsx`
249
+ - For create/edit: adjust field visibility/order in the meta via `entity.getForms()`
250
+ or `field.setShowInCreate(false)` / `setShowInEdit(false)`
251
+
252
+ ---
253
+
254
+ ### Adding custom routes directly to `routes.tsx`
255
+
256
+ **Wrong:** editing the generated `routes.tsx` to add a new route.
257
+
258
+ **Why:** overwritten on regen.
259
+
260
+ **Correct:** add custom routes in `additionalRoutes.tsx`.