runlify 0.0.794 → 0.0.797

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 (58) 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/layout/Menu.js +1 -1
  17. package/build/projectsGeneration/generators/fileTemplates/ui/environment/src/routes.js +5 -5
  18. package/build/projectsGeneration/generators/fileTemplates/ui/getDefaultMenu.js +5 -1
  19. package/build/projectsGeneration/generators/fileTemplates/ui/i18n/skeleton/uiI18nIndexTmpl.js +35 -0
  20. package/build/projectsGeneration/generators/fileTemplates/ui/i18n/skeleton/uiI18nTypesTmpl.js +33 -0
  21. package/build/projectsGeneration/generators/fileTemplates/ui/i18n/skeleton/uiI18nValidationTmpl.js +38 -0
  22. package/build/projectsGeneration/generators/fileTemplates/ui/pages/EntityCreate/DefaultEntityCreate.js +16 -5
  23. package/build/projectsGeneration/generators/fileTemplates/ui/pages/EntityEdit/DefaultEntityEdit.js +27 -11
  24. package/build/projectsGeneration/generators/fileTemplates/ui/pages/EntityList/DefaultEntityFilter.js +13 -4
  25. package/build/projectsGeneration/generators/fileTemplates/ui/pages/EntityList/DefaultEntityList.js +14 -7
  26. package/build/projectsGeneration/generators/fileTemplates/ui/pages/EntityShow/DefaultMainTab.js +13 -4
  27. package/build/projectsGeneration/generators/fileTemplates/ui/pages/EntityShow/DependencyTab.js +12 -3
  28. package/build/projectsGeneration/generators/fileTemplates/ui/resources.js +21 -8
  29. package/build/projectsGeneration/generators/fileTemplates/ui/utils/permissions.js +89 -0
  30. package/build/projectsGeneration/generators/ui/getShowComponent.js +2 -2
  31. package/build/types/projectsGeneration/builders/generationPaths.d.ts +42 -0
  32. package/build/types/projectsGeneration/generateProject/fileHandlers.d.ts +1 -0
  33. package/build/types/projectsGeneration/generateProject/types.d.ts +1 -1
  34. package/build/types/projectsGeneration/generateProject/utils.d.ts +1 -1
  35. package/build/types/projectsGeneration/generators/fileTemplates/back/roles/customPermissions.d.ts +2 -0
  36. package/build/types/projectsGeneration/generators/fileTemplates/back/roles/customUiPermissions.d.ts +2 -0
  37. package/build/types/projectsGeneration/generators/fileTemplates/back/roles/initPermissions.d.ts +2 -0
  38. package/build/types/projectsGeneration/generators/fileTemplates/back/roles/uiPermissions.d.ts +3 -0
  39. package/build/types/projectsGeneration/generators/fileTemplates/ui/NotFoundPage.d.ts +2 -0
  40. package/build/types/projectsGeneration/generators/fileTemplates/ui/PermissionPage.d.ts +2 -0
  41. package/build/types/projectsGeneration/generators/fileTemplates/ui/i18n/skeleton/uiI18nIndexTmpl.d.ts +2 -0
  42. package/build/types/projectsGeneration/generators/fileTemplates/ui/i18n/skeleton/uiI18nTypesTmpl.d.ts +3 -0
  43. package/build/types/projectsGeneration/generators/fileTemplates/ui/i18n/skeleton/uiI18nValidationTmpl.d.ts +2 -0
  44. package/build/types/projectsGeneration/generators/fileTemplates/ui/pages/EntityEdit/DefaultEntityEdit.d.ts +1 -0
  45. package/build/types/projectsGeneration/generators/fileTemplates/ui/utils/permissions.d.ts +2 -0
  46. package/docs/01-overview.md +172 -0
  47. package/docs/02-system-meta-builder.md +405 -0
  48. package/docs/03-entity-types.md +471 -0
  49. package/docs/04-fields.md +357 -0
  50. package/docs/05-custom-methods.md +249 -0
  51. package/docs/06-storage.md +129 -0
  52. package/docs/07-backend-file-graph.md +231 -0
  53. package/docs/08-frontend-file-graph.md +260 -0
  54. package/docs/09-options.md +213 -0
  55. package/docs/10-modules.md +164 -0
  56. package/docs/11-permissions.md +231 -0
  57. package/docs/INDEX.md +70 -0
  58. package/package.json +1 -1
@@ -0,0 +1,357 @@
1
+ # Fields
2
+
3
+ > **Load this file when:** you need to add or configure fields on an entity — scalar
4
+ > fields, link fields, file/image fields, view-link fields, or the id field. Also covers
5
+ > filters, display visibility, required/optional semantics, and indexes.
6
+ >
7
+ > Related: [03-entity-types.md](./03-entity-types.md) ·
8
+ > [02-system-meta-builder.md](./02-system-meta-builder.md)
9
+
10
+ ---
11
+
12
+ ## Field types overview
13
+
14
+ | Builder class | Created by | Used for |
15
+ |--------------|-----------|---------|
16
+ | `ScalarFieldBuilder` | `entity.addField()` | Primitive values: string, int, float, bool, datetime, date |
17
+ | `IdFieldBuilder` | auto (always `id`) | Primary key of an entity |
18
+ | `LinkFieldBuilder` | `entity.addLinkField()`, `addFileField()`, `addImageField()` | FK reference to another entity |
19
+ | `ViewLinkFieldBuilder` | `entity.addViewLinkField()` | Read-only join, not a real FK column |
20
+
21
+ ---
22
+
23
+ ## Scalar types (`FieldType`)
24
+
25
+ | Type | TypeScript | Prisma | Notes |
26
+ |------|-----------|--------|-------|
27
+ | `'string'` | `string` | `String` | Default. Use `setStringType()` to specialise |
28
+ | `'int'` | `number` | `Int` | |
29
+ | `'bigint'` | `bigint` | `BigInt` | |
30
+ | `'float'` | `number` | `Float` | |
31
+ | `'bool'` | `boolean` | `Boolean` | `searchable` set to `false` automatically |
32
+ | `'datetime'` | `Date` | `DateTime` | |
33
+ | `'date'` | `Date` | `DateTime` | Date only (no time component in UI) |
34
+
35
+ Default type for new fields created via `addField()` is `'int'`. Always call
36
+ `setType()` explicitly.
37
+
38
+ ---
39
+
40
+ ## String sub-types (`StringType`)
41
+
42
+ Set via `field.setStringType(StringType.X)`. Only valid when `type === 'string'`.
43
+
44
+ | Value | Effect | Notes |
45
+ |-------|--------|-------|
46
+ | `StringType.Plain` | Single-line text input | Default |
47
+ | `StringType.Number` | Number-formatted string | Displayed as number but stored as string |
48
+ | `StringType.Multiline` | Multi-line textarea | |
49
+ | `StringType.RichEdit` | WYSIWYG rich text editor | |
50
+ | `StringType.Markdown` | Markdown editor | Auto-sets `showInList=false`, `showInFilter=false` |
51
+ | `StringType.Json` | JSON editor | Auto-sets `showInList=false`, `showInFilter=false` |
52
+
53
+ ---
54
+
55
+ ## Number sub-types (`NumberType`)
56
+
57
+ Set via `field.setNumberType(NumberType.X)`. Only valid for `int`, `bigint`, `float`.
58
+
59
+ | Value | Effect |
60
+ |-------|--------|
61
+ | `NumberType.Base` | Plain number (default) |
62
+ | `NumberType.Money` | Formatted as monetary value in UI |
63
+
64
+ ---
65
+
66
+ ## BaseFieldBuilder — all methods
67
+
68
+ Available on every field type.
69
+
70
+ ### Type & subtype
71
+
72
+ #### `setType(type: FieldType): this` {#setType}
73
+
74
+ Sets the scalar type. Validates that existing filters are compatible with the new type.
75
+ When set to `'bool'`, automatically sets `searchable = false`.
76
+
77
+ #### `setStringType(stringType: StringType): this` {#setStringType}
78
+
79
+ Only for `type === 'string'`. Throws otherwise.
80
+
81
+ #### `setNumberType(numberType: NumberType): this` {#setNumberType}
82
+
83
+ Only for `type === 'int' | 'bigint' | 'float'`. Throws otherwise.
84
+
85
+ ---
86
+
87
+ ### Required / optional semantics {#required}
88
+
89
+ #### `setRequired(value?: boolean): this`
90
+
91
+ Marks the field as required in the DB (`NOT NULL`) and in GraphQL input types.
92
+ Also sets `requiredOnInput = true` if it has not been set explicitly before.
93
+
94
+ #### `setNotRequired(): this`
95
+
96
+ Marks the field as optional (`NULL` allowed). Equivalent to `setRequired(false)`.
97
+
98
+ #### `setRequiredOnInput(value: boolean, defaultValueExpression?: string): this`
99
+
100
+ Controls whether the field is required in GraphQL **input types** independently of
101
+ whether it is required in the DB. Use when the field is required in the DB but has a
102
+ server-side default, so the client doesn't need to send it.
103
+
104
+ ---
105
+
106
+ ### Updatability {#updatability}
107
+
108
+ #### `setNotUpdatableByUser(defaultValueExpression?, defaultBackendValueExpression?): this` {#setNotUpdatableByUser}
109
+
110
+ Marks the field as **not updatable via user-facing GraphQL mutations**.
111
+ Internally calls `setRequiredOnInput(false, defaultValueExpression)`.
112
+
113
+ | Parameter | Description |
114
+ |-----------|-------------|
115
+ | `defaultValueExpression` | Frontend expression used as default value in create forms |
116
+ | `defaultBackendValueExpression` | Server-side expression evaluated on create/update |
117
+
118
+ **Anti-pattern — using `setNotUpdatableByUser` to make a field immutable when it is
119
+ also `setRequired()`:**
120
+
121
+ ```ts
122
+ // WRONG: setNotUpdatableByUser sets requiredOnInput=false
123
+ // This means the field is optional in update input even though it is required in DB.
124
+ // For a create-only field that the server fills, this is CORRECT.
125
+ // For a field that the user MUST provide on create, use setRequiredOnInput explicitly:
126
+ field
127
+ .setRequired() // required in DB
128
+ .setNotUpdatableByUser() // NOT in update mutations
129
+ .setRequiredOnInput(true) // required in CREATE mutation
130
+ ```
131
+
132
+ **Anti-pattern — using `setNotUpdatableByUser` on every field to hide entity from UI:**
133
+
134
+ ```ts
135
+ // WRONG: intended to make entity read-only, but breaks required/type semantics
136
+ entity.addField('name').setRequired().setNotUpdatableByUser()
137
+ entity.addField('code').setRequired().setNotUpdatableByUser()
138
+
139
+ // CORRECT: hide at entity level
140
+ entity.setCreatableByUser(false).setUpdatableByUser(false).setRemovableByUser(false)
141
+ ```
142
+
143
+ See [03-entity-types.md#access-control](./03-entity-types.md#access-control).
144
+
145
+ #### `setUpdatableByUser(): this`
146
+
147
+ Reverses `setNotUpdatableByUser`. Marks field as editable by user again.
148
+
149
+ #### `setUpdatable(value?: boolean): this`
150
+
151
+ Controls whether the field appears in backend update operations at all (not just user-
152
+ facing). Lower-level than `setUpdatableByUser`.
153
+
154
+ ---
155
+
156
+ ### Visibility {#visibility}
157
+
158
+ All default to `true` except where noted.
159
+
160
+ | Method | Controls |
161
+ |--------|---------|
162
+ | `setShowInList(value?)` | Shown in entity list table |
163
+ | `setShowInFilter(value?)` | Shown in list filter panel |
164
+ | `setShowInCreate(value?)` | Shown in create form |
165
+ | `setShowInEdit(value?)` | Shown in edit form |
166
+ | `setShowInShow(value?)` | Shown in record detail view |
167
+ | `setHidden(value?)` | Hidden everywhere (UI + generated TS types) |
168
+ | `setSearchable(value?)` | Included in full-text search indexing |
169
+
170
+ `Markdown` and `Json` string types auto-set `showInList=false` and `showInFilter=false`.
171
+
172
+ ---
173
+
174
+ ### Default values {#defaults}
175
+
176
+ #### `setDefaultDbValue(value: string | boolean | undefined): this`
177
+
178
+ Sets the Prisma `@default(...)` value in the schema. This is a DB-level default.
179
+
180
+ #### `setDefaultValueExpression(value: string): this`
181
+
182
+ Sets the expression used on the **frontend** (create forms) and as the backend default
183
+ expression. Cannot be `'null'` or `'undefined'` — throws.
184
+
185
+ Also automatically calls `setDefaultBackendValueExpression(value)`.
186
+
187
+ #### `setDefaultBackendValueExpression(value: string): this`
188
+
189
+ Sets only the backend expression (TypeScript code evaluated during create/update).
190
+ Cannot be `'null'` or `'undefined'` — throws.
191
+
192
+ ---
193
+
194
+ ### Filters {#filters}
195
+
196
+ Default filter for all fields: `['equal']`.
197
+
198
+ #### `setFilters(filters: Filter[]): this`
199
+
200
+ Replaces the filter list entirely. Validates compatibility with the field type.
201
+
202
+ #### `addFilter(filter: Filter): this` / `addFilters(filters: Filter[]): this`
203
+
204
+ Adds one or more filters.
205
+
206
+ #### `delFilter(filter: Filter): this`
207
+
208
+ Removes a filter.
209
+
210
+ **Allowed filters per type:**
211
+
212
+ | Type | Allowed filters |
213
+ |------|----------------|
214
+ | `string` | `equal`, `defined`, `not_defined`, `in`, `not_in` |
215
+ | `int`, `bigint`, `float` | `equal`, `defined`, `not_defined`, `in`, `not_in`, `lte`, `gte`, `lt`, `gt` |
216
+ | `date`, `datetime` | `equal`, `defined`, `not_defined`, `lte`, `gte`, `lt`, `gt` |
217
+ | `bool` | `equal`, `defined`, `not_defined` |
218
+
219
+ **Anti-pattern — `in` / `not_in` on a scalar field:**
220
+
221
+ ```ts
222
+ // WRONG: throws at build() time — 'in'/'not_in' only allowed on LinkFields
223
+ entity.addField('statusCode').setType('string').addFilter('in')
224
+
225
+ // CORRECT: 'in'/'not_in' is only valid on link fields
226
+ entity.addLinkField('statuses', 'statusId').addFilter('in')
227
+ ```
228
+
229
+ ---
230
+
231
+ ### Other
232
+
233
+ #### `setTitle(title: string, language?: string): this`
234
+
235
+ Sets human-readable title for a language. Defaults to the system's default language.
236
+
237
+ #### `setTitles(titles: Record<string, string>): this`
238
+
239
+ Sets titles for multiple languages at once: `{ en: 'Name', ru: 'Имя' }`.
240
+
241
+ #### `setNeedFor(text: string): this`
242
+
243
+ Documents what this field is used for. Included in generated docs.
244
+
245
+ #### `setSharded(value?: boolean): this`
246
+
247
+ Marks this field as a sharding key. The field becomes part of the composite unique
248
+ constraint that groups records in a shard.
249
+
250
+ #### `setArray(value?: boolean): this`
251
+
252
+ Marks the field as an array type in GraphQL and TypeScript output types.
253
+
254
+ #### `setMeaning(meaning: 'img'): this`
255
+
256
+ Marks the field with a semantic meaning. Currently only `'img'` is supported — hints
257
+ the UI to render this string field as an image URL.
258
+
259
+ ---
260
+
261
+ ## IdFieldBuilder {#id-field}
262
+
263
+ Accessed via `entity.getKey()`. Auto-created as `id`.
264
+
265
+ #### `setType(type: TKeyFieldType): this`
266
+
267
+ | Value | Prisma | Auto-generation |
268
+ |-------|--------|----------------|
269
+ | `'int'` | `Int` | `@default(autoincrement())` — default |
270
+ | `'bigint'` | `BigInt` | `@default(autoincrement())` |
271
+ | `'string'` | `String` | `@default(cuid())` |
272
+
273
+ ---
274
+
275
+ ## LinkFieldBuilder {#link-field}
276
+
277
+ Created via `entity.addLinkField(entityNameOrBuilder, name, title?)`.
278
+
279
+ **Name MUST end in `Id`** — validated on construction. Throws immediately if violated.
280
+
281
+ ```ts
282
+ entity.addLinkField('statuses', 'statusId') // correct
283
+ entity.addLinkField('statuses', 'status') // throws: must end in 'Id'
284
+ ```
285
+
286
+ #### `setType(type: TKeyFieldType): this`
287
+
288
+ Sets the FK column type: `'int'` (default), `'bigint'`, or `'string'`.
289
+ When passing a `CatalogBuilder` instance to `addLinkField`, the type is inferred
290
+ automatically from the target entity's key type.
291
+
292
+ #### `setPredefinedLinkedEntity(value: 'none' | 'file'): this`
293
+
294
+ `'file'` — marks this field as linking to the built-in `files` catalog with special
295
+ file-upload UI treatment. Set automatically by `addFileField()` and `addImageField()`.
296
+
297
+ #### `setFileType(type: 'plain' | 'image'): this`
298
+
299
+ Only valid when `predefinedLinkedEntity === 'file'`. Throws otherwise.
300
+ Set automatically by `addImageField()` to `'image'`.
301
+
302
+ **Anti-pattern — using `addLinkField` for file/image fields:**
303
+
304
+ ```ts
305
+ // WRONG: works but misses file-upload UI and correct type semantics
306
+ entity.addLinkField('files', 'avatarId')
307
+
308
+ // CORRECT: use the dedicated helpers
309
+ entity.addFileField('documentId') // file upload UI
310
+ entity.addImageField('avatarId') // image upload UI with preview
311
+ ```
312
+
313
+ ---
314
+
315
+ ## ViewLinkFieldBuilder {#view-link-field}
316
+
317
+ Created via `entity.addViewLinkField(entityNameOrBuilder, name, title?)`.
318
+
319
+ A **read-only join** — does not create a real FK column in the database. Used to display
320
+ data from a related entity without storing a FK. The name must still end in `Id`.
321
+
322
+ Use for: displaying computed or external references that should not be part of the
323
+ entity's own schema.
324
+
325
+ ---
326
+
327
+ ## Convenience helpers on entity
328
+
329
+ ```ts
330
+ entity.addFileField(name, title?) // LinkField to 'files', predefined='file', fileType='plain'
331
+ entity.addImageField(name, title?) // LinkField to 'files', predefined='file', fileType='image'
332
+ ```
333
+
334
+ ---
335
+
336
+ ## Field name rules
337
+
338
+ Field names must match `/^[a-zA-Z0-9]+$/`. No underscores, hyphens, or special chars.
339
+ Validated on construction — throws immediately.
340
+
341
+ **Anti-pattern — underscores or special chars in field names:**
342
+
343
+ ```ts
344
+ // WRONG: throws immediately
345
+ entity.addField('first_name')
346
+ entity.addField('first-name')
347
+ entity.addField('firstName!')
348
+
349
+ // CORRECT
350
+ entity.addField('firstName')
351
+ ```
352
+
353
+ ---
354
+
355
+ > **Examples:** this file intentionally omits code examples. Add real usage examples
356
+ > from the consuming project here. A reference metadata file is typically at
357
+ > `src/meta/metadata.ts` in the meta project.
@@ -0,0 +1,249 @@
1
+ # Custom Methods
2
+
3
+ > **Load this file when:** you need to add custom GraphQL operations (Query or Mutation)
4
+ > beyond the standard CRUD — either at the system level or on a specific entity.
5
+ > Also covers input/output models, args, return types, workers, and cron schedules.
6
+ >
7
+ > Related: [02-system-meta-builder.md#addMethod](./02-system-meta-builder.md#addMethod) ·
8
+ > [03-entity-types.md#entity-methods](./03-entity-types.md#entity-methods)
9
+
10
+ ---
11
+
12
+ ## Where to add methods
13
+
14
+ Methods can live on:
15
+
16
+ | Scope | How to create | Generated location |
17
+ |-------|--------------|-------------------|
18
+ | **System-level** | `system.addMethod(name, type)` | Top-level GraphQL schema + `AdditionalService` |
19
+ | **Entity-level** | `entity.addMethod(name, type)` | Inside entity's `<Entity>Service` |
20
+
21
+ Standard CRUD methods (`all`, `create`, `update`, `delete`) are auto-generated on every
22
+ savable entity. Do not add them manually.
23
+
24
+ ---
25
+
26
+ ## `addMethod(name, methodType, title?): MethodBuilder` {#addMethod}
27
+
28
+ | Parameter | Type | Values |
29
+ |-----------|------|--------|
30
+ | `name` | `string` | Method name, `camelCase`, unique within the owner |
31
+ | `methodType` | `MethodType` | `MethodType.Query` — read operation; `MethodType.Mutation` — write operation |
32
+ | `title` | `string` | Optional human-readable title |
33
+
34
+ Returns a `MethodBuilder`.
35
+
36
+ ---
37
+
38
+ ## MethodBuilder API
39
+
40
+ ### `setExportedToApi(value?: boolean): this` {#setExportedToApi}
41
+
42
+ Exposes this method in the generated GraphQL schema. Default `false` for custom methods
43
+ (standard CRUD methods are exported automatically via their own mechanism).
44
+
45
+ Call `.setExportedToApi()` on any custom method that should be callable from the
46
+ frontend.
47
+
48
+ **Anti-pattern — forgetting `setExportedToApi`:**
49
+
50
+ ```ts
51
+ // WRONG: method is generated in the service but NOT in the GraphQL schema
52
+ entity.addMethod('recalculate', MethodType.Mutation)
53
+
54
+ // CORRECT
55
+ entity.addMethod('recalculate', MethodType.Mutation).setExportedToApi()
56
+ ```
57
+
58
+ ---
59
+
60
+ ### `setMethodType(type: MethodType): this`
61
+
62
+ Changes the method type after creation.
63
+
64
+ ---
65
+
66
+ ### `setAsync(value?: boolean): this`
67
+
68
+ Marks the method as `async`. Affects the generated service method signature.
69
+
70
+ ---
71
+
72
+ ### `setWorker(workerName: string): this` / `resetWorker(): this`
73
+
74
+ Associates this method with a named worker. The worker must be registered via
75
+ `system.addWorker(name)`. When a worker is set, the method runs in the worker process
76
+ rather than the main backend.
77
+
78
+ ---
79
+
80
+ ### `addRunSchedule(cronExpression: string): this` / `resetRunSchedule(): this`
81
+
82
+ Adds a cron expression to automatically trigger this method on a schedule.
83
+ Multiple schedules can be added. Duplicate cron expressions throw.
84
+
85
+ ```ts
86
+ method.addRunSchedule('0 * * * *') // every hour
87
+ method.addRunSchedule('0 0 * * *') // every day at midnight
88
+ ```
89
+
90
+ ---
91
+
92
+ ### `getArgsModel(): ArgsModelBuilder` {#argsModel}
93
+
94
+ Returns the **args model** — the input type for this method. Add fields to it to define
95
+ what the caller must pass.
96
+
97
+ `ArgsModelBuilder` extends `BaseModelBuilder` — it has the same `addField()` and
98
+ `addModelField()` API (see [Models section](#models) below).
99
+
100
+ ---
101
+
102
+ ### Return type
103
+
104
+ A method's return type is one of three variants. Set exactly one.
105
+
106
+ #### `setReturnObjectModel(name: string): ReturnObjectBuilder` {#setReturnObjectModel}
107
+
108
+ Sets return type to a custom object type. Returns a `ReturnObjectBuilder` which extends
109
+ `BaseModelBuilder` — add fields to define the returned shape.
110
+
111
+ The returned model name is auto-namespaced: `${methodName}${PascalCase(name)}`.
112
+
113
+ `ReturnObjectBuilder` additionally supports:
114
+ ```ts
115
+ returnModel.setArray(true) // returns an array of objects
116
+ ```
117
+
118
+ #### `setReturnScalarModel(): ReturnScalarBuilder` {#setReturnScalarModel}
119
+
120
+ Sets return type to a scalar. Returns a `ReturnScalarBuilder` which extends
121
+ `ScalarFieldBuilder` — call `setType()` to set the scalar type, `setArray()` to return
122
+ an array of scalars.
123
+
124
+ ```ts
125
+ const ret = method.setReturnScalarModel()
126
+ ret.setType('string')
127
+ ret.setArray() // returns string[]
128
+ ```
129
+
130
+ #### `setReturnVoidModel(name?: string): ReturnVoidBuilder` {#setReturnVoidModel}
131
+
132
+ Sets return type to `void` (no return value). This is the **default** — methods start
133
+ as void.
134
+
135
+ ---
136
+
137
+ ## Models — input and output {#models}
138
+
139
+ Models are named TypeScript / GraphQL types used as method arguments or return values.
140
+ They are defined on the owner (system or entity) and referenced by methods.
141
+
142
+ ### Creating models
143
+
144
+ | Method | Type | Use as |
145
+ |--------|------|--------|
146
+ | `createGeneralModel(name, title?)` | `BaseModelBuilder` | Input or output |
147
+ | `createInputModel(name, title?)` | `BaseModelBuilder` | Input only (GraphQL `input` type) |
148
+ | `createOutputModel(name, title?)` | `BaseModelBuilder` | Output only (GraphQL `type`) |
149
+
150
+ ### `BaseModelBuilder` API
151
+
152
+ #### `addField(name, title?): ScalarFieldBuilder`
153
+
154
+ Adds a scalar field to the model. Returns a `ScalarFieldBuilder` with the full field API
155
+ from [04-fields.md](./04-fields.md).
156
+
157
+ #### `addModelField(model, name, title?): ModelFieldBuilder`
158
+
159
+ Adds a reference to another model defined on the same owner. The referenced model must
160
+ exist at the time of this call — throws if not found.
161
+
162
+ ```ts
163
+ const address = system.createGeneralModel('address')
164
+ address.addField('street').setType('string').setRequired()
165
+ address.addField('city').setType('string').setRequired()
166
+
167
+ const output = method.setReturnObjectModel('result')
168
+ output.addModelField('address', 'shippingAddress')
169
+ ```
170
+
171
+ #### `delField(name): this`
172
+
173
+ Removes a field from the model.
174
+
175
+ ---
176
+
177
+ ## Full wiring example pattern
178
+
179
+ ```
180
+ system
181
+ .addMethod('calculateDiscount', MethodType.Query)
182
+ .setExportedToApi()
183
+ .setAsync()
184
+ ┌─ .getArgsModel()
185
+ │ .addField('orderId').setType('int').setRequired()
186
+ │ .addField('promoCode').setType('string')
187
+ │
188
+ └─ .setReturnObjectModel('result')
189
+ .addField('discountPercent').setType('float').setRequired()
190
+ .addField('finalPrice').setType('float').setRequired()
191
+ ```
192
+
193
+ The method name, args model, and return model combine to generate:
194
+ - A GraphQL schema entry (`Query.calculateDiscount(orderId: Int!, promoCode: String): CalculateDiscountResult`)
195
+ - A service method stub in the `Additional*Service.ts` file (yours to implement)
196
+
197
+ ---
198
+
199
+ ## Anti-patterns
200
+
201
+ ### Defining return shape via separate `createOutputModel` without linking it to the method
202
+
203
+ **Wrong:**
204
+ ```ts
205
+ system.createOutputModel('discountResult') // created but never linked
206
+
207
+ const method = system.addMethod('getDiscount', MethodType.Query)
208
+ // forgot to call setReturnObjectModel()
209
+ ```
210
+
211
+ **Why:** the model is generated but the method returns `void`. The GraphQL schema
212
+ doesn't reference the model.
213
+
214
+ **Correct:** always call one of `setReturnObjectModel`, `setReturnScalarModel`, or
215
+ `setReturnVoidModel` explicitly. For object returns, the model is created and registered
216
+ by `setReturnObjectModel` automatically — no need to pre-create it separately.
217
+
218
+ ---
219
+
220
+ ### Adding standard CRUD method names manually
221
+
222
+ **Wrong:**
223
+ ```ts
224
+ entity.addMethod('all', MethodType.Query) // throws: 'all' already exists
225
+ entity.addMethod('create', MethodType.Mutation)
226
+ ```
227
+
228
+ **Why:** `all`, `create`, `update`, `delete` are auto-added by `BaseSavableEntityBuilder`
229
+ constructor. Adding them again throws a duplicate name error.
230
+
231
+ **Correct:** use only unique custom names.
232
+
233
+ ---
234
+
235
+ ### Duplicate cron expressions on the same method
236
+
237
+ **Wrong:**
238
+ ```ts
239
+ method.addRunSchedule('0 * * * *')
240
+ method.addRunSchedule('0 * * * *') // throws: duplicate cron
241
+ ```
242
+
243
+ **Correct:** each cron expression must be unique per method.
244
+
245
+ ---
246
+
247
+ > **Examples:** this file intentionally omits code examples. Add real usage examples
248
+ > from the consuming project here. A reference metadata file is typically at
249
+ > `src/meta/metadata.ts` in the meta project.