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,471 @@
1
+ # Entity Types
2
+
3
+ > **Load this file when:** you need to choose which entity type to use, understand
4
+ > what each type provides, or configure entity-level behaviour (deletable, auditable,
5
+ > multitenancy, storage, permissions, etc.).
6
+ >
7
+ > Related: [02-system-meta-builder.md](./02-system-meta-builder.md) ·
8
+ > [04-fields.md](./04-fields.md) · [06-storage.md](./06-storage.md)
9
+
10
+ ---
11
+
12
+ ## Choosing an entity type
13
+
14
+ | Type | Use when | Deletable by default | Auto-added fields |
15
+ |------|----------|---------------------|-------------------|
16
+ | `Catalog` | Reference / lookup data (statuses, categories, countries) | No | `id`, `search` |
17
+ | `Document` | Business transactions (orders, invoices, payments) | No | `id`, `date`, `search` |
18
+ | `InfoRegistry` | Accounting-style state snapshots (inventory levels, balances at a point in time) | No | `id`, + registrar fields if `registrarDepended=true` |
19
+ | `SumRegistry` | Accounting-style running totals (sum of amounts per dimension) | No | `id`, `date`, + registrar fields if `registrarDepended=true` |
20
+ | `Report` | Read-only report pages (no CRUD, no storage) | — | none |
21
+
22
+ All savable entity types (`Catalog`, `Document`, `InfoRegistry`, `SumRegistry`) inherit
23
+ from `BaseSavableEntityBuilder` and share the same core API described below.
24
+
25
+ ---
26
+
27
+ ## BaseSavableEntityBuilder — shared API
28
+
29
+ All methods below are available on every savable entity.
30
+
31
+ ### Default state
32
+
33
+ | Property | Default |
34
+ |----------|---------|
35
+ | `deletable` | `false` |
36
+ | `editable` | `true` |
37
+ | `auditable` | `true` |
38
+ | `creatableByUser` | `true` |
39
+ | `updatableByUser` | `true` |
40
+ | `removableByUser` | `true` |
41
+ | `exportableByUser` | `true` |
42
+ | `searchEnabled` | `true` |
43
+ | `storage` | `POSTGRES` |
44
+ | `multitenancy` | `'none'` |
45
+ | `database` | `'main'` |
46
+ | `sortField` | `'id'` |
47
+ | `sortOrder` | `'DESC'` |
48
+
49
+ ---
50
+
51
+ ### Access control {#access-control}
52
+
53
+ #### `setCreatableByUser(value?: boolean): this`
54
+
55
+ Controls whether users can create records via the UI and GraphQL mutation.
56
+ Defaults to `true`.
57
+
58
+ #### `setUpdatableByUser(value?: boolean): this`
59
+
60
+ Controls whether users can update records. Defaults to `true`.
61
+
62
+ #### `setRemovableByUser(value?: boolean): this`
63
+
64
+ Controls whether users can delete records. Defaults to `true`.
65
+
66
+ **Anti-pattern — hiding entity from UI by setting `setNotUpdatableByUser` on every field:**
67
+
68
+ ```ts
69
+ // WRONG: makes fields optional in generated types, conflicts with setRequired()
70
+ entity.addField('name').setRequired().setNotUpdatableByUser()
71
+ entity.addField('status').setRequired().setNotUpdatableByUser()
72
+ // ... repeat for every field
73
+
74
+ // CORRECT: disable at the entity level
75
+ entity
76
+ .setCreatableByUser(false)
77
+ .setUpdatableByUser(false)
78
+ .setRemovableByUser(false)
79
+ ```
80
+
81
+ `setNotUpdatableByUser()` on a field marks that field as **not required in update input
82
+ types**. When combined with `setRequired()`, the field is required in the DB but not in
83
+ the update GraphQL input — which is the correct semantic for immutable fields. Using it
84
+ globally to hide an entity from the UI is the wrong tool.
85
+
86
+ #### `setExportableByUser(value?: boolean): this`
87
+
88
+ Controls whether users can export records (e.g. to CSV). Defaults to `true`.
89
+
90
+ See [11-permissions.md](./11-permissions.md) for how access control actually works —
91
+ both the backend-enforced `<entity>.<action>` permissions and the frontend-only
92
+ `ui.<entity>.list` permission.
93
+
94
+ ---
95
+
96
+ ### Deletion and editing {#deletion-and-editing}
97
+
98
+ #### `setDeletable(value?: boolean): this`
99
+
100
+ Allows physical deletion of records. Default `false` — most entities use soft-delete or
101
+ are not deletable at all.
102
+
103
+ #### `setEditable(value?: boolean): this`
104
+
105
+ Controls whether the edit UI form is shown. Default `true`.
106
+
107
+ ---
108
+
109
+ ### Auditing {#auditing}
110
+
111
+ #### `setAuditable(value?: boolean): this`
112
+
113
+ Enables audit logging (recording who created/updated/deleted records). Default `true`.
114
+
115
+ ---
116
+
117
+ ### Caching {#caching}
118
+
119
+ #### `setCacheable(value?: boolean): this`
120
+
121
+ Enables in-memory caching for this entity. Default `false`. Use for small, rarely
122
+ changing reference data (e.g. currencies, countries).
123
+
124
+ ---
125
+
126
+ ### Storage {#storage}
127
+
128
+ #### `setStorage(storage: StorageType): this`
129
+
130
+ Sets the storage engine. See [06-storage.md](./06-storage.md) for all options.
131
+
132
+ When set to an external-search storage, automatically:
133
+ - Forces `id` type to `string` with `cuid()` auto-generation
134
+ - Creates an `externalSearchTrackings` helper entity
135
+
136
+ #### `setDatabase(name: string): this`
137
+
138
+ Assigns this entity to a named database. Default `'main'`.
139
+ The database must be registered first via `system.addDatabase(name)`.
140
+ See [02-system-meta-builder.md#addDatabase](./02-system-meta-builder.md#addDatabase).
141
+
142
+ ---
143
+
144
+ ### Search {#search}
145
+
146
+ #### `setSearchEnabled(value?: boolean): this`
147
+
148
+ Enables/disables full-text search for this entity. Default `true` for Catalog/Document,
149
+ `false` for InfoRegistry.
150
+
151
+ ---
152
+
153
+ ### Sorting {#sorting}
154
+
155
+ #### `setSort(field: string, order?: 'ASC' | 'DESC'): this`
156
+
157
+ Sets the default sort order for list queries. The field must exist on the entity.
158
+ Default: `'id'`, `'DESC'`.
159
+
160
+ ---
161
+
162
+ ### Unique constraints & indexes {#constraints-and-indexes}
163
+
164
+ #### `addUniqueConstraint(fields: string[]): this`
165
+
166
+ Adds a multi-column unique constraint. All field names must exist on the entity.
167
+
168
+ #### `addIndex({ fields, type }: Index): this`
169
+
170
+ Adds a database index.
171
+
172
+ | `type` | `IndexType` value | Notes |
173
+ |--------|------------------|-------|
174
+ | BTree | `IndexType.BTree` | Default; supports multi-column |
175
+ | Hash | `IndexType.Hash` | Single-column only |
176
+ | GIN | `IndexType.Gin` | For full-text / array search |
177
+
178
+ **Anti-pattern — multi-column Hash index:**
179
+
180
+ ```ts
181
+ // WRONG: throws at build() time
182
+ entity.addIndex({ fields: ['statusId', 'date'], type: IndexType.Hash })
183
+
184
+ // CORRECT: use BTree for multi-column
185
+ entity.addIndex({ fields: ['statusId', 'date'], type: IndexType.BTree })
186
+ ```
187
+
188
+ ---
189
+
190
+ ### Multitenancy {#multitenancy}
191
+
192
+ #### `setMultitenancy(multitenancy, commonElementsVisibleToAll?): this`
193
+
194
+ | Value | Behaviour |
195
+ |-------|-----------|
196
+ | `'none'` | No tenant isolation (default) |
197
+ | `'optional'` | Records may belong to a tenant or be global. Pass `commonElementsVisibleToAll` to control whether global records are visible to all tenants |
198
+ | `'required'` | Every record must belong to a tenant. `tenantId` is auto-populated from the current user's profile |
199
+
200
+ Automatically adds a `tenantId` link field to `tenants` entity when `!== 'none'`.
201
+
202
+ **Anti-pattern — `commonElementsVisibleToAll` with non-optional multitenancy:**
203
+
204
+ ```ts
205
+ // WRONG: throws at build() time
206
+ entity.setMultitenancy('required', true)
207
+
208
+ // CORRECT: commonElementsVisibleToAll is only valid for 'optional'
209
+ entity.setMultitenancy('optional', true)
210
+ ```
211
+
212
+ ---
213
+
214
+ ### Predefined data {#predefined-data}
215
+
216
+ ```ts
217
+ addPredefinedElements(rows: Record<string, any>[]) // seeds data in all environments
218
+ setPredefinedElements(rows: Record<string, any>[]) // replaces (not appends) seed data
219
+ addDevPredefinedElements(rows: Record<string, any>[]) // seeds data in dev environment only
220
+ setDevPredefinedElements(rows: Record<string, any>[])
221
+ ```
222
+
223
+ ---
224
+
225
+ ### Data cleanup {#data-cleanup}
226
+
227
+ #### `setClearDBAfter(count: number, unit: DateUnit): this`
228
+
229
+ Automatically deletes records older than `count` `unit`s.
230
+
231
+ `DateUnit` values: `'year' | 'month' | 'day' | 'hour' | 'minute'`
232
+
233
+ ---
234
+
235
+ ### UI / menu {#ui-menu}
236
+
237
+ #### `setExcludeFromCommonMenu(exclude?: boolean): this`
238
+
239
+ Prevents this entity from appearing in the auto-generated sidebar menu. Default `false`.
240
+
241
+ #### `getForms(): FormsBuilder`
242
+
243
+ Returns the UI forms builder for customising list/show/create/edit form fields and
244
+ their display order. See [08-frontend-file-graph.md](./08-frontend-file-graph.md).
245
+
246
+ ---
247
+
248
+ ### Key field {#key-field}
249
+
250
+ #### `getKey(): IdFieldBuilder`
251
+
252
+ Returns the `id` field builder. Use to change the key type:
253
+
254
+ ```ts
255
+ entity.getKey().setType('string') // cuid() auto-generated string id
256
+ entity.getKey().setType('int') // autoincrement integer
257
+ entity.getKey().setType('bigint') // autoincrement bigint
258
+ ```
259
+
260
+ Default type is `int` (autoincrement).
261
+
262
+ ---
263
+
264
+ ### Title field {#title-field}
265
+
266
+ #### `setTitleFieldByName(fieldName: string): this`
267
+
268
+ Sets which field is shown as the record's display name in dropdowns and relation fields.
269
+ Default is `id`. Usually set to a `name` or `title` scalar field.
270
+
271
+ ---
272
+
273
+ ### Fields {#fields}
274
+
275
+ All field methods are described in [04-fields.md](./04-fields.md).
276
+
277
+ ```ts
278
+ addField(name, title?, options?) // ScalarFieldBuilder
279
+ addLinkField(entity, name, title?) // LinkFieldBuilder — name MUST end in 'Id'
280
+ addFileField(name, title?) // LinkFieldBuilder to 'files' (plain file)
281
+ addImageField(name, title?) // LinkFieldBuilder to 'files' (image)
282
+ addViewLinkField(entity, name, title?) // ViewLinkFieldBuilder (read-only join)
283
+ delField(name) // removes a field
284
+ getFiled(name) // returns a field builder or throws
285
+ getFieldIfExist(name) // returns a field builder or undefined
286
+ getLinkFileds() // returns all LinkFieldBuilders
287
+ ```
288
+
289
+ ---
290
+
291
+ ### Custom methods & models on an entity {#entity-methods}
292
+
293
+ Entities can have their own custom GraphQL operations in addition to the standard CRUD.
294
+
295
+ ```ts
296
+ entity.addMethod(name, methodType, title?) // MethodType.Query | Mutation
297
+ entity.createGeneralModel(name, title?) // model for input or output
298
+ entity.createInputModel(name, title?) // input-only model
299
+ entity.createOutputModel(name, title?) // output-only model
300
+ ```
301
+
302
+ See [05-custom-methods.md](./05-custom-methods.md).
303
+
304
+ ---
305
+
306
+ ## Catalog {#catalog}
307
+
308
+ Created via `system.addCatalog(name, title?, options?)`.
309
+
310
+ **Additional auto-added fields:** `search` (hidden, string, not updatable by user).
311
+
312
+ **Additional properties:**
313
+
314
+ | Property | Default | Setter |
315
+ |----------|---------|--------|
316
+ | `deletable` | `false` | `setDeletable()` |
317
+ | `editable` | `true` | `setEditable()` |
318
+
319
+ Use for: statuses, categories, countries, currencies, units of measure, any lookup table.
320
+
321
+ ---
322
+
323
+ ## Document {#document}
324
+
325
+ Created via `system.addDocument(name, title?, options?)`.
326
+
327
+ **Additional auto-added fields:** `date` (datetime, required, defaults to `new Date()`)
328
+ and `search` (hidden string).
329
+
330
+ **Additional methods:**
331
+
332
+ #### `addRegistry(registryName: string)`
333
+
334
+ Links this document to an InfoRegistry or SumRegistry that it drives.
335
+ `registryName` is the name of the registry entity.
336
+
337
+ #### `setSharded(value?: boolean): this`
338
+
339
+ When `true`, additionally adds hidden fields `repostRequired` (bool, default `true`) and
340
+ `deleteRequired` (bool, default `false`) used for sharded accounting workflows.
341
+
342
+ Use for: orders, invoices, payments, events, anything that represents a business
343
+ transaction at a point in time.
344
+
345
+ ---
346
+
347
+ ## InfoRegistry {#inforegistry}
348
+
349
+ Created via `system.addInfoRegistry(name, registrarDepended, title?, options?)`.
350
+
351
+ An information register stores **state snapshots** — the value of something at a
352
+ specific moment. Think of it as a time-series table of dimension→resource pairs.
353
+
354
+ **Search disabled by default.**
355
+
356
+ **When `registrarDepended = true`**, auto-adds:
357
+ - `registrarTypeId` (string link to `entities`)
358
+ - `registrarId` (int or string, configurable via `RegistryOptions.registrarIdType`)
359
+ - `row` (int, default `1`)
360
+ - Unique constraint on `[registrarTypeId, registrarId, row]`
361
+
362
+ **Dimension vs Resource:**
363
+
364
+ | Role | Meaning | Methods |
365
+ |------|---------|---------|
366
+ | **Dimension** | What you're measuring (product, warehouse, account) | `addDimension`, `addDimensionLinkField`, `addDimensionViewLinkField` |
367
+ | **Resource** | The value being stored (quantity, amount, flag) | `addResource`, `addResourceLinkField`, `addResourceViewLinkField` |
368
+
369
+ Auto-generated unique constraint: all dimension field names combined.
370
+
371
+ #### `setPeriod(period: InfoRegistryPeriod): this`
372
+
373
+ | Value | Auto-added field |
374
+ |-------|-----------------|
375
+ | `'notPeriodic'` | none |
376
+ | `'second'` | `date` (datetime) |
377
+ | `'day'` | `date` (date) |
378
+ | `'month'` | `date` (date) |
379
+ | `'year'` | `date` (date) |
380
+
381
+ #### `delDimension(name) / delResource(name)`
382
+
383
+ Removes a dimension or resource field.
384
+
385
+ ---
386
+
387
+ ## SumRegistry {#sumregistry}
388
+
389
+ Created via `system.addSumRegistry(name, registrarDepended, title?, options?)`.
390
+
391
+ A totals register stores **running aggregated sums** across dimensions. Think of it as
392
+ a materialized aggregate table.
393
+
394
+ `RegistryOptions`:
395
+
396
+ | Field | Type | Default | Description |
397
+ |-------|------|---------|-------------|
398
+ | `registrarIdType` | `'int' \| 'string'` | `'int'` | Type of the `registrarId` field |
399
+ | `sharded` | `boolean` | `undefined` | Use `ViewLinkField` instead of `LinkField` for `registrarTypeId` |
400
+
401
+ **When `registrarDepended = true`**, auto-adds:
402
+ - `date` (datetime, required)
403
+ - `registrarTypeId` (string link/view-link to `entities`)
404
+ - `registrarId` (int or string)
405
+ - `row` (int, default `1`)
406
+ - Unique constraint on `[registrarTypeId, registrarId, row]`
407
+ - **Also sets `creatableByUser(false)`, `updatableByUser(false)`, `removableByUser(false)`**
408
+
409
+ Same dimension/resource API as `InfoRegistry`.
410
+
411
+ ---
412
+
413
+ ## Report {#report}
414
+
415
+ Created via `system.addReport(name, title?, options?)`.
416
+
417
+ A report is a **read-only UI page** with no storage, no CRUD GraphQL, and no Prisma
418
+ model. It generates a page and a `read` permission automatically — see
419
+ [11-permissions.md](./11-permissions.md) for how permissions are structured and enforced.
420
+
421
+ Reports do not inherit from `BaseSavableEntityBuilder` — they have no fields, no
422
+ methods, and no storage configuration.
423
+
424
+ Use for: dashboards, custom analytics pages, external data views.
425
+
426
+ ---
427
+
428
+ ## Anti-patterns
429
+
430
+ ### Using `Document` for reference data
431
+
432
+ **Wrong:** creating a `Document` to store countries or categories.
433
+
434
+ **Why:** Documents auto-add a `date` field and are designed for transactions. Reference
435
+ data has no meaningful transaction date.
436
+
437
+ **Correct:** use `Catalog` for lookup / reference data.
438
+
439
+ ---
440
+
441
+ ### Using `Catalog` for transactional data
442
+
443
+ **Wrong:** creating a `Catalog` to store orders.
444
+
445
+ **Why:** Catalogs have no `date` field, no registry linking, and no sharding support
446
+ designed for transactions.
447
+
448
+ **Correct:** use `Document` for transactional entities.
449
+
450
+ ---
451
+
452
+ ### Forgetting `system.addDatabase()` before `setDatabase()`
453
+
454
+ **Wrong:**
455
+ ```ts
456
+ const orders = system.addDocument('orders')
457
+ orders.setDatabase('archive') // throws at build(): 'archive' is not registered
458
+ ```
459
+
460
+ **Correct:**
461
+ ```ts
462
+ system.addDatabase('archive')
463
+ const orders = system.addDocument('orders')
464
+ orders.setDatabase('archive')
465
+ ```
466
+
467
+ ---
468
+
469
+ > **Examples:** this file intentionally omits code examples. Add real usage examples
470
+ > from the consuming project here. A reference metadata file is typically at
471
+ > `src/meta/metadata.ts` in the meta project.