@postxl/generator 0.69.0 → 0.70.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +1 -20
  2. package/dist/generator.js +41 -40
  3. package/dist/generators/indices/{datamodule.generator.d.ts → data/module.generator.d.ts} +2 -2
  4. package/dist/generators/indices/{datamodule.generator.js → data/module.generator.js} +4 -5
  5. package/dist/generators/indices/{dataservice.generator.d.ts → data/service.generator.d.ts} +2 -2
  6. package/dist/generators/indices/{dataservice.generator.js → data/service.generator.js} +5 -5
  7. package/dist/generators/indices/{data-types.generator.d.ts → data/types.generator.d.ts} +2 -2
  8. package/dist/generators/indices/{data-types.generator.js → data/types.generator.js} +2 -2
  9. package/dist/generators/indices/{importexport-convert-import-functions.generator.d.ts → import-export/importexport-convert-import-functions.generator.d.ts} +2 -2
  10. package/dist/generators/indices/{importexport-convert-import-functions.generator.js → import-export/importexport-convert-import-functions.generator.js} +4 -4
  11. package/dist/generators/indices/{importexport-exporter-class.generator.d.ts → import-export/importexport-exporter-class.generator.d.ts} +2 -2
  12. package/dist/generators/indices/{importexport-exporter-class.generator.js → import-export/importexport-exporter-class.generator.js} +4 -5
  13. package/dist/generators/indices/{importexport-import-service.generator.d.ts → import-export/importexport-import-service.generator.d.ts} +2 -2
  14. package/dist/generators/indices/{importexport-import-service.generator.js → import-export/importexport-import-service.generator.js} +4 -4
  15. package/dist/generators/indices/{importexport-types.generator.d.ts → import-export/importexport-types.generator.d.ts} +2 -2
  16. package/dist/generators/indices/{importexport-types.generator.js → import-export/importexport-types.generator.js} +2 -2
  17. package/dist/generators/indices/{routes-index.generator.js → routes.generator.js} +3 -0
  18. package/dist/generators/indices/seed-template.generator.js +2 -2
  19. package/dist/generators/indices/types.generator.js +13 -1
  20. package/dist/generators/indices/{businesslogic-actiontypes.generator.d.ts → update/actiontypes.generator.d.ts} +2 -2
  21. package/dist/generators/indices/{businesslogic-actiontypes.generator.js → update/actiontypes.generator.js} +3 -3
  22. package/dist/generators/indices/{businesslogic-update-module.generator.d.ts → update/module.generator.d.ts} +2 -2
  23. package/dist/generators/indices/{businesslogic-update-module.generator.js → update/module.generator.js} +2 -2
  24. package/dist/generators/indices/{businesslogic-update-service.generator.d.ts → update/service.generator.d.ts} +2 -2
  25. package/dist/generators/indices/{businesslogic-update-service.generator.js → update/service.generator.js} +2 -2
  26. package/dist/generators/indices/{businesslogic-view-module.generator.d.ts → view/module.generator.d.ts} +2 -2
  27. package/dist/generators/indices/{businesslogic-view-module.generator.js → view/module.generator.js} +2 -2
  28. package/dist/generators/indices/{businesslogic-view-service.generator.d.ts → view/service.generator.d.ts} +2 -2
  29. package/dist/generators/indices/{businesslogic-view-service.generator.js → view/service.generator.js} +2 -2
  30. package/dist/generators/models/importexport-decoder.generator.d.ts +1 -2
  31. package/dist/generators/models/importexport-decoder.generator.js +17 -46
  32. package/dist/generators/models/repository.generator.d.ts +0 -39
  33. package/dist/generators/models/repository.generator.js +103 -175
  34. package/dist/generators/models/route.generator.js +7 -3
  35. package/dist/generators/models/types.generator.js +27 -2
  36. package/dist/generators/models/{businesslogic-update.generator.d.ts → update/service.generator.d.ts} +2 -2
  37. package/dist/generators/models/update/service.generator.js +252 -0
  38. package/dist/generators/models/{businesslogic-view.generator.d.ts → view/service.generator.d.ts} +2 -2
  39. package/dist/generators/models/{businesslogic-view.generator.js → view/service.generator.js} +12 -18
  40. package/dist/lib/meta.d.ts +16 -31
  41. package/dist/lib/meta.js +2 -19
  42. package/dist/lib/schema/fields.d.ts +1 -1
  43. package/dist/lib/zod.d.ts +1 -1
  44. package/dist/prisma/parse.js +1 -1
  45. package/package.json +5 -8
  46. package/dist/generators/models/businesslogic-update.generator.js +0 -324
  47. /package/dist/generators/indices/{routes-index.generator.d.ts → routes.generator.d.ts} +0 -0
  48. /package/dist/generators/models/{react.generator → react}/context.generator.d.ts +0 -0
  49. /package/dist/generators/models/{react.generator → react}/context.generator.js +0 -0
  50. /package/dist/generators/models/{react.generator → react}/index.d.ts +0 -0
  51. /package/dist/generators/models/{react.generator → react}/index.js +0 -0
  52. /package/dist/generators/models/{react.generator → react}/library.generator.d.ts +0 -0
  53. /package/dist/generators/models/{react.generator → react}/library.generator.js +0 -0
  54. /package/dist/generators/models/{react.generator → react}/lookup.generator.d.ts +0 -0
  55. /package/dist/generators/models/{react.generator → react}/lookup.generator.js +0 -0
  56. /package/dist/generators/models/{react.generator → react}/modals.generator.d.ts +0 -0
  57. /package/dist/generators/models/{react.generator → react}/modals.generator.js +0 -0
@@ -5,17 +5,18 @@ const imports_1 = require("../../lib/imports");
5
5
  const meta_1 = require("../../lib/meta");
6
6
  const types_1 = require("../../lib/schema/types");
7
7
  const types_2 = require("../../lib/types");
8
+ const zodPackageName = (0, types_1.toPackageName)('@postxl/zod');
8
9
  /**
9
10
  * Creates a decoder for the Seed Excel template.
10
11
  */
11
- function generateModelImportExportDecoder({ model, meta, schemaMeta, }) {
12
+ function generateModelImportExportDecoder({ model, meta }) {
12
13
  const { filePath: decoderFilePath, itemEncoderFunctionName: rowExportFunctionName, encodedExcelType: excelType, arrayEncoderFunctionName: tableExportFunctionName, tableDecoder: tableImportDecoder, } = meta.importExport.decoder;
13
14
  const imports = imports_1.ImportsGenerator.from(decoderFilePath).addImports({
14
- [schemaMeta.backendModules.common.importPath]: [schemaMeta.backendModules.common.functions.excelNullOrBlankDecoder],
15
+ [zodPackageName]: [(0, types_1.toFunctionName)('excelNullOrBlankDecoder')],
15
16
  [meta.types.importPath]: [(0, types_1.toAnnotatedTypeName)(meta.types.typeName)],
16
17
  });
17
18
  const { userFriendlyName: userFriendly, userFriendlyNamePlural: userFriendlyPlural, internalSingularName: singular, internalSingularNameCapitalized: singularCapitalized, } = meta;
18
- const { fieldDecoders, blankFieldDecoders } = generateFieldDecoders({ model, meta, schemaMeta, imports });
19
+ const { fieldDecoders, blankFieldDecoders } = generateFieldDecoders({ model, meta, imports });
19
20
  return `
20
21
  import * as z from 'zod'
21
22
 
@@ -72,7 +73,7 @@ export const ${tableExportFunctionName} = (items: ${model.typeName}[]): ${excelT
72
73
  `;
73
74
  }
74
75
  exports.generateModelImportExportDecoder = generateModelImportExportDecoder;
75
- function generateFieldDecoders({ model, meta, schemaMeta, imports, }) {
76
+ function generateFieldDecoders({ model, meta, imports, }) {
76
77
  const fieldDecoders = [];
77
78
  const blankFieldDecoders = [];
78
79
  for (const field of model.fields) {
@@ -84,20 +85,16 @@ function generateFieldDecoders({ model, meta, schemaMeta, imports, }) {
84
85
  imports.addImport({ items: [meta.types.toBrandedIdTypeFnName], from: meta.types.importPath });
85
86
  fieldDecoders.push(`${fieldMeta.excelColumnName}: ${toExcelDecoder({
86
87
  tsTypeName: field.unbrandedTypeName,
87
- dbTypeName: field.schemaType,
88
88
  nullable: false,
89
89
  imports,
90
- schemaMeta,
91
90
  })}.transform((id: ${field.unbrandedTypeName}) => ${meta.types.toBrandedIdTypeFnName}(id))`);
92
91
  break;
93
92
  }
94
93
  case 'scalar': {
95
94
  fieldDecoders.push(`${fieldMeta.excelColumnName}: ${toExcelDecoder({
96
95
  tsTypeName: field.tsTypeName,
97
- dbTypeName: field.schemaType,
98
96
  nullable: !field.isRequired,
99
97
  imports,
100
- schemaMeta,
101
98
  })}${optionalModifier}`);
102
99
  break;
103
100
  }
@@ -107,10 +104,8 @@ function generateFieldDecoders({ model, meta, schemaMeta, imports, }) {
107
104
  imports.addImport({ items: [refMeta.types.toBrandedIdTypeFnName], from: refMeta.types.importPath });
108
105
  fieldDecoders.push(`${fieldMeta.excelColumnName}: ${toExcelDecoder({
109
106
  tsTypeName: field.unbrandedTypeName,
110
- dbTypeName: field.schemaType,
111
107
  nullable: !field.isRequired,
112
108
  imports,
113
- schemaMeta,
114
109
  })}
115
110
  .transform(
116
111
  (id: ${field.unbrandedTypeName}${field.isRequired ? '' : '| null'}) =>
@@ -131,50 +126,26 @@ function generateFieldDecoders({ model, meta, schemaMeta, imports, }) {
131
126
  }
132
127
  return { fieldDecoders, blankFieldDecoders };
133
128
  }
134
- function toExcelDecoder({ tsTypeName,
135
- // We will use the DB type later when extending the number decoder
136
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
137
- dbTypeName, nullable, imports, schemaMeta, }) {
129
+ function toExcelDecoder({ tsTypeName, nullable, imports, }) {
138
130
  switch (tsTypeName) {
139
131
  case 'string': {
140
- const decoder = schemaMeta.backendModules.common.functions[nullable ? 'excelStringNullableDecoder' : 'excelStringDecoder'];
141
- imports.addImport({ items: [decoder], from: schemaMeta.backendModules.common.importPath });
132
+ const decoder = nullable ? (0, types_1.toFunctionName)('excelStringNullableDecoder') : (0, types_1.toFunctionName)('excelStringDecoder');
133
+ imports.addImport({ items: [decoder], from: zodPackageName });
134
+ return decoder;
135
+ }
136
+ case 'boolean': {
137
+ const decoder = (0, types_1.toFunctionName)('excelBooleanDecoder');
138
+ imports.addImport({ items: [decoder], from: zodPackageName });
142
139
  return decoder;
143
140
  }
144
- case 'boolean':
145
- imports.addImport({
146
- items: [schemaMeta.backendModules.common.functions.excelBooleanDecoder],
147
- from: schemaMeta.backendModules.common.importPath,
148
- });
149
- return 'excelBooleanDecoder';
150
141
  case 'number': {
151
- let decoder;
152
- switch (dbTypeName) {
153
- case 'Int':
154
- decoder =
155
- schemaMeta.backendModules.common.functions[nullable ? 'excelNumberNullableDecoder' : 'excelNumberDecoder'];
156
- break;
157
- case 'BigInt':
158
- decoder =
159
- schemaMeta.backendModules.common.functions[nullable ? 'excelNumberNullableDecoder' : 'excelNumberDecoder'];
160
- break;
161
- default:
162
- decoder =
163
- schemaMeta.backendModules.common.functions[nullable ? 'excelNumberNullableDecoder' : 'excelNumberDecoder'];
164
- break;
165
- }
166
- imports.addImport({
167
- items: [decoder],
168
- from: schemaMeta.backendModules.common.importPath,
169
- });
142
+ const decoder = nullable ? (0, types_1.toFunctionName)('excelNumberNullableDecoder') : (0, types_1.toFunctionName)('excelNumberDecoder');
143
+ imports.addImport({ items: [decoder], from: zodPackageName });
170
144
  return decoder;
171
145
  }
172
146
  case 'Date': {
173
- const decoder = schemaMeta.backendModules.common.functions[nullable ? 'excelDateNullableDecoder' : 'excelDateDecoder'];
174
- imports.addImport({
175
- items: [decoder],
176
- from: schemaMeta.backendModules.common.importPath,
177
- });
147
+ const decoder = nullable ? (0, types_1.toFunctionName)('excelDateNullableDecoder') : (0, types_1.toFunctionName)('excelDateDecoder');
148
+ imports.addImport({ items: [decoder], from: zodPackageName });
178
149
  return decoder;
179
150
  }
180
151
  default:
@@ -7,42 +7,3 @@ export declare function generateRepository({ model, meta }: {
7
7
  model: Model;
8
8
  meta: ModelMetaData;
9
9
  }): string;
10
- type FnSignature = {
11
- /**
12
- * A list of ordered TypeScript types where the first type in the list corresponds to the type of the
13
- * first parameter and so on. Note that the parameters don't contain names, only types.
14
- *
15
- * @example
16
- *
17
- * ```
18
- * function foo(a: string, b: number, c: boolean) {}
19
- * // ['string', 'number', 'boolean']
20
- * ```
21
- */
22
- parameters: string[];
23
- /**
24
- * The return type of the function.
25
- */
26
- returnType: string;
27
- /**
28
- * The JSDoc comment of the function.
29
- */
30
- jsDoc?: string[];
31
- };
32
- /**
33
- * Returns a collection of type signatures for the repository methods of a given model.
34
- */
35
- export declare function getRepositoryMethodsTypeSignatures({ model, meta }: {
36
- model: Model;
37
- meta: ModelMetaData;
38
- }): {
39
- create: FnSignature;
40
- createMany: FnSignature;
41
- update: FnSignature;
42
- updateMany: FnSignature;
43
- upsert: FnSignature;
44
- upsertMany: FnSignature;
45
- delete: FnSignature;
46
- deleteMany: FnSignature;
47
- };
48
- export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getRepositoryMethodsTypeSignatures = exports.generateRepository = void 0;
3
+ exports.generateRepository = void 0;
4
4
  const imports_1 = require("../../lib/imports");
5
5
  const meta_1 = require("../../lib/meta");
6
6
  const fields_1 = require("../../lib/schema/fields");
@@ -21,35 +21,19 @@ function generateRepository({ model, meta }) {
21
21
  (0, types_1.toAnnotatedTypeName)(model.typeName),
22
22
  (0, types_1.toAnnotatedTypeName)(model.brandedIdType),
23
23
  meta.types.toBrandedIdTypeFnName,
24
- (0, types_1.toAnnotatedTypeName)(meta.types.dto.create),
25
- (0, types_1.toAnnotatedTypeName)(meta.types.dto.update),
26
- (0, types_1.toAnnotatedTypeName)(meta.types.dto.upsert),
24
+ meta.types.dto.create,
25
+ meta.types.dto.update,
26
+ meta.types.dto.upsert,
27
+ meta.types.dto.clone,
27
28
  ],
28
29
  [schemaMeta.actions.execution.interfaceLocation.import]: [schemaMeta.actions.execution.interface],
29
30
  });
30
- // NOTE: We first generate different parts of the code responsible for a particular task
31
- // and then we combine them into the final code block.
32
- //
33
- // Based on the model, the repository is generated slightly differently:
34
- // 1.) if the model has a default field, the repository will have a public variable "defaultValue"
35
- // that is set and verified during init,
36
- // 2.) if the model has a generated id, the repository will have a function "generateNextId" that
37
- // is used to generate the next id. The `create` and `createMany` functions will use this function
38
- // and allow being called with an id,
39
- // 3.) unique string fields are ensured to be unique,
40
- // 4.) max length string fields are ensured to not exceed their max length,
41
- // 5.) index for fields marked with index or unique attribute,
42
- // 6.) relations are indexed by the foreign key.
43
- //
44
- // The repository is generated differently based on whether the model is stored in the database or in memory.
45
- // If the model is stored in the database, all CUD operations will result in a database call. If the model is
46
- // stored in memory, all CUD operations will be performed in memory without any database calls.
47
31
  const idBlocks = generateIdBlocks({ model, meta });
48
32
  const defaultValueBlocks = generateDefaultBlocks({ model, meta });
49
33
  const uniqueStringFieldsBlocks = generateUniqueFieldsBlocks({ model, meta });
50
34
  const maxLengthBlocks = generateMaxLengthBlocks({ model, meta });
51
35
  const validationBlocks = generateValidationBlocks({ model, meta });
52
- const indexBlocks = generateIndexBlocks({ model, schemaMeta, meta, imports });
36
+ const indexBlocks = generateIndexBlocks({ model, imports });
53
37
  const relationsBlocks = generateRelationsBlocks({ model, meta, imports });
54
38
  imports.addImports({
55
39
  [meta.types.importPath]: [meta.types.zodDecoderFnNames.fromDatabase],
@@ -58,7 +42,6 @@ function generateRepository({ model, meta }) {
58
42
  [(0, types_1.toPackageName)('@postxl/runtime')]: [(0, types_1.toFunctionName)('format'), (0, types_1.toFunctionName)('pluralize')],
59
43
  });
60
44
  const userRepositorySpecificBlocks = generateUserRepositorySpecificBlocks_InDatabase({ model, meta, imports });
61
- const methodTypeSignatures = getRepositoryMethodsTypeSignatures({ model, meta });
62
45
  return `
63
46
  import { Injectable, Logger } from '@nestjs/common'
64
47
  ${idBlocks.libraryImports}
@@ -193,10 +176,10 @@ export class ${meta.data.repository.className} implements Repository<${model.typ
193
176
  }
194
177
  }
195
178
 
196
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.create.jsDoc)}
179
+ ${(0, jsdoc_1.toJsDocComment)([`Creates a new ${meta.userFriendlyName} and returns it.`])}
197
180
  public async create(
198
- { item, execution }: ${methodTypeSignatures.create.parameters[0]}
199
- ): ${methodTypeSignatures.create.returnType} {
181
+ { item, execution }: { item: ${meta.types.dto.create}, execution: ${schemaMeta.actions.execution.interface} }
182
+ ): Promise<${model.typeName}> {
200
183
  const mutationId = await execution.startCreateMutation({
201
184
  model: '${meta.actions.actionScopeConstType}',
202
185
  createObject: item
@@ -218,10 +201,10 @@ export class ${meta.data.repository.className} implements Repository<${model.typ
218
201
  }
219
202
  }
220
203
 
221
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.createMany.jsDoc)}
204
+ ${(0, jsdoc_1.toJsDocComment)([`Creates multiple new ${meta.userFriendlyNamePlural} and returns them.`])}
222
205
  public async createMany(
223
- {items, execution}: ${methodTypeSignatures.createMany.parameters[0]}
224
- ): ${methodTypeSignatures.createMany.returnType} {
206
+ {items, execution}: { items: ${meta.types.dto.create}[], execution: ${schemaMeta.actions.execution.interface} }
207
+ ): Promise<${model.typeName}[]> {
225
208
  const mutationId = await execution.startCreateManyMutation({
226
209
  model: '${meta.actions.actionScopeConstType}',
227
210
  createObjects: items
@@ -256,10 +239,10 @@ export class ${meta.data.repository.className} implements Repository<${model.typ
256
239
  }
257
240
  }
258
241
 
259
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.update.jsDoc)}
242
+ ${(0, jsdoc_1.toJsDocComment)([`Updates a ${meta.userFriendlyName} and returns it.`])}
260
243
  public async update(
261
- { item, execution }: ${methodTypeSignatures.update.parameters[0]}
262
- ): ${methodTypeSignatures.update.returnType} {
244
+ { item, execution }: { item: ${meta.types.dto.update}, execution: ${schemaMeta.actions.execution.interface} }
245
+ ): Promise<${model.typeName}> {
263
246
  const existingItem = await this.get(item.${idField.name})
264
247
  if (!existingItem) {
265
248
  throw new Error(\`Could not update ${meta.userFriendlyName} with id \${item.id}. Not found!\`)
@@ -303,10 +286,10 @@ export class ${meta.data.repository.className} implements Repository<${model.typ
303
286
  }
304
287
  }
305
288
 
306
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.updateMany.jsDoc)}
289
+ ${(0, jsdoc_1.toJsDocComment)([`Updates multiple ${meta.userFriendlyNamePlural} and returns them.`])}
307
290
  public async updateMany(
308
- { items, execution }: ${methodTypeSignatures.updateMany.parameters[0]}
309
- ): ${methodTypeSignatures.updateMany.returnType} {
291
+ { items, execution }: { items: ${meta.types.dto.update}[], execution: ${schemaMeta.actions.execution.interface} }
292
+ ): Promise<${model.typeName}[]> {
310
293
  const result: ${model.typeName}[] = []
311
294
  for (const item of items) {
312
295
  try {
@@ -319,10 +302,10 @@ export class ${meta.data.repository.className} implements Repository<${model.typ
319
302
  return result
320
303
  }
321
304
 
322
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.upsert.jsDoc)}
305
+ ${(0, jsdoc_1.toJsDocComment)([`Creates or updates a ${meta.userFriendlyName} and returns it.`])}
323
306
  public async upsert(
324
- { item, execution }: ${methodTypeSignatures.upsert.parameters[0]}
325
- ): ${methodTypeSignatures.upsert.returnType} {
307
+ { item, execution }: { item: ${meta.types.dto.upsert}, execution: ${schemaMeta.actions.execution.interface} }
308
+ ): Promise<${model.typeName}> {
326
309
  const existingItem = item.${model.idField.name} ? (await this.get(item.${model.idField.name})) : null
327
310
  if (existingItem) {
328
311
  return this.update({ item: item as ${meta.types.dto.update}, execution })
@@ -331,10 +314,10 @@ export class ${meta.data.repository.className} implements Repository<${model.typ
331
314
  }
332
315
  }
333
316
 
334
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.upsertMany.jsDoc)}
317
+ ${(0, jsdoc_1.toJsDocComment)([`Creates or updates multiple ${meta.userFriendlyNamePlural} and returns them.`])}
335
318
  public async upsertMany(
336
- { items, execution }: ${methodTypeSignatures.upsertMany.parameters[0]}
337
- ): ${methodTypeSignatures.upsertMany.returnType} {
319
+ { items, execution }: { items: ${meta.types.dto.upsert}[], execution: ${schemaMeta.actions.execution.interface} }
320
+ ): Promise<${model.typeName}[]> {
338
321
  const result: ${model.typeName}[] = []
339
322
  for (const item of items) {
340
323
  try {
@@ -347,10 +330,10 @@ export class ${meta.data.repository.className} implements Repository<${model.typ
347
330
  return result
348
331
  }
349
332
 
350
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.delete.jsDoc)}
333
+ ${(0, jsdoc_1.toJsDocComment)([`Deletes a ${meta.userFriendlyName} and returns its id.`])}
351
334
  public async delete(
352
- { id, execution }: ${methodTypeSignatures.delete.parameters[0]}
353
- ): ${methodTypeSignatures.delete.returnType} {
335
+ { id, execution }: { id: ${model.brandedIdType}, execution: ${schemaMeta.actions.execution.interface} }
336
+ ): Promise<${model.brandedIdType}> {
354
337
  const existingItem = await this.get(id)
355
338
  if (!existingItem) {
356
339
  throw new Error(\`Could not delete ${model.typeName} with id \${id}. Not found!\`)
@@ -374,10 +357,10 @@ export class ${meta.data.repository.className} implements Repository<${model.typ
374
357
  }
375
358
  }
376
359
 
377
- ${(0, jsdoc_1.toJsDocComment)(methodTypeSignatures.deleteMany.jsDoc)}
360
+ ${(0, jsdoc_1.toJsDocComment)([`Deletes multiple ${meta.userFriendlyNamePlural} and returns their ids.`])}
378
361
  public async deleteMany(
379
- { ids, execution }: ${methodTypeSignatures.deleteMany.parameters[0]}
380
- ): ${methodTypeSignatures.deleteMany.returnType} {
362
+ { ids, execution }: { ids: ${model.brandedIdType}[], execution: ${schemaMeta.actions.execution.interface} }
363
+ ): Promise<${model.brandedIdType}[]> {
381
364
  const deletedIds: ${model.brandedIdType}[] = []
382
365
  for (const id of ids) {
383
366
  try {
@@ -390,6 +373,23 @@ export class ${meta.data.repository.className} implements Repository<${model.typ
390
373
  return deletedIds
391
374
  }
392
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
393
  ${relationsBlocks.getterFunctions.join('\n')}
394
394
 
395
395
  ${maxLengthBlocks.ensureMaxLengthFunctions.join('\n')}
@@ -593,139 +593,67 @@ function generateSharedRootUserBlocks({ model, meta, imports, }) {
593
593
  function generateIdBlocks({ model, meta }) {
594
594
  const idField = model.idField;
595
595
  if (!idField.isGenerated) {
596
- return _generateIdBlocks_NoGeneration({ idField, model, meta });
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
+ };
597
610
  }
598
611
  if (idField.schemaType === 'Int') {
599
- return _generateIdBlock_Int({ idField, model, meta });
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
+ };
600
633
  }
601
634
  if (idField.schemaType === 'String') {
602
- return _generateIdBlock_UUID({ idField, model, meta });
603
- }
604
- throw new Error(`Repository block only supports Id generation for number and strings! Found ${idField.schemaType} for model ${model.name} instead!`);
605
- }
606
- /**
607
- * Returns a collection of type signatures for the repository methods of a given model.
608
- */
609
- // NOTE: We export this function as an interface simplification to the repository generator. Internally, we
610
- // use the same functions as the ones used in this function which we don't want to expose.
611
- function getRepositoryMethodsTypeSignatures({ model, meta }) {
612
- const schemaMeta = (0, meta_1.getSchemaMetadata)({ config: model.schemaConfig });
613
- return {
614
- create: {
615
- jsDoc: [`Creates a new ${meta.userFriendlyName} and returns it.`],
616
- parameters: [`{item: ${meta.types.dto.create}, execution: ${schemaMeta.actions.execution.interface}}`],
617
- returnType: `Promise<${model.typeName}>`,
618
- },
619
- createMany: {
620
- jsDoc: [`Creates multiple new ${meta.userFriendlyNamePlural} and returns them.`],
621
- parameters: [`{items: ${meta.types.dto.create}[], execution: ${schemaMeta.actions.execution.interface}}`],
622
- returnType: `Promise<${model.typeName}[]>`,
623
- },
624
- update: {
625
- jsDoc: [`Updates a ${meta.userFriendlyName} and returns it.`],
626
- parameters: [`{item: ${meta.types.dto.update}, execution: ${schemaMeta.actions.execution.interface}}`],
627
- returnType: `Promise<${model.typeName}>`,
628
- },
629
- updateMany: {
630
- jsDoc: [`Updates multiple ${meta.userFriendlyNamePlural} and returns them.`],
631
- parameters: [`{items: ${meta.types.dto.update}[], execution: ${schemaMeta.actions.execution.interface}}`],
632
- returnType: `Promise<${model.typeName}[]>`,
633
- },
634
- upsert: {
635
- jsDoc: [`Creates or updates a ${meta.userFriendlyName} and returns it.`],
636
- parameters: [`{item: ${meta.types.dto.upsert}, execution: ${schemaMeta.actions.execution.interface}}`],
637
- returnType: `Promise<${model.typeName}>`,
638
- },
639
- upsertMany: {
640
- jsDoc: [`Creates or updates multiple ${meta.userFriendlyNamePlural} and returns them.`],
641
- parameters: [`{items: ${meta.types.dto.upsert}[], execution: ${schemaMeta.actions.execution.interface}}`],
642
- returnType: `Promise<${model.typeName}[]>`,
643
- },
644
- delete: {
645
- jsDoc: [`Deletes a ${meta.userFriendlyName} and returns its id.`],
646
- parameters: [`{id: ${model.brandedIdType}, execution: ${schemaMeta.actions.execution.interface}}`],
647
- returnType: `Promise<${model.brandedIdType}>`,
648
- },
649
- deleteMany: {
650
- jsDoc: [`Deletes multiple ${meta.userFriendlyNamePlural} and returns their ids.`],
651
- parameters: [`{ids: ${model.brandedIdType}[], execution: ${schemaMeta.actions.execution.interface}}`],
652
- returnType: `Promise<${model.brandedIdType}[]>`,
653
- },
654
- };
655
- }
656
- exports.getRepositoryMethodsTypeSignatures = getRepositoryMethodsTypeSignatures;
657
- /**
658
- * Generates the id block code chunks for a model that requires an ID field to be manually
659
- * supplied to the create function.
660
- */
661
- function _generateIdBlocks_NoGeneration({ idField, model, meta, }) {
662
- return {
663
- libraryImports: '',
664
- generateNextIdFunctionName: '',
665
- initCode: '',
666
- verifyFunctionComment: `an error is thrown as field has no default setting in schema.`,
667
- verifyFunctionParameterType: meta.types.dto.create,
668
- verifyCode: `
669
- if (item.${idField.name} === undefined) {
670
- throw new Error('Id field ${idField.name} is required!')
671
- }
672
- const ${idField.name} = ${meta.types.toBrandedIdTypeFnName}(item.${idField.name})`,
673
- setCode: '',
674
- createFunctionParameterType: model.typeName,
675
- };
676
- }
677
- /**
678
- * Generates the id block code chunks for a model that has an integer id field.
679
- * Given chunks make sure that the id is unique if provided or generate a new one if not.
680
- */
681
- function _generateIdBlock_Int({ idField, model, meta, }) {
682
- const generatedFields = model.fields.filter((f) => f.kind === 'id' || f.attributes.isReadonly);
683
- const readonlyFields = model.fields.filter((f) => f.attributes.isReadonly && f.kind !== 'id');
684
- return {
685
- libraryImports: '',
686
- generateNextIdFunctionName: `
687
- protected currentMaxId = 0
688
- public generateNextId(): ${model.brandedIdType} {
689
- return ${meta.types.toBrandedIdTypeFnName}(++this.currentMaxId)
690
- }`,
691
- initCode: `this.currentMaxId = (await this.db.${meta.data.repository.getMethodFnName}.aggregate({ _max: { ${idField.sourceName}: true } }))._max.${idField.sourceName} ?? 0`,
692
- verifyFunctionComment: 'the id is generated by increasing the highest former id and assigned to the item.',
693
- // prettier-ignore
694
- verifyFunctionParameterType: `(Omit<${model.typeName}, ${generatedFields.map((f) => `'${f.name}'`).join(' | ')}> & Partial<{${idField.name}: ${idField.unbrandedTypeName}}>)`,
695
- verifyCode: `const ${idField.name} = (item.${idField.name} !== undefined) ? ${meta.types.toBrandedIdTypeFnName}(item.${idField.name}) : this.generateNextId()`,
696
- createFunctionParameterType:
697
- // NOTE: In case we have readonly fields, we need to omit them from the create function.
698
- readonlyFields.length === 0
699
- ? model.typeName
700
- : `Omit<${model.typeName}, ${readonlyFields.map((f) => `'${f.name}'`).join(' |')}>`,
701
- setCode: `if (item.id > this.currentMaxId) { this.currentMaxId = item.id }`,
702
- };
703
- }
704
- /**
705
- * Generates the id block code chunks for a model that has a UUID id field.
706
- * It allows you to provide a custom id or generates a new one if not.
707
- */
708
- function _generateIdBlock_UUID({ idField, model, meta, }) {
709
- const dbGeneratedFields = model.fields.filter((f) => f.kind === 'id' || f.attributes.isReadonly);
710
- const readonlyFields = model.fields.filter((f) => f.attributes.isReadonly && f.kind !== 'id');
711
- return {
712
- libraryImports: `import { randomUUID } from 'crypto'`,
713
- generateNextIdFunctionName: `
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: `
714
640
  public generateNextId(): ${model.brandedIdType} {
715
641
  return ${meta.types.toBrandedIdTypeFnName}(randomUUID())
716
642
  }`,
717
- initCode: '',
718
- verifyFunctionComment: 'a new UUID is generated and assigned to the item.',
719
- // prettier-ignore
720
- verifyFunctionParameterType: `(Omit<${model.typeName}, ${dbGeneratedFields.map((f) => `'${f.name}'`).join(' | ')}> & Partial<{${idField.name}: ${idField.unbrandedTypeName}}>)`,
721
- verifyCode: `const ${idField.name} = (item.${idField.name} !== undefined) ? ${meta.types.toBrandedIdTypeFnName}(item.${idField.name}) : this.generateNextId()`,
722
- createFunctionParameterType:
723
- // NOTE: In case we have readonly fields, we need to omit them from the create function.
724
- readonlyFields.length === 0
725
- ? model.typeName
726
- : `Omit<${model.typeName}, ${readonlyFields.map((f) => `'${f.name}'`).join(' |')}>`,
727
- setCode: '',
728
- };
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!`);
729
657
  }
730
658
  /**
731
659
  * Returns the code chunks that define the default value property and its initialization.
@@ -900,10 +828,10 @@ function generateValidationBlocks({ model }) {
900
828
  }
901
829
  return result;
902
830
  }
903
- function generateIndexBlocks({ model, imports, schemaMeta, }) {
831
+ function generateIndexBlocks({ model, imports }) {
904
832
  const indexes = model.attributes.index ? [getIndexDefinition({ fieldNames: model.attributes.index, model })] : [];
905
833
  if (indexes.length > 0) {
906
- imports.addTypeImport({ items: [(0, types_1.toTypeName)('NestedMap')], from: schemaMeta.backendModules.common.importPath });
834
+ imports.addTypeImport({ from: (0, types_1.toPackageName)('@postxl/runtime'), items: [(0, types_1.toTypeName)('NestedMap')] });
907
835
  }
908
836
  const result = {
909
837
  nestedMapDeclarations: [],
@@ -8,9 +8,9 @@ const types_1 = require("../../lib/schema/types");
8
8
  */
9
9
  function generateRoute({ model, meta }) {
10
10
  const { idField, defaultField } = model;
11
- const { scopeName, dataRepositoryVariableName } = meta.update;
11
+ const { scopeName } = meta.update;
12
12
  const defaultValueMethod = `
13
- getDefault: procedure.query(({ ctx }) => ctx.view.${meta.data.dataServiceName}.${dataRepositoryVariableName}.defaultValue),
13
+ getDefault: procedure.query(({ ctx }) => ctx.view.${meta.data.dataServiceName}.data.defaultValue),
14
14
  `;
15
15
  const imports = imports_1.ImportsGenerator.from(meta.trpc.routerFilePath).addImports({
16
16
  [meta.types.importPath]: [
@@ -27,7 +27,7 @@ function generateRoute({ model, meta }) {
27
27
  });
28
28
  return /* ts */ `
29
29
  import { z } from 'zod'
30
- import { procedure, router } from '../trpc'
30
+ import { authMiddleware, procedure, router } from '../trpc'
31
31
 
32
32
  ${imports.generate()}
33
33
 
@@ -64,18 +64,22 @@ export const ${meta.trpc.routerName} = router({
64
64
  }),
65
65
 
66
66
  create: procedure
67
+ .use(authMiddleware)
67
68
  .input(${meta.update.createInputDecoder})
68
69
  .mutation(({ input, ctx }) => ctx.dispatch({scope: "${scopeName}", type: "create", payload: input})),
69
70
 
70
71
  update: procedure
72
+ .use(authMiddleware)
71
73
  .input(${meta.update.updateInputDecoder})
72
74
  .mutation(({ input, ctx }) => ctx.dispatch({scope: "${scopeName}", type: "update", payload: input})),
73
75
 
74
76
  upsert: procedure
77
+ .use(authMiddleware)
75
78
  .input(${meta.update.upsertInputDecoder})
76
79
  .mutation(({ input, ctx }) => ctx.dispatch({scope: "${scopeName}", type: "upsert", payload: input})),
77
80
 
78
81
  delete: procedure
82
+ .use(authMiddleware)
79
83
  .input(${meta.types.zodDecoderFnNames.id})
80
84
  .mutation(({ input, ctx }) => ctx.dispatch({scope: "${scopeName}", type: "delete", payload: input})),
81
85
  })
@@ -53,8 +53,6 @@ export type ${meta.types.typeName} = {
53
53
  .join('\n')}
54
54
  }
55
55
 
56
-
57
-
58
56
  /**
59
57
  * Branded Id type that should be used to identify an instance of a ${meta.userFriendlyName}.
60
58
  */
@@ -95,6 +93,33 @@ export type ${meta.types.dto.update} = ${schemaMeta.types.dto.update}<${meta.typ
95
93
  * Data transfer object for upserting a new or existing ${meta.userFriendlyName} instance.
96
94
  */
97
95
  export type ${meta.types.dto.upsert} = ${schemaMeta.types.dto.upsert}<${meta.types.typeName}, ${model.brandedIdType}>
96
+
97
+ /**
98
+ * Data transfer object for cloning a new ${meta.userFriendlyName} instance.
99
+ */
100
+ export type ${meta.types.dto.clone} = {
101
+ ${model.fields
102
+ .map((f) => {
103
+ const type = (0, typescript_1.getFieldType)(f);
104
+ // NOTE: ID field is always required to resolve the source.
105
+ if (f.kind === 'id') {
106
+ return `${f.name}: ${type}`;
107
+ }
108
+ if (f.isUnique) {
109
+ // NOTE: `unique` fields require a new value.
110
+ if (f.isRequired) {
111
+ return `${f.name}: ${type}`;
112
+ }
113
+ return `${f.name}: ${type} | null`;
114
+ }
115
+ // NOTE: Non-unique fields can be copied from the source.
116
+ if (f.isRequired) {
117
+ return `${f.name}?: ${type}`;
118
+ }
119
+ return `${f.name}?: ${type} | null`;
120
+ })
121
+ .join(', ')}
122
+ }
98
123
  `;
99
124
  }
100
125
  exports.generateModelTypes = generateModelTypes;