@postxl/generator 0.74.1 → 1.0.1

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 (189) hide show
  1. package/LICENSE +50 -0
  2. package/README.md +79 -1
  3. package/dist/generator-manager.class.d.ts +59 -0
  4. package/dist/generator-manager.class.js +221 -0
  5. package/dist/generator.class.d.ts +90 -0
  6. package/dist/generator.class.js +32 -0
  7. package/dist/generator.context.d.ts +174 -0
  8. package/dist/generator.context.js +125 -0
  9. package/dist/helpers/branded.types.d.ts +149 -0
  10. package/dist/helpers/branded.types.js +111 -0
  11. package/dist/helpers/config-builder.class.d.ts +27 -0
  12. package/dist/helpers/config-builder.class.js +54 -0
  13. package/dist/helpers/import-generator.class.d.ts +70 -0
  14. package/dist/helpers/import-generator.class.js +166 -0
  15. package/dist/helpers/importable.types.d.ts +52 -0
  16. package/dist/helpers/importable.types.js +15 -0
  17. package/dist/helpers/index-generator.class.d.ts +10 -0
  18. package/dist/helpers/index-generator.class.js +46 -0
  19. package/dist/helpers/index.d.ts +8 -0
  20. package/dist/helpers/index.js +24 -0
  21. package/dist/helpers/package-json.generator.d.ts +56 -0
  22. package/dist/helpers/package-json.generator.js +36 -0
  23. package/dist/helpers/tsconfig.generator.d.ts +1 -0
  24. package/dist/helpers/tsconfig.generator.js +14 -0
  25. package/dist/helpers/verify-context.d.ts +4 -0
  26. package/dist/helpers/verify-context.js +23 -0
  27. package/dist/index.d.ts +5 -0
  28. package/dist/index.js +21 -0
  29. package/dist/utils/checksum.d.ts +10 -0
  30. package/dist/utils/checksum.js +132 -0
  31. package/dist/utils/fs-utils.d.ts +34 -0
  32. package/dist/utils/fs-utils.js +126 -0
  33. package/dist/utils/index.d.ts +10 -0
  34. package/dist/utils/index.js +26 -0
  35. package/dist/utils/jsdoc.d.ts +12 -0
  36. package/dist/utils/jsdoc.js +37 -0
  37. package/dist/utils/lint.d.ts +46 -0
  38. package/dist/utils/lint.js +154 -0
  39. package/dist/utils/lockfile.d.ts +7 -0
  40. package/dist/utils/lockfile.js +80 -0
  41. package/dist/utils/logger.class.d.ts +25 -0
  42. package/dist/utils/logger.class.js +55 -0
  43. package/dist/utils/merge-conflict.d.ts +55 -0
  44. package/dist/utils/merge-conflict.js +264 -0
  45. package/dist/utils/path.d.ts +52 -0
  46. package/dist/utils/path.js +183 -0
  47. package/dist/utils/prettier-config.d.ts +2 -0
  48. package/dist/utils/prettier-config.js +13 -0
  49. package/dist/utils/prettier.d.ts +5 -0
  50. package/dist/utils/prettier.js +67 -0
  51. package/dist/utils/prettier.skiptest.d.ts +1 -0
  52. package/dist/utils/prettier.skiptest.js +22 -0
  53. package/dist/utils/promise.d.ts +2 -0
  54. package/dist/utils/promise.js +10 -0
  55. package/dist/utils/string-functions.d.ts +9 -0
  56. package/dist/utils/string-functions.js +23 -0
  57. package/dist/utils/sync-log-result.d.ts +9 -0
  58. package/dist/utils/sync-log-result.js +90 -0
  59. package/dist/utils/sync.d.ts +143 -0
  60. package/dist/utils/sync.js +325 -0
  61. package/dist/utils/template.d.ts +66 -0
  62. package/dist/utils/template.js +159 -0
  63. package/dist/utils/vfs.class.d.ts +115 -0
  64. package/dist/utils/vfs.class.js +239 -0
  65. package/dist/utils/zip.d.ts +13 -0
  66. package/dist/utils/zip.js +40 -0
  67. package/package.json +57 -34
  68. package/dist/generator.d.ts +0 -13
  69. package/dist/generator.js +0 -455
  70. package/dist/generators/enums/react.generator.d.ts +0 -10
  71. package/dist/generators/enums/react.generator.js +0 -110
  72. package/dist/generators/enums/types.generator.d.ts +0 -10
  73. package/dist/generators/enums/types.generator.js +0 -39
  74. package/dist/generators/indices/data/module.generator.d.ts +0 -9
  75. package/dist/generators/indices/data/module.generator.js +0 -60
  76. package/dist/generators/indices/data/service.generator.d.ts +0 -9
  77. package/dist/generators/indices/data/service.generator.js +0 -249
  78. package/dist/generators/indices/data/types.generator.d.ts +0 -9
  79. package/dist/generators/indices/data/types.generator.js +0 -49
  80. package/dist/generators/indices/dispatcher-service.generator.d.ts +0 -9
  81. package/dist/generators/indices/dispatcher-service.generator.js +0 -107
  82. package/dist/generators/indices/export/class.generator.d.ts +0 -9
  83. package/dist/generators/indices/export/class.generator.js +0 -140
  84. package/dist/generators/indices/export/encoder.generator.d.ts +0 -9
  85. package/dist/generators/indices/export/encoder.generator.js +0 -50
  86. package/dist/generators/indices/import/convert-functions.generator.d.ts +0 -9
  87. package/dist/generators/indices/import/convert-functions.generator.js +0 -509
  88. package/dist/generators/indices/import/decoder.generator.d.ts +0 -9
  89. package/dist/generators/indices/import/decoder.generator.js +0 -40
  90. package/dist/generators/indices/import/service.generator.d.ts +0 -9
  91. package/dist/generators/indices/import/service.generator.js +0 -573
  92. package/dist/generators/indices/import/types.generator.d.ts +0 -9
  93. package/dist/generators/indices/import/types.generator.js +0 -242
  94. package/dist/generators/indices/repositories.generator.d.ts +0 -9
  95. package/dist/generators/indices/repositories.generator.js +0 -25
  96. package/dist/generators/indices/routes.generator.d.ts +0 -9
  97. package/dist/generators/indices/routes.generator.js +0 -29
  98. package/dist/generators/indices/seed-migration.generator.d.ts +0 -9
  99. package/dist/generators/indices/seed-migration.generator.js +0 -36
  100. package/dist/generators/indices/seed-template.generator.d.ts +0 -9
  101. package/dist/generators/indices/seed-template.generator.js +0 -80
  102. package/dist/generators/indices/testids.generator.d.ts +0 -7
  103. package/dist/generators/indices/testids.generator.js +0 -71
  104. package/dist/generators/indices/types.generator.d.ts +0 -10
  105. package/dist/generators/indices/types.generator.js +0 -35
  106. package/dist/generators/indices/update/actiontypes.generator.d.ts +0 -9
  107. package/dist/generators/indices/update/actiontypes.generator.js +0 -49
  108. package/dist/generators/indices/update/module.generator.d.ts +0 -9
  109. package/dist/generators/indices/update/module.generator.js +0 -41
  110. package/dist/generators/indices/update/service.generator.d.ts +0 -9
  111. package/dist/generators/indices/update/service.generator.js +0 -34
  112. package/dist/generators/indices/view/module.generator.d.ts +0 -9
  113. package/dist/generators/indices/view/module.generator.js +0 -39
  114. package/dist/generators/indices/view/service.generator.d.ts +0 -9
  115. package/dist/generators/indices/view/service.generator.js +0 -34
  116. package/dist/generators/models/admin.page.generator.d.ts +0 -7
  117. package/dist/generators/models/admin.page.generator.js +0 -74
  118. package/dist/generators/models/export/encoder.generator.d.ts +0 -9
  119. package/dist/generators/models/export/encoder.generator.js +0 -51
  120. package/dist/generators/models/import/decoder.generator.d.ts +0 -9
  121. package/dist/generators/models/import/decoder.generator.js +0 -148
  122. package/dist/generators/models/react/context.generator.d.ts +0 -9
  123. package/dist/generators/models/react/context.generator.js +0 -71
  124. package/dist/generators/models/react/index.d.ts +0 -10
  125. package/dist/generators/models/react/index.js +0 -31
  126. package/dist/generators/models/react/library.generator.d.ts +0 -10
  127. package/dist/generators/models/react/library.generator.js +0 -94
  128. package/dist/generators/models/react/lookup.generator.d.ts +0 -9
  129. package/dist/generators/models/react/lookup.generator.js +0 -175
  130. package/dist/generators/models/react/modals.generator.d.ts +0 -23
  131. package/dist/generators/models/react/modals.generator.js +0 -710
  132. package/dist/generators/models/repository.generator.d.ts +0 -9
  133. package/dist/generators/models/repository.generator.js +0 -955
  134. package/dist/generators/models/route.generator.d.ts +0 -9
  135. package/dist/generators/models/route.generator.js +0 -92
  136. package/dist/generators/models/seed.generator.d.ts +0 -21
  137. package/dist/generators/models/seed.generator.js +0 -285
  138. package/dist/generators/models/stub.generator.d.ts +0 -9
  139. package/dist/generators/models/stub.generator.js +0 -92
  140. package/dist/generators/models/types.generator.d.ts +0 -9
  141. package/dist/generators/models/types.generator.js +0 -125
  142. package/dist/generators/models/update/service.generator.d.ts +0 -10
  143. package/dist/generators/models/update/service.generator.js +0 -302
  144. package/dist/generators/models/view/service.generator.d.ts +0 -10
  145. package/dist/generators/models/view/service.generator.js +0 -239
  146. package/dist/lib/attributes.d.ts +0 -114
  147. package/dist/lib/attributes.js +0 -2
  148. package/dist/lib/exports.d.ts +0 -45
  149. package/dist/lib/exports.js +0 -90
  150. package/dist/lib/imports.d.ts +0 -65
  151. package/dist/lib/imports.js +0 -114
  152. package/dist/lib/meta.d.ts +0 -1191
  153. package/dist/lib/meta.js +0 -434
  154. package/dist/lib/schema/fields.d.ts +0 -46
  155. package/dist/lib/schema/fields.js +0 -62
  156. package/dist/lib/schema/schema.d.ts +0 -466
  157. package/dist/lib/schema/schema.js +0 -18
  158. package/dist/lib/schema/types.d.ts +0 -201
  159. package/dist/lib/schema/types.js +0 -112
  160. package/dist/lib/serializer.d.ts +0 -15
  161. package/dist/lib/serializer.js +0 -24
  162. package/dist/lib/test-id-collector.d.ts +0 -42
  163. package/dist/lib/test-id-collector.js +0 -53
  164. package/dist/lib/types.d.ts +0 -7
  165. package/dist/lib/types.js +0 -13
  166. package/dist/lib/typescript.d.ts +0 -5
  167. package/dist/lib/typescript.js +0 -22
  168. package/dist/lib/utils/ast.d.ts +0 -29
  169. package/dist/lib/utils/ast.js +0 -23
  170. package/dist/lib/utils/error.d.ts +0 -17
  171. package/dist/lib/utils/error.js +0 -52
  172. package/dist/lib/utils/file.d.ts +0 -10
  173. package/dist/lib/utils/file.js +0 -56
  174. package/dist/lib/utils/jsdoc.d.ts +0 -9
  175. package/dist/lib/utils/jsdoc.js +0 -37
  176. package/dist/lib/utils/logger.d.ts +0 -17
  177. package/dist/lib/utils/logger.js +0 -12
  178. package/dist/lib/utils/string.d.ts +0 -40
  179. package/dist/lib/utils/string.js +0 -187
  180. package/dist/lib/utils/types.d.ts +0 -12
  181. package/dist/lib/utils/types.js +0 -2
  182. package/dist/lib/zod.d.ts +0 -8
  183. package/dist/lib/zod.js +0 -60
  184. package/dist/prisma/attributes.d.ts +0 -21
  185. package/dist/prisma/attributes.js +0 -175
  186. package/dist/prisma/client-path.d.ts +0 -7
  187. package/dist/prisma/client-path.js +0 -29
  188. package/dist/prisma/parse.d.ts +0 -12
  189. package/dist/prisma/parse.js +0 -452
@@ -1,955 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateRepository = void 0;
4
- const imports_1 = require("../../lib/imports");
5
- const meta_1 = require("../../lib/meta");
6
- const fields_1 = require("../../lib/schema/fields");
7
- const schema_1 = require("../../lib/schema/schema");
8
- const types_1 = require("../../lib/schema/types");
9
- const types_2 = require("../../lib/types");
10
- const jsdoc_1 = require("../../lib/utils/jsdoc");
11
- const string_1 = require("../../lib/utils/string");
12
- /**
13
- * Generates repository data structure for a given model.
14
- */
15
- function generateRepository({ model, meta }) {
16
- const { idField } = model;
17
- const schemaMeta = (0, meta_1.getSchemaMetadata)({ config: model.schemaConfig });
18
- const imports = imports_1.ImportsGenerator.from(meta.data.repository.location.path).addImports({
19
- [schemaMeta.data.repository.typeFilePath]: (0, types_1.toAnnotatedTypeName)(schemaMeta.data.repository.typeName),
20
- [meta.types.importPath]: [
21
- (0, types_1.toAnnotatedTypeName)(model.typeName),
22
- (0, types_1.toAnnotatedTypeName)(model.brandedIdType),
23
- meta.types.toBrandedIdTypeFnName,
24
- meta.types.dto.create,
25
- meta.types.dto.update,
26
- meta.types.dto.upsert,
27
- meta.types.dto.clone,
28
- ],
29
- [schemaMeta.actions.execution.interfaceLocation.import]: [schemaMeta.actions.execution.interface],
30
- });
31
- const idBlocks = generateIdBlocks({ model, meta });
32
- const defaultValueBlocks = generateDefaultBlocks({ model, meta });
33
- const uniqueStringFieldsBlocks = generateUniqueFieldsBlocks({ model, meta });
34
- const maxLengthBlocks = generateMaxLengthBlocks({ model, meta });
35
- const validationBlocks = generateValidationBlocks({ model, meta });
36
- const indexBlocks = generateIndexBlocks({ model, imports });
37
- const relationsBlocks = generateRelationsBlocks({ model, meta, imports });
38
- imports.addImports({
39
- [meta.types.importPath]: [meta.types.zodDecoderFnNames.fromDatabase],
40
- [schemaMeta.backendModules.db.databaseService.location.import]: [schemaMeta.backendModules.db.databaseService.name],
41
- [schemaMeta.backendModules.db.typesImportPath]: [(0, types_1.toAnnotatedTypeName)((0, types_1.toTypeName)(`${model.sourceName} as DbType`))],
42
- [(0, types_1.toPackageName)('@postxl/runtime')]: [(0, types_1.toFunctionName)('format'), (0, types_1.toFunctionName)('pluralize')],
43
- });
44
- const userRepositorySpecificBlocks = generateUserRepositorySpecificBlocks_InDatabase({ model, meta, imports });
45
- return `
46
- import { Injectable, Logger } from '@nestjs/common'
47
- ${idBlocks.libraryImports}
48
- ${imports.generate()}
49
-
50
- @Injectable()
51
- export class ${meta.data.repository.className} implements Repository<${model.typeName}, ${idField.unbrandedTypeName}> {
52
- protected data: Map<${model.brandedIdType}, ${model.typeName}> = new Map()
53
- protected logger = new Logger(${meta.data.repository.className}.name)
54
-
55
- ${relationsBlocks.mapDeclarations.join('\n')}
56
-
57
- ${defaultValueBlocks.publicVariableDeclaration}
58
-
59
- ${idBlocks.generateNextIdFunctionName}
60
-
61
- protected uniqueIds = {
62
- ${uniqueStringFieldsBlocks.mapDeclarations.join(',\n')}
63
- }
64
-
65
- ${indexBlocks.nestedMapDeclarations.join('\n')}
66
-
67
- ${userRepositorySpecificBlocks.rootUserNameConst}
68
-
69
- ${userRepositorySpecificBlocks.getterBlock}
70
-
71
- constructor(protected db: ${schemaMeta.backendModules.db.databaseService.name}) {}
72
-
73
- public async init() {
74
- this.data.clear()
75
-
76
- ${relationsBlocks.clearCode.join('\n')}
77
-
78
- ${uniqueStringFieldsBlocks.clearCode.join('\n')}
79
-
80
- ${defaultValueBlocks.init.resetCode}
81
-
82
- ${indexBlocks.initCode.join('\n')}
83
-
84
- const data = await this.db.${meta.data.repository.getMethodFnName}.findMany({})
85
-
86
- for (const rawItem of data) {
87
- const item = this.${meta.data.repository.decoderFnName}(rawItem)
88
- this.set(item)
89
-
90
- ${defaultValueBlocks.init.setCode}
91
- }
92
-
93
- ${idBlocks.initCode}
94
-
95
- ${defaultValueBlocks.init.checkCode}
96
-
97
- ${userRepositorySpecificBlocks.initCall}
98
-
99
- this.logger.log(\`\${format(this.data.size)} \${pluralize('${model.typeName}', this.data.size)} loaded\`)
100
- ${indexBlocks.initLogCode.join('\n')}
101
- }
102
-
103
- ${userRepositorySpecificBlocks.rootUserInitializeBlock}
104
-
105
- // NOTE: The current implementation is synchronous, but it needs to be async to conform to the interface.
106
- // eslint-disable-next-line @typescript-eslint/require-await
107
- public async get(id: ${model.brandedIdType} | null): Promise<${model.typeName} | null> {
108
- if (id === null) {
109
- return null
110
- }
111
- return this.data.get(id) ?? null
112
- }
113
-
114
- // NOTE: The current implementation is synchronous, but it needs to be async to conform to the interface.
115
- // eslint-disable-next-line @typescript-eslint/require-await
116
- public async getAll(): Promise<Map<${model.brandedIdType}, ${model.typeName}>> {
117
- return new Map(this.data)
118
- }
119
-
120
- // NOTE: The current implementation is synchronous, but it needs to be async to conform to the interface.
121
- // eslint-disable-next-line @typescript-eslint/require-await
122
- public async getAllAsArray(): Promise<${model.typeName}[]> {
123
- return Array.from(this.data.values())
124
- }
125
-
126
- ${indexBlocks.getterFunctions.join('\n')}
127
-
128
- ${uniqueStringFieldsBlocks.getByFunctions.join('\n')}
129
-
130
- public async filter(predicate: (item: ${model.typeName}) => boolean): Promise<${model.typeName}[]> {
131
- return (await this.getAllAsArray()).filter(predicate)
132
- }
133
-
134
- public async findFirst(predicate: (item: ${model.typeName}) => boolean): Promise<${model.typeName} | null> {
135
- return (await this.getAllAsArray()).find(predicate) ?? null
136
- }
137
-
138
- // NOTE: The current implementation is synchronous, but it needs to be async to conform to the interface.
139
- // eslint-disable-next-line @typescript-eslint/require-await
140
- public async count(): Promise<number> {
141
- return this.data.size
142
- }
143
-
144
- ${(0, jsdoc_1.toJsDocComment)([
145
- `Checks that item has the ${idField.name} field.`,
146
- `In case none exists, ${idBlocks.verifyFunctionComment}`,
147
- uniqueStringFieldsBlocks.verifyFunctionComment,
148
- maxLengthBlocks.verifyFunctionComment,
149
- ])}
150
- private verifyItem(
151
- item: ${idBlocks.verifyFunctionParameterType}
152
- ): ${idBlocks.createFunctionParameterType} {
153
- ${idBlocks.verifyCode}
154
-
155
- ${maxLengthBlocks.verifyCode.join('\n')}
156
-
157
- ${uniqueStringFieldsBlocks.verifyCode.join('\n')}
158
-
159
- ${validationBlocks.verifyCode.join('\n')}
160
-
161
- return {
162
- ${idField.name},
163
- ${model.fields
164
- .filter((f) => f.kind !== 'id' && !f.attributes.isReadonly)
165
- .map((f) => `${f.name}: item.${f.name}`)
166
- .join(',\n')}
167
- }
168
- }
169
-
170
- private toCreateItem(item: ${idBlocks.createFunctionParameterType}) {
171
- return {
172
- ${model.fields
173
- .filter((f) => !f.attributes.isReadonly || f.kind === 'id')
174
- .map((f) => `${f.sourceName}: item.${f.name}`)
175
- .join(',\n')},
176
- }
177
- }
178
-
179
- ${(0, jsdoc_1.toJsDocComment)([`Creates a new ${meta.userFriendlyName} and returns it.`])}
180
- public async create(
181
- { item, execution }: { item: ${meta.types.dto.create}, execution: ${schemaMeta.actions.execution.interface} }
182
- ): Promise<${model.typeName}> {
183
- const mutationId = await execution.startCreateMutation({
184
- model: '${meta.actions.actionScopeConstType}',
185
- createObject: item
186
- })
187
-
188
- try {
189
- const newItem = this.${meta.data.repository.decoderFnName}(
190
- await this.db.${meta.data.repository.getMethodFnName}.create({
191
- data: this.toCreateItem(this.verifyItem(item)),
192
- }),
193
- )
194
-
195
- this.set(newItem)
196
- await execution.finishCreateMutation({ mutationId, createdObject: newItem, entityId: newItem.id })
197
- return newItem
198
- } catch (error) {
199
- await execution.errorMutation({ mutationId, error })
200
- throw error
201
- }
202
- }
203
-
204
- ${(0, jsdoc_1.toJsDocComment)([`Creates multiple new ${meta.userFriendlyNamePlural} and returns them.`])}
205
- public async createMany(
206
- {items, execution}: { items: ${meta.types.dto.create}[], execution: ${schemaMeta.actions.execution.interface} }
207
- ): Promise<${model.typeName}[]> {
208
- const mutationId = await execution.startCreateManyMutation({
209
- model: '${meta.actions.actionScopeConstType}',
210
- createObjects: items
211
- })
212
-
213
- try {
214
- const newItems = items.map((item) => this.verifyItem(item))
215
-
216
- await this.db.${meta.data.repository.getMethodFnName}.createMany({ data: newItems.map(i => this.toCreateItem(i)) })
217
-
218
- const dbItems = await this.db.${meta.data.repository.getMethodFnName}.findMany({
219
- where: {
220
- ${model.idField.sourceName}: { in: newItems.map(i => i.${model.idField.name}) }
221
- }
222
- })
223
-
224
- const result = dbItems.map((item) => this.${meta.data.repository.decoderFnName}(item))
225
-
226
- for (const item of result) {
227
- this.set(item)
228
- }
229
-
230
- await execution.finishCreateManyMutation({
231
- mutationId,
232
- createdObjects: result,
233
- entityIds: newItems.map((i) => i.id),
234
- })
235
- return result
236
- } catch (error) {
237
- await execution.errorMutation({ mutationId, error })
238
- throw error
239
- }
240
- }
241
-
242
- ${(0, jsdoc_1.toJsDocComment)([`Updates a ${meta.userFriendlyName} and returns it.`])}
243
- public async update(
244
- { item, execution }: { item: ${meta.types.dto.update}, execution: ${schemaMeta.actions.execution.interface} }
245
- ): Promise<${model.typeName}> {
246
- const existingItem = await this.get(item.${idField.name})
247
- if (!existingItem) {
248
- throw new Error(\`Could not update ${meta.userFriendlyName} with id \${item.id}. Not found!\`)
249
- }
250
-
251
- const mutationId = await execution.startUpdateMutation({
252
- model: '${meta.actions.actionScopeConstType}',
253
- entityId: item.id,
254
- sourceObject: existingItem,
255
- updateObject: item,
256
- })
257
- try {
258
- ${maxLengthBlocks.updateCode.join('\n')}
259
-
260
- ${uniqueStringFieldsBlocks.updateCode.join('\n')}
261
-
262
- const newItem = this.${meta.data.repository.decoderFnName}(
263
- await this.db.${meta.data.repository.getMethodFnName}.update({
264
- where: {
265
- ${idField.sourceName}: item.${idField.name},
266
- },
267
- data: {
268
- ${[...model.fields.values()]
269
- .filter((f) => f.kind !== 'id' && !f.attributes.isReadonly)
270
- .map((f) => f.isRequired
271
- ? `${f.sourceName}: item.${f.name} ?? existingItem.${f.name}`
272
- : `${f.sourceName}: item.${f.name}`)
273
- .join(',\n')}
274
- },
275
- }),
276
- )
277
-
278
- this.remove(existingItem)
279
- this.set(newItem)
280
-
281
- await execution.finishUpdateMutation({ mutationId, updatedObject: newItem })
282
- return newItem
283
- } catch (error) {
284
- await execution.errorMutation({ mutationId, error })
285
- throw error
286
- }
287
- }
288
-
289
- ${(0, jsdoc_1.toJsDocComment)([`Updates multiple ${meta.userFriendlyNamePlural} and returns them.`])}
290
- public async updateMany(
291
- { items, execution }: { items: ${meta.types.dto.update}[], execution: ${schemaMeta.actions.execution.interface} }
292
- ): Promise<${model.typeName}[]> {
293
- const result: ${model.typeName}[] = []
294
- for (const item of items) {
295
- try {
296
- const updated = await this.update({ item, execution })
297
- result.push(updated)
298
- } catch {
299
- /* empty */
300
- }
301
- }
302
- return result
303
- }
304
-
305
- ${(0, jsdoc_1.toJsDocComment)([`Creates or updates a ${meta.userFriendlyName} and returns it.`])}
306
- public async upsert(
307
- { item, execution }: { item: ${meta.types.dto.upsert}, execution: ${schemaMeta.actions.execution.interface} }
308
- ): Promise<${model.typeName}> {
309
- const existingItem = item.${model.idField.name} ? (await this.get(item.${model.idField.name})) : null
310
- if (existingItem) {
311
- return this.update({ item: item as ${meta.types.dto.update}, execution })
312
- } else {
313
- return this.create({ item: item as ${meta.types.dto.create}, execution })
314
- }
315
- }
316
-
317
- ${(0, jsdoc_1.toJsDocComment)([`Creates or updates multiple ${meta.userFriendlyNamePlural} and returns them.`])}
318
- public async upsertMany(
319
- { items, execution }: { items: ${meta.types.dto.upsert}[], execution: ${schemaMeta.actions.execution.interface} }
320
- ): Promise<${model.typeName}[]> {
321
- const result: ${model.typeName}[] = []
322
- for (const item of items) {
323
- try {
324
- const updated = await this.upsert({ item, execution })
325
- result.push(updated)
326
- } catch {
327
- /* empty */
328
- }
329
- }
330
- return result
331
- }
332
-
333
- ${(0, jsdoc_1.toJsDocComment)([`Deletes a ${meta.userFriendlyName} and returns its id.`])}
334
- public async delete(
335
- { id, execution }: { id: ${model.brandedIdType}, execution: ${schemaMeta.actions.execution.interface} }
336
- ): Promise<${model.brandedIdType}> {
337
- const existingItem = await this.get(id)
338
- if (!existingItem) {
339
- throw new Error(\`Could not delete ${model.typeName} with id \${id}. Not found!\`)
340
- }
341
-
342
- const mutationId = await execution.startDeleteMutation({
343
- model: '${meta.actions.actionScopeConstType}',
344
- entityId: id,
345
- sourceObject: existingItem,
346
- })
347
- try {
348
-
349
- await this.db.${meta.data.repository.getMethodFnName}.delete({ where: { ${idField.sourceName}:id } })
350
-
351
- this.remove(existingItem)
352
- await execution.finishDeleteMutation({ mutationId })
353
- return id
354
- } catch (error) {
355
- await execution.errorMutation({ mutationId, error })
356
- throw error
357
- }
358
- }
359
-
360
- ${(0, jsdoc_1.toJsDocComment)([`Deletes multiple ${meta.userFriendlyNamePlural} and returns their ids.`])}
361
- public async deleteMany(
362
- { ids, execution }: { ids: ${model.brandedIdType}[], execution: ${schemaMeta.actions.execution.interface} }
363
- ): Promise<${model.brandedIdType}[]> {
364
- const deletedIds: ${model.brandedIdType}[] = []
365
- for (const id of ids) {
366
- try {
367
- await this.delete({ id, execution })
368
- deletedIds.push(id)
369
- } catch {
370
- /* empty */
371
- }
372
- }
373
- return deletedIds
374
- }
375
-
376
- ${(0, jsdoc_1.toJsDocComment)([`Creates a new ${meta.userFriendlyName} shallow clone and returns it.`])}
377
- public async clone({ id, item, execution }: {
378
- id: ${model.brandedIdType}
379
- item: (item: ${model.typeName}) => ${meta.types.dto.clone},
380
- execution: ${schemaMeta.actions.execution.interface}
381
- }): Promise<${model.typeName}> {
382
- const source = await this.get(id)
383
- if (!source) {
384
- throw new Error(\`${meta.userFriendlyName} with id \${id} not found\`)
385
- }
386
-
387
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
388
- const { id: _, ...data } = { ...source, ...item(source) }
389
-
390
- return await this.create({ item: data, execution })
391
- }
392
-
393
- ${relationsBlocks.getterFunctions.join('\n')}
394
-
395
- ${maxLengthBlocks.ensureMaxLengthFunctions.join('\n')}
396
-
397
- ${uniqueStringFieldsBlocks.ensureUniqueFunctions.join('\n\n')}
398
-
399
- /**
400
- * Function that adds/updates a given item to the internal data store, indexes, etc.
401
- */
402
- private set(item: ${model.typeName}): void {
403
- ${idBlocks.setCode}
404
-
405
- this.data.set(item.id, item)
406
-
407
- ${uniqueStringFieldsBlocks.setCode.join('\n')}
408
-
409
- ${relationsBlocks.setCode.join('\n')}
410
-
411
- ${indexBlocks.setCode.join('\n')}
412
- }
413
-
414
- /**
415
- * Function that removes a given item from the internal data store, indexes, etc.
416
- */
417
- private remove(item: ${model.typeName}): void {
418
- this.data.delete(item.id)
419
- ${uniqueStringFieldsBlocks.removeCode.join('\n')}
420
-
421
- ${relationsBlocks.removeCode.join('\n')}
422
-
423
- ${indexBlocks.removeCode.join('\n')}
424
- }
425
-
426
- /**
427
- * Utility function that converts a given Database object to a TypeScript model instance
428
- */
429
- private ${meta.data.repository.decoderFnName}(
430
- item: Pick<DbType, ${model.fields.map((f) => `'${f.sourceName}'`).join(' | ')}>
431
- ): ${model.typeName} {
432
- return ${meta.types.zodDecoderFnNames.fromDatabase}.parse({
433
- ${model.fields.map((f) => `${f.name}: item.${f.sourceName}`).join(',\n')}
434
- })
435
- }
436
- }
437
- `;
438
- }
439
- exports.generateRepository = generateRepository;
440
- function generateUserRepositorySpecificBlocks_InDatabase({ model, meta, imports, }) {
441
- if (model.name !== 'User') {
442
- return {
443
- rootUserNameConst: '',
444
- getterBlock: '',
445
- initCall: '',
446
- rootUserInitializeBlock: '',
447
- };
448
- }
449
- const { rootUserId, rootUserValue } = generateSharedRootUserBlocks({ model, meta, imports });
450
- return {
451
- rootUserNameConst: `public static ROOT_USER_ID = ${meta.types.toBrandedIdTypeFnName}(${rootUserId})`,
452
- getterBlock: `
453
- // We initialize the root user in the init() function
454
- private _rootUser!: ${meta.types.typeName}
455
- public get rootUser(): ${meta.types.typeName} {
456
- return this._rootUser
457
- }`,
458
- initCall: `await this.initializeRootUser()`,
459
- rootUserInitializeBlock: `
460
- private async initializeRootUser(): Promise<void> {
461
- const existingRootUser = await this.get(${meta.data.repository.className}.ROOT_USER_ID)
462
- if (existingRootUser) {
463
- this._rootUser = existingRootUser
464
- return
465
- }
466
-
467
- const rawUser = await this.db.user.create({
468
- data: { ${rootUserValue} },
469
- })
470
- const newRootUser = this.toUser(rawUser)
471
- this.set(newRootUser)
472
- this._rootUser = newRootUser
473
- }`,
474
- };
475
- }
476
- function generateSharedRootUserBlocks({ model, meta, imports, }) {
477
- var _a;
478
- const providedDefault = model.attributes.systemUser;
479
- const assignments = [];
480
- let rootUserId = '';
481
- for (const field of model.fields) {
482
- let value = undefined;
483
- if (providedDefault && field.name in providedDefault) {
484
- value = providedDefault[field.name];
485
- }
486
- else if ((_a = field.attributes.examples) === null || _a === void 0 ? void 0 : _a.length) {
487
- value = field.attributes.examples[0];
488
- }
489
- else if (!field.isRequired) {
490
- value = null;
491
- }
492
- else if (field.kind === 'id') {
493
- if (field.unbrandedTypeName === 'string') {
494
- value = 'rootId';
495
- }
496
- else if (field.unbrandedTypeName === 'number') {
497
- value = -1;
498
- }
499
- else {
500
- throw new Error(`Could not generate root user: Unsupported id type ${field.unbrandedTypeName}!`);
501
- }
502
- }
503
- else {
504
- if (field.isRequired && !field.attributes.isCreatedAt && !field.attributes.isUpdatedAt) {
505
- throw new Error(`Could not generate root user: No value for field ${field.name} provided!`);
506
- }
507
- else {
508
- continue;
509
- }
510
- }
511
- switch (field.kind) {
512
- case 'id': {
513
- if (field.unbrandedTypeName === 'string') {
514
- value = `'${value}'`;
515
- }
516
- else if (field.unbrandedTypeName === 'number') {
517
- value = `${value}`;
518
- }
519
- else {
520
- throw new Error(`Could not generate root user: Unsupported id type ${field.unbrandedTypeName}!`);
521
- }
522
- rootUserId = value;
523
- break;
524
- }
525
- case 'scalar': {
526
- if (value === null) {
527
- break;
528
- }
529
- switch (field.tsTypeName) {
530
- case 'string': {
531
- value = `'${value}'`;
532
- break;
533
- }
534
- case 'number': {
535
- value = `${value}`;
536
- break;
537
- }
538
- case 'boolean': {
539
- value = value ? 'true' : 'false';
540
- break;
541
- }
542
- case 'Date': {
543
- value = `${value}`;
544
- break;
545
- }
546
- default: {
547
- throw new Error(`Could not generate root user: Unsupported scalar type ${field.tsTypeName}!`);
548
- }
549
- }
550
- break;
551
- }
552
- case 'relation': {
553
- if (value === null) {
554
- break;
555
- }
556
- if (field.unbrandedTypeName === 'string') {
557
- value = `'${value}'`;
558
- }
559
- else if (field.unbrandedTypeName === 'number') {
560
- value = `${value}`;
561
- }
562
- else {
563
- throw new Error(`Could not generate root user: Unsupported relation type ${field.unbrandedTypeName}!`);
564
- }
565
- break;
566
- }
567
- case 'enum': {
568
- if (value === null) {
569
- break;
570
- }
571
- imports.addImport({
572
- from: meta.types.importPath,
573
- items: [field.enumerator.name],
574
- });
575
- value = `${field.enumerator.name}.${value}`;
576
- break;
577
- }
578
- default: {
579
- throw new types_2.ExhaustiveSwitchCheck(field);
580
- }
581
- }
582
- assignments.push(`${field.name}: ${value}`);
583
- }
584
- return {
585
- rootUserId,
586
- rootUserValue: assignments.join(', '),
587
- };
588
- }
589
- /**
590
- * Generates code chunks responsible for verifying the ID validity of a model instance and generating the id
591
- * value of a model with auto-generated id.
592
- */
593
- function generateIdBlocks({ model, meta }) {
594
- const idField = model.idField;
595
- if (!idField.isGenerated) {
596
- return {
597
- libraryImports: '',
598
- generateNextIdFunctionName: '',
599
- initCode: '',
600
- verifyFunctionComment: `an error is thrown as field has no default setting in schema.`,
601
- verifyFunctionParameterType: meta.types.dto.create,
602
- verifyCode: `
603
- if (item.${idField.name} === undefined) {
604
- throw new Error('Id field ${idField.name} is required!')
605
- }
606
- const ${idField.name} = ${meta.types.toBrandedIdTypeFnName}(item.${idField.name})`,
607
- setCode: '',
608
- createFunctionParameterType: model.typeName,
609
- };
610
- }
611
- if (idField.schemaType === 'Int') {
612
- const generatedFields = model.fields.filter((f) => f.kind === 'id' || f.attributes.isReadonly);
613
- const readonlyFields = model.fields.filter((f) => f.attributes.isReadonly && f.kind !== 'id');
614
- return {
615
- libraryImports: '',
616
- generateNextIdFunctionName: `
617
- protected currentMaxId = 0
618
- public generateNextId(): ${model.brandedIdType} {
619
- return ${meta.types.toBrandedIdTypeFnName}(++this.currentMaxId)
620
- }`,
621
- initCode: `this.currentMaxId = (await this.db.${meta.data.repository.getMethodFnName}.aggregate({ _max: { ${idField.sourceName}: true } }))._max.${idField.sourceName} ?? 0`,
622
- verifyFunctionComment: 'the id is generated by increasing the highest former id and assigned to the item.',
623
- // prettier-ignore
624
- verifyFunctionParameterType: `(Omit<${model.typeName}, ${generatedFields.map((f) => `'${f.name}'`).join(' | ')}> & Partial<{${idField.name}: ${idField.unbrandedTypeName}}>)`,
625
- verifyCode: `const ${idField.name} = (item.${idField.name} !== undefined) ? ${meta.types.toBrandedIdTypeFnName}(item.${idField.name}) : this.generateNextId()`,
626
- createFunctionParameterType:
627
- // NOTE: In case we have readonly fields, we need to omit them from the create function.
628
- readonlyFields.length === 0
629
- ? model.typeName
630
- : `Omit<${model.typeName}, ${readonlyFields.map((f) => `'${f.name}'`).join(' |')}>`,
631
- setCode: `if (item.id > this.currentMaxId) { this.currentMaxId = item.id }`,
632
- };
633
- }
634
- if (idField.schemaType === 'String') {
635
- const dbGeneratedFields = model.fields.filter((f) => f.kind === 'id' || f.attributes.isReadonly);
636
- const readonlyFields = model.fields.filter((f) => f.attributes.isReadonly && f.kind !== 'id');
637
- return {
638
- libraryImports: `import { randomUUID } from 'crypto'`,
639
- generateNextIdFunctionName: `
640
- public generateNextId(): ${model.brandedIdType} {
641
- return ${meta.types.toBrandedIdTypeFnName}(randomUUID())
642
- }`,
643
- initCode: '',
644
- verifyFunctionComment: 'a new UUID is generated and assigned to the item.',
645
- // prettier-ignore
646
- verifyFunctionParameterType: `(Omit<${model.typeName}, ${dbGeneratedFields.map((f) => `'${f.name}'`).join(' | ')}> & Partial<{${idField.name}: ${idField.unbrandedTypeName}}>)`,
647
- verifyCode: `const ${idField.name} = (item.${idField.name} !== undefined) ? ${meta.types.toBrandedIdTypeFnName}(item.${idField.name}) : this.generateNextId()`,
648
- createFunctionParameterType:
649
- // NOTE: In case we have readonly fields, we need to omit them from the create function.
650
- readonlyFields.length === 0
651
- ? model.typeName
652
- : `Omit<${model.typeName}, ${readonlyFields.map((f) => `'${f.name}'`).join(' |')}>`,
653
- setCode: '',
654
- };
655
- }
656
- throw new Error(`Repository block only supports Id generation for number and strings! Found ${idField.schemaType} for model ${model.name} instead!`);
657
- }
658
- /**
659
- * Returns the code chunks that define the default value property and its initialization.
660
- */
661
- function generateDefaultBlocks({ model, meta }) {
662
- const defaultField = model.defaultField;
663
- if (!defaultField) {
664
- return {
665
- init: {
666
- resetCode: '',
667
- setCode: '',
668
- checkCode: '',
669
- },
670
- publicVariableDeclaration: '',
671
- };
672
- }
673
- return {
674
- init: {
675
- resetCode: `
676
- // We re-initialize the default value to undefined so the check for exactly one item does not fail upon re-initialization
677
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-non-null-assertion
678
- this.defaultValue = undefined!
679
- `,
680
- setCode: `
681
- if (item.${defaultField.name}) {
682
- if (this.defaultValue) {
683
- console.warn(\`More than one default ${meta.userFriendlyName} found! \${this.defaultValue.id} and \${item.id}\`)
684
- }
685
- this.defaultValue = item
686
- }`,
687
- checkCode: `
688
- if (!this.defaultValue) {
689
- throw new Error('No default ${meta.userFriendlyName} found!')
690
- }`,
691
- },
692
- publicVariableDeclaration: `
693
- // We can safely skip the assignment here as this is done in the init function
694
- public defaultValue!: ${model.typeName}
695
- `,
696
- };
697
- }
698
- /**
699
- * Generates code chunks that enforce the uniqueness of a given field.
700
- */
701
- function generateUniqueFieldsBlocks({ model }) {
702
- const fields = model.fields.filter(fields_1.isUniqueStringField);
703
- const result = {
704
- mapDeclarations: [],
705
- getByFunctions: [],
706
- clearCode: [],
707
- verifyFunctionComment: '',
708
- verifyCode: [],
709
- updateCode: [],
710
- ensureUniqueFunctions: [],
711
- setCode: [],
712
- removeCode: [],
713
- };
714
- for (const f of fields) {
715
- const returnType = `string${f.isRequired ? '' : ' | null'}`;
716
- result.mapDeclarations.push(`'${f.name}': new Map<string, ${model.typeName}>()`);
717
- result.getByFunctions.push(`
718
- // NOTE: The current implementation is synchronous, but it needs to be async to conform to the interface.
719
- // eslint-disable-next-line @typescript-eslint/require-await
720
- public async getBy${(0, string_1.toPascalCase)(f.name)}(${f.name}: ${returnType}): Promise<${model.typeName} | undefined> {
721
- ${f.isRequired ? '' : `if (${f.name} === null) { return undefined }`}
722
- return this.uniqueIds.${f.name}.get(${(0, string_1.toCamelCase)(f.name)})
723
- }`);
724
- result.clearCode.push(`this.uniqueIds.${f.name}.clear()`);
725
- result.verifyCode.push(`this.${getEnsureUniqueFnName(f)}(item)`);
726
- result.updateCode.push(`
727
- if (item.${f.name} !== undefined && existingItem.${f.name} !== item.${f.name}) {
728
- this.${getEnsureUniqueFnName(f)}(item)
729
- }`);
730
- result.ensureUniqueFunctions.push(`
731
- /**
732
- * Utility function that ensures that the ${f.name} field is unique
733
- */
734
- private ${getEnsureUniqueFnName(f)}(item: { ${f.name}?: ${returnType} }) {
735
- if (!item.${f.name}) {
736
- return
737
- }
738
- if (!this.uniqueIds.${f.name}.has(item.${f.name})) {
739
- return
740
- }
741
- let counter = 1
742
-
743
- let ${f.name}: string
744
- const source${f.name} =${!f.attributes.maxLength ? `item.${f.name}` : `item.${f.name}.substring(0, ${f.attributes.maxLength - 5})`}
745
-
746
- do {
747
- ${f.name} = \`\${source${f.name}} (\${++counter})\`
748
- } while (this.uniqueIds.${f.name}.has(${f.name}))
749
-
750
- this.logger.log(\`${model.typeName} ${f.name} "\${item.${f.name}}" already exists. Renaming to "\${${f.name}}")\`)
751
- item.${f.name} = ${f.name}
752
- }`);
753
- if (f.isRequired) {
754
- result.setCode.push(`this.uniqueIds.${f.name}.set(item.${f.name}, item)`);
755
- result.removeCode.push(`this.uniqueIds.${f.name}.delete(item.${f.name})`);
756
- }
757
- else {
758
- result.setCode.push(`if (item.${f.name} !== null) { this.uniqueIds.${f.name}.set(item.${f.name}, item) }`);
759
- result.removeCode.push(`if (item.${f.name} !== null) { this.uniqueIds.${f.name}.delete(item.${f.name}) }`);
760
- }
761
- }
762
- if (fields.length > 1) {
763
- result.verifyFunctionComment = `In case a value of the fields ${fields
764
- .map((f) => f.name)
765
- .join(', ')} is not unique, it is renamed.\n`;
766
- }
767
- else if (fields.length === 1) {
768
- result.verifyFunctionComment = `In case the value of the field ${fields[0].name} is not unique, it is renamed.\n`;
769
- }
770
- return result;
771
- }
772
- function getEnsureUniqueFnName(field) {
773
- return `ensureUnique${(0, string_1.toPascalCase)(field.name)}`;
774
- }
775
- function generateMaxLengthBlocks({ model }) {
776
- const fields = model.fields.filter(fields_1.isMaxLengthStringField);
777
- const result = {
778
- verifyFunctionComment: '',
779
- verifyCode: [],
780
- updateCode: [],
781
- ensureMaxLengthFunctions: [],
782
- };
783
- for (const f of fields) {
784
- result.verifyCode.push(`this.${getEnsureMaxLengthFnName(f)}(item)`);
785
- result.updateCode.push(`
786
- if (item.${f.name} !== undefined && existingItem.${f.name} !== item.${f.name}) {
787
- this.${getEnsureMaxLengthFnName(f)}(item)
788
- }`);
789
- result.ensureMaxLengthFunctions.push(`
790
- /**
791
- * Utility function that ensures that the ${f.name} field has a max length of ${f.attributes.maxLength}
792
- */
793
- private ${getEnsureMaxLengthFnName(f)}(item: { ${f.name}?: string }) {
794
- if (!item.${f.name}) {
795
- return
796
- }
797
- if (item.${f.name}.length <= ${f.attributes.maxLength}) {
798
- return
799
- }
800
- item.${f.name} = item.${f.name}.substring(0, ${f.attributes.maxLength - 4}) + \`...\`
801
- }`);
802
- }
803
- if (fields.length > 1) {
804
- result.verifyFunctionComment = `In case a value of the fields ${fields
805
- .map((f) => f.name)
806
- .join(', ')} exceeds its max length, it is truncated.\n`;
807
- }
808
- else if (fields.length === 1) {
809
- result.verifyFunctionComment = `In case the value of the field ${fields[0].name} exceeds its max length, it is truncated.\n`;
810
- }
811
- return result;
812
- }
813
- function getEnsureMaxLengthFnName(field) {
814
- return `ensureMaxLength${(0, string_1.toPascalCase)(field.name)}`;
815
- }
816
- function generateValidationBlocks({ model }) {
817
- const fields = model.fields.filter((f) => f.kind === 'scalar' && f.validation && f.validation.type === 'int');
818
- const result = {
819
- verifyCode: [],
820
- };
821
- for (const f of fields) {
822
- const itemExists = f.isRequired ? '' : `item.${f.name} !== null && `;
823
- result.verifyCode.push(`
824
- // ensure that ${f.name} is an integer
825
- if (${itemExists}item.${f.name} !== Math.floor(item.${f.name})) {
826
- throw new Error(\`Invalid value for field ${f.name}: \${item.${f.name}}. Value must be an integer.\`)
827
- }`);
828
- }
829
- return result;
830
- }
831
- function generateIndexBlocks({ model, imports }) {
832
- const indexes = model.attributes.index ? [getIndexDefinition({ fieldNames: model.attributes.index, model })] : [];
833
- if (indexes.length > 0) {
834
- imports.addImport({ items: [(0, types_1.toClassName)('NestedMap')], from: (0, types_1.toPackageName)('@postxl/runtime') });
835
- }
836
- const result = {
837
- nestedMapDeclarations: [],
838
- initCode: [],
839
- initLogCode: [],
840
- getterFunctions: [],
841
- setCode: [],
842
- removeCode: [],
843
- };
844
- for (const i of indexes) {
845
- result.nestedMapDeclarations.push(`
846
- protected ${i.name} = new NestedMap<${i.fields.map((f) => f.meta.types.brandedIdType).join(',')}, ${model.typeName}>(
847
- ${i.fields.map((f) => `(x) => x.${f.name}`).join(',')}
848
- )`);
849
- result.initCode.push(`this.${i.name}.clear()`);
850
- result.initLogCode.push(`this.logger.log(\`\${this.${i.name}.size} ${model.typeName} loaded into index ${i.name}\`)`);
851
- result.getterFunctions.push(`
852
- public getFrom${indexes.length === 1 ? 'Index' : (0, string_1.toPascalCase)(i.name)}
853
- ({ ${i.fields.map((f) => f.name).join(',')} }: { ${i.fields
854
- .map((f) => `${f.name} : ${f.meta.types.brandedIdType}`)
855
- .join(';')} }): ${model.typeName} | null {
856
- return this.${i.name}.get(${i.fields.map((f) => f.name).join(',')}) ?? null
857
- }`);
858
- result.setCode.push(`this.${i.name}.set(item)`);
859
- result.removeCode.push(`this.${i.name}.delete(item)`);
860
- }
861
- return result;
862
- }
863
- function getIndexDefinition({ fieldNames, model }) {
864
- const fields = fieldNames.map((f) => {
865
- const result = model.fields.find((field) => field.name === f);
866
- if (!result) {
867
- throw new Error(`Index field ${f} not found in model ${model.name}`);
868
- }
869
- if (result.kind !== 'relation') {
870
- throw new Error(`Index field ${f} is not a relation in model ${model.name}`);
871
- }
872
- const meta = (0, meta_1.getModelMetadata)({ model: result.relationToModel });
873
- return Object.assign(Object.assign({}, result), { meta });
874
- });
875
- const [firstField, ...restFields] = fields;
876
- if (!restFields || restFields.length === 0) {
877
- throw new Error(`Index for model ${model.name} must have at least 2 fields!`);
878
- }
879
- return {
880
- fields,
881
- name: (0, string_1.toCamelCase)(`${firstField.name}${restFields.map((f) => (0, string_1.toPascalCase)(f.name)).join('')}Index`),
882
- };
883
- }
884
- function generateRelationsBlocks({ model, imports, }) {
885
- const relations = model.fields.filter(schema_1.isFieldRelation);
886
- const result = {
887
- mapDeclarations: [],
888
- clearCode: [],
889
- getterFunctions: [],
890
- setCode: [],
891
- removeCode: [],
892
- };
893
- for (const r of relations) {
894
- const fieldMeta = (0, meta_1.getFieldMetadata)({ field: r });
895
- const relationModelMeta = (0, meta_1.getModelMetadata)({ model: r.relationToModel });
896
- imports.addTypeImport({
897
- items: [relationModelMeta.types.brandedIdType],
898
- from: relationModelMeta.types.importPath,
899
- });
900
- result.mapDeclarations.push(`protected ${r.name}Map: Map<${relationModelMeta.types.brandedIdType}, Map<${model.brandedIdType}, ${model.typeName}>> = new Map()`);
901
- result.clearCode.push(`this.${r.name}Map.clear()`);
902
- result.getterFunctions.push(`
903
- /**
904
- * Function to retrieve all ${(0, string_1.pluralize)(model.name)} that are related to a ${r.name}
905
- */
906
- // NOTE: The current implementation is synchronous, but it needs to be async to conform to the interface.
907
- // eslint-disable-next-line @typescript-eslint/require-await
908
- public async ${fieldMeta.getByForeignKeyMethodFnName}(
909
- id: ${relationModelMeta.types.brandedIdType}
910
- ): Promise<Map<${model.brandedIdType}, ${model.typeName}>> {
911
- const result = this.${r.name}Map.get(id)
912
- if (!result) {
913
- return new Map<${model.brandedIdType}, ${model.typeName}>()
914
- }
915
- return new Map(result)
916
- }
917
-
918
- /**
919
- * Function to retrieve all ${model.brandedIdType}s that are related to a ${r.name}
920
- */
921
- // NOTE: The current implementation is synchronous, but it needs to be async to conform to the interface.
922
- // eslint-disable-next-line @typescript-eslint/require-await
923
- public async ${fieldMeta.getByForeignKeyIdsMethodFnName}(
924
- id: ${relationModelMeta.types.brandedIdType}
925
- ): Promise<${model.brandedIdType}[]> {
926
- const s = this.${r.name}Map.get(id)
927
- if (!s) {
928
- return []
929
- }
930
-
931
- return Array.from(s.keys())
932
- }`);
933
- result.setCode.push(`
934
- ${!r.isRequired ? `if (item.${r.name}) {` : ''}
935
- let ${r.name}Map = this.${r.name}Map.get(item.${r.name})
936
- if (!${r.name}Map) {
937
- ${r.name}Map = new Map()
938
- this.${r.name}Map.set(item.${r.name}, ${r.name}Map)
939
- }
940
- ${r.name}Map.set(item.id, item)
941
- ${!r.isRequired ? `}` : ''}`);
942
- result.removeCode.push(`
943
- ${!r.isRequired ? `if (item.${r.name}) {` : ''}
944
- const ${r.name}Map = this.${r.name}Map.get(item.${r.name})
945
- if (${r.name}Map) {
946
- ${r.name}Map.delete(item.id)
947
- if (${r.name}Map.size === 0) {
948
- this.${r.name}Map.delete(item.${r.name})
949
- }
950
- }
951
- ${!r.isRequired ? '}' : ''}
952
- `);
953
- }
954
- return result;
955
- }