runlify 0.0.793 → 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,213 @@
1
+ # Bootstrap Options (`BootstrapEntityOptions`)
2
+
3
+ > **Load this file when:** you need to toggle a generation feature on/off, change
4
+ > project identity settings, configure infrastructure (k8s, CI, Docker), or understand
5
+ > what a specific option flag does.
6
+ >
7
+ > Source: `src/projectsGeneration/types.ts`
8
+ > Consumed from: `src/meta/options.json` (read by `runlify regen`)
9
+
10
+ ---
11
+
12
+ ## How options are applied
13
+
14
+ `options.json` is read once per `regen` and applied globally to all entities.
15
+ Individual entities can receive their own options override by passing `options` as the
16
+ third argument to `addCatalog`, `addDocument`, etc.:
17
+
18
+ ```ts
19
+ system.addCatalog('specialCatalog', undefined, { ...defaultBootstrapEntityOptions, genUiResources: false })
20
+ ```
21
+
22
+ Most projects use the same options object for all entities.
23
+
24
+ ---
25
+
26
+ ## All flags
27
+
28
+ ### Code generation toggles — Backend
29
+
30
+ | Flag | Default | Description |
31
+ |------|---------|-------------|
32
+ | `genPrismaServices` | `true` | Generate Prisma-based service files |
33
+ | `genPrismaSchema` | `true` | Generate `.prisma` schema files |
34
+ | `genGraphSchema` | `true` | Generate GraphQL type definitions |
35
+ | `genGraphResolvers` | `true` | Generate GraphQL resolvers |
36
+ | `genContext` | `true` | Generate request context wiring |
37
+ | `typesOnly` | `false` | Internal — first generation pass (types only). Do not set manually. |
38
+ | `readOnly` | `false` | Skip write operations in generation. Rarely used. |
39
+ | `corePrismaGetter` | `true` | Generate the Prisma client getter (`getPrisma.ts`) |
40
+ | `coreIndex` | `true` | Generate the main `index.ts` entry point |
41
+
42
+ ---
43
+
44
+ ### Code generation toggles — Frontend
45
+
46
+ | Flag | Default | Description |
47
+ |------|---------|-------------|
48
+ | `genFrontend` | `true` | Generate the entire frontend. Set `false` to generate backend only (`regen --backOnly` also sets this). |
49
+ | `genUiResources` | `true` | Generate react-admin resource registrations |
50
+ | `genUiResourcesPage` | `true` | Generate the `/resources` debug page |
51
+ | `genUiEntityMapping` | `true` | Generate `entityMapping.ts` |
52
+ | `genUiMenu` | `true` | Generate `getDefaultMenu.ts` |
53
+ | `genUiElements` | `true` | Generate entity page components |
54
+ | `genUiRoutes` | `true` | Generate `routes.tsx` |
55
+ | `genUIApp` | `true` | Generate the root `App.tsx` |
56
+ | `genUiFunctions` | `true` | Generate the functions page |
57
+ | `genUiDashboard` | `true` | Generate `Dashboard.tsx` (created once) |
58
+ | `genUiCountWidget` | `true` | Generate count widgets per entity |
59
+ | `genUiListWidget` | `true` | Generate list widgets per entity |
60
+ | `genUiAppBar` | `true` | Generate `AppBar.tsx` |
61
+ | `showMetaPage` | `true` | Show the `/meta` debug page in the UI |
62
+
63
+ ---
64
+
65
+ ### Form generation toggles
66
+
67
+ Nested under `forms`:
68
+
69
+ | Flag | Default | Description |
70
+ |------|---------|-------------|
71
+ | `forms.list.gen` | `true` | Generate list page components |
72
+ | `forms.show.gen` | `true` | Generate show page components |
73
+ | `forms.edit.gen` | `true` | Generate edit form components |
74
+ | `forms.edit.idEditable` | `false` | Show `id` field in edit forms |
75
+ | `forms.create.gen` | `true` | Generate create form components |
76
+ | `forms.create.idEditable` | `false` | Show `id` field in create forms |
77
+ | `forms.menu.show` | `true` | Show entity in sidebar menu |
78
+ | `forms.resourcesPage.show` | `true` | Show entity on resources page |
79
+
80
+ ---
81
+
82
+ ### CI/CD — GitLab CI
83
+
84
+ | Flag | Default | Description |
85
+ |------|---------|-------------|
86
+ | `genBackGitlabCi` | `true` | Generate `.gitlab-ci.yml` for backend |
87
+ | `genUiGitlabCi` | `true` | Generate `.gitlab-ci.yml` for frontend |
88
+ | `genBackCiNotify` | `true` | Include Slack/chat notifications in backend CI |
89
+ | `genUiCiNotify` | `true` | Include Slack/chat notifications in frontend CI |
90
+
91
+ ---
92
+
93
+ ### CI/CD — Docker
94
+
95
+ | Flag | Default | Description |
96
+ |------|---------|-------------|
97
+ | `genDockerfileBack` | `true` | Generate `Dockerfile` for backend |
98
+ | `genDockerfileUI` | `true` | Generate `Dockerfile` for frontend |
99
+ | `adminBaseDockerimage` | `'nginx:1.23-alpine'` | Base image for frontend Docker build |
100
+ | `backendBaseDockerimage` | `'registry.gitlab.com/making.ventures/images/node-base'` | Base image for backend Docker build |
101
+
102
+ ---
103
+
104
+ ### Kubernetes / Helm
105
+
106
+ | Flag | Default | Description |
107
+ |------|---------|-------------|
108
+ | `genBackChartValues` | `true` | Generate `chart/values.yaml` for backend |
109
+ | `genBackChartIngress` | `true` | Generate `chart/templates/ingress.yaml` for backend |
110
+ | `genBackChartBack` | `true` | Generate `chart/templates/back.yaml` for backend |
111
+ | `genUiChartIngress` | `true` | Generate ingress chart for frontend |
112
+ | `genUiChartFront` | `true` | Generate deployment chart for frontend |
113
+ | `k8sChartName` | `''` | Helm chart name |
114
+ | `k8sNamespacePrefix` | `''` | Kubernetes namespace prefix |
115
+ | `k8sAppsDomain` | `'apps.making.ventures'` | Base domain for k8s ingress |
116
+ | `k8sSubdomainPrefix` | `''` | Subdomain prefix applied to all ingress hosts |
117
+ | `k8sImagePullSecrets` | `'docker-registry'` | k8s image pull secret name |
118
+ | `ingressAnnotationBodySize` | `'50m'` | `nginx.ingress.kubernetes.io/proxy-body-size` annotation |
119
+
120
+ ---
121
+
122
+ ### Project identity
123
+
124
+ | Flag | Default | Description |
125
+ |------|---------|-------------|
126
+ | `projectsGroup` | `''` | GitLab group or organisation name |
127
+ | `projectPrefix` | `''` | Technical prefix (e.g. `'myapp'`) — used in k8s names, image tags |
128
+ | `dbName` | `''` | Default database name in Postgres |
129
+ | `projectName` | `''` | Human-readable project name |
130
+
131
+ ---
132
+
133
+ ### Feature flags
134
+
135
+ | Flag | Default | Description |
136
+ |------|---------|-------------|
137
+ | `usersEnabled` | `true` | Generate user management entities and auth |
138
+ | `tenantsAvailable` | `false` | Enable multi-tenancy support |
139
+ | `themesEnabled` | `true` | Enable theme switching in UI |
140
+ | `mainColorOfAppTitile` | `true` | Apply primary color to app title |
141
+ | `sharding` | `false` | Enable sharding support globally |
142
+ | `breadcrumb` | `false` | Enable breadcrumb navigation in UI |
143
+ | `telemetry` | `false` | Enable OpenTelemetry tracing |
144
+ | `mountebankEnabled` | `false` | Enable Mountebank mock server integration |
145
+ | `exportHtmlEnabled` | `false` | Enable HTML export feature |
146
+ | `auditableOnlyByUser` | `false` | Only audit changes made by users (not system) |
147
+ | `useSortedFilter` | `false` | Use sorted filter UI in list pages |
148
+
149
+ ---
150
+
151
+ ### System menu visibility
152
+
153
+ | Flag | Default | Description |
154
+ |------|---------|-------------|
155
+ | `showFunctionsInMenu` | `true` | Show system Functions page in menu |
156
+ | `showResourcesInMenu` | `true` | Show Resources debug page in menu |
157
+ | `showMetaInMenu` | `true` | Show Meta debug page in menu |
158
+
159
+ ---
160
+
161
+ ### Output paths
162
+
163
+ | Flag | Default | Description |
164
+ |------|---------|-------------|
165
+ | `detachedBackProject` | `''` | Absolute path to the backend project. Auto-resolved to `../<prefix>-back` if empty. |
166
+ | `detachedUiProject` | `''` | Absolute path to the frontend project. Auto-resolved to `../<prefix>-ui` if empty. |
167
+
168
+ ---
169
+
170
+ ### Other
171
+
172
+ | Flag | Default | Description |
173
+ |------|---------|-------------|
174
+ | `skipWarningThisIsGenerated` | `false` | Suppress the `DO NOT EDIT! THIS IS GENERATED FILE` header |
175
+ | `graphGeneratorCommand` | `''` | Command to run the local GraphQL schema generator (alternative to built-in) |
176
+
177
+ ---
178
+
179
+ ## Anti-patterns
180
+
181
+ ### Setting `typesOnly: true` in `options.json`
182
+
183
+ **Wrong:** manually setting `typesOnly: true` in the options file.
184
+
185
+ **Why:** this is an internal flag used by `generateProject` for the first generation
186
+ pass. It produces an incomplete output — only TypeScript type files, no resolvers, no
187
+ Prisma schema. If set permanently, your backend will be broken.
188
+
189
+ **Correct:** never set `typesOnly` manually.
190
+
191
+ ---
192
+
193
+ ### Setting `genFrontend: false` in `options.json` permanently to skip frontend
194
+
195
+ **Wrong:** permanently disabling frontend in `options.json` while the team still needs
196
+ frontend generation.
197
+
198
+ **Why:** other developers running `regen` won't regenerate the frontend, leading to
199
+ divergent states.
200
+
201
+ **Correct:** use the CLI flag `runlify regen --backOnly` for a one-off backend-only
202
+ regen. It temporarily sets `genFrontend=false` without changing `options.json`.
203
+
204
+ ---
205
+
206
+ ### Leaving `projectPrefix`, `dbName`, `projectName` empty
207
+
208
+ **Wrong:** keeping default empty strings in `options.json`.
209
+
210
+ **Why:** these values are embedded into generated Helm chart names, Docker image tags,
211
+ database names, and CI variables. Empty strings produce broken infrastructure configs.
212
+
213
+ **Correct:** always set all three to meaningful values for your project.
@@ -0,0 +1,164 @@
1
+ # Modules
2
+
3
+ > **Load this file when:** you need to use a built-in module, understand what
4
+ > `addCommonEntities` adds to the system, or write a reusable module function.
5
+ >
6
+ > Related: [02-system-meta-builder.md](./02-system-meta-builder.md) ·
7
+ > [03-entity-types.md](./03-entity-types.md)
8
+
9
+ ---
10
+
11
+ ## What a module is
12
+
13
+ A module is a plain function with the signature:
14
+
15
+ ```ts
16
+ (system: SystemMetaBuilder) => void
17
+ ```
18
+
19
+ It calls `system.addCatalog`, `system.addDocument`, `system.addConfigVar`, etc. to
20
+ register a bundle of related entities and config in one call. There is no special
21
+ registration mechanism — a module is just a function you call.
22
+
23
+ ---
24
+
25
+ ## `addCommonEntities(system)` {#addCommonEntities}
26
+
27
+ Source: `src/projectsGeneration/commonEntities/addCommonEntities.ts`
28
+ Import: `import { addCommonEntities } from 'runlify'`
29
+
30
+ The standard foundation layer. Adds all infrastructure entities required by the
31
+ framework itself. Most meta projects call this once near the top of `metadata.ts`.
32
+
33
+ ### What it adds
34
+
35
+ | Entity | Type | Purpose |
36
+ |--------|------|---------|
37
+ | `languages` | Catalog | Supported UI languages |
38
+ | `tenants` | Catalog | Tenants (only used when `tenantsAvailable=true`) |
39
+ | `managers` | Catalog | Admin users |
40
+ | `users` | Catalog | End users |
41
+ | `auditLogs` | Catalog | Change history records |
42
+ | `roles` | Catalog | Access roles |
43
+ | `refreshTokens` | Catalog | JWT refresh token storage |
44
+ | `autogeneration` | Catalog | Code generation tracking |
45
+ | `aggregateTrackings` | Catalog | External search sync tracking |
46
+
47
+ Plus:
48
+ - Email module entities (see [`addEmailModuleEntities`](#addEmailModuleEntities))
49
+ - Configuration variable entities (`configurationVariables` — stores runtime config in DB)
50
+ - Standard system CLI commands
51
+
52
+ **Anti-pattern — calling `addCommonEntities` after adding your own entities:**
53
+
54
+ The common entities use fixed names. If you add a catalog named `users` or `roles`
55
+ before calling `addCommonEntities`, the call throws (duplicate entity name).
56
+
57
+ **Correct:** always call `addCommonEntities(system)` **before** adding your domain
58
+ entities.
59
+
60
+ ---
61
+
62
+ ## `addEmailModuleEntities(system)` {#addEmailModuleEntities}
63
+
64
+ Source: `src/projectsGeneration/modules/addEmailModuleEntities.ts`
65
+ Import: `import { addEmailModuleEntities } from 'runlify'` (also called by `addCommonEntities`)
66
+
67
+ Adds all entities required for the email / notification system.
68
+
69
+ ### What it adds
70
+
71
+ | Entity | Type | Purpose |
72
+ |--------|------|---------|
73
+ | `messageTypes` | Catalog | Message type registry (e.g. `plain`) |
74
+ | `templateStyles` | Catalog | HTML/CSS styles for email templates |
75
+ | `messageTemplates` | Catalog | Email template definitions |
76
+ | `mailingCampaignStatuses` | Catalog | Campaign lifecycle statuses |
77
+ | `mailingCampaigns` | Catalog | Mailing campaign records |
78
+ | `mailingMessages` | Catalog | Individual messages in a campaign |
79
+
80
+ Called automatically by `addCommonEntities`. Only call it directly if you are NOT using
81
+ `addCommonEntities` but still need the email module.
82
+
83
+ **Anti-pattern — calling `addEmailModuleEntities` twice:**
84
+
85
+ Called once by `addCommonEntities`. Calling it again throws duplicate entity name errors.
86
+
87
+ ---
88
+
89
+ ## Writing your own module {#custom-module}
90
+
91
+ A module is just a function. Convention: one file per module, function named `add<ModuleName>`.
92
+
93
+ ```ts
94
+ // src/meta/modules/addOrderingModule.ts
95
+ import SystemMetaBuilder from 'runlify'
96
+
97
+ export const addOrderingModule = (system: SystemMetaBuilder) => {
98
+ const orderStatuses = system.addCatalog('orderStatuses', { singular: 'Order status', plural: 'Order statuses' })
99
+ orderStatuses.getKey().setType('string')
100
+ orderStatuses.addField('title', undefined, { isTitleField: true }).setType('string').setRequired()
101
+ orderStatuses.addPredefinedElements([
102
+ { id: 'new', title: 'New' },
103
+ { id: 'processing', title: 'Processing' },
104
+ { id: 'completed', title: 'Completed' },
105
+ ])
106
+
107
+ const orders = system.addDocument('orders', { singular: 'Order', plural: 'Orders' })
108
+ orders.addField('amount').setType('float').setRequired()
109
+ orders.addLinkField(orderStatuses, 'statusId').setRequired()
110
+ }
111
+ ```
112
+
113
+ Then in `metadata.ts`:
114
+
115
+ ```ts
116
+ import { addOrderingModule } from './modules/addOrderingModule'
117
+
118
+ const system = new SystemMetaBuilder('myapp')
119
+ addCommonEntities(system)
120
+ addOrderingModule(system)
121
+ ```
122
+
123
+ ---
124
+
125
+ ## Module composition rules
126
+
127
+ 1. **Order matters.** Modules that reference entities from other modules must be called
128
+ after those modules.
129
+ 2. **No circular references.** Module A cannot reference entities that Module B adds, if
130
+ Module B also references entities from Module A.
131
+ 3. **Call `addCommonEntities` first** — it seeds `users`, `tenants`, `roles`, and other
132
+ system entities that domain modules typically reference.
133
+
134
+ ---
135
+
136
+ ## Anti-patterns
137
+
138
+ ### Defining a module that conditionally adds entities
139
+
140
+ **Wrong:**
141
+ ```ts
142
+ export const addOrderingModule = (system: SystemMetaBuilder, withReturns: boolean) => {
143
+ system.addDocument('orders')
144
+ if (withReturns) {
145
+ system.addDocument('returns') // conditional entity
146
+ }
147
+ }
148
+ ```
149
+
150
+ **Why:** generates inconsistent meta across environments or team members. The system
151
+ description must be deterministic and environment-independent.
152
+
153
+ **Correct:** always include or always exclude. Use separate modules for separate
154
+ feature sets: `addOrderingModule` and `addOrderingWithReturnsModule`.
155
+
156
+ ---
157
+
158
+ ### Putting module logic inside `metadata.ts` directly
159
+
160
+ **Wrong:** writing 500 lines of entity definitions directly in `metadata.ts`.
161
+
162
+ **Why:** hard to maintain, hard to reuse across projects, impossible to test in isolation.
163
+
164
+ **Correct:** extract domain areas into module functions in `src/meta/modules/`.
@@ -0,0 +1,231 @@
1
+ # Permissions
2
+
3
+ > **Load this file when:** you need to understand how access control works — which
4
+ > permission gates a page/field/action, how permission strings are named, or how new
5
+ > permissions get seeded into the database.
6
+ >
7
+ > Related: [07-backend-file-graph.md](./07-backend-file-graph.md) ·
8
+ > [08-frontend-file-graph.md](./08-frontend-file-graph.md)
9
+
10
+ ---
11
+
12
+ ## Two permission spaces
13
+
14
+ Runlify generates checks against **two distinct kinds** of permission string. They look
15
+ similar but are enforced completely differently.
16
+
17
+ | Space | Format | Enforced by | Meaning |
18
+ |-------|--------|-------------|---------|
19
+ | **Data** | `<entity>.<action>` | **Backend** — every GraphQL resolver checks it | Can the caller read/write this entity's data at all, from anywhere (its own page, a reference field on another page, an autocomplete, …) |
20
+ | **UI** | `ui.<entity>.list` | **Frontend only** — never sent to or checked by the backend | Can the caller open this entity's own list page / see it in the menu |
21
+
22
+ The split exists because a single `<entity>.all` permission used to do both jobs at
23
+ once: granting it opened the entity's own list page **and** silently made the entity
24
+ visible everywhere it's referenced from another entity (filters, autocompletes,
25
+ reference fields, related-record tabs). Those are different concerns — someone might
26
+ need to see a member's `memberType` label in the members list without ever needing to
27
+ open the `memberTypes` list page themselves.
28
+
29
+ `ui.<entity>.list` is **only ever checked in the generated frontend code**. It is never
30
+ part of a GraphQL permission check, never appears in
31
+ [`permissionsToGraphql.ts`](./07-backend-file-graph.md), and granting it does **not**
32
+ give access to any data. It exists purely to gate navigation.
33
+
34
+ ---
35
+
36
+ ## Data-space actions
37
+
38
+ | Action | Meaning |
39
+ |--------|---------|
40
+ | `.get` | Read a single record — gates the Show page and `ReferenceField` links to it |
41
+ | `.all` | Read a list of records — gates list queries, autocompletes, filters, reference display |
42
+ | `.meta` | Read list pagination metadata (total count) |
43
+ | `.create` | Create a record |
44
+ | `.update` | Update a record |
45
+ | `.delete` | Delete a record |
46
+
47
+ These come from `getRuntimePermissions()` (backend, not generated by Runlify — see
48
+ [Where data-space permissions come from](#where-data-space-permissions-come-from)),
49
+ which reflects over every service's methods. Any method a service exposes becomes a
50
+ checkable permission; `get`/`all`/`meta`/`create`/`update`/`delete` exist simply because
51
+ `BaseService` defines those methods and every entity service extends it.
52
+
53
+ ---
54
+
55
+ ## Where each permission is checked
56
+
57
+ ### Own page — `resources.ts`
58
+
59
+ Each `<Resource>`'s `show`/`edit`/`create`/`list` prop is wrapped in
60
+ [`withPermission`](#uti-lspermissionsts) so an unauthorized visit always renders a
61
+ fallback component instead of `undefined` (see
62
+ [Fallback components](#fallback-components)):
63
+
64
+ | Resource prop | Required permissions |
65
+ |---------------|----------------------|
66
+ | `show` | `<entity>.get` |
67
+ | `list` | `<entity>.all` **and** `<entity>.meta` **and** `ui.<entity>.list` |
68
+ | `edit` | `<entity>.update` **and** `<ref>.all` for every **required** link field shown in the edit form |
69
+ | `create` | `<entity>.create` **and** `<ref>.all` for every **required** link field shown in the create form |
70
+
71
+ `list` needs all three because opening the list page requires reading records
72
+ (`.all`), reading pagination metadata (`.meta`), and having UI access to the page
73
+ itself (`ui.<entity>.list`) — the data permissions alone are not enough to open the
74
+ page, and the UI permission alone is not enough to load any data.
75
+
76
+ `edit`/`create` add the referenced entity's `.all` permission for every **required**
77
+ link field, because a required autocomplete that the user cannot see would make the
78
+ form impossible to submit. Optional link fields don't gate the whole page — see
79
+ [Optional link fields](#optional-link-fields) below.
80
+
81
+ ### Referenced entity — everywhere else
82
+
83
+ When an entity's data is shown or picked *from inside another entity's page* (not its
84
+ own page), only the data-space `<ref>.all` / `<ref>.get` permissions are checked —
85
+ never `ui.<ref>.list`. Seeing a reference's title, filtering by it, or autocompleting
86
+ it does not require access to that entity's own list page.
87
+
88
+ | Location | Permission checked |
89
+ |----------|---------------------|
90
+ | `ReferenceField` (List, Show `MainTab`, dependency tabs) | `<ref>.all` gates whether the field renders at all; `<ref>.get` gates whether it renders as a clickable link to the Show page |
91
+ | `ReferenceInput` in a Filter | `<ref>.all` gates whether the whole input renders |
92
+ | `rowClick` on a list Datagrid | `<entity>.get` — can the user open the Show page for the clicked row |
93
+ | `BulkDeleteButton` / `DeleteButton` | `<entity>.delete` |
94
+ | Menu item for an entity | `<entity>.all`, `<entity>.meta`, `ui.<entity>.list` — same trio as the list page, so a menu entry never links to a page the user can't open |
95
+
96
+ ### System pages — `ui.dashboard` / `ui.functions` / `ui.resources` / `ui.meta` {#system-pages}
97
+
98
+ A handful of non-entity pages (Dashboard, Functions, Resources debug page, Meta debug
99
+ page) use the same `ui.*` namespace, via `<Guard shouldHave='ui.<page>'>` in `routes.ts`
100
+ and `permissions: ['ui.<page>']` on the corresponding menu item in `getDefaultMenu.ts`.
101
+ They aren't tied to an entity, so they aren't generated by `uiPermissions.ts`'s
102
+ per-entity loop — instead they're added conditionally based on the `BootstrapEntityOptions`
103
+ flag that controls whether the page is generated at all (`genUiDashboard`,
104
+ `showFunctionsInMenu`, `showResourcesInMenu`, `showMetaInMenu`). If a page is disabled
105
+ in options, its `ui.*` permission isn't generated either.
106
+
107
+ ### Optional link fields — Create/Edit forms {#optional-link-fields}
108
+
109
+ A link field that is **not required on input** doesn't gate the whole page. Instead,
110
+ the individual autocomplete is hidden if the user lacks `<ref>.all` — the rest of the
111
+ form still renders normally:
112
+
113
+ ```
114
+ {debug && hasPermission(permissions, '<ref>.all') && <Grid item>...</Grid>}
115
+ ```
116
+
117
+ (`debug &&` is a pre-existing, unrelated dev-only field toggle — it wraps every
118
+ optional field, not just link fields.)
119
+
120
+ A **required** link field is never hidden this way, because the form couldn't be
121
+ submitted without it. Instead its permission is added to the page-level check in
122
+ `resources.ts` (see the table above) — if the user can't see it, they can't open the
123
+ page at all.
124
+
125
+ ---
126
+
127
+ ## `utils/permissions.ts` {#utils-permissionsts}
128
+
129
+ Generated (`[gen]`) — see [08-frontend-file-graph.md](./08-frontend-file-graph.md).
130
+ Exports four functions, all operating on the raw `permissions: string[]` array from
131
+ `usePermissions()`:
132
+
133
+ | Function | Behaviour |
134
+ |----------|-----------|
135
+ | `hasPermission(permissions, key)` | `true` if `key` matches (see below) |
136
+ | `hasAnyPermission(permissions, keys)` | `true` if at least one of `keys` matches (empty `keys` → `true`) |
137
+ | `hasAllPermissions(permissions, keys)` | `true` only if every key in `keys` matches (empty `keys` → `true`) |
138
+ | `withPermission(permissions, keys, Component)` | Returns `Component` if `hasAllPermissions` passes, otherwise `PermissionPage` |
139
+
140
+ **Matching:** a required key ending in `.*` (e.g. `'ui.memberTypes.*'`) matches by
141
+ prefix — satisfied if at least one current permission starts with `'ui.memberTypes.'`.
142
+ A plain key without `.*` is matched by exact equality.
143
+
144
+ `withPermission` is what makes every `<Resource>` prop in `resources.ts` always resolve
145
+ to a real component — never `undefined`. Passing `undefined` to a react-admin
146
+ `<Resource>` prop causes react-router to silently fall through to a nearby catch-all
147
+ route (usually `list`) instead of showing a permission error, which is confusing and
148
+ was the original motivation for this file.
149
+
150
+ ---
151
+
152
+ ## Fallback components {#fallback-components}
153
+
154
+ Two once (`[once]`) components, generated only if missing:
155
+
156
+ | File | Content | Used when |
157
+ |------|---------|-----------|
158
+ | `src/adm/PermissionPage.tsx` | Re-exports react-admin's `AccessDenied` | `withPermission` denies access (missing permission) |
159
+ | `src/adm/NotFoundPage.tsx` | Re-exports react-admin's `NotFound` | The action doesn't exist for this entity at all — e.g. `edit` when `entity.setUpdatableByUser(false)` |
160
+
161
+ Both are safe to replace with a custom component — Runlify never touches them again
162
+ once created. Anything importing `withPermission`'s fallback keeps working as long as
163
+ the file still default-exports a component.
164
+
165
+ ---
166
+
167
+ ## Where data-space permissions come from {#where-data-space-permissions-come-from}
168
+
169
+ `getRuntimePermissions.ts` is **not generated by Runlify** — it's a hand-written
170
+ backend file (`src/adm/services/getRuntimePermissions.ts`) that reflects over every
171
+ registered service's prototype chain at runtime and turns every method name into
172
+ `<service>.<methodName>`. Because `BaseService` defines `get`/`all`/`meta`/`create`/
173
+ `update`/`delete`, and every entity service extends it, those six actions exist for
174
+ every entity automatically — no per-entity registration needed, in Runlify or
175
+ anywhere else. Adding a new entity and running `regen` is enough; the next time
176
+ `getRuntimePermissions()` runs, the new service's methods are picked up.
177
+
178
+ This is separate from `initPermissions.ts` actually **inserting** rows into the
179
+ `permissions` table — see the next section.
180
+
181
+ ---
182
+
183
+ ## Seeding permissions into the database {#seeding}
184
+
185
+ ```
186
+ src/init/roles/
187
+ generated/
188
+ initPermissions.ts [gen] Collects all four sources, writes them to the DB
189
+ uiPermissions.ts [gen] One 'ui.<entity>.list' per entity + enabled system pages (see #system-pages)
190
+ customPermissions.ts [once] Hand-written data-space permissions (empty array to start)
191
+ customUiPermissions.ts [once] Hand-written UI-space permissions (empty array to start)
192
+ ```
193
+
194
+ The two `[gen]` files are grouped in `generated/` so it's visually unambiguous which
195
+ files in this directory are regenerated and which are yours — the two `custom*.ts`
196
+ files sit one level up, outside `generated/`.
197
+
198
+ `initPermissions.ts` merges four sources and calls
199
+ `ctx.service('permissions').createMany(...)`:
200
+
201
+ ```
202
+ getRuntimePermissions(ctx) ← reflected data-space permissions (see above)
203
+ uiPermissions ← generated 'ui.<entity>.list' list, one per entity
204
+ customPermissions ← your hand-written extras (data-space)
205
+ customUiPermissions ← your hand-written extras (UI-space)
206
+ ```
207
+
208
+ Running `initPermissions` (via `yarn init:permissions` or equivalent) is a **manual
209
+ step** — it is not triggered automatically by `regen`. A new entity's permissions exist
210
+ in code as soon as `regen` finishes (the service exists, `uiPermissions.ts` lists it),
211
+ but they only land in the `permissions` table the next time someone runs the seed
212
+ script.
213
+
214
+ **Anti-pattern — adding a custom permission string directly to `initPermissions.ts` or
215
+ `uiPermissions.ts`:**
216
+
217
+ Both are `[gen]` — overwritten on every `regen`. Add hand-written permissions to
218
+ `customPermissions.ts` (data-space) or `customUiPermissions.ts` (UI-space) instead.
219
+
220
+ ---
221
+
222
+ ## Naming a new permission
223
+
224
+ - Data-space, tied to an existing service method → nothing to name; it already exists
225
+ via reflection.
226
+ - Data-space, not backed by a service method (a custom action) → add it to
227
+ `customPermissions.ts`, as `<entity-or-domain>.<action>`, matching the existing
228
+ `<entity>.<action>` convention.
229
+ - UI-only, not covered by the generated `ui.<entity>.list` set → add it to
230
+ `customUiPermissions.ts`, prefixed `ui.` so it's unambiguous that it is never
231
+ enforced by the backend.
package/docs/INDEX.md ADDED
@@ -0,0 +1,70 @@
1
+ # Runlify Documentation
2
+
3
+ > Written for **consumers** of Runlify — projects that already use it.
4
+ > Primary audience: AI agents. Secondary: human developers.
5
+ >
6
+ > Each file in this documentation is **self-contained** for its topic.
7
+ > Load only the file relevant to your current task.
8
+
9
+ ---
10
+
11
+ ## Table of Contents
12
+
13
+ | # | File | Topic |
14
+ |---|------|-------|
15
+ | 1 | [01-overview.md](./01-overview.md) | Mental model, data flow, two classes of files (generated vs yours) |
16
+ | 2 | [02-system-meta-builder.md](./02-system-meta-builder.md) | All top-level methods on `SystemMetaBuilder` |
17
+ | 3 | [03-entity-types.md](./03-entity-types.md) | `Catalog`, `Document`, `InfoRegistry`, `SumRegistry`, `Report` — when to use each |
18
+ | 4 | [04-fields.md](./04-fields.md) | All field builders, scalar types, filters, indexes, constraints |
19
+ | 5 | [05-custom-methods.md](./05-custom-methods.md) | `MethodBuilder`, Input/Output models, workers, cron schedules |
20
+ | 6 | [06-storage.md](./06-storage.md) | `StorageType` options: Postgres, ClickHouse, Elasticsearch |
21
+ | 7 | [07-backend-file-graph.md](./07-backend-file-graph.md) | Annotated file tree for `<prefix>-back` |
22
+ | 8 | [08-frontend-file-graph.md](./08-frontend-file-graph.md) | Annotated file tree for `<prefix>-ui` |
23
+ | 9 | [09-options.md](./09-options.md) | All `BootstrapEntityOptions` flags with defaults |
24
+ | 10 | [10-modules.md](./10-modules.md) | Built-in modules, how to write your own |
25
+ | 11 | [11-permissions.md](./11-permissions.md) | Data-space vs UI-space permissions, where each is checked, how they're seeded |
26
+
27
+ ---
28
+
29
+ ## Quick orientation
30
+
31
+ ```
32
+ meta-project/
33
+ src/meta/metadata.ts ← you write this (SystemMetaBuilder DSL)
34
+ src/meta/options.json ← generation options (BootstrapEntityOptions)
35
+
36
+ runlify regen ← regenerates everything from the meta
37
+
38
+ <prefix>-back/ ← generated NestJS/Prisma/GraphQL backend
39
+ <prefix>-ui/ ← generated react-admin frontend
40
+ ```
41
+
42
+ The meta project **describes** the system. Runlify **generates** the implementation.
43
+ Business logic lives only in files Runlify creates once and never overwrites.
44
+
45
+ ---
46
+
47
+ ## Two classes of files
48
+
49
+ | Class | Owner | Overwritten on `regen`? | Marker |
50
+ |-------|-------|------------------------|--------|
51
+ | **Generated** (`[gen]`) | Runlify | **Yes — every run** | `DO NOT EDIT! THIS IS GENERATED FILE` at top |
52
+ | **Yours** (`[yours]` / `[once]`) | Developer | No — created only if missing | Most have no marker; some (e.g. `PermissionPage.tsx`, `i18n/<lang>/index.ts`) have a short `Generated once, not overwritten on next generation. Edit freely.` note instead |
53
+
54
+ Yours files: `Additional<Entity>Service.ts`, `hooks/*`, `additionalRoutes.tsx`,
55
+ `Dashboard.tsx`, `getAdditionalMenu.ts`, `restRouter.ts`, `PermissionPage.tsx`,
56
+ `NotFoundPage.tsx`, `customPermissions.ts`, `customUiPermissions.ts`, and others.
57
+ See [07-backend-file-graph.md](./07-backend-file-graph.md) and [08-frontend-file-graph.md](./08-frontend-file-graph.md) for the full list.
58
+
59
+ ---
60
+
61
+ ## Features not covered in this documentation
62
+
63
+ The following exist in Runlify but are intentionally omitted here — they are used
64
+ selectively and vary significantly per project. Refer to the Runlify source at
65
+ `src/projectsGeneration/builders/` and `src/commands/` for details.
66
+
67
+ - **Workers** — `addWorker()` on `SystemMetaBuilder`
68
+ - **Telegram Bots** — `addTelegramBot()` on `SystemMetaBuilder`
69
+ - **Integration Clients** — `addIntegrationClient()` on `SystemMetaBuilder`
70
+ - **Cloud CLI** — `login`, `pullEnvs`, `runlify start` commands
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runlify",
3
- "version": "0.0.793",
3
+ "version": "0.0.796",
4
4
  "description": "runlify CLI",
5
5
  "private": false,
6
6
  "repository": {