@slingr/cli 0.0.3 → 0.0.4

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 (251) hide show
  1. package/LICENSE.txt +202 -0
  2. package/README.md +490 -319
  3. package/bin/dev.cmd +2 -2
  4. package/bin/dev.js +5 -5
  5. package/bin/run.cmd +2 -2
  6. package/bin/run.js +4 -4
  7. package/bin/slingr +1 -0
  8. package/dist/commands/build.d.ts +20 -0
  9. package/dist/commands/build.d.ts.map +1 -0
  10. package/dist/commands/build.js +206 -0
  11. package/dist/commands/build.js.map +1 -0
  12. package/dist/commands/create-app.d.ts +0 -1
  13. package/dist/commands/create-app.d.ts.map +1 -1
  14. package/dist/commands/create-app.js +38 -57
  15. package/dist/commands/create-app.js.map +1 -1
  16. package/dist/commands/debug.d.ts +28 -0
  17. package/dist/commands/debug.d.ts.map +1 -0
  18. package/dist/commands/debug.js +474 -0
  19. package/dist/commands/debug.js.map +1 -0
  20. package/dist/commands/ds.d.ts +14 -1
  21. package/dist/commands/ds.d.ts.map +1 -1
  22. package/dist/commands/ds.js +450 -121
  23. package/dist/commands/ds.js.map +1 -1
  24. package/dist/commands/gql.d.ts +1 -1
  25. package/dist/commands/gql.d.ts.map +1 -1
  26. package/dist/commands/gql.js +190 -184
  27. package/dist/commands/gql.js.map +1 -1
  28. package/dist/commands/infra/down.d.ts.map +1 -1
  29. package/dist/commands/infra/down.js +8 -7
  30. package/dist/commands/infra/down.js.map +1 -1
  31. package/dist/commands/infra/up.d.ts.map +1 -1
  32. package/dist/commands/infra/up.js +8 -7
  33. package/dist/commands/infra/up.js.map +1 -1
  34. package/dist/commands/infra/update.d.ts +1 -0
  35. package/dist/commands/infra/update.d.ts.map +1 -1
  36. package/dist/commands/infra/update.js +33 -69
  37. package/dist/commands/infra/update.js.map +1 -1
  38. package/dist/commands/run.d.ts +29 -2
  39. package/dist/commands/run.d.ts.map +1 -1
  40. package/dist/commands/run.js +628 -130
  41. package/dist/commands/run.js.map +1 -1
  42. package/dist/commands/setup.d.ts +1 -1
  43. package/dist/commands/setup.d.ts.map +1 -1
  44. package/dist/commands/setup.js +34 -71
  45. package/dist/commands/setup.js.map +1 -1
  46. package/dist/commands/sync-metadata.d.ts +15 -0
  47. package/dist/commands/sync-metadata.d.ts.map +1 -0
  48. package/dist/commands/sync-metadata.js +225 -0
  49. package/dist/commands/sync-metadata.js.map +1 -0
  50. package/dist/commands/users.d.ts +30 -0
  51. package/dist/commands/users.d.ts.map +1 -0
  52. package/dist/commands/users.js +472 -0
  53. package/dist/commands/users.js.map +1 -0
  54. package/dist/commands/views.d.ts +11 -0
  55. package/dist/commands/views.d.ts.map +1 -0
  56. package/dist/commands/views.js +73 -0
  57. package/dist/commands/views.js.map +1 -0
  58. package/dist/projectStructure.d.ts +2 -2
  59. package/dist/projectStructure.d.ts.map +1 -1
  60. package/dist/projectStructure.js +281 -69
  61. package/dist/projectStructure.js.map +1 -1
  62. package/dist/scripts/generate-metadata.d.ts +13 -0
  63. package/dist/scripts/generate-metadata.d.ts.map +1 -0
  64. package/dist/scripts/generate-metadata.js +412 -0
  65. package/dist/scripts/generate-metadata.js.map +1 -0
  66. package/dist/scripts/generate-metadata.ts +498 -0
  67. package/dist/scripts/generate-schema.d.ts +1 -1
  68. package/dist/scripts/generate-schema.js +168 -74
  69. package/dist/scripts/generate-schema.js.map +1 -1
  70. package/dist/scripts/generate-schema.ts +258 -143
  71. package/dist/templates/.env.template +23 -0
  72. package/dist/templates/.firebaserc.template +5 -0
  73. package/dist/templates/.github/copilot-instructions.md.template +652 -17
  74. package/dist/templates/backend/Dockerfile.template +30 -0
  75. package/dist/templates/config/datasource.ts.template +12 -9
  76. package/dist/templates/config/jest.config.ts +30 -30
  77. package/dist/templates/config/jest.setup.ts +1 -1
  78. package/dist/templates/config/tsconfig.json.template +50 -29
  79. package/dist/templates/dataSources/mysql.ts.template +16 -13
  80. package/dist/templates/dataSources/postgres.ts.template +15 -13
  81. package/dist/templates/dataset-generator-script.ts.template +139 -139
  82. package/dist/templates/datasets/mysql-default/.slingr-schema.json.template +5 -0
  83. package/dist/templates/datasets/mysql-default/Address.jsonl.template +3 -3
  84. package/dist/templates/datasets/mysql-default/App.jsonl.template +4 -4
  85. package/dist/templates/datasets/mysql-default/Company.jsonl.template +3 -3
  86. package/dist/templates/datasets/mysql-default/Person.jsonl.template +2 -2
  87. package/dist/templates/datasets/mysql-default/User.jsonl.template +1 -0
  88. package/dist/templates/datasets/mysql-default/instructions.md.template +1 -0
  89. package/dist/templates/datasets/postgres-default/.slingr-schema.json.template +5 -0
  90. package/dist/templates/datasets/postgres-default/Address.jsonl.template +3 -3
  91. package/dist/templates/datasets/postgres-default/App.jsonl.template +4 -4
  92. package/dist/templates/datasets/postgres-default/Company.jsonl.template +3 -3
  93. package/dist/templates/datasets/postgres-default/Person.jsonl.template +2 -2
  94. package/dist/templates/datasets/postgres-default/User.jsonl.template +1 -0
  95. package/dist/templates/datasets/postgres-default/instructions.md.template +1 -0
  96. package/dist/templates/docker-compose.prod-test.yml.template +32 -0
  97. package/dist/templates/docker-compose.yml.template +24 -0
  98. package/dist/templates/docs/app-description.md.template +33 -33
  99. package/dist/templates/firebase.json.template +68 -0
  100. package/dist/templates/frontend/.umirc.ts.template +23 -0
  101. package/dist/templates/frontend/package.json.template +45 -0
  102. package/dist/templates/frontend/public/config.json +6 -0
  103. package/dist/templates/frontend/public/logo.svg +6 -0
  104. package/dist/templates/frontend/src/app.tsx.template +44 -0
  105. package/dist/templates/frontend/src/global.less.template +117 -0
  106. package/dist/templates/frontend/src/layouts/MainLayout.tsx.template +75 -0
  107. package/dist/templates/frontend/src/types/graphql-augmentation.d.ts.template +44 -0
  108. package/dist/templates/frontend/src/views/customViews/user/UserCreateView.tsx.template +18 -0
  109. package/dist/templates/frontend/src/views/customViews/user/UserEditView.tsx.template +29 -0
  110. package/dist/templates/frontend/src/views/customViews/user/UserReadView.tsx.template +24 -0
  111. package/dist/templates/frontend/src/views/customViews/user/UserTableView.tsx.template +38 -0
  112. package/dist/templates/frontend/src/views/customViews/welcome.tsx.template +34 -0
  113. package/dist/templates/frontend/tsconfig.json.template +50 -0
  114. package/dist/templates/gql/codegen.yml.template +25 -25
  115. package/dist/templates/gql/index.ts.template +17 -24
  116. package/dist/templates/gql/operations.graphql.template +30 -30
  117. package/dist/templates/ops/README.md.template +1045 -0
  118. package/dist/templates/ops/cloudbuild.yaml.template +161 -0
  119. package/dist/templates/ops/scripts/_utils.js.template +217 -0
  120. package/dist/templates/ops/scripts/deploy.js.template +145 -0
  121. package/dist/templates/ops/scripts/setup-gcp.js.template +330 -0
  122. package/dist/templates/ops/scripts/setup-secrets.js.template +76 -0
  123. package/dist/templates/ops/scripts/test-prod-local.js.template +49 -0
  124. package/dist/templates/package.json.template +50 -38
  125. package/dist/templates/pnpm-workspace.yaml.template +3 -0
  126. package/dist/templates/prompt-analysis.md.template +110 -110
  127. package/dist/templates/prompt-script-generation.md.template +258 -258
  128. package/dist/templates/src/Address.ts.template +28 -31
  129. package/dist/templates/src/App.ts.template +17 -61
  130. package/dist/templates/src/Company.ts.template +41 -47
  131. package/dist/templates/src/Models.test.ts.template +654 -654
  132. package/dist/templates/src/Person.test.ts.template +289 -289
  133. package/dist/templates/src/Person.ts.template +90 -105
  134. package/dist/templates/src/actions/index.ts.template +11 -11
  135. package/dist/templates/src/auth/permissions.ts.template +34 -0
  136. package/dist/templates/src/data/App.ts.template +48 -0
  137. package/dist/templates/src/data/User.ts.template +35 -0
  138. package/dist/templates/src/types/gql.d.ts.template +17 -17
  139. package/dist/templates/vscode/extensions.json +4 -3
  140. package/dist/templates/vscode/settings.json +17 -11
  141. package/dist/templates/workspace-package.json.template +21 -0
  142. package/dist/utils/buildCache.d.ts +12 -0
  143. package/dist/utils/buildCache.d.ts.map +1 -0
  144. package/dist/utils/buildCache.js +102 -0
  145. package/dist/utils/buildCache.js.map +1 -0
  146. package/dist/utils/checkFramework.d.ts +27 -0
  147. package/dist/utils/checkFramework.d.ts.map +1 -0
  148. package/dist/utils/checkFramework.js +104 -0
  149. package/dist/utils/checkFramework.js.map +1 -0
  150. package/dist/utils/datasourceParser.d.ts +11 -0
  151. package/dist/utils/datasourceParser.d.ts.map +1 -1
  152. package/dist/utils/datasourceParser.js +154 -56
  153. package/dist/utils/datasourceParser.js.map +1 -1
  154. package/dist/utils/dockerManager.d.ts +25 -0
  155. package/dist/utils/dockerManager.d.ts.map +1 -0
  156. package/dist/utils/dockerManager.js +281 -0
  157. package/dist/utils/dockerManager.js.map +1 -0
  158. package/dist/utils/infraFileParser.d.ts +26 -0
  159. package/dist/utils/infraFileParser.d.ts.map +1 -0
  160. package/dist/utils/infraFileParser.js +75 -0
  161. package/dist/utils/infraFileParser.js.map +1 -0
  162. package/dist/utils/jsonlLoader.d.ts +91 -12
  163. package/dist/utils/jsonlLoader.d.ts.map +1 -1
  164. package/dist/utils/jsonlLoader.js +674 -63
  165. package/dist/utils/jsonlLoader.js.map +1 -1
  166. package/dist/utils/model-analyzer.d.ts.map +1 -1
  167. package/dist/utils/model-analyzer.js +67 -13
  168. package/dist/utils/model-analyzer.js.map +1 -1
  169. package/dist/utils/userManagement.d.ts +57 -0
  170. package/dist/utils/userManagement.d.ts.map +1 -0
  171. package/dist/utils/userManagement.js +288 -0
  172. package/dist/utils/userManagement.js.map +1 -0
  173. package/dist/utils/viewsGenerator.d.ts +15 -0
  174. package/dist/utils/viewsGenerator.d.ts.map +1 -0
  175. package/dist/utils/viewsGenerator.js +311 -0
  176. package/dist/utils/viewsGenerator.js.map +1 -0
  177. package/oclif.manifest.json +445 -20
  178. package/package.json +29 -26
  179. package/src/templates/.env.template +23 -0
  180. package/src/templates/.firebaserc.template +5 -0
  181. package/src/templates/.github/copilot-instructions.md.template +652 -17
  182. package/src/templates/backend/Dockerfile.template +30 -0
  183. package/src/templates/config/datasource.ts.template +12 -9
  184. package/src/templates/config/jest.config.ts +30 -30
  185. package/src/templates/config/jest.setup.ts +1 -1
  186. package/src/templates/config/tsconfig.json.template +50 -29
  187. package/src/templates/dataSources/mysql.ts.template +16 -13
  188. package/src/templates/dataSources/postgres.ts.template +15 -13
  189. package/src/templates/dataset-generator-script.ts.template +139 -139
  190. package/src/templates/datasets/mysql-default/.slingr-schema.json.template +5 -0
  191. package/src/templates/datasets/mysql-default/Address.jsonl.template +3 -3
  192. package/src/templates/datasets/mysql-default/App.jsonl.template +4 -4
  193. package/src/templates/datasets/mysql-default/Company.jsonl.template +3 -3
  194. package/src/templates/datasets/mysql-default/Person.jsonl.template +2 -2
  195. package/src/templates/datasets/mysql-default/User.jsonl.template +1 -0
  196. package/src/templates/datasets/mysql-default/instructions.md.template +1 -0
  197. package/src/templates/datasets/postgres-default/.slingr-schema.json.template +5 -0
  198. package/src/templates/datasets/postgres-default/Address.jsonl.template +3 -3
  199. package/src/templates/datasets/postgres-default/App.jsonl.template +4 -4
  200. package/src/templates/datasets/postgres-default/Company.jsonl.template +3 -3
  201. package/src/templates/datasets/postgres-default/Person.jsonl.template +2 -2
  202. package/src/templates/datasets/postgres-default/User.jsonl.template +1 -0
  203. package/src/templates/datasets/postgres-default/instructions.md.template +1 -0
  204. package/src/templates/docker-compose.prod-test.yml.template +32 -0
  205. package/src/templates/docker-compose.yml.template +24 -0
  206. package/src/templates/docs/app-description.md.template +33 -33
  207. package/src/templates/firebase.json.template +68 -0
  208. package/src/templates/frontend/.umirc.ts.template +23 -0
  209. package/src/templates/frontend/package.json.template +45 -0
  210. package/src/templates/frontend/public/config.json +6 -0
  211. package/src/templates/frontend/public/logo.svg +6 -0
  212. package/src/templates/frontend/src/app.tsx.template +44 -0
  213. package/src/templates/frontend/src/global.less.template +117 -0
  214. package/src/templates/frontend/src/layouts/MainLayout.tsx.template +75 -0
  215. package/src/templates/frontend/src/types/graphql-augmentation.d.ts.template +44 -0
  216. package/src/templates/frontend/src/views/customViews/user/UserCreateView.tsx.template +18 -0
  217. package/src/templates/frontend/src/views/customViews/user/UserEditView.tsx.template +29 -0
  218. package/src/templates/frontend/src/views/customViews/user/UserReadView.tsx.template +24 -0
  219. package/src/templates/frontend/src/views/customViews/user/UserTableView.tsx.template +38 -0
  220. package/src/templates/frontend/src/views/customViews/welcome.tsx.template +34 -0
  221. package/src/templates/frontend/tsconfig.json.template +50 -0
  222. package/src/templates/gql/codegen.yml.template +25 -25
  223. package/src/templates/gql/index.ts.template +17 -24
  224. package/src/templates/gql/operations.graphql.template +30 -30
  225. package/src/templates/ops/README.md.template +1045 -0
  226. package/src/templates/ops/cloudbuild.yaml.template +161 -0
  227. package/src/templates/ops/scripts/_utils.js.template +217 -0
  228. package/src/templates/ops/scripts/deploy.js.template +145 -0
  229. package/src/templates/ops/scripts/setup-gcp.js.template +330 -0
  230. package/src/templates/ops/scripts/setup-secrets.js.template +76 -0
  231. package/src/templates/ops/scripts/test-prod-local.js.template +49 -0
  232. package/src/templates/package.json.template +50 -38
  233. package/src/templates/pnpm-workspace.yaml.template +3 -0
  234. package/src/templates/prompt-analysis.md.template +110 -110
  235. package/src/templates/prompt-script-generation.md.template +258 -258
  236. package/src/templates/src/Address.ts.template +28 -31
  237. package/src/templates/src/App.ts.template +17 -61
  238. package/src/templates/src/Company.ts.template +41 -47
  239. package/src/templates/src/Models.test.ts.template +654 -654
  240. package/src/templates/src/Person.test.ts.template +289 -289
  241. package/src/templates/src/Person.ts.template +90 -105
  242. package/src/templates/src/actions/index.ts.template +11 -11
  243. package/src/templates/src/auth/permissions.ts.template +34 -0
  244. package/src/templates/src/data/App.ts.template +48 -0
  245. package/src/templates/src/data/User.ts.template +35 -0
  246. package/src/templates/src/types/gql.d.ts.template +17 -17
  247. package/src/templates/vscode/extensions.json +4 -3
  248. package/src/templates/vscode/settings.json +17 -11
  249. package/src/templates/workspace-package.json.template +21 -0
  250. package/dist/templates/src/index.ts +0 -66
  251. package/src/templates/src/index.ts +0 -66
@@ -1,17 +1,652 @@
1
- # GitHub Copilot Instructions for {{APP_NAME}}
2
-
3
- This is a {{APP_TYPE}} application built with Slingr.
4
-
5
- ## Project Description
6
- {{DESCRIPTION}}
7
-
8
- ## Architecture
9
- - Backend: {{HAS_BACKEND}}
10
- - Frontend: {{HAS_FRONTEND}}
11
- - Database: {{DB_TYPE}}
12
-
13
- ## Development Guidelines
14
- - Use TypeScript for all code
15
- - Follow Slingr conventions and patterns
16
- - Maintain clean, readable code with proper documentation
17
- - Use the provided data models as starting points
1
+ # GitHub Copilot Instructions for {{APP_NAME}}
2
+
3
+ This is a {{APP_TYPE}} application built with Slingr.
4
+
5
+ ## Project Description
6
+
7
+ {{DESCRIPTION}}
8
+
9
+ ## Architecture
10
+
11
+ - Backend: {{HAS_BACKEND}}
12
+ - Frontend: {{HAS_FRONTEND}}
13
+ - Database: {{DB_TYPE}}
14
+
15
+ ## Development Guidelines
16
+
17
+ - Use TypeScript for all code
18
+ - Follow Slingr conventions and patterns
19
+ - Maintain clean, readable code with proper documentation
20
+ - Use the provided data models as starting points
21
+
22
+ ## Slingr Framework Patterns
23
+
24
+ ### Model Definition
25
+
26
+ Always use shortcut decorators that combine @Field and type-specific decorators:
27
+
28
+ ```typescript
29
+ import { BaseDataModel, DataModel, TextField, EmailField, IntegerField } from '@slingr/framework-backend';
30
+
31
+ @DataModel()
32
+ export class User extends BaseDataModel {
33
+ @TextField({ required: true, maxLength: 100 })
34
+ name: string;
35
+
36
+ @EmailField({ required: true })
37
+ email: string;
38
+
39
+ @IntegerField({ min: 0, max: 120 })
40
+ age?: number;
41
+ }
42
+ ```
43
+
44
+ ### Relationship Fields
45
+
46
+ **CRITICAL**: Relationship decorators ALWAYS require the `type` parameter.
47
+
48
+ #### Single Reference
49
+
50
+ ```typescript
51
+ import { ReferenceField } from '@slingr/framework-backend';
52
+
53
+ @DataModel()
54
+ export class Task extends BaseDataModel {
55
+ @TextField({ required: true })
56
+ title: string;
57
+
58
+ // ALWAYS include type for references
59
+ @ReferenceField({ required: true, type: () => Project })
60
+ project: Project;
61
+ }
62
+ ```
63
+
64
+ #### Reference Arrays
65
+
66
+ ```typescript
67
+ @ReferenceField({ type: () => User })
68
+ assignees: User[];
69
+ ```
70
+
71
+ #### Composition (Parent-Child)
72
+
73
+ ```typescript
74
+ import { CompositionField, OwnerReferenceField } from '@slingr/framework-backend';
75
+
76
+ @DataModel()
77
+ export class Order extends BaseDataModel {
78
+ // Composition ALWAYS requires type
79
+ @CompositionField({ type: () => LineItem })
80
+ items: LineItem[];
81
+ }
82
+
83
+ @DataModel()
84
+ export class LineItem extends BaseDataModel {
85
+ @TextField({ required: true })
86
+ productName: string;
87
+
88
+ // Owner reference ALWAYS requires type
89
+ @OwnerReferenceField({ type: () => Order })
90
+ owner: Order;
91
+ }
92
+ ```
93
+
94
+ #### Shared Composition
95
+
96
+ ```typescript
97
+ import { SharedCompositionField } from '@slingr/framework-backend';
98
+
99
+ @DataModel()
100
+ export class Story extends BaseDataModel {
101
+ // Shared composition ALWAYS requires type
102
+ @SharedCompositionField({ type: () => Note })
103
+ notes: Note[];
104
+ }
105
+ ```
106
+
107
+ ### All Field Types
108
+
109
+ #### String Fields
110
+
111
+ ```typescript
112
+ import { TextField, EmailField, HtmlField, UuidField } from '@slingr/framework-backend';
113
+
114
+ @DataModel()
115
+ export class Document extends BaseDataModel {
116
+ // Primary key - ALWAYS use UuidField
117
+ @UuidField({ primaryKey: true, generated: true })
118
+ id!: string;
119
+
120
+ // Text with validation
121
+ @TextField({ required: true, minLength: 3, maxLength: 100 })
122
+ title!: string;
123
+
124
+ // Email validation
125
+ @EmailField({ required: true })
126
+ email!: string;
127
+
128
+ // HTML content
129
+ @HtmlField({ maxLength: 5000 })
130
+ content?: string;
131
+
132
+ // Array of strings
133
+ @TextField()
134
+ tags!: string[];
135
+ }
136
+ ```
137
+
138
+ #### Numeric Fields
139
+
140
+ ```typescript
141
+ import { IntegerField, DecimalField, MoneyField, NumberField } from '@slingr/framework-backend';
142
+
143
+ @DataModel()
144
+ export class Product extends BaseDataModel {
145
+ // Integer with constraints
146
+ @IntegerField({ required: true, min: 0, max: 999999 })
147
+ quantity!: number;
148
+
149
+ // Decimal - requires decimals and roundingType
150
+ @DecimalField({
151
+ decimals: 4,
152
+ roundingType: 'roundHalfToEven',
153
+ positive: true,
154
+ })
155
+ weight!: string;
156
+
157
+ // Money - requires decimals and roundingType
158
+ @MoneyField({
159
+ decimals: 2,
160
+ roundingType: 'roundHalfToEven',
161
+ min: '0',
162
+ })
163
+ price!: string;
164
+
165
+ // Floating point number
166
+ @NumberField({ min: 0, max: 1 })
167
+ discount?: number;
168
+ }
169
+ ```
170
+
171
+ #### Boolean and DateTime
172
+
173
+ ```typescript
174
+ import { BooleanField, DateTimeField } from '@slingr/framework-backend';
175
+
176
+ @DataModel()
177
+ export class Article extends BaseDataModel {
178
+ @BooleanField({ required: true })
179
+ published: boolean = false;
180
+
181
+ @DateTimeField({ required: true })
182
+ publishedAt!: Date;
183
+
184
+ @DateTimeField()
185
+ lastModified?: Date;
186
+ }
187
+ ```
188
+
189
+ #### Choice/Enum Fields
190
+
191
+ ```typescript
192
+ import { ChoiceField } from '@slingr/framework-backend';
193
+
194
+ enum Status {
195
+ Draft = 'draft',
196
+ Published = 'published',
197
+ }
198
+
199
+ @DataModel()
200
+ export class Post extends BaseDataModel {
201
+ // ALWAYS include type for enums
202
+ @ChoiceField({ required: true, type: () => Status })
203
+ status: Status = Status.Draft;
204
+
205
+ // Array of enum values
206
+ @ChoiceField({ type: () => Status })
207
+ history!: Status[];
208
+ }
209
+ ```
210
+
211
+ ### Model Options
212
+
213
+ ```typescript
214
+ import { DataModel } from '@slingr/framework-backend';
215
+
216
+ // Simple model
217
+ @DataModel({
218
+ docs: 'User model',
219
+ })
220
+ export class User extends BaseDataModel {
221
+ // fields...
222
+ }
223
+
224
+ // Model with CRUD actions
225
+ @DataModel({
226
+ docs: 'Task with auto-generated CRUD',
227
+ crud: {
228
+ generate: true,
229
+ api: 'gql', // Expose via GraphQL
230
+ actions: ['create', 'findById', 'findBy', 'update', 'deleteById'],
231
+ },
232
+ })
233
+ export class Task extends BaseDataModel {
234
+ // fields...
235
+ }
236
+
237
+ // Model with global validation
238
+ @DataModel({
239
+ validation: (model: PasswordChange) => {
240
+ const errors = [];
241
+ if (model.newPassword !== model.confirmPassword) {
242
+ errors.push({
243
+ constraint: 'passwordMismatch',
244
+ message: 'Passwords do not match',
245
+ });
246
+ }
247
+ return errors;
248
+ },
249
+ })
250
+ export class PasswordChange extends BaseDataModel {
251
+ @TextField({ required: true })
252
+ newPassword!: string;
253
+
254
+ @TextField({ required: true })
255
+ confirmPassword!: string;
256
+ }
257
+ ```
258
+
259
+ ### Actions Framework
260
+
261
+ #### ModelAction (Class-level actions)
262
+
263
+ ```typescript
264
+ import { Action, ModelAction } from '@slingr/framework-backend';
265
+
266
+ @DataModel()
267
+ class CreateTaskParams extends BaseDataModel {
268
+ @TextField({ required: true })
269
+ title!: string;
270
+
271
+ @ReferenceField({ required: true, type: () => Project })
272
+ project!: Project;
273
+ }
274
+
275
+ @Action({
276
+ type: 'write',
277
+ api: 'gql',
278
+ model: Task,
279
+ params: CreateTaskParams,
280
+ returns: Task,
281
+ })
282
+ export class CreateTask extends ModelAction<Task, CreateTaskParams, Task> {
283
+ async execute(params: CreateTaskParams): Promise<Task> {
284
+ const task = new Task();
285
+ task.title = params.title;
286
+ task.project = params.project;
287
+ return task;
288
+ }
289
+ }
290
+ ```
291
+
292
+ #### ObjectAction (Instance-level actions)
293
+
294
+ ```typescript
295
+ import { ObjectAction } from '@slingr/framework-backend';
296
+
297
+ @Action({
298
+ type: 'write',
299
+ api: 'gql',
300
+ model: Task,
301
+ returns: Task,
302
+ })
303
+ export class StartTask extends ObjectAction<Task, void, Task> {
304
+ async execute(task: Task): Promise<Task> {
305
+ task.status = 'inProgress';
306
+ task.startedAt = new Date();
307
+ return task;
308
+ }
309
+
310
+ async canExecute(task: Task): Promise<boolean | string> {
311
+ if (task.status !== 'todo') {
312
+ return 'Task must be in todo status';
313
+ }
314
+ return true;
315
+ }
316
+ }
317
+ ```
318
+
319
+ #### GlobalAction (Application-level actions)
320
+
321
+ ```typescript
322
+ import { GlobalAction } from '@slingr/framework-backend';
323
+
324
+ @DataModel()
325
+ class StatsResult extends BaseDataModel {
326
+ @IntegerField()
327
+ totalTasks!: number;
328
+ }
329
+
330
+ @Action({
331
+ type: 'read',
332
+ api: 'gql',
333
+ returns: StatsResult,
334
+ })
335
+ export class GetStats extends GlobalAction<void, StatsResult> {
336
+ async execute(): Promise<StatsResult> {
337
+ const stats = new StatsResult();
338
+ stats.totalTasks = 100;
339
+ return stats;
340
+ }
341
+ }
342
+ ```
343
+
344
+ ### Permissions Management
345
+
346
+ Slingr uses CASL for fine-grained access control. Define permissions in your application:
347
+
348
+ ```typescript
349
+ import { app } from '@slingr/framework-backend';
350
+
351
+ // Guest permissions (non-authenticated)
352
+ app.defineGuestPermissions(({ can, cannot }) => {
353
+ can('access', Article, { isPublic: true });
354
+ can('read', Article, ['id', 'title', 'body']);
355
+ });
356
+
357
+ // Global permissions (all authenticated users)
358
+ app.defineGlobalPermissions((user, { can, cannot }) => {
359
+ // Users can manage their own profile
360
+ can('read', User, { id: user.id });
361
+ can('update', User, { id: user.id });
362
+ cannot('update', User, ['role', 'status']);
363
+ });
364
+
365
+ // Role-based permissions
366
+ enum UserRole {
367
+ Admin = 'admin',
368
+ Editor = 'editor',
369
+ }
370
+
371
+ app.definePermissionsForRole(UserRole.Admin, (user, { can }) => {
372
+ can('manage', 'all'); // Full access
373
+ });
374
+
375
+ app.definePermissionsForRole(UserRole.Editor, (user, { can, cannot }) => {
376
+ // Conditional access to tasks
377
+ can('access', Task, {
378
+ project: {
379
+ members: { elemMatch: { id: { eq: user.id } } },
380
+ },
381
+ });
382
+
383
+ can('create', Task);
384
+ can('update', Task);
385
+ can('read', Task);
386
+ can('write', Task);
387
+
388
+ // Field-level restrictions
389
+ cannot('write', Task, ['status', 'priority']);
390
+ cannot('read', Task, ['internalNotes']);
391
+
392
+ // Action permissions
393
+ can('execute', CreateTask);
394
+ });
395
+
396
+ // Callback-based permissions
397
+ app.definePermissionsForRole(UserRole.Editor, (user, { can }) => {
398
+ can('update', Task, task => {
399
+ return task.assignee?.id === user.id;
400
+ });
401
+ });
402
+ ```
403
+
404
+ ### Permission Actions
405
+
406
+ - `manage` - Matches any action (admin)
407
+ - `access` - Access to objects (CRUD)
408
+ - `create` - Create new objects
409
+ - `update` - Update existing objects
410
+ - `delete` - Delete objects
411
+ - `read` - Read specific fields
412
+ - `write` - Write specific fields
413
+ - `execute` - Execute actions
414
+
415
+ ### Permission Conditions
416
+
417
+ ```typescript
418
+ // Field conditions
419
+ { eq: value } // Equal
420
+ { ne: value } // Not equal
421
+ { gt: value } // Greater than
422
+ { gte: value } // Greater than or equal
423
+ { lt: value } // Less than
424
+ { lte: value } // Less than or equal
425
+ { in: [values] } // In array
426
+ { nin: [values] } // Not in array
427
+
428
+ // Array conditions
429
+ { elemMatch: query } // Element matches
430
+ { all: [values] } // Has all values
431
+ { size: number } // Array length
432
+
433
+ // Example
434
+ can('read', Article, {
435
+ status: { in: ['published', 'featured'] },
436
+ views: { gte: 1000 }
437
+ });
438
+ ```
439
+
440
+ ### Common Mistakes to Avoid
441
+
442
+ ❌ **Missing type**:
443
+
444
+ ```typescript
445
+ @ReferenceField({ required: true })
446
+ project: Project; // ERROR: type is required!
447
+ ```
448
+
449
+ ✅ **Correct**:
450
+
451
+ ```typescript
452
+ @ReferenceField({ required: true, type: () => Project })
453
+ project: Project;
454
+ ```
455
+
456
+ ❌ **Using generic @RelationshipField**:
457
+
458
+ ```typescript
459
+ @RelationshipField({ type: 'reference', type: () => Project })
460
+ project: Project;
461
+ ```
462
+
463
+ ✅ **Use specific shortcuts**:
464
+
465
+ ```typescript
466
+ @ReferenceField({ type: () => Project })
467
+ project: Project;
468
+ ```
469
+
470
+ ### Reference Field Options
471
+
472
+ ```typescript
473
+ // With onDelete behavior
474
+ @ReferenceField({
475
+ type: () => Project,
476
+ onDelete: 'removeReference' // or 'delete' or 'nothing'
477
+ })
478
+ project: Project;
479
+
480
+ // With filter for available records
481
+ @ReferenceField({
482
+ type: () => Project,
483
+ filter: (task) => ({ status: 'active' })
484
+ })
485
+ activeProjects: Project[];
486
+
487
+ // Lazy vs eager loading
488
+ @ReferenceField({
489
+ type: () => Project,
490
+ load: false // default for references (lazy)
491
+ })
492
+ project: Project;
493
+ ```
494
+
495
+ ### Field Decorators Reference
496
+
497
+ - **@TextField** - String fields with validation (maxLength, minLength, regex)
498
+ - **@EmailField** - Email validation
499
+ - **@HtmlField** - HTML content
500
+ - **@UuidField** - UUID fields (use for primary keys with generated: true)
501
+ - **@IntegerField** - Integer numbers
502
+ - **@DecimalField** - Precise decimals (requires decimals and roundingType)
503
+ - **@MoneyField** - Money amounts (requires decimals and roundingType)
504
+ - **@NumberField** - Floating point numbers
505
+ - **@BooleanField** - Boolean values
506
+ - **@DateTimeField** - Date and time
507
+ - **@ChoiceField** - Enum selections (requires type for enums)
508
+ - **@ReferenceField** - Model references (requires type)
509
+ - **@CompositionField** - Parent-child relationships (requires type)
510
+ - **@SharedCompositionField** - Shared compositions (requires type)
511
+ - **@OwnerReferenceField** - Child to parent reference (requires type)
512
+
513
+ ## Build & Compilation
514
+
515
+ ### ALWAYS use `slingr build` — never `npm run build` directly
516
+
517
+ Slingr apps have **auto-generated files** in `backend/generated/` and `frontend/generated/` (view registries, GraphQL schema, SDK). These files are produced by `slingr sync-metadata` and must exist before TypeScript can compile. Running `npm run build` directly will fail with errors like:
518
+
519
+ ```
520
+ generated/viewsRegistry.ts:25:19 - error TS2307: Cannot find module '../src/views/...'
521
+ ```
522
+
523
+ **Always use:**
524
+
525
+ ```bash
526
+ slingr build # sync metadata + compile backend + build frontend
527
+ slingr build --skip-frontend # sync metadata + compile backend only
528
+ slingr build --skip-metadata # skip metadata (if already up-to-date)
529
+ slingr build --verbose # show detailed output
530
+ ```
531
+
532
+ ### Never edit files in `generated/`
533
+
534
+ Files in `backend/generated/` and `frontend/generated/` are **auto-generated**. Never edit them manually — changes will be overwritten on the next `slingr sync-metadata` or `slingr build`.
535
+
536
+ If you see TypeScript errors in `generated/` files:
537
+ 1. Run `slingr build` to regenerate them
538
+ 2. Do **not** try to fix the errors by editing `generated/` files
539
+
540
+ ### `slingr sync-metadata` (metadata only)
541
+
542
+ If you only need to regenerate the metadata without recompiling:
543
+
544
+ ```bash
545
+ slingr sync-metadata # regenerate all generated files
546
+ slingr sync-metadata --skip-views # skip view registries
547
+ slingr sync-metadata --skip-schema # skip GraphQL schema
548
+ slingr sync-metadata --skip-sdk # skip GraphQL SDK
549
+ ```
550
+
551
+ ---
552
+
553
+ ## Class API Reference — Overridable Methods and Fields
554
+
555
+ When generating code that extends framework base classes, only use the methods and fields listed below. Do NOT suggest private or internal members.
556
+
557
+ ### BaseDataModel (backend)
558
+
559
+ Overridable public methods:
560
+
561
+ - `onRefresh?(changedFields: string[]): void | Promise<void>` — React to field changes server-side.
562
+ - `validate(): Promise<ValidationError[]>` — Run validation rules.
563
+ - `toJSON(): Record<string, any>` — Serialize to plain object.
564
+ - `calculate(maxIterations?: number): Promise<void>` — Recalculate manual calculated fields.
565
+ - `filter(): void` — Clear unavailable field values.
566
+
567
+ Static methods:
568
+
569
+ - `fromJSON(json): T` — Deserialize a plain object into a typed model instance.
570
+
571
+ ### Actions (backend)
572
+
573
+ #### GlobalAction\<P, R\>
574
+
575
+ - `async execute(params: P): Promise<R>` — (required) Implement the action logic.
576
+ - `async canExecute(): Promise<boolean | string>` — Return `false` or error string to prevent execution.
577
+
578
+ #### ModelAction\<M, P, R\>
579
+
580
+ - `async execute(params: P): Promise<R>` — (required) Implement the action logic.
581
+ - `async canExecute(): Promise<boolean | string>` — Return `false` or error string to prevent execution.
582
+
583
+ #### ObjectAction\<M, P, R\>
584
+
585
+ - `async execute(target: M, params: P): Promise<R>` — (required) Implement the action logic.
586
+ - `async canExecute(target: M): Promise<boolean | string>` — Return `false` or error string to prevent execution.
587
+
588
+ ### Workflow Actions (backend)
589
+
590
+ All workflow actions share the action pattern plus:
591
+
592
+ - `callStep(stepName: string, ...args): Promise<any>` — Invoke a `@Step()` method.
593
+ - `reportProgress(progress: number): void` — Report execution progress (0–100).
594
+
595
+ ### BaseQueue (backend)
596
+
597
+ Overridable lifecycle hooks:
598
+
599
+ - `onWorkflowActive(workflow: Workflow): void | Promise<void>`
600
+ - `onWorkflowCompleted(workflow: Workflow, result: any): void | Promise<void>`
601
+ - `onWorkflowFailed(workflow: Workflow, error: Error): void | Promise<void>`
602
+ - `onWorkflowProgress(workflow: Workflow, progress: number): void | Promise<void>`
603
+
604
+ Public API:
605
+
606
+ - `getActiveCount()`, `getCompletedCount()`, `getFailedCount()`, `getName()`, `getConfig()`
607
+
608
+ ### BaseLayout (frontend)
609
+
610
+ Overridable configuration:
611
+
612
+ - `navigation` — `'mix'` | `'left'` | `'top'`
613
+ - `contentWidth` — `'fluid'` | `'fixed'`
614
+ - `features` — Controls header, footer, mainMenu, secondaryMenu, userMenu visibility.
615
+ - `header?`, `footer?`, `mainMenu?`, `secondaryMenu?`, `userMenu?`
616
+
617
+ Lifecycle hooks: `onMenuClick?()`, `onMenuCollapse?(collapsed: boolean)`, `onPageSwitch?()`
618
+
619
+ ### CustomViewComponent (frontend)
620
+
621
+ Configuration: `header?`, `footer?`, `modal?`, `menu?`, `layout?`, `modalSize?`, `modalPosition?`
622
+
623
+ Lifecycle hooks:
624
+
625
+ - `onLoad()` — Initialize data after mount.
626
+ - `onLeave()` — Clean up before unmount.
627
+ - `onRender(): React.ReactNode` — (required) Render the view content.
628
+ - `onParamsChange?(prevParams, newParams)` — Handle route param changes.
629
+
630
+ Methods: `getParams()`, `getQuery()`, `openView()`, `closeView()`, `getContext()`, `isInModal()`, `setState()`, `forceUpdate()`, `app` (antd message/modal/notification)
631
+
632
+ ### TableViewComponent (frontend)
633
+
634
+ Required: `tableOptions: TableViewTableOptions<T>` — (required) columns, pagination, selection. Optional: `header?`, `menu?`, `layout?`, `hideHeader?`, `persistence?`, `modalSize?`, `modalPosition?` Hook: `afterActionExecution?(response)` — Post-action hook. Static toolbar: `toolbar.modelActionButton(name)`, `toolbar.globalActionButton(name)`, etc.
635
+
636
+ ### Form Views (frontend)
637
+
638
+ #### CreateViewComponent\<T\>
639
+
640
+ Config: `fields?`, `formLayout?`, `layout?`, `refreshMode?`, `refreshTriggers?`, `formProps?` Hooks: `beforeCreate()`, `afterCreated(response)`, `onRefresh(changedFields, data, prevData)`, `onRenderForm(context)`
641
+
642
+ #### EditViewComponent\<T\>
643
+
644
+ Same as Create plus: `modalSize?`, `modalPosition?` Hooks: `beforeSave()`, `afterSaved(response)`, `onSave()`, `onRefresh()`, `onRender()`, `onRenderForm()` Methods: `getFormValue(field)`, `getFormValues()`, `setFormValue(field, value)`, `setFormValues(values)`
645
+
646
+ #### ReadViewComponent\<T\>
647
+
648
+ Config: `fields?`, `formLayout?`, `layout?`, `formProps?`, `deleteFallbackPath?` Hooks: `afterActionExecution(response)`, `onRenderForm(context)` Fields: `id`, `object`, `objectActions`, `modelActions`, `globalActions` Static toolbar: `toolbar.refreshButton()`, `toolbar.editButton()`, `toolbar.deleteButton()`, `toolbar.closeButton()`, `toolbar.actionsDropdown()`
649
+
650
+ #### ActionViewComponent\<T\>
651
+
652
+ Config: `fields?`, `formLayout?`, `layout?`, `refreshMode?`, `refreshTriggers?`, `modalSize?`, `modalPosition?` Hooks: `onLoad()`, `beforeExecute()`, `onExecute()`, `afterExecuted(response)`, `onCancel()`, `onRefresh()`, `onRender()`, `onRenderForm()` Fields: `actionInfo`, `initialData`, `targetObject`, `idForRefresh`
@@ -0,0 +1,30 @@
1
+ # syntax=docker/dockerfile:1
2
+ # Build context: this app's root directory (the folder containing backend/ and frontend/).
3
+ # Usage: docker build -f backend/Dockerfile -t <image> .
4
+
5
+ # ── Stage 1: build ──────────────────────────────────────────────────────────
6
+ FROM node:20-alpine AS builder
7
+ WORKDIR /app
8
+
9
+ # Install dependencies separately to leverage Docker layer caching.
10
+ # Re-runs only when package.json / package-lock.json change.
11
+ COPY backend/package*.json ./
12
+ RUN npm install
13
+
14
+ # Compile TypeScript.
15
+ COPY backend/ ./
16
+ RUN npm run build
17
+
18
+ # ── Stage 2: runtime ─────────────────────────────────────────────────────────
19
+ FROM node:20-alpine AS runtime
20
+ WORKDIR /app
21
+
22
+ COPY --from=builder /app/dist/ dist/
23
+ COPY --from=builder /app/node_modules/ node_modules/
24
+ COPY --from=builder /app/package.json ./
25
+
26
+ # Cloud Run injects $PORT; default to 3000 for local use.
27
+ ENV PORT=3000
28
+ EXPOSE 3000
29
+
30
+ CMD ["node", "dist/src/App.js"]